Parcourir la source

SubGhz: add protocol BinRAW (binarization of data quantized by the minimum correlated duration) (#2322)

* SubGhz: add protocol DataRAW (binarization of data quantized by the minimum correlated duration)
* SubGhz: fix name history
* SubGhz: add encoder Data_RAW protocol
* SubGhz: decreasing the size of the LevelDuration structure
* SubGhz: history, added check that there is free RAM
* SubGhz: checking for free memory, support to pass without gap
* SubGhz: add running average to average the result, auto cut noise at the end of a burst
* SubGhz: support for repeating sequences
* SubGhz: fix secplus_v2 decoder
* SubGhz: bin_RAW fix add history
* SubGhz: add debug
* SubGhz: debug refactoring
* FURI_LOG: add FURI_LOG_RAW_x formatted string output like printf
* SubGhz: fix new FURI_LOG metod
* FURI_LOG: fix unit test
* SubGhz: add enable/disable BinRAW protocol decoding
* SubGhz: fix PVS
* SubGhz: forcibly turn off the speaker when exiting SubGhz
* SubGhz: adaptive adjustment to the noise level

Co-authored-by: Aleksandr Kutuzov <alleteam@gmail.com>
Skorpionm il y a 2 ans
Parent
commit
163be139eb

+ 1 - 1
applications/debug/unit_tests/test_index.c

@@ -70,7 +70,7 @@ void minunit_print_progress() {
 }
 
 void minunit_print_fail(const char* str) {
-    printf(FURI_LOG_CLR_E "%s\r\n" FURI_LOG_CLR_RESET, str);
+    printf(_FURI_LOG_CLR_E "%s\r\n" _FURI_LOG_CLR_RESET, str);
 }
 
 void unit_tests_cli(Cli* cli, FuriString* args, void* context) {

+ 1 - 1
applications/main/subghz/application.fam

@@ -12,7 +12,7 @@ App(
     ],
     provides=["subghz_start"],
     icon="A_Sub1ghz_14",
-    stack_size=2 * 1024,
+    stack_size=3 * 1024,
     order=10,
 )
 

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

@@ -411,5 +411,5 @@ void subghz_scene_read_raw_on_exit(void* context) {
     notification_message(subghz->notifications, &sequence_reset_rgb);
 
     //filter restoration
-    subghz_receiver_set_filter(subghz->txrx->receiver, SubGhzProtocolFlag_Decodable);
+    subghz_receiver_set_filter(subghz->txrx->receiver, subghz->txrx->filter);
 }

+ 13 - 0
applications/main/subghz/scenes/subghz_scene_receiver.c

@@ -1,6 +1,7 @@
 #include "../subghz_i.h"
 #include "../views/receiver.h"
 #include <dolphin/dolphin.h>
+#include <lib/subghz/protocols/bin_raw.h>
 
 static const NotificationSequence subghs_sequence_rx = {
     &message_green_255,
@@ -143,6 +144,11 @@ void subghz_scene_receiver_on_enter(void* context) {
     }
     subghz_view_receiver_set_idx_menu(subghz->subghz_receiver, subghz->txrx->idx_menu_chosen);
 
+    //to use a universal decoder, we are looking for a link to it
+    subghz->txrx->decoder_result = subghz_receiver_search_decoder_base_by_name(
+        subghz->txrx->receiver, SUBGHZ_PROTOCOL_BIN_RAW_NAME);
+    furi_assert(subghz->txrx->decoder_result);
+
     view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewIdReceiver);
 }
 
@@ -208,6 +214,13 @@ bool subghz_scene_receiver_on_event(void* context, SceneManagerEvent event) {
             subghz_hopper_update(subghz);
             subghz_scene_receiver_update_statusbar(subghz);
         }
+
+        //get RSSI
+        float rssi = furi_hal_subghz_get_rssi();
+        subghz_receiver_rssi(subghz->subghz_receiver, rssi);
+        subghz_protocol_decoder_bin_raw_data_input_rssi(
+            (SubGhzProtocolDecoderBinRAW*)subghz->txrx->decoder_result, rssi);
+
         switch(subghz->state_notifications) {
         case SubGhzNotificationStateRx:
             notification_message(subghz->notifications, &sequence_blink_cyan_10);

+ 32 - 0
applications/main/subghz/scenes/subghz_scene_receiver_config.c

@@ -5,6 +5,7 @@ enum SubGhzSettingIndex {
     SubGhzSettingIndexFrequency,
     SubGhzSettingIndexHopping,
     SubGhzSettingIndexModulation,
+    SubGhzSettingIndexBinRAW,
     SubGhzSettingIndexSound,
     SubGhzSettingIndexLock,
     SubGhzSettingIndexRAWThesholdRSSI,
@@ -58,6 +59,15 @@ const uint32_t speaker_value[SPEAKER_COUNT] = {
     SubGhzSpeakerStateShutdown,
     SubGhzSpeakerStateEnable,
 };
+#define BIN_RAW_COUNT 2
+const char* const bin_raw_text[BIN_RAW_COUNT] = {
+    "OFF",
+    "ON",
+};
+const uint32_t bin_raw_value[BIN_RAW_COUNT] = {
+    SubGhzProtocolFlag_Decodable,
+    SubGhzProtocolFlag_Decodable | SubGhzProtocolFlag_BinRAW,
+};
 
 uint8_t subghz_scene_receiver_config_next_frequency(const uint32_t value, void* context) {
     furi_assert(context);
@@ -186,6 +196,15 @@ static void subghz_scene_receiver_config_set_speaker(VariableItem* item) {
     subghz->txrx->speaker_state = speaker_value[index];
 }
 
+static void subghz_scene_receiver_config_set_bin_raw(VariableItem* item) {
+    SubGhz* subghz = variable_item_get_context(item);
+    uint8_t index = variable_item_get_current_value_index(item);
+
+    variable_item_set_current_value_text(item, bin_raw_text[index]);
+    subghz->txrx->filter = bin_raw_value[index];
+    subghz_receiver_set_filter(subghz->txrx->receiver, subghz->txrx->filter);
+}
+
 static void subghz_scene_receiver_config_set_raw_threshold_rssi(VariableItem* item) {
     SubGhz* subghz = variable_item_get_context(item);
     uint8_t index = variable_item_get_current_value_index(item);
@@ -254,6 +273,19 @@ void subghz_scene_receiver_config_on_enter(void* context) {
     variable_item_set_current_value_text(
         item, subghz_setting_get_preset_name(subghz->setting, value_index));
 
+    if(scene_manager_get_scene_state(subghz->scene_manager, SubGhzSceneReadRAW) !=
+       SubGhzCustomEventManagerSet) {
+        item = variable_item_list_add(
+            subghz->variable_item_list,
+            "Bin_RAW:",
+            BIN_RAW_COUNT,
+            subghz_scene_receiver_config_set_bin_raw,
+            subghz);
+        value_index = value_index_uint32(subghz->txrx->filter, bin_raw_value, BIN_RAW_COUNT);
+        variable_item_set_current_value_index(item, value_index);
+        variable_item_set_current_value_text(item, bin_raw_text[value_index]);
+    }
+
     item = variable_item_list_add(
         subghz->variable_item_list,
         "Sound:",

+ 4 - 1
applications/main/subghz/subghz.c

@@ -194,7 +194,8 @@ SubGhz* subghz_alloc() {
     subghz_environment_set_protocol_registry(
         subghz->txrx->environment, (void*)&subghz_protocol_registry);
     subghz->txrx->receiver = subghz_receiver_alloc_init(subghz->txrx->environment);
-    subghz_receiver_set_filter(subghz->txrx->receiver, SubGhzProtocolFlag_Decodable);
+    subghz->txrx->filter = SubGhzProtocolFlag_Decodable;
+    subghz_receiver_set_filter(subghz->txrx->receiver, subghz->txrx->filter);
 
     subghz_worker_set_overrun_callback(
         subghz->txrx->worker, (SubGhzWorkerOverrunCallback)subghz_receiver_reset);
@@ -218,6 +219,8 @@ void subghz_free(SubGhz* subghz) {
         subghz->rpc_ctx = NULL;
     }
 
+    subghz_speaker_off(subghz);
+
     // Packet Test
     view_dispatcher_remove_view(subghz->view_dispatcher, SubGhzViewIdTestPacket);
     subghz_test_packet_free(subghz->subghz_test_packet);

+ 25 - 15
applications/main/subghz/subghz_history.c

@@ -5,6 +5,7 @@
 #include <furi.h>
 
 #define SUBGHZ_HISTORY_MAX 50
+#define SUBGHZ_HISTORY_FREE_HEAP 20480
 #define TAG "SubGhzHistory"
 
 typedef struct {
@@ -121,8 +122,12 @@ FlipperFormat* subghz_history_get_raw_data(SubGhzHistory* instance, uint16_t idx
 }
 bool subghz_history_get_text_space_left(SubGhzHistory* instance, FuriString* output) {
     furi_assert(instance);
+    if(memmgr_get_free_heap() < SUBGHZ_HISTORY_FREE_HEAP) {
+        if(output != NULL) furi_string_printf(output, "    Free heap LOW");
+        return true;
+    }
     if(instance->last_index_write == SUBGHZ_HISTORY_MAX) {
-        if(output != NULL) furi_string_printf(output, "Memory is FULL");
+        if(output != NULL) furi_string_printf(output, "   Memory is FULL");
         return true;
     }
     if(output != NULL)
@@ -142,6 +147,7 @@ bool subghz_history_add_to_history(
     furi_assert(instance);
     furi_assert(context);
 
+    if(memmgr_get_free_heap() < SUBGHZ_HISTORY_FREE_HEAP) return false;
     if(instance->last_index_write >= SUBGHZ_HISTORY_MAX) return false;
 
     SubGhzProtocolDecoderBase* decoder_base = context;
@@ -200,27 +206,31 @@ bool subghz_history_add_to_history(
         }
         uint8_t key_data[sizeof(uint64_t)] = {0};
         if(!flipper_format_read_hex(item->flipper_string, "Key", key_data, sizeof(uint64_t))) {
-            FURI_LOG_E(TAG, "Missing Key");
-            break;
+            FURI_LOG_D(TAG, "No Key");
         }
         uint64_t data = 0;
         for(uint8_t i = 0; i < sizeof(uint64_t); i++) {
             data = (data << 8) | key_data[i];
         }
-        if(!(uint32_t)(data >> 32)) {
-            furi_string_printf(
-                item->item_str,
-                "%s %lX",
-                furi_string_get_cstr(instance->tmp_string),
-                (uint32_t)(data & 0xFFFFFFFF));
+        if(data != 0) {
+            if(!(uint32_t)(data >> 32)) {
+                furi_string_printf(
+                    item->item_str,
+                    "%s %lX",
+                    furi_string_get_cstr(instance->tmp_string),
+                    (uint32_t)(data & 0xFFFFFFFF));
+            } else {
+                furi_string_printf(
+                    item->item_str,
+                    "%s %lX%08lX",
+                    furi_string_get_cstr(instance->tmp_string),
+                    (uint32_t)(data >> 32),
+                    (uint32_t)(data & 0xFFFFFFFF));
+            }
         } else {
-            furi_string_printf(
-                item->item_str,
-                "%s %lX%08lX",
-                furi_string_get_cstr(instance->tmp_string),
-                (uint32_t)(data >> 32),
-                (uint32_t)(data & 0xFFFFFFFF));
+            furi_string_printf(item->item_str, "%s", furi_string_get_cstr(instance->tmp_string));
         }
+
     } while(false);
 
     furi_string_free(text);

+ 1 - 0
applications/main/subghz/subghz_i.h

@@ -45,6 +45,7 @@ struct SubGhzTxRx {
     SubGhzEnvironment* environment;
     SubGhzReceiver* receiver;
     SubGhzTransmitter* transmitter;
+    SubGhzProtocolFlag filter;
     SubGhzProtocolDecoderBase* decoder_result;
     FlipperFormat* fff_data;
 

+ 30 - 3
applications/main/subghz/views/receiver.c

@@ -12,6 +12,8 @@
 #define MENU_ITEMS 4u
 #define UNLOCK_CNT 3
 
+#define SUBGHZ_RAW_TRESHOLD_MIN -90.0f
+
 typedef struct {
     FuriString* item_str;
     uint8_t type;
@@ -59,8 +61,24 @@ typedef struct {
     uint16_t list_offset;
     uint16_t history_item;
     SubGhzViewReceiverBarShow bar_show;
+    uint8_t u_rssi;
 } SubGhzViewReceiverModel;
 
+void subghz_receiver_rssi(SubGhzViewReceiver* instance, float rssi) {
+    furi_assert(instance);
+    with_view_model(
+        instance->view,
+        SubGhzViewReceiverModel * model,
+        {
+            if(rssi < SUBGHZ_RAW_TRESHOLD_MIN) {
+                model->u_rssi = 0;
+            } else {
+                model->u_rssi = (uint8_t)(rssi - SUBGHZ_RAW_TRESHOLD_MIN);
+            }
+        },
+        true);
+}
+
 void subghz_view_receiver_set_lock(SubGhzViewReceiver* subghz_receiver, SubGhzLock lock) {
     furi_assert(subghz_receiver);
     subghz_receiver->lock_count = 0;
@@ -168,13 +186,22 @@ static void subghz_view_receiver_draw_frame(Canvas* canvas, uint16_t idx, bool s
     canvas_draw_dot(canvas, scrollbar ? 121 : 126, (0 + idx * FRAME_HEIGHT) + 11);
 }
 
+static void subghz_view_rssi_draw(Canvas* canvas, SubGhzViewReceiverModel* model) {
+    for(uint8_t i = 1; i < model->u_rssi; i++) {
+        if(i % 5) {
+            canvas_draw_dot(canvas, 46 + i, 50);
+            canvas_draw_dot(canvas, 47 + i, 51);
+            canvas_draw_dot(canvas, 46 + i, 52);
+        }
+    }
+}
+
 void subghz_view_receiver_draw(Canvas* canvas, SubGhzViewReceiverModel* model) {
     canvas_clear(canvas);
     canvas_set_color(canvas, ColorBlack);
     canvas_set_font(canvas, FontSecondary);
 
     elements_button_left(canvas, "Config");
-    canvas_draw_line(canvas, 46, 51, 125, 51);
 
     bool scrollbar = model->history_item > 4;
     FuriString* str_buff;
@@ -206,11 +233,11 @@ void subghz_view_receiver_draw(Canvas* canvas, SubGhzViewReceiverModel* model) {
     if(model->history_item == 0) {
         canvas_draw_icon(canvas, 0, 0, &I_Scanning_123x52);
         canvas_set_font(canvas, FontPrimary);
-        canvas_draw_str(canvas, 63, 46, "Scanning...");
-        canvas_draw_line(canvas, 46, 51, 125, 51);
+        canvas_draw_str(canvas, 63, 44, "Scanning...");
         canvas_set_font(canvas, FontSecondary);
     }
 
+    subghz_view_rssi_draw(canvas, model);
     switch(model->bar_show) {
     case SubGhzViewReceiverBarShowLock:
         canvas_draw_icon(canvas, 64, 55, &I_Lock_7x8);

+ 2 - 0
applications/main/subghz/views/receiver.h

@@ -8,6 +8,8 @@ typedef struct SubGhzViewReceiver SubGhzViewReceiver;
 
 typedef void (*SubGhzViewReceiverCallback)(SubGhzCustomEvent event, void* context);
 
+void subghz_receiver_rssi(SubGhzViewReceiver* instance, float rssi);
+
 void subghz_view_receiver_set_lock(SubGhzViewReceiver* subghz_receiver, SubGhzLock keyboard);
 
 void subghz_view_receiver_set_callback(

+ 0 - 1
applications/main/subghz/views/subghz_frequency_analyzer.c

@@ -79,7 +79,6 @@ void subghz_frequency_analyzer_draw_rssi(Canvas* canvas, uint8_t rssi, uint8_t x
 void subghz_frequency_analyzer_draw_log_rssi(Canvas* canvas, uint8_t rssi, uint8_t x, uint8_t y) {
     uint8_t column_height = 6;
     if(rssi) {
-        //rssi = rssi
         if(rssi > 54) rssi = 54;
         for(uint8_t i = 1; i < rssi; i++) {
             if(i % 5) {

+ 4 - 3
applications/main/subghz/views/transmitter.c

@@ -84,9 +84,10 @@ void subghz_view_transmitter_draw(Canvas* canvas, SubGhzViewTransmitterModel* mo
     canvas_clear(canvas);
     canvas_set_color(canvas, ColorBlack);
     canvas_set_font(canvas, FontSecondary);
-    elements_multiline_text(canvas, 0, 8, furi_string_get_cstr(model->key_str));
-    canvas_draw_str(canvas, 78, 8, furi_string_get_cstr(model->frequency_str));
-    canvas_draw_str(canvas, 113, 8, furi_string_get_cstr(model->preset_str));
+    elements_multiline_text_aligned(
+        canvas, 0, 0, AlignLeft, AlignTop, furi_string_get_cstr(model->key_str));
+    canvas_draw_str(canvas, 78, 7, furi_string_get_cstr(model->frequency_str));
+    canvas_draw_str(canvas, 113, 7, furi_string_get_cstr(model->preset_str));
     if(model->show_button) subghz_view_transmitter_button_right(canvas, "Send");
 }
 

+ 2 - 1
firmware/targets/f7/api_symbols.csv

@@ -1409,6 +1409,7 @@ Function,+,furi_kernel_unlock,int32_t,
 Function,+,furi_log_get_level,FuriLogLevel,
 Function,-,furi_log_init,void,
 Function,+,furi_log_print_format,void,"FuriLogLevel, const char*, const char*, ..."
+Function,+,furi_log_print_raw_format,void,"FuriLogLevel, const char*, ..."
 Function,+,furi_log_set_level,void,FuriLogLevel
 Function,-,furi_log_set_puts,void,FuriLogPuts
 Function,-,furi_log_set_timestamp,void,FuriLogTimestamp
@@ -2599,7 +2600,7 @@ Function,+,subghz_protocol_blocks_crc8le,uint8_t,"const uint8_t[], size_t, uint8
 Function,+,subghz_protocol_blocks_get_bit_array,_Bool,"uint8_t[], size_t"
 Function,+,subghz_protocol_blocks_get_hash_data,uint8_t,"SubGhzBlockDecoder*, size_t"
 Function,+,subghz_protocol_blocks_get_parity,uint8_t,"uint64_t, uint8_t"
-Function,+,subghz_protocol_blocks_get_upload,size_t,"uint8_t[], size_t, LevelDuration*, size_t, uint32_t"
+Function,+,subghz_protocol_blocks_get_upload_from_bit_array,size_t,"uint8_t[], size_t, LevelDuration*, size_t, uint32_t, SubGhzProtocolBlockAlignBit"
 Function,+,subghz_protocol_blocks_lfsr_digest16,uint16_t,"const uint8_t[], size_t, uint16_t, uint16_t"
 Function,+,subghz_protocol_blocks_lfsr_digest8,uint8_t,"const uint8_t[], size_t, uint8_t, uint8_t"
 Function,+,subghz_protocol_blocks_lfsr_digest8_reflect,uint8_t,"const uint8_t[], size_t, uint8_t, uint8_t"

+ 24 - 7
furi/core/log.c

@@ -28,27 +28,27 @@ void furi_log_print_format(FuriLogLevel level, const char* tag, const char* form
         FuriString* string;
         string = furi_string_alloc();
 
-        const char* color = FURI_LOG_CLR_RESET;
+        const char* color = _FURI_LOG_CLR_RESET;
         const char* log_letter = " ";
         switch(level) {
         case FuriLogLevelError:
-            color = FURI_LOG_CLR_E;
+            color = _FURI_LOG_CLR_E;
             log_letter = "E";
             break;
         case FuriLogLevelWarn:
-            color = FURI_LOG_CLR_W;
+            color = _FURI_LOG_CLR_W;
             log_letter = "W";
             break;
         case FuriLogLevelInfo:
-            color = FURI_LOG_CLR_I;
+            color = _FURI_LOG_CLR_I;
             log_letter = "I";
             break;
         case FuriLogLevelDebug:
-            color = FURI_LOG_CLR_D;
+            color = _FURI_LOG_CLR_D;
             log_letter = "D";
             break;
         case FuriLogLevelTrace:
-            color = FURI_LOG_CLR_T;
+            color = _FURI_LOG_CLR_T;
             log_letter = "T";
             break;
         default:
@@ -58,7 +58,7 @@ void furi_log_print_format(FuriLogLevel level, const char* tag, const char* form
         // Timestamp
         furi_string_printf(
             string,
-            "%lu %s[%s][%s] " FURI_LOG_CLR_RESET,
+            "%lu %s[%s][%s] " _FURI_LOG_CLR_RESET,
             furi_log.timestamp(),
             color,
             log_letter,
@@ -80,6 +80,23 @@ void furi_log_print_format(FuriLogLevel level, const char* tag, const char* form
     }
 }
 
+void furi_log_print_raw_format(FuriLogLevel level, const char* format, ...) {
+    if(level <= furi_log.log_level &&
+       furi_mutex_acquire(furi_log.mutex, FuriWaitForever) == FuriStatusOk) {
+        FuriString* string;
+        string = furi_string_alloc();
+        va_list args;
+        va_start(args, format);
+        furi_string_vprintf(string, format, args);
+        va_end(args);
+
+        furi_log.puts(furi_string_get_cstr(string));
+        furi_string_free(string);
+
+        furi_mutex_release(furi_log.mutex);
+    }
+}
+
 void furi_log_set_level(FuriLogLevel level) {
     if(level == FuriLogLevelDefault) {
         level = FURI_LOG_LEVEL_DEFAULT;

+ 40 - 15
furi/core/log.h

@@ -22,21 +22,21 @@ typedef enum {
     FuriLogLevelTrace = 6,
 } FuriLogLevel;
 
-#define FURI_LOG_CLR(clr) "\033[0;" clr "m"
-#define FURI_LOG_CLR_RESET "\033[0m"
-
-#define FURI_LOG_CLR_BLACK "30"
-#define FURI_LOG_CLR_RED "31"
-#define FURI_LOG_CLR_GREEN "32"
-#define FURI_LOG_CLR_BROWN "33"
-#define FURI_LOG_CLR_BLUE "34"
-#define FURI_LOG_CLR_PURPLE "35"
-
-#define FURI_LOG_CLR_E FURI_LOG_CLR(FURI_LOG_CLR_RED)
-#define FURI_LOG_CLR_W FURI_LOG_CLR(FURI_LOG_CLR_BROWN)
-#define FURI_LOG_CLR_I FURI_LOG_CLR(FURI_LOG_CLR_GREEN)
-#define FURI_LOG_CLR_D FURI_LOG_CLR(FURI_LOG_CLR_BLUE)
-#define FURI_LOG_CLR_T FURI_LOG_CLR(FURI_LOG_CLR_PURPLE)
+#define _FURI_LOG_CLR(clr) "\033[0;" clr "m"
+#define _FURI_LOG_CLR_RESET "\033[0m"
+
+#define _FURI_LOG_CLR_BLACK "30"
+#define _FURI_LOG_CLR_RED "31"
+#define _FURI_LOG_CLR_GREEN "32"
+#define _FURI_LOG_CLR_BROWN "33"
+#define _FURI_LOG_CLR_BLUE "34"
+#define _FURI_LOG_CLR_PURPLE "35"
+
+#define _FURI_LOG_CLR_E _FURI_LOG_CLR(_FURI_LOG_CLR_RED)
+#define _FURI_LOG_CLR_W _FURI_LOG_CLR(_FURI_LOG_CLR_BROWN)
+#define _FURI_LOG_CLR_I _FURI_LOG_CLR(_FURI_LOG_CLR_GREEN)
+#define _FURI_LOG_CLR_D _FURI_LOG_CLR(_FURI_LOG_CLR_BLUE)
+#define _FURI_LOG_CLR_T _FURI_LOG_CLR(_FURI_LOG_CLR_PURPLE)
 
 typedef void (*FuriLogPuts)(const char* data);
 typedef uint32_t (*FuriLogTimestamp)(void);
@@ -54,6 +54,15 @@ void furi_log_init();
 void furi_log_print_format(FuriLogLevel level, const char* tag, const char* format, ...)
     _ATTRIBUTE((__format__(__printf__, 3, 4)));
 
+/** Print log record
+ * 
+ * @param level 
+ * @param format 
+ * @param ... 
+ */
+void furi_log_print_raw_format(FuriLogLevel level, const char* format, ...)
+    _ATTRIBUTE((__format__(__printf__, 2, 3)));
+
 /** Set log level
  *
  * @param[in]  level  The level
@@ -95,6 +104,22 @@ void furi_log_set_timestamp(FuriLogTimestamp timestamp);
 #define FURI_LOG_T(tag, format, ...) \
     furi_log_print_format(FuriLogLevelTrace, tag, format, ##__VA_ARGS__)
 
+/** Log methods
+ *
+ * @param      format  The raw format 
+ * @param      ...     VA Args
+ */
+#define FURI_LOG_RAW_E(format, ...) \
+    furi_log_print_raw_format(FuriLogLevelError, format, ##__VA_ARGS__)
+#define FURI_LOG_RAW_W(format, ...) \
+    furi_log_print_raw_format(FuriLogLevelWarn, format, ##__VA_ARGS__)
+#define FURI_LOG_RAW_I(format, ...) \
+    furi_log_print_raw_format(FuriLogLevelInfo, format, ##__VA_ARGS__)
+#define FURI_LOG_RAW_D(format, ...) \
+    furi_log_print_raw_format(FuriLogLevelDebug, format, ##__VA_ARGS__)
+#define FURI_LOG_RAW_T(format, ...) \
+    furi_log_print_raw_format(FuriLogLevelTrace, format, ##__VA_ARGS__)
+
 #ifdef __cplusplus
 }
 #endif

+ 18 - 5
lib/subghz/blocks/encoder.c

@@ -2,6 +2,8 @@
 #include "math.h"
 #include <core/check.h>
 
+#include "furi.h"
+
 #define TAG "SubGhzBlockEncoder"
 
 void subghz_protocol_blocks_set_bit_array(
@@ -17,21 +19,32 @@ bool subghz_protocol_blocks_get_bit_array(uint8_t data_array[], size_t read_inde
     return bit_read(data_array[read_index_bit >> 3], 7 - (read_index_bit & 0x7));
 }
 
-size_t subghz_protocol_blocks_get_upload(
+size_t subghz_protocol_blocks_get_upload_from_bit_array(
     uint8_t data_array[],
     size_t count_bit_data_array,
     LevelDuration* upload,
     size_t max_size_upload,
-    uint32_t duration_bit) {
-    size_t index_bit = 0;
+    uint32_t duration_bit,
+    SubGhzProtocolBlockAlignBit align_bit) {
+    size_t bias_bit = 0;
     size_t size_upload = 0;
     uint32_t duration = duration_bit;
+
+    if(align_bit == SubGhzProtocolBlockAlignBitRight) {
+        if(count_bit_data_array & 0x7) {
+            bias_bit = 8 - (count_bit_data_array & 0x7);
+        }
+    }
+    size_t index_bit = bias_bit;
+
     bool last_bit = subghz_protocol_blocks_get_bit_array(data_array, index_bit++);
-    for(size_t i = 1; i < count_bit_data_array; i++) {
+    for(size_t i = 1 + bias_bit; i < count_bit_data_array + bias_bit; i++) {
         if(last_bit == subghz_protocol_blocks_get_bit_array(data_array, index_bit)) {
             duration += duration_bit;
         } else {
-            furi_assert(max_size_upload > size_upload);
+            if(size_upload > max_size_upload) {
+                furi_crash("SubGhz: Encoder buffer overflow");
+            }
             upload[size_upload++] = level_duration_make(
                 subghz_protocol_blocks_get_bit_array(data_array, index_bit - 1), duration);
             last_bit = !last_bit;

+ 9 - 2
lib/subghz/blocks/encoder.h

@@ -19,6 +19,11 @@ typedef struct {
 
 } SubGhzProtocolBlockEncoder;
 
+typedef enum {
+    SubGhzProtocolBlockAlignBitLeft,
+    SubGhzProtocolBlockAlignBitRight,
+} SubGhzProtocolBlockAlignBit;
+
 /**
  * Set data bit when encoding HEX array.
  * @param bit_value The value of the bit to be set
@@ -47,13 +52,15 @@ bool subghz_protocol_blocks_get_bit_array(uint8_t data_array[], size_t read_inde
  * @param upload Pointer to a LevelDuration
  * @param max_size_upload upload size, check not to overflow
  * @param duration_bit duration 1 bit
+ * @param align_bit alignment of useful bits in an array
  */
-size_t subghz_protocol_blocks_get_upload(
+size_t subghz_protocol_blocks_get_upload_from_bit_array(
     uint8_t data_array[],
     size_t count_bit_data_array,
     LevelDuration* upload,
     size_t max_size_upload,
-    uint32_t duration_bit);
+    uint32_t duration_bit,
+    SubGhzProtocolBlockAlignBit align_bit);
 
 #ifdef __cplusplus
 }

+ 1 - 1
lib/subghz/blocks/generic.c

@@ -100,7 +100,7 @@ bool subghz_block_generic_deserialize(SubGhzBlockGeneric* instance, FlipperForma
             FURI_LOG_E(TAG, "Missing Bit");
             break;
         }
-        instance->data_count_bit = (uint8_t)temp_data;
+        instance->data_count_bit = (uint16_t)temp_data;
 
         uint8_t key_data[sizeof(uint64_t)] = {0};
         if(!flipper_format_read_hex(flipper_format, "Key", key_data, sizeof(uint64_t))) {

+ 1 - 1
lib/subghz/blocks/generic.h

@@ -19,7 +19,7 @@ struct SubGhzBlockGeneric {
     const char* protocol_name;
     uint64_t data;
     uint32_t serial;
-    uint8_t data_count_bit;
+    uint16_t data_count_bit;
     uint8_t btn;
     uint32_t cnt;
 };

+ 1120 - 0
lib/subghz/protocols/bin_raw.c

@@ -0,0 +1,1120 @@
+#include "bin_raw.h"
+
+#include "../blocks/const.h"
+#include "../blocks/decoder.h"
+#include "../blocks/encoder.h"
+#include "../blocks/generic.h"
+#include "../blocks/math.h"
+#include <lib/toolbox/float_tools.h>
+#include <lib/toolbox/stream/stream.h>
+#include <lib/flipper_format/flipper_format_i.h>
+
+#define TAG "SubGhzProtocolBinRAW"
+
+//change very carefully, RAM ends at the most inopportune moment
+#define BIN_RAW_BUF_RAW_SIZE 2048
+#define BIN_RAW_BUF_DATA_SIZE 512
+
+#define BIN_RAW_THRESHOLD_RSSI -85.0f
+#define BIN_RAW_DELTA_RSSI 7.0f
+#define BIN_RAW_SEARCH_CLASSES 20
+#define BIN_RAW_TE_MIN_COUNT 40
+#define BIN_RAW_BUF_MIN_DATA_COUNT 128
+#define BIN_RAW_MAX_MARKUP_COUNT 20
+
+//#define BIN_RAW_DEBUG
+
+#ifdef BIN_RAW_DEBUG
+#define bin_raw_debug(...) FURI_LOG_RAW_D(__VA_ARGS__)
+#define bin_raw_debug_tag(tag, ...)                \
+    FURI_LOG_RAW_D("\033[0;32m[" tag "]\033[0m "); \
+    FURI_LOG_RAW_D(__VA_ARGS__)
+#else
+#define bin_raw_debug(...)
+#define bin_raw_debug_tag(...)
+#endif
+
+static const SubGhzBlockConst subghz_protocol_bin_raw_const = {
+    .te_short = 30,
+    .te_long = 65000,
+    .te_delta = 0,
+    .min_count_bit_for_found = 0,
+};
+
+typedef enum {
+    BinRAWDecoderStepReset = 0,
+    BinRAWDecoderStepWrite,
+    BinRAWDecoderStepBufFull,
+    BinRAWDecoderStepNoParse,
+} BinRAWDecoderStep;
+
+typedef enum {
+    BinRAWTypeUnknown = 0,
+    BinRAWTypeNoGap,
+    BinRAWTypeGap,
+    BinRAWTypeGapRecurring,
+    BinRAWTypeGapRolling,
+    BinRAWTypeGapUnknown,
+} BinRAWType;
+
+struct BinRAW_Markup {
+    uint16_t byte_bias;
+    uint16_t bit_count;
+};
+typedef struct BinRAW_Markup BinRAW_Markup;
+
+struct SubGhzProtocolDecoderBinRAW {
+    SubGhzProtocolDecoderBase base;
+
+    SubGhzBlockDecoder decoder;
+    SubGhzBlockGeneric generic;
+    int32_t* data_raw;
+    uint8_t* data;
+    BinRAW_Markup data_markup[BIN_RAW_MAX_MARKUP_COUNT];
+    size_t data_raw_ind;
+    uint32_t te;
+    float adaptive_threshold_rssi;
+};
+
+struct SubGhzProtocolEncoderBinRAW {
+    SubGhzProtocolEncoderBase base;
+
+    SubGhzProtocolBlockEncoder encoder;
+    SubGhzBlockGeneric generic;
+
+    uint8_t* data;
+    BinRAW_Markup data_markup[BIN_RAW_MAX_MARKUP_COUNT];
+    uint32_t te;
+};
+
+const SubGhzProtocolDecoder subghz_protocol_bin_raw_decoder = {
+    .alloc = subghz_protocol_decoder_bin_raw_alloc,
+    .free = subghz_protocol_decoder_bin_raw_free,
+
+    .feed = subghz_protocol_decoder_bin_raw_feed,
+    .reset = subghz_protocol_decoder_bin_raw_reset,
+
+    .get_hash_data = subghz_protocol_decoder_bin_raw_get_hash_data,
+    .serialize = subghz_protocol_decoder_bin_raw_serialize,
+    .deserialize = subghz_protocol_decoder_bin_raw_deserialize,
+    .get_string = subghz_protocol_decoder_bin_raw_get_string,
+};
+
+const SubGhzProtocolEncoder subghz_protocol_bin_raw_encoder = {
+    .alloc = subghz_protocol_encoder_bin_raw_alloc,
+    .free = subghz_protocol_encoder_bin_raw_free,
+
+    .deserialize = subghz_protocol_encoder_bin_raw_deserialize,
+    .stop = subghz_protocol_encoder_bin_raw_stop,
+    .yield = subghz_protocol_encoder_bin_raw_yield,
+};
+
+const SubGhzProtocol subghz_protocol_bin_raw = {
+    .name = SUBGHZ_PROTOCOL_BIN_RAW_NAME,
+    .type = SubGhzProtocolTypeStatic,
+#ifdef BIN_RAW_DEBUG
+    .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_315 | SubGhzProtocolFlag_868 |
+            SubGhzProtocolFlag_AM | SubGhzProtocolFlag_FM | SubGhzProtocolFlag_Decodable |
+            SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Send,
+#else
+    .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_315 | SubGhzProtocolFlag_868 |
+            SubGhzProtocolFlag_AM | SubGhzProtocolFlag_FM | SubGhzProtocolFlag_BinRAW |
+            SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Send,
+#endif
+    .decoder = &subghz_protocol_bin_raw_decoder,
+    .encoder = &subghz_protocol_bin_raw_encoder,
+};
+
+static uint16_t subghz_protocol_bin_raw_get_full_byte(uint16_t bit_count) {
+    if(bit_count & 0x7) {
+        return (bit_count >> 3) + 1;
+    } else {
+        return (bit_count >> 3);
+    }
+}
+
+void* subghz_protocol_encoder_bin_raw_alloc(SubGhzEnvironment* environment) {
+    UNUSED(environment);
+    SubGhzProtocolEncoderBinRAW* instance = malloc(sizeof(SubGhzProtocolEncoderBinRAW));
+
+    instance->base.protocol = &subghz_protocol_bin_raw;
+    instance->generic.protocol_name = instance->base.protocol->name;
+
+    instance->encoder.repeat = 10;
+    instance->encoder.size_upload = BIN_RAW_BUF_DATA_SIZE * 5;
+    instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration));
+    instance->data = malloc(instance->encoder.size_upload * sizeof(uint8_t));
+    memset(instance->data_markup, 0x00, BIN_RAW_MAX_MARKUP_COUNT * sizeof(BinRAW_Markup));
+    instance->encoder.is_running = false;
+    return instance;
+}
+
+void subghz_protocol_encoder_bin_raw_free(void* context) {
+    furi_assert(context);
+    SubGhzProtocolEncoderBinRAW* instance = context;
+    free(instance->encoder.upload);
+    free(instance->data);
+    free(instance);
+}
+
+/**
+ * Generating an upload from data.
+ * @param instance Pointer to a SubGhzProtocolEncoderBinRAW instance
+ * @return true On success
+ */
+static bool subghz_protocol_encoder_bin_raw_get_upload(SubGhzProtocolEncoderBinRAW* instance) {
+    furi_assert(instance);
+
+    //we glue all the pieces of the package into 1 long sequence with left alignment,
+    //in the uploaded data we have right alignment.
+
+    bin_raw_debug_tag(TAG, "Recovery of offset bits in sequences\r\n");
+    uint16_t i = 0;
+    uint16_t ind = 0;
+    bin_raw_debug("\tind  byte_bias\tbit_count\tbit_bias\r\n");
+    while((i < BIN_RAW_MAX_MARKUP_COUNT) && (instance->data_markup[i].bit_count != 0)) {
+        uint8_t bit_bias =
+            subghz_protocol_bin_raw_get_full_byte(instance->data_markup[i].bit_count) * 8 -
+            instance->data_markup[i].bit_count;
+        bin_raw_debug(
+            "\t%d\t%d\t%d :\t\t%d\r\n",
+            i,
+            instance->data_markup[i].byte_bias,
+            instance->data_markup[i].bit_count,
+            bit_bias);
+        for(uint16_t y = instance->data_markup[i].byte_bias * 8;
+            y < instance->data_markup[i].byte_bias * 8 +
+                    subghz_protocol_bin_raw_get_full_byte(instance->data_markup[i].bit_count) * 8 -
+                    bit_bias;
+            y++) {
+            subghz_protocol_blocks_set_bit_array(
+                subghz_protocol_blocks_get_bit_array(instance->data, y + bit_bias),
+                instance->data,
+                ind++,
+                BIN_RAW_BUF_DATA_SIZE);
+        }
+        i++;
+    }
+    bin_raw_debug("\r\n");
+#ifdef BIN_RAW_DEBUG
+    bin_raw_debug_tag(TAG, "Restored Sequence left aligned\r\n");
+    for(uint16_t y = 0; y < subghz_protocol_bin_raw_get_full_byte(ind); y++) {
+        bin_raw_debug("%02X ", instance->data[y]);
+    }
+    bin_raw_debug("\r\n\tbin_count_result= %d\r\n\r\n", ind);
+
+    bin_raw_debug_tag(
+        TAG, "Maximum levels encoded in upload %zu\r\n", instance->encoder.size_upload);
+#endif
+    instance->encoder.size_upload = subghz_protocol_blocks_get_upload_from_bit_array(
+        instance->data,
+        ind,
+        instance->encoder.upload,
+        instance->encoder.size_upload,
+        instance->te,
+        SubGhzProtocolBlockAlignBitLeft);
+
+    bin_raw_debug_tag(TAG, "The result %zu is levels\r\n", instance->encoder.size_upload);
+    bin_raw_debug_tag(TAG, "Remaining free memory %zu\r\n", memmgr_get_free_heap());
+    return true;
+}
+
+bool subghz_protocol_encoder_bin_raw_deserialize(void* context, FlipperFormat* flipper_format) {
+    furi_assert(context);
+    SubGhzProtocolEncoderBinRAW* instance = context;
+
+    bool res = false;
+    uint32_t temp_data = 0;
+
+    do {
+        if(!flipper_format_rewind(flipper_format)) {
+            FURI_LOG_E(TAG, "Rewind error");
+            break;
+        }
+        if(!flipper_format_read_uint32(flipper_format, "Bit", (uint32_t*)&temp_data, 1)) {
+            FURI_LOG_E(TAG, "Missing Bit");
+            break;
+        }
+
+        instance->generic.data_count_bit = (uint16_t)temp_data;
+
+        if(!flipper_format_read_uint32(flipper_format, "TE", (uint32_t*)&instance->te, 1)) {
+            FURI_LOG_E(TAG, "Missing TE");
+            break;
+        }
+
+        temp_data = 0;
+        uint16_t ind = 0;
+        uint16_t byte_bias = 0;
+        uint16_t byte_count = 0;
+        memset(instance->data_markup, 0x00, BIN_RAW_MAX_MARKUP_COUNT * sizeof(BinRAW_Markup));
+        while(flipper_format_read_uint32(flipper_format, "Bit_RAW", (uint32_t*)&temp_data, 1)) {
+            if(ind >= BIN_RAW_MAX_MARKUP_COUNT) {
+                FURI_LOG_E(TAG, "Markup overflow");
+                break;
+            }
+            byte_count += subghz_protocol_bin_raw_get_full_byte(temp_data);
+            if(byte_count > BIN_RAW_BUF_DATA_SIZE) {
+                FURI_LOG_E(TAG, "Receive buffer overflow");
+                break;
+            }
+
+            instance->data_markup[ind].bit_count = temp_data;
+            instance->data_markup[ind].byte_bias = byte_bias;
+            byte_bias += subghz_protocol_bin_raw_get_full_byte(temp_data);
+
+            if(!flipper_format_read_hex(
+                   flipper_format,
+                   "Data_RAW",
+                   instance->data + instance->data_markup[ind].byte_bias,
+                   subghz_protocol_bin_raw_get_full_byte(temp_data))) {
+                instance->data_markup[ind].bit_count = 0;
+                FURI_LOG_E(TAG, "Missing Data_RAW");
+                break;
+            }
+            ind++;
+        }
+
+#ifdef BIN_RAW_DEBUG
+        uint16_t i = 0;
+        bin_raw_debug_tag(TAG, "Download data to encoder\r\n");
+        bin_raw_debug("\tind  byte_bias\tbit_count\t\tbin_data");
+        while((i < BIN_RAW_MAX_MARKUP_COUNT) && (instance->data_markup[i].bit_count != 0)) {
+            bin_raw_debug(
+                "\r\n\t%d\t%d\t%d :\t",
+                i,
+                instance->data_markup[i].byte_bias,
+                instance->data_markup[i].bit_count);
+            for(uint16_t y = instance->data_markup[i].byte_bias;
+                y < instance->data_markup[i].byte_bias +
+                        subghz_protocol_bin_raw_get_full_byte(instance->data_markup[i].bit_count);
+                y++) {
+                bin_raw_debug("%02X ", instance->data[y]);
+            }
+            i++;
+        }
+        bin_raw_debug("\r\n\r\n");
+#endif
+        if(!flipper_format_rewind(flipper_format)) {
+            FURI_LOG_E(TAG, "Rewind error");
+            break;
+        }
+        //optional parameter parameter
+        flipper_format_read_uint32(
+            flipper_format, "Repeat", (uint32_t*)&instance->encoder.repeat, 1);
+
+        if(!subghz_protocol_encoder_bin_raw_get_upload(instance)) break;
+        instance->encoder.is_running = true;
+
+        res = true;
+    } while(0);
+
+    return res;
+}
+
+void subghz_protocol_encoder_bin_raw_stop(void* context) {
+    SubGhzProtocolEncoderBinRAW* instance = context;
+    instance->encoder.is_running = false;
+}
+
+LevelDuration subghz_protocol_encoder_bin_raw_yield(void* context) {
+    SubGhzProtocolEncoderBinRAW* instance = context;
+
+    if(instance->encoder.repeat == 0 || !instance->encoder.is_running) {
+        instance->encoder.is_running = 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_bin_raw_alloc(SubGhzEnvironment* environment) {
+    UNUSED(environment);
+    SubGhzProtocolDecoderBinRAW* instance = malloc(sizeof(SubGhzProtocolDecoderBinRAW));
+    instance->base.protocol = &subghz_protocol_bin_raw;
+    instance->generic.protocol_name = instance->base.protocol->name;
+    instance->data_raw_ind = 0;
+    instance->data_raw = malloc(BIN_RAW_BUF_RAW_SIZE * sizeof(int32_t));
+    instance->data = malloc(BIN_RAW_BUF_RAW_SIZE * sizeof(uint8_t));
+    memset(instance->data_markup, 0x00, BIN_RAW_MAX_MARKUP_COUNT * sizeof(BinRAW_Markup));
+    instance->adaptive_threshold_rssi = BIN_RAW_THRESHOLD_RSSI;
+    return instance;
+}
+
+void subghz_protocol_decoder_bin_raw_free(void* context) {
+    furi_assert(context);
+    SubGhzProtocolDecoderBinRAW* instance = context;
+    free(instance->data_raw);
+    free(instance->data);
+    free(instance);
+}
+
+void subghz_protocol_decoder_bin_raw_reset(void* context) {
+    furi_assert(context);
+    SubGhzProtocolDecoderBinRAW* instance = context;
+#ifdef BIN_RAW_DEBUG
+    UNUSED(instance);
+#else
+    instance->decoder.parser_step = BinRAWDecoderStepNoParse;
+    instance->data_raw_ind = 0;
+#endif
+}
+
+void subghz_protocol_decoder_bin_raw_feed(void* context, bool level, uint32_t duration) {
+    furi_assert(context);
+    SubGhzProtocolDecoderBinRAW* instance = context;
+
+    if(instance->decoder.parser_step == BinRAWDecoderStepWrite) {
+        if(instance->data_raw_ind == BIN_RAW_BUF_RAW_SIZE) {
+            instance->decoder.parser_step = BinRAWDecoderStepBufFull;
+        } else {
+            instance->data_raw[instance->data_raw_ind++] = (level ? duration : -duration);
+        }
+    }
+}
+
+/** 
+ * Analysis of received data
+ * @param instance Pointer to a SubGhzProtocolDecoderBinRAW* instance
+ */
+static bool
+    subghz_protocol_bin_raw_check_remote_controller(SubGhzProtocolDecoderBinRAW* instance) {
+    struct {
+        float data;
+        uint16_t count;
+    } classes[BIN_RAW_SEARCH_CLASSES];
+
+    size_t ind = 0;
+
+    memset(classes, 0x00, sizeof(classes));
+
+    uint16_t data_markup_ind = 0;
+    memset(instance->data_markup, 0x00, BIN_RAW_MAX_MARKUP_COUNT * sizeof(BinRAW_Markup));
+
+    if(instance->data_raw_ind < 512) {
+        ind =
+            instance->data_raw_ind -
+            100; //there is usually garbage at the end of the record, we exclude it from the classification
+    } else {
+        ind = 512;
+    }
+
+    //sort the durations to find the shortest correlated interval
+    for(size_t i = 0; i < ind; i++) {
+        for(size_t k = 0; k < BIN_RAW_SEARCH_CLASSES; k++) {
+            if(classes[k].count == 0) {
+                classes[k].data = (float)(abs(instance->data_raw[i]));
+                classes[k].count++;
+                break;
+            } else if(
+                DURATION_DIFF((float)(abs(instance->data_raw[i])), (classes[k].data)) <
+                (classes[k].data / 4)) { //if the test value does not differ by more than 25%
+                classes[k].data += ((float)(abs(instance->data_raw[i])) - classes[k].data) *
+                                   0.05f; //running average k=0.05
+                classes[k].count++;
+                break;
+            }
+        }
+    }
+
+    // if(classes[BIN_RAW_SEARCH_CLASSES - 1].count != 0) {
+    //     //filling the classifier, it means that they received an unclean signal
+    //     return false;
+    // }
+
+    //looking for the minimum te with an occurrence greater than BIN_RAW_TE_MIN_COUNT
+    instance->te = subghz_protocol_bin_raw_const.te_long * 2;
+
+    bool te_ok = false;
+    uint16_t gap_ind = 0;
+    uint16_t gap_delta = 0;
+    uint32_t gap = 0;
+    int data_temp = 0;
+    BinRAWType bin_raw_type = BinRAWTypeUnknown;
+
+    //sort by number of occurrences
+    bool swap = true;
+    while(swap) {
+        swap = false;
+        for(size_t i = 1; i < BIN_RAW_SEARCH_CLASSES; i++) {
+            if(classes[i].count > classes[i - 1].count) {
+                uint32_t data = classes[i - 1].data;
+                uint32_t count = classes[i - 1].count;
+                classes[i - 1].data = classes[i].data;
+                classes[i - 1].count = classes[i].count;
+                classes[i].data = data;
+                classes[i].count = count;
+                swap = true;
+            }
+        }
+    }
+#ifdef BIN_RAW_DEBUG
+    bin_raw_debug_tag(TAG, "Sorted durations\r\n");
+    bin_raw_debug("\t\tind\tcount\tus\r\n");
+    for(size_t k = 0; k < BIN_RAW_SEARCH_CLASSES; k++) {
+        bin_raw_debug("\t\t%zu\t%u\t%lu\r\n", k, classes[k].count, (uint32_t)classes[k].data);
+    }
+    bin_raw_debug("\r\n");
+#endif
+    if((classes[0].count > BIN_RAW_TE_MIN_COUNT) && (classes[1].count == 0)) {
+        //adopted only the preamble
+        instance->te = (uint32_t)classes[0].data;
+        te_ok = true;
+        gap = 0; //gap no
+    } else {
+        //take the 2 most common durations
+        //check that there are enough
+        if((classes[0].count < BIN_RAW_TE_MIN_COUNT) ||
+           (classes[1].count < (BIN_RAW_TE_MIN_COUNT >> 1)))
+            return false;
+        //arrange the first 2 date values in ascending order
+        if(classes[0].data > classes[1].data) {
+            uint32_t data = classes[1].data;
+            classes[0].data = classes[1].data;
+            classes[1].data = data;
+        }
+
+        //determine the value to be corrected
+        for(uint8_t k = 1; k < 5; k++) {
+            float delta = (classes[1].data / (classes[0].data / k));
+            bin_raw_debug_tag(TAG, "K_div= %f\r\n", (double)(delta));
+            delta -= (uint32_t)delta;
+
+            if((delta < 0.20f) || (delta > 0.80f)) {
+                instance->te = (uint32_t)classes[0].data / k;
+                bin_raw_debug_tag(TAG, "K= %d\r\n", k);
+                te_ok = true; //found a correlated duration
+                break;
+            }
+        }
+        if(!te_ok) {
+            //did not find the minimum TE satisfying the condition
+            return false;
+        }
+        bin_raw_debug_tag(TAG, "TE= %lu\r\n\r\n", instance->te);
+
+        //looking for a gap
+        for(size_t k = 2; k < BIN_RAW_SEARCH_CLASSES; k++) {
+            if((classes[k].count > 2) && (classes[k].data > gap)) {
+                gap = (uint32_t)classes[k].data;
+                gap_delta = gap / 5; //calculate 20% deviation from ideal value
+            }
+        }
+
+        if((gap / instance->te) <
+           10) { //make an assumption, the longest gap should be more than 10 TE
+            gap = 0; //check that our signal has a gap greater than 10*TE
+            bin_raw_type = BinRAWTypeNoGap;
+        } else {
+            bin_raw_type = BinRAWTypeGap;
+            //looking for the last occurrence of gap
+            ind = instance->data_raw_ind - 1;
+            while((ind > 0) && (DURATION_DIFF(abs(instance->data_raw[ind]), gap) > gap_delta)) {
+                ind--;
+            }
+            gap_ind = ind;
+        }
+    }
+
+    //if we consider that there is a gap, then we divide the signal with respect to this gap
+    //processing input data from the end
+    if(bin_raw_type == BinRAWTypeGap) {
+        bin_raw_debug_tag(TAG, "Tinted sequence\r\n");
+        ind = (BIN_RAW_BUF_DATA_SIZE * 8);
+        uint16_t bit_count = 0;
+        do {
+            gap_ind--;
+            data_temp = (int)(round((float)(instance->data_raw[gap_ind]) / instance->te));
+            bin_raw_debug("%d ", data_temp);
+            if(data_temp == 0) bit_count++; //there is noise in the package
+            for(size_t i = 0; i < abs(data_temp); i++) {
+                bit_count++;
+                if(ind) {
+                    ind--;
+                } else {
+                    break;
+                }
+                if(data_temp > 0) {
+                    subghz_protocol_blocks_set_bit_array(
+                        true, instance->data, ind, BIN_RAW_BUF_DATA_SIZE);
+                } else {
+                    subghz_protocol_blocks_set_bit_array(
+                        false, instance->data, ind, BIN_RAW_BUF_DATA_SIZE);
+                }
+            }
+            //split into full bytes if gap is caught
+            if(DURATION_DIFF(abs(instance->data_raw[gap_ind]), gap) < gap_delta) {
+                instance->data_markup[data_markup_ind].byte_bias = ind >> 3;
+                instance->data_markup[data_markup_ind++].bit_count = bit_count;
+                bit_count = 0;
+
+                if(data_markup_ind == BIN_RAW_MAX_MARKUP_COUNT) break;
+                ind &= 0xFFFFFFF8; //jump to the pre whole byte
+            }
+        } while(gap_ind != 0);
+        if((data_markup_ind != BIN_RAW_MAX_MARKUP_COUNT) && (ind != 0)) {
+            instance->data_markup[data_markup_ind].byte_bias = ind >> 3;
+            instance->data_markup[data_markup_ind++].bit_count = bit_count;
+        }
+
+        bin_raw_debug("\r\n\t count bit= %zu\r\n\r\n", (BIN_RAW_BUF_DATA_SIZE * 8) - ind);
+
+        //reset the classifier and classify the received data
+        memset(classes, 0x00, sizeof(classes));
+
+        bin_raw_debug_tag(TAG, "Sort the found pieces by the number of bits in them\r\n");
+        for(size_t i = 0; i < data_markup_ind; i++) {
+            for(size_t k = 0; k < BIN_RAW_SEARCH_CLASSES; k++) {
+                if(classes[k].count == 0) {
+                    classes[k].data = instance->data_markup[i].bit_count;
+                    classes[k].count++;
+                    break;
+                } else if(instance->data_markup[i].bit_count == (uint16_t)classes[k].data) {
+                    classes[k].count++;
+                    break;
+                }
+            }
+        }
+
+#ifdef BIN_RAW_DEBUG
+        bin_raw_debug("\t\tind\tcount\tus\r\n");
+        for(size_t k = 0; k < BIN_RAW_SEARCH_CLASSES; k++) {
+            bin_raw_debug("\t\t%zu\t%u\t%lu\r\n", k, classes[k].count, (uint32_t)classes[k].data);
+        }
+        bin_raw_debug("\r\n");
+#endif
+
+        //choose the value with the maximum repetition
+        data_temp = 0;
+        for(size_t i = 0; i < BIN_RAW_SEARCH_CLASSES; i++) {
+            if((classes[i].count > 1) && (data_temp < classes[i].count))
+                data_temp = (int)classes[i].data;
+        }
+
+        //if(data_markup_ind == 0) return false;
+
+#ifdef BIN_RAW_DEBUG
+        //output in reverse order
+        bin_raw_debug_tag(TAG, "Found sequences\r\n");
+        bin_raw_debug("\tind  byte_bias\tbit_count\t\tbin_data\r\n");
+        uint16_t data_markup_ind_temp = data_markup_ind;
+        if(data_markup_ind) {
+            data_markup_ind_temp--;
+            for(size_t i = (ind / 8); i < BIN_RAW_BUF_DATA_SIZE; i++) {
+                if(instance->data_markup[data_markup_ind_temp].byte_bias == i) {
+                    bin_raw_debug(
+                        "\r\n\t%d\t%d\t%d :\t",
+                        data_markup_ind_temp,
+                        instance->data_markup[data_markup_ind_temp].byte_bias,
+                        instance->data_markup[data_markup_ind_temp].bit_count);
+                    if(data_markup_ind_temp != 0) data_markup_ind_temp--;
+                }
+                bin_raw_debug("%02X ", instance->data[i]);
+            }
+            bin_raw_debug("\r\n\r\n");
+        }
+        //compare data in chunks with the same number of bits
+        bin_raw_debug_tag(TAG, "Analyze sequences of long %d bit\r\n\r\n", data_temp);
+#endif
+
+        //if(data_temp == 0) data_temp = (int)classes[0].data;
+
+        if(data_temp != 0) {
+            //check that data in transmission is repeated every packet
+            for(uint16_t i = 0; i < data_markup_ind - 1; i++) {
+                if((instance->data_markup[i].bit_count == data_temp) &&
+                   (instance->data_markup[i + 1].bit_count == data_temp)) {
+                    //if the number of bits in adjacent parcels is the same, compare the data
+                    bin_raw_debug_tag(
+                        TAG,
+                        "Comparison of neighboring sequences ind_1=%d ind_2=%d %02X=%02X .... %02X=%02X\r\n",
+                        i,
+                        i + 1,
+                        instance->data[instance->data_markup[i].byte_bias],
+                        instance->data[instance->data_markup[i + 1].byte_bias],
+                        instance->data
+                            [instance->data_markup[i].byte_bias +
+                             subghz_protocol_bin_raw_get_full_byte(
+                                 instance->data_markup[i].bit_count) -
+                             1],
+                        instance->data
+                            [instance->data_markup[i + 1].byte_bias +
+                             subghz_protocol_bin_raw_get_full_byte(
+                                 instance->data_markup[i + 1].bit_count) -
+                             1]);
+
+                    uint16_t byte_count =
+                        subghz_protocol_bin_raw_get_full_byte(instance->data_markup[i].bit_count);
+                    if(memcmp(
+                           instance->data + instance->data_markup[i].byte_bias,
+                           instance->data + instance->data_markup[i + 1].byte_bias,
+                           byte_count - 1) == 0) {
+                        bin_raw_debug_tag(
+                            TAG, "Match found bin_raw_type=BinRAWTypeGapRecurring\r\n\r\n");
+
+                        //place in 1 element the offset to valid data
+                        instance->data_markup[0].bit_count = instance->data_markup[i].bit_count;
+                        instance->data_markup[0].byte_bias = instance->data_markup[i].byte_bias;
+                        //markup end sign
+                        instance->data_markup[1].bit_count = 0;
+                        instance->data_markup[1].byte_bias = 0;
+
+                        bin_raw_type = BinRAWTypeGapRecurring;
+                        i = data_markup_ind;
+                        break;
+                    }
+                }
+            }
+        }
+
+        if(bin_raw_type == BinRAWTypeGap) {
+            // check that retry occurs every n packets
+            for(uint16_t i = 0; i < data_markup_ind - 2; i++) {
+                uint16_t byte_count =
+                    subghz_protocol_bin_raw_get_full_byte(instance->data_markup[i].bit_count);
+                for(uint16_t y = i + 1; y < data_markup_ind - 1; y++) {
+                    bin_raw_debug_tag(
+                        TAG,
+                        "Comparison every N sequences ind_1=%d ind_2=%d %02X=%02X .... %02X=%02X\r\n",
+                        i,
+                        y,
+                        instance->data[instance->data_markup[i].byte_bias],
+                        instance->data[instance->data_markup[y].byte_bias],
+                        instance->data
+                            [instance->data_markup[i].byte_bias +
+                             subghz_protocol_bin_raw_get_full_byte(
+                                 instance->data_markup[i].bit_count) -
+                             1],
+                        instance->data
+                            [instance->data_markup[y].byte_bias +
+                             subghz_protocol_bin_raw_get_full_byte(
+                                 instance->data_markup[y].bit_count) -
+                             1]);
+
+                    if(byte_count ==
+                       subghz_protocol_bin_raw_get_full_byte(
+                           instance->data_markup[y].bit_count)) { //if the length in bytes matches
+
+                        if((memcmp(
+                                instance->data + instance->data_markup[i].byte_bias,
+                                instance->data + instance->data_markup[y].byte_bias,
+                                byte_count - 1) == 0) &&
+                           (memcmp(
+                                instance->data + instance->data_markup[i + 1].byte_bias,
+                                instance->data + instance->data_markup[y + 1].byte_bias,
+                                byte_count - 1) == 0)) {
+                            uint8_t index = 0;
+#ifdef BIN_RAW_DEBUG
+                            bin_raw_debug_tag(
+                                TAG, "Match found bin_raw_type=BinRAWTypeGapRolling\r\n\r\n");
+                            //output in reverse order
+                            bin_raw_debug("\tind  byte_bias\tbit_count\t\tbin_data\r\n");
+                            index = y - 1;
+                            for(size_t z = instance->data_markup[y].byte_bias + byte_count;
+                                z < instance->data_markup[i].byte_bias + byte_count;
+                                z++) {
+                                if(instance->data_markup[index].byte_bias == z) {
+                                    bin_raw_debug(
+                                        "\r\n\t%d\t%d\t%d :\t",
+                                        index,
+                                        instance->data_markup[index].byte_bias,
+                                        instance->data_markup[index].bit_count);
+                                    if(index != 0) index--;
+                                }
+                                bin_raw_debug("%02X ", instance->data[z]);
+                            }
+
+                            bin_raw_debug("\r\n\r\n");
+#endif
+                            //todo can be optimized
+                            BinRAW_Markup markup_temp[BIN_RAW_MAX_MARKUP_COUNT];
+                            memcpy(
+                                markup_temp,
+                                instance->data_markup,
+                                BIN_RAW_MAX_MARKUP_COUNT * sizeof(BinRAW_Markup));
+                            memset(
+                                instance->data_markup,
+                                0x00,
+                                BIN_RAW_MAX_MARKUP_COUNT * sizeof(BinRAW_Markup));
+
+                            for(index = i; index < y; index++) {
+                                instance->data_markup[index - i].bit_count =
+                                    markup_temp[y - index - 1].bit_count;
+                                instance->data_markup[index - i].byte_bias =
+                                    markup_temp[y - index - 1].byte_bias;
+                            }
+
+                            bin_raw_type = BinRAWTypeGapRolling;
+                            i = data_markup_ind;
+                            break;
+                        }
+                    }
+                }
+            }
+        }
+        //todo can be optimized
+        if(bin_raw_type == BinRAWTypeGap) {
+            if(data_temp != 0) { //there are sequences with the same number of bits
+
+                BinRAW_Markup markup_temp[BIN_RAW_MAX_MARKUP_COUNT];
+                memcpy(
+                    markup_temp,
+                    instance->data_markup,
+                    BIN_RAW_MAX_MARKUP_COUNT * sizeof(BinRAW_Markup));
+                memset(
+                    instance->data_markup, 0x00, BIN_RAW_MAX_MARKUP_COUNT * sizeof(BinRAW_Markup));
+                uint16_t byte_count = subghz_protocol_bin_raw_get_full_byte(data_temp);
+                uint16_t index = 0;
+                uint16_t it = BIN_RAW_MAX_MARKUP_COUNT;
+                do {
+                    it--;
+                    if(subghz_protocol_bin_raw_get_full_byte(markup_temp[it].bit_count) ==
+                       byte_count) {
+                        instance->data_markup[index].bit_count = markup_temp[it].bit_count;
+                        instance->data_markup[index].byte_bias = markup_temp[it].byte_bias;
+                        index++;
+                        bin_raw_type = BinRAWTypeGapUnknown;
+                    }
+                } while(it != 0);
+            }
+        }
+
+        if(bin_raw_type != BinRAWTypeGap)
+            return true;
+        else
+            return false;
+
+    } else {
+        // if bin_raw_type == BinRAWTypeGap
+        bin_raw_debug_tag(TAG, "Sequence analysis without gap\r\n");
+        ind = 0;
+        for(size_t i = 0; i < instance->data_raw_ind; i++) {
+            int data_temp = (int)(round((float)(instance->data_raw[i]) / instance->te));
+            if(data_temp == 0) break; //found an interval 2 times shorter than TE, this is noise
+            bin_raw_debug("%d  ", data_temp);
+
+            for(size_t k = 0; k < abs(data_temp); k++) {
+                if(data_temp > 0) {
+                    subghz_protocol_blocks_set_bit_array(
+                        true, instance->data, ind++, BIN_RAW_BUF_DATA_SIZE);
+                } else {
+                    subghz_protocol_blocks_set_bit_array(
+                        false, instance->data, ind++, BIN_RAW_BUF_DATA_SIZE);
+                }
+                if(ind == BIN_RAW_BUF_DATA_SIZE * 8) {
+                    i = instance->data_raw_ind;
+                    break;
+                }
+            }
+        }
+
+        if(ind != 0) {
+            bin_raw_type = BinRAWTypeNoGap;
+            //right alignment
+            uint8_t bit_bias = (subghz_protocol_bin_raw_get_full_byte(ind) << 3) - ind;
+#ifdef BIN_RAW_DEBUG
+            bin_raw_debug(
+                "\r\n\t count bit= %zu\tcount full byte= %d\tbias bit= %d\r\n\r\n",
+                ind,
+                subghz_protocol_bin_raw_get_full_byte(ind),
+                bit_bias);
+
+            for(size_t i = 0; i < subghz_protocol_bin_raw_get_full_byte(ind); i++) {
+                bin_raw_debug("%02X ", instance->data[i]);
+            }
+            bin_raw_debug("\r\n\r\n");
+#endif
+            //checking that the received sequence contains useful data
+            bool data_check = false;
+            for(size_t i = 0; i < subghz_protocol_bin_raw_get_full_byte(ind); i++) {
+                if(instance->data[i] != 0) {
+                    data_check = true;
+                    break;
+                }
+            }
+
+            if(data_check) {
+                for(size_t i = subghz_protocol_bin_raw_get_full_byte(ind) - 1; i > 0; i--) {
+                    instance->data[i] = (instance->data[i - 1] << (8 - bit_bias)) |
+                                        (instance->data[i] >> bit_bias);
+                }
+                instance->data[0] = (instance->data[0] >> bit_bias);
+
+#ifdef BIN_RAW_DEBUG
+                bin_raw_debug_tag(TAG, "Data right alignment\r\n");
+                for(size_t i = 0; i < subghz_protocol_bin_raw_get_full_byte(ind); i++) {
+                    bin_raw_debug("%02X ", instance->data[i]);
+                }
+                bin_raw_debug("\r\n\r\n");
+#endif
+                instance->data_markup[0].bit_count = ind;
+                instance->data_markup[0].byte_bias = 0;
+
+                return true;
+            } else {
+                return false;
+            }
+        } else {
+            return false;
+        }
+    }
+    return false;
+}
+
+void subghz_protocol_decoder_bin_raw_data_input_rssi(
+    SubGhzProtocolDecoderBinRAW* instance,
+    float rssi) {
+    furi_assert(instance);
+    switch(instance->decoder.parser_step) {
+    case BinRAWDecoderStepReset:
+
+        bin_raw_debug("%ld %ld :", (int32_t)rssi, (int32_t)instance->adaptive_threshold_rssi);
+        if(rssi > (instance->adaptive_threshold_rssi + BIN_RAW_DELTA_RSSI)) {
+            instance->data_raw_ind = 0;
+            memset(instance->data_raw, 0x00, BIN_RAW_BUF_RAW_SIZE * sizeof(int32_t));
+            memset(instance->data, 0x00, BIN_RAW_BUF_RAW_SIZE * sizeof(uint8_t));
+            instance->decoder.parser_step = BinRAWDecoderStepWrite;
+            bin_raw_debug_tag(TAG, "RSSI\r\n");
+        } else {
+            //adaptive noise level adjustment
+            instance->adaptive_threshold_rssi += (rssi - instance->adaptive_threshold_rssi) * 0.2f;
+        }
+        break;
+
+    case BinRAWDecoderStepBufFull:
+    case BinRAWDecoderStepWrite:
+#ifdef BIN_RAW_DEBUG
+        if(rssi > (instance->adaptive_threshold_rssi + BIN_RAW_DELTA_RSSI)) {
+            bin_raw_debug("\033[0;32m%ld \033[0m ", (int32_t)rssi);
+        } else {
+            bin_raw_debug("%ld ", (int32_t)rssi);
+        }
+#endif
+        if(rssi < instance->adaptive_threshold_rssi + BIN_RAW_DELTA_RSSI) {
+#ifdef BIN_RAW_DEBUG
+            bin_raw_debug("\r\n\r\n");
+            bin_raw_debug_tag(TAG, "Data for analysis, positive high, negative low, us\r\n");
+            for(size_t i = 0; i < instance->data_raw_ind; i++) {
+                bin_raw_debug("%ld ", instance->data_raw[i]);
+            }
+            bin_raw_debug("\r\n\t count data= %zu\r\n\r\n", instance->data_raw_ind);
+#endif
+            instance->decoder.parser_step = BinRAWDecoderStepReset;
+            instance->generic.data_count_bit = 0;
+            if(instance->data_raw_ind >= BIN_RAW_BUF_MIN_DATA_COUNT) {
+                if(subghz_protocol_bin_raw_check_remote_controller(instance)) {
+                    bin_raw_debug_tag(TAG, "Sequence found\r\n");
+                    bin_raw_debug("\tind  byte_bias\tbit_count\t\tbin_data");
+                    uint16_t i = 0;
+                    while((i < BIN_RAW_MAX_MARKUP_COUNT) &&
+                          (instance->data_markup[i].bit_count != 0)) {
+                        instance->generic.data_count_bit += instance->data_markup[i].bit_count;
+#ifdef BIN_RAW_DEBUG
+                        bin_raw_debug(
+                            "\r\n\t%d\t%d\t%d :\t",
+                            i,
+                            instance->data_markup[i].byte_bias,
+                            instance->data_markup[i].bit_count);
+                        for(uint16_t y = instance->data_markup[i].byte_bias;
+                            y < instance->data_markup[i].byte_bias +
+                                    subghz_protocol_bin_raw_get_full_byte(
+                                        instance->data_markup[i].bit_count);
+                            y++) {
+                            bin_raw_debug("%02X ", instance->data[y]);
+                        }
+#endif
+                        i++;
+                    }
+                    bin_raw_debug("\r\n");
+                    if(instance->base.callback)
+                        instance->base.callback(&instance->base, instance->base.context);
+                }
+            }
+        }
+        break;
+
+    default:
+        //if instance->decoder.parser_step == BinRAWDecoderStepNoParse or others, restore the initial state
+        if(rssi < instance->adaptive_threshold_rssi + BIN_RAW_DELTA_RSSI) {
+            instance->decoder.parser_step = BinRAWDecoderStepReset;
+        }
+        break;
+    }
+}
+
+uint8_t subghz_protocol_decoder_bin_raw_get_hash_data(void* context) {
+    furi_assert(context);
+    SubGhzProtocolDecoderBinRAW* instance = context;
+    return subghz_protocol_blocks_add_bytes(
+        instance->data + instance->data_markup[0].byte_bias,
+        subghz_protocol_bin_raw_get_full_byte(instance->data_markup[0].bit_count));
+}
+
+bool subghz_protocol_decoder_bin_raw_serialize(
+    void* context,
+    FlipperFormat* flipper_format,
+    SubGhzRadioPreset* preset) {
+    furi_assert(context);
+    SubGhzProtocolDecoderBinRAW* instance = context;
+
+    bool res = false;
+    FuriString* temp_str;
+    temp_str = furi_string_alloc();
+    do {
+        stream_clean(flipper_format_get_raw_stream(flipper_format));
+        if(!flipper_format_write_header_cstr(
+               flipper_format, SUBGHZ_KEY_FILE_TYPE, SUBGHZ_KEY_FILE_VERSION)) {
+            FURI_LOG_E(TAG, "Unable to add header");
+            break;
+        }
+
+        if(!flipper_format_write_uint32(flipper_format, "Frequency", &preset->frequency, 1)) {
+            FURI_LOG_E(TAG, "Unable to add Frequency");
+            break;
+        }
+
+        subghz_block_generic_get_preset_name(furi_string_get_cstr(preset->name), temp_str);
+        if(!flipper_format_write_string_cstr(
+               flipper_format, "Preset", furi_string_get_cstr(temp_str))) {
+            FURI_LOG_E(TAG, "Unable to add Preset");
+            break;
+        }
+        if(!strcmp(furi_string_get_cstr(temp_str), "FuriHalSubGhzPresetCustom")) {
+            if(!flipper_format_write_string_cstr(
+                   flipper_format, "Custom_preset_module", "CC1101")) {
+                FURI_LOG_E(TAG, "Unable to add Custom_preset_module");
+                break;
+            }
+            if(!flipper_format_write_hex(
+                   flipper_format, "Custom_preset_data", preset->data, preset->data_size)) {
+                FURI_LOG_E(TAG, "Unable to add Custom_preset_data");
+                break;
+            }
+        }
+        if(!flipper_format_write_string_cstr(
+               flipper_format, "Protocol", instance->generic.protocol_name)) {
+            FURI_LOG_E(TAG, "Unable to add Protocol");
+            break;
+        }
+
+        uint32_t temp = instance->generic.data_count_bit;
+        if(!flipper_format_write_uint32(flipper_format, "Bit", &temp, 1)) {
+            FURI_LOG_E(TAG, "Unable to add Bit");
+            break;
+        }
+
+        if(!flipper_format_write_uint32(flipper_format, "TE", &instance->te, 1)) {
+            FURI_LOG_E(TAG, "Unable to add TE");
+            break;
+        }
+
+        uint16_t i = 0;
+        while((i < BIN_RAW_MAX_MARKUP_COUNT) && (instance->data_markup[i].bit_count != 0)) {
+            temp = instance->data_markup[i].bit_count;
+            if(!flipper_format_write_uint32(flipper_format, "Bit_RAW", &temp, 1)) {
+                FURI_LOG_E(TAG, "Bit_RAW");
+                break;
+            }
+            if(!flipper_format_write_hex(
+                   flipper_format,
+                   "Data_RAW",
+                   instance->data + instance->data_markup[i].byte_bias,
+                   subghz_protocol_bin_raw_get_full_byte(instance->data_markup[i].bit_count))) {
+                FURI_LOG_E(TAG, "Unable to add Data_RAW");
+                break;
+            }
+            i++;
+        }
+
+        res = true;
+    } while(false);
+    furi_string_free(temp_str);
+    return res;
+}
+
+bool subghz_protocol_decoder_bin_raw_deserialize(void* context, FlipperFormat* flipper_format) {
+    furi_assert(context);
+    SubGhzProtocolDecoderBinRAW* instance = context;
+
+    bool res = false;
+    uint32_t temp_data = 0;
+
+    do {
+        if(!flipper_format_rewind(flipper_format)) {
+            FURI_LOG_E(TAG, "Rewind error");
+            break;
+        }
+        if(!flipper_format_read_uint32(flipper_format, "Bit", (uint32_t*)&temp_data, 1)) {
+            FURI_LOG_E(TAG, "Missing Bit");
+            break;
+        }
+
+        instance->generic.data_count_bit = (uint16_t)temp_data;
+
+        if(!flipper_format_read_uint32(flipper_format, "TE", (uint32_t*)&instance->te, 1)) {
+            FURI_LOG_E(TAG, "Missing TE");
+            break;
+        }
+
+        temp_data = 0;
+        uint16_t ind = 0;
+        uint16_t byte_bias = 0;
+        uint16_t byte_count = 0;
+        memset(instance->data_markup, 0x00, BIN_RAW_MAX_MARKUP_COUNT * sizeof(BinRAW_Markup));
+        while(flipper_format_read_uint32(flipper_format, "Bit_RAW", (uint32_t*)&temp_data, 1)) {
+            if(ind >= BIN_RAW_MAX_MARKUP_COUNT) {
+                FURI_LOG_E(TAG, "Markup overflow");
+                break;
+            }
+            byte_count += subghz_protocol_bin_raw_get_full_byte(temp_data);
+            if(byte_count > BIN_RAW_BUF_DATA_SIZE) {
+                FURI_LOG_E(TAG, "Receive buffer overflow");
+                break;
+            }
+
+            instance->data_markup[ind].bit_count = temp_data;
+            instance->data_markup[ind].byte_bias = byte_bias;
+            byte_bias += subghz_protocol_bin_raw_get_full_byte(temp_data);
+
+            if(!flipper_format_read_hex(
+                   flipper_format,
+                   "Data_RAW",
+                   instance->data + instance->data_markup[ind].byte_bias,
+                   subghz_protocol_bin_raw_get_full_byte(temp_data))) {
+                instance->data_markup[ind].bit_count = 0;
+                FURI_LOG_E(TAG, "Missing Data_RAW");
+                break;
+            }
+            ind++;
+        }
+
+        res = true;
+    } while(0);
+
+    return res;
+}
+
+void subghz_protocol_decoder_bin_raw_get_string(void* context, FuriString* output) {
+    furi_assert(context);
+    SubGhzProtocolDecoderBinRAW* instance = context;
+    furi_string_cat_printf(
+        output,
+        "%s %dbit\r\n"
+        "Key:",
+        instance->generic.protocol_name,
+        instance->generic.data_count_bit);
+
+    uint16_t byte_count = subghz_protocol_bin_raw_get_full_byte(instance->generic.data_count_bit);
+    for(size_t i = 0; (byte_count < 36 ? i < byte_count : i < 36); i++) {
+        furi_string_cat_printf(output, "%02X", instance->data[i]);
+    }
+
+    furi_string_cat_printf(output, "\r\nTe:%luus\r\n", instance->te);
+}

+ 111 - 0
lib/subghz/protocols/bin_raw.h

@@ -0,0 +1,111 @@
+#pragma once
+
+#include "base.h"
+
+#define SUBGHZ_PROTOCOL_BIN_RAW_NAME "BinRAW"
+
+typedef struct SubGhzProtocolDecoderBinRAW SubGhzProtocolDecoderBinRAW;
+typedef struct SubGhzProtocolEncoderBinRAW SubGhzProtocolEncoderBinRAW;
+
+extern const SubGhzProtocolDecoder subghz_protocol_bin_raw_decoder;
+extern const SubGhzProtocolEncoder subghz_protocol_bin_raw_encoder;
+extern const SubGhzProtocol subghz_protocol_bin_raw;
+
+/**
+ * Allocate SubGhzProtocolEncoderBinRAW.
+ * @param environment Pointer to a SubGhzEnvironment instance
+ * @return SubGhzProtocolEncoderBinRAW* pointer to a SubGhzProtocolEncoderBinRAW instance
+ */
+void* subghz_protocol_encoder_bin_raw_alloc(SubGhzEnvironment* environment);
+
+/**
+ * Free SubGhzProtocolEncoderBinRAW.
+ * @param context Pointer to a SubGhzProtocolEncoderBinRAW instance
+ */
+void subghz_protocol_encoder_bin_raw_free(void* context);
+
+/**
+ * Deserialize and generating an upload to send.
+ * @param context Pointer to a SubGhzProtocolEncoderBinRAW instance
+ * @param flipper_format Pointer to a FlipperFormat instance
+ * @return true On success
+ */
+bool subghz_protocol_encoder_bin_raw_deserialize(void* context, FlipperFormat* flipper_format);
+
+/**
+ * Forced transmission stop.
+ * @param context Pointer to a SubGhzProtocolEncoderBinRAW instance
+ */
+void subghz_protocol_encoder_bin_raw_stop(void* context);
+
+/**
+ * Getting the level and duration of the upload to be loaded into DMA.
+ * @param context Pointer to a SubGhzProtocolEncoderBinRAW instance
+ * @return LevelDuration 
+ */
+LevelDuration subghz_protocol_encoder_bin_raw_yield(void* context);
+
+/**
+ * Allocate SubGhzProtocolDecoderBinRAW.
+ * @param environment Pointer to a SubGhzEnvironment instance
+ * @return SubGhzProtocolDecoderBinRAW* pointer to a SubGhzProtocolDecoderBinRAW instance
+ */
+void* subghz_protocol_decoder_bin_raw_alloc(SubGhzEnvironment* environment);
+
+/**
+ * Free SubGhzProtocolDecoderBinRAW.
+ * @param context Pointer to a SubGhzProtocolDecoderBinRAW instance
+ */
+void subghz_protocol_decoder_bin_raw_free(void* context);
+
+/**
+ * Reset decoder SubGhzProtocolDecoderBinRAW.
+ * @param context Pointer to a SubGhzProtocolDecoderBinRAW instance
+ */
+void subghz_protocol_decoder_bin_raw_reset(void* context);
+
+/**
+ * Parse a raw sequence of levels and durations received from the air.
+ * @param context Pointer to a SubGhzProtocolDecoderBinRAW instance
+ * @param level Signal level true-high false-low
+ * @param duration Duration of this level in, us
+ */
+void subghz_protocol_decoder_bin_raw_feed(void* context, bool level, uint32_t duration);
+
+/**
+ * Getting the hash sum of the last randomly received parcel.
+ * @param context Pointer to a SubGhzProtocolDecoderBinRAW instance
+ * @return hash Hash sum
+ */
+uint8_t subghz_protocol_decoder_bin_raw_get_hash_data(void* context);
+
+void subghz_protocol_decoder_bin_raw_data_input_rssi(
+    SubGhzProtocolDecoderBinRAW* instance,
+    float rssi);
+
+/**
+ * Serialize data SubGhzProtocolDecoderBinRAW.
+ * @param context Pointer to a SubGhzProtocolDecoderBinRAW instance
+ * @param flipper_format Pointer to a FlipperFormat instance
+ * @param preset The modulation on which the signal was received, SubGhzRadioPreset
+ * @return true On success
+ */
+bool subghz_protocol_decoder_bin_raw_serialize(
+    void* context,
+    FlipperFormat* flipper_format,
+    SubGhzRadioPreset* preset);
+
+/**
+ * Deserialize data SubGhzProtocolDecoderBinRAW.
+ * @param context Pointer to a SubGhzProtocolDecoderBinRAW instance
+ * @param flipper_format Pointer to a FlipperFormat instance
+ * @return true On success
+ */
+bool subghz_protocol_decoder_bin_raw_deserialize(void* context, FlipperFormat* flipper_format);
+
+/**
+ * Getting a textual representation of the received data.
+ * @param context Pointer to a SubGhzProtocolDecoderBinRAW instance
+ * @param output Resulting text
+ */
+void subghz_protocol_decoder_bin_raw_get_string(void* context, FuriString* output);

+ 3 - 2
lib/subghz/protocols/chamberlain_code.c

@@ -196,12 +196,13 @@ static bool
         break;
     }
 
-    instance->encoder.size_upload = subghz_protocol_blocks_get_upload(
+    instance->encoder.size_upload = subghz_protocol_blocks_get_upload_from_bit_array(
         upload_hex_data,
         upload_hex_count_bit,
         instance->encoder.upload,
         instance->encoder.size_upload,
-        subghz_protocol_chamb_code_const.te_short);
+        subghz_protocol_chamb_code_const.te_short,
+        SubGhzProtocolBlockAlignBitLeft);
 
     return true;
 }

+ 1 - 0
lib/subghz/protocols/protocol_items.c

@@ -42,6 +42,7 @@ const SubGhzProtocol* subghz_protocol_registry_items[] = {
     &subghz_protocol_dooya,
     &subghz_protocol_alutech_at_4n,
     &subghz_protocol_kinggates_stylo_4k,
+    &subghz_protocol_bin_raw,
 };
 
 const SubGhzProtocolRegistry subghz_protocol_registry = {

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

@@ -42,5 +42,6 @@
 #include "dooya.h"
 #include "alutech_at_4n.h"
 #include "kinggates_stylo_4k.h"
+#include "bin_raw.h"
 
 extern const SubGhzProtocolRegistry subghz_protocol_registry;

+ 4 - 4
lib/subghz/protocols/secplus_v2.c

@@ -261,16 +261,16 @@ static bool
     data = order << 4 | invert;
     int k = 0;
     for(int i = 6; i >= 0; i -= 2) {
-        roll_array[k++] = (data >> i) & 0x03;
-        if(roll_array[k] == 3) {
+        roll_array[k] = (data >> i) & 0x03;
+        if(roll_array[k++] == 3) {
             FURI_LOG_E(TAG, "Roll_Array FAIL");
             return false;
         }
     }
 
     for(int i = 8; i >= 0; i -= 2) {
-        roll_array[k++] = (p[2] >> i) & 0x03;
-        if(roll_array[k] == 3) {
+        roll_array[k] = (p[2] >> i) & 0x03;
+        if(roll_array[k++] == 3) {
             FURI_LOG_E(TAG, "Roll_Array FAIL");
             return false;
         }

+ 1 - 1
lib/subghz/receiver.c

@@ -64,7 +64,7 @@ void subghz_receiver_decode(SubGhzReceiver* instance, bool level, uint32_t durat
 
     for
         M_EACH(slot, instance->slots, SubGhzReceiverSlotArray_t) {
-            if((slot->base->protocol->flag & instance->filter) == instance->filter) {
+            if((slot->base->protocol->flag & instance->filter) != 0) {
                 slot->base->protocol->decoder->feed(slot->base, level, duration);
             }
         }

+ 1 - 0
lib/subghz/types.h

@@ -90,6 +90,7 @@ typedef enum {
     SubGhzProtocolFlag_Save = (1 << 7),
     SubGhzProtocolFlag_Load = (1 << 8),
     SubGhzProtocolFlag_Send = (1 << 9),
+    SubGhzProtocolFlag_BinRAW = (1 << 10),
 } SubGhzProtocolFlag;
 
 typedef struct {

+ 2 - 2
lib/toolbox/level_duration.h

@@ -13,8 +13,8 @@
 #define LEVEL_DURATION_RESERVED 0x800000U
 
 typedef struct {
-    uint32_t level;
-    uint32_t duration;
+    uint32_t duration : 30;
+    uint8_t level : 2;
 } LevelDuration;
 
 static inline LevelDuration level_duration_make(bool level, uint32_t duration) {