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

[FL-1913, FL-1963] SubGhz: save raw signal, add came atomo decoder (#783)

* File_Worker: getting the name of a new file with an index
* SubGhz: add decoder RAW protocol
* SubGhz: add view Save RAW
* SubGhz: refactoring subghz custom event
* SubGhz: fix syntax
* SubGhz: fix error build
* SubGhz: test build
* SubGhz: refactoring subghz, add rename, delete, start and emulate RAW signal
* SubGhz: fix triangle glitch in save raw view
* SubGhz: fix receiver config scene
* SubGhz: fix transfer after returning from save scene
* Canvas: add font rotation
* SubGhz: raw protocol encoder
* SubGhz: fix error completion of transfer raw encoder
* SubGhz: increased the speed of reading RAW data from a flash drive, displaying the name of the saved file in the Save RAW scene
* Canvas: fix font rotation
* SubGhz: fix navigation save RAW  scene
* SubGhz: add decode came atomo
* Git: renormalize
* Cleanup sources and enums
* Gui: add font direction to canvas reset, canvas init sequence cleanup.
* SubGhz: reorder menu.
* Gui: correct canvas_set_font_direction signature

Co-authored-by: Aleksandr Kutuzov <alleteam@gmail.com>
Skorpionm 4 лет назад
Родитель
Сommit
9d952ed855
59 измененных файлов с 1693 добавлено и 199 удалено
  1. 0 1
      .gitattributes
  2. 17 6
      applications/gui/canvas.c
  3. 16 0
      applications/gui/canvas.h
  4. 28 0
      applications/subghz/helpers/subghz_custom_event.h
  5. 2 1
      applications/subghz/scenes/subghz_scene_config.h
  6. 3 7
      applications/subghz/scenes/subghz_scene_delete.c
  7. 4 4
      applications/subghz/scenes/subghz_scene_delete_success.c
  8. 2 8
      applications/subghz/scenes/subghz_scene_frequency_analyzer.c
  9. 5 4
      applications/subghz/scenes/subghz_scene_receiver.c
  10. 23 16
      applications/subghz/scenes/subghz_scene_receiver_config.c
  11. 9 13
      applications/subghz/scenes/subghz_scene_receiver_info.c
  12. 8 7
      applications/subghz/scenes/subghz_scene_save_name.c
  13. 169 0
      applications/subghz/scenes/subghz_scene_save_raw.c
  14. 3 4
      applications/subghz/scenes/subghz_scene_save_success.c
  15. 3 4
      applications/subghz/scenes/subghz_scene_show_error.c
  16. 3 4
      applications/subghz/scenes/subghz_scene_show_only_rx.c
  17. 14 2
      applications/subghz/scenes/subghz_scene_start.c
  18. 17 9
      applications/subghz/scenes/subghz_scene_transmitter.c
  19. 12 0
      applications/subghz/subghz.c
  20. 27 3
      applications/subghz/subghz_i.c
  21. 5 0
      applications/subghz/subghz_i.h
  22. 2 5
      applications/subghz/views/subghz_frequency_analyzer.h
  23. 4 3
      applications/subghz/views/subghz_receiver.c
  24. 2 7
      applications/subghz/views/subghz_receiver.h
  25. 313 0
      applications/subghz/views/subghz_save_raw.c
  26. 30 0
      applications/subghz/views/subghz_save_raw.h
  27. 4 2
      applications/subghz/views/subghz_transmitter.c
  28. 2 8
      applications/subghz/views/subghz_transmitter.h
  29. 1 0
      firmware/targets/f6/furi-hal/furi-hal-subghz.c
  30. 1 0
      firmware/targets/f7/furi-hal/furi-hal-subghz.c
  31. 31 1
      lib/app-scened-template/file-worker.c
  32. 21 1
      lib/app-scened-template/file-worker.h
  33. 2 1
      lib/subghz/protocols/subghz_protocol_came.c
  34. 4 2
      lib/subghz/protocols/subghz_protocol_came.h
  35. 123 21
      lib/subghz/protocols/subghz_protocol_came_atomo.c
  36. 10 1
      lib/subghz/protocols/subghz_protocol_came_atomo.h
  37. 2 1
      lib/subghz/protocols/subghz_protocol_came_twee.c
  38. 3 1
      lib/subghz/protocols/subghz_protocol_came_twee.h
  39. 53 22
      lib/subghz/protocols/subghz_protocol_common.c
  40. 22 1
      lib/subghz/protocols/subghz_protocol_common.h
  41. 3 2
      lib/subghz/protocols/subghz_protocol_gate_tx.c
  42. 8 2
      lib/subghz/protocols/subghz_protocol_gate_tx.h
  43. 2 1
      lib/subghz/protocols/subghz_protocol_keeloq.c
  44. 8 2
      lib/subghz/protocols/subghz_protocol_keeloq.h
  45. 2 1
      lib/subghz/protocols/subghz_protocol_nero_radio.c
  46. 12 3
      lib/subghz/protocols/subghz_protocol_nero_radio.h
  47. 3 2
      lib/subghz/protocols/subghz_protocol_nero_sketch.c
  48. 13 4
      lib/subghz/protocols/subghz_protocol_nero_sketch.h
  49. 3 2
      lib/subghz/protocols/subghz_protocol_nice_flo.c
  50. 8 2
      lib/subghz/protocols/subghz_protocol_nice_flo.h
  51. 2 1
      lib/subghz/protocols/subghz_protocol_princeton.c
  52. 6 4
      lib/subghz/protocols/subghz_protocol_princeton.h
  53. 265 0
      lib/subghz/protocols/subghz_protocol_raw.c
  54. 64 0
      lib/subghz/protocols/subghz_protocol_raw.h
  55. 210 0
      lib/subghz/subghz_file_encoder_worker.c
  56. 39 0
      lib/subghz/subghz_file_encoder_worker.h
  57. 16 0
      lib/subghz/subghz_parser.c
  58. 18 3
      lib/subghz/subghz_parser.h
  59. 11 0
      lib/toolbox/level_duration.h

+ 0 - 1
.gitattributes

@@ -1,2 +1 @@
 * text=auto
-

+ 17 - 6
applications/gui/canvas.c

@@ -11,17 +11,20 @@ Canvas* canvas_init() {
 
     furi_hal_power_insomnia_enter();
 
-    canvas->orientation = CanvasOrientationHorizontal;
+    // Setup u8g2
     u8g2_Setup_st756x_flipper(&canvas->fb, U8G2_R0, u8x8_hw_spi_stm32, u8g2_gpio_and_delay_stm32);
-
-    // send init sequence to the display, display is in sleep mode after this
+    canvas->orientation = CanvasOrientationHorizontal;
+    // Initialize display
     u8g2_InitDisplay(&canvas->fb);
-    // wake up display
-    u8g2_ClearBuffer(&canvas->fb);
+    // Wake up display
     u8g2_SetPowerSave(&canvas->fb, 0);
-    u8g2_SendBuffer(&canvas->fb);
+
+    // Clear buffer and send to device
+    canvas_clear(canvas);
+    canvas_commit(canvas);
 
     furi_hal_power_insomnia_exit();
+
     return canvas;
 }
 
@@ -32,9 +35,12 @@ void canvas_free(Canvas* canvas) {
 
 void canvas_reset(Canvas* canvas) {
     furi_assert(canvas);
+
     canvas_clear(canvas);
+
     canvas_set_color(canvas, ColorBlack);
     canvas_set_font(canvas, FontSecondary);
+    canvas_set_font_direction(canvas, CanvasFontDirectionLeftToRight);
 }
 
 void canvas_commit(Canvas* canvas) {
@@ -96,6 +102,11 @@ void canvas_set_color(Canvas* canvas, Color color) {
     u8g2_SetDrawColor(&canvas->fb, color);
 }
 
+void canvas_set_font_direction(Canvas* canvas, CanvasFontDirection dir) {
+    furi_assert(canvas);
+    u8g2_SetFontDirection(&canvas->fb, dir);
+}
+
 void canvas_invert_color(Canvas* canvas) {
     canvas->fb.draw_color = !canvas->fb.draw_color;
 }

+ 16 - 0
applications/gui/canvas.h

@@ -37,6 +37,14 @@ typedef enum {
     CanvasOrientationVertical,
 } CanvasOrientation;
 
+/** Font Direction */
+typedef enum {
+    CanvasFontDirectionLeftToRight,
+    CanvasFontDirectionTopToDown,
+    CanvasFontDirectionRightToLeft,
+    CanvasFontDirectionDownToTop,
+} CanvasFontDirection;
+
 /** Canvas anonymouse structure */
 typedef struct Canvas Canvas;
 
@@ -77,6 +85,14 @@ void canvas_clear(Canvas* canvas);
  */
 void canvas_set_color(Canvas* canvas, Color color);
 
+/** Set font swap
+ * Argument String Rotation Description
+ *
+ * @param      canvas  Canvas instance
+ * @param      dir     Direction font
+ */
+void canvas_set_font_direction(Canvas* canvas, CanvasFontDirection dir);
+
 /** Invert drawing color
  *
  * @param      canvas  Canvas instance

+ 28 - 0
applications/subghz/helpers/subghz_custom_event.h

@@ -0,0 +1,28 @@
+#pragma once
+
+typedef enum {
+    SubghzCustomEventSceneDeleteSuccess = 100,
+    SubghzCustomEventSceneDelete,
+    SubghzCustomEventSceneReceiverInfoTxStart,
+    SubghzCustomEventSceneReceiverInfoTxStop,
+    SubghzCustomEventSceneReceiverInfoSave,
+    SubghzCustomEventSceneSaveName,
+    SubghzCustomEventSceneSaveSuccess,
+    SubghzCustomEventSceneShowError,
+    SubghzCustomEventSceneShowOnlyRX,
+
+    SubghzCustomEventViewReceverOK,
+    SubghzCustomEventViewReceverConfig,
+    SubghzCustomEventViewReceverBack,
+
+    SubghzCustomEventViewSaveRAWBack,
+    SubghzCustomEventViewSaveRAWIDLE,
+    SubghzCustomEventViewSaveRAWREC,
+    SubghzCustomEventViewSaveRAWConfig,
+    SubghzCustomEventViewSaveRAWMore,
+
+    SubghzCustomEventViewTransmitterBack,
+    SubghzCustomEventViewTransmitterSendStart,
+    SubghzCustomEventViewTransmitterSendStop,
+    SubghzCustomEventViewTransmitterError,
+} SubghzCustomEvent;

+ 2 - 1
applications/subghz/scenes/subghz_scene_config.h

@@ -16,4 +16,5 @@ ADD_SCENE(subghz, test_static, TestStatic)
 ADD_SCENE(subghz, test_carrier, TestCarrier)
 ADD_SCENE(subghz, test_packet, TestPacket)
 ADD_SCENE(subghz, set_type, SetType)
-ADD_SCENE(subghz, frequency_analyzer, FrequencyAnalyzer)
+ADD_SCENE(subghz, frequency_analyzer, FrequencyAnalyzer)
+ADD_SCENE(subghz, save_raw, SaveRAW)

+ 3 - 7
applications/subghz/scenes/subghz_scene_delete.c

@@ -1,15 +1,11 @@
 #include "../subghz_i.h"
-
-typedef enum {
-    SubGhzSceneDeleteInfoCustomEventDelete,
-} SubGhzSceneDeleteInfoCustomEvent;
+#include "../helpers/subghz_custom_event.h"
 
 void subghz_scene_delete_callback(GuiButtonType result, InputType type, void* context) {
     furi_assert(context);
     SubGhz* subghz = context;
     if((result == GuiButtonTypeRight) && (type == InputTypeShort)) {
-        view_dispatcher_send_custom_event(
-            subghz->view_dispatcher, SubGhzSceneDeleteInfoCustomEventDelete);
+        view_dispatcher_send_custom_event(subghz->view_dispatcher, SubghzCustomEventSceneDelete);
     }
 }
 
@@ -53,7 +49,7 @@ void subghz_scene_delete_on_enter(void* context) {
 bool subghz_scene_delete_on_event(void* context, SceneManagerEvent event) {
     SubGhz* subghz = context;
     if(event.type == SceneManagerEventTypeCustom) {
-        if(event.event == SubGhzSceneDeleteInfoCustomEventDelete) {
+        if(event.event == SubghzCustomEventSceneDelete) {
             memcpy(subghz->file_name_tmp, subghz->file_name, strlen(subghz->file_name));
             if(subghz_delete_file(subghz)) {
                 scene_manager_next_scene(subghz->scene_manager, SubGhzSceneDeleteSuccess);

+ 4 - 4
applications/subghz/scenes/subghz_scene_delete_success.c

@@ -1,10 +1,10 @@
 #include "../subghz_i.h"
-
-#define SCENE_DELETE_SUCCESS_CUSTOM_EVENT (0UL)
+#include "../helpers/subghz_custom_event.h"
 
 void subghz_scene_delete_success_popup_callback(void* context) {
     SubGhz* subghz = context;
-    view_dispatcher_send_custom_event(subghz->view_dispatcher, SCENE_DELETE_SUCCESS_CUSTOM_EVENT);
+    view_dispatcher_send_custom_event(
+        subghz->view_dispatcher, SubghzCustomEventSceneDeleteSuccess);
 }
 
 void subghz_scene_delete_success_on_enter(void* context) {
@@ -25,7 +25,7 @@ bool subghz_scene_delete_success_on_event(void* context, SceneManagerEvent event
     SubGhz* subghz = context;
 
     if(event.type == SceneManagerEventTypeCustom) {
-        if(event.event == SCENE_DELETE_SUCCESS_CUSTOM_EVENT) {
+        if(event.event == SubghzCustomEventSceneDeleteSuccess) {
             return scene_manager_search_and_switch_to_previous_scene(
                 subghz->scene_manager, SubGhzSceneStart);
         }

+ 2 - 8
applications/subghz/scenes/subghz_scene_frequency_analyzer.c

@@ -1,7 +1,7 @@
 #include "../subghz_i.h"
 #include "../views/subghz_frequency_analyzer.h"
 
-void subghz_scene_frequency_analyzer_callback(SubghzFrequencyAnalyzerEvent event, void* context) {
+void subghz_scene_frequency_analyzer_callback(SubghzCustomEvent event, void* context) {
     furi_assert(context);
     SubGhz* subghz = context;
     view_dispatcher_send_custom_event(subghz->view_dispatcher, event);
@@ -15,13 +15,7 @@ void subghz_scene_frequency_analyzer_on_enter(void* context) {
 }
 
 bool subghz_scene_frequency_analyzer_on_event(void* context, SceneManagerEvent event) {
-    SubGhz* subghz = context;
-    if(event.type == SceneManagerEventTypeCustom) {
-        if(event.event == SubghzFrequencyAnalyzerEventOnlyRx) {
-            scene_manager_next_scene(subghz->scene_manager, SubGhzSceneShowOnlyRx);
-            return true;
-        }
-    }
+    //SubGhz* subghz = context;
     return false;
 }
 

+ 5 - 4
applications/subghz/scenes/subghz_scene_receiver.c

@@ -34,7 +34,7 @@ static void subghz_scene_receiver_update_statusbar(void* context) {
     string_clear(history_stat_str);
 }
 
-void subghz_scene_receiver_callback(SubghzReceverEvent event, void* context) {
+void subghz_scene_receiver_callback(SubghzCustomEvent event, void* context) {
     furi_assert(context);
     SubGhz* subghz = context;
     view_dispatcher_send_custom_event(subghz->view_dispatcher, event);
@@ -102,8 +102,9 @@ bool subghz_scene_receiver_on_event(void* context, SceneManagerEvent event) {
 
     if(event.type == SceneManagerEventTypeCustom) {
         switch(event.event) {
-        case SubghzReceverEventBack:
+        case SubghzCustomEventViewReceverBack:
             // Stop CC1101 Rx
+            subghz->state_notifications = NOTIFICATION_IDLE_STATE;
             if(subghz->txrx->txrx_state == SubGhzTxRxStateRx) {
                 subghz_rx_end(subghz);
                 subghz_sleep(subghz);
@@ -118,12 +119,12 @@ bool subghz_scene_receiver_on_event(void* context, SceneManagerEvent event) {
                 subghz->scene_manager, SubGhzSceneStart);
             return true;
             break;
-        case SubghzReceverEventOK:
+        case SubghzCustomEventViewReceverOK:
             subghz->txrx->idx_menu_chosen = subghz_receiver_get_idx_menu(subghz->subghz_receiver);
             scene_manager_next_scene(subghz->scene_manager, SubGhzSceneReceiverInfo);
             return true;
             break;
-        case SubghzReceverEventConfig:
+        case SubghzCustomEventViewReceverConfig:
             subghz->state_notifications = NOTIFICATION_IDLE_STATE;
             subghz->txrx->idx_menu_chosen = subghz_receiver_get_idx_menu(subghz->subghz_receiver);
             scene_manager_next_scene(subghz->scene_manager, SubGhzSceneReceiverConfig);

+ 23 - 16
applications/subghz/scenes/subghz_scene_receiver_config.c

@@ -66,6 +66,8 @@ static void subghz_scene_receiver_config_set_frequency(VariableItem* item) {
     if(subghz->txrx->hopper_state == SubGhzHopperStateOFF) {
         variable_item_set_current_value_text(item, subghz_frequencies_text[index]);
         subghz->txrx->frequency = subghz_frequencies[index];
+    } else {
+        variable_item_set_current_value_index(item, subghz_frequencies_433_92);
     }
 }
 
@@ -88,22 +90,24 @@ static void subghz_scene_receiver_config_set_hopping_runing(VariableItem* item)
                 subghz->scene_manager, SubGhzSceneReceiverConfig),
             subghz_frequencies_text[subghz_frequencies_433_92]);
         subghz->txrx->frequency = subghz_frequencies[subghz_frequencies_433_92];
+        variable_item_set_current_value_index(
+            (VariableItem*)scene_manager_get_scene_state(
+                subghz->scene_manager, SubGhzSceneReceiverConfig),
+            subghz_frequencies_433_92);
     } else {
         variable_item_set_current_value_text(
             (VariableItem*)scene_manager_get_scene_state(
                 subghz->scene_manager, SubGhzSceneReceiverConfig),
             " -----");
+        variable_item_set_current_value_index(
+            (VariableItem*)scene_manager_get_scene_state(
+                subghz->scene_manager, SubGhzSceneReceiverConfig),
+            subghz_frequencies_433_92);
     }
 
     subghz->txrx->hopper_state = hopping_value[index];
 }
 
-void subghz_scene_receiver_config_callback(SubghzReceverEvent event, void* context) {
-    furi_assert(context);
-    SubGhz* subghz = context;
-    view_dispatcher_send_custom_event(subghz->view_dispatcher, event);
-}
-
 void subghz_scene_receiver_config_on_enter(void* context) {
     SubGhz* subghz = context;
     VariableItem* item;
@@ -122,16 +126,18 @@ void subghz_scene_receiver_config_on_enter(void* context) {
     variable_item_set_current_value_index(item, value_index);
     variable_item_set_current_value_text(item, subghz_frequencies_text[value_index]);
 
-    item = variable_item_list_add(
-        subghz->variable_item_list,
-        "Hopping:",
-        HOPPING_COUNT,
-        subghz_scene_receiver_config_set_hopping_runing,
-        subghz);
-    value_index = subghz_scene_receiver_config_hopper_value_index(
-        subghz->txrx->hopper_state, hopping_value, HOPPING_COUNT, subghz);
-    variable_item_set_current_value_index(item, value_index);
-    variable_item_set_current_value_text(item, hopping_text[value_index]);
+    if(!scene_manager_get_scene_state(subghz->scene_manager, SubGhzSceneSaveRAW)) {
+        item = variable_item_list_add(
+            subghz->variable_item_list,
+            "Hopping:",
+            HOPPING_COUNT,
+            subghz_scene_receiver_config_set_hopping_runing,
+            subghz);
+        value_index = subghz_scene_receiver_config_hopper_value_index(
+            subghz->txrx->hopper_state, hopping_value, HOPPING_COUNT, subghz);
+        variable_item_set_current_value_index(item, value_index);
+        variable_item_set_current_value_text(item, hopping_text[value_index]);
+    }
 
     item = variable_item_list_add(
         subghz->variable_item_list,
@@ -155,4 +161,5 @@ bool subghz_scene_receiver_config_on_event(void* context, SceneManagerEvent even
 void subghz_scene_receiver_config_on_exit(void* context) {
     SubGhz* subghz = context;
     variable_item_list_clean(subghz->variable_item_list);
+    scene_manager_set_scene_state(subghz->scene_manager, SubGhzSceneSaveRAW, 0);
 }

+ 9 - 13
applications/subghz/scenes/subghz_scene_receiver_info.c

@@ -1,10 +1,5 @@
 #include "../subghz_i.h"
-
-typedef enum {
-    SubGhzSceneReceiverInfoCustomEventTxStart,
-    SubGhzSceneReceiverInfoCustomEventTxStop,
-    SubGhzSceneReceiverInfoCustomEventSave,
-} SubGhzSceneReceiverInfoCustomEvent;
+#include "../helpers/subghz_custom_event.h"
 
 void subghz_scene_receiver_info_callback(GuiButtonType result, InputType type, void* context) {
     furi_assert(context);
@@ -12,13 +7,13 @@ void subghz_scene_receiver_info_callback(GuiButtonType result, InputType type, v
 
     if((result == GuiButtonTypeCenter) && (type == InputTypePress)) {
         view_dispatcher_send_custom_event(
-            subghz->view_dispatcher, SubGhzSceneReceiverInfoCustomEventTxStart);
+            subghz->view_dispatcher, SubghzCustomEventSceneReceiverInfoTxStart);
     } else if((result == GuiButtonTypeCenter) && (type == InputTypeRelease)) {
         view_dispatcher_send_custom_event(
-            subghz->view_dispatcher, SubGhzSceneReceiverInfoCustomEventTxStop);
+            subghz->view_dispatcher, SubghzCustomEventSceneReceiverInfoTxStop);
     } else if((result == GuiButtonTypeRight) && (type == InputTypeShort)) {
         view_dispatcher_send_custom_event(
-            subghz->view_dispatcher, SubGhzSceneReceiverInfoCustomEventSave);
+            subghz->view_dispatcher, SubghzCustomEventSceneReceiverInfoSave);
     }
 }
 
@@ -101,7 +96,7 @@ void subghz_scene_receiver_info_on_enter(void* context) {
 bool subghz_scene_receiver_info_on_event(void* context, SceneManagerEvent event) {
     SubGhz* subghz = context;
     if(event.type == SceneManagerEventTypeCustom) {
-        if(event.event == SubGhzSceneReceiverInfoCustomEventTxStart) {
+        if(event.event == SubghzCustomEventSceneReceiverInfoTxStart) {
             //CC1101 Stop RX -> Start TX
             if(subghz->txrx->hopper_state != SubGhzHopperStateOFF) {
                 subghz->txrx->hopper_state = SubGhzHopperStatePause;
@@ -112,7 +107,8 @@ bool subghz_scene_receiver_info_on_event(void* context, SceneManagerEvent event)
             if(!subghz_scene_receiver_info_update_parser(subghz)) {
                 return false;
             }
-            if(subghz->txrx->txrx_state == SubGhzTxRxStateIdle) {
+            if(subghz->txrx->txrx_state == SubGhzTxRxStateIdle ||
+               subghz->txrx->txrx_state == SubGhzTxRxStateSleep) {
                 if(!subghz_tx_start(subghz)) {
                     scene_manager_next_scene(subghz->scene_manager, SubGhzSceneShowOnlyRx);
                 } else {
@@ -120,7 +116,7 @@ bool subghz_scene_receiver_info_on_event(void* context, SceneManagerEvent event)
                 }
             }
             return true;
-        } else if(event.event == SubGhzSceneReceiverInfoCustomEventTxStop) {
+        } else if(event.event == SubghzCustomEventSceneReceiverInfoTxStop) {
             //CC1101 Stop Tx -> Start RX
             subghz->state_notifications = NOTIFICATION_IDLE_STATE;
             if(subghz->txrx->txrx_state == SubGhzTxRxStateTx) {
@@ -135,7 +131,7 @@ bool subghz_scene_receiver_info_on_event(void* context, SceneManagerEvent event)
             }
             subghz->state_notifications = NOTIFICATION_RX_STATE;
             return true;
-        } else if(event.event == SubGhzSceneReceiverInfoCustomEventSave) {
+        } else if(event.event == SubghzCustomEventSceneReceiverInfoSave) {
             //CC1101 Stop RX -> Save
             subghz->state_notifications = NOTIFICATION_IDLE_STATE;
             if(subghz->txrx->hopper_state != SubGhzHopperStateOFF) {

+ 8 - 7
applications/subghz/scenes/subghz_scene_save_name.c

@@ -1,12 +1,11 @@
 #include "../subghz_i.h"
 #include <lib/toolbox/random_name.h>
 #include "file-worker.h"
-
-#define SCENE_SAVE_NAME_CUSTOM_EVENT (0UL)
+#include "../helpers/subghz_custom_event.h"
 
 void subghz_scene_save_name_text_input_callback(void* context) {
     SubGhz* subghz = context;
-    view_dispatcher_send_custom_event(subghz->view_dispatcher, SCENE_SAVE_NAME_CUSTOM_EVENT);
+    view_dispatcher_send_custom_event(subghz->view_dispatcher, SubghzCustomEventSceneSaveName);
 }
 
 void subghz_scene_save_name_on_enter(void* context) {
@@ -38,12 +37,14 @@ bool subghz_scene_save_name_on_event(void* context, SceneManagerEvent event) {
     SubGhz* subghz = context;
 
     if(event.type == SceneManagerEventTypeCustom) {
-        if(event.event == SCENE_SAVE_NAME_CUSTOM_EVENT) {
-            if(strcmp(subghz->file_name, "") &&
-               subghz_save_protocol_to_file(subghz, subghz->file_name)) {
+        if(event.event == SubghzCustomEventSceneSaveName) {
+            if(strcmp(subghz->file_name, "")) {
                 if(strcmp(subghz->file_name_tmp, "")) {
-                    subghz_delete_file(subghz);
+                    subghz_rename_file(subghz);
+                } else {
+                    subghz_save_protocol_to_file(subghz, subghz->file_name);
                 }
+
                 subghz_file_name_clear(subghz);
                 scene_manager_next_scene(subghz->scene_manager, SubGhzSceneSaveSuccess);
                 return true;

+ 169 - 0
applications/subghz/scenes/subghz_scene_save_raw.c

@@ -0,0 +1,169 @@
+#include "../subghz_i.h"
+#include "../views/subghz_save_raw.h"
+#include <lib/subghz/protocols/subghz_protocol_raw.h>
+#include <lib/subghz/subghz_parser.h>
+
+static void subghz_scene_save_raw_update_statusbar(void* context) {
+    furi_assert(context);
+    SubGhz* subghz = context;
+    char frequency_str[20];
+    char preset_str[10];
+
+    snprintf(
+        frequency_str,
+        sizeof(frequency_str),
+        "%03ld.%02ld",
+        subghz->txrx->frequency / 1000000 % 1000,
+        subghz->txrx->frequency / 10000 % 100);
+    if(subghz->txrx->preset == FuriHalSubGhzPresetOok650Async ||
+       subghz->txrx->preset == FuriHalSubGhzPresetOok270Async) {
+        snprintf(preset_str, sizeof(preset_str), "AM");
+    } else if(
+        subghz->txrx->preset == FuriHalSubGhzPreset2FSKDev238Async ||
+        subghz->txrx->preset == FuriHalSubGhzPreset2FSKDev476Async) {
+        snprintf(preset_str, sizeof(preset_str), "FM");
+    } else {
+        furi_crash(NULL);
+    }
+    subghz_save_raw_add_data_statusbar(subghz->subghz_save_raw, frequency_str, preset_str);
+}
+
+void subghz_scene_save_raw_callback(SubghzCustomEvent event, void* context) {
+    furi_assert(context);
+    SubGhz* subghz = context;
+    view_dispatcher_send_custom_event(subghz->view_dispatcher, event);
+}
+
+void subghz_scene_save_raw_on_enter(void* context) {
+    SubGhz* subghz = context;
+    subghz_scene_save_raw_update_statusbar(subghz);
+    subghz_save_raw_set_callback(subghz->subghz_save_raw, subghz_scene_save_raw_callback, subghz);
+
+    subghz->txrx->protocol_result = subghz_parser_get_by_name(subghz->txrx->parser, "RAW");
+    furi_assert(subghz->txrx->protocol_result);
+
+    subghz_worker_set_pair_callback(
+        subghz->txrx->worker, (SubGhzWorkerPairCallback)subghz_parser_raw_parse);
+
+    view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewSaveRAW);
+}
+
+bool subghz_scene_save_raw_on_event(void* context, SceneManagerEvent event) {
+    SubGhz* subghz = context;
+    if(event.type == SceneManagerEventTypeCustom) {
+        switch(event.event) {
+        case SubghzCustomEventViewSaveRAWBack:
+            if(subghz->txrx->txrx_state == SubGhzTxRxStateRx) {
+                subghz_rx_end(subghz);
+                subghz_sleep(subghz);
+            };
+            subghz->txrx->frequency = subghz_frequencies[subghz_frequencies_433_92];
+            subghz->txrx->preset = FuriHalSubGhzPresetOok650Async;
+            subghz_protocol_save_raw_to_file_stop(
+                (SubGhzProtocolRAW*)subghz->txrx->protocol_result);
+            scene_manager_search_and_switch_to_previous_scene(
+                subghz->scene_manager, SubGhzSceneStart);
+            subghz->state_notifications = NOTIFICATION_IDLE_STATE;
+            return true;
+            break;
+        case SubghzCustomEventViewSaveRAWConfig:
+            scene_manager_set_scene_state(subghz->scene_manager, SubGhzSceneSaveRAW, 1);
+            scene_manager_next_scene(subghz->scene_manager, SubGhzSceneReceiverConfig);
+            return true;
+            break;
+        case SubghzCustomEventViewSaveRAWIDLE:
+            if(subghz->txrx->txrx_state == SubGhzTxRxStateRx) {
+                subghz_rx_end(subghz);
+                subghz_sleep(subghz);
+            };
+            subghz_protocol_save_raw_to_file_stop(
+                (SubGhzProtocolRAW*)subghz->txrx->protocol_result);
+            subghz->state_notifications = NOTIFICATION_IDLE_STATE;
+            //send the name of the saved file to the account
+            subghz_save_raw_set_file_name(
+                subghz->subghz_save_raw,
+                subghz_protocol_get_last_file_name(
+                    (SubGhzProtocolRAW*)subghz->txrx->protocol_result));
+            return true;
+            break;
+        case SubghzCustomEventViewSaveRAWREC:
+            if(subghz_protocol_save_raw_to_file_init(
+                   (SubGhzProtocolRAW*)subghz->txrx->protocol_result,
+                   "Raw",
+                   subghz->txrx->frequency,
+                   subghz->txrx->preset)) {
+                if((subghz->txrx->txrx_state == SubGhzTxRxStateIdle) ||
+                   (subghz->txrx->txrx_state == SubGhzTxRxStateSleep)) {
+                    subghz_begin(subghz, subghz->txrx->preset);
+                    subghz_rx(subghz, subghz->txrx->frequency);
+                }
+                subghz->state_notifications = NOTIFICATION_RX_STATE;
+            } else {
+                string_set(subghz->error_str, "No SD card");
+                scene_manager_next_scene(subghz->scene_manager, SubGhzSceneShowError);
+            }
+            return true;
+            break;
+        case SubghzCustomEventViewSaveRAWMore:
+            if(strcmp(
+                   subghz_protocol_get_last_file_name(
+                       (SubGhzProtocolRAW*)subghz->txrx->protocol_result),
+                   "")) {
+                strlcpy(
+                    subghz->file_name,
+                    subghz_protocol_get_last_file_name(
+                        (SubGhzProtocolRAW*)subghz->txrx->protocol_result),
+                    strlen(subghz_protocol_get_last_file_name(
+                        (SubGhzProtocolRAW*)subghz->txrx->protocol_result)) +
+                        1);
+                //set the path to read the file
+                string_t temp_str;
+                string_init_printf(
+                    temp_str,
+                    "%s/%s%s",
+                    SUBGHZ_APP_PATH_FOLDER,
+                    subghz->file_name,
+                    SUBGHZ_APP_EXTENSION);
+                subghz_protocol_set_last_file_name(
+                    (SubGhzProtocolRAW*)subghz->txrx->protocol_result, string_get_cstr(temp_str));
+                string_clear(temp_str);
+
+                scene_manager_next_scene(subghz->scene_manager, SubGhzSceneSavedMenu);
+            }
+            return true;
+            break;
+
+        default:
+            break;
+        }
+    } else if(event.type == SceneManagerEventTypeTick) {
+        switch(subghz->state_notifications) {
+        case NOTIFICATION_RX_STATE:
+            notification_message(subghz->notifications, &sequence_blink_blue_10);
+            subghz_save_raw_update_sample_write(
+                subghz->subghz_save_raw,
+                subghz_save_protocol_raw_get_sample_write(
+                    (SubGhzProtocolRAW*)subghz->txrx->protocol_result));
+            subghz_save_raw_add_data_rssi(subghz->subghz_save_raw, furi_hal_subghz_get_rssi());
+            break;
+        default:
+            break;
+        }
+    }
+    return false;
+}
+
+void subghz_scene_save_raw_on_exit(void* context) {
+    SubGhz* subghz = context;
+
+    //Stop CC1101
+    if(subghz->txrx->txrx_state == SubGhzTxRxStateRx) {
+        subghz_rx_end(subghz);
+        subghz_sleep(subghz);
+    };
+    subghz->state_notifications = NOTIFICATION_IDLE_STATE;
+
+    //Сallback restoration
+    subghz_worker_set_pair_callback(
+        subghz->txrx->worker, (SubGhzWorkerPairCallback)subghz_parser_parse);
+}

+ 3 - 4
applications/subghz/scenes/subghz_scene_save_success.c

@@ -1,10 +1,9 @@
 #include "../subghz_i.h"
-
-#define SCENE_SAVE_SUCCESS_CUSTOM_EVENT (0UL)
+#include "../helpers/subghz_custom_event.h"
 
 void subghz_scene_save_success_popup_callback(void* context) {
     SubGhz* subghz = context;
-    view_dispatcher_send_custom_event(subghz->view_dispatcher, SCENE_SAVE_SUCCESS_CUSTOM_EVENT);
+    view_dispatcher_send_custom_event(subghz->view_dispatcher, SubghzCustomEventSceneSaveSuccess);
 }
 
 void subghz_scene_save_success_on_enter(void* context) {
@@ -24,7 +23,7 @@ void subghz_scene_save_success_on_enter(void* context) {
 bool subghz_scene_save_success_on_event(void* context, SceneManagerEvent event) {
     SubGhz* subghz = context;
     if(event.type == SceneManagerEventTypeCustom) {
-        if(event.event == SCENE_SAVE_SUCCESS_CUSTOM_EVENT) {
+        if(event.event == SubghzCustomEventSceneSaveSuccess) {
             if(!scene_manager_search_and_switch_to_previous_scene(
                    subghz->scene_manager, SubGhzSceneReceiver)) {
                 scene_manager_search_and_switch_to_previous_scene(

+ 3 - 4
applications/subghz/scenes/subghz_scene_show_error.c

@@ -1,10 +1,9 @@
 #include "../subghz_i.h"
-
-#define SCENE_NO_MAN_CUSTOM_EVENT (11UL)
+#include "../helpers/subghz_custom_event.h"
 
 void subghz_scene_show_error_popup_callback(void* context) {
     SubGhz* subghz = context;
-    view_dispatcher_send_custom_event(subghz->view_dispatcher, SCENE_NO_MAN_CUSTOM_EVENT);
+    view_dispatcher_send_custom_event(subghz->view_dispatcher, SubghzCustomEventSceneShowError);
 }
 
 void subghz_scene_show_error_on_enter(void* context) {
@@ -24,7 +23,7 @@ void subghz_scene_show_error_on_enter(void* context) {
 bool subghz_scene_show_error_on_event(void* context, SceneManagerEvent event) {
     SubGhz* subghz = context;
     if(event.type == SceneManagerEventTypeCustom) {
-        if(event.event == SCENE_NO_MAN_CUSTOM_EVENT) {
+        if(event.event == SubghzCustomEventSceneShowError) {
             scene_manager_search_and_switch_to_previous_scene(
                 subghz->scene_manager, SubGhzSceneStart);
             return true;

+ 3 - 4
applications/subghz/scenes/subghz_scene_show_only_rx.c

@@ -1,10 +1,9 @@
 #include "../subghz_i.h"
-
-#define SCENE_NO_MAN_CUSTOM_EVENT (11UL)
+#include "../helpers/subghz_custom_event.h"
 
 void subghz_scene_show_only_rx_popup_callback(void* context) {
     SubGhz* subghz = context;
-    view_dispatcher_send_custom_event(subghz->view_dispatcher, SCENE_NO_MAN_CUSTOM_EVENT);
+    view_dispatcher_send_custom_event(subghz->view_dispatcher, SubghzCustomEventSceneShowOnlyRX);
 }
 
 void subghz_scene_show_only_rx_on_enter(void* context) {
@@ -30,7 +29,7 @@ void subghz_scene_show_only_rx_on_enter(void* context) {
 const bool subghz_scene_show_only_rx_on_event(void* context, SceneManagerEvent event) {
     SubGhz* subghz = context;
     if(event.type == SceneManagerEventTypeCustom) {
-        if(event.event == SCENE_NO_MAN_CUSTOM_EVENT) {
+        if(event.event == SubghzCustomEventSceneShowOnlyRX) {
             scene_manager_previous_scene(subghz->scene_manager);
             return true;
         }

+ 14 - 2
applications/subghz/scenes/subghz_scene_start.c

@@ -1,11 +1,12 @@
 #include "../subghz_i.h"
 
 enum SubmenuIndex {
-    SubmenuIndexRead,
+    SubmenuIndexRead = 10,
     SubmenuIndexSaved,
     SubmenuIndexTest,
     SubmenuIndexAddManualy,
     SubmenuIndexFrequencyAnalyzer,
+    SubmenuIndexSaveRAW,
 };
 
 void subghz_scene_start_submenu_callback(void* context, uint32_t index) {
@@ -20,6 +21,12 @@ void subghz_scene_start_on_enter(void* context) {
     }
     submenu_add_item(
         subghz->submenu, "Read", SubmenuIndexRead, subghz_scene_start_submenu_callback, subghz);
+    submenu_add_item(
+        subghz->submenu,
+        "Read Raw",
+        SubmenuIndexSaveRAW,
+        subghz_scene_start_submenu_callback,
+        subghz);
     submenu_add_item(
         subghz->submenu, "Saved", SubmenuIndexSaved, subghz_scene_start_submenu_callback, subghz);
     submenu_add_item(
@@ -47,7 +54,12 @@ bool subghz_scene_start_on_event(void* context, SceneManagerEvent event) {
     SubGhz* subghz = context;
 
     if(event.type == SceneManagerEventTypeCustom) {
-        if(event.event == SubmenuIndexRead) {
+        if(event.event == SubmenuIndexSaveRAW) {
+            scene_manager_set_scene_state(
+                subghz->scene_manager, SubGhzSceneStart, SubmenuIndexSaveRAW);
+            scene_manager_next_scene(subghz->scene_manager, SubGhzSceneSaveRAW);
+            return true;
+        } else if(event.event == SubmenuIndexRead) {
             scene_manager_set_scene_state(
                 subghz->scene_manager, SubGhzSceneStart, SubmenuIndexRead);
             scene_manager_next_scene(subghz->scene_manager, SubGhzSceneReceiver);

+ 17 - 9
applications/subghz/scenes/subghz_scene_transmitter.c

@@ -2,13 +2,13 @@
 #include "../views/subghz_transmitter.h"
 #include <lib/subghz/protocols/subghz_protocol_keeloq.h>
 
-void subghz_scene_transmitter_callback(SubghzTransmitterEvent event, void* context) {
+void subghz_scene_transmitter_callback(SubghzCustomEvent event, void* context) {
     furi_assert(context);
     SubGhz* subghz = context;
     view_dispatcher_send_custom_event(subghz->view_dispatcher, event);
 }
 
-static void subghz_scene_transmitter_update_data_show(void* context) {
+bool subghz_scene_transmitter_update_data_show(void* context) {
     SubGhz* subghz = context;
 
     if(subghz->txrx->protocol_result && subghz->txrx->protocol_result->get_upload_protocol) {
@@ -51,17 +51,22 @@ static void subghz_scene_transmitter_update_data_show(void* context) {
             preset_str,
             show_button);
         string_clear(key_str);
-    } else {
-        string_set(subghz->error_str, "Protocol not found");
-        scene_manager_next_scene(subghz->scene_manager, SubGhzSceneShowError);
+
+        return true;
     }
+    return false;
 }
 
 void subghz_scene_transmitter_on_enter(void* context) {
     SubGhz* subghz = context;
+    if(!subghz_scene_transmitter_update_data_show(subghz)) {
+        view_dispatcher_send_custom_event(
+            subghz->view_dispatcher, SubghzCustomEventViewTransmitterError);
+    }
+
     subghz_transmitter_set_callback(
         subghz->subghz_transmitter, subghz_scene_transmitter_callback, subghz);
-    subghz_scene_transmitter_update_data_show(subghz);
+
     subghz->state_notifications = NOTIFICATION_IDLE_STATE;
     view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewTransmitter);
 }
@@ -69,7 +74,7 @@ void subghz_scene_transmitter_on_enter(void* context) {
 bool subghz_scene_transmitter_on_event(void* context, SceneManagerEvent event) {
     SubGhz* subghz = context;
     if(event.type == SceneManagerEventTypeCustom) {
-        if(event.event == SubghzTransmitterEventSendStart) {
+        if(event.event == SubghzCustomEventViewTransmitterSendStart) {
             subghz->state_notifications = NOTIFICATION_IDLE_STATE;
             if(subghz->txrx->txrx_state == SubGhzTxRxStateRx) {
                 subghz_rx_end(subghz);
@@ -84,18 +89,21 @@ bool subghz_scene_transmitter_on_event(void* context, SceneManagerEvent event) {
                 }
             }
             return true;
-        } else if(event.event == SubghzTransmitterEventSendStop) {
+        } else if(event.event == SubghzCustomEventViewTransmitterSendStop) {
             subghz->state_notifications = NOTIFICATION_IDLE_STATE;
             if(subghz->txrx->txrx_state == SubGhzTxRxStateTx) {
                 subghz_tx_stop(subghz);
                 subghz_sleep(subghz);
             }
             return true;
-        } else if(event.event == SubghzTransmitterEventBack) {
+        } else if(event.event == SubghzCustomEventViewTransmitterBack) {
             subghz->state_notifications = NOTIFICATION_IDLE_STATE;
             scene_manager_search_and_switch_to_previous_scene(
                 subghz->scene_manager, SubGhzSceneStart);
             return true;
+        } else if(event.event == SubghzCustomEventViewTransmitterError) {
+            string_set(subghz->error_str, "Protocol not found");
+            scene_manager_next_scene(subghz->scene_manager, SubGhzSceneShowError);
         }
     } else if(event.type == SceneManagerEventTypeTick) {
         if(subghz->state_notifications == NOTIFICATION_TX_STATE) {

+ 12 - 0
applications/subghz/subghz.c

@@ -140,6 +140,13 @@ SubGhz* subghz_alloc() {
         SubGhzViewFrequencyAnalyzer,
         subghz_frequency_analyzer_get_view(subghz->subghz_frequency_analyzer));
 
+    // Save RAW
+    subghz->subghz_save_raw = subghz_save_raw_alloc();
+    view_dispatcher_add_view(
+        subghz->view_dispatcher,
+        SubGhzViewSaveRAW,
+        subghz_save_raw_get_view(subghz->subghz_save_raw));
+
     // Carrier Test Module
     subghz->subghz_test_carrier = subghz_test_carrier_alloc();
     view_dispatcher_add_view(
@@ -181,6 +188,7 @@ SubGhz* subghz_alloc() {
 
     subghz_parser_load_keeloq_file(subghz->txrx->parser, "/ext/subghz/keeloq_mfcodes");
     subghz_parser_load_nice_flor_s_file(subghz->txrx->parser, "/ext/subghz/nice_floor_s_rx");
+    subghz_parser_load_came_atomo_file(subghz->txrx->parser, "/ext/subghz/came_atomo");
 
     //subghz_parser_enable_dump_text(subghz->protocol, subghz_text_callback, subghz);
 
@@ -226,6 +234,10 @@ void subghz_free(SubGhz* subghz) {
     view_dispatcher_remove_view(subghz->view_dispatcher, SubGhzViewFrequencyAnalyzer);
     subghz_frequency_analyzer_free(subghz->subghz_frequency_analyzer);
 
+    // Save RAW
+    view_dispatcher_remove_view(subghz->view_dispatcher, SubGhzViewSaveRAW);
+    subghz_save_raw_free(subghz->subghz_save_raw);
+
     // Submenu
     view_dispatcher_remove_view(subghz->view_dispatcher, SubGhzViewMenu);
     submenu_free(subghz->submenu);

+ 27 - 3
applications/subghz/subghz_i.c

@@ -191,7 +191,7 @@ bool subghz_key_load(SubGhz* subghz, const char* file_path) {
             break;
         }
         if(!subghz->txrx->protocol_result->to_load_protocol_from_file(
-               file_worker, subghz->txrx->protocol_result)) {
+               file_worker, subghz->txrx->protocol_result, string_get_cstr(path))) {
             break;
         }
         loaded = true;
@@ -334,8 +334,10 @@ bool subghz_load_protocol_from_file(SubGhz* subghz) {
         if(subghz->txrx->protocol_result == NULL) {
             break;
         }
-        if(!subghz->txrx->protocol_result->to_load_protocol_from_file(
-               file_worker, subghz->txrx->protocol_result)) {
+
+        if(subghz->txrx->protocol_result->to_load_protocol_from_file == NULL ||
+           !subghz->txrx->protocol_result->to_load_protocol_from_file(
+               file_worker, subghz->txrx->protocol_result, string_get_cstr(protocol_file_name))) {
             break;
         }
         res = true;
@@ -354,6 +356,28 @@ bool subghz_load_protocol_from_file(SubGhz* subghz) {
     return res;
 }
 
+bool subghz_rename_file(SubGhz* subghz) {
+    furi_assert(subghz);
+    bool ret = false;
+    string_t old_path;
+    string_t new_path;
+
+    FileWorker* file_worker = file_worker_alloc(false);
+
+    string_init_printf(
+        old_path, "%s/%s%s", SUBGHZ_APP_PATH_FOLDER, subghz->file_name_tmp, SUBGHZ_APP_EXTENSION);
+
+    string_init_printf(
+        new_path, "%s/%s%s", SUBGHZ_APP_PATH_FOLDER, subghz->file_name, SUBGHZ_APP_EXTENSION);
+
+    ret = file_worker_rename(file_worker, string_get_cstr(old_path), string_get_cstr(new_path));
+    string_clear(old_path);
+    string_clear(new_path);
+    file_worker_close(file_worker);
+    file_worker_free(file_worker);
+    return ret;
+}
+
 bool subghz_delete_file(SubGhz* subghz) {
     furi_assert(subghz);
 

+ 5 - 0
applications/subghz/subghz_i.h

@@ -4,6 +4,7 @@
 #include "views/subghz_receiver.h"
 #include "views/subghz_transmitter.h"
 #include "views/subghz_frequency_analyzer.h"
+#include "views/subghz_save_raw.h"
 
 #include "views/subghz_test_static.h"
 #include "views/subghz_test_carrier.h"
@@ -64,6 +65,7 @@ struct SubGhzTxRx {
     SubGhzWorker* worker;
     SubGhzParser* parser;
     SubGhzProtocolCommon* protocol_result;
+    //SubGhzProtocolCommon* protocol_save_raw;
     SubGhzProtocolCommonEncoder* encoder;
     uint32_t frequency;
     FuriHalSubGhzPreset preset;
@@ -100,6 +102,7 @@ struct SubGhz {
     VariableItemList* variable_item_list;
 
     SubghzFrequencyAnalyzer* subghz_frequency_analyzer;
+    SubghzSaveRAW* subghz_save_raw;
     SubghzTestStatic* subghz_test_static;
     SubghzTestCarrier* subghz_test_carrier;
     SubghzTestPacket* subghz_test_packet;
@@ -116,6 +119,7 @@ typedef enum {
     SubGhzViewTransmitter,
     SubGhzViewVariableItemList,
     SubGhzViewFrequencyAnalyzer,
+    SubGhzViewSaveRAW,
     SubGhzViewStatic,
     SubGhzViewTestCarrier,
     SubGhzViewTestPacket,
@@ -130,6 +134,7 @@ void subghz_tx_stop(SubGhz* subghz);
 bool subghz_key_load(SubGhz* subghz, const char* file_path);
 bool subghz_save_protocol_to_file(SubGhz* subghz, const char* dev_name);
 bool subghz_load_protocol_from_file(SubGhz* subghz);
+bool subghz_rename_file(SubGhz* subghz);
 bool subghz_delete_file(SubGhz* subghz);
 void subghz_file_name_clear(SubGhz* subghz);
 uint32_t subghz_random_serial(void);

+ 2 - 5
applications/subghz/views/subghz_frequency_analyzer.h

@@ -1,14 +1,11 @@
 #pragma once
 
 #include <gui/view.h>
-
-typedef enum {
-    SubghzFrequencyAnalyzerEventOnlyRx,
-} SubghzFrequencyAnalyzerEvent;
+#include "../helpers/subghz_custom_event.h"
 
 typedef struct SubghzFrequencyAnalyzer SubghzFrequencyAnalyzer;
 
-typedef void (*SubghzFrequencyAnalyzerCallback)(SubghzFrequencyAnalyzerEvent event, void* context);
+typedef void (*SubghzFrequencyAnalyzerCallback)(SubghzCustomEvent event, void* context);
 
 void subghz_frequency_analyzer_set_callback(
     SubghzFrequencyAnalyzer* subghz_frequency_analyzer,

+ 4 - 3
applications/subghz/views/subghz_receiver.c

@@ -181,7 +181,7 @@ bool subghz_receiver_input(InputEvent* event, void* context) {
     SubghzReceiver* subghz_receiver = context;
 
     if(event->key == InputKeyBack && event->type == InputTypeShort) {
-        subghz_receiver->callback(SubghzReceverEventBack, subghz_receiver->context);
+        subghz_receiver->callback(SubghzCustomEventViewReceverBack, subghz_receiver->context);
     } else if(
         event->key == InputKeyUp &&
         (event->type == InputTypeShort || event->type == InputTypeRepeat)) {
@@ -199,12 +199,13 @@ bool subghz_receiver_input(InputEvent* event, void* context) {
                 return true;
             });
     } else if(event->key == InputKeyLeft && event->type == InputTypeShort) {
-        subghz_receiver->callback(SubghzReceverEventConfig, subghz_receiver->context);
+        subghz_receiver->callback(SubghzCustomEventViewReceverConfig, subghz_receiver->context);
     } else if(event->key == InputKeyOk && event->type == InputTypeShort) {
         with_view_model(
             subghz_receiver->view, (SubghzReceiverModel * model) {
                 if(model->history_item != 0) {
-                    subghz_receiver->callback(SubghzReceverEventOK, subghz_receiver->context);
+                    subghz_receiver->callback(
+                        SubghzCustomEventViewReceverOK, subghz_receiver->context);
                 }
                 return false;
             });

+ 2 - 7
applications/subghz/views/subghz_receiver.h

@@ -1,16 +1,11 @@
 #pragma once
 
 #include <gui/view.h>
-
-typedef enum {
-    SubghzReceverEventOK,
-    SubghzReceverEventConfig,
-    SubghzReceverEventBack,
-} SubghzReceverEvent;
+#include "../helpers/subghz_custom_event.h"
 
 typedef struct SubghzReceiver SubghzReceiver;
 
-typedef void (*SubghzReceiverCallback)(SubghzReceverEvent event, void* context);
+typedef void (*SubghzReceiverCallback)(SubghzCustomEvent event, void* context);
 
 void subghz_receiver_set_callback(
     SubghzReceiver* subghz_receiver,

+ 313 - 0
applications/subghz/views/subghz_save_raw.c

@@ -0,0 +1,313 @@
+#include "subghz_save_raw.h"
+#include "../subghz_i.h"
+
+#include <math.h>
+#include <furi.h>
+#include <furi-hal.h>
+#include <input/input.h>
+#include <gui/elements.h>
+#include <lib/subghz/protocols/subghz_protocol_princeton.h>
+
+#include <assets_icons.h>
+#define SUBGHZ_SAVE_RAW_RSSI_HISTORY_SIZE 100
+
+typedef enum {
+    SubghzSaveRAWStatusStart,
+    SubghzSaveRAWStatusIDLE,
+    SubghzSaveRAWStatusREC,
+    SubghzSaveRAWStatusShowName,
+} SubghzSaveRAWStatus;
+
+struct SubghzSaveRAW {
+    View* view;
+    osTimerId timer;
+    SubghzSaveRAWCallback callback;
+    void* context;
+};
+
+typedef struct {
+    string_t frequency_str;
+    string_t preset_str;
+    string_t sample_write;
+    string_t file_name;
+    uint8_t* rssi_history;
+    bool rssi_history_end;
+    uint8_t ind_write;
+    SubghzSaveRAWStatus satus;
+} SubghzSaveRAWModel;
+
+void subghz_save_raw_set_callback(
+    SubghzSaveRAW* subghz_save_raw,
+    SubghzSaveRAWCallback callback,
+    void* context) {
+    furi_assert(subghz_save_raw);
+    furi_assert(callback);
+    subghz_save_raw->callback = callback;
+    subghz_save_raw->context = context;
+}
+
+void subghz_save_raw_add_data_statusbar(
+    SubghzSaveRAW* instance,
+    const char* frequency_str,
+    const char* preset_str) {
+    furi_assert(instance);
+    with_view_model(
+        instance->view, (SubghzSaveRAWModel * model) {
+            string_set(model->frequency_str, frequency_str);
+            string_set(model->preset_str, preset_str);
+            return true;
+        });
+}
+
+void subghz_save_raw_set_file_name(SubghzSaveRAW* instance, const char* file_name) {
+    furi_assert(instance);
+    with_view_model(
+        instance->view, (SubghzSaveRAWModel * model) {
+            string_set(model->file_name, file_name);
+            return true;
+        });
+}
+
+static void subghz_save_raw_timer_callback(void* context) {
+    furi_assert(context);
+    SubghzSaveRAW* instance = context;
+
+    with_view_model(
+        instance->view, (SubghzSaveRAWModel * model) {
+            model->satus = SubghzSaveRAWStatusIDLE;
+            return true;
+        });
+}
+
+void subghz_save_raw_add_data_rssi(SubghzSaveRAW* instance, float rssi) {
+    furi_assert(instance);
+    uint8_t u_rssi = 0;
+
+    if(rssi < -90) {
+        u_rssi = 0;
+    } else {
+        u_rssi = (uint8_t)((rssi + 90) / 2.7);
+    }
+    //if(u_rssi > 34) u_rssi = 34;
+
+    with_view_model(
+        instance->view, (SubghzSaveRAWModel * model) {
+            model->rssi_history[model->ind_write++] = u_rssi;
+            if(model->ind_write > SUBGHZ_SAVE_RAW_RSSI_HISTORY_SIZE) {
+                model->rssi_history_end = true;
+                model->ind_write = 0;
+            }
+            return true;
+        });
+}
+
+void subghz_save_raw_update_sample_write(SubghzSaveRAW* instance, size_t sample) {
+    furi_assert(instance);
+
+    with_view_model(
+        instance->view, (SubghzSaveRAWModel * model) {
+            string_printf(model->sample_write, "%d spl.", sample);
+            return false;
+        });
+}
+
+void subghz_save_raw_draw_rssi(Canvas* canvas, SubghzSaveRAWModel* model) {
+    int ind = 0;
+    int base = 0;
+    if(model->rssi_history_end == false) {
+        for(int i = model->ind_write; i >= 0; i--) {
+            canvas_draw_line(canvas, i, 47, i, 47 - model->rssi_history[i]);
+        }
+        if(model->ind_write > 3) {
+            canvas_draw_line(canvas, model->ind_write, 47, model->ind_write, 13);
+            canvas_draw_line(canvas, model->ind_write - 2, 12, model->ind_write + 2, 12);
+            canvas_draw_line(canvas, model->ind_write - 1, 13, model->ind_write + 1, 13);
+        }
+    } else {
+        base = SUBGHZ_SAVE_RAW_RSSI_HISTORY_SIZE - model->ind_write;
+        for(int i = SUBGHZ_SAVE_RAW_RSSI_HISTORY_SIZE; i >= 0; i--) {
+            ind = i - base;
+            if(ind < 0) ind += SUBGHZ_SAVE_RAW_RSSI_HISTORY_SIZE;
+            canvas_draw_line(canvas, i, 47, i, 47 - model->rssi_history[ind]);
+        }
+        canvas_draw_line(
+            canvas, SUBGHZ_SAVE_RAW_RSSI_HISTORY_SIZE, 47, SUBGHZ_SAVE_RAW_RSSI_HISTORY_SIZE, 13);
+        canvas_draw_line(
+            canvas,
+            SUBGHZ_SAVE_RAW_RSSI_HISTORY_SIZE - 2,
+            12,
+            SUBGHZ_SAVE_RAW_RSSI_HISTORY_SIZE + 2,
+            12);
+        canvas_draw_line(
+            canvas,
+            SUBGHZ_SAVE_RAW_RSSI_HISTORY_SIZE - 1,
+            13,
+            SUBGHZ_SAVE_RAW_RSSI_HISTORY_SIZE + 1,
+            13);
+    }
+}
+
+void subghz_save_raw_draw(Canvas* canvas, SubghzSaveRAWModel* model) {
+    canvas_set_color(canvas, ColorBlack);
+    canvas_set_font(canvas, FontSecondary);
+    if(model->satus != SubghzSaveRAWStatusShowName) {
+        canvas_draw_str(canvas, 5, 8, string_get_cstr(model->frequency_str));
+        canvas_draw_str(canvas, 40, 8, string_get_cstr(model->preset_str));
+        canvas_draw_str_aligned(
+            canvas, 126, 0, AlignRight, AlignTop, string_get_cstr(model->sample_write));
+    } else {
+        canvas_draw_str_aligned(
+            canvas, 61, 1, AlignRight, AlignTop, string_get_cstr(model->file_name));
+        canvas_draw_str(canvas, 65, 8, "Saved!");
+    }
+
+    canvas_draw_line(canvas, 0, 14, 115, 14);
+    subghz_save_raw_draw_rssi(canvas, model);
+    canvas_draw_line(canvas, 0, 48, 115, 48);
+    canvas_draw_line(canvas, 115, 14, 115, 48);
+
+    if(model->satus == SubghzSaveRAWStatusIDLE) {
+        elements_button_left(canvas, "Config");
+        elements_button_center(canvas, "REC");
+        elements_button_right(canvas, "More");
+    } else if(model->satus == SubghzSaveRAWStatusStart) {
+        elements_button_left(canvas, "Config");
+        elements_button_center(canvas, "REC");
+    } else {
+        elements_button_center(canvas, "Stop");
+    }
+
+    canvas_set_font_direction(canvas, 3);
+    canvas_draw_str(canvas, 126, 40, "RSSI");
+    canvas_set_font_direction(canvas, 0);
+}
+
+bool subghz_save_raw_input(InputEvent* event, void* context) {
+    furi_assert(context);
+    SubghzSaveRAW* instance = context;
+
+    if(event->key == InputKeyBack && event->type == InputTypeShort) {
+        instance->callback(SubghzCustomEventViewSaveRAWBack, instance->context);
+    } else if(event->key == InputKeyLeft && event->type == InputTypeShort) {
+        with_view_model(
+            instance->view, (SubghzSaveRAWModel * model) {
+                if(model->satus == SubghzSaveRAWStatusIDLE ||
+                   model->satus == SubghzSaveRAWStatusStart) {
+                    instance->callback(SubghzCustomEventViewSaveRAWConfig, instance->context);
+                }
+                return true;
+            });
+    } else if(event->key == InputKeyRight && event->type == InputTypeShort) {
+        with_view_model(
+            instance->view, (SubghzSaveRAWModel * model) {
+                if(model->satus == SubghzSaveRAWStatusIDLE) {
+                    instance->callback(SubghzCustomEventViewSaveRAWMore, instance->context);
+                }
+                return true;
+            });
+    } else if(event->key == InputKeyOk && event->type == InputTypeShort) {
+        with_view_model(
+            instance->view, (SubghzSaveRAWModel * model) {
+                if(model->satus == SubghzSaveRAWStatusIDLE ||
+                   model->satus == SubghzSaveRAWStatusStart) {
+                    instance->callback(SubghzCustomEventViewSaveRAWREC, instance->context);
+                    model->satus = SubghzSaveRAWStatusREC;
+                    model->ind_write = 0;
+                    model->rssi_history_end = false;
+                } else {
+                    instance->callback(SubghzCustomEventViewSaveRAWIDLE, instance->context);
+                    model->satus = SubghzSaveRAWStatusShowName;
+                    osTimerStart(instance->timer, 1024);
+                }
+                return true;
+            });
+    }
+
+    if(event->key == InputKeyBack) {
+        return false;
+    }
+
+    return true;
+}
+
+void subghz_save_raw_enter(void* context) {
+    furi_assert(context);
+    SubghzSaveRAW* instance = context;
+
+    with_view_model(
+        instance->view, (SubghzSaveRAWModel * model) {
+            model->satus = SubghzSaveRAWStatusStart;
+            model->rssi_history = furi_alloc(SUBGHZ_SAVE_RAW_RSSI_HISTORY_SIZE * sizeof(uint8_t));
+            model->rssi_history_end = false;
+            model->ind_write = 0;
+            string_set(model->sample_write, "0 spl.");
+            return true;
+        });
+}
+
+void subghz_save_raw_exit(void* context) {
+    furi_assert(context);
+    SubghzSaveRAW* instance = context;
+
+    with_view_model(
+        instance->view, (SubghzSaveRAWModel * model) {
+            if(model->satus != SubghzSaveRAWStatusIDLE &&
+               model->satus != SubghzSaveRAWStatusStart) {
+                instance->callback(SubghzCustomEventViewSaveRAWIDLE, instance->context);
+                model->satus = SubghzSaveRAWStatusStart;
+            }
+            string_clean(model->frequency_str);
+            string_clean(model->preset_str);
+            string_clean(model->sample_write);
+            string_clean(model->file_name);
+            free(model->rssi_history);
+            return true;
+        });
+}
+
+SubghzSaveRAW* subghz_save_raw_alloc() {
+    SubghzSaveRAW* instance = furi_alloc(sizeof(SubghzSaveRAW));
+
+    // View allocation and configuration
+    instance->view = view_alloc();
+    view_allocate_model(instance->view, ViewModelTypeLocking, sizeof(SubghzSaveRAWModel));
+    view_set_context(instance->view, instance);
+    view_set_draw_callback(instance->view, (ViewDrawCallback)subghz_save_raw_draw);
+    view_set_input_callback(instance->view, subghz_save_raw_input);
+    view_set_enter_callback(instance->view, subghz_save_raw_enter);
+    view_set_exit_callback(instance->view, subghz_save_raw_exit);
+
+    instance->timer = osTimerNew(subghz_save_raw_timer_callback, osTimerOnce, instance, NULL);
+
+    with_view_model(
+        instance->view, (SubghzSaveRAWModel * model) {
+            string_init(model->frequency_str);
+            string_init(model->preset_str);
+            string_init(model->sample_write);
+            string_init(model->file_name);
+            return true;
+        });
+
+    return instance;
+}
+
+void subghz_save_raw_free(SubghzSaveRAW* instance) {
+    furi_assert(instance);
+
+    with_view_model(
+        instance->view, (SubghzSaveRAWModel * model) {
+            string_clear(model->frequency_str);
+            string_clear(model->preset_str);
+            string_clear(model->sample_write);
+            string_clear(model->file_name);
+            return true;
+        });
+    osTimerDelete(instance->timer);
+    view_free(instance->view);
+    free(instance);
+}
+
+View* subghz_save_raw_get_view(SubghzSaveRAW* instance) {
+    furi_assert(instance);
+    return instance->view;
+}

+ 30 - 0
applications/subghz/views/subghz_save_raw.h

@@ -0,0 +1,30 @@
+#pragma once
+
+#include <gui/view.h>
+#include "../helpers/subghz_custom_event.h"
+
+typedef struct SubghzSaveRAW SubghzSaveRAW;
+
+typedef void (*SubghzSaveRAWCallback)(SubghzCustomEvent event, void* context);
+
+void subghz_save_raw_set_callback(
+    SubghzSaveRAW* subghz_save_raw,
+    SubghzSaveRAWCallback callback,
+    void* context);
+
+SubghzSaveRAW* subghz_save_raw_alloc();
+
+void subghz_save_raw_free(SubghzSaveRAW* subghz_static);
+
+void subghz_save_raw_add_data_statusbar(
+    SubghzSaveRAW* instance,
+    const char* frequency_str,
+    const char* preset_str);
+
+void subghz_save_raw_set_file_name(SubghzSaveRAW* instance, const char* file_name);
+
+void subghz_save_raw_update_sample_write(SubghzSaveRAW* instance, size_t sample);
+
+void subghz_save_raw_add_data_rssi(SubghzSaveRAW* instance, float rssi);
+
+View* subghz_save_raw_get_view(SubghzSaveRAW* subghz_static);

+ 4 - 2
applications/subghz/views/subghz_transmitter.c

@@ -111,10 +111,12 @@ bool subghz_transmitter_input(InputEvent* event, void* context) {
         });
 
     if(can_be_sent && event->key == InputKeyOk && event->type == InputTypePress) {
-        subghz_transmitter->callback(SubghzTransmitterEventSendStart, subghz_transmitter->context);
+        subghz_transmitter->callback(
+            SubghzCustomEventViewTransmitterSendStart, subghz_transmitter->context);
         return true;
     } else if(can_be_sent && event->key == InputKeyOk && event->type == InputTypeRelease) {
-        subghz_transmitter->callback(SubghzTransmitterEventSendStop, subghz_transmitter->context);
+        subghz_transmitter->callback(
+            SubghzCustomEventViewTransmitterSendStop, subghz_transmitter->context);
         return true;
     }
 

+ 2 - 8
applications/subghz/views/subghz_transmitter.h

@@ -1,17 +1,11 @@
 #pragma once
 
 #include <gui/view.h>
-
-typedef enum {
-    SubghzTransmitterEventSendStart,
-    SubghzTransmitterEventSendStop,
-    SubghzTransmitterEventBack,
-    SubghzTransmitterEventNoMan,
-} SubghzTransmitterEvent;
+#include "../helpers/subghz_custom_event.h"
 
 typedef struct SubghzTransmitter SubghzTransmitter;
 
-typedef void (*SubghzTransmitterCallback)(SubghzTransmitterEvent event, void* context);
+typedef void (*SubghzTransmitterCallback)(SubghzCustomEvent event, void* context);
 
 void subghz_transmitter_set_callback(
     SubghzTransmitter* subghz_transmitter,

+ 1 - 0
firmware/targets/f6/furi-hal/furi-hal-subghz.c

@@ -658,6 +658,7 @@ static void furi_hal_subghz_async_tx_refill(uint32_t* buffer, size_t samples) {
         bool is_odd = samples % 2;
         LevelDuration ld =
             furi_hal_subghz_async_tx.callback(furi_hal_subghz_async_tx.callback_context);
+        if(level_duration_is_wait(ld)) return;
         if(level_duration_is_reset(ld)) {
             // One more even sample required to end at low level
             if(is_odd) {

+ 1 - 0
firmware/targets/f7/furi-hal/furi-hal-subghz.c

@@ -658,6 +658,7 @@ static void furi_hal_subghz_async_tx_refill(uint32_t* buffer, size_t samples) {
         bool is_odd = samples % 2;
         LevelDuration ld =
             furi_hal_subghz_async_tx.callback(furi_hal_subghz_async_tx.callback_context);
+        if(level_duration_is_wait(ld)) return;
         if(level_duration_is_reset(ld)) {
             // One more even sample required to end at low level
             if(is_odd) {

+ 31 - 1
lib/app-scened-template/file-worker.c

@@ -79,6 +79,32 @@ bool file_worker_remove(FileWorker* file_worker, const char* filename) {
     return file_worker_check_common_errors(file_worker);
 }
 
+void file_worker_get_next_filename(
+    FileWorker* file_worker,
+    const char* dirname,
+    const char* filename,
+    const char* fileextension,
+    string_t nextfilename) {
+    string_t temp_str;
+    string_init(temp_str);
+    uint16_t num = 0;
+
+    string_printf(temp_str, "%s/%s%s", dirname, filename, fileextension);
+
+    while(storage_common_stat(file_worker->api, string_get_cstr(temp_str), NULL) == FSE_OK) {
+        num++;
+        string_printf(temp_str, "%s/%s%d%s", dirname, filename, num, fileextension);
+    }
+
+    if(num) {
+        string_printf(nextfilename, "%s%d", filename, num);
+    } else {
+        string_printf(nextfilename, "%s", filename);
+    }
+
+    string_clear(temp_str);
+}
+
 bool file_worker_read(FileWorker* file_worker, void* buffer, uint16_t bytes_to_read) {
     if(!file_worker_read_internal(file_worker, buffer, bytes_to_read)) {
         return false;
@@ -355,7 +381,11 @@ bool file_worker_read_until_buffered(
     return string_size(str_result) || *file_buf_cnt;
 }
 
-bool file_worker_get_value_from_key(FileWorker* file_worker, string_t key, char delimiter, string_t value) {
+bool file_worker_get_value_from_key(
+    FileWorker* file_worker,
+    string_t key,
+    char delimiter,
+    string_t value) {
     bool found = false;
     string_t next_line;
     string_t next_key;

+ 21 - 1
lib/app-scened-template/file-worker.h

@@ -68,6 +68,22 @@ bool file_worker_mkdir(FileWorker* file_worker, const char* dirname);
  */
 bool file_worker_remove(FileWorker* file_worker, const char* filename);
 
+/**
+ * @brief Get next free filename.
+ * 
+ * @param file_worker FileWorker instance 
+ * @param dirname 
+ * @param filename 
+ * @param fileextension 
+ * @param nextfilename return name
+ */
+void file_worker_get_next_filename(
+    FileWorker* file_worker,
+    const char* dirname,
+    const char* filename,
+    const char* fileextension,
+    string_t nextfilename);
+
 /**
  * @brief Reads data from a file.
  * 
@@ -194,7 +210,11 @@ bool file_worker_read_until_buffered(
  * @param value value for given key
  * @return true on success
  */
-bool file_worker_get_value_from_key(FileWorker* file_worker, string_t key, char delimiter, string_t value);
+bool file_worker_get_value_from_key(
+    FileWorker* file_worker,
+    string_t key,
+    char delimiter,
+    string_t value);
 
 /**
  * @brief Check whether file exist or not

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

@@ -182,7 +182,8 @@ void subghz_protocol_came_to_save_str(SubGhzProtocolCame* instance, string_t out
 
 bool subghz_protocol_came_to_load_protocol_from_file(
     FileWorker* file_worker,
-    SubGhzProtocolCame* instance) {
+    SubGhzProtocolCame* instance,
+    const char* file_path) {
     bool loaded = false;
     string_t temp_str;
     string_init(temp_str);

+ 4 - 2
lib/subghz/protocols/subghz_protocol_came.h

@@ -56,15 +56,17 @@ void subghz_protocol_came_to_save_str(SubGhzProtocolCame* instance, string_t out
  * 
  * @param file_worker - FileWorker file_worker
  * @param instance - SubGhzProtocolCame instance
+ * @param file_path - file path
  * @return bool
  */
 bool subghz_protocol_came_to_load_protocol_from_file(
     FileWorker* file_worker,
-    SubGhzProtocolCame* instance);
+    SubGhzProtocolCame* instance,
+    const char* file_path);
 
 /** Loading protocol from bin data
  * 
  * @param instance - SubGhzProtocolCame instance
  * @param context - SubGhzProtocolCommonLoad context
  */
-void subghz_decoder_came_to_load_protocol(SubGhzProtocolCame* instance, void* context);
+void subghz_decoder_came_to_load_protocol(SubGhzProtocolCame* instance, void* context);

+ 123 - 21
lib/subghz/protocols/subghz_protocol_came_atomo.c

@@ -2,9 +2,12 @@
 #include "subghz_protocol_common.h"
 #include <lib/toolbox/manchester-decoder.h>
 
+#define SUBGHZ_NO_CAME_ATOMO_RAINBOW_TABLE 0xFFFFFFFFFFFFFFFF
+
 struct SubGhzProtocolCameAtomo {
     SubGhzProtocolCommon common;
     ManchesterState manchester_saved_state;
+    const char* rainbow_table_file_name;
 };
 
 typedef enum {
@@ -39,11 +42,118 @@ void subghz_protocol_came_atomo_free(SubGhzProtocolCameAtomo* instance) {
     free(instance);
 }
 
+void subghz_protocol_came_atomo_name_file(SubGhzProtocolCameAtomo* instance, const char* name) {
+    instance->rainbow_table_file_name = name;
+    printf("Loading CAME Atomo rainbow table %s\r\n", name);
+}
+
+/** Read bytes from rainbow table
+ * 
+ * @param instance - SubGhzProtocolCameAtomo* instance
+ * @param number_atomo_magic_xor
+ * @return atomo_magic_xor
+ */
+uint64_t subghz_came_atomo_get_atomo_magic_xor_in_file(
+    SubGhzProtocolCameAtomo* instance,
+    uint8_t number_atomo_magic_xor) {
+    if(!strcmp(instance->rainbow_table_file_name, "")) return SUBGHZ_NO_CAME_ATOMO_RAINBOW_TABLE;
+
+    uint8_t buffer[sizeof(uint64_t)] = {0};
+    uint32_t address = number_atomo_magic_xor * sizeof(uint64_t);
+    uint64_t atomo_magic_xor = 0;
+
+    FileWorker* file_worker = file_worker_alloc(true);
+    if(file_worker_open(
+           file_worker, instance->rainbow_table_file_name, FSAM_READ, FSOM_OPEN_EXISTING)) {
+        file_worker_seek(file_worker, address, true);
+        file_worker_read(file_worker, &buffer, sizeof(uint64_t));
+        for(size_t i = 0; i < sizeof(uint64_t); i++) {
+            atomo_magic_xor = (atomo_magic_xor << 8) | buffer[i];
+        }
+    } else {
+        atomo_magic_xor = SUBGHZ_NO_CAME_ATOMO_RAINBOW_TABLE;
+    }
+    file_worker_close(file_worker);
+    file_worker_free(file_worker);
+
+    return atomo_magic_xor;
+}
+
 /** Analysis of received data
  * 
  * @param instance SubGhzProtocolCameAtomo instance
  */
 void subghz_protocol_came_atomo_remote_controller(SubGhzProtocolCameAtomo* instance) {
+    /* 
+    * 0x1fafef3ed0f7d9ef
+    * 0x185fcc1531ee86e7
+    * 0x184fa96912c567ff
+    * 0x187f8a42f3dc38f7
+    * 0x186f63915492a5cd
+    * 0x181f40bab58bfac5
+    * 0x180f25c696a01bdd
+    * 0x183f06ed77b944d5
+    * 0x182ef661d83d21a9
+    * 0x18ded54a39247ea1
+    * 0x18ceb0361a0f9fb9
+    * 0x18fe931dfb16c0b1
+    * 0x18ee7ace5c585d8b
+    * ........ 
+    * transmission consists of 99 parcels with increasing counter while holding down the button
+    * with each new press, the counter in the encrypted part increases
+    * 
+    * 0x1FAFF13ED0F7D9EF
+    * 0x1FAFF11ED0F7D9EF
+    * 0x1FAFF10ED0F7D9EF
+    * 0x1FAFF0FED0F7D9EF
+    * 0x1FAFF0EED0F7D9EF
+    * 0x1FAFF0DED0F7D9EF
+    * 0x1FAFF0CED0F7D9EF
+    * 0x1FAFF0BED0F7D9EF
+    * 0x1FAFF0AED0F7D9EF 
+    * 
+    *                   where     0x1FAF - parcel counter, 0хF0A - button press counter,
+    *                           0xED0F7D9E - serial number, 0хF -  key
+    * 0x1FAF parcel counter - 1 in the parcel queue ^ 0x185F =  0x07F0
+    * 0x185f ^ 0x185F = 0x0000
+    * 0x184f ^ 0x185F = 0x0010
+    * 0x187f ^ 0x185F = 0x0020
+    * .....
+    * 0x182e ^ 0x185F = 0x0071 
+    * 0x18de ^ 0x185F = 0x0081
+    * .....
+    * 0x1e43 ^ 0x185F = 0x061C
+    *                           where the last nibble is incremented every 8 samples
+    * 
+    * Decode
+    * 
+    * 0x1cf6931dfb16c0b1 => 0x1cf6
+    * 0x1cf6 ^ 0x185F = 0x04A9
+    * 0x04A9 => 0x04A = 74 (dec)
+    * 74+1 % 32(atomo_magic_xor) = 11
+    * GET atomo_magic_xor[11] = 0xXXXXXXXXXXXXXXXX
+    * 0x931dfb16c0b1 ^ 0xXXXXXXXXXXXXXXXX =  0xEF3ED0F7D9EF
+    * 0xEF3 ED0F7D9E F  => 0xEF3 - CNT, 0xED0F7D9E - SN, 0xF - key
+    * 
+    * */
+
+    uint16_t parcel_counter = instance->common.code_last_found >> 48;
+    parcel_counter = parcel_counter ^ 0x185F;
+    parcel_counter >>= 4;
+    uint8_t ind = (parcel_counter + 1) % 32;
+    uint64_t temp_data = instance->common.code_last_found & 0x0000FFFFFFFFFFFF;
+    uint64_t atomo_magic_xor = subghz_came_atomo_get_atomo_magic_xor_in_file(instance, ind);
+
+    if(atomo_magic_xor != SUBGHZ_NO_CAME_ATOMO_RAINBOW_TABLE) {
+        temp_data = temp_data ^ atomo_magic_xor;
+        instance->common.cnt = temp_data >> 36;
+        instance->common.serial = (temp_data >> 4) & 0x000FFFFFFFF;
+        instance->common.btn = temp_data & 0xF;
+    } else {
+        instance->common.cnt = 0;
+        instance->common.serial = 0;
+        instance->common.btn = 0;
+    }
 }
 
 void subghz_protocol_came_atomo_reset(SubGhzProtocolCameAtomo* instance) {
@@ -89,26 +199,10 @@ void subghz_protocol_came_atomo_parse(
             } else if(DURATION_DIFF(duration, instance->common.te_long) < instance->common.te_delta) {
                 event = ManchesterEventLongLow;
             } else if(duration >= (instance->common.te_long * 2 + instance->common.te_delta)) {
-                if(instance->common.code_count_bit >=
+                if(instance->common.code_count_bit ==
                    instance->common.code_min_count_bit_for_found) {
                     instance->common.code_last_found = instance->common.code_found;
                     instance->common.code_last_count_bit = instance->common.code_count_bit;
-                    // uint32_t code_found_hi = instance->common.code_last_found >> 32;
-                    // uint32_t code_found_lo = instance->common.code_last_found & 0x00000000ffffffff;
-
-                    // uint64_t code_found_reverse = subghz_protocol_common_reverse_key(
-                    //     instance->common.code_last_found, instance->common.code_last_count_bit);
-
-                    // uint32_t code_found_reverse_hi = code_found_reverse >> 32;
-                    // uint32_t code_found_reverse_lo = code_found_reverse & 0x00000000ffffffff;
-                    // FURI_LOG_I(
-                    //     "ATOMO",
-                    //     "%08lX%08lX  %08lX%08lX  %d",
-                    //     code_found_hi,
-                    //     code_found_lo,
-                    //     code_found_reverse_hi,
-                    //     code_found_reverse_lo,
-                    //     instance->common.code_last_count_bit);
                     if(instance->common.callback)
                         instance->common.callback(
                             (SubGhzProtocolCommon*)instance, instance->common.context);
@@ -151,17 +245,24 @@ void subghz_protocol_came_atomo_parse(
     }
 }
 void subghz_protocol_came_atomo_to_str(SubGhzProtocolCameAtomo* instance, string_t output) {
+    subghz_protocol_came_atomo_remote_controller(instance);
     uint32_t code_found_hi = instance->common.code_last_found >> 32;
     uint32_t code_found_lo = instance->common.code_last_found & 0x00000000ffffffff;
 
     string_cat_printf(
         output,
-        "%s %dbit\r\n"
-        "Key:0x%lX%08lX\r\n",
+        "%s %db\r\n"
+        "Key:0x%lX%08lX\r\n"
+        "Sn:0x%08lX  Btn:0x%01X\r\n"
+        "Cnt:0x%03X\r\n",
+
         instance->common.name,
         instance->common.code_last_count_bit,
         code_found_hi,
-        code_found_lo);
+        code_found_lo,
+        instance->common.serial,
+        instance->common.btn,
+        instance->common.cnt);
 }
 
 // void subghz_protocol_came_atomo_to_save_str(SubGhzProtocolCameAtomo* instance, string_t output) {
@@ -178,7 +279,8 @@ void subghz_protocol_came_atomo_to_str(SubGhzProtocolCameAtomo* instance, string
 
 // bool subghz_protocol_came_atomo_to_load_protocol_from_file(
 //     FileWorker* file_worker,
-//     SubGhzProtocolCameAtomo* instance) {
+//     SubGhzProtocolCameAtomo* instance,
+//     const char* file_path) {
 //     bool loaded = false;
 //     string_t temp_str;
 //     string_init(temp_str);

+ 10 - 1
lib/subghz/protocols/subghz_protocol_came_atomo.h

@@ -16,6 +16,13 @@ SubGhzProtocolCameAtomo* subghz_protocol_came_atomo_alloc();
  */
 void subghz_protocol_came_atomo_free(SubGhzProtocolCameAtomo* instance);
 
+/** File name rainbow table CAME Atomo
+ * 
+ * @param instance - SubGhzProtocolCameAtomo instance
+ * @param file_name - "path/file_name"
+ */
+void subghz_protocol_came_atomo_name_file(SubGhzProtocolCameAtomo* instance, const char* name);
+
 // /** Get upload protocol
 //  *
 //  * @param instance - SubGhzProtocolCameAtomo instance
@@ -59,11 +66,13 @@ void subghz_protocol_came_atomo_to_str(SubGhzProtocolCameAtomo* instance, string
 //  *
 //  * @param file_worker - FileWorker file_worker
 //  * @param instance - SubGhzProtocolCameAtomo instance
+//  * @param file_path - file path
 //  * @return bool
 //  */
 // bool subghz_protocol_came_atomo_to_load_protocol_from_file(
 //     FileWorker* file_worker,
-//     SubGhzProtocolCameAtomo* instance);
+//     SubGhzProtocolCameAtomo* instance,
+//     const char* file_path);
 
 /** Loading protocol from bin data
  * 

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

@@ -341,7 +341,8 @@ void subghz_protocol_came_twee_to_save_str(SubGhzProtocolCameTwee* instance, str
 
 bool subghz_protocol_came_twee_to_load_protocol_from_file(
     FileWorker* file_worker,
-    SubGhzProtocolCameTwee* instance) {
+    SubGhzProtocolCameTwee* instance,
+    const char* file_path) {
     bool loaded = false;
     string_t temp_str;
     string_init(temp_str);

+ 3 - 1
lib/subghz/protocols/subghz_protocol_came_twee.h

@@ -59,11 +59,13 @@ void subghz_protocol_came_twee_to_save_str(SubGhzProtocolCameTwee* instance, str
  * 
  * @param file_worker - FileWorker file_worker
  * @param instance - SubGhzProtocolCameTwee instance
+ * @param file_path - file path
  * @return bool
  */
 bool subghz_protocol_came_twee_to_load_protocol_from_file(
     FileWorker* file_worker,
-    SubGhzProtocolCameTwee* instance);
+    SubGhzProtocolCameTwee* instance,
+    const char* file_path);
 
 /** Loading protocol from bin data
  * 

+ 53 - 22
lib/subghz/protocols/subghz_protocol_common.c

@@ -2,7 +2,6 @@
 #include <stdio.h>
 #include <lib/toolbox/hex.h>
 
-
 SubGhzProtocolCommonEncoder* subghz_protocol_encoder_common_alloc() {
     SubGhzProtocolCommonEncoder* instance = furi_alloc(sizeof(SubGhzProtocolCommonEncoder));
     instance->upload = furi_alloc(SUBGHZ_ENCODER_UPLOAD_MAX_SIZE * sizeof(LevelDuration));
@@ -13,6 +12,9 @@ SubGhzProtocolCommonEncoder* subghz_protocol_encoder_common_alloc() {
 
 void subghz_protocol_encoder_common_free(SubGhzProtocolCommonEncoder* instance) {
     furi_assert(instance);
+    if(instance->callback_end) {
+        instance->callback_end((SubGhzProtocolCommon*)instance->context_end);
+    }
     free(instance->upload);
     free(instance);
 }
@@ -22,10 +24,34 @@ size_t subghz_encoder_common_get_repeat_left(SubGhzProtocolCommonEncoder* instan
     return instance->repeat;
 }
 
+void subghz_protocol_encoder_common_set_callback(
+    SubGhzProtocolCommonEncoder* instance,
+    SubGhzProtocolCommonEncoderCallback callback,
+    void* context) {
+    furi_assert(instance);
+    furi_assert(callback);
+    instance->callback = callback;
+    instance->context = context;
+}
+
+void subghz_protocol_encoder_common_set_callback_end(
+    SubGhzProtocolCommonEncoder* instance,
+    SubGhzProtocolCommonEncoderCallbackEnd callback_end,
+    void* context_end) {
+    furi_assert(instance);
+    furi_assert(callback_end);
+    instance->callback_end = callback_end;
+    instance->context_end = context_end;
+}
+
 LevelDuration subghz_protocol_encoder_common_yield(void* context) {
     SubGhzProtocolCommonEncoder* instance = context;
 
-    if(instance->repeat == 0){
+    if(instance->callback) {
+        return instance->callback((SubGhzProtocolCommon*)instance->context);
+    }
+
+    if(instance->repeat == 0) {
         return level_duration_reset();
     }
 
@@ -39,46 +65,53 @@ LevelDuration subghz_protocol_encoder_common_yield(void* context) {
     return ret;
 }
 
-void subghz_protocol_common_add_bit(SubGhzProtocolCommon *common, uint8_t bit){
+void subghz_protocol_common_add_bit(SubGhzProtocolCommon* common, uint8_t bit) {
     common->code_found = common->code_found << 1 | bit;
     common->code_count_bit++;
 }
 
-bool subghz_protocol_common_check_interval(SubGhzProtocolCommon *common, uint32_t duration, uint16_t duration_check) {
-    if ((duration_check >= (duration - common->te_delta))&&(duration_check <= (duration + common->te_delta))){
+bool subghz_protocol_common_check_interval(
+    SubGhzProtocolCommon* common,
+    uint32_t duration,
+    uint16_t duration_check) {
+    if((duration_check >= (duration - common->te_delta)) &&
+       (duration_check <= (duration + common->te_delta))) {
         return true;
     } else {
         return false;
     }
 }
 
-uint64_t subghz_protocol_common_reverse_key(uint64_t key, uint8_t count_bit){
-    uint64_t key_reverse=0;
-    for(uint8_t i=0; i<count_bit; i++) {
-        key_reverse=key_reverse<<1|bit_read(key,i);
+uint64_t subghz_protocol_common_reverse_key(uint64_t key, uint8_t count_bit) {
+    uint64_t key_reverse = 0;
+    for(uint8_t i = 0; i < count_bit; i++) {
+        key_reverse = key_reverse << 1 | bit_read(key, i);
     }
     return key_reverse;
 }
 
-void subghz_protocol_common_set_callback(SubGhzProtocolCommon* common, SubGhzProtocolCommonCallback callback, void* context) {
+void subghz_protocol_common_set_callback(
+    SubGhzProtocolCommon* common,
+    SubGhzProtocolCommonCallback callback,
+    void* context) {
     common->callback = callback;
     common->context = context;
 }
 
-
 void subghz_protocol_common_to_str(SubGhzProtocolCommon* instance, string_t output) {
-    if (instance->to_string) {
+    if(instance->to_string) {
         instance->to_string(instance, output);
     } else {
         uint32_t code_found_hi = instance->code_found >> 32;
         uint32_t code_found_lo = instance->code_found & 0x00000000ffffffff;
 
-        uint64_t code_found_reverse = subghz_protocol_common_reverse_key(instance->code_found, instance->code_count_bit);
+        uint64_t code_found_reverse =
+            subghz_protocol_common_reverse_key(instance->code_found, instance->code_count_bit);
 
-        uint32_t code_found_reverse_hi = code_found_reverse>>32;
-        uint32_t code_found_reverse_lo = code_found_reverse&0x00000000ffffffff;
+        uint32_t code_found_reverse_hi = code_found_reverse >> 32;
+        uint32_t code_found_reverse_lo = code_found_reverse & 0x00000000ffffffff;
 
-        if (code_found_hi>0) {
+        if(code_found_hi > 0) {
             string_cat_printf(
                 output,
                 "Protocol %s, %d Bit\r\n"
@@ -92,8 +125,7 @@ void subghz_protocol_common_to_str(SubGhzProtocolCommon* instance, string_t outp
                 code_found_reverse_hi,
                 code_found_reverse_lo,
                 instance->serial,
-                instance->btn
-            );
+                instance->btn);
         } else {
             string_cat_printf(
                 output,
@@ -108,8 +140,7 @@ void subghz_protocol_common_to_str(SubGhzProtocolCommon* instance, string_t outp
                 code_found_reverse_hi,
                 code_found_reverse_lo,
                 instance->serial,
-                instance->btn
-            );
+                instance->btn);
         }
     }
 }
@@ -124,9 +155,9 @@ bool subghz_protocol_common_read_hex(string_t str, uint8_t* buff, uint16_t len)
         if(hex_char_to_hex_nibble(string_get_char(str, 0), &nibble_high) &&
            hex_char_to_hex_nibble(string_get_char(str, 1), &nibble_low)) {
             buff[i] = (nibble_high << 4) | nibble_low;
-            if(string_size(str)>2){
+            if(string_size(str) > 2) {
                 string_right(str, 2);
-            }else if(i<len-1){
+            } else if(i < len - 1) {
                 parsed = false;
                 break;
             };

+ 22 - 1
lib/subghz/protocols/subghz_protocol_common.h

@@ -16,6 +16,8 @@
 
 #define SUBGHZ_APP_FOLDER "/any/subghz"
 #define SUBGHZ_APP_PATH_FOLDER "/any/subghz/saved"
+#define SUBGHZ_RAW_FOLDER "/ext/subghz"
+#define SUBGHZ_RAW_PATH_FOLDER "/ext/subghz/saved"
 #define SUBGHZ_APP_EXTENSION ".sub"
 #define SUBGHZ_ENCODER_UPLOAD_MAX_SIZE 2048
 
@@ -23,6 +25,7 @@ typedef enum {
     SubGhzProtocolCommonTypeUnknown,
     SubGhzProtocolCommonTypeStatic,
     SubGhzProtocolCommonTypeDynamic,
+    SubGhzProtocolCommonTypeRAW,
 } SubGhzProtocolCommonType;
 
 typedef struct SubGhzProtocolCommon SubGhzProtocolCommon;
@@ -38,7 +41,7 @@ typedef void (*SubGhzProtocolCommonGetStrSave)(SubGhzProtocolCommon* instance, s
 
 //Load protocol from file
 typedef bool (
-    *SubGhzProtocolCommonLoadFromFile)(FileWorker* file_worker, SubGhzProtocolCommon* instance);
+    *SubGhzProtocolCommonLoadFromFile)(FileWorker* file_worker, SubGhzProtocolCommon* instance, const char* file_path);
 //Load protocol
 typedef void (*SubGhzProtocolCommonLoadFromRAW)(SubGhzProtocolCommon* instance, void* context);
 //Get upload encoder protocol
@@ -46,6 +49,9 @@ typedef bool (*SubGhzProtocolCommonEncoderGetUpLoad)(
     SubGhzProtocolCommon* instance,
     SubGhzProtocolCommonEncoder* encoder);
 
+typedef LevelDuration (*SubGhzProtocolCommonEncoderCallback)(void* context);
+typedef void (*SubGhzProtocolCommonEncoderCallbackEnd)(void* context);
+
 struct SubGhzProtocolCommon {
     const char* name;
     uint16_t te_long;
@@ -86,6 +92,11 @@ struct SubGhzProtocolCommonEncoder {
     size_t front;
     size_t size_upload;
     LevelDuration* upload;
+
+    SubGhzProtocolCommonEncoderCallback callback;
+    SubGhzProtocolCommonEncoderCallbackEnd callback_end;
+    void* context;
+    void* context_end;
 };
 
 struct SubGhzProtocolCommonLoad {
@@ -108,6 +119,16 @@ SubGhzProtocolCommonEncoder* subghz_protocol_encoder_common_alloc();
  */
 void subghz_protocol_encoder_common_free(SubGhzProtocolCommonEncoder* instance);
 
+void subghz_protocol_encoder_common_set_callback(
+    SubGhzProtocolCommonEncoder* instance,
+    SubGhzProtocolCommonEncoderCallback callback,
+    void* context);
+
+void subghz_protocol_encoder_common_set_callback_end(
+    SubGhzProtocolCommonEncoder* instance,
+    SubGhzProtocolCommonEncoderCallbackEnd callback_end,
+    void* context_end);
+
 /** Get count repeat left
  * 
  * @param instance - SubGhzProtocolCommonEncoder instance

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

@@ -182,7 +182,8 @@ void subghz_protocol_gate_tx_to_save_str(SubGhzProtocolGateTX* instance, string_
 
 bool subghz_protocol_gate_tx_to_load_protocol_from_file(
     FileWorker* file_worker,
-    SubGhzProtocolGateTX* instance) {
+    SubGhzProtocolGateTX* instance,
+    const char* file_path) {
     bool loaded = false;
     string_t temp_str;
     string_init(temp_str);
@@ -227,4 +228,4 @@ void subghz_decoder_gate_tx_to_load_protocol(SubGhzProtocolGateTX* instance, voi
     instance->common.code_last_found = data->code_found;
     instance->common.code_last_count_bit = data->code_count_bit;
     subghz_protocol_gate_tx_check_remote_controller(instance);
-}
+}

+ 8 - 2
lib/subghz/protocols/subghz_protocol_gate_tx.h

@@ -22,7 +22,9 @@ void subghz_protocol_gate_tx_free(SubGhzProtocolGateTX* instance);
  * @param encoder - SubGhzProtocolCommonEncoder encoder
  * @return bool
  */
-bool subghz_protocol_gate_tx_send_key(SubGhzProtocolGateTX* instance, SubGhzProtocolCommonEncoder* encoder);
+bool subghz_protocol_gate_tx_send_key(
+    SubGhzProtocolGateTX* instance,
+    SubGhzProtocolCommonEncoder* encoder);
 
 /** Reset internal state
  * @param instance - SubGhzProtocolGateTX instance
@@ -54,9 +56,13 @@ void subghz_protocol_gate_tx_to_save_str(SubGhzProtocolGateTX* instance, string_
  * 
  * @param file_worker - FileWorker file_worker
  * @param instance - SubGhzProtocolGateTX instance
+ * @param file_path - file path
  * @return bool
  */
-bool subghz_protocol_gate_tx_to_load_protocol_from_file(FileWorker* file_worker, SubGhzProtocolGateTX* instance);
+bool subghz_protocol_gate_tx_to_load_protocol_from_file(
+    FileWorker* file_worker,
+    SubGhzProtocolGateTX* instance,
+    const char* file_path);
 
 /** Loading protocol from bin data
  * 

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

@@ -436,7 +436,8 @@ void subghz_protocol_keeloq_to_save_str(SubGhzProtocolKeeloq* instance, string_t
 
 bool subghz_protocol_keeloq_to_load_protocol_from_file(
     FileWorker* file_worker,
-    SubGhzProtocolKeeloq* instance) {
+    SubGhzProtocolKeeloq* instance,
+    const char* file_path) {
     bool loaded = false;
     string_t temp_str;
     string_init(temp_str);

+ 8 - 2
lib/subghz/protocols/subghz_protocol_keeloq.h

@@ -53,7 +53,9 @@ uint64_t subghz_protocol_keeloq_gen_key(void* context);
  * @param encoder - SubGhzProtocolCommonEncoder encoder
  * @return bool
  */
-bool subghz_protocol_keeloq_send_key(SubGhzProtocolKeeloq* instance, SubGhzProtocolCommonEncoder* encoder);
+bool subghz_protocol_keeloq_send_key(
+    SubGhzProtocolKeeloq* instance,
+    SubGhzProtocolCommonEncoder* encoder);
 
 /** Reset internal state
  * @param instance - SubGhzProtocolKeeloq instance
@@ -85,9 +87,13 @@ void subghz_protocol_keeloq_to_save_str(SubGhzProtocolKeeloq* instance, string_t
  * 
  * @param file_worker - FileWorker file_worker
  * @param instance - SubGhzProtocolKeeloq instance
+ * @param file_path - file path
  * @return bool
  */
-bool subghz_protocol_keeloq_to_load_protocol_from_file(FileWorker* file_worker, SubGhzProtocolKeeloq* instance);
+bool subghz_protocol_keeloq_to_load_protocol_from_file(
+    FileWorker* file_worker,
+    SubGhzProtocolKeeloq* instance,
+    const char* file_path);
 
 /** Loading protocol from bin data
  * 

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

@@ -245,7 +245,8 @@ void subghz_protocol_nero_radio_to_save_str(SubGhzProtocolNeroRadio* instance, s
 
 bool subghz_protocol_nero_radio_to_load_protocol_from_file(
     FileWorker* file_worker,
-    SubGhzProtocolNeroRadio* instance) {
+    SubGhzProtocolNeroRadio* instance,
+    const char* file_path) {
     bool loaded = false;
     string_t temp_str;
     string_init(temp_str);

+ 12 - 3
lib/subghz/protocols/subghz_protocol_nero_radio.h

@@ -22,7 +22,9 @@ void subghz_protocol_nero_radio_free(SubGhzProtocolNeroRadio* instance);
  * @param encoder - SubGhzProtocolCommonEncoder encoder
  * @return bool
  */
-bool subghz_protocol_nero_radio_send_key(SubGhzProtocolNeroRadio* instance, SubGhzProtocolCommonEncoder* encoder);
+bool subghz_protocol_nero_radio_send_key(
+    SubGhzProtocolNeroRadio* instance,
+    SubGhzProtocolCommonEncoder* encoder);
 
 /** Reset internal state
  * @param instance - SubGhzProtocolNeroRadio instance
@@ -40,7 +42,10 @@ void subghz_protocol_nero_radio_check_remote_controller(SubGhzProtocolNeroRadio*
  * @param instance - SubGhzProtocolNeroRadio instance
  * @param data - LevelDuration level_duration
  */
-void subghz_protocol_nero_radio_parse(SubGhzProtocolNeroRadio* instance, bool level, uint32_t duration);
+void subghz_protocol_nero_radio_parse(
+    SubGhzProtocolNeroRadio* instance,
+    bool level,
+    uint32_t duration);
 
 /** Outputting information from the parser
  * 
@@ -60,9 +65,13 @@ void subghz_protocol_nero_radio_to_save_str(SubGhzProtocolNeroRadio* instance, s
  * 
  * @param file_worker - FileWorker file_worker
  * @param instance - SubGhzProtocolNeroRadio instance
+ * @param file_path - file path
  * @return bool
  */
-bool subghz_protocol_nero_radio_to_load_protocol_from_file(FileWorker* file_worker, SubGhzProtocolNeroRadio* instance);
+bool subghz_protocol_nero_radio_to_load_protocol_from_file(
+    FileWorker* file_worker,
+    SubGhzProtocolNeroRadio* instance,
+    const char* file_path);
 
 /** Loading protocol from bin data
  * 

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

@@ -238,7 +238,8 @@ void subghz_protocol_nero_sketch_to_save_str(SubGhzProtocolNeroSketch* instance,
 
 bool subghz_protocol_nero_sketch_to_load_protocol_from_file(
     FileWorker* file_worker,
-    SubGhzProtocolNeroSketch* instance) {
+    SubGhzProtocolNeroSketch* instance,
+    const char* file_path) {
     bool loaded = false;
     string_t temp_str;
     string_init(temp_str);
@@ -282,4 +283,4 @@ void subghz_decoder_nero_sketch_to_load_protocol(SubGhzProtocolNeroSketch* insta
     SubGhzProtocolCommonLoad* data = context;
     instance->common.code_last_found = data->code_found;
     instance->common.code_last_count_bit = data->code_count_bit;
-}
+}

+ 13 - 4
lib/subghz/protocols/subghz_protocol_nero_sketch.h

@@ -22,7 +22,9 @@ void subghz_protocol_nero_sketch_free(SubGhzProtocolNeroSketch* instance);
  * @param encoder - SubGhzProtocolCommonEncoder encoder
  * @return bool
  */
-bool subghz_protocol_nero_sketch_send_key(SubGhzProtocolNeroSketch* instance, SubGhzProtocolCommonEncoder* encoder);
+bool subghz_protocol_nero_sketch_send_key(
+    SubGhzProtocolNeroSketch* instance,
+    SubGhzProtocolCommonEncoder* encoder);
 
 /** Reset internal state
  * @param instance - SubGhzProtocolNeroSketch instance
@@ -40,7 +42,10 @@ void subghz_protocol_nero_sketch_check_remote_controller(SubGhzProtocolNeroSketc
  * @param instance - SubGhzProtocolNeroSketch instance
  * @param data - LevelDuration level_duration
  */
-void subghz_protocol_nero_sketch_parse(SubGhzProtocolNeroSketch* instance, bool level, uint32_t duration);
+void subghz_protocol_nero_sketch_parse(
+    SubGhzProtocolNeroSketch* instance,
+    bool level,
+    uint32_t duration);
 
 /** Outputting information from the parser
  * 
@@ -60,13 +65,17 @@ void subghz_protocol_nero_sketch_to_save_str(SubGhzProtocolNeroSketch* instance,
  * 
  * @param file_worker - FileWorker file_worker
  * @param instance - SubGhzProtocolNeroSketch instance
+ * @param file_path - file path
  * @return bool
  */
-bool subghz_protocol_nero_sketch_to_load_protocol_from_file(FileWorker* file_worker, SubGhzProtocolNeroSketch* instance);
+bool subghz_protocol_nero_sketch_to_load_protocol_from_file(
+    FileWorker* file_worker,
+    SubGhzProtocolNeroSketch* instance,
+    const char* file_path);
 
 /** Loading protocol from bin data
  * 
  * @param instance - SubGhzProtocolNeroSketch instance
  * @param context - SubGhzProtocolCommonLoad context
  */
-void subghz_decoder_nero_sketch_to_load_protocol(SubGhzProtocolNeroSketch* instance, void* context);
+void subghz_decoder_nero_sketch_to_load_protocol(SubGhzProtocolNeroSketch* instance, void* context);

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

@@ -179,7 +179,8 @@ void subghz_protocol_nice_flo_to_save_str(SubGhzProtocolNiceFlo* instance, strin
 
 bool subghz_protocol_nice_flo_to_load_protocol_from_file(
     FileWorker* file_worker,
-    SubGhzProtocolNiceFlo* instance) {
+    SubGhzProtocolNiceFlo* instance,
+    const char* file_path) {
     bool loaded = false;
     string_t temp_str;
     string_init(temp_str);
@@ -224,4 +225,4 @@ void subghz_decoder_nice_flo_to_load_protocol(SubGhzProtocolNiceFlo* instance, v
     instance->common.code_last_count_bit = data->code_count_bit;
     instance->common.serial = 0x0;
     instance->common.btn = 0x0;
-}
+}

+ 8 - 2
lib/subghz/protocols/subghz_protocol_nice_flo.h

@@ -22,7 +22,9 @@ void subghz_protocol_nice_flo_free(SubGhzProtocolNiceFlo* instance);
  * @param encoder - SubGhzProtocolCommonEncoder encoder
  * @return bool
  */
-bool subghz_protocol_nice_flo_send_key(SubGhzProtocolNiceFlo* instance, SubGhzProtocolCommonEncoder* encoder);
+bool subghz_protocol_nice_flo_send_key(
+    SubGhzProtocolNiceFlo* instance,
+    SubGhzProtocolCommonEncoder* encoder);
 
 /** Reset internal state
  * @param instance - SubGhzProtocolNiceFlo instance
@@ -54,9 +56,13 @@ void subghz_protocol_nice_flo_to_save_str(SubGhzProtocolNiceFlo* instance, strin
  * 
  * @param file_worker - FileWorker file_worker
  * @param instance - SubGhzProtocolNiceFlo instance
+ * @param file_path - file path
  * @return bool
  */
-bool subghz_protocol_nice_flo_to_load_protocol_from_file(FileWorker* file_worker, SubGhzProtocolNiceFlo* instance);
+bool subghz_protocol_nice_flo_to_load_protocol_from_file(
+    FileWorker* file_worker,
+    SubGhzProtocolNiceFlo* instance,
+    const char* file_path);
 
 /** Loading protocol from bin data
  * 

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

@@ -308,7 +308,8 @@ void subghz_decoder_princeton_to_save_str(SubGhzDecoderPrinceton* instance, stri
 
 bool subghz_decoder_princeton_to_load_protocol_from_file(
     FileWorker* file_worker,
-    SubGhzDecoderPrinceton* instance) {
+    SubGhzDecoderPrinceton* instance,
+    const char* file_path) {
     bool loaded = false;
     string_t temp_str;
     string_init(temp_str);

+ 6 - 4
lib/subghz/protocols/subghz_protocol_princeton.h

@@ -111,15 +111,17 @@ void subghz_decoder_princeton_to_save_str(SubGhzDecoderPrinceton* instance, stri
  * 
  * @param file_worker - FileWorker file_worker
  * @param instance - SubGhzDecoderPrinceton instance
+ * @param file_path - file path
  * @return bool
  */
-bool subghz_decoder_princeton_to_load_protocol_from_file(FileWorker* file_worker, SubGhzDecoderPrinceton* instance);
+bool subghz_decoder_princeton_to_load_protocol_from_file(
+    FileWorker* file_worker,
+    SubGhzDecoderPrinceton* instance,
+    const char* file_path);
 
 /** Loading protocol from bin data
  * 
  * @param instance - SubGhzDecoderPrinceton instance
  * @param context - SubGhzProtocolCommonLoad context
  */
-void subghz_decoder_princeton_to_load_protocol(
-    SubGhzDecoderPrinceton* instance,
-    void* context) ;
+void subghz_decoder_princeton_to_load_protocol(SubGhzDecoderPrinceton* instance, void* context);

+ 265 - 0
lib/subghz/protocols/subghz_protocol_raw.c

@@ -0,0 +1,265 @@
+#include "subghz_protocol_raw.h"
+#include "file-worker.h"
+#include "../subghz_file_encoder_worker.h"
+
+#define SUBGHZ_DOWNLOAD_MAX_SIZE 512
+
+struct SubGhzProtocolRAW {
+    SubGhzProtocolCommon common;
+
+    int16_t* upload_raw;
+    uint16_t ind_write;
+    FileWorker* file_worker;
+    SubGhzFileEncoderWorker* file_worker_encoder;
+    uint32_t file_is_open;
+    string_t file_name;
+    size_t sample_write;
+    bool last_level;
+};
+
+typedef enum {
+    RAWFileIsOpenClose = 0,
+    RAWFileIsOpenWrite,
+    RAWFileIsOpenRead,
+} RAWFilIsOpen;
+
+SubGhzProtocolRAW* subghz_protocol_raw_alloc(void) {
+    SubGhzProtocolRAW* instance = furi_alloc(sizeof(SubGhzProtocolRAW));
+
+    instance->upload_raw = NULL;
+    instance->ind_write = 0;
+
+    instance->last_level = false;
+
+    instance->file_worker = file_worker_alloc(false);
+    instance->file_is_open = RAWFileIsOpenClose;
+    string_init(instance->file_name);
+
+    instance->common.name = "RAW";
+    instance->common.code_min_count_bit_for_found = 0;
+    instance->common.te_short = 80;
+    instance->common.te_long = 32700;
+    instance->common.te_delta = 0;
+    instance->common.type_protocol = SubGhzProtocolCommonTypeRAW;
+    instance->common.to_load_protocol_from_file =
+        (SubGhzProtocolCommonLoadFromFile)subghz_protocol_raw_to_load_protocol_from_file;
+    instance->common.to_string = (SubGhzProtocolCommonToStr)subghz_protocol_raw_to_str;
+    //instance->common.to_load_protocol =
+    //    (SubGhzProtocolCommonLoadFromRAW)subghz_decoder_raw_to_load_protocol;
+    instance->common.get_upload_protocol =
+        (SubGhzProtocolCommonEncoderGetUpLoad)subghz_protocol_raw_send_key;
+
+    return instance;
+}
+
+void subghz_protocol_raw_free(SubGhzProtocolRAW* instance) {
+    furi_assert(instance);
+    string_clear(instance->file_name);
+    file_worker_free(instance->file_worker);
+    free(instance);
+}
+
+void subghz_protocol_raw_file_encoder_worker_stop(void* context) {
+    furi_assert(context);
+    SubGhzProtocolRAW* instance = context;
+    if(subghz_file_encoder_worker_is_running(instance->file_worker_encoder)) {
+        subghz_file_encoder_worker_stop(instance->file_worker_encoder);
+        subghz_file_encoder_worker_free(instance->file_worker_encoder);
+        instance->file_is_open = RAWFileIsOpenClose;
+    }
+}
+
+bool subghz_protocol_raw_send_key(
+    SubGhzProtocolRAW* instance,
+    SubGhzProtocolCommonEncoder* encoder) {
+    furi_assert(instance);
+    furi_assert(encoder);
+
+    bool loaded = false;
+
+    instance->file_worker_encoder = subghz_file_encoder_worker_alloc();
+
+    if(subghz_file_encoder_worker_start(
+           instance->file_worker_encoder, string_get_cstr(instance->file_name))) {
+        //the worker needs a file in order to open and read part of the file
+        osDelay(100);
+        instance->file_is_open = RAWFileIsOpenRead;
+        subghz_protocol_encoder_common_set_callback(
+            encoder, subghz_file_encoder_worker_get_level_duration, instance->file_worker_encoder);
+        subghz_protocol_encoder_common_set_callback_end(
+            encoder, subghz_protocol_raw_file_encoder_worker_stop, instance);
+
+        loaded = true;
+    } else {
+        subghz_protocol_raw_file_encoder_worker_stop(instance);
+    }
+    return loaded;
+}
+
+void subghz_protocol_raw_reset(SubGhzProtocolRAW* instance) {
+    instance->ind_write = 0;
+}
+
+void subghz_protocol_raw_parse(SubGhzProtocolRAW* instance, bool level, uint32_t duration) {
+    if(instance->upload_raw != NULL) {
+        if(duration > instance->common.te_short) {
+            if(duration > instance->common.te_long) duration = instance->common.te_long;
+            if(instance->last_level != level) {
+                instance->last_level = (level ? true : false);
+                instance->upload_raw[instance->ind_write++] = (level ? duration : -duration);
+            }
+        }
+
+        if(instance->ind_write == SUBGHZ_DOWNLOAD_MAX_SIZE) {
+            subghz_protocol_save_raw_to_file_write(instance);
+        }
+    }
+}
+
+void subghz_protocol_raw_to_str(SubGhzProtocolRAW* instance, string_t output) {
+    string_cat_printf(output, "RAW Date");
+}
+
+const char* subghz_protocol_get_last_file_name(SubGhzProtocolRAW* instance) {
+    return string_get_cstr(instance->file_name);
+}
+
+void subghz_protocol_set_last_file_name(SubGhzProtocolRAW* instance, const char* name) {
+    string_printf(instance->file_name, "%s", name);
+}
+
+bool subghz_protocol_save_raw_to_file_init(
+    SubGhzProtocolRAW* instance,
+    const char* dev_name,
+    uint32_t frequency,
+    FuriHalSubGhzPreset preset) {
+    furi_assert(instance);
+
+    string_t dev_file_name;
+    string_init(dev_file_name);
+    string_t temp_str;
+    string_init(temp_str);
+    bool init = false;
+
+    do {
+        // Create subghz folder directory if necessary
+        if(!file_worker_mkdir(instance->file_worker, SUBGHZ_RAW_FOLDER)) {
+            break;
+        }
+        // Create saved directory if necessary
+        if(!file_worker_mkdir(instance->file_worker, SUBGHZ_RAW_PATH_FOLDER)) {
+            break;
+        }
+        //get the name of the next free file
+        file_worker_get_next_filename(
+            instance->file_worker,
+            SUBGHZ_RAW_PATH_FOLDER,
+            dev_name,
+            SUBGHZ_APP_EXTENSION,
+            temp_str);
+
+        string_set(instance->file_name, temp_str);
+
+        string_printf(
+            dev_file_name,
+            "%s/%s%s",
+            SUBGHZ_RAW_PATH_FOLDER,
+            string_get_cstr(temp_str),
+            SUBGHZ_APP_EXTENSION);
+        // Open file
+        if(!file_worker_open(
+               instance->file_worker,
+               string_get_cstr(dev_file_name),
+               FSAM_WRITE,
+               FSOM_CREATE_ALWAYS)) {
+            break;
+        }
+        //Get string frequency preset protocol
+        string_printf(
+            temp_str,
+            "Frequency: %d\n"
+            "Preset: %d\n"
+            "Protocol: RAW\n",
+            (int)frequency,
+            (int)preset);
+
+        if(!file_worker_write(
+               instance->file_worker, string_get_cstr(temp_str), string_size(temp_str))) {
+            break;
+        }
+
+        instance->upload_raw = furi_alloc(SUBGHZ_DOWNLOAD_MAX_SIZE * sizeof(uint16_t));
+        instance->file_is_open = RAWFileIsOpenWrite;
+        instance->sample_write = 0;
+        init = true;
+    } while(0);
+
+    string_clear(temp_str);
+    string_clear(dev_file_name);
+
+    return init;
+}
+
+void subghz_protocol_save_raw_to_file_stop(SubGhzProtocolRAW* instance) {
+    furi_assert(instance);
+
+    if(instance->file_is_open == RAWFileIsOpenWrite && instance->ind_write)
+        subghz_protocol_save_raw_to_file_write(instance);
+    if(instance->file_is_open != RAWFileIsOpenClose) {
+        free(instance->upload_raw);
+        instance->upload_raw = NULL;
+    }
+
+    file_worker_close(instance->file_worker);
+    instance->file_is_open = RAWFileIsOpenClose;
+}
+
+bool subghz_protocol_save_raw_to_file_write(SubGhzProtocolRAW* instance) {
+    furi_assert(instance);
+
+    string_t temp_str;
+    string_init(temp_str);
+    bool is_write = false;
+    if(instance->file_is_open == RAWFileIsOpenWrite) {
+        do {
+            string_printf(temp_str, "RAW_Data: ");
+
+            if(!file_worker_write(
+                   instance->file_worker, string_get_cstr(temp_str), string_size(temp_str))) {
+                break;
+            }
+
+            for(size_t i = 0; i < instance->ind_write - 1; i++) {
+                string_printf(temp_str, "%d, ", instance->upload_raw[i]);
+                if(!file_worker_write(
+                       instance->file_worker, string_get_cstr(temp_str), string_size(temp_str))) {
+                    break;
+                }
+            }
+
+            string_printf(temp_str, "%d\n", instance->upload_raw[instance->ind_write - 1]);
+            if(!file_worker_write(
+                   instance->file_worker, string_get_cstr(temp_str), string_size(temp_str))) {
+                break;
+            }
+
+            instance->sample_write += instance->ind_write;
+            instance->ind_write = 0;
+            is_write = true;
+        } while(0);
+        string_clear(temp_str);
+    }
+    return is_write;
+}
+
+size_t subghz_save_protocol_raw_get_sample_write(SubGhzProtocolRAW* instance) {
+    return instance->sample_write + instance->ind_write;
+}
+
+bool subghz_protocol_raw_to_load_protocol_from_file(
+    FileWorker* file_worker,
+    SubGhzProtocolRAW* instance,
+    const char* file_path) {
+    subghz_protocol_set_last_file_name(instance, file_path);
+    return true;
+}

+ 64 - 0
lib/subghz/protocols/subghz_protocol_raw.h

@@ -0,0 +1,64 @@
+#pragma once
+
+#include "subghz_protocol_common.h"
+
+typedef struct SubGhzProtocolRAW SubGhzProtocolRAW;
+
+/** Allocate SubGhzProtocolRAW
+ * 
+ * @return SubGhzProtocolRAW* 
+ */
+SubGhzProtocolRAW* subghz_protocol_raw_alloc();
+
+/** Free SubGhzProtocolRAW
+ * 
+ * @param instance 
+ */
+void subghz_protocol_raw_free(SubGhzProtocolRAW* instance);
+
+/** Reset internal state
+ * @param instance - SubGhzProtocolRAW instance
+ */
+void subghz_protocol_raw_reset(SubGhzProtocolRAW* instance);
+
+/** Get upload protocol
+ * 
+ * @param instance - SubGhzProtocolRAW instance
+ * @param encoder - SubGhzProtocolCommonEncoder encoder
+ * @return bool
+ */
+bool subghz_protocol_raw_send_key(
+    SubGhzProtocolRAW* instance,
+    SubGhzProtocolCommonEncoder* encoder);
+
+/** Parse accepted duration
+ * 
+ * @param instance - SubGhzProtocolRAW instance
+ * @param data - LevelDuration level_duration
+ */
+void subghz_protocol_raw_parse(SubGhzProtocolRAW* instance, bool level, uint32_t duration);
+
+/** Outputting information from the parser
+ *
+ * @param instance - SubGhzProtocolRAW* instance
+ * @param output   - output string
+ */
+void subghz_protocol_raw_to_str(SubGhzProtocolRAW* instance, string_t output);
+
+const char* subghz_protocol_get_last_file_name(SubGhzProtocolRAW* instance);
+
+void subghz_protocol_set_last_file_name(SubGhzProtocolRAW* instance, const char* name);
+
+bool subghz_protocol_save_raw_to_file_init(
+    SubGhzProtocolRAW* instance,
+    const char* dev_name,
+    uint32_t frequency,
+    FuriHalSubGhzPreset preset);
+void subghz_protocol_save_raw_to_file_stop(SubGhzProtocolRAW* instance);
+bool subghz_protocol_save_raw_to_file_write(SubGhzProtocolRAW* instance);
+size_t subghz_save_protocol_raw_get_sample_write(SubGhzProtocolRAW* instance);
+
+bool subghz_protocol_raw_to_load_protocol_from_file(
+    FileWorker* file_worker,
+    SubGhzProtocolRAW* instance,
+    const char* file_path);

+ 210 - 0
lib/subghz/subghz_file_encoder_worker.c

@@ -0,0 +1,210 @@
+#include "subghz_file_encoder_worker.h"
+#include <stream_buffer.h>
+
+#include "file-worker.h"
+
+#define SUBGHZ_FILE_ENCODER_LOAD 512
+
+struct SubGhzFileEncoderWorker {
+    FuriThread* thread;
+    StreamBufferHandle_t stream;
+    FileWorker* file_worker;
+
+    volatile bool worker_running;
+    bool level;
+    int16_t duration;
+    string_t str_data;
+    string_t file_path;
+};
+
+void subghz_file_encoder_worker_add_livel_duration(
+    SubGhzFileEncoderWorker* instance,
+    int16_t duration) {
+    bool res = true;
+    if(duration < 0 && !instance->level) {
+        instance->duration += duration;
+        res = false;
+    } else if(duration > 0 && instance->level) {
+        instance->duration += duration;
+        res = false;
+    } else if(duration == 0) {
+        instance->duration = 0;
+    }
+
+    if(res) {
+        instance->level = !instance->level;
+        instance->duration += duration;
+        xStreamBufferSend(instance->stream, &instance->duration, sizeof(int16_t), 10);
+        instance->duration = 0;
+    }
+}
+
+bool subghz_file_encoder_worker_data_parse(
+    SubGhzFileEncoderWorker* instance,
+    const char* strStart,
+    size_t len) {
+    char* str1;
+    size_t ind_start = (size_t)strStart; //store the start address of the beginning of the line
+    bool res = false;
+
+    str1 = strstr(
+        strStart, "RAW_Data: "); //looking for the beginning of the desired title in the line
+    if(str1 != NULL) {
+        str1 = strchr(
+            str1,
+            ' '); //if found, shift the pointer by 1 element per line "RAW_Data: -1, 2, -2..."
+        subghz_file_encoder_worker_add_livel_duration(instance, atoi(str1));
+        while(
+            strchr(str1, ',') != NULL &&
+            ((size_t)str1 <
+             (len +
+              ind_start))) { //check that there is still an element in the line and that it has not gone beyond the line
+            str1 = strchr(str1, ',');
+            str1 += 2; //if found, shift the pointer by next element per line
+            subghz_file_encoder_worker_add_livel_duration(instance, atoi(str1));
+        }
+        res = true;
+    }
+    return res;
+}
+
+LevelDuration subghz_file_encoder_worker_get_level_duration(void* context) {
+    furi_assert(context);
+    SubGhzFileEncoderWorker* instance = context;
+    int16_t duration;
+    BaseType_t xHigherPriorityTaskWoken = pdFALSE;
+    int ret = xStreamBufferReceiveFromISR(
+        instance->stream, &duration, sizeof(int16_t), &xHigherPriorityTaskWoken);
+    portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
+    if(ret == sizeof(int16_t)) {
+        LevelDuration level_duration = {.level = LEVEL_DURATION_RESET};
+        if(duration < 0) {
+            level_duration = level_duration_make(false, duration * -1);
+        } else if(duration > 0) {
+            level_duration = level_duration_make(true, duration);
+        } else if(duration == 0) {
+            level_duration = level_duration_reset();
+            FURI_LOG_I("SubGhzFileEncoderWorker", "Stop transmission");
+        }
+        return level_duration;
+    } else {
+        FURI_LOG_E("SubGhzFileEncoderWorker", "Slow flash read");
+        return level_duration_wait();
+    }
+}
+
+/** Worker thread
+ * 
+ * @param context 
+ * @return exit code 
+ */
+static int32_t subghz_file_encoder_worker_thread(void* context) {
+    SubGhzFileEncoderWorker* instance = context;
+    FURI_LOG_I("SubGhzFileEncoderWorker", "Worker start");
+    bool res = false;
+    do {
+        if(!file_worker_open(
+               instance->file_worker,
+               string_get_cstr(instance->file_path),
+               FSAM_READ,
+               FSOM_OPEN_EXISTING)) {
+            break;
+        }
+        //todo skips 3 lines file header
+        if(!file_worker_read_until(instance->file_worker, instance->str_data, '\n')) {
+            break;
+        }
+        if(!file_worker_read_until(instance->file_worker, instance->str_data, '\n')) {
+            break;
+        }
+        if(!file_worker_read_until(instance->file_worker, instance->str_data, '\n')) {
+            break;
+        }
+        res = true;
+        FURI_LOG_I("SubGhzFileEncoderWorker", "Start transmission");
+    } while(0);
+
+    while(res && instance->worker_running) {
+        size_t stream_free_byte = xStreamBufferSpacesAvailable(instance->stream);
+        if((stream_free_byte / sizeof(int16_t)) >= SUBGHZ_FILE_ENCODER_LOAD) {
+            if(file_worker_read_until(instance->file_worker, instance->str_data, '\n')) {
+                if(!subghz_file_encoder_worker_data_parse(
+                       instance,
+                       string_get_cstr(instance->str_data),
+                       strlen(string_get_cstr(instance->str_data)))) {
+                    //to stop DMA correctly
+                    subghz_file_encoder_worker_add_livel_duration(instance, LEVEL_DURATION_RESET);
+                    subghz_file_encoder_worker_add_livel_duration(instance, LEVEL_DURATION_RESET);
+
+                    break;
+                }
+            } else {
+                subghz_file_encoder_worker_add_livel_duration(instance, LEVEL_DURATION_RESET);
+                subghz_file_encoder_worker_add_livel_duration(instance, LEVEL_DURATION_RESET);
+                break;
+            }
+        }
+    }
+    //waiting for the end of the transfer
+    while(instance->worker_running) {
+        osDelay(50);
+    }
+    file_worker_close(instance->file_worker);
+    FURI_LOG_I("SubGhzFileEncoderWorker", "Worker stop");
+    return 0;
+}
+
+SubGhzFileEncoderWorker* subghz_file_encoder_worker_alloc() {
+    SubGhzFileEncoderWorker* instance = furi_alloc(sizeof(SubGhzFileEncoderWorker));
+
+    instance->thread = furi_thread_alloc();
+    furi_thread_set_name(instance->thread, "subghz_file_encoder_worker");
+    furi_thread_set_stack_size(instance->thread, 2048);
+    furi_thread_set_context(instance->thread, instance);
+    furi_thread_set_callback(instance->thread, subghz_file_encoder_worker_thread);
+    instance->stream = xStreamBufferCreate(sizeof(int16_t) * 4096, sizeof(int16_t));
+
+    instance->file_worker = file_worker_alloc(false);
+    string_init(instance->str_data);
+    string_init(instance->file_path);
+    instance->level = false;
+
+    return instance;
+}
+
+void subghz_file_encoder_worker_free(SubGhzFileEncoderWorker* instance) {
+    furi_assert(instance);
+
+    vStreamBufferDelete(instance->stream);
+    furi_thread_free(instance->thread);
+
+    string_clear(instance->str_data);
+    string_clear(instance->file_path);
+    file_worker_free(instance->file_worker);
+
+    free(instance);
+}
+
+bool subghz_file_encoder_worker_start(SubGhzFileEncoderWorker* instance, const char* file_path) {
+    furi_assert(instance);
+    furi_assert(!instance->worker_running);
+
+    xStreamBufferReset(instance->stream);
+    string_set(instance->file_path, file_path);
+    instance->worker_running = true;
+    furi_thread_start(instance->thread);
+    return true;
+}
+
+void subghz_file_encoder_worker_stop(SubGhzFileEncoderWorker* instance) {
+    furi_assert(instance);
+    furi_assert(instance->worker_running);
+
+    instance->worker_running = false;
+    furi_thread_join(instance->thread);
+}
+
+bool subghz_file_encoder_worker_is_running(SubGhzFileEncoderWorker* instance) {
+    furi_assert(instance);
+    return instance->worker_running;
+}

+ 39 - 0
lib/subghz/subghz_file_encoder_worker.h

@@ -0,0 +1,39 @@
+#pragma once
+
+#include <furi-hal.h>
+
+typedef struct SubGhzFileEncoderWorker SubGhzFileEncoderWorker;
+
+/** Allocate SubGhzFileEncoderWorker
+ * 
+ * @return SubGhzFileEncoderWorker* 
+ */
+SubGhzFileEncoderWorker* subghz_file_encoder_worker_alloc();
+
+/** Free SubGhzFileEncoderWorker
+ * 
+ * @param instance SubGhzFileEncoderWorker instance
+ */
+void subghz_file_encoder_worker_free(SubGhzFileEncoderWorker* instance);
+
+LevelDuration subghz_file_encoder_worker_get_level_duration(void* context);
+
+/** Start SubGhzFileEncoderWorker
+ * 
+ * @param instance SubGhzFileEncoderWorker instance
+ * @return bool - true if ok
+ */
+bool subghz_file_encoder_worker_start(SubGhzFileEncoderWorker* instance, const char* file_path);
+
+/** Stop SubGhzFileEncoderWorker
+ * 
+ * @param instance SubGhzFileEncoderWorker instance
+ */
+void subghz_file_encoder_worker_stop(SubGhzFileEncoderWorker* instance);
+
+/** Check if worker is running
+ * 
+ * @param instance SubGhzFileEncoderWorker instance
+ * @return bool - true if running
+ */
+bool subghz_file_encoder_worker_is_running(SubGhzFileEncoderWorker* instance);

+ 16 - 0
lib/subghz/subghz_parser.c

@@ -16,6 +16,7 @@
 #include "protocols/subghz_protocol_nero_radio.h"
 #include "protocols/subghz_protocol_scher_khan.h"
 #include "protocols/subghz_protocol_kia.h"
+#include "protocols/subghz_protocol_raw.h"
 
 #include "subghz_keystore.h"
 
@@ -38,6 +39,7 @@ typedef enum {
     SubGhzProtocolTypeNeroRadio,
     SubGhzProtocolTypeScherKhan,
     SubGhzProtocolTypeKIA,
+    SubGhzProtocolTypeRAW,
 
     SubGhzProtocolTypeMax,
 } SubGhzProtocolType;
@@ -109,6 +111,8 @@ SubGhzParser* subghz_parser_alloc() {
         (SubGhzProtocolCommon*)subghz_protocol_scher_khan_alloc();
     instance->protocols[SubGhzProtocolTypeKIA] =
         (SubGhzProtocolCommon*)subghz_protocol_kia_alloc();
+    instance->protocols[SubGhzProtocolTypeRAW] =
+        (SubGhzProtocolCommon*)subghz_protocol_raw_alloc();
 
     return instance;
 }
@@ -143,6 +147,7 @@ void subghz_parser_free(SubGhzParser* instance) {
     subghz_protocol_scher_khan_free(
         (SubGhzProtocolScherKhan*)instance->protocols[SubGhzProtocolTypeScherKhan]);
     subghz_protocol_kia_free((SubGhzProtocolKIA*)instance->protocols[SubGhzProtocolTypeKIA]);
+    subghz_protocol_raw_free((SubGhzProtocolRAW*)instance->protocols[SubGhzProtocolTypeRAW]);
 
     subghz_keystore_free(instance->keystore);
 
@@ -197,6 +202,11 @@ void subghz_parser_load_nice_flor_s_file(SubGhzParser* instance, const char* fil
         (SubGhzProtocolNiceFlorS*)instance->protocols[SubGhzProtocolTypeNiceFlorS], file_name);
 }
 
+void subghz_parser_load_came_atomo_file(SubGhzParser* instance, const char* file_name) {
+    subghz_protocol_came_atomo_name_file(
+        (SubGhzProtocolCameAtomo*)instance->protocols[SubGhzProtocolTypeCameAtomo], file_name);
+}
+
 void subghz_parser_load_keeloq_file(SubGhzParser* instance, const char* file_name) {
     subghz_keystore_load(instance->keystore, file_name);
 }
@@ -229,6 +239,12 @@ void subghz_parser_reset(SubGhzParser* instance) {
     subghz_protocol_scher_khan_reset(
         (SubGhzProtocolScherKhan*)instance->protocols[SubGhzProtocolTypeScherKhan]);
     subghz_protocol_kia_reset((SubGhzProtocolKIA*)instance->protocols[SubGhzProtocolTypeKIA]);
+    subghz_protocol_raw_reset((SubGhzProtocolRAW*)instance->protocols[SubGhzProtocolTypeRAW]);
+}
+
+void subghz_parser_raw_parse(SubGhzParser* instance, bool level, uint32_t duration) {
+    subghz_protocol_raw_parse(
+        (SubGhzProtocolRAW*)instance->protocols[SubGhzProtocolTypeRAW], level, duration);
 }
 
 void subghz_parser_parse(SubGhzParser* instance, bool level, uint32_t duration) {

+ 18 - 3
lib/subghz/subghz_parser.h

@@ -3,7 +3,7 @@
 #include "protocols/subghz_protocol_common.h"
 
 typedef void (*SubGhzProtocolTextCallback)(string_t text, void* context);
-typedef void (*SubGhzProtocolCommonCallbackDump)(SubGhzProtocolCommon *parser, void* context);
+typedef void (*SubGhzProtocolCommonCallbackDump)(SubGhzProtocolCommon* parser, void* context);
 
 typedef struct SubGhzParser SubGhzParser;
 
@@ -33,7 +33,10 @@ SubGhzProtocolCommon* subghz_parser_get_by_name(SubGhzParser* instance, const ch
  * @param callback - SubGhzProtocolTextCallback callback
  * @param context
  */
-void subghz_parser_enable_dump_text(SubGhzParser* instance, SubGhzProtocolTextCallback callback, void* context);
+void subghz_parser_enable_dump_text(
+    SubGhzParser* instance,
+    SubGhzProtocolTextCallback callback,
+    void* context);
 
 /** Outputting data SubGhzParser from all parsers
  * 
@@ -41,7 +44,10 @@ void subghz_parser_enable_dump_text(SubGhzParser* instance, SubGhzProtocolTextCa
  * @param callback - SubGhzProtocolTextCallback callback
  * @param context
  */
-void subghz_parser_enable_dump(SubGhzParser* instance, SubGhzProtocolCommonCallbackDump callback, void* context);
+void subghz_parser_enable_dump(
+    SubGhzParser* instance,
+    SubGhzProtocolCommonCallbackDump callback,
+    void* context);
 
 /** File name rainbow table Nice Flor-S
  * 
@@ -50,6 +56,13 @@ void subghz_parser_enable_dump(SubGhzParser* instance, SubGhzProtocolCommonCallb
  */
 void subghz_parser_load_nice_flor_s_file(SubGhzParser* instance, const char* file_name);
 
+/** File name rainbow table Came Atomo
+ * 
+ * @param instance - SubGhzParser instance
+ * @param file_name - "path/file_name"
+ */
+void subghz_parser_load_came_atomo_file(SubGhzParser* instance, const char* file_name);
+
 /** File upload manufacture keys
  * 
  * @param instance - SubGhzParser instance
@@ -63,6 +76,8 @@ void subghz_parser_load_keeloq_file(SubGhzParser* instance, const char* file_nam
  */
 void subghz_parser_reset(SubGhzParser* instance);
 
+void subghz_parser_raw_parse(SubGhzParser* instance, bool level, uint32_t duration);
+
 /** Loading data into all parsers
  * 
  * @param instance - SubGhzParser instance

+ 11 - 0
lib/toolbox/level_duration.h

@@ -9,6 +9,7 @@
 #define LEVEL_DURATION_RESET 0U
 #define LEVEL_DURATION_LEVEL_LOW 1U
 #define LEVEL_DURATION_LEVEL_HIGH 2U
+#define LEVEL_DURATION_WAIT 3U
 #define LEVEL_DURATION_RESERVED 0x800000U
 
 typedef struct {
@@ -29,10 +30,20 @@ static inline LevelDuration level_duration_reset() {
     return level_duration;
 }
 
+static inline LevelDuration level_duration_wait() {
+    LevelDuration level_duration;
+    level_duration.level = LEVEL_DURATION_WAIT;
+    return level_duration;
+}
+
 static inline bool level_duration_is_reset(LevelDuration level_duration) {
     return level_duration.level == LEVEL_DURATION_RESET;
 }
 
+static inline bool level_duration_is_wait(LevelDuration level_duration) {
+    return level_duration.level == LEVEL_DURATION_WAIT;
+}
+
 static inline bool level_duration_get_level(LevelDuration level_duration) {
     return level_duration.level == LEVEL_DURATION_LEVEL_HIGH;
 }