Kaynağa Gözat

[FL-2047] SubGhz: New GUI ReadRAW view (#832)

* SubGhz: GUI RAW Read view
* SubGhz:  GUI Read RAW refactoring
* SubGhz: fix bug wrong frequency of the allowed transmission
* GUI Read RAW refactoring
* SubGhz: fix set the default frequency
* SubGhz: fix save filename when returning from another menu
* SubGhz: fix Send and Back button behavior

Co-authored-by: あく <alleteam@gmail.com>
Skorpionm 4 yıl önce
ebeveyn
işleme
a13f87fedb

+ 6 - 3
applications/subghz/helpers/subghz_custom_event.h

@@ -14,8 +14,8 @@ typedef enum {
     SubghzCustomEventSceneShowError,
     SubghzCustomEventSceneShowError,
     SubghzCustomEventSceneShowOnlyRX,
     SubghzCustomEventSceneShowOnlyRX,
 
 
-    SubghzCustomEventSceneNeedSavingNo,
-    SubghzCustomEventSceneNeedSavingYes,
+    SubghzCustomEventSceneExit,
+    SubghzCustomEventSceneStay,
 
 
     SubghzCustomEventViewReceverOK,
     SubghzCustomEventViewReceverOK,
     SubghzCustomEventViewReceverConfig,
     SubghzCustomEventViewReceverConfig,
@@ -25,7 +25,10 @@ typedef enum {
     SubghzCustomEventViewReadRAWIDLE,
     SubghzCustomEventViewReadRAWIDLE,
     SubghzCustomEventViewReadRAWREC,
     SubghzCustomEventViewReadRAWREC,
     SubghzCustomEventViewReadRAWConfig,
     SubghzCustomEventViewReadRAWConfig,
-    SubghzCustomEventViewReadRAWMore,
+    SubghzCustomEventViewReadRAWErase,
+    SubghzCustomEventViewReadRAWSendStart,
+    SubghzCustomEventViewReadRAWSendStop,
+    SubghzCustomEventViewReadRAWSave,
 
 
     SubghzCustomEventViewTransmitterBack,
     SubghzCustomEventViewTransmitterBack,
     SubghzCustomEventViewTransmitterSendStart,
     SubghzCustomEventViewTransmitterSendStart,

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

@@ -18,5 +18,4 @@ ADD_SCENE(subghz, test_packet, TestPacket)
 ADD_SCENE(subghz, set_type, SetType)
 ADD_SCENE(subghz, set_type, SetType)
 ADD_SCENE(subghz, frequency_analyzer, FrequencyAnalyzer)
 ADD_SCENE(subghz, frequency_analyzer, FrequencyAnalyzer)
 ADD_SCENE(subghz, read_raw, ReadRAW)
 ADD_SCENE(subghz, read_raw, ReadRAW)
-ADD_SCENE(subghz, read_raw_menu, ReadRAWMenu)
 ADD_SCENE(subghz, need_saving, NeedSaving)
 ADD_SCENE(subghz, need_saving, NeedSaving)

+ 16 - 12
applications/subghz/scenes/subghz_scene_need_saving.c

@@ -6,42 +6,46 @@ void subghz_scene_need_saving_callback(GuiButtonType result, InputType type, voi
     SubGhz* subghz = context;
     SubGhz* subghz = context;
 
 
     if((result == GuiButtonTypeRight) && (type == InputTypeShort)) {
     if((result == GuiButtonTypeRight) && (type == InputTypeShort)) {
-        view_dispatcher_send_custom_event(
-            subghz->view_dispatcher, SubghzCustomEventSceneNeedSavingYes);
+        view_dispatcher_send_custom_event(subghz->view_dispatcher, SubghzCustomEventSceneStay);
     } else if((result == GuiButtonTypeLeft) && (type == InputTypeShort)) {
     } else if((result == GuiButtonTypeLeft) && (type == InputTypeShort)) {
-        view_dispatcher_send_custom_event(
-            subghz->view_dispatcher, SubghzCustomEventSceneNeedSavingNo);
+        view_dispatcher_send_custom_event(subghz->view_dispatcher, SubghzCustomEventSceneExit);
     }
     }
 }
 }
 
 
 void subghz_scene_need_saving_on_enter(void* context) {
 void subghz_scene_need_saving_on_enter(void* context) {
     SubGhz* subghz = context;
     SubGhz* subghz = context;
 
 
+    widget_add_string_multiline_element(
+        subghz->widget, 64, 13, AlignCenter, AlignCenter, FontPrimary, "Exit to Gub-Ghz menu?");
     widget_add_string_multiline_element(
     widget_add_string_multiline_element(
         subghz->widget,
         subghz->widget,
         64,
         64,
-        25,
+        32,
         AlignCenter,
         AlignCenter,
         AlignCenter,
         AlignCenter,
         FontSecondary,
         FontSecondary,
-        "There is an unsaved data.\nDo you want to save it?");
+        "All unsaved will be\nlost.");
 
 
     widget_add_button_element(
     widget_add_button_element(
-        subghz->widget, GuiButtonTypeRight, "Save", subghz_scene_need_saving_callback, subghz);
+        subghz->widget, GuiButtonTypeRight, "Stay", subghz_scene_need_saving_callback, subghz);
     widget_add_button_element(
     widget_add_button_element(
-        subghz->widget, GuiButtonTypeLeft, "Delete", subghz_scene_need_saving_callback, subghz);
+        subghz->widget, GuiButtonTypeLeft, "Exit", subghz_scene_need_saving_callback, subghz);
 
 
     view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewWidget);
     view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewWidget);
 }
 }
 
 
 bool subghz_scene_need_saving_on_event(void* context, SceneManagerEvent event) {
 bool subghz_scene_need_saving_on_event(void* context, SceneManagerEvent event) {
     SubGhz* subghz = context;
     SubGhz* subghz = context;
-    if(event.type == SceneManagerEventTypeCustom) {
-        if(event.event == SubghzCustomEventSceneNeedSavingYes) {
-            subghz->txrx->rx_key_state = SubGhzRxKeyStateNeedSave;
+    if(event.type == SceneManagerEventTypeBack) {
+        subghz->txrx->rx_key_state = SubGhzRxKeyStateBack;
+        scene_manager_previous_scene(subghz->scene_manager);
+        return true;
+    } else if(event.type == SceneManagerEventTypeCustom) {
+        if(event.event == SubghzCustomEventSceneStay) {
+            subghz->txrx->rx_key_state = SubGhzRxKeyStateBack;
             scene_manager_previous_scene(subghz->scene_manager);
             scene_manager_previous_scene(subghz->scene_manager);
             return true;
             return true;
-        } else if(event.event == SubghzCustomEventSceneNeedSavingNo) {
+        } else if(event.event == SubghzCustomEventSceneExit) {
             if(subghz->txrx->rx_key_state == SubGhzRxKeyStateExit) {
             if(subghz->txrx->rx_key_state == SubGhzRxKeyStateExit) {
                 subghz->txrx->rx_key_state = SubGhzRxKeyStateIDLE;
                 subghz->txrx->rx_key_state = SubGhzRxKeyStateIDLE;
                 scene_manager_search_and_switch_to_previous_scene(
                 scene_manager_search_and_switch_to_previous_scene(

+ 109 - 17
applications/subghz/scenes/subghz_scene_read_raw.c

@@ -2,6 +2,9 @@
 #include "../views/subghz_read_raw.h"
 #include "../views/subghz_read_raw.h"
 #include <lib/subghz/protocols/subghz_protocol_raw.h>
 #include <lib/subghz/protocols/subghz_protocol_raw.h>
 #include <lib/subghz/subghz_parser.h>
 #include <lib/subghz/subghz_parser.h>
+#include <lib/toolbox/path.h>
+
+#define RAW_FILE_NAME "Raw_temp"
 
 
 static void subghz_scene_read_raw_update_statusbar(void* context) {
 static void subghz_scene_read_raw_update_statusbar(void* context) {
     furi_assert(context);
     furi_assert(context);
@@ -27,13 +30,20 @@ void subghz_scene_read_raw_callback(SubghzCustomEvent event, void* context) {
     view_dispatcher_send_custom_event(subghz->view_dispatcher, event);
     view_dispatcher_send_custom_event(subghz->view_dispatcher, event);
 }
 }
 
 
+void subghz_scene_read_raw_callback_end_tx(void* context) {
+    furi_assert(context);
+    SubGhz* subghz = context;
+    view_dispatcher_send_custom_event(
+        subghz->view_dispatcher, SubghzCustomEventViewReadRAWSendStop);
+}
+
 void subghz_scene_read_raw_on_enter(void* context) {
 void subghz_scene_read_raw_on_enter(void* context) {
     SubGhz* subghz = context;
     SubGhz* subghz = context;
 
 
-    if(subghz->txrx->rx_key_state == SubGhzRxKeyStateNeedSave) {
-        view_dispatcher_send_custom_event(
-            subghz->view_dispatcher, SubghzCustomEventViewReadRAWMore);
+    if(subghz->txrx->rx_key_state == SubGhzRxKeyStateBack) {
+        subghz_read_raw_set_status(subghz->subghz_read_raw, SubghzReadRAWStatusIDLE);
     } else {
     } else {
+        subghz_read_raw_set_status(subghz->subghz_read_raw, SubghzReadRAWStatusStart);
         subghz->txrx->rx_key_state = SubGhzRxKeyStateIDLE;
         subghz->txrx->rx_key_state = SubGhzRxKeyStateIDLE;
     }
     }
 
 
@@ -46,6 +56,11 @@ void subghz_scene_read_raw_on_enter(void* context) {
     subghz_worker_set_pair_callback(
     subghz_worker_set_pair_callback(
         subghz->txrx->worker, (SubGhzWorkerPairCallback)subghz_parser_raw_parse);
         subghz->txrx->worker, (SubGhzWorkerPairCallback)subghz_parser_raw_parse);
 
 
+    subghz_protocol_raw_file_encoder_worker_set_callback_end(
+        (SubGhzProtocolRAW*)subghz->txrx->protocol_result,
+        subghz_scene_read_raw_callback_end_tx,
+        subghz);
+
     view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewReadRAW);
     view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewReadRAW);
 }
 }
 
 
@@ -54,20 +69,29 @@ bool subghz_scene_read_raw_on_event(void* context, SceneManagerEvent event) {
     if(event.type == SceneManagerEventTypeCustom) {
     if(event.type == SceneManagerEventTypeCustom) {
         switch(event.event) {
         switch(event.event) {
         case SubghzCustomEventViewReadRAWBack:
         case SubghzCustomEventViewReadRAWBack:
+            //Stop TX
+            if(subghz->txrx->txrx_state == SubGhzTxRxStateTx) {
+                subghz_tx_stop(subghz);
+                subghz_sleep(subghz);
+            }
+            //Stop RX
             if(subghz->txrx->txrx_state == SubGhzTxRxStateRx) {
             if(subghz->txrx->txrx_state == SubGhzTxRxStateRx) {
                 subghz_rx_end(subghz);
                 subghz_rx_end(subghz);
                 subghz_sleep(subghz);
                 subghz_sleep(subghz);
             };
             };
-            subghz->txrx->frequency = subghz_frequencies[subghz_frequencies_433_92];
-            subghz->txrx->preset = FuriHalSubGhzPresetOok650Async;
+            //Stop save file
             subghz_protocol_raw_save_to_file_stop(
             subghz_protocol_raw_save_to_file_stop(
                 (SubGhzProtocolRAW*)subghz->txrx->protocol_result);
                 (SubGhzProtocolRAW*)subghz->txrx->protocol_result);
             subghz->state_notifications = SubGhzNotificationStateIDLE;
             subghz->state_notifications = SubGhzNotificationStateIDLE;
-
-            if(subghz->txrx->rx_key_state == SubGhzRxKeyStateAddKey) {
+            //needed save?
+            if((subghz->txrx->rx_key_state == SubGhzRxKeyStateAddKey) ||
+               (subghz->txrx->rx_key_state == SubGhzRxKeyStateBack)) {
                 subghz->txrx->rx_key_state = SubGhzRxKeyStateExit;
                 subghz->txrx->rx_key_state = SubGhzRxKeyStateExit;
                 scene_manager_next_scene(subghz->scene_manager, SubGhzSceneNeedSaving);
                 scene_manager_next_scene(subghz->scene_manager, SubGhzSceneNeedSaving);
             } else {
             } else {
+                //Restore default setting
+                subghz->txrx->frequency = subghz_frequencies[subghz_frequencies_433_92];
+                subghz->txrx->preset = FuriHalSubGhzPresetOok650Async;
                 scene_manager_search_and_switch_to_previous_scene(
                 scene_manager_search_and_switch_to_previous_scene(
                     subghz->scene_manager, SubGhzSceneStart);
                     subghz->scene_manager, SubGhzSceneStart);
             }
             }
@@ -80,6 +104,63 @@ bool subghz_scene_read_raw_on_event(void* context, SceneManagerEvent event) {
             scene_manager_next_scene(subghz->scene_manager, SubGhzSceneReceiverConfig);
             scene_manager_next_scene(subghz->scene_manager, SubGhzSceneReceiverConfig);
             return true;
             return true;
             break;
             break;
+        case SubghzCustomEventViewReadRAWErase:
+            subghz->txrx->rx_key_state = SubGhzRxKeyStateIDLE;
+            return true;
+            break;
+        case SubghzCustomEventViewReadRAWSendStart:
+            //set the path to read the file
+            if(strcmp(
+                   subghz_protocol_raw_get_last_file_name(
+                       (SubGhzProtocolRAW*)subghz->txrx->protocol_result),
+                   "")) {
+                string_t temp_str;
+                string_init_printf(
+                    temp_str,
+                    "%s",
+                    subghz_protocol_raw_get_last_file_name(
+                        (SubGhzProtocolRAW*)subghz->txrx->protocol_result));
+                path_extract_filename_no_ext(string_get_cstr(temp_str), temp_str);
+                strlcpy(
+                    subghz->file_name,
+                    string_get_cstr(temp_str),
+                    strlen(string_get_cstr(temp_str)) + 1);
+                string_printf(
+                    temp_str,
+                    "%s/%s%s",
+                    SUBGHZ_APP_PATH_FOLDER,
+                    subghz->file_name,
+                    SUBGHZ_APP_EXTENSION);
+
+                subghz_protocol_raw_set_last_file_name(
+                    (SubGhzProtocolRAW*)subghz->txrx->protocol_result, string_get_cstr(temp_str));
+                string_clear(temp_str);
+                //start send
+                subghz->state_notifications = SubGhzNotificationStateIDLE;
+                if(subghz->txrx->txrx_state == SubGhzTxRxStateRx) {
+                    subghz_rx_end(subghz);
+                }
+                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 {
+                        subghz->state_notifications = SubGhzNotificationStateTX;
+                        subghz->txrx->rx_key_state = SubGhzRxKeyStateAddKey;
+                    }
+                }
+            }
+            return true;
+            break;
+        case SubghzCustomEventViewReadRAWSendStop:
+            subghz->state_notifications = SubGhzNotificationStateIDLE;
+            if(subghz->txrx->txrx_state == SubGhzTxRxStateTx) {
+                subghz_tx_stop(subghz);
+                subghz_sleep(subghz);
+            }
+            subghz_read_raw_stop_send(subghz->subghz_read_raw);
+            return true;
+            break;
         case SubghzCustomEventViewReadRAWIDLE:
         case SubghzCustomEventViewReadRAWIDLE:
             if(subghz->txrx->txrx_state == SubGhzTxRxStateRx) {
             if(subghz->txrx->txrx_state == SubGhzTxRxStateRx) {
                 subghz_rx_end(subghz);
                 subghz_rx_end(subghz);
@@ -101,7 +182,7 @@ bool subghz_scene_read_raw_on_event(void* context, SceneManagerEvent event) {
                 subghz_get_preset_name(subghz, subghz->error_str);
                 subghz_get_preset_name(subghz, subghz->error_str);
                 if(subghz_protocol_raw_save_to_file_init(
                 if(subghz_protocol_raw_save_to_file_init(
                        (SubGhzProtocolRAW*)subghz->txrx->protocol_result,
                        (SubGhzProtocolRAW*)subghz->txrx->protocol_result,
-                       "Raw_temp",
+                       RAW_FILE_NAME,
                        subghz->txrx->frequency,
                        subghz->txrx->frequency,
                        string_get_cstr(subghz->error_str))) {
                        string_get_cstr(subghz->error_str))) {
                     if((subghz->txrx->txrx_state == SubGhzTxRxStateIDLE) ||
                     if((subghz->txrx->txrx_state == SubGhzTxRxStateIDLE) ||
@@ -118,21 +199,25 @@ bool subghz_scene_read_raw_on_event(void* context, SceneManagerEvent event) {
 
 
             return true;
             return true;
             break;
             break;
-        case SubghzCustomEventViewReadRAWMore:
+        case SubghzCustomEventViewReadRAWSave:
             if(strcmp(
             if(strcmp(
                    subghz_protocol_raw_get_last_file_name(
                    subghz_protocol_raw_get_last_file_name(
                        (SubGhzProtocolRAW*)subghz->txrx->protocol_result),
                        (SubGhzProtocolRAW*)subghz->txrx->protocol_result),
                    "")) {
                    "")) {
+                string_t temp_str;
+                string_init_printf(
+                    temp_str,
+                    "%s",
+                    subghz_protocol_raw_get_last_file_name(
+                        (SubGhzProtocolRAW*)subghz->txrx->protocol_result));
+                path_extract_filename_no_ext(string_get_cstr(temp_str), temp_str);
                 strlcpy(
                 strlcpy(
                     subghz->file_name,
                     subghz->file_name,
-                    subghz_protocol_raw_get_last_file_name(
-                        (SubGhzProtocolRAW*)subghz->txrx->protocol_result),
-                    strlen(subghz_protocol_raw_get_last_file_name(
-                        (SubGhzProtocolRAW*)subghz->txrx->protocol_result)) +
-                        1);
+                    string_get_cstr(temp_str),
+                    strlen(string_get_cstr(temp_str)) + 1);
+
                 //set the path to read the file
                 //set the path to read the file
-                string_t temp_str;
-                string_init_printf(
+                string_printf(
                     temp_str,
                     temp_str,
                     "%s/%s%s",
                     "%s/%s%s",
                     SUBGHZ_APP_PATH_FOLDER,
                     SUBGHZ_APP_PATH_FOLDER,
@@ -142,7 +227,10 @@ bool subghz_scene_read_raw_on_event(void* context, SceneManagerEvent event) {
                     (SubGhzProtocolRAW*)subghz->txrx->protocol_result, string_get_cstr(temp_str));
                     (SubGhzProtocolRAW*)subghz->txrx->protocol_result, string_get_cstr(temp_str));
                 string_clear(temp_str);
                 string_clear(temp_str);
 
 
-                scene_manager_next_scene(subghz->scene_manager, SubGhzSceneReadRAWMenu);
+                scene_manager_set_scene_state(
+                    subghz->scene_manager, SubGhzSceneReadRAW, SubghzCustomEventManagerSet);
+                subghz->txrx->rx_key_state = SubGhzRxKeyStateBack;
+                scene_manager_next_scene(subghz->scene_manager, SubGhzSceneSaveName);
             }
             }
             return true;
             return true;
             break;
             break;
@@ -160,6 +248,10 @@ bool subghz_scene_read_raw_on_event(void* context, SceneManagerEvent event) {
                     (SubGhzProtocolRAW*)subghz->txrx->protocol_result));
                     (SubGhzProtocolRAW*)subghz->txrx->protocol_result));
             subghz_read_raw_add_data_rssi(subghz->subghz_read_raw, furi_hal_subghz_get_rssi());
             subghz_read_raw_add_data_rssi(subghz->subghz_read_raw, furi_hal_subghz_get_rssi());
             break;
             break;
+        case SubGhzNotificationStateTX:
+            notification_message(subghz->notifications, &sequence_blink_green_10);
+            subghz_read_raw_update_sin(subghz->subghz_read_raw);
+            break;
         default:
         default:
             break;
             break;
         }
         }

+ 0 - 72
applications/subghz/scenes/subghz_scene_read_raw_menu.c

@@ -1,72 +0,0 @@
-#include "../subghz_i.h"
-
-enum SubmenuIndex {
-    SubmenuIndexEmulate,
-    SubmenuIndexEdit,
-    SubmenuIndexDelete,
-};
-
-void subghz_scene_read_raw_menu_submenu_callback(void* context, uint32_t index) {
-    SubGhz* subghz = context;
-    view_dispatcher_send_custom_event(subghz->view_dispatcher, index);
-}
-
-void subghz_scene_read_raw_menu_on_enter(void* context) {
-    SubGhz* subghz = context;
-    submenu_add_item(
-        subghz->submenu,
-        "Emulate",
-        SubmenuIndexEmulate,
-        subghz_scene_read_raw_menu_submenu_callback,
-        subghz);
-
-    submenu_add_item(
-        subghz->submenu,
-        "Save",
-        SubmenuIndexEdit,
-        subghz_scene_read_raw_menu_submenu_callback,
-        subghz);
-
-    submenu_add_item(
-        subghz->submenu,
-        "Delete",
-        SubmenuIndexDelete,
-        subghz_scene_read_raw_menu_submenu_callback,
-        subghz);
-
-    submenu_set_selected_item(
-        subghz->submenu,
-        scene_manager_get_scene_state(subghz->scene_manager, SubGhzSceneSavedMenu));
-
-    view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewMenu);
-}
-
-bool subghz_scene_read_raw_menu_on_event(void* context, SceneManagerEvent event) {
-    SubGhz* subghz = context;
-
-    if(event.type == SceneManagerEventTypeCustom) {
-        if(event.event == SubmenuIndexEmulate) {
-            scene_manager_set_scene_state(
-                subghz->scene_manager, SubGhzSceneReadRAWMenu, SubmenuIndexEmulate);
-            scene_manager_next_scene(subghz->scene_manager, SubGhzSceneTransmitter);
-            return true;
-        } else if(event.event == SubmenuIndexDelete) {
-            scene_manager_set_scene_state(
-                subghz->scene_manager, SubGhzSceneReadRAWMenu, SubmenuIndexDelete);
-            scene_manager_next_scene(subghz->scene_manager, SubGhzSceneDelete);
-            return true;
-        } else if(event.event == SubmenuIndexEdit) {
-            scene_manager_set_scene_state(
-                subghz->scene_manager, SubGhzSceneReadRAWMenu, SubghzCustomEventManagerSet);
-            scene_manager_next_scene(subghz->scene_manager, SubGhzSceneSaveName);
-            return true;
-        }
-    }
-    return false;
-}
-
-void subghz_scene_read_raw_menu_on_exit(void* context) {
-    SubGhz* subghz = context;
-    submenu_clean(subghz->submenu);
-    subghz->txrx->rx_key_state = SubGhzRxKeyStateIDLE;
-}

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

@@ -2,6 +2,7 @@
 #include <lib/toolbox/random_name.h>
 #include <lib/toolbox/random_name.h>
 #include "file-worker.h"
 #include "file-worker.h"
 #include "../helpers/subghz_custom_event.h"
 #include "../helpers/subghz_custom_event.h"
+#include <lib/subghz/protocols/subghz_protocol_raw.h>
 
 
 void subghz_scene_save_name_text_input_callback(void* context) {
 void subghz_scene_save_name_text_input_callback(void* context) {
     SubGhz* subghz = context;
     SubGhz* subghz = context;
@@ -20,7 +21,7 @@ void subghz_scene_save_name_on_enter(void* context) {
         dev_name_empty = true;
         dev_name_empty = true;
     } else {
     } else {
         memcpy(subghz->file_name_tmp, subghz->file_name, strlen(subghz->file_name) + 1);
         memcpy(subghz->file_name_tmp, subghz->file_name, strlen(subghz->file_name) + 1);
-        if(scene_manager_get_scene_state(subghz->scene_manager, SubGhzSceneReadRAWMenu) ==
+        if(scene_manager_get_scene_state(subghz->scene_manager, SubGhzSceneReadRAW) ==
            SubghzCustomEventManagerSet) {
            SubghzCustomEventManagerSet) {
             subghz_get_next_name_file(subghz);
             subghz_get_next_name_file(subghz);
         }
         }
@@ -49,6 +50,11 @@ bool subghz_scene_save_name_on_event(void* context, SceneManagerEvent event) {
                     subghz_save_protocol_to_file(subghz, subghz->file_name);
                     subghz_save_protocol_to_file(subghz, subghz->file_name);
                 }
                 }
 
 
+                if(scene_manager_get_scene_state(subghz->scene_manager, SubGhzSceneReadRAW) ==
+                   SubghzCustomEventManagerSet) {
+                    subghz_protocol_raw_set_last_file_name(
+                        (SubGhzProtocolRAW*)subghz->txrx->protocol_result, subghz->file_name);
+                }
                 subghz_file_name_clear(subghz);
                 subghz_file_name_clear(subghz);
                 scene_manager_next_scene(subghz->scene_manager, SubGhzSceneSaveSuccess);
                 scene_manager_next_scene(subghz->scene_manager, SubGhzSceneSaveSuccess);
                 return true;
                 return true;
@@ -68,5 +74,5 @@ void subghz_scene_save_name_on_exit(void* context) {
     // Clear view
     // Clear view
     text_input_clean(subghz->text_input);
     text_input_clean(subghz->text_input);
     scene_manager_set_scene_state(
     scene_manager_set_scene_state(
-        subghz->scene_manager, SubGhzSceneReadRAWMenu, SubghzCustomEventManagerNoSet);
+        subghz->scene_manager, SubGhzSceneReadRAW, SubghzCustomEventManagerNoSet);
 }
 }

+ 5 - 2
applications/subghz/scenes/subghz_scene_save_success.c

@@ -26,8 +26,11 @@ bool subghz_scene_save_success_on_event(void* context, SceneManagerEvent event)
         if(event.event == SubghzCustomEventSceneSaveSuccess) {
         if(event.event == SubghzCustomEventSceneSaveSuccess) {
             if(!scene_manager_search_and_switch_to_previous_scene(
             if(!scene_manager_search_and_switch_to_previous_scene(
                    subghz->scene_manager, SubGhzSceneReceiver)) {
                    subghz->scene_manager, SubGhzSceneReceiver)) {
-                scene_manager_search_and_switch_to_previous_scene(
-                    subghz->scene_manager, SubGhzSceneStart);
+                if(!scene_manager_search_and_switch_to_previous_scene(
+                       subghz->scene_manager, SubGhzSceneReadRAW)) {
+                    scene_manager_search_and_switch_to_previous_scene(
+                        subghz->scene_manager, SubGhzSceneStart);
+                }
             }
             }
             return true;
             return true;
         }
         }

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

@@ -23,7 +23,7 @@ void subghz_scene_start_on_enter(void* context) {
         subghz->submenu, "Read", SubmenuIndexRead, subghz_scene_start_submenu_callback, subghz);
         subghz->submenu, "Read", SubmenuIndexRead, subghz_scene_start_submenu_callback, subghz);
     submenu_add_item(
     submenu_add_item(
         subghz->submenu,
         subghz->submenu,
-        "Read Raw",
+        "Read RAW",
         SubmenuIndexReadRAW,
         SubmenuIndexReadRAW,
         subghz_scene_start_submenu_callback,
         subghz_scene_start_submenu_callback,
         subghz);
         subghz);
@@ -57,6 +57,7 @@ bool subghz_scene_start_on_event(void* context, SceneManagerEvent event) {
         if(event.event == SubmenuIndexReadRAW) {
         if(event.event == SubmenuIndexReadRAW) {
             scene_manager_set_scene_state(
             scene_manager_set_scene_state(
                 subghz->scene_manager, SubGhzSceneStart, SubmenuIndexReadRAW);
                 subghz->scene_manager, SubGhzSceneStart, SubmenuIndexReadRAW);
+            subghz->txrx->rx_key_state = SubGhzRxKeyStateIDLE;
             scene_manager_next_scene(subghz->scene_manager, SubGhzSceneReadRAW);
             scene_manager_next_scene(subghz->scene_manager, SubGhzSceneReadRAW);
             return true;
             return true;
         } else if(event.event == SubmenuIndexRead) {
         } else if(event.event == SubmenuIndexRead) {

+ 1 - 0
applications/subghz/subghz_i.h

@@ -70,6 +70,7 @@ typedef enum {
     SubGhzRxKeyStateIDLE,
     SubGhzRxKeyStateIDLE,
     SubGhzRxKeyStateNoSave,
     SubGhzRxKeyStateNoSave,
     SubGhzRxKeyStateNeedSave,
     SubGhzRxKeyStateNeedSave,
+    SubGhzRxKeyStateBack,
     SubGhzRxKeyStateAddKey,
     SubGhzRxKeyStateAddKey,
     SubGhzRxKeyStateExit,
     SubGhzRxKeyStateExit,
 } SubGhzRxKeyState;
 } SubGhzRxKeyState;

+ 175 - 34
applications/subghz/views/subghz_read_raw.c

@@ -11,13 +11,6 @@
 #include <assets_icons.h>
 #include <assets_icons.h>
 #define SUBGHZ_READ_RAW_RSSI_HISTORY_SIZE 100
 #define SUBGHZ_READ_RAW_RSSI_HISTORY_SIZE 100
 
 
-typedef enum {
-    SubghzReadRAWStatusStart,
-    SubghzReadRAWStatusIDLE,
-    SubghzReadRAWStatusREC,
-    //SubghzReadRAWStatusShowName,
-} SubghzReadRAWStatus;
-
 struct SubghzReadRAW {
 struct SubghzReadRAW {
     View* view;
     View* view;
     SubghzReadRAWCallback callback;
     SubghzReadRAWCallback callback;
@@ -31,6 +24,7 @@ typedef struct {
     uint8_t* rssi_history;
     uint8_t* rssi_history;
     bool rssi_history_end;
     bool rssi_history_end;
     uint8_t ind_write;
     uint8_t ind_write;
+    uint8_t ind_sin;
     SubghzReadRAWStatus satus;
     SubghzReadRAWStatus satus;
 } SubghzReadRAWModel;
 } SubghzReadRAWModel;
 
 
@@ -89,6 +83,88 @@ void subghz_read_raw_update_sample_write(SubghzReadRAW* instance, size_t sample)
         });
         });
 }
 }
 
 
+void subghz_read_raw_stop_send(SubghzReadRAW* instance) {
+    furi_assert(instance);
+
+    with_view_model(
+        instance->view, (SubghzReadRAWModel * model) {
+            if(model->satus == SubghzReadRAWStatusTXRepeat) {
+                // Start TX
+                instance->callback(SubghzCustomEventViewReadRAWSendStart, instance->context);
+            } else {
+                model->satus = SubghzReadRAWStatusIDLE;
+            }
+            return true;
+        });
+}
+
+void subghz_read_raw_update_sin(SubghzReadRAW* instance) {
+    furi_assert(instance);
+    with_view_model(
+        instance->view, (SubghzReadRAWModel * model) {
+            if(model->ind_sin++ > 62) {
+                model->ind_sin = 0;
+            }
+            return true;
+        });
+}
+
+static int8_t subghz_read_raw_tab_sin(uint8_t x) {
+    const uint8_t tab_sin[64] = {0,   3,   6,   9,   12,  16,  19,  22,  25,  28,  31,  34,  37,
+                                 40,  43,  46,  49,  51,  54,  57,  60,  63,  65,  68,  71,  73,
+                                 76,  78,  81,  83,  85,  88,  90,  92,  94,  96,  98,  100, 102,
+                                 104, 106, 107, 109, 111, 112, 113, 115, 116, 117, 118, 120, 121,
+                                 122, 122, 123, 124, 125, 125, 126, 126, 126, 127, 127, 127};
+
+    int8_t r = tab_sin[((x & 0x40) ? -x - 1 : x) & 0x3f];
+    if(x & 0x80) return -r;
+    return r;
+}
+
+void subghz_read_raw_draw_sin(Canvas* canvas, SubghzReadRAWModel* model) {
+#define SUBGHZ_RAW_SIN_AMPLITUDE 11
+    for(int i = 114; i > 0; i--) {
+        canvas_draw_line(
+            canvas,
+            i,
+            32 - subghz_read_raw_tab_sin(i + model->ind_sin * 16) / SUBGHZ_RAW_SIN_AMPLITUDE,
+            i + 1,
+            32 + subghz_read_raw_tab_sin((i + model->ind_sin * 16 + 1) * 2) /
+                     SUBGHZ_RAW_SIN_AMPLITUDE);
+        canvas_draw_line(
+            canvas,
+            i + 1,
+            32 - subghz_read_raw_tab_sin((i + model->ind_sin * 16)) / SUBGHZ_RAW_SIN_AMPLITUDE,
+            i + 2,
+            32 + subghz_read_raw_tab_sin((i + model->ind_sin * 16 + 1) * 2) /
+                     SUBGHZ_RAW_SIN_AMPLITUDE);
+    }
+}
+
+void subghz_read_raw_draw_scale(Canvas* canvas, SubghzReadRAWModel* model) {
+#define SUBGHZ_RAW_TOP_SCALE 14
+#define SUBGHZ_RAW_END_SCALE 115
+
+    if(model->rssi_history_end == false) {
+        for(int i = SUBGHZ_RAW_END_SCALE; i > 0; i -= 15) {
+            canvas_draw_line(canvas, i, SUBGHZ_RAW_TOP_SCALE, i, SUBGHZ_RAW_TOP_SCALE + 4);
+            canvas_draw_line(canvas, i - 5, SUBGHZ_RAW_TOP_SCALE, i - 5, SUBGHZ_RAW_TOP_SCALE + 2);
+            canvas_draw_line(
+                canvas, i - 10, SUBGHZ_RAW_TOP_SCALE, i - 10, SUBGHZ_RAW_TOP_SCALE + 2);
+        }
+    } else {
+        for(int i = SUBGHZ_RAW_END_SCALE - model->ind_write % 15; i > -15; i -= 15) {
+            canvas_draw_line(canvas, i, SUBGHZ_RAW_TOP_SCALE, i, SUBGHZ_RAW_TOP_SCALE + 4);
+            if(SUBGHZ_RAW_END_SCALE > i + 5)
+                canvas_draw_line(
+                    canvas, i + 5, SUBGHZ_RAW_TOP_SCALE, i + 5, SUBGHZ_RAW_TOP_SCALE + 2);
+            if(SUBGHZ_RAW_END_SCALE > i + 10)
+                canvas_draw_line(
+                    canvas, i + 10, SUBGHZ_RAW_TOP_SCALE, i + 10, SUBGHZ_RAW_TOP_SCALE + 2);
+        }
+    }
+}
+
 void subghz_read_raw_draw_rssi(Canvas* canvas, SubghzReadRAWModel* model) {
 void subghz_read_raw_draw_rssi(Canvas* canvas, SubghzReadRAWModel* model) {
     int ind = 0;
     int ind = 0;
     int base = 0;
     int base = 0;
@@ -134,17 +210,26 @@ void subghz_read_raw_draw(Canvas* canvas, SubghzReadRAWModel* model) {
         canvas, 126, 0, AlignRight, AlignTop, string_get_cstr(model->sample_write));
         canvas, 126, 0, AlignRight, AlignTop, string_get_cstr(model->sample_write));
 
 
     canvas_draw_line(canvas, 0, 14, 115, 14);
     canvas_draw_line(canvas, 0, 14, 115, 14);
-    subghz_read_raw_draw_rssi(canvas, model);
     canvas_draw_line(canvas, 0, 48, 115, 48);
     canvas_draw_line(canvas, 0, 48, 115, 48);
     canvas_draw_line(canvas, 115, 14, 115, 48);
     canvas_draw_line(canvas, 115, 14, 115, 48);
+    subghz_read_raw_draw_scale(canvas, model);
+
+    if((model->satus == SubghzReadRAWStatusTX) || (model->satus == SubghzReadRAWStatusTXRepeat)) {
+        subghz_read_raw_draw_sin(canvas, model);
+    } else {
+        subghz_read_raw_draw_rssi(canvas, model);
+    }
 
 
     if(model->satus == SubghzReadRAWStatusIDLE) {
     if(model->satus == SubghzReadRAWStatusIDLE) {
-        elements_button_left(canvas, "Config");
-        elements_button_center(canvas, "REC");
-        elements_button_right(canvas, "More");
+        elements_button_left(canvas, "Erase");
+        elements_button_center(canvas, "Send");
+        elements_button_right(canvas, "Save");
     } else if(model->satus == SubghzReadRAWStatusStart) {
     } else if(model->satus == SubghzReadRAWStatusStart) {
         elements_button_left(canvas, "Config");
         elements_button_left(canvas, "Config");
         elements_button_center(canvas, "REC");
         elements_button_center(canvas, "REC");
+    } else if(
+        (model->satus == SubghzReadRAWStatusTX) || (model->satus == SubghzReadRAWStatusTXRepeat)) {
+        elements_button_center(canvas, "Send");
     } else {
     } else {
         elements_button_center(canvas, "Stop");
         elements_button_center(canvas, "Stop");
     }
     }
@@ -158,35 +243,83 @@ bool subghz_read_raw_input(InputEvent* event, void* context) {
     furi_assert(context);
     furi_assert(context);
     SubghzReadRAW* instance = context;
     SubghzReadRAW* instance = context;
 
 
-    if(event->key == InputKeyBack && event->type == InputTypeShort) {
-        instance->callback(SubghzCustomEventViewReadRAWBack, instance->context);
+    if(event->key == InputKeyOk && event->type == InputTypePress) {
+        with_view_model(
+            instance->view, (SubghzReadRAWModel * model) {
+                uint8_t ret = false;
+                if(model->satus == SubghzReadRAWStatusIDLE) {
+                    // Start TX
+                    instance->callback(SubghzCustomEventViewReadRAWSendStart, instance->context);
+                    model->satus = SubghzReadRAWStatusTXRepeat;
+                    ret = true;
+                } else if(model->satus == SubghzReadRAWStatusTX) {
+                    model->satus = SubghzReadRAWStatusTXRepeat;
+                }
+                return ret;
+            });
+    } else if(event->key == InputKeyOk && event->type == InputTypeRelease) {
+        with_view_model(
+            instance->view, (SubghzReadRAWModel * model) {
+                if(model->satus == SubghzReadRAWStatusTXRepeat) {
+                    // Stop repeat TX
+                    model->satus = SubghzReadRAWStatusTX;
+                }
+                return false;
+            });
+    } else if(event->key == InputKeyBack && event->type == InputTypeShort) {
+        with_view_model(
+            instance->view, (SubghzReadRAWModel * model) {
+                if(model->satus == SubghzReadRAWStatusREC) {
+                    //Stop REC
+                    instance->callback(SubghzCustomEventViewReadRAWIDLE, instance->context);
+                    model->satus = SubghzReadRAWStatusIDLE;
+                } else {
+                    //Exit
+                    instance->callback(SubghzCustomEventViewReadRAWBack, instance->context);
+                }
+                return true;
+            });
+
     } else if(event->key == InputKeyLeft && event->type == InputTypeShort) {
     } else if(event->key == InputKeyLeft && event->type == InputTypeShort) {
         with_view_model(
         with_view_model(
             instance->view, (SubghzReadRAWModel * model) {
             instance->view, (SubghzReadRAWModel * model) {
-                if(model->satus == SubghzReadRAWStatusIDLE ||
-                   model->satus == SubghzReadRAWStatusStart) {
+                if(model->satus == SubghzReadRAWStatusStart) {
+                    //Config
                     instance->callback(SubghzCustomEventViewReadRAWConfig, instance->context);
                     instance->callback(SubghzCustomEventViewReadRAWConfig, instance->context);
                 }
                 }
+
+                if(model->satus == SubghzReadRAWStatusIDLE) {
+                    //Erase
+                    model->satus = SubghzReadRAWStatusStart;
+                    model->rssi_history_end = false;
+                    model->ind_write = 0;
+                    string_set(model->sample_write, "0 spl.");
+                    instance->callback(SubghzCustomEventViewReadRAWErase, instance->context);
+                }
                 return true;
                 return true;
             });
             });
     } else if(event->key == InputKeyRight && event->type == InputTypeShort) {
     } else if(event->key == InputKeyRight && event->type == InputTypeShort) {
         with_view_model(
         with_view_model(
             instance->view, (SubghzReadRAWModel * model) {
             instance->view, (SubghzReadRAWModel * model) {
+                //Save
                 if(model->satus == SubghzReadRAWStatusIDLE) {
                 if(model->satus == SubghzReadRAWStatusIDLE) {
-                    instance->callback(SubghzCustomEventViewReadRAWMore, instance->context);
+                    instance->callback(SubghzCustomEventViewReadRAWSave, instance->context);
                 }
                 }
                 return true;
                 return true;
             });
             });
     } else if(event->key == InputKeyOk && event->type == InputTypeShort) {
     } else if(event->key == InputKeyOk && event->type == InputTypeShort) {
         with_view_model(
         with_view_model(
             instance->view, (SubghzReadRAWModel * model) {
             instance->view, (SubghzReadRAWModel * model) {
-                if(model->satus == SubghzReadRAWStatusIDLE ||
-                   model->satus == SubghzReadRAWStatusStart) {
+                if(model->satus == SubghzReadRAWStatusStart) {
+                    //Record
                     instance->callback(SubghzCustomEventViewReadRAWREC, instance->context);
                     instance->callback(SubghzCustomEventViewReadRAWREC, instance->context);
                     model->satus = SubghzReadRAWStatusREC;
                     model->satus = SubghzReadRAWStatusREC;
                     model->ind_write = 0;
                     model->ind_write = 0;
                     model->rssi_history_end = false;
                     model->rssi_history_end = false;
-                } else {
+                } else if(
+                    (model->satus != SubghzReadRAWStatusTX) &&
+                    (model->satus != SubghzReadRAWStatusTXRepeat)) {
+                    //Stop
                     instance->callback(SubghzCustomEventViewReadRAWIDLE, instance->context);
                     instance->callback(SubghzCustomEventViewReadRAWIDLE, instance->context);
                     model->satus = SubghzReadRAWStatusIDLE;
                     model->satus = SubghzReadRAWStatusIDLE;
                 }
                 }
@@ -197,19 +330,29 @@ bool subghz_read_raw_input(InputEvent* event, void* context) {
     return true;
     return true;
 }
 }
 
 
+void subghz_read_raw_set_status(SubghzReadRAW* instance, SubghzReadRAWStatus satus) {
+    furi_assert(instance);
+    if(satus == SubghzReadRAWStatusStart) {
+        with_view_model(
+            instance->view, (SubghzReadRAWModel * model) {
+                model->satus = SubghzReadRAWStatusStart;
+                model->rssi_history_end = false;
+                model->ind_write = 0;
+                string_set(model->sample_write, "0 spl.");
+                return true;
+            });
+    } else if(satus == SubghzReadRAWStatusIDLE) {
+        with_view_model(
+            instance->view, (SubghzReadRAWModel * model) {
+                model->satus = SubghzReadRAWStatusIDLE;
+                return true;
+            });
+    }
+}
+
 void subghz_read_raw_enter(void* context) {
 void subghz_read_raw_enter(void* context) {
     furi_assert(context);
     furi_assert(context);
-    SubghzReadRAW* instance = context;
-
-    with_view_model(
-        instance->view, (SubghzReadRAWModel * model) {
-            model->satus = SubghzReadRAWStatusStart;
-            model->rssi_history = furi_alloc(SUBGHZ_READ_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;
-        });
+    //SubghzReadRAW* instance = context;
 }
 }
 
 
 void subghz_read_raw_exit(void* context) {
 void subghz_read_raw_exit(void* context) {
@@ -223,10 +366,6 @@ void subghz_read_raw_exit(void* context) {
                 instance->callback(SubghzCustomEventViewReadRAWIDLE, instance->context);
                 instance->callback(SubghzCustomEventViewReadRAWIDLE, instance->context);
                 model->satus = SubghzReadRAWStatusStart;
                 model->satus = SubghzReadRAWStatusStart;
             }
             }
-            string_reset(model->frequency_str);
-            string_reset(model->preset_str);
-            string_reset(model->sample_write);
-            free(model->rssi_history);
             return true;
             return true;
         });
         });
 }
 }
@@ -248,6 +387,7 @@ SubghzReadRAW* subghz_read_raw_alloc() {
             string_init(model->frequency_str);
             string_init(model->frequency_str);
             string_init(model->preset_str);
             string_init(model->preset_str);
             string_init(model->sample_write);
             string_init(model->sample_write);
+            model->rssi_history = furi_alloc(SUBGHZ_READ_RAW_RSSI_HISTORY_SIZE * sizeof(uint8_t));
             return true;
             return true;
         });
         });
 
 
@@ -262,6 +402,7 @@ void subghz_read_raw_free(SubghzReadRAW* instance) {
             string_clear(model->frequency_str);
             string_clear(model->frequency_str);
             string_clear(model->preset_str);
             string_clear(model->preset_str);
             string_clear(model->sample_write);
             string_clear(model->sample_write);
+            free(model->rssi_history);
             return true;
             return true;
         });
         });
     view_free(instance->view);
     view_free(instance->view);

+ 14 - 0
applications/subghz/views/subghz_read_raw.h

@@ -7,6 +7,14 @@ typedef struct SubghzReadRAW SubghzReadRAW;
 
 
 typedef void (*SubghzReadRAWCallback)(SubghzCustomEvent event, void* context);
 typedef void (*SubghzReadRAWCallback)(SubghzCustomEvent event, void* context);
 
 
+typedef enum {
+    SubghzReadRAWStatusStart,
+    SubghzReadRAWStatusIDLE,
+    SubghzReadRAWStatusREC,
+    SubghzReadRAWStatusTX,
+    SubghzReadRAWStatusTXRepeat,
+} SubghzReadRAWStatus;
+
 void subghz_read_raw_set_callback(
 void subghz_read_raw_set_callback(
     SubghzReadRAW* subghz_read_raw,
     SubghzReadRAW* subghz_read_raw,
     SubghzReadRAWCallback callback,
     SubghzReadRAWCallback callback,
@@ -23,6 +31,12 @@ void subghz_read_raw_add_data_statusbar(
 
 
 void subghz_read_raw_update_sample_write(SubghzReadRAW* instance, size_t sample);
 void subghz_read_raw_update_sample_write(SubghzReadRAW* instance, size_t sample);
 
 
+void subghz_read_raw_stop_send(SubghzReadRAW* instance);
+
+void subghz_read_raw_update_sin(SubghzReadRAW* instance);
+
 void subghz_read_raw_add_data_rssi(SubghzReadRAW* instance, float rssi);
 void subghz_read_raw_add_data_rssi(SubghzReadRAW* instance, float rssi);
 
 
+void subghz_read_raw_set_status(SubghzReadRAW* instance, SubghzReadRAWStatus satus);
+
 View* subghz_read_raw_get_view(SubghzReadRAW* subghz_static);
 View* subghz_read_raw_get_view(SubghzReadRAW* subghz_static);

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

@@ -469,7 +469,7 @@ uint32_t furi_hal_subghz_set_frequency(uint32_t value) {
     case FuriHalVersionRegionEuRu:
     case FuriHalVersionRegionEuRu:
         //433,05..434,79; 868,15..868,55
         //433,05..434,79; 868,15..868,55
         if(!(value >= 433050000 && value <= 434790000) &&
         if(!(value >= 433050000 && value <= 434790000) &&
-           !(value >= 868150000 && value <= 8680550000)) {
+           !(value >= 868150000 && value <= 868550000)) {
         } else {
         } else {
             txrx = true;
             txrx = true;
         }
         }

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

@@ -469,7 +469,7 @@ uint32_t furi_hal_subghz_set_frequency(uint32_t value) {
     case FuriHalVersionRegionEuRu:
     case FuriHalVersionRegionEuRu:
         //433,05..434,79; 868,15..868,55
         //433,05..434,79; 868,15..868,55
         if(!(value >= 433050000 && value <= 434790000) &&
         if(!(value >= 433050000 && value <= 434790000) &&
-           !(value >= 868150000 && value <= 8680550000)) {
+           !(value >= 868150000 && value <= 868550000)) {
         } else {
         } else {
             txrx = true;
             txrx = true;
         }
         }

+ 27 - 1
lib/subghz/protocols/subghz_protocol_raw.c

@@ -17,6 +17,8 @@ struct SubGhzProtocolRAW {
     string_t file_name;
     string_t file_name;
     size_t sample_write;
     size_t sample_write;
     bool last_level;
     bool last_level;
+    SubGhzProtocolRAWCallbackEnd callback_end;
+    void* context_end;
 };
 };
 
 
 typedef enum {
 typedef enum {
@@ -65,6 +67,22 @@ void subghz_protocol_raw_free(SubGhzProtocolRAW* instance) {
     free(instance);
     free(instance);
 }
 }
 
 
+void subghz_protocol_raw_file_encoder_worker_callback_end(void* context) {
+    furi_assert(context);
+    SubGhzProtocolRAW* instance = context;
+    if(instance->callback_end) instance->callback_end(instance->context_end);
+}
+
+void subghz_protocol_raw_file_encoder_worker_set_callback_end(
+    SubGhzProtocolRAW* instance,
+    SubGhzProtocolRAWCallbackEnd callback_end,
+    void* context_end) {
+    furi_assert(instance);
+    furi_assert(callback_end);
+    instance->callback_end = callback_end;
+    instance->context_end = context_end;
+}
+
 void subghz_protocol_raw_file_encoder_worker_stop(void* context) {
 void subghz_protocol_raw_file_encoder_worker_stop(void* context) {
     furi_assert(context);
     furi_assert(context);
     SubGhzProtocolRAW* instance = context;
     SubGhzProtocolRAW* instance = context;
@@ -90,10 +108,17 @@ bool subghz_protocol_raw_send_key(
         //the worker needs a file in order to open and read part of the file
         //the worker needs a file in order to open and read part of the file
         osDelay(100);
         osDelay(100);
         instance->file_is_open = RAWFileIsOpenRead;
         instance->file_is_open = RAWFileIsOpenRead;
+        //Forwarding UPLOAD to common encoder
         subghz_protocol_encoder_common_set_callback(
         subghz_protocol_encoder_common_set_callback(
             encoder, subghz_file_encoder_worker_get_level_duration, instance->file_worker_encoder);
             encoder, subghz_file_encoder_worker_get_level_duration, instance->file_worker_encoder);
+        //forced stop of transmission
         subghz_protocol_encoder_common_set_callback_end(
         subghz_protocol_encoder_common_set_callback_end(
             encoder, subghz_protocol_raw_file_encoder_worker_stop, instance);
             encoder, subghz_protocol_raw_file_encoder_worker_stop, instance);
+        //file transfer complete callback
+        subghz_file_encoder_worker_callback_end(
+            instance->file_worker_encoder,
+            subghz_protocol_raw_file_encoder_worker_callback_end,
+            instance);
 
 
         loaded = true;
         loaded = true;
     } else {
     } else {
@@ -187,7 +212,8 @@ bool subghz_protocol_raw_save_to_file_init(
             break;
             break;
         }
         }
 
 
-        if(!flipper_file_write_string_cstr(instance->flipper_file, "Protocol", instance->common.name)) {
+        if(!flipper_file_write_string_cstr(
+               instance->flipper_file, "Protocol", instance->common.name)) {
             FURI_LOG_E(TAG, "Unable to add Protocol");
             FURI_LOG_E(TAG, "Unable to add Protocol");
             break;
             break;
         }
         }

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

@@ -2,6 +2,8 @@
 
 
 #include "subghz_protocol_common.h"
 #include "subghz_protocol_common.h"
 
 
+typedef void (*SubGhzProtocolRAWCallbackEnd)(void* context);
+
 typedef struct SubGhzProtocolRAW SubGhzProtocolRAW;
 typedef struct SubGhzProtocolRAW SubGhzProtocolRAW;
 
 
 /** Allocate SubGhzProtocolRAW
 /** Allocate SubGhzProtocolRAW
@@ -16,6 +18,11 @@ SubGhzProtocolRAW* subghz_protocol_raw_alloc();
  */
  */
 void subghz_protocol_raw_free(SubGhzProtocolRAW* instance);
 void subghz_protocol_raw_free(SubGhzProtocolRAW* instance);
 
 
+void subghz_protocol_raw_file_encoder_worker_set_callback_end(
+    SubGhzProtocolRAW* instance,
+    SubGhzProtocolRAWCallbackEnd callback_end,
+    void* context_end);
+
 /** Reset internal state
 /** Reset internal state
  * @param instance - SubGhzProtocolRAW instance
  * @param instance - SubGhzProtocolRAW instance
  */
  */

+ 22 - 3
lib/subghz/subghz_file_encoder_worker.c

@@ -16,12 +16,26 @@ struct SubGhzFileEncoderWorker {
     FlipperFile* flipper_file;
     FlipperFile* flipper_file;
 
 
     volatile bool worker_running;
     volatile bool worker_running;
+    volatile bool worker_stoping;
     bool level;
     bool level;
     int32_t duration;
     int32_t duration;
     string_t str_data;
     string_t str_data;
     string_t file_path;
     string_t file_path;
+
+    SubGhzFileEncoderWorkerCallbackEnd callback_end;
+    void* context_end;
 };
 };
 
 
+void subghz_file_encoder_worker_callback_end(
+    SubGhzFileEncoderWorker* instance,
+    SubGhzFileEncoderWorkerCallbackEnd callback_end,
+    void* context_end) {
+    furi_assert(instance);
+    furi_assert(callback_end);
+    instance->callback_end = callback_end;
+    instance->context_end = context_end;
+}
+
 void subghz_file_encoder_worker_add_livel_duration(
 void subghz_file_encoder_worker_add_livel_duration(
     SubGhzFileEncoderWorker* instance,
     SubGhzFileEncoderWorker* instance,
     int32_t duration) {
     int32_t duration) {
@@ -89,6 +103,7 @@ LevelDuration subghz_file_encoder_worker_get_level_duration(void* context) {
         } else if(duration == 0) {
         } else if(duration == 0) {
             level_duration = level_duration_reset();
             level_duration = level_duration_reset();
             FURI_LOG_I(TAG, "Stop transmission");
             FURI_LOG_I(TAG, "Stop transmission");
+            instance->worker_stoping = true;
         }
         }
         return level_duration;
         return level_duration;
     } else {
     } else {
@@ -111,9 +126,7 @@ static int32_t subghz_file_encoder_worker_thread(void* context) {
         if(!flipper_file_open_existing(
         if(!flipper_file_open_existing(
                instance->flipper_file, string_get_cstr(instance->file_path))) {
                instance->flipper_file, string_get_cstr(instance->file_path))) {
             FURI_LOG_E(
             FURI_LOG_E(
-                TAG,
-                "Unable to open file for read: %s",
-                string_get_cstr(instance->file_path));
+                TAG, "Unable to open file for read: %s", string_get_cstr(instance->file_path));
             break;
             break;
         }
         }
         if(!flipper_file_read_string(instance->flipper_file, "Protocol", instance->str_data)) {
         if(!flipper_file_read_string(instance->flipper_file, "Protocol", instance->str_data)) {
@@ -124,6 +137,7 @@ static int32_t subghz_file_encoder_worker_thread(void* context) {
         //skip the end of the previous line "\n"
         //skip the end of the previous line "\n"
         storage_file_seek(file, 1, false);
         storage_file_seek(file, 1, false);
         res = true;
         res = true;
+        instance->worker_stoping = false;
         FURI_LOG_I(TAG, "Start transmission");
         FURI_LOG_I(TAG, "Start transmission");
     } while(0);
     } while(0);
 
 
@@ -152,7 +166,11 @@ static int32_t subghz_file_encoder_worker_thread(void* context) {
     }
     }
     //waiting for the end of the transfer
     //waiting for the end of the transfer
     FURI_LOG_I(TAG, "End read file");
     FURI_LOG_I(TAG, "End read file");
+
     while(instance->worker_running) {
     while(instance->worker_running) {
+        if(instance->worker_stoping) {
+            if(instance->callback_end) instance->callback_end(instance->context_end);
+        }
         osDelay(50);
         osDelay(50);
     }
     }
     flipper_file_close(instance->flipper_file);
     flipper_file_close(instance->flipper_file);
@@ -177,6 +195,7 @@ SubGhzFileEncoderWorker* subghz_file_encoder_worker_alloc() {
     string_init(instance->str_data);
     string_init(instance->str_data);
     string_init(instance->file_path);
     string_init(instance->file_path);
     instance->level = false;
     instance->level = false;
+    instance->worker_stoping = true;
 
 
     return instance;
     return instance;
 }
 }

+ 12 - 0
lib/subghz/subghz_file_encoder_worker.h

@@ -2,8 +2,20 @@
 
 
 #include <furi-hal.h>
 #include <furi-hal.h>
 
 
+typedef void (*SubGhzFileEncoderWorkerCallbackEnd)(void* context);
+
 typedef struct SubGhzFileEncoderWorker SubGhzFileEncoderWorker;
 typedef struct SubGhzFileEncoderWorker SubGhzFileEncoderWorker;
 
 
+/** End callback SubGhzWorker
+ * 
+ * @param instance SubGhzFileEncoderWorker instance
+ * @param callback SubGhzFileEncoderWorkerCallbackEnd callback
+ */
+void subghz_file_encoder_worker_callback_end(
+    SubGhzFileEncoderWorker* instance,
+    SubGhzFileEncoderWorkerCallbackEnd callback_end,
+    void* context_end);
+
 /** Allocate SubGhzFileEncoderWorker
 /** Allocate SubGhzFileEncoderWorker
  * 
  * 
  * @return SubGhzFileEncoderWorker* 
  * @return SubGhzFileEncoderWorker*