Просмотр исходного кода

[FL-2718, FL-2719] SubGhz: add protocol BERNER / ELKA / TEDSEN / TELETASTER / Doitrand / Marantec / Phoenix V2 (static mode) / Phox (static mode), fix Princeton (#1516)

* SubGhz: add protocol marantec
* SubGhz: add protocol BERNER / ELKA / TEDSEN / TELETASTER
* SubGhz: add protocol Doitrand
* SubGhz: delete debug
* SubGhz: add protocol Phoenix V2 (static mode)
* SubGhz: fix serial decode Phoenix V2
* SubGhz: fix Princeton, display serial number and button on boot
* SubGhz: fix Bett decoder and fix unit_test
* SubGhz: update test_random_raw for unit_test

Co-authored-by: あく <alleteam@gmail.com>
Skorpionm 3 лет назад
Родитель
Сommit
416cce9ffc

+ 60 - 1
applications/unit_tests/subghz/subghz_test.c

@@ -13,7 +13,7 @@
 #define CAME_ATOMO_DIR_NAME EXT_PATH("subghz/assets/came_atomo")
 #define CAME_ATOMO_DIR_NAME EXT_PATH("subghz/assets/came_atomo")
 #define NICE_FLOR_S_DIR_NAME EXT_PATH("subghz/assets/nice_flor_s")
 #define NICE_FLOR_S_DIR_NAME EXT_PATH("subghz/assets/nice_flor_s")
 #define TEST_RANDOM_DIR_NAME EXT_PATH("unit_tests/subghz/test_random_raw.sub")
 #define TEST_RANDOM_DIR_NAME EXT_PATH("unit_tests/subghz/test_random_raw.sub")
-#define TEST_RANDOM_COUNT_PARSE 119
+#define TEST_RANDOM_COUNT_PARSE 158
 #define TEST_TIMEOUT 10000
 #define TEST_TIMEOUT 10000
 
 
 static SubGhzEnvironment* environment_handler;
 static SubGhzEnvironment* environment_handler;
@@ -377,6 +377,33 @@ MU_TEST(subghz_decoder_power_smart_test) {
         "Test decoder " SUBGHZ_PROTOCOL_POWER_SMART_NAME " error\r\n");
         "Test decoder " SUBGHZ_PROTOCOL_POWER_SMART_NAME " error\r\n");
 }
 }
 
 
+MU_TEST(subghz_decoder_marantec_test) {
+    mu_assert(
+        subghz_decoder_test(
+            EXT_PATH("unit_tests/subghz/marantec_raw.sub"), SUBGHZ_PROTOCOL_MARANTEC_NAME),
+        "Test decoder " SUBGHZ_PROTOCOL_MARANTEC_NAME " error\r\n");
+}
+
+MU_TEST(subghz_decoder_bett_test) {
+    mu_assert(
+        subghz_decoder_test(EXT_PATH("unit_tests/subghz/bett_raw.sub"), SUBGHZ_PROTOCOL_BETT_NAME),
+        "Test decoder " SUBGHZ_PROTOCOL_BETT_NAME " error\r\n");
+}
+
+MU_TEST(subghz_decoder_doitrand_test) {
+    mu_assert(
+        subghz_decoder_test(
+            EXT_PATH("unit_tests/subghz/doitrand_raw.sub"), SUBGHZ_PROTOCOL_DOITRAND_NAME),
+        "Test decoder " SUBGHZ_PROTOCOL_DOITRAND_NAME " error\r\n");
+}
+
+MU_TEST(subghz_decoder_phoenix_v2_test) {
+    mu_assert(
+        subghz_decoder_test(
+            EXT_PATH("unit_tests/subghz/phoenix_v2_raw.sub"), SUBGHZ_PROTOCOL_PHOENIX_V2_NAME),
+        "Test decoder " SUBGHZ_PROTOCOL_PHOENIX_V2_NAME " error\r\n");
+}
+
 //test encoders
 //test encoders
 MU_TEST(subghz_encoder_princeton_test) {
 MU_TEST(subghz_encoder_princeton_test) {
     mu_assert(
     mu_assert(
@@ -450,6 +477,30 @@ MU_TEST(subghz_encoder_power_smart_test) {
         "Test encoder " SUBGHZ_PROTOCOL_POWER_SMART_NAME " error\r\n");
         "Test encoder " SUBGHZ_PROTOCOL_POWER_SMART_NAME " error\r\n");
 }
 }
 
 
+MU_TEST(subghz_encoder_marantec_test) {
+    mu_assert(
+        subghz_encoder_test(EXT_PATH("unit_tests/subghz/marantec.sub")),
+        "Test encoder " SUBGHZ_PROTOCOL_MARANTEC_NAME " error\r\n");
+}
+
+MU_TEST(subghz_encoder_bett_test) {
+    mu_assert(
+        subghz_encoder_test(EXT_PATH("unit_tests/subghz/bett.sub")),
+        "Test encoder " SUBGHZ_PROTOCOL_BETT_NAME " error\r\n");
+}
+
+MU_TEST(subghz_encoder_doitrand_test) {
+    mu_assert(
+        subghz_encoder_test(EXT_PATH("unit_tests/subghz/doitrand.sub")),
+        "Test encoder " SUBGHZ_PROTOCOL_DOITRAND_NAME " error\r\n");
+}
+
+MU_TEST(subghz_encoder_phoenix_v2_test) {
+    mu_assert(
+        subghz_encoder_test(EXT_PATH("unit_tests/subghz/phoenix_v2.sub")),
+        "Test encoder " SUBGHZ_PROTOCOL_PHOENIX_V2_NAME " error\r\n");
+}
+
 MU_TEST(subghz_random_test) {
 MU_TEST(subghz_random_test) {
     mu_assert(subghz_decode_random_test(TEST_RANDOM_DIR_NAME), "Random test error\r\n");
     mu_assert(subghz_decode_random_test(TEST_RANDOM_DIR_NAME), "Random test error\r\n");
 }
 }
@@ -482,6 +533,10 @@ MU_TEST_SUITE(subghz) {
     MU_RUN_TEST(subghz_decoder_secplus_v2_test);
     MU_RUN_TEST(subghz_decoder_secplus_v2_test);
     MU_RUN_TEST(subghz_decoder_holtek_test);
     MU_RUN_TEST(subghz_decoder_holtek_test);
     MU_RUN_TEST(subghz_decoder_power_smart_test);
     MU_RUN_TEST(subghz_decoder_power_smart_test);
+    MU_RUN_TEST(subghz_decoder_marantec_test);
+    MU_RUN_TEST(subghz_decoder_bett_test);
+    MU_RUN_TEST(subghz_decoder_doitrand_test);
+    MU_RUN_TEST(subghz_decoder_phoenix_v2_test);
 
 
     MU_RUN_TEST(subghz_encoder_princeton_test);
     MU_RUN_TEST(subghz_encoder_princeton_test);
     MU_RUN_TEST(subghz_encoder_came_test);
     MU_RUN_TEST(subghz_encoder_came_test);
@@ -495,6 +550,10 @@ MU_TEST_SUITE(subghz) {
     MU_RUN_TEST(subghz_encoder_secplus_v1_test);
     MU_RUN_TEST(subghz_encoder_secplus_v1_test);
     MU_RUN_TEST(subghz_encoder_secplus_v2_test);
     MU_RUN_TEST(subghz_encoder_secplus_v2_test);
     MU_RUN_TEST(subghz_encoder_power_smart_test);
     MU_RUN_TEST(subghz_encoder_power_smart_test);
+    MU_RUN_TEST(subghz_encoder_marantec_test);
+    MU_RUN_TEST(subghz_encoder_bett_test);
+    MU_RUN_TEST(subghz_encoder_doitrand_test);
+    MU_RUN_TEST(subghz_encoder_phoenix_v2_test);
 
 
     MU_RUN_TEST(subghz_random_test);
     MU_RUN_TEST(subghz_random_test);
     subghz_test_deinit();
     subghz_test_deinit();

+ 7 - 0
assets/unit_tests/subghz/bett.sub

@@ -0,0 +1,7 @@
+Filetype: Flipper SubGhz Key File
+Version: 1
+Frequency: 433920000
+Preset: FuriHalSubGhzPresetOok650Async
+Protocol: BETT
+Bit: 18
+Key: 00 00 00 00 00 00 B8 B8

Разница между файлами не показана из-за своего большого размера
+ 5 - 0
assets/unit_tests/subghz/bett_raw.sub


+ 7 - 0
assets/unit_tests/subghz/doitrand.sub

@@ -0,0 +1,7 @@
+Filetype: Flipper SubGhz Key File
+Version: 1
+Frequency: 433920000
+Preset: FuriHalSubGhzPresetOok650Async
+Protocol: Doitrand
+Bit: 37
+Key: 00 00 00 1E 60 08 2F 5F

Разница между файлами не показана из-за своего большого размера
+ 5 - 0
assets/unit_tests/subghz/doitrand_raw.sub


+ 7 - 0
assets/unit_tests/subghz/marantec.sub

@@ -0,0 +1,7 @@
+Filetype: Flipper SubGhz Key File
+Version: 1
+Frequency: 433920000
+Preset: FuriHalSubGhzPresetOok650Async
+Protocol: Marantec
+Bit: 49
+Key: 00 01 30 07 10 DF 86 9F

Разница между файлами не показана из-за своего большого размера
+ 5 - 0
assets/unit_tests/subghz/marantec_raw.sub


+ 7 - 0
assets/unit_tests/subghz/phoenix_v2.sub

@@ -0,0 +1,7 @@
+Filetype: Flipper SubGhz Key File
+Version: 1
+Frequency: 433920000
+Preset: FuriHalSubGhzPresetOok650Async
+Protocol: Phoenix_V2
+Bit: 52
+Key: 00 0F 0F BD 7E 7F 10 AE

Разница между файлами не показана из-за своего большого размера
+ 5 - 0
assets/unit_tests/subghz/phoenix_v2_raw.sub


Разница между файлами не показана из-за своего большого размера
+ 0 - 0
assets/unit_tests/subghz/test_random_raw.sub


+ 357 - 0
lib/subghz/protocols/bett.c

@@ -0,0 +1,357 @@
+#include "bett.h"
+
+#include "../blocks/const.h"
+#include "../blocks/decoder.h"
+#include "../blocks/encoder.h"
+#include "../blocks/generic.h"
+#include "../blocks/math.h"
+
+// protocol BERNER / ELKA / TEDSEN / TELETASTER
+#define TAG "SubGhzProtocolBETT"
+
+#define DIP_P 0b11 //(+)
+#define DIP_O 0b10 //(0)
+#define DIP_N 0b00 //(-)
+
+#define DIP_PATTERN "%c%c%c%c%c%c%c%c%c"
+#define SHOW_DIP_P(dip, check_dip)                         \
+    ((((dip >> 0x8) >> 0x8) == check_dip) ? '*' : '_'),    \
+        ((((dip >> 0xE) & 0x3) == check_dip) ? '*' : '_'), \
+        ((((dip >> 0xC) & 0x3) == check_dip) ? '*' : '_'), \
+        ((((dip >> 0xA) & 0x3) == check_dip) ? '*' : '_'), \
+        ((((dip >> 0x8) & 0x3) == check_dip) ? '*' : '_'), \
+        ((((dip >> 0x6) & 0x3) == check_dip) ? '*' : '_'), \
+        ((((dip >> 0x4) & 0x3) == check_dip) ? '*' : '_'), \
+        ((((dip >> 0x2) & 0x3) == check_dip) ? '*' : '_'), \
+        ((((dip >> 0x0) & 0x3) == check_dip) ? '*' : '_')
+
+static const SubGhzBlockConst subghz_protocol_bett_const = {
+    .te_short = 340,
+    .te_long = 2000,
+    .te_delta = 150,
+    .min_count_bit_for_found = 18,
+};
+
+struct SubGhzProtocolDecoderBETT {
+    SubGhzProtocolDecoderBase base;
+
+    SubGhzBlockDecoder decoder;
+    SubGhzBlockGeneric generic;
+};
+
+struct SubGhzProtocolEncoderBETT {
+    SubGhzProtocolEncoderBase base;
+
+    SubGhzProtocolBlockEncoder encoder;
+    SubGhzBlockGeneric generic;
+};
+
+typedef enum {
+    BETTDecoderStepReset = 0,
+    BETTDecoderStepSaveDuration,
+    BETTDecoderStepCheckDuration,
+} BETTDecoderStep;
+
+const SubGhzProtocolDecoder subghz_protocol_bett_decoder = {
+    .alloc = subghz_protocol_decoder_bett_alloc,
+    .free = subghz_protocol_decoder_bett_free,
+
+    .feed = subghz_protocol_decoder_bett_feed,
+    .reset = subghz_protocol_decoder_bett_reset,
+
+    .get_hash_data = subghz_protocol_decoder_bett_get_hash_data,
+    .serialize = subghz_protocol_decoder_bett_serialize,
+    .deserialize = subghz_protocol_decoder_bett_deserialize,
+    .get_string = subghz_protocol_decoder_bett_get_string,
+};
+
+const SubGhzProtocolEncoder subghz_protocol_bett_encoder = {
+    .alloc = subghz_protocol_encoder_bett_alloc,
+    .free = subghz_protocol_encoder_bett_free,
+
+    .deserialize = subghz_protocol_encoder_bett_deserialize,
+    .stop = subghz_protocol_encoder_bett_stop,
+    .yield = subghz_protocol_encoder_bett_yield,
+};
+
+const SubGhzProtocol subghz_protocol_bett = {
+    .name = SUBGHZ_PROTOCOL_BETT_NAME,
+    .type = SubGhzProtocolTypeStatic,
+    .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable |
+            SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Send,
+
+    .decoder = &subghz_protocol_bett_decoder,
+    .encoder = &subghz_protocol_bett_encoder,
+};
+
+void* subghz_protocol_encoder_bett_alloc(SubGhzEnvironment* environment) {
+    UNUSED(environment);
+    SubGhzProtocolEncoderBETT* instance = malloc(sizeof(SubGhzProtocolEncoderBETT));
+
+    instance->base.protocol = &subghz_protocol_bett;
+    instance->generic.protocol_name = instance->base.protocol->name;
+
+    instance->encoder.repeat = 10;
+    instance->encoder.size_upload = 52; //max 24bit*2 + 2 (start, stop)
+    instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration));
+    instance->encoder.is_runing = false;
+    return instance;
+}
+
+void subghz_protocol_encoder_bett_free(void* context) {
+    furi_assert(context);
+    SubGhzProtocolEncoderBETT* instance = context;
+    free(instance->encoder.upload);
+    free(instance);
+}
+
+/**
+ * Generating an upload from data.
+ * @param instance Pointer to a SubGhzProtocolEncoderBETT instance
+ * @return true On success
+ */
+static bool subghz_protocol_encoder_bett_get_upload(SubGhzProtocolEncoderBETT* instance) {
+    furi_assert(instance);
+    size_t index = 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;
+    }
+
+    for(uint8_t i = instance->generic.data_count_bit; i > 1; i--) {
+        if(bit_read(instance->generic.data, i - 1)) {
+            //send bit 1
+            instance->encoder.upload[index++] =
+                level_duration_make(true, (uint32_t)subghz_protocol_bett_const.te_long);
+            instance->encoder.upload[index++] =
+                level_duration_make(false, (uint32_t)subghz_protocol_bett_const.te_short);
+        } else {
+            //send bit 0
+            instance->encoder.upload[index++] =
+                level_duration_make(true, (uint32_t)subghz_protocol_bett_const.te_short);
+            instance->encoder.upload[index++] =
+                level_duration_make(false, (uint32_t)subghz_protocol_bett_const.te_long);
+        }
+    }
+    if(bit_read(instance->generic.data, 0)) {
+        //send bit 1
+        instance->encoder.upload[index++] =
+            level_duration_make(true, (uint32_t)subghz_protocol_bett_const.te_long);
+        instance->encoder.upload[index++] = level_duration_make(
+            false,
+            (uint32_t)subghz_protocol_bett_const.te_short +
+                subghz_protocol_bett_const.te_long * 7);
+    } else {
+        //send bit 0
+        instance->encoder.upload[index++] =
+            level_duration_make(true, (uint32_t)subghz_protocol_bett_const.te_short);
+        instance->encoder.upload[index++] = level_duration_make(
+            false,
+            (uint32_t)subghz_protocol_bett_const.te_long + subghz_protocol_bett_const.te_long * 7);
+    }
+    return true;
+}
+
+bool subghz_protocol_encoder_bett_deserialize(void* context, FlipperFormat* flipper_format) {
+    furi_assert(context);
+    SubGhzProtocolEncoderBETT* instance = context;
+    bool res = false;
+    do {
+        if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) {
+            FURI_LOG_E(TAG, "Deserialize error");
+            break;
+        }
+        if(instance->generic.data_count_bit !=
+           subghz_protocol_bett_const.min_count_bit_for_found) {
+            FURI_LOG_E(TAG, "Wrong number of bits in key");
+            break;
+        }
+        //optional parameter parameter
+        flipper_format_read_uint32(
+            flipper_format, "Repeat", (uint32_t*)&instance->encoder.repeat, 1);
+
+        subghz_protocol_encoder_bett_get_upload(instance);
+        instance->encoder.is_runing = true;
+
+        res = true;
+    } while(false);
+
+    return res;
+}
+
+void subghz_protocol_encoder_bett_stop(void* context) {
+    SubGhzProtocolEncoderBETT* instance = context;
+    instance->encoder.is_runing = false;
+}
+
+LevelDuration subghz_protocol_encoder_bett_yield(void* context) {
+    SubGhzProtocolEncoderBETT* 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_bett_alloc(SubGhzEnvironment* environment) {
+    UNUSED(environment);
+    SubGhzProtocolDecoderBETT* instance = malloc(sizeof(SubGhzProtocolDecoderBETT));
+    instance->base.protocol = &subghz_protocol_bett;
+    instance->generic.protocol_name = instance->base.protocol->name;
+    return instance;
+}
+
+void subghz_protocol_decoder_bett_free(void* context) {
+    furi_assert(context);
+    SubGhzProtocolDecoderBETT* instance = context;
+    free(instance);
+}
+
+void subghz_protocol_decoder_bett_reset(void* context) {
+    furi_assert(context);
+    SubGhzProtocolDecoderBETT* instance = context;
+    instance->decoder.parser_step = BETTDecoderStepReset;
+}
+
+void subghz_protocol_decoder_bett_feed(void* context, bool level, uint32_t duration) {
+    furi_assert(context);
+    SubGhzProtocolDecoderBETT* instance = context;
+
+    switch(instance->decoder.parser_step) {
+    case BETTDecoderStepReset:
+        if((!level) && (DURATION_DIFF(duration, subghz_protocol_bett_const.te_short * 42) <
+                        subghz_protocol_bett_const.te_delta * 21)) {
+            //Found Preambula
+            instance->decoder.parser_step = BETTDecoderStepCheckDuration;
+        }
+        break;
+    case BETTDecoderStepSaveDuration:
+        if(!level) {
+            if(duration >= ((uint32_t)subghz_protocol_bett_const.te_short * 10 +
+                            subghz_protocol_bett_const.te_delta)) {
+                instance->decoder.parser_step = BETTDecoderStepSaveDuration;
+                if(instance->decoder.decode_count_bit ==
+                   subghz_protocol_bett_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);
+                } else {
+                    instance->decoder.parser_step = BETTDecoderStepReset;
+                }
+                instance->decoder.decode_data = 0;
+                instance->decoder.decode_count_bit = 0;
+                break;
+            } else {
+                if((DURATION_DIFF(duration, subghz_protocol_bett_const.te_short) <
+                    subghz_protocol_bett_const.te_delta) ||
+                   (DURATION_DIFF(duration, subghz_protocol_bett_const.te_long) <
+                    subghz_protocol_bett_const.te_delta * 3)) {
+                    instance->decoder.parser_step = BETTDecoderStepCheckDuration;
+                } else {
+                    instance->decoder.parser_step = BETTDecoderStepReset;
+                }
+            }
+        }
+        break;
+    case BETTDecoderStepCheckDuration:
+        if(level) {
+            if(DURATION_DIFF(duration, subghz_protocol_bett_const.te_long) <
+               subghz_protocol_bett_const.te_delta * 3) {
+                subghz_protocol_blocks_add_bit(&instance->decoder, 1);
+                instance->decoder.parser_step = BETTDecoderStepSaveDuration;
+            } else if(
+                DURATION_DIFF(duration, subghz_protocol_bett_const.te_short) <
+                subghz_protocol_bett_const.te_delta) {
+                subghz_protocol_blocks_add_bit(&instance->decoder, 0);
+                instance->decoder.parser_step = BETTDecoderStepSaveDuration;
+            } else {
+                instance->decoder.parser_step = BETTDecoderStepReset;
+            }
+        } else {
+            instance->decoder.parser_step = BETTDecoderStepReset;
+        }
+        break;
+    }
+}
+
+/** 
+ * Analysis of received data
+ * @param instance Pointer to a SubGhzBlockGeneric* instance
+ */
+static void subghz_protocol_bett_check_remote_controller(SubGhzBlockGeneric* instance) {
+    uint32_t code_found_reverse =
+        subghz_protocol_blocks_reverse_key(instance->data, instance->data_count_bit);
+
+    instance->serial = (code_found_reverse & 0xFF) << 12 |
+                       ((code_found_reverse >> 8) & 0xFF) << 4 |
+                       ((code_found_reverse >> 20) & 0x0F);
+    instance->btn = ((code_found_reverse >> 16) & 0x0F);
+}
+
+uint8_t subghz_protocol_decoder_bett_get_hash_data(void* context) {
+    furi_assert(context);
+    SubGhzProtocolDecoderBETT* instance = context;
+    return subghz_protocol_blocks_get_hash_data(
+        &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1);
+}
+
+bool subghz_protocol_decoder_bett_serialize(
+    void* context,
+    FlipperFormat* flipper_format,
+    SubGhzPresetDefinition* preset) {
+    furi_assert(context);
+    SubGhzProtocolDecoderBETT* instance = context;
+    return subghz_block_generic_serialize(&instance->generic, flipper_format, preset);
+}
+
+bool subghz_protocol_decoder_bett_deserialize(void* context, FlipperFormat* flipper_format) {
+    furi_assert(context);
+    SubGhzProtocolDecoderBETT* instance = context;
+    bool ret = false;
+    do {
+        if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) {
+            break;
+        }
+        if(instance->generic.data_count_bit !=
+           subghz_protocol_bett_const.min_count_bit_for_found) {
+            FURI_LOG_E(TAG, "Wrong number of bits in key");
+            break;
+        }
+        ret = true;
+    } while(false);
+    return ret;
+}
+
+void subghz_protocol_decoder_bett_get_string(void* context, string_t output) {
+    furi_assert(context);
+    SubGhzProtocolDecoderBETT* instance = context;
+    subghz_protocol_bett_check_remote_controller(&instance->generic);
+    uint32_t data = (uint32_t)(instance->generic.data & 0xFFFFFF);
+    string_cat_printf(
+        output,
+        "%s %dbit\r\n"
+        "Key:%05lX\r\n"
+        "  +:   " DIP_PATTERN "\r\n"
+        "  o:   " DIP_PATTERN "\r\n"
+        "  -:   " DIP_PATTERN "\r\n",
+        instance->generic.protocol_name,
+        instance->generic.data_count_bit,
+        (uint32_t)(instance->generic.data & 0xFFFFFF),
+        SHOW_DIP_P(data, DIP_P),
+        SHOW_DIP_P(data, DIP_O),
+        SHOW_DIP_P(data, DIP_N));
+}

+ 107 - 0
lib/subghz/protocols/bett.h

@@ -0,0 +1,107 @@
+#pragma once
+
+#include "base.h"
+
+#define SUBGHZ_PROTOCOL_BETT_NAME "BETT"
+
+typedef struct SubGhzProtocolDecoderBETT SubGhzProtocolDecoderBETT;
+typedef struct SubGhzProtocolEncoderBETT SubGhzProtocolEncoderBETT;
+
+extern const SubGhzProtocolDecoder subghz_protocol_bett_decoder;
+extern const SubGhzProtocolEncoder subghz_protocol_bett_encoder;
+extern const SubGhzProtocol subghz_protocol_bett;
+
+/**
+ * Allocate SubGhzProtocolEncoderBETT.
+ * @param environment Pointer to a SubGhzEnvironment instance
+ * @return SubGhzProtocolEncoderBETT* pointer to a SubGhzProtocolEncoderBETT instance
+ */
+void* subghz_protocol_encoder_bett_alloc(SubGhzEnvironment* environment);
+
+/**
+ * Free SubGhzProtocolEncoderBETT.
+ * @param context Pointer to a SubGhzProtocolEncoderBETT instance
+ */
+void subghz_protocol_encoder_bett_free(void* context);
+
+/**
+ * Deserialize and generating an upload to send.
+ * @param context Pointer to a SubGhzProtocolEncoderBETT instance
+ * @param flipper_format Pointer to a FlipperFormat instance
+ * @return true On success
+ */
+bool subghz_protocol_encoder_bett_deserialize(void* context, FlipperFormat* flipper_format);
+
+/**
+ * Forced transmission stop.
+ * @param context Pointer to a SubGhzProtocolEncoderBETT instance
+ */
+void subghz_protocol_encoder_bett_stop(void* context);
+
+/**
+ * Getting the level and duration of the upload to be loaded into DMA.
+ * @param context Pointer to a SubGhzProtocolEncoderBETT instance
+ * @return LevelDuration 
+ */
+LevelDuration subghz_protocol_encoder_bett_yield(void* context);
+
+/**
+ * Allocate SubGhzProtocolDecoderBETT.
+ * @param environment Pointer to a SubGhzEnvironment instance
+ * @return SubGhzProtocolDecoderBETT* pointer to a SubGhzProtocolDecoderBETT instance
+ */
+void* subghz_protocol_decoder_bett_alloc(SubGhzEnvironment* environment);
+
+/**
+ * Free SubGhzProtocolDecoderBETT.
+ * @param context Pointer to a SubGhzProtocolDecoderBETT instance
+ */
+void subghz_protocol_decoder_bett_free(void* context);
+
+/**
+ * Reset decoder SubGhzProtocolDecoderBETT.
+ * @param context Pointer to a SubGhzProtocolDecoderBETT instance
+ */
+void subghz_protocol_decoder_bett_reset(void* context);
+
+/**
+ * Parse a raw sequence of levels and durations received from the air.
+ * @param context Pointer to a SubGhzProtocolDecoderBETT instance
+ * @param level Signal level true-high false-low
+ * @param duration Duration of this level in, us
+ */
+void subghz_protocol_decoder_bett_feed(void* context, bool level, uint32_t duration);
+
+/**
+ * Getting the hash sum of the last randomly received parcel.
+ * @param context Pointer to a SubGhzProtocolDecoderBETT instance
+ * @return hash Hash sum
+ */
+uint8_t subghz_protocol_decoder_bett_get_hash_data(void* context);
+
+/**
+ * Serialize data SubGhzProtocolDecoderBETT.
+ * @param context Pointer to a SubGhzProtocolDecoderBETT instance
+ * @param flipper_format Pointer to a FlipperFormat instance
+ * @param preset The modulation on which the signal was received, SubGhzPresetDefinition
+ * @return true On success
+ */
+bool subghz_protocol_decoder_bett_serialize(
+    void* context,
+    FlipperFormat* flipper_format,
+    SubGhzPresetDefinition* preset);
+
+/**
+ * Deserialize data SubGhzProtocolDecoderBETT.
+ * @param context Pointer to a SubGhzProtocolDecoderBETT instance
+ * @param flipper_format Pointer to a FlipperFormat instance
+ * @return true On success
+ */
+bool subghz_protocol_decoder_bett_deserialize(void* context, FlipperFormat* flipper_format);
+
+/**
+ * Getting a textual representation of the received data.
+ * @param context Pointer to a SubGhzProtocolDecoderBETT instance
+ * @param output Resulting text
+ */
+void subghz_protocol_decoder_bett_get_string(void* context, string_t output);

+ 356 - 0
lib/subghz/protocols/doitrand.c

@@ -0,0 +1,356 @@
+#include "doitrand.h"
+
+#include "../blocks/const.h"
+#include "../blocks/decoder.h"
+#include "../blocks/encoder.h"
+#include "../blocks/generic.h"
+#include "../blocks/math.h"
+
+#define TAG "SubGhzProtocolDoitrand"
+
+#define DIP_PATTERN "%c%c%c%c%c%c%c%c%c%c"
+#define CNT_TO_DIP(dip)                                                                     \
+    (dip & 0x0001 ? '1' : '0'), (dip & 0x0100 ? '1' : '0'), (dip & 0x0080 ? '1' : '0'),     \
+        (dip & 0x0040 ? '1' : '0'), (dip & 0x0020 ? '1' : '0'), (dip & 0x1000 ? '1' : '0'), \
+        (dip & 0x0800 ? '1' : '0'), (dip & 0x0400 ? '1' : '0'), (dip & 0x0200 ? '1' : '0'), \
+        (dip & 0x0002 ? '1' : '0')
+
+static const SubGhzBlockConst subghz_protocol_doitrand_const = {
+    .te_short = 400,
+    .te_long = 1100,
+    .te_delta = 150,
+    .min_count_bit_for_found = 37,
+};
+
+struct SubGhzProtocolDecoderDoitrand {
+    SubGhzProtocolDecoderBase base;
+
+    SubGhzBlockDecoder decoder;
+    SubGhzBlockGeneric generic;
+};
+
+struct SubGhzProtocolEncoderDoitrand {
+    SubGhzProtocolEncoderBase base;
+
+    SubGhzProtocolBlockEncoder encoder;
+    SubGhzBlockGeneric generic;
+};
+
+typedef enum {
+    DoitrandDecoderStepReset = 0,
+    DoitrandDecoderStepFoundStartBit,
+    DoitrandDecoderStepSaveDuration,
+    DoitrandDecoderStepCheckDuration,
+} DoitrandDecoderStep;
+
+const SubGhzProtocolDecoder subghz_protocol_doitrand_decoder = {
+    .alloc = subghz_protocol_decoder_doitrand_alloc,
+    .free = subghz_protocol_decoder_doitrand_free,
+
+    .feed = subghz_protocol_decoder_doitrand_feed,
+    .reset = subghz_protocol_decoder_doitrand_reset,
+
+    .get_hash_data = subghz_protocol_decoder_doitrand_get_hash_data,
+    .serialize = subghz_protocol_decoder_doitrand_serialize,
+    .deserialize = subghz_protocol_decoder_doitrand_deserialize,
+    .get_string = subghz_protocol_decoder_doitrand_get_string,
+};
+
+const SubGhzProtocolEncoder subghz_protocol_doitrand_encoder = {
+    .alloc = subghz_protocol_encoder_doitrand_alloc,
+    .free = subghz_protocol_encoder_doitrand_free,
+
+    .deserialize = subghz_protocol_encoder_doitrand_deserialize,
+    .stop = subghz_protocol_encoder_doitrand_stop,
+    .yield = subghz_protocol_encoder_doitrand_yield,
+};
+
+const SubGhzProtocol subghz_protocol_doitrand = {
+    .name = SUBGHZ_PROTOCOL_DOITRAND_NAME,
+    .type = SubGhzProtocolTypeStatic,
+    .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable |
+            SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Send,
+
+    .decoder = &subghz_protocol_doitrand_decoder,
+    .encoder = &subghz_protocol_doitrand_encoder,
+};
+
+void* subghz_protocol_encoder_doitrand_alloc(SubGhzEnvironment* environment) {
+    UNUSED(environment);
+    SubGhzProtocolEncoderDoitrand* instance = malloc(sizeof(SubGhzProtocolEncoderDoitrand));
+
+    instance->base.protocol = &subghz_protocol_doitrand;
+    instance->generic.protocol_name = instance->base.protocol->name;
+
+    instance->encoder.repeat = 10;
+    instance->encoder.size_upload = 128;
+    instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration));
+    instance->encoder.is_runing = false;
+    return instance;
+}
+
+void subghz_protocol_encoder_doitrand_free(void* context) {
+    furi_assert(context);
+    SubGhzProtocolEncoderDoitrand* instance = context;
+    free(instance->encoder.upload);
+    free(instance);
+}
+
+/**
+ * Generating an upload from data.
+ * @param instance Pointer to a SubGhzProtocolEncoderDoitrand instance
+ * @return true On success
+ */
+static bool subghz_protocol_encoder_doitrand_get_upload(SubGhzProtocolEncoderDoitrand* instance) {
+    furi_assert(instance);
+    size_t index = 0;
+    size_t size_upload = (instance->generic.data_count_bit * 2) + 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;
+    }
+    //Send header
+    instance->encoder.upload[index++] =
+        level_duration_make(false, (uint32_t)subghz_protocol_doitrand_const.te_short * 62);
+    //Send start bit
+    instance->encoder.upload[index++] =
+        level_duration_make(true, (uint32_t)subghz_protocol_doitrand_const.te_short * 2 - 100);
+    //Send key data
+    for(uint8_t i = instance->generic.data_count_bit; i > 0; i--) {
+        if(bit_read(instance->generic.data, i - 1)) {
+            //send bit 1
+            instance->encoder.upload[index++] =
+                level_duration_make(false, (uint32_t)subghz_protocol_doitrand_const.te_long);
+            instance->encoder.upload[index++] =
+                level_duration_make(true, (uint32_t)subghz_protocol_doitrand_const.te_short);
+        } else {
+            //send bit 0
+            instance->encoder.upload[index++] =
+                level_duration_make(false, (uint32_t)subghz_protocol_doitrand_const.te_short);
+            instance->encoder.upload[index++] =
+                level_duration_make(true, (uint32_t)subghz_protocol_doitrand_const.te_long);
+        }
+    }
+    return true;
+}
+
+bool subghz_protocol_encoder_doitrand_deserialize(void* context, FlipperFormat* flipper_format) {
+    furi_assert(context);
+    SubGhzProtocolEncoderDoitrand* instance = context;
+    bool res = false;
+    do {
+        if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) {
+            FURI_LOG_E(TAG, "Deserialize error");
+            break;
+        }
+        if(instance->generic.data_count_bit !=
+           subghz_protocol_doitrand_const.min_count_bit_for_found) {
+            FURI_LOG_E(TAG, "Wrong number of bits in key");
+            break;
+        }
+        //optional parameter parameter
+        flipper_format_read_uint32(
+            flipper_format, "Repeat", (uint32_t*)&instance->encoder.repeat, 1);
+
+        subghz_protocol_encoder_doitrand_get_upload(instance);
+        instance->encoder.is_runing = true;
+
+        res = true;
+    } while(false);
+
+    return res;
+}
+
+void subghz_protocol_encoder_doitrand_stop(void* context) {
+    SubGhzProtocolEncoderDoitrand* instance = context;
+    instance->encoder.is_runing = false;
+}
+
+LevelDuration subghz_protocol_encoder_doitrand_yield(void* context) {
+    SubGhzProtocolEncoderDoitrand* 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_doitrand_alloc(SubGhzEnvironment* environment) {
+    UNUSED(environment);
+    SubGhzProtocolDecoderDoitrand* instance = malloc(sizeof(SubGhzProtocolDecoderDoitrand));
+    instance->base.protocol = &subghz_protocol_doitrand;
+    instance->generic.protocol_name = instance->base.protocol->name;
+    return instance;
+}
+
+void subghz_protocol_decoder_doitrand_free(void* context) {
+    furi_assert(context);
+    SubGhzProtocolDecoderDoitrand* instance = context;
+    free(instance);
+}
+
+void subghz_protocol_decoder_doitrand_reset(void* context) {
+    furi_assert(context);
+    SubGhzProtocolDecoderDoitrand* instance = context;
+    instance->decoder.parser_step = DoitrandDecoderStepReset;
+}
+
+void subghz_protocol_decoder_doitrand_feed(void* context, bool level, uint32_t duration) {
+    furi_assert(context);
+    SubGhzProtocolDecoderDoitrand* instance = context;
+
+    switch(instance->decoder.parser_step) {
+    case DoitrandDecoderStepReset:
+        if((!level) && (DURATION_DIFF(duration, subghz_protocol_doitrand_const.te_short * 62) <
+                        subghz_protocol_doitrand_const.te_delta * 30)) {
+            //Found Preambula
+            instance->decoder.parser_step = DoitrandDecoderStepFoundStartBit;
+        }
+        break;
+    case DoitrandDecoderStepFoundStartBit:
+        if(level && ((DURATION_DIFF(duration, (subghz_protocol_doitrand_const.te_short * 2)) <
+                      subghz_protocol_doitrand_const.te_delta * 3))) {
+            //Found start bit
+            instance->decoder.parser_step = DoitrandDecoderStepSaveDuration;
+            instance->decoder.decode_data = 0;
+            instance->decoder.decode_count_bit = 0;
+        } else {
+            instance->decoder.parser_step = DoitrandDecoderStepReset;
+        }
+        break;
+    case DoitrandDecoderStepSaveDuration:
+        if(!level) {
+            if(duration >= ((uint32_t)subghz_protocol_doitrand_const.te_short * 10 +
+                            subghz_protocol_doitrand_const.te_delta)) {
+                instance->decoder.parser_step = DoitrandDecoderStepFoundStartBit;
+                if(instance->decoder.decode_count_bit ==
+                   subghz_protocol_doitrand_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);
+                }
+                instance->decoder.decode_data = 0;
+                instance->decoder.decode_count_bit = 0;
+                break;
+            } else {
+                instance->decoder.te_last = duration;
+                instance->decoder.parser_step = DoitrandDecoderStepCheckDuration;
+            }
+        }
+        break;
+    case DoitrandDecoderStepCheckDuration:
+        if(level) {
+            if((DURATION_DIFF(instance->decoder.te_last, subghz_protocol_doitrand_const.te_short) <
+                subghz_protocol_doitrand_const.te_delta) &&
+               (DURATION_DIFF(duration, subghz_protocol_doitrand_const.te_long) <
+                subghz_protocol_doitrand_const.te_delta * 3)) {
+                subghz_protocol_blocks_add_bit(&instance->decoder, 0);
+                instance->decoder.parser_step = DoitrandDecoderStepSaveDuration;
+            } else if(
+                (DURATION_DIFF(instance->decoder.te_last, subghz_protocol_doitrand_const.te_long) <
+                 subghz_protocol_doitrand_const.te_delta * 3) &&
+                (DURATION_DIFF(duration, subghz_protocol_doitrand_const.te_short) <
+                 subghz_protocol_doitrand_const.te_delta)) {
+                subghz_protocol_blocks_add_bit(&instance->decoder, 1);
+                instance->decoder.parser_step = DoitrandDecoderStepSaveDuration;
+            } else {
+                instance->decoder.parser_step = DoitrandDecoderStepReset;
+            }
+        } else {
+            instance->decoder.parser_step = DoitrandDecoderStepReset;
+        }
+        break;
+    }
+}
+
+/** 
+ * Analysis of received data
+ * @param instance Pointer to a SubGhzBlockGeneric* instance
+ */
+static void subghz_protocol_doitrand_check_remote_controller(SubGhzBlockGeneric* instance) {
+    /*
+*               67892345   0      k    1                    
+* 0000082F5F => 00000000000000000 10 000010111101011111 
+* 0002082F5F => 00000000000100000 10 000010111101011111
+* 0200082F5F => 00010000000000000 10 000010111101011111
+* 0400082F5F => 00100000000000000 10 000010111101011111
+* 0800082F5F => 01000000000000000 10 000010111101011111
+* 1000082F5F => 10000000000000000 10 000010111101011111
+* 0020082F5F => 00000001000000000 10 000010111101011111
+* 0040082F5F => 00000010000000000 10 000010111101011111
+* 0080082F5F => 00000100000000000 10 000010111101011111
+* 0100082F5F => 00001000000000000 10 000010111101011111
+* 000008AF5F => 00000000000000000 10 001010111101011111
+* 1FE208AF5F => 11111111000100000 10 001010111101011111
+*
+* 0...9 - DIP
+* k- KEY
+*/
+    instance->cnt = (instance->data >> 24) | ((instance->data >> 15) & 0x1);
+    instance->btn = ((instance->data >> 18) & 0x3);
+}
+
+uint8_t subghz_protocol_decoder_doitrand_get_hash_data(void* context) {
+    furi_assert(context);
+    SubGhzProtocolDecoderDoitrand* instance = context;
+    return subghz_protocol_blocks_get_hash_data(
+        &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1);
+}
+
+bool subghz_protocol_decoder_doitrand_serialize(
+    void* context,
+    FlipperFormat* flipper_format,
+    SubGhzPresetDefinition* preset) {
+    furi_assert(context);
+    SubGhzProtocolDecoderDoitrand* instance = context;
+    return subghz_block_generic_serialize(&instance->generic, flipper_format, preset);
+}
+
+bool subghz_protocol_decoder_doitrand_deserialize(void* context, FlipperFormat* flipper_format) {
+    furi_assert(context);
+    SubGhzProtocolDecoderDoitrand* instance = context;
+    bool ret = false;
+    do {
+        if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) {
+            break;
+        }
+        if(instance->generic.data_count_bit !=
+           subghz_protocol_doitrand_const.min_count_bit_for_found) {
+            FURI_LOG_E(TAG, "Wrong number of bits in key");
+            break;
+        }
+        ret = true;
+    } while(false);
+    return ret;
+}
+
+void subghz_protocol_decoder_doitrand_get_string(void* context, string_t output) {
+    furi_assert(context);
+    SubGhzProtocolDecoderDoitrand* instance = context;
+    subghz_protocol_doitrand_check_remote_controller(&instance->generic);
+    string_cat_printf(
+        output,
+        "%s %dbit\r\n"
+        "Key:%02lX%08lX\r\n"
+        "Btn:%lX\r\n"
+        "DIP:" DIP_PATTERN "\r\n",
+        instance->generic.protocol_name,
+        instance->generic.data_count_bit,
+        (uint32_t)(instance->generic.data >> 32) & 0xFFFFFFFF,
+        (uint32_t)(instance->generic.data & 0xFFFFFFFF),
+        instance->generic.btn,
+        CNT_TO_DIP(instance->generic.cnt));
+}

+ 107 - 0
lib/subghz/protocols/doitrand.h

@@ -0,0 +1,107 @@
+#pragma once
+
+#include "base.h"
+
+#define SUBGHZ_PROTOCOL_DOITRAND_NAME "Doitrand"
+
+typedef struct SubGhzProtocolDecoderDoitrand SubGhzProtocolDecoderDoitrand;
+typedef struct SubGhzProtocolEncoderDoitrand SubGhzProtocolEncoderDoitrand;
+
+extern const SubGhzProtocolDecoder subghz_protocol_doitrand_decoder;
+extern const SubGhzProtocolEncoder subghz_protocol_doitrand_encoder;
+extern const SubGhzProtocol subghz_protocol_doitrand;
+
+/**
+ * Allocate SubGhzProtocolEncoderDoitrand.
+ * @param environment Pointer to a SubGhzEnvironment instance
+ * @return SubGhzProtocolEncoderDoitrand* pointer to a SubGhzProtocolEncoderDoitrand instance
+ */
+void* subghz_protocol_encoder_doitrand_alloc(SubGhzEnvironment* environment);
+
+/**
+ * Free SubGhzProtocolEncoderDoitrand.
+ * @param context Pointer to a SubGhzProtocolEncoderDoitrand instance
+ */
+void subghz_protocol_encoder_doitrand_free(void* context);
+
+/**
+ * Deserialize and generating an upload to send.
+ * @param context Pointer to a SubGhzProtocolEncoderDoitrand instance
+ * @param flipper_format Pointer to a FlipperFormat instance
+ * @return true On success
+ */
+bool subghz_protocol_encoder_doitrand_deserialize(void* context, FlipperFormat* flipper_format);
+
+/**
+ * Forced transmission stop.
+ * @param context Pointer to a SubGhzProtocolEncoderDoitrand instance
+ */
+void subghz_protocol_encoder_doitrand_stop(void* context);
+
+/**
+ * Getting the level and duration of the upload to be loaded into DMA.
+ * @param context Pointer to a SubGhzProtocolEncoderDoitrand instance
+ * @return LevelDuration 
+ */
+LevelDuration subghz_protocol_encoder_doitrand_yield(void* context);
+
+/**
+ * Allocate SubGhzProtocolDecoderDoitrand.
+ * @param environment Pointer to a SubGhzEnvironment instance
+ * @return SubGhzProtocolDecoderDoitrand* pointer to a SubGhzProtocolDecoderDoitrand instance
+ */
+void* subghz_protocol_decoder_doitrand_alloc(SubGhzEnvironment* environment);
+
+/**
+ * Free SubGhzProtocolDecoderDoitrand.
+ * @param context Pointer to a SubGhzProtocolDecoderDoitrand instance
+ */
+void subghz_protocol_decoder_doitrand_free(void* context);
+
+/**
+ * Reset decoder SubGhzProtocolDecoderDoitrand.
+ * @param context Pointer to a SubGhzProtocolDecoderDoitrand instance
+ */
+void subghz_protocol_decoder_doitrand_reset(void* context);
+
+/**
+ * Parse a raw sequence of levels and durations received from the air.
+ * @param context Pointer to a SubGhzProtocolDecoderDoitrand instance
+ * @param level Signal level true-high false-low
+ * @param duration Duration of this level in, us
+ */
+void subghz_protocol_decoder_doitrand_feed(void* context, bool level, uint32_t duration);
+
+/**
+ * Getting the hash sum of the last randomly received parcel.
+ * @param context Pointer to a SubGhzProtocolDecoderDoitrand instance
+ * @return hash Hash sum
+ */
+uint8_t subghz_protocol_decoder_doitrand_get_hash_data(void* context);
+
+/**
+ * Serialize data SubGhzProtocolDecoderDoitrand.
+ * @param context Pointer to a SubGhzProtocolDecoderDoitrand instance
+ * @param flipper_format Pointer to a FlipperFormat instance
+ * @param preset The modulation on which the signal was received, SubGhzPresetDefinition
+ * @return true On success
+ */
+bool subghz_protocol_decoder_doitrand_serialize(
+    void* context,
+    FlipperFormat* flipper_format,
+    SubGhzPresetDefinition* preset);
+
+/**
+ * Deserialize data SubGhzProtocolDecoderDoitrand.
+ * @param context Pointer to a SubGhzProtocolDecoderDoitrand instance
+ * @param flipper_format Pointer to a FlipperFormat instance
+ * @return true On success
+ */
+bool subghz_protocol_decoder_doitrand_deserialize(void* context, FlipperFormat* flipper_format);
+
+/**
+ * Getting a textual representation of the received data.
+ * @param context Pointer to a SubGhzProtocolDecoderDoitrand instance
+ * @param output Resulting text
+ */
+void subghz_protocol_decoder_doitrand_get_string(void* context, string_t output);

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

@@ -359,7 +359,7 @@ void subghz_protocol_decoder_holtek_get_string(void* context, string_t output) {
         output,
         output,
         "%s %dbit\r\n"
         "%s %dbit\r\n"
         "Key:0x%lX%08lX\r\n"
         "Key:0x%lX%08lX\r\n"
-        "Sn:0x%05lX BTN:%X ",
+        "Sn:0x%05lX Btn:%X ",
         instance->generic.protocol_name,
         instance->generic.protocol_name,
         instance->generic.data_count_bit,
         instance->generic.data_count_bit,
         (uint32_t)((instance->generic.data >> 32) & 0xFFFFFFFF),
         (uint32_t)((instance->generic.data >> 32) & 0xFFFFFFFF),

+ 393 - 0
lib/subghz/protocols/marantec.c

@@ -0,0 +1,393 @@
+#include "marantec.h"
+#include <lib/toolbox/manchester_decoder.h>
+#include <lib/toolbox/manchester_encoder.h>
+#include "../blocks/const.h"
+#include "../blocks/decoder.h"
+#include "../blocks/encoder.h"
+#include "../blocks/generic.h"
+#include "../blocks/math.h"
+
+#define TAG "SubGhzProtocolMarantec"
+
+static const SubGhzBlockConst subghz_protocol_marantec_const = {
+    .te_short = 1000,
+    .te_long = 2000,
+    .te_delta = 200,
+    .min_count_bit_for_found = 49,
+};
+
+struct SubGhzProtocolDecoderMarantec {
+    SubGhzProtocolDecoderBase base;
+
+    SubGhzBlockDecoder decoder;
+    SubGhzBlockGeneric generic;
+    ManchesterState manchester_saved_state;
+    uint16_t header_count;
+};
+
+struct SubGhzProtocolEncoderMarantec {
+    SubGhzProtocolEncoderBase base;
+
+    SubGhzProtocolBlockEncoder encoder;
+    SubGhzBlockGeneric generic;
+};
+
+typedef enum {
+    MarantecDecoderStepReset = 0,
+    MarantecDecoderFoundHeader,
+    MarantecDecoderStepDecoderData,
+} MarantecDecoderStep;
+
+const SubGhzProtocolDecoder subghz_protocol_marantec_decoder = {
+    .alloc = subghz_protocol_decoder_marantec_alloc,
+    .free = subghz_protocol_decoder_marantec_free,
+
+    .feed = subghz_protocol_decoder_marantec_feed,
+    .reset = subghz_protocol_decoder_marantec_reset,
+
+    .get_hash_data = subghz_protocol_decoder_marantec_get_hash_data,
+    .serialize = subghz_protocol_decoder_marantec_serialize,
+    .deserialize = subghz_protocol_decoder_marantec_deserialize,
+    .get_string = subghz_protocol_decoder_marantec_get_string,
+};
+
+const SubGhzProtocolEncoder subghz_protocol_marantec_encoder = {
+    .alloc = subghz_protocol_encoder_marantec_alloc,
+    .free = subghz_protocol_encoder_marantec_free,
+
+    .deserialize = subghz_protocol_encoder_marantec_deserialize,
+    .stop = subghz_protocol_encoder_marantec_stop,
+    .yield = subghz_protocol_encoder_marantec_yield,
+};
+
+const SubGhzProtocol subghz_protocol_marantec = {
+    .name = SUBGHZ_PROTOCOL_MARANTEC_NAME,
+    .type = SubGhzProtocolTypeStatic,
+    .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable |
+            SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Send,
+
+    .decoder = &subghz_protocol_marantec_decoder,
+    .encoder = &subghz_protocol_marantec_encoder,
+};
+
+void* subghz_protocol_encoder_marantec_alloc(SubGhzEnvironment* environment) {
+    UNUSED(environment);
+    SubGhzProtocolEncoderMarantec* instance = malloc(sizeof(SubGhzProtocolEncoderMarantec));
+
+    instance->base.protocol = &subghz_protocol_marantec;
+    instance->generic.protocol_name = instance->base.protocol->name;
+
+    instance->encoder.repeat = 10;
+    instance->encoder.size_upload = 256;
+    instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration));
+    instance->encoder.is_runing = false;
+    return instance;
+}
+
+void subghz_protocol_encoder_marantec_free(void* context) {
+    furi_assert(context);
+    SubGhzProtocolEncoderMarantec* instance = context;
+    free(instance->encoder.upload);
+    free(instance);
+}
+
+static LevelDuration
+    subghz_protocol_encoder_marantec_add_duration_to_upload(ManchesterEncoderResult result) {
+    LevelDuration data = {.duration = 0, .level = 0};
+    switch(result) {
+    case ManchesterEncoderResultShortLow:
+        data.duration = subghz_protocol_marantec_const.te_short;
+        data.level = false;
+        break;
+    case ManchesterEncoderResultLongLow:
+        data.duration = subghz_protocol_marantec_const.te_long;
+        data.level = false;
+        break;
+    case ManchesterEncoderResultLongHigh:
+        data.duration = subghz_protocol_marantec_const.te_long;
+        data.level = true;
+        break;
+    case ManchesterEncoderResultShortHigh:
+        data.duration = subghz_protocol_marantec_const.te_short;
+        data.level = true;
+        break;
+
+    default:
+        furi_crash("SubGhz: ManchesterEncoderResult is incorrect.");
+        break;
+    }
+    return level_duration_make(data.level, data.duration);
+}
+
+/**
+ * Generating an upload from data.
+ * @param instance Pointer to a SubGhzProtocolEncoderMarantec instance
+ */
+static void subghz_protocol_encoder_marantec_get_upload(SubGhzProtocolEncoderMarantec* instance) {
+    furi_assert(instance);
+    size_t index = 0;
+
+    ManchesterEncoderState enc_state;
+    manchester_encoder_reset(&enc_state);
+    ManchesterEncoderResult result;
+
+    if(!manchester_encoder_advance(
+           &enc_state,
+           bit_read(instance->generic.data, instance->generic.data_count_bit - 1),
+           &result)) {
+        instance->encoder.upload[index++] =
+            subghz_protocol_encoder_marantec_add_duration_to_upload(result);
+        manchester_encoder_advance(
+            &enc_state,
+            bit_read(instance->generic.data, instance->generic.data_count_bit - 1),
+            &result);
+    }
+    instance->encoder.upload[index++] =
+        level_duration_make(false, (uint32_t)subghz_protocol_marantec_const.te_long * 5);
+
+    for(uint8_t i = instance->generic.data_count_bit - 1; i > 0; i--) {
+        if(!manchester_encoder_advance(
+               &enc_state, bit_read(instance->generic.data, i - 1), &result)) {
+            instance->encoder.upload[index++] =
+                subghz_protocol_encoder_marantec_add_duration_to_upload(result);
+            manchester_encoder_advance(
+                &enc_state, bit_read(instance->generic.data, i - 1), &result);
+        }
+        instance->encoder.upload[index++] =
+            subghz_protocol_encoder_marantec_add_duration_to_upload(result);
+    }
+    instance->encoder.upload[index] = subghz_protocol_encoder_marantec_add_duration_to_upload(
+        manchester_encoder_finish(&enc_state));
+    if(level_duration_get_level(instance->encoder.upload[index])) {
+        index++;
+    }
+    instance->encoder.size_upload = index;
+}
+
+uint8_t subghz_protocol_marantec_crc8(uint8_t* data, size_t len) {
+    uint8_t crc = 0x08;
+    size_t i, j;
+    for(i = 0; i < len; i++) {
+        crc ^= data[i];
+        for(j = 0; j < 8; j++) {
+            if((crc & 0x80) != 0)
+                crc = (uint8_t)((crc << 1) ^ 0x1D);
+            else
+                crc <<= 1;
+        }
+    }
+    return crc;
+}
+
+/** 
+ * Analysis of received data
+ * @param instance Pointer to a SubGhzBlockGeneric* instance
+ */
+static void subghz_protocol_marantec_remote_controller(SubGhzBlockGeneric* instance) {
+    instance->btn = (instance->data >> 16) & 0xF;
+    instance->serial = ((instance->data >> 12) & 0xFFFFFF00) | ((instance->data >> 8) & 0xFF);
+}
+
+bool subghz_protocol_encoder_marantec_deserialize(void* context, FlipperFormat* flipper_format) {
+    furi_assert(context);
+    SubGhzProtocolEncoderMarantec* instance = context;
+    bool res = false;
+    do {
+        if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) {
+            FURI_LOG_E(TAG, "Deserialize error");
+            break;
+        }
+        if(instance->generic.data_count_bit !=
+           subghz_protocol_marantec_const.min_count_bit_for_found) {
+            FURI_LOG_E(TAG, "Wrong number of bits in key");
+            break;
+        }
+        //optional parameter parameter
+        flipper_format_read_uint32(
+            flipper_format, "Repeat", (uint32_t*)&instance->encoder.repeat, 1);
+
+        subghz_protocol_marantec_remote_controller(&instance->generic);
+        subghz_protocol_encoder_marantec_get_upload(instance);
+        instance->encoder.is_runing = true;
+
+        res = true;
+    } while(false);
+
+    return res;
+}
+
+void subghz_protocol_encoder_marantec_stop(void* context) {
+    SubGhzProtocolEncoderMarantec* instance = context;
+    instance->encoder.is_runing = false;
+}
+
+LevelDuration subghz_protocol_encoder_marantec_yield(void* context) {
+    SubGhzProtocolEncoderMarantec* 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_marantec_alloc(SubGhzEnvironment* environment) {
+    UNUSED(environment);
+    SubGhzProtocolDecoderMarantec* instance = malloc(sizeof(SubGhzProtocolDecoderMarantec));
+    instance->base.protocol = &subghz_protocol_marantec;
+    instance->generic.protocol_name = instance->base.protocol->name;
+    return instance;
+}
+
+void subghz_protocol_decoder_marantec_free(void* context) {
+    furi_assert(context);
+    SubGhzProtocolDecoderMarantec* instance = context;
+    free(instance);
+}
+
+void subghz_protocol_decoder_marantec_reset(void* context) {
+    furi_assert(context);
+    SubGhzProtocolDecoderMarantec* instance = context;
+    manchester_advance(
+        instance->manchester_saved_state,
+        ManchesterEventReset,
+        &instance->manchester_saved_state,
+        NULL);
+}
+
+void subghz_protocol_decoder_marantec_feed(void* context, bool level, volatile uint32_t duration) {
+    furi_assert(context);
+    SubGhzProtocolDecoderMarantec* instance = context;
+    ManchesterEvent event = ManchesterEventReset;
+
+    switch(instance->decoder.parser_step) {
+    case MarantecDecoderStepReset:
+        if((!level) && (DURATION_DIFF(duration, subghz_protocol_marantec_const.te_long * 5) <
+                        subghz_protocol_marantec_const.te_delta * 8)) {
+            //Found header marantec
+            instance->decoder.parser_step = MarantecDecoderStepDecoderData;
+            instance->decoder.decode_data = 1;
+            instance->decoder.decode_count_bit = 1;
+            manchester_advance(
+                instance->manchester_saved_state,
+                ManchesterEventReset,
+                &instance->manchester_saved_state,
+                NULL);
+        }
+        break;
+    case MarantecDecoderStepDecoderData:
+        if(!level) {
+            if(DURATION_DIFF(duration, subghz_protocol_marantec_const.te_short) <
+               subghz_protocol_marantec_const.te_delta) {
+                event = ManchesterEventShortLow;
+            } else if(
+                DURATION_DIFF(duration, subghz_protocol_marantec_const.te_long) <
+                subghz_protocol_marantec_const.te_delta) {
+                event = ManchesterEventLongLow;
+            } else if(
+                duration >= ((uint32_t)subghz_protocol_marantec_const.te_long * 2 +
+                             subghz_protocol_marantec_const.te_delta)) {
+                if(instance->decoder.decode_count_bit ==
+                   subghz_protocol_marantec_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);
+                }
+                instance->decoder.decode_data = 1;
+                instance->decoder.decode_count_bit = 1;
+                manchester_advance(
+                    instance->manchester_saved_state,
+                    ManchesterEventReset,
+                    &instance->manchester_saved_state,
+                    NULL);
+            } else {
+                instance->decoder.parser_step = MarantecDecoderStepReset;
+            }
+        } else {
+            if(DURATION_DIFF(duration, subghz_protocol_marantec_const.te_short) <
+               subghz_protocol_marantec_const.te_delta) {
+                event = ManchesterEventShortHigh;
+            } else if(
+                DURATION_DIFF(duration, subghz_protocol_marantec_const.te_long) <
+                subghz_protocol_marantec_const.te_delta) {
+                event = ManchesterEventLongHigh;
+            } else {
+                instance->decoder.parser_step = MarantecDecoderStepReset;
+            }
+        }
+        if(event != ManchesterEventReset) {
+            bool data;
+            bool data_ok = manchester_advance(
+                instance->manchester_saved_state, event, &instance->manchester_saved_state, &data);
+
+            if(data_ok) {
+                instance->decoder.decode_data = (instance->decoder.decode_data << 1) | data;
+                instance->decoder.decode_count_bit++;
+            }
+        }
+        break;
+    }
+}
+
+uint8_t subghz_protocol_decoder_marantec_get_hash_data(void* context) {
+    furi_assert(context);
+    SubGhzProtocolDecoderMarantec* instance = context;
+    return subghz_protocol_blocks_get_hash_data(
+        &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1);
+}
+
+bool subghz_protocol_decoder_marantec_serialize(
+    void* context,
+    FlipperFormat* flipper_format,
+    SubGhzPresetDefinition* preset) {
+    furi_assert(context);
+    SubGhzProtocolDecoderMarantec* instance = context;
+    return subghz_block_generic_serialize(&instance->generic, flipper_format, preset);
+}
+
+bool subghz_protocol_decoder_marantec_deserialize(void* context, FlipperFormat* flipper_format) {
+    furi_assert(context);
+    SubGhzProtocolDecoderMarantec* instance = context;
+    bool ret = false;
+    do {
+        if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) {
+            break;
+        }
+        if(instance->generic.data_count_bit !=
+           subghz_protocol_marantec_const.min_count_bit_for_found) {
+            FURI_LOG_E(TAG, "Wrong number of bits in key");
+            break;
+        }
+        ret = true;
+    } while(false);
+    return ret;
+}
+
+void subghz_protocol_decoder_marantec_get_string(void* context, string_t output) {
+    furi_assert(context);
+    SubGhzProtocolDecoderMarantec* instance = context;
+    subghz_protocol_marantec_remote_controller(&instance->generic);
+
+    string_cat_printf(
+        output,
+        "%s %db\r\n"
+        "Key:0x%lX%08lX\r\n"
+        "Sn:0x%07lX \r\n"
+        "Btn:%lX\r\n",
+        instance->generic.protocol_name,
+        instance->generic.data_count_bit,
+        (uint32_t)(instance->generic.data >> 32),
+        (uint32_t)(instance->generic.data & 0xFFFFFFFF),
+        instance->generic.serial,
+        instance->generic.btn);
+}

+ 107 - 0
lib/subghz/protocols/marantec.h

@@ -0,0 +1,107 @@
+#pragma once
+
+#include "base.h"
+
+#define SUBGHZ_PROTOCOL_MARANTEC_NAME "Marantec"
+
+typedef struct SubGhzProtocolDecoderMarantec SubGhzProtocolDecoderMarantec;
+typedef struct SubGhzProtocolEncoderMarantec SubGhzProtocolEncoderMarantec;
+
+extern const SubGhzProtocolDecoder subghz_protocol_marantec_decoder;
+extern const SubGhzProtocolEncoder subghz_protocol_marantec_encoder;
+extern const SubGhzProtocol subghz_protocol_marantec;
+
+/**
+ * Allocate SubGhzProtocolEncoderMarantec.
+ * @param environment Pointer to a SubGhzEnvironment instance
+ * @return SubGhzProtocolEncoderMarantec* pointer to a SubGhzProtocolEncoderMarantec instance
+ */
+void* subghz_protocol_encoder_marantec_alloc(SubGhzEnvironment* environment);
+
+/**
+ * Free SubGhzProtocolEncoderMarantec.
+ * @param context Pointer to a SubGhzProtocolEncoderMarantec instance
+ */
+void subghz_protocol_encoder_marantec_free(void* context);
+
+/**
+ * Deserialize and generating an upload to send.
+ * @param context Pointer to a SubGhzProtocolEncoderMarantec instance
+ * @param flipper_format Pointer to a FlipperFormat instance
+ * @return true On success
+ */
+bool subghz_protocol_encoder_marantec_deserialize(void* context, FlipperFormat* flipper_format);
+
+/**
+ * Forced transmission stop.
+ * @param context Pointer to a SubGhzProtocolEncoderMarantec instance
+ */
+void subghz_protocol_encoder_marantec_stop(void* context);
+
+/**
+ * Getting the level and duration of the upload to be loaded into DMA.
+ * @param context Pointer to a SubGhzProtocolEncoderMarantec instance
+ * @return LevelDuration 
+ */
+LevelDuration subghz_protocol_encoder_marantec_yield(void* context);
+
+/**
+ * Allocate SubGhzProtocolDecoderMarantec.
+ * @param environment Pointer to a SubGhzEnvironment instance
+ * @return SubGhzProtocolDecoderMarantec* pointer to a SubGhzProtocolDecoderMarantec instance
+ */
+void* subghz_protocol_decoder_marantec_alloc(SubGhzEnvironment* environment);
+
+/**
+ * Free SubGhzProtocolDecoderMarantec.
+ * @param context Pointer to a SubGhzProtocolDecoderMarantec instance
+ */
+void subghz_protocol_decoder_marantec_free(void* context);
+
+/**
+ * Reset decoder SubGhzProtocolDecoderMarantec.
+ * @param context Pointer to a SubGhzProtocolDecoderMarantec instance
+ */
+void subghz_protocol_decoder_marantec_reset(void* context);
+
+/**
+ * Parse a raw sequence of levels and durations received from the air.
+ * @param context Pointer to a SubGhzProtocolDecoderMarantec instance
+ * @param level Signal level true-high false-low
+ * @param duration Duration of this level in, us
+ */
+void subghz_protocol_decoder_marantec_feed(void* context, bool level, uint32_t duration);
+
+/**
+ * Getting the hash sum of the last randomly received parcel.
+ * @param context Pointer to a SubGhzProtocolDecoderMarantec instance
+ * @return hash Hash sum
+ */
+uint8_t subghz_protocol_decoder_marantec_get_hash_data(void* context);
+
+/**
+ * Serialize data SubGhzProtocolDecoderMarantec.
+ * @param context Pointer to a SubGhzProtocolDecoderMarantec instance
+ * @param flipper_format Pointer to a FlipperFormat instance
+ * @param preset The modulation on which the signal was received, SubGhzPresetDefinition
+ * @return true On success
+ */
+bool subghz_protocol_decoder_marantec_serialize(
+    void* context,
+    FlipperFormat* flipper_format,
+    SubGhzPresetDefinition* preset);
+
+/**
+ * Deserialize data SubGhzProtocolDecoderMarantec.
+ * @param context Pointer to a SubGhzProtocolDecoderMarantec instance
+ * @param flipper_format Pointer to a FlipperFormat instance
+ * @return true On success
+ */
+bool subghz_protocol_decoder_marantec_deserialize(void* context, FlipperFormat* flipper_format);
+
+/**
+ * Getting a textual representation of the received data.
+ * @param context Pointer to a SubGhzProtocolDecoderMarantec instance
+ * @param output Resulting text
+ */
+void subghz_protocol_decoder_marantec_get_string(void* context, string_t output);

+ 339 - 0
lib/subghz/protocols/phoenix_v2.c

@@ -0,0 +1,339 @@
+#include "phoenix_v2.h"
+
+#include "../blocks/const.h"
+#include "../blocks/decoder.h"
+#include "../blocks/encoder.h"
+#include "../blocks/generic.h"
+#include "../blocks/math.h"
+
+#define TAG "SubGhzProtocolPhoenix_V2"
+
+//transmission only static mode
+
+static const SubGhzBlockConst subghz_protocol_phoenix_v2_const = {
+    .te_short = 427,
+    .te_long = 853,
+    .te_delta = 100,
+    .min_count_bit_for_found = 52,
+};
+
+struct SubGhzProtocolDecoderPhoenix_V2 {
+    SubGhzProtocolDecoderBase base;
+
+    SubGhzBlockDecoder decoder;
+    SubGhzBlockGeneric generic;
+};
+
+struct SubGhzProtocolEncoderPhoenix_V2 {
+    SubGhzProtocolEncoderBase base;
+
+    SubGhzProtocolBlockEncoder encoder;
+    SubGhzBlockGeneric generic;
+};
+
+typedef enum {
+    Phoenix_V2DecoderStepReset = 0,
+    Phoenix_V2DecoderStepFoundStartBit,
+    Phoenix_V2DecoderStepSaveDuration,
+    Phoenix_V2DecoderStepCheckDuration,
+} Phoenix_V2DecoderStep;
+
+const SubGhzProtocolDecoder subghz_protocol_phoenix_v2_decoder = {
+    .alloc = subghz_protocol_decoder_phoenix_v2_alloc,
+    .free = subghz_protocol_decoder_phoenix_v2_free,
+
+    .feed = subghz_protocol_decoder_phoenix_v2_feed,
+    .reset = subghz_protocol_decoder_phoenix_v2_reset,
+
+    .get_hash_data = subghz_protocol_decoder_phoenix_v2_get_hash_data,
+    .serialize = subghz_protocol_decoder_phoenix_v2_serialize,
+    .deserialize = subghz_protocol_decoder_phoenix_v2_deserialize,
+    .get_string = subghz_protocol_decoder_phoenix_v2_get_string,
+};
+
+const SubGhzProtocolEncoder subghz_protocol_phoenix_v2_encoder = {
+    .alloc = subghz_protocol_encoder_phoenix_v2_alloc,
+    .free = subghz_protocol_encoder_phoenix_v2_free,
+
+    .deserialize = subghz_protocol_encoder_phoenix_v2_deserialize,
+    .stop = subghz_protocol_encoder_phoenix_v2_stop,
+    .yield = subghz_protocol_encoder_phoenix_v2_yield,
+};
+
+const SubGhzProtocol subghz_protocol_phoenix_v2 = {
+    .name = SUBGHZ_PROTOCOL_PHOENIX_V2_NAME,
+    .type = SubGhzProtocolTypeStatic,
+    .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable |
+            SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Send,
+
+    .decoder = &subghz_protocol_phoenix_v2_decoder,
+    .encoder = &subghz_protocol_phoenix_v2_encoder,
+};
+
+void* subghz_protocol_encoder_phoenix_v2_alloc(SubGhzEnvironment* environment) {
+    UNUSED(environment);
+    SubGhzProtocolEncoderPhoenix_V2* instance = malloc(sizeof(SubGhzProtocolEncoderPhoenix_V2));
+
+    instance->base.protocol = &subghz_protocol_phoenix_v2;
+    instance->generic.protocol_name = instance->base.protocol->name;
+
+    instance->encoder.repeat = 10;
+    instance->encoder.size_upload = 128;
+    instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration));
+    instance->encoder.is_runing = false;
+    return instance;
+}
+
+void subghz_protocol_encoder_phoenix_v2_free(void* context) {
+    furi_assert(context);
+    SubGhzProtocolEncoderPhoenix_V2* instance = context;
+    free(instance->encoder.upload);
+    free(instance);
+}
+
+/**
+ * Generating an upload from data.
+ * @param instance Pointer to a SubGhzProtocolEncoderPhoenix_V2 instance
+ * @return true On success
+ */
+static bool
+    subghz_protocol_encoder_phoenix_v2_get_upload(SubGhzProtocolEncoderPhoenix_V2* instance) {
+    furi_assert(instance);
+    size_t index = 0;
+    size_t size_upload = (instance->generic.data_count_bit * 2) + 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;
+    }
+    //Send header
+    instance->encoder.upload[index++] =
+        level_duration_make(false, (uint32_t)subghz_protocol_phoenix_v2_const.te_short * 60);
+    //Send start bit
+    instance->encoder.upload[index++] =
+        level_duration_make(true, (uint32_t)subghz_protocol_phoenix_v2_const.te_short * 6);
+    //Send key data
+    for(uint8_t i = instance->generic.data_count_bit; i > 0; i--) {
+        if(!bit_read(instance->generic.data, i - 1)) {
+            //send bit 1
+            instance->encoder.upload[index++] =
+                level_duration_make(false, (uint32_t)subghz_protocol_phoenix_v2_const.te_long);
+            instance->encoder.upload[index++] =
+                level_duration_make(true, (uint32_t)subghz_protocol_phoenix_v2_const.te_short);
+        } else {
+            //send bit 0
+            instance->encoder.upload[index++] =
+                level_duration_make(false, (uint32_t)subghz_protocol_phoenix_v2_const.te_short);
+            instance->encoder.upload[index++] =
+                level_duration_make(true, (uint32_t)subghz_protocol_phoenix_v2_const.te_long);
+        }
+    }
+    return true;
+}
+
+bool subghz_protocol_encoder_phoenix_v2_deserialize(void* context, FlipperFormat* flipper_format) {
+    furi_assert(context);
+    SubGhzProtocolEncoderPhoenix_V2* instance = context;
+    bool res = false;
+    do {
+        if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) {
+            FURI_LOG_E(TAG, "Deserialize error");
+            break;
+        }
+        if(instance->generic.data_count_bit !=
+           subghz_protocol_phoenix_v2_const.min_count_bit_for_found) {
+            FURI_LOG_E(TAG, "Wrong number of bits in key");
+            break;
+        }
+        //optional parameter parameter
+        flipper_format_read_uint32(
+            flipper_format, "Repeat", (uint32_t*)&instance->encoder.repeat, 1);
+
+        subghz_protocol_encoder_phoenix_v2_get_upload(instance);
+        instance->encoder.is_runing = true;
+
+        res = true;
+    } while(false);
+
+    return res;
+}
+
+void subghz_protocol_encoder_phoenix_v2_stop(void* context) {
+    SubGhzProtocolEncoderPhoenix_V2* instance = context;
+    instance->encoder.is_runing = false;
+}
+
+LevelDuration subghz_protocol_encoder_phoenix_v2_yield(void* context) {
+    SubGhzProtocolEncoderPhoenix_V2* 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_phoenix_v2_alloc(SubGhzEnvironment* environment) {
+    UNUSED(environment);
+    SubGhzProtocolDecoderPhoenix_V2* instance = malloc(sizeof(SubGhzProtocolDecoderPhoenix_V2));
+    instance->base.protocol = &subghz_protocol_phoenix_v2;
+    instance->generic.protocol_name = instance->base.protocol->name;
+    return instance;
+}
+
+void subghz_protocol_decoder_phoenix_v2_free(void* context) {
+    furi_assert(context);
+    SubGhzProtocolDecoderPhoenix_V2* instance = context;
+    free(instance);
+}
+
+void subghz_protocol_decoder_phoenix_v2_reset(void* context) {
+    furi_assert(context);
+    SubGhzProtocolDecoderPhoenix_V2* instance = context;
+    instance->decoder.parser_step = Phoenix_V2DecoderStepReset;
+}
+
+void subghz_protocol_decoder_phoenix_v2_feed(void* context, bool level, uint32_t duration) {
+    furi_assert(context);
+    SubGhzProtocolDecoderPhoenix_V2* instance = context;
+
+    switch(instance->decoder.parser_step) {
+    case Phoenix_V2DecoderStepReset:
+        if((!level) && (DURATION_DIFF(duration, subghz_protocol_phoenix_v2_const.te_short * 60) <
+                        subghz_protocol_phoenix_v2_const.te_delta * 30)) {
+            //Found Preambula
+            instance->decoder.parser_step = Phoenix_V2DecoderStepFoundStartBit;
+        }
+        break;
+    case Phoenix_V2DecoderStepFoundStartBit:
+        if(level && ((DURATION_DIFF(duration, (subghz_protocol_phoenix_v2_const.te_short * 6)) <
+                      subghz_protocol_phoenix_v2_const.te_delta * 4))) {
+            //Found start bit
+            instance->decoder.parser_step = Phoenix_V2DecoderStepSaveDuration;
+            instance->decoder.decode_data = 0;
+            instance->decoder.decode_count_bit = 0;
+        } else {
+            instance->decoder.parser_step = Phoenix_V2DecoderStepReset;
+        }
+        break;
+    case Phoenix_V2DecoderStepSaveDuration:
+        if(!level) {
+            if(duration >= ((uint32_t)subghz_protocol_phoenix_v2_const.te_short * 10 +
+                            subghz_protocol_phoenix_v2_const.te_delta)) {
+                instance->decoder.parser_step = Phoenix_V2DecoderStepFoundStartBit;
+                if(instance->decoder.decode_count_bit ==
+                   subghz_protocol_phoenix_v2_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);
+                }
+                instance->decoder.decode_data = 0;
+                instance->decoder.decode_count_bit = 0;
+                break;
+            } else {
+                instance->decoder.te_last = duration;
+                instance->decoder.parser_step = Phoenix_V2DecoderStepCheckDuration;
+            }
+        }
+        break;
+    case Phoenix_V2DecoderStepCheckDuration:
+        if(level) {
+            if((DURATION_DIFF(
+                    instance->decoder.te_last, subghz_protocol_phoenix_v2_const.te_short) <
+                subghz_protocol_phoenix_v2_const.te_delta) &&
+               (DURATION_DIFF(duration, subghz_protocol_phoenix_v2_const.te_long) <
+                subghz_protocol_phoenix_v2_const.te_delta * 3)) {
+                subghz_protocol_blocks_add_bit(&instance->decoder, 1);
+                instance->decoder.parser_step = Phoenix_V2DecoderStepSaveDuration;
+            } else if(
+                (DURATION_DIFF(
+                     instance->decoder.te_last, subghz_protocol_phoenix_v2_const.te_long) <
+                 subghz_protocol_phoenix_v2_const.te_delta * 3) &&
+                (DURATION_DIFF(duration, subghz_protocol_phoenix_v2_const.te_short) <
+                 subghz_protocol_phoenix_v2_const.te_delta)) {
+                subghz_protocol_blocks_add_bit(&instance->decoder, 0);
+                instance->decoder.parser_step = Phoenix_V2DecoderStepSaveDuration;
+            } else {
+                instance->decoder.parser_step = Phoenix_V2DecoderStepReset;
+            }
+        } else {
+            instance->decoder.parser_step = Phoenix_V2DecoderStepReset;
+        }
+        break;
+    }
+}
+
+/** 
+ * Analysis of received data
+ * @param instance Pointer to a SubGhzBlockGeneric* instance
+ */
+static void subghz_protocol_phoenix_v2_check_remote_controller(SubGhzBlockGeneric* instance) {
+    uint64_t data_rev =
+        subghz_protocol_blocks_reverse_key(instance->data, instance->data_count_bit + 4);
+    instance->serial = data_rev & 0xFFFFFFFF;
+    instance->cnt = (data_rev >> 40) & 0xFFFF;
+    instance->btn = (data_rev >> 32) & 0xF;
+}
+
+uint8_t subghz_protocol_decoder_phoenix_v2_get_hash_data(void* context) {
+    furi_assert(context);
+    SubGhzProtocolDecoderPhoenix_V2* instance = context;
+    return subghz_protocol_blocks_get_hash_data(
+        &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1);
+}
+
+bool subghz_protocol_decoder_phoenix_v2_serialize(
+    void* context,
+    FlipperFormat* flipper_format,
+    SubGhzPresetDefinition* preset) {
+    furi_assert(context);
+    SubGhzProtocolDecoderPhoenix_V2* instance = context;
+    return subghz_block_generic_serialize(&instance->generic, flipper_format, preset);
+}
+
+bool subghz_protocol_decoder_phoenix_v2_deserialize(void* context, FlipperFormat* flipper_format) {
+    furi_assert(context);
+    SubGhzProtocolDecoderPhoenix_V2* instance = context;
+    bool ret = false;
+    do {
+        if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) {
+            break;
+        }
+        if(instance->generic.data_count_bit !=
+           subghz_protocol_phoenix_v2_const.min_count_bit_for_found) {
+            FURI_LOG_E(TAG, "Wrong number of bits in key");
+            break;
+        }
+        ret = true;
+    } while(false);
+    return ret;
+}
+
+void subghz_protocol_decoder_phoenix_v2_get_string(void* context, string_t output) {
+    furi_assert(context);
+    SubGhzProtocolDecoderPhoenix_V2* instance = context;
+    subghz_protocol_phoenix_v2_check_remote_controller(&instance->generic);
+    string_cat_printf(
+        output,
+        "%s %dbit\r\n"
+        "Key:%02lX%08lX\r\n"
+        "Sn:0x%07lX \r\n"
+        "Btn:%lX\r\n",
+        instance->generic.protocol_name,
+        instance->generic.data_count_bit,
+        (uint32_t)(instance->generic.data >> 32) & 0xFFFFFFFF,
+        (uint32_t)(instance->generic.data & 0xFFFFFFFF),
+        instance->generic.serial,
+        instance->generic.btn);
+}

+ 107 - 0
lib/subghz/protocols/phoenix_v2.h

@@ -0,0 +1,107 @@
+#pragma once
+
+#include "base.h"
+
+#define SUBGHZ_PROTOCOL_PHOENIX_V2_NAME "Phoenix_V2"
+
+typedef struct SubGhzProtocolDecoderPhoenix_V2 SubGhzProtocolDecoderPhoenix_V2;
+typedef struct SubGhzProtocolEncoderPhoenix_V2 SubGhzProtocolEncoderPhoenix_V2;
+
+extern const SubGhzProtocolDecoder subghz_protocol_phoenix_v2_decoder;
+extern const SubGhzProtocolEncoder subghz_protocol_phoenix_v2_encoder;
+extern const SubGhzProtocol subghz_protocol_phoenix_v2;
+
+/**
+ * Allocate SubGhzProtocolEncoderPhoenix_V2.
+ * @param environment Pointer to a SubGhzEnvironment instance
+ * @return SubGhzProtocolEncoderPhoenix_V2* pointer to a SubGhzProtocolEncoderPhoenix_V2 instance
+ */
+void* subghz_protocol_encoder_phoenix_v2_alloc(SubGhzEnvironment* environment);
+
+/**
+ * Free SubGhzProtocolEncoderPhoenix_V2.
+ * @param context Pointer to a SubGhzProtocolEncoderPhoenix_V2 instance
+ */
+void subghz_protocol_encoder_phoenix_v2_free(void* context);
+
+/**
+ * Deserialize and generating an upload to send.
+ * @param context Pointer to a SubGhzProtocolEncoderPhoenix_V2 instance
+ * @param flipper_format Pointer to a FlipperFormat instance
+ * @return true On success
+ */
+bool subghz_protocol_encoder_phoenix_v2_deserialize(void* context, FlipperFormat* flipper_format);
+
+/**
+ * Forced transmission stop.
+ * @param context Pointer to a SubGhzProtocolEncoderPhoenix_V2 instance
+ */
+void subghz_protocol_encoder_phoenix_v2_stop(void* context);
+
+/**
+ * Getting the level and duration of the upload to be loaded into DMA.
+ * @param context Pointer to a SubGhzProtocolEncoderPhoenix_V2 instance
+ * @return LevelDuration 
+ */
+LevelDuration subghz_protocol_encoder_phoenix_v2_yield(void* context);
+
+/**
+ * Allocate SubGhzProtocolDecoderPhoenix_V2.
+ * @param environment Pointer to a SubGhzEnvironment instance
+ * @return SubGhzProtocolDecoderPhoenix_V2* pointer to a SubGhzProtocolDecoderPhoenix_V2 instance
+ */
+void* subghz_protocol_decoder_phoenix_v2_alloc(SubGhzEnvironment* environment);
+
+/**
+ * Free SubGhzProtocolDecoderPhoenix_V2.
+ * @param context Pointer to a SubGhzProtocolDecoderPhoenix_V2 instance
+ */
+void subghz_protocol_decoder_phoenix_v2_free(void* context);
+
+/**
+ * Reset decoder SubGhzProtocolDecoderPhoenix_V2.
+ * @param context Pointer to a SubGhzProtocolDecoderPhoenix_V2 instance
+ */
+void subghz_protocol_decoder_phoenix_v2_reset(void* context);
+
+/**
+ * Parse a raw sequence of levels and durations received from the air.
+ * @param context Pointer to a SubGhzProtocolDecoderPhoenix_V2 instance
+ * @param level Signal level true-high false-low
+ * @param duration Duration of this level in, us
+ */
+void subghz_protocol_decoder_phoenix_v2_feed(void* context, bool level, uint32_t duration);
+
+/**
+ * Getting the hash sum of the last randomly received parcel.
+ * @param context Pointer to a SubGhzProtocolDecoderPhoenix_V2 instance
+ * @return hash Hash sum
+ */
+uint8_t subghz_protocol_decoder_phoenix_v2_get_hash_data(void* context);
+
+/**
+ * Serialize data SubGhzProtocolDecoderPhoenix_V2.
+ * @param context Pointer to a SubGhzProtocolDecoderPhoenix_V2 instance
+ * @param flipper_format Pointer to a FlipperFormat instance
+ * @param preset The modulation on which the signal was received, SubGhzPresetDefinition
+ * @return true On success
+ */
+bool subghz_protocol_decoder_phoenix_v2_serialize(
+    void* context,
+    FlipperFormat* flipper_format,
+    SubGhzPresetDefinition* preset);
+
+/**
+ * Deserialize data SubGhzProtocolDecoderPhoenix_V2.
+ * @param context Pointer to a SubGhzProtocolDecoderPhoenix_V2 instance
+ * @param flipper_format Pointer to a FlipperFormat instance
+ * @return true On success
+ */
+bool subghz_protocol_decoder_phoenix_v2_deserialize(void* context, FlipperFormat* flipper_format);
+
+/**
+ * Getting a textual representation of the received data.
+ * @param context Pointer to a SubGhzProtocolDecoderPhoenix_V2 instance
+ * @param output Resulting text
+ */
+void subghz_protocol_decoder_phoenix_v2_get_string(void* context, string_t output);

+ 14 - 11
lib/subghz/protocols/princeton.c

@@ -256,8 +256,6 @@ void subghz_protocol_decoder_princeton_feed(void* context, bool level, uint32_t
 
 
                         instance->generic.data = instance->decoder.decode_data;
                         instance->generic.data = instance->decoder.decode_data;
                         instance->generic.data_count_bit = instance->decoder.decode_count_bit;
                         instance->generic.data_count_bit = instance->decoder.decode_count_bit;
-                        instance->generic.serial = instance->decoder.decode_data >> 4;
-                        instance->generic.btn = (uint8_t)instance->decoder.decode_data & 0x00000F;
 
 
                         if(instance->base.callback)
                         if(instance->base.callback)
                             instance->base.callback(&instance->base, instance->base.context);
                             instance->base.callback(&instance->base, instance->base.context);
@@ -295,6 +293,15 @@ void subghz_protocol_decoder_princeton_feed(void* context, bool level, uint32_t
     }
     }
 }
 }
 
 
+/** 
+ * Analysis of received data
+ * @param instance Pointer to a SubGhzBlockGeneric* instance
+ */
+static void subghz_protocol_princeton_check_remote_controller(SubGhzBlockGeneric* instance) {
+    instance->serial = instance->data >> 4;
+    instance->btn = instance->data & 0xF;
+}
+
 uint8_t subghz_protocol_decoder_princeton_get_hash_data(void* context) {
 uint8_t subghz_protocol_decoder_princeton_get_hash_data(void* context) {
     furi_assert(context);
     furi_assert(context);
     SubGhzProtocolDecoderPrinceton* instance = context;
     SubGhzProtocolDecoderPrinceton* instance = context;
@@ -347,25 +354,21 @@ bool subghz_protocol_decoder_princeton_deserialize(void* context, FlipperFormat*
 void subghz_protocol_decoder_princeton_get_string(void* context, string_t output) {
 void subghz_protocol_decoder_princeton_get_string(void* context, string_t output) {
     furi_assert(context);
     furi_assert(context);
     SubGhzProtocolDecoderPrinceton* instance = context;
     SubGhzProtocolDecoderPrinceton* instance = context;
-
-    uint32_t code_found_lo = instance->generic.data & 0x00000000ffffffff;
-
-    uint64_t code_found_reverse = subghz_protocol_blocks_reverse_key(
+    subghz_protocol_princeton_check_remote_controller(&instance->generic);
+    uint32_t data_rev = subghz_protocol_blocks_reverse_key(
         instance->generic.data, instance->generic.data_count_bit);
         instance->generic.data, instance->generic.data_count_bit);
 
 
-    uint32_t code_found_reverse_lo = code_found_reverse & 0x00000000ffffffff;
-
     string_cat_printf(
     string_cat_printf(
         output,
         output,
         "%s %dbit\r\n"
         "%s %dbit\r\n"
         "Key:0x%08lX\r\n"
         "Key:0x%08lX\r\n"
         "Yek:0x%08lX\r\n"
         "Yek:0x%08lX\r\n"
-        "Sn:0x%05lX BTN:%02X\r\n"
+        "Sn:0x%05lX Btn:%01X\r\n"
         "Te:%dus\r\n",
         "Te:%dus\r\n",
         instance->generic.protocol_name,
         instance->generic.protocol_name,
         instance->generic.data_count_bit,
         instance->generic.data_count_bit,
-        code_found_lo,
-        code_found_reverse_lo,
+        (uint32_t)(instance->generic.data & 0xFFFFFF),
+        data_rev,
         instance->generic.serial,
         instance->generic.serial,
         instance->generic.btn,
         instance->generic.btn,
         instance->te);
         instance->te);

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

@@ -9,7 +9,8 @@ const SubGhzProtocol* subghz_protocol_registry[] = {
     &subghz_protocol_somfy_keytis, &subghz_protocol_scher_khan,  &subghz_protocol_princeton,
     &subghz_protocol_somfy_keytis, &subghz_protocol_scher_khan,  &subghz_protocol_princeton,
     &subghz_protocol_raw,          &subghz_protocol_linear,      &subghz_protocol_secplus_v2,
     &subghz_protocol_raw,          &subghz_protocol_linear,      &subghz_protocol_secplus_v2,
     &subghz_protocol_secplus_v1,   &subghz_protocol_megacode,    &subghz_protocol_holtek,
     &subghz_protocol_secplus_v1,   &subghz_protocol_megacode,    &subghz_protocol_holtek,
-    &subghz_protocol_chamb_code,   &subghz_protocol_power_smart,
+    &subghz_protocol_chamb_code,   &subghz_protocol_power_smart, &subghz_protocol_marantec,
+    &subghz_protocol_bett,         &subghz_protocol_doitrand,    &subghz_protocol_phoenix_v2,
 
 
 };
 };
 
 

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

@@ -28,6 +28,10 @@
 #include "holtek.h"
 #include "holtek.h"
 #include "chamberlain_code.h"
 #include "chamberlain_code.h"
 #include "power_smart.h"
 #include "power_smart.h"
+#include "marantec.h"
+#include "bett.h"
+#include "doitrand.h"
+#include "phoenix_v2.h"
 
 
 /**
 /**
  * Registration by name SubGhzProtocol.
  * Registration by name SubGhzProtocol.

Некоторые файлы не были показаны из-за большого количества измененных файлов