Przeglądaj źródła

SubGhz: add protocol MegaCode (#1204)

* SubGhz: add protocol MegaCode
* SubGhz: check for guard time injection at the end of buffer
* SubGhz: rollback samples counting in trasmitter
* SubGhz: fix subghz_file_encoder_worker incorrect pulse sequence
* Input: tune debounce interval
* SubGhz: fix spelling in subghz_file_encoder_worker_add_level_duration

Co-authored-by: Aleksandr Kutuzov <alleteam@gmail.com>
Skorpionm 3 lat temu
rodzic
commit
f04d0eea96

+ 2 - 1
applications/subghz/scenes/subghz_scene_read_raw.c

@@ -202,7 +202,8 @@ bool subghz_scene_read_raw_on_event(void* context, SceneManagerEvent event) {
                         DOLPHIN_DEED(DolphinDeedSubGhzSend);
                         // set callback end tx
                         subghz_protocol_raw_file_encoder_worker_set_callback_end(
-                            (SubGhzProtocolEncoderRAW*)subghz->txrx->transmitter->protocol_instance,
+                            (SubGhzProtocolEncoderRAW*)subghz_transmitter_get_protocol_instance(
+                                subghz->txrx->transmitter),
                             subghz_scene_read_raw_callback_end_tx,
                             subghz);
                         subghz->state_notifications = SubGhzNotificationStateTx;

+ 2 - 2
applications/subghz/scenes/subghz_scene_set_type.c

@@ -268,7 +268,7 @@ bool subghz_scene_set_type_on_event(void* context, SceneManagerEvent event) {
                 subghz->txrx->environment, SUBGHZ_PROTOCOL_KEELOQ_NAME);
             if(subghz->txrx->transmitter) {
                 subghz_protocol_keeloq_create_data(
-                    subghz->txrx->transmitter->protocol_instance,
+                    subghz_transmitter_get_protocol_instance(subghz->txrx->transmitter),
                     subghz->txrx->fff_data,
                     key & 0x0FFFFFFF,
                     0x2,
@@ -292,7 +292,7 @@ bool subghz_scene_set_type_on_event(void* context, SceneManagerEvent event) {
                 subghz->txrx->environment, SUBGHZ_PROTOCOL_KEELOQ_NAME);
             if(subghz->txrx->transmitter) {
                 subghz_protocol_keeloq_create_data(
-                    subghz->txrx->transmitter->protocol_instance,
+                    subghz_transmitter_get_protocol_instance(subghz->txrx->transmitter),
                     subghz->txrx->fff_data,
                     key & 0x0FFFFFFF,
                     0x2,

+ 1 - 1
firmware/targets/f7/furi_hal/furi_hal_resources.h

@@ -10,7 +10,7 @@ extern "C" {
 #endif
 
 /* Input Related Constants */
-#define INPUT_DEBOUNCE_TICKS 20
+#define INPUT_DEBOUNCE_TICKS 30
 
 /* Input Keys */
 typedef enum {

+ 3 - 0
firmware/targets/f7/furi_hal/furi_hal_subghz.c

@@ -799,6 +799,9 @@ static void furi_hal_subghz_async_tx_refill(uint32_t* buffer, size_t samples) {
                 } else {
                     furi_hal_subghz_async_tx.duty_low += API_HAL_SUBGHZ_ASYNC_TX_GUARD_TIME;
                 }
+                // This code must be invoked only once: when encoder starts with low level.
+                // Otherwise whole thing will crash.
+                furi_check(samples > 0);
             }
 
             uint32_t duration = level_duration_get_duration(ld);

+ 413 - 0
lib/subghz/protocols/megacode.c

@@ -0,0 +1,413 @@
+#include "megacode.h"
+
+#include "../blocks/const.h"
+#include "../blocks/decoder.h"
+#include "../blocks/encoder.h"
+#include "../blocks/generic.h"
+#include "../blocks/math.h"
+
+/*
+ * Help
+ * https://wiki.cuvoodoo.info/doku.php?id=megacode
+ * https://wiki.cuvoodoo.info/lib/exe/fetch.php?media=megacode:megacode_1.pdf
+ * https://fccid.io/EF4ACP00872/Test-Report/Megacode-2-112615.pdf
+ * https://github.com/aaronsp777/megadecoder
+ * https://github.com/rjmendez/Linear_keyfob
+ * https://github.com/j07rdi/Linear_MegaCode_Garage_Remote
+ *
+ */
+
+#define TAG "SubGhzProtocolMegaCode"
+
+static const SubGhzBlockConst subghz_protocol_megacode_const = {
+    .te_short = 1000,
+    .te_long = 1000,
+    .te_delta = 200,
+    .min_count_bit_for_found = 24,
+};
+
+struct SubGhzProtocolDecoderMegaCode {
+    SubGhzProtocolDecoderBase base;
+
+    SubGhzBlockDecoder decoder;
+    SubGhzBlockGeneric generic;
+    uint8_t last_bit;
+};
+
+struct SubGhzProtocolEncoderMegaCode {
+    SubGhzProtocolEncoderBase base;
+
+    SubGhzProtocolBlockEncoder encoder;
+    SubGhzBlockGeneric generic;
+};
+
+typedef enum {
+    MegaCodeDecoderStepReset = 0,
+    MegaCodeDecoderStepFoundStartBit,
+    MegaCodeDecoderStepSaveDuration,
+    MegaCodeDecoderStepCheckDuration,
+} MegaCodeDecoderStep;
+
+const SubGhzProtocolDecoder subghz_protocol_megacode_decoder = {
+    .alloc = subghz_protocol_decoder_megacode_alloc,
+    .free = subghz_protocol_decoder_megacode_free,
+
+    .feed = subghz_protocol_decoder_megacode_feed,
+    .reset = subghz_protocol_decoder_megacode_reset,
+
+    .get_hash_data = subghz_protocol_decoder_megacode_get_hash_data,
+    .serialize = subghz_protocol_decoder_megacode_serialize,
+    .deserialize = subghz_protocol_decoder_megacode_deserialize,
+    .get_string = subghz_protocol_decoder_megacode_get_string,
+};
+
+const SubGhzProtocolEncoder subghz_protocol_megacode_encoder = {
+    .alloc = subghz_protocol_encoder_megacode_alloc,
+    .free = subghz_protocol_encoder_megacode_free,
+
+    .deserialize = subghz_protocol_encoder_megacode_deserialize,
+    .stop = subghz_protocol_encoder_megacode_stop,
+    .yield = subghz_protocol_encoder_megacode_yield,
+};
+
+const SubGhzProtocol subghz_protocol_megacode = {
+    .name = SUBGHZ_PROTOCOL_MEGACODE_NAME,
+    .type = SubGhzProtocolTypeStatic,
+    .flag = SubGhzProtocolFlag_315 | SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable |
+            SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Send,
+
+    .decoder = &subghz_protocol_megacode_decoder,
+    .encoder = &subghz_protocol_megacode_encoder,
+};
+
+void* subghz_protocol_encoder_megacode_alloc(SubGhzEnvironment* environment) {
+    UNUSED(environment);
+    SubGhzProtocolEncoderMegaCode* instance = malloc(sizeof(SubGhzProtocolEncoderMegaCode));
+
+    instance->base.protocol = &subghz_protocol_megacode;
+    instance->generic.protocol_name = instance->base.protocol->name;
+
+    instance->encoder.repeat = 10;
+    instance->encoder.size_upload = 52;
+    instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration));
+    instance->encoder.is_runing = false;
+    return instance;
+}
+
+void subghz_protocol_encoder_megacode_free(void* context) {
+    furi_assert(context);
+    SubGhzProtocolEncoderMegaCode* instance = context;
+    free(instance->encoder.upload);
+    free(instance);
+}
+
+/**
+ * Generating an upload from data.
+ * @param instance Pointer to a SubGhzProtocolEncoderMegaCode instance
+ * @return true On success
+ */
+static bool subghz_protocol_encoder_megacode_get_upload(SubGhzProtocolEncoderMegaCode* instance) {
+    furi_assert(instance);
+    uint8_t last_bit = 0;
+    size_t size_upload = (instance->generic.data_count_bit * 2);
+    if(size_upload > instance->encoder.size_upload) {
+        FURI_LOG_E(TAG, "Size upload exceeds allocated encoder buffer.");
+        return false;
+    } else {
+        instance->encoder.size_upload = size_upload;
+    }
+
+    /*
+    * Due to the nature of the protocol
+    *
+    *  00000 1
+    *  _____|-| = 1 becomes
+    * 
+    *  00 1 000
+    *  __|-|___ = 0 becomes
+    * 
+    * it's easier for us to generate an upload backwards
+    */
+
+    size_t index = size_upload - 1;
+
+    // Send end level
+    instance->encoder.upload[index--] =
+        level_duration_make(true, (uint32_t)subghz_protocol_megacode_const.te_short);
+    if(bit_read(instance->generic.data, 0)) {
+        last_bit = 1;
+    } else {
+        last_bit = 0;
+    }
+
+    //Send key data
+    for(uint8_t i = 1; i < instance->generic.data_count_bit; i++) {
+        if(bit_read(instance->generic.data, i)) {
+            //if bit 1
+            instance->encoder.upload[index--] = level_duration_make(
+                false,
+                last_bit ? (uint32_t)subghz_protocol_megacode_const.te_short * 5 :
+                           (uint32_t)subghz_protocol_megacode_const.te_short * 2);
+            last_bit = 1;
+        } else {
+            //if bit 0
+            instance->encoder.upload[index--] = level_duration_make(
+                false,
+                last_bit ? (uint32_t)subghz_protocol_megacode_const.te_short * 8 :
+                           (uint32_t)subghz_protocol_megacode_const.te_short * 5);
+            last_bit = 0;
+        }
+        instance->encoder.upload[index--] =
+            level_duration_make(true, (uint32_t)subghz_protocol_megacode_const.te_short);
+    }
+
+    //Send PT_GUARD
+    if(bit_read(instance->generic.data, 0)) {
+        //if end bit 1
+        instance->encoder.upload[index] =
+            level_duration_make(false, (uint32_t)subghz_protocol_megacode_const.te_short * 11);
+    } else {
+        //if end bit 1
+        instance->encoder.upload[index] =
+            level_duration_make(false, (uint32_t)subghz_protocol_megacode_const.te_short * 14);
+    }
+
+    return true;
+}
+
+bool subghz_protocol_encoder_megacode_deserialize(void* context, FlipperFormat* flipper_format) {
+    furi_assert(context);
+    SubGhzProtocolEncoderMegaCode* instance = context;
+    bool res = false;
+    do {
+        if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) {
+            FURI_LOG_E(TAG, "Deserialize error");
+            break;
+        }
+
+        //optional parameter parameter
+        flipper_format_read_uint32(
+            flipper_format, "Repeat", (uint32_t*)&instance->encoder.repeat, 1);
+
+        subghz_protocol_encoder_megacode_get_upload(instance);
+        instance->encoder.is_runing = true;
+
+        res = true;
+    } while(false);
+
+    return res;
+}
+
+void subghz_protocol_encoder_megacode_stop(void* context) {
+    SubGhzProtocolEncoderMegaCode* instance = context;
+    instance->encoder.is_runing = false;
+}
+
+LevelDuration subghz_protocol_encoder_megacode_yield(void* context) {
+    SubGhzProtocolEncoderMegaCode* instance = context;
+
+    if(instance->encoder.repeat == 0 || !instance->encoder.is_runing) {
+        instance->encoder.is_runing = false;
+        return level_duration_reset();
+    }
+
+    LevelDuration ret = instance->encoder.upload[instance->encoder.front];
+
+    if(++instance->encoder.front == instance->encoder.size_upload) {
+        instance->encoder.repeat--;
+        instance->encoder.front = 0;
+    }
+
+    return ret;
+}
+
+void* subghz_protocol_decoder_megacode_alloc(SubGhzEnvironment* environment) {
+    UNUSED(environment);
+    SubGhzProtocolDecoderMegaCode* instance = malloc(sizeof(SubGhzProtocolDecoderMegaCode));
+    instance->base.protocol = &subghz_protocol_megacode;
+    instance->generic.protocol_name = instance->base.protocol->name;
+    return instance;
+}
+
+void subghz_protocol_decoder_megacode_free(void* context) {
+    furi_assert(context);
+    SubGhzProtocolDecoderMegaCode* instance = context;
+    free(instance);
+}
+
+void subghz_protocol_decoder_megacode_reset(void* context) {
+    furi_assert(context);
+    SubGhzProtocolDecoderMegaCode* instance = context;
+    instance->decoder.parser_step = MegaCodeDecoderStepReset;
+}
+
+void subghz_protocol_decoder_megacode_feed(void* context, bool level, uint32_t duration) {
+    furi_assert(context);
+    SubGhzProtocolDecoderMegaCode* instance = context;
+    switch(instance->decoder.parser_step) {
+    case MegaCodeDecoderStepReset:
+        if((!level) && (DURATION_DIFF(duration, subghz_protocol_megacode_const.te_short * 13) <
+                        subghz_protocol_megacode_const.te_delta * 15)) { //10..16ms
+            //Found header MegaCode
+            instance->decoder.parser_step = MegaCodeDecoderStepFoundStartBit;
+        }
+        break;
+    case MegaCodeDecoderStepFoundStartBit:
+        if(level && (DURATION_DIFF(duration, subghz_protocol_megacode_const.te_short) <
+                     subghz_protocol_megacode_const.te_delta)) {
+            //Found start bit MegaCode
+            instance->decoder.parser_step = MegaCodeDecoderStepSaveDuration;
+            instance->decoder.decode_data = 0;
+            instance->decoder.decode_count_bit = 0;
+            subghz_protocol_blocks_add_bit(&instance->decoder, 1);
+            instance->last_bit = 1;
+
+        } else {
+            instance->decoder.parser_step = MegaCodeDecoderStepReset;
+        }
+        break;
+    case MegaCodeDecoderStepSaveDuration:
+        if(!level) { //save interval
+            if(duration >= (subghz_protocol_megacode_const.te_short * 10)) {
+                instance->decoder.parser_step = MegaCodeDecoderStepReset;
+                if(instance->decoder.decode_count_bit >=
+                   subghz_protocol_megacode_const.min_count_bit_for_found) {
+                    instance->generic.data = instance->decoder.decode_data;
+                    instance->generic.data_count_bit = instance->decoder.decode_count_bit;
+
+                    if(instance->base.callback)
+                        instance->base.callback(&instance->base, instance->base.context);
+                }
+                break;
+            }
+
+            if(!instance->last_bit) {
+                instance->decoder.te_last = duration - subghz_protocol_megacode_const.te_short * 3;
+            } else {
+                instance->decoder.te_last = duration;
+            }
+            instance->decoder.parser_step = MegaCodeDecoderStepCheckDuration;
+        } else {
+            instance->decoder.parser_step = MegaCodeDecoderStepReset;
+        }
+        break;
+    case MegaCodeDecoderStepCheckDuration:
+        if(level) {
+            if((DURATION_DIFF(
+                    instance->decoder.te_last, subghz_protocol_megacode_const.te_short * 5) <
+                subghz_protocol_megacode_const.te_delta * 5) &&
+               (DURATION_DIFF(duration, subghz_protocol_megacode_const.te_short) <
+                subghz_protocol_megacode_const.te_delta)) {
+                subghz_protocol_blocks_add_bit(&instance->decoder, 1);
+                instance->last_bit = 1;
+                instance->decoder.parser_step = MegaCodeDecoderStepSaveDuration;
+            } else if(
+                (DURATION_DIFF(
+                     instance->decoder.te_last, subghz_protocol_megacode_const.te_short * 2) <
+                 subghz_protocol_megacode_const.te_delta * 2) &&
+                (DURATION_DIFF(duration, subghz_protocol_megacode_const.te_short) <
+                 subghz_protocol_megacode_const.te_delta)) {
+                subghz_protocol_blocks_add_bit(&instance->decoder, 0);
+                instance->last_bit = 0;
+                instance->decoder.parser_step = MegaCodeDecoderStepSaveDuration;
+            } else
+                instance->decoder.parser_step = MegaCodeDecoderStepReset;
+        } else {
+            instance->decoder.parser_step = MegaCodeDecoderStepReset;
+        }
+        break;
+    }
+}
+
+/** 
+ * Analysis of received data
+ * @param instance Pointer to a SubGhzBlockGeneric* instance
+ */
+static void subghz_protocol_megacode_check_remote_controller(SubGhzBlockGeneric* instance) {
+    /*
+    * Short: 1000 µs
+    * Long: 1000 µs
+    * Gap: 11000 .. 14000 µs
+    * A Linear Megacode transmission consists of 24 bit frames starting with 
+    * the most significant bit and ending with the least. Each of the 24 bit 
+    * frames is 6 milliseconds wide and always contains a single 1 millisecond 
+    * pulse. A frame with more than 1 pulse or a frame with no pulse is invalid 
+    * and a receiver should reset and begin watching for another start bit. 
+    * Start bit is always 1.
+    * 
+    * 
+    * Example (I created with my own remote):
+    * Remote “A” has the code “17316”, a Facility Code of “3”, and a single button.
+    * Start bit (S) = 1
+    * Facility Code 3 (F) = 0011
+    * Remote Code (Key) 17316 = 43A4 = 0100001110100100
+    * Button (Btn) 1 = 001
+    *          S  F        Key         Btn
+    * Result = 1|0011|0100001110100100|001
+    * 
+    *  00000 1
+    *  _____|-| = 1 becomes
+    * 
+    *  00 1 000
+    *  __|-|___ = 0 becomes
+    * 
+    * The device needs to transmit with a 9000 µs gap between retransmissions:
+    * 000001 001000 001000 000001 000001 001000 000001 001000 001000 001000 001000 000001
+    * 000001 000001 001000 000001 001000 001000 000001 001000 001000 001000 001000 000001
+    * wait 9000 µs
+    * 000001 001000 001000 000001 000001 001000 000001 001000 001000 001000 001000 000001
+    * 000001 000001 001000 000001 001000 001000 000001 001000 001000 001000 001000 000001
+    * 
+    */
+    if((instance->data >> 23) == 1) {
+        instance->serial = (instance->data >> 3) & 0xFFFF;
+        instance->btn = instance->data & 0b111;
+        instance->cnt = (instance->data >> 19) & 0b1111;
+    } else {
+        instance->serial = 0;
+        instance->btn = 0;
+        instance->cnt = 0;
+    }
+}
+
+uint8_t subghz_protocol_decoder_megacode_get_hash_data(void* context) {
+    furi_assert(context);
+    SubGhzProtocolDecoderMegaCode* instance = context;
+    return subghz_protocol_blocks_get_hash_data(
+        &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1);
+}
+
+bool subghz_protocol_decoder_megacode_serialize(
+    void* context,
+    FlipperFormat* flipper_format,
+    uint32_t frequency,
+    FuriHalSubGhzPreset preset) {
+    furi_assert(context);
+    SubGhzProtocolDecoderMegaCode* instance = context;
+    return subghz_block_generic_serialize(&instance->generic, flipper_format, frequency, preset);
+}
+
+bool subghz_protocol_decoder_megacode_deserialize(void* context, FlipperFormat* flipper_format) {
+    furi_assert(context);
+    SubGhzProtocolDecoderMegaCode* instance = context;
+    return subghz_block_generic_deserialize(&instance->generic, flipper_format);
+}
+
+void subghz_protocol_decoder_megacode_get_string(void* context, string_t output) {
+    furi_assert(context);
+    SubGhzProtocolDecoderMegaCode* instance = context;
+    subghz_protocol_megacode_check_remote_controller(&instance->generic);
+
+    string_cat_printf(
+        output,
+        "%s %dbit\r\n"
+        "Key:%06lX\r\n"
+        "Sn:%04lX Btn:%X\r\n"
+        "Facility:%X\r\n",
+        instance->generic.protocol_name,
+        instance->generic.data_count_bit,
+        (uint32_t)instance->generic.data,
+        instance->generic.serial,
+        instance->generic.btn,
+        instance->generic.cnt);
+}

+ 109 - 0
lib/subghz/protocols/megacode.h

@@ -0,0 +1,109 @@
+#pragma once
+
+#include "base.h"
+
+#define SUBGHZ_PROTOCOL_MEGACODE_NAME "MegaCode"
+
+typedef struct SubGhzProtocolDecoderMegaCode SubGhzProtocolDecoderMegaCode;
+typedef struct SubGhzProtocolEncoderMegaCode SubGhzProtocolEncoderMegaCode;
+
+extern const SubGhzProtocolDecoder subghz_protocol_megacode_decoder;
+extern const SubGhzProtocolEncoder subghz_protocol_megacode_encoder;
+extern const SubGhzProtocol subghz_protocol_megacode;
+
+/**
+ * Allocate SubGhzProtocolEncoderMegaCode.
+ * @param environment Pointer to a SubGhzEnvironment instance
+ * @return SubGhzProtocolEncoderMegaCode* pointer to a SubGhzProtocolEncoderMegaCode instance
+ */
+void* subghz_protocol_encoder_megacode_alloc(SubGhzEnvironment* environment);
+
+/**
+ * Free SubGhzProtocolEncoderMegaCode.
+ * @param context Pointer to a SubGhzProtocolEncoderMegaCode instance
+ */
+void subghz_protocol_encoder_megacode_free(void* context);
+
+/**
+ * Deserialize and generating an upload to send.
+ * @param context Pointer to a SubGhzProtocolEncoderMegaCode instance
+ * @param flipper_format Pointer to a FlipperFormat instance
+ * @return true On success
+ */
+bool subghz_protocol_encoder_megacode_deserialize(void* context, FlipperFormat* flipper_format);
+
+/**
+ * Forced transmission stop.
+ * @param context Pointer to a SubGhzProtocolEncoderMegaCode instance
+ */
+void subghz_protocol_encoder_megacode_stop(void* context);
+
+/**
+ * Getting the level and duration of the upload to be loaded into DMA.
+ * @param context Pointer to a SubGhzProtocolEncoderMegaCode instance
+ * @return LevelDuration 
+ */
+LevelDuration subghz_protocol_encoder_megacode_yield(void* context);
+
+/**
+ * Allocate SubGhzProtocolDecoderMegaCode.
+ * @param environment Pointer to a SubGhzEnvironment instance
+ * @return SubGhzProtocolDecoderMegaCode* pointer to a SubGhzProtocolDecoderMegaCode instance
+ */
+void* subghz_protocol_decoder_megacode_alloc(SubGhzEnvironment* environment);
+
+/**
+ * Free SubGhzProtocolDecoderMegaCode.
+ * @param context Pointer to a SubGhzProtocolDecoderMegaCode instance
+ */
+void subghz_protocol_decoder_megacode_free(void* context);
+
+/**
+ * Reset decoder SubGhzProtocolDecoderMegaCode.
+ * @param context Pointer to a SubGhzProtocolDecoderMegaCode instance
+ */
+void subghz_protocol_decoder_megacode_reset(void* context);
+
+/**
+ * Parse a raw sequence of levels and durations received from the air.
+ * @param context Pointer to a SubGhzProtocolDecoderMegaCode instance
+ * @param level Signal level true-high false-low
+ * @param duration Duration of this level in, us
+ */
+void subghz_protocol_decoder_megacode_feed(void* context, bool level, uint32_t duration);
+
+/**
+ * Getting the hash sum of the last randomly received parcel.
+ * @param context Pointer to a SubGhzProtocolDecoderMegaCode instance
+ * @return hash Hash sum
+ */
+uint8_t subghz_protocol_decoder_megacode_get_hash_data(void* context);
+
+/**
+ * Serialize data SubGhzProtocolDecoderMegaCode.
+ * @param context Pointer to a SubGhzProtocolDecoderMegaCode instance
+ * @param flipper_format Pointer to a FlipperFormat instance
+ * @param frequency The frequency at which the signal was received, Hz
+ * @param preset The modulation on which the signal was received, FuriHalSubGhzPreset
+ * @return true On success
+ */
+bool subghz_protocol_decoder_megacode_serialize(
+    void* context,
+    FlipperFormat* flipper_format,
+    uint32_t frequency,
+    FuriHalSubGhzPreset preset);
+
+/**
+ * Deserialize data SubGhzProtocolDecoderMegaCode.
+ * @param context Pointer to a SubGhzProtocolDecoderMegaCode instance
+ * @param flipper_format Pointer to a FlipperFormat instance
+ * @return true On success
+ */
+bool subghz_protocol_decoder_megacode_deserialize(void* context, FlipperFormat* flipper_format);
+
+/**
+ * Getting a textual representation of the received data.
+ * @param context Pointer to a SubGhzProtocolDecoderMegaCode instance
+ * @param output Resulting text
+ */
+void subghz_protocol_decoder_megacode_get_string(void* context, string_t output);

+ 1 - 1
lib/subghz/protocols/registry.c

@@ -8,7 +8,7 @@ const SubGhzProtocol* subghz_protocol_registry[] = {
     &subghz_protocol_hormann,      &subghz_protocol_nero_radio, &subghz_protocol_somfy_telis,
     &subghz_protocol_somfy_keytis, &subghz_protocol_scher_khan, &subghz_protocol_gate_tx,
     &subghz_protocol_raw,          &subghz_protocol_firefly,    &subghz_protocol_secplus_v2,
-    &subghz_protocol_secplus_v1,
+    &subghz_protocol_secplus_v1,   &subghz_protocol_megacode,
 
 };
 

+ 1 - 0
lib/subghz/protocols/registry.h

@@ -24,6 +24,7 @@
 #include "firefly.h"
 #include "secplus_v2.h"
 #include "secplus_v1.h"
+#include "megacode.h"
 
 /**
  * Registration by name SubGhzProtocol.

+ 9 - 14
lib/subghz/subghz_file_encoder_worker.c

@@ -19,7 +19,6 @@ struct SubGhzFileEncoderWorker {
     volatile bool worker_running;
     volatile bool worker_stoping;
     bool level;
-    int32_t duration;
     string_t str_data;
     string_t file_path;
 
@@ -37,25 +36,21 @@ void subghz_file_encoder_worker_callback_end(
     instance->context_end = context_end;
 }
 
-void subghz_file_encoder_worker_add_livel_duration(
+void subghz_file_encoder_worker_add_level_duration(
     SubGhzFileEncoderWorker* instance,
     int32_t duration) {
     bool res = true;
     if(duration < 0 && !instance->level) {
-        instance->duration += duration;
         res = false;
     } else if(duration > 0 && instance->level) {
-        instance->duration += duration;
         res = false;
-    } else if(duration == 0) {
-        instance->duration = 0;
     }
 
     if(res) {
         instance->level = !instance->level;
-        instance->duration += duration;
-        xStreamBufferSend(instance->stream, &instance->duration, sizeof(int32_t), 10);
-        instance->duration = 0;
+        xStreamBufferSend(instance->stream, &duration, sizeof(int32_t), 100);
+    } else {
+        FURI_LOG_E(TAG, "Invalid level in the stream");
     }
 }
 
@@ -80,7 +75,7 @@ bool subghz_file_encoder_worker_data_parse(
               ind_start))) { //check that there is still an element in the line and that it has not gone beyond the line
             str1 = strchr(str1, ' ');
             str1 += 1; //if found, shift the pointer by next element per line
-            subghz_file_encoder_worker_add_livel_duration(instance, atoi(str1));
+            subghz_file_encoder_worker_add_level_duration(instance, atoi(str1));
         }
         res = true;
     }
@@ -152,14 +147,14 @@ static int32_t subghz_file_encoder_worker_thread(void* context) {
                        string_get_cstr(instance->str_data),
                        strlen(string_get_cstr(instance->str_data)))) {
                     //to stop DMA correctly
-                    subghz_file_encoder_worker_add_livel_duration(instance, LEVEL_DURATION_RESET);
-                    subghz_file_encoder_worker_add_livel_duration(instance, LEVEL_DURATION_RESET);
+                    subghz_file_encoder_worker_add_level_duration(instance, LEVEL_DURATION_RESET);
+                    subghz_file_encoder_worker_add_level_duration(instance, LEVEL_DURATION_RESET);
 
                     break;
                 }
             } else {
-                subghz_file_encoder_worker_add_livel_duration(instance, LEVEL_DURATION_RESET);
-                subghz_file_encoder_worker_add_livel_duration(instance, LEVEL_DURATION_RESET);
+                subghz_file_encoder_worker_add_level_duration(instance, LEVEL_DURATION_RESET);
+                subghz_file_encoder_worker_add_level_duration(instance, LEVEL_DURATION_RESET);
                 break;
             }
         }

+ 10 - 1
lib/subghz/transmitter.c

@@ -3,6 +3,11 @@
 #include "protocols/base.h"
 #include "protocols/registry.h"
 
+struct SubGhzTransmitter {
+    const SubGhzProtocol* protocol;
+    SubGhzProtocolEncoderBase* protocol_instance;
+};
+
 SubGhzTransmitter*
     subghz_transmitter_alloc_init(SubGhzEnvironment* environment, const char* protocol_name) {
     SubGhzTransmitter* instance = NULL;
@@ -23,6 +28,11 @@ void subghz_transmitter_free(SubGhzTransmitter* instance) {
     free(instance);
 }
 
+SubGhzProtocolEncoderBase* subghz_transmitter_get_protocol_instance(SubGhzTransmitter* instance) {
+    furi_assert(instance);
+    return instance->protocol_instance;
+}
+
 bool subghz_transmitter_stop(SubGhzTransmitter* instance) {
     furi_assert(instance);
     bool ret = false;
@@ -46,6 +56,5 @@ bool subghz_transmitter_deserialize(SubGhzTransmitter* instance, FlipperFormat*
 
 LevelDuration subghz_transmitter_yield(void* context) {
     SubGhzTransmitter* instance = context;
-
     return instance->protocol->encoder->yield(instance->protocol_instance);
 }

+ 5 - 5
lib/subghz/transmitter.h

@@ -6,11 +6,6 @@
 
 typedef struct SubGhzTransmitter SubGhzTransmitter;
 
-struct SubGhzTransmitter {
-    const SubGhzProtocol* protocol;
-    SubGhzProtocolEncoderBase* protocol_instance;
-};
-
 /**
  * Allocate and init SubGhzTransmitter.
  * @param environment Pointer to a SubGhzEnvironment instance
@@ -25,6 +20,11 @@ SubGhzTransmitter*
  */
 void subghz_transmitter_free(SubGhzTransmitter* instance);
 
+/** Get protocol instance.
+ * @param instance Pointer to a SubGhzTransmitter instance
+ */
+SubGhzProtocolEncoderBase* subghz_transmitter_get_protocol_instance(SubGhzTransmitter* instance);
+
 /**
  * Forced transmission stop.
  * @param instance Pointer to a SubGhzTransmitter instance