Explorar o código

SubGhz: support for custom frequencies for SubGhz (#1108)

* SubGhz: add load setting
* SubGhz: add support file upload with custom frequencies
* SubGhz: add load region setting
* SubGhz: fix syntax
* SubGhz: fix furi_halt error
* Desktop: hide dolphin controls in production build
* Notification: fix crash on NotificationMessageTypeLedDisplayUnlock message

Co-authored-by: あく <alleteam@gmail.com>
Skorpionm %!s(int64=3) %!d(string=hai) anos
pai
achega
a5cc3453c8

+ 2 - 8
applications/desktop/views/desktop_view_debug.c

@@ -114,14 +114,8 @@ bool desktop_debug_input(InputEvent* event, void* context) {
     DesktopViewStatsScreens current = 0;
     with_view_model(
         debug_view->view, (DesktopDebugViewModel * model) {
-#if SRV_DOLPHIN_STATE_DEBUG == 1
-            if(event->key == InputKeyDown) {
-                model->screen = (model->screen + 1) % DesktopViewStatsTotalCount;
-            } else if(event->key == InputKeyUp) {
-                model->screen = ((model->screen - 1) + DesktopViewStatsTotalCount) %
-                                DesktopViewStatsTotalCount;
-            }
-#else
+
+#ifdef SRV_DOLPHIN_STATE_DEBUG
             if((event->key == InputKeyDown) || (event->key == InputKeyUp)) {
                 model->screen = !model->screen;
             }

+ 6 - 7
applications/notification/notification_app.c

@@ -164,7 +164,6 @@ void notification_process_notification_message(
     notification_message = (*message->sequence)[notification_message_index];
 
     bool led_active = false;
-    uint8_t display_led_lock = 0;
     uint8_t led_values[NOTIFICATION_LED_COUNT] = {0x00, 0x00, 0x00};
     bool reset_notifications = true;
     float speaker_volume_setting = app->settings.speaker_volume;
@@ -192,18 +191,18 @@ void notification_process_notification_message(
             reset_mask |= reset_display_mask;
             break;
         case NotificationMessageTypeLedDisplayLock:
-            furi_assert(display_led_lock < UINT8_MAX);
-            display_led_lock++;
-            if(display_led_lock == 1) {
+            furi_assert(app->display_led_lock < UINT8_MAX);
+            app->display_led_lock++;
+            if(app->display_led_lock == 1) {
                 notification_apply_internal_led_layer(
                     &app->display,
                     notification_message->data.led.value * display_brightness_setting);
             }
             break;
         case NotificationMessageTypeLedDisplayUnlock:
-            furi_assert(display_led_lock > 0);
-            display_led_lock--;
-            if(display_led_lock == 0) {
+            furi_assert(app->display_led_lock > 0);
+            app->display_led_lock--;
+            if(app->display_led_lock == 0) {
                 notification_apply_internal_led_layer(
                     &app->display,
                     notification_message->data.led.value * display_brightness_setting);

+ 1 - 0
applications/notification/notification_app.h

@@ -49,6 +49,7 @@ struct NotificationApp {
 
     NotificationLedLayer display;
     NotificationLedLayer led[NOTIFICATION_LED_COUNT];
+    uint8_t display_led_lock;
 
     NotificationSettings settings;
 };

+ 9 - 4
applications/subghz/helpers/subghz_frequency_analyzer_worker.c

@@ -29,6 +29,7 @@ struct SubGhzFrequencyAnalyzerWorker {
     volatile bool worker_running;
     uint8_t count_repet;
     FrequencyRSSI frequency_rssi_buf;
+    SubGhzSetting* setting;
 
     float filVal;
 
@@ -77,10 +78,12 @@ static int32_t subghz_frequency_analyzer_worker_thread(void* context) {
         frequency_rssi.rssi = -127.0f;
         furi_hal_subghz_idle();
         furi_hal_subghz_load_registers(subghz_preset_ook_650khz);
-        for(size_t i = 0; i < subghz_frequencies_count; i++) {
-            if(furi_hal_subghz_is_frequency_valid(subghz_frequencies[i])) {
+        for(size_t i = 0; i < subghz_setting_get_frequency_count(instance->setting); i++) {
+            if(furi_hal_subghz_is_frequency_valid(
+                   subghz_setting_get_frequency(instance->setting, i))) {
                 furi_hal_subghz_idle();
-                frequency = furi_hal_subghz_set_frequency(subghz_frequencies[i]);
+                frequency = furi_hal_subghz_set_frequency(
+                    subghz_setting_get_frequency(instance->setting, i));
                 furi_hal_subghz_rx();
                 osDelay(3);
                 rssi = furi_hal_subghz_get_rssi();
@@ -150,6 +153,8 @@ SubGhzFrequencyAnalyzerWorker* subghz_frequency_analyzer_worker_alloc() {
     furi_thread_set_context(instance->thread, instance);
     furi_thread_set_callback(instance->thread, subghz_frequency_analyzer_worker_thread);
 
+    instance->setting = subghz_setting_alloc();
+    subghz_setting_load(instance->setting, "/ext/subghz/assets/setting_frequency_analyzer_user");
     return instance;
 }
 
@@ -157,7 +162,7 @@ void subghz_frequency_analyzer_worker_free(SubGhzFrequencyAnalyzerWorker* instan
     furi_assert(instance);
 
     furi_thread_free(instance->thread);
-
+    subghz_setting_free(instance->setting);
     free(instance);
 }
 

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

@@ -127,7 +127,8 @@ bool subghz_scene_read_raw_on_event(void* context, SceneManagerEvent event) {
                 scene_manager_next_scene(subghz->scene_manager, SubGhzSceneNeedSaving);
             } else {
                 //Restore default setting
-                subghz->txrx->frequency = subghz_frequencies[subghz_frequencies_433_92];
+                subghz->txrx->frequency = subghz_setting_get_frequency(
+                    subghz->setting, subghz_setting_get_frequency_default_index(subghz->setting));
                 subghz->txrx->preset = FuriHalSubGhzPresetOok650Async;
                 if(!scene_manager_search_and_switch_to_previous_scene(
                        subghz->scene_manager, SubGhzSceneSaved)) {

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

@@ -120,7 +120,8 @@ bool subghz_scene_receiver_on_event(void* context, SceneManagerEvent event) {
                 subghz_sleep(subghz);
             };
             subghz->txrx->hopper_state = SubGhzHopperStateOFF;
-            subghz->txrx->frequency = subghz_frequencies[subghz_frequencies_433_92];
+            subghz->txrx->frequency = subghz_setting_get_frequency(
+                subghz->setting, subghz_setting_get_frequency_default_index(subghz->setting));
             subghz->txrx->preset = FuriHalSubGhzPresetOok650Async;
             subghz->txrx->idx_menu_chosen = 0;
             subghz_receiver_set_rx_callback(subghz->txrx->receiver, NULL, subghz);

+ 50 - 11
applications/subghz/scenes/subghz_scene_receiver_config.c

@@ -40,6 +40,21 @@ uint8_t subghz_scene_receiver_config_uint32_value_index(
     return index;
 }
 
+uint8_t subghz_scene_receiver_config_next_frequency(const uint32_t value, void* context) {
+    furi_assert(context);
+    SubGhz* subghz = context;
+    int64_t last_value = INT64_MIN;
+    uint8_t index = 0;
+    for(uint8_t i = 0; i < subghz_setting_get_frequency_count(subghz->setting); i++) {
+        if((value >= last_value) && (value <= subghz_setting_get_frequency(subghz->setting, i))) {
+            index = i;
+            break;
+        }
+        last_value = subghz_setting_get_frequency(subghz->setting, i);
+    }
+    return index;
+}
+
 uint8_t subghz_scene_receiver_config_hopper_value_index(
     const uint32_t value,
     const uint32_t values[],
@@ -64,10 +79,17 @@ static void subghz_scene_receiver_config_set_frequency(VariableItem* item) {
     uint8_t index = variable_item_get_current_value_index(item);
 
     if(subghz->txrx->hopper_state == SubGhzHopperStateOFF) {
-        variable_item_set_current_value_text(item, subghz_frequencies_text[index]);
-        subghz->txrx->frequency = subghz_frequencies[index];
+        char text_buf[10] = {0};
+        sprintf(
+            text_buf,
+            "%lu.%02lu",
+            subghz_setting_get_frequency(subghz->setting, index) / 1000000,
+            (subghz_setting_get_frequency(subghz->setting, index) % 1000000) / 10000);
+        variable_item_set_current_value_text(item, text_buf);
+        subghz->txrx->frequency = subghz_setting_get_frequency(subghz->setting, index);
     } else {
-        variable_item_set_current_value_index(item, subghz_frequencies_433_92);
+        variable_item_set_current_value_index(
+            item, subghz_setting_get_frequency_default_index(subghz->setting));
     }
 }
 
@@ -85,15 +107,27 @@ static void subghz_scene_receiver_config_set_hopping_runing(VariableItem* item)
 
     variable_item_set_current_value_text(item, hopping_text[index]);
     if(hopping_value[index] == SubGhzHopperStateOFF) {
+        char text_buf[10] = {0};
+        sprintf(
+            text_buf,
+            "%lu.%02lu",
+            subghz_setting_get_frequency(
+                subghz->setting, subghz_setting_get_frequency_default_index(subghz->setting)) /
+                1000000,
+            (subghz_setting_get_frequency(
+                 subghz->setting, subghz_setting_get_frequency_default_index(subghz->setting)) %
+             1000000) /
+                10000);
         variable_item_set_current_value_text(
             (VariableItem*)scene_manager_get_scene_state(
                 subghz->scene_manager, SubGhzSceneReceiverConfig),
-            subghz_frequencies_text[subghz_frequencies_433_92]);
-        subghz->txrx->frequency = subghz_frequencies[subghz_frequencies_433_92];
+            text_buf);
+        subghz->txrx->frequency = subghz_setting_get_frequency(
+            subghz->setting, subghz_setting_get_frequency_default_index(subghz->setting));
         variable_item_set_current_value_index(
             (VariableItem*)scene_manager_get_scene_state(
                 subghz->scene_manager, SubGhzSceneReceiverConfig),
-            subghz_frequencies_433_92);
+            subghz_setting_get_frequency_default_index(subghz->setting));
     } else {
         variable_item_set_current_value_text(
             (VariableItem*)scene_manager_get_scene_state(
@@ -102,7 +136,7 @@ static void subghz_scene_receiver_config_set_hopping_runing(VariableItem* item)
         variable_item_set_current_value_index(
             (VariableItem*)scene_manager_get_scene_state(
                 subghz->scene_manager, SubGhzSceneReceiverConfig),
-            subghz_frequencies_433_92);
+            subghz_setting_get_frequency_default_index(subghz->setting));
     }
 
     subghz->txrx->hopper_state = hopping_value[index];
@@ -116,15 +150,20 @@ void subghz_scene_receiver_config_on_enter(void* context) {
     item = variable_item_list_add(
         subghz->variable_item_list,
         "Frequency:",
-        subghz_frequencies_count,
+        subghz_setting_get_frequency_count(subghz->setting),
         subghz_scene_receiver_config_set_frequency,
         subghz);
-    value_index = subghz_scene_receiver_config_uint32_value_index(
-        subghz->txrx->frequency, subghz_frequencies, subghz_frequencies_count);
+    value_index = subghz_scene_receiver_config_next_frequency(subghz->txrx->frequency, subghz);
     scene_manager_set_scene_state(
         subghz->scene_manager, SubGhzSceneReceiverConfig, (uint32_t)item);
     variable_item_set_current_value_index(item, value_index);
-    variable_item_set_current_value_text(item, subghz_frequencies_text[value_index]);
+    char text_buf[10] = {0};
+    sprintf(
+        text_buf,
+        "%lu.%02lu",
+        subghz_setting_get_frequency(subghz->setting, value_index) / 1000000,
+        (subghz_setting_get_frequency(subghz->setting, value_index) % 1000000) / 10000);
+    variable_item_set_current_value_text(item, text_buf);
 
     if(scene_manager_get_scene_state(subghz->scene_manager, SubGhzSceneReadRAW) !=
        SubGhzCustomEventManagerSet) {

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

@@ -46,7 +46,7 @@ bool subghz_scene_set_type_submenu_gen_data_protocol(
         if(!subghz_protocol_decoder_base_serialize(
                subghz->txrx->decoder_result,
                subghz->txrx->fff_data,
-               subghz_frequencies[subghz_frequencies_433_92],
+               subghz_setting_get_frequency_default_index(subghz->setting),
                FuriHalSubGhzPresetOok650Async)) {
             FURI_LOG_E(TAG, "Unable to serialize");
             break;
@@ -213,7 +213,7 @@ bool subghz_scene_set_type_on_event(void* context, SceneManagerEvent event) {
                     0x2,
                     0x0003,
                     "DoorHan",
-                    subghz_frequencies[subghz_frequencies_433_92],
+                    subghz_setting_get_frequency_default_index(subghz->setting),
                     FuriHalSubGhzPresetOok650Async);
                 generated_protocol = true;
             } else {
@@ -237,7 +237,7 @@ bool subghz_scene_set_type_on_event(void* context, SceneManagerEvent event) {
                     0x2,
                     0x0003,
                     "DoorHan",
-                    subghz_frequencies[subghz_frequencies_315_00],
+                    315000000,
                     FuriHalSubGhzPresetOok650Async);
                 generated_protocol = true;
             } else {

+ 9 - 63
applications/subghz/subghz.c

@@ -3,68 +3,6 @@
 #include "subghz_i.h"
 #include <lib/toolbox/path.h>
 
-const char* const subghz_frequencies_text[] = {
-
-    "300.00",
-    "303.88",
-    "304.25",
-    "315.00",
-    "318.00",
-
-    "390.00",
-    "418.00",
-    "433.08",
-    "433.42",
-    "433.92",
-    "434.42",
-    "434.78",
-    "438.90",
-
-    "868.35",
-    "915.00",
-    "925.00",
-};
-
-const uint32_t subghz_frequencies[] = {
-
-    /* 300 - 348 */
-    300000000,
-    303875000,
-    304250000,
-    315000000,
-    318000000,
-
-    /* 387 - 464 */
-    390000000,
-    418000000,
-    433075000, /* LPD433 first */
-    433420000,
-    433920000, /* LPD433 mid */
-    434420000,
-    434775000, /* LPD433 last channels */
-    438900000,
-
-    /* 779 - 928 */
-    868350000,
-    915000000,
-    925000000,
-
-};
-
-const uint32_t subghz_hopper_frequencies[] = {
-    315000000,
-    318000000,
-    390000000,
-    433920000,
-    868350000,
-};
-
-const uint32_t subghz_frequencies_count = sizeof(subghz_frequencies) / sizeof(uint32_t);
-const uint32_t subghz_hopper_frequencies_count =
-    sizeof(subghz_hopper_frequencies) / sizeof(uint32_t);
-const uint32_t subghz_frequencies_433_92 = 9;
-const uint32_t subghz_frequencies_315_00 = 3;
-
 bool subghz_custom_event_callback(void* context, uint32_t event) {
     furi_assert(context);
     SubGhz* subghz = context;
@@ -186,9 +124,14 @@ SubGhz* subghz_alloc() {
         SubGhzViewIdStatic,
         subghz_test_static_get_view(subghz->subghz_test_static));
 
+    //init setting
+    subghz->setting = subghz_setting_alloc();
+    subghz_setting_load(subghz->setting, "/ext/subghz/assets/setting_user");
+
     //init Worker & Protocol & History
     subghz->txrx = malloc(sizeof(SubGhzTxRx));
-    subghz->txrx->frequency = subghz_frequencies[subghz_frequencies_433_92];
+    subghz->txrx->frequency = subghz_setting_get_frequency(
+        subghz->setting, subghz_setting_get_frequency_default_index(subghz->setting));
     subghz->txrx->preset = FuriHalSubGhzPresetOok650Async;
     subghz->txrx->txrx_state = SubGhzTxRxStateSleep;
     subghz->txrx->hopper_state = SubGhzHopperStateOFF;
@@ -281,6 +224,9 @@ void subghz_free(SubGhz* subghz) {
     furi_record_close("gui");
     subghz->gui = NULL;
 
+    //setting
+    subghz_setting_free(subghz->setting);
+
     //Worker & Protocol & History
     subghz_receiver_free(subghz->txrx->receiver);
     subghz_environment_free(subghz->txrx->environment);

+ 4 - 3
applications/subghz/subghz_i.c

@@ -514,9 +514,9 @@ void subghz_hopper_update(SubGhz* subghz) {
     } else {
         subghz->txrx->hopper_state = SubGhzHopperStateRunnig;
     }
-
     // Select next frequency
-    if(subghz->txrx->hopper_idx_frequency < subghz_hopper_frequencies_count - 1) {
+    if(subghz->txrx->hopper_idx_frequency <
+       subghz_setting_get_hopper_frequency_count(subghz->setting) - 1) {
         subghz->txrx->hopper_idx_frequency++;
     } else {
         subghz->txrx->hopper_idx_frequency = 0;
@@ -527,7 +527,8 @@ void subghz_hopper_update(SubGhz* subghz) {
     };
     if(subghz->txrx->txrx_state == SubGhzTxRxStateIDLE) {
         subghz_receiver_reset(subghz->txrx->receiver);
-        subghz->txrx->frequency = subghz_hopper_frequencies[subghz->txrx->hopper_idx_frequency];
+        subghz->txrx->frequency = subghz_setting_get_hopper_frequency(
+            subghz->setting, subghz->txrx->hopper_idx_frequency);
         subghz_rx(subghz, subghz->txrx->frequency);
     }
 }

+ 2 - 8
applications/subghz/subghz_i.h

@@ -30,19 +30,12 @@
 #include <lib/subghz/transmitter.h>
 
 #include "subghz_history.h"
+#include "subghz_setting.h"
 
 #include <gui/modules/variable_item_list.h>
 
 #define SUBGHZ_MAX_LEN_NAME 40
 
-extern const char* const subghz_frequencies_text[];
-extern const uint32_t subghz_frequencies[];
-extern const uint32_t subghz_hopper_frequencies[];
-extern const uint32_t subghz_frequencies_count;
-extern const uint32_t subghz_hopper_frequencies_count;
-extern const uint32_t subghz_frequencies_433_92;
-extern const uint32_t subghz_frequencies_315_00;
-
 /** SubGhzNotification state */
 typedef enum {
     SubGhzNotificationStateStarting,
@@ -137,6 +130,7 @@ struct SubGhz {
     SubGhzTestCarrier* subghz_test_carrier;
     SubGhzTestPacket* subghz_test_packet;
     string_t error_str;
+    SubGhzSetting* setting;
 };
 
 typedef enum {

+ 361 - 0
applications/subghz/subghz_setting.c

@@ -0,0 +1,361 @@
+#include "subghz_setting.h"
+#include "subghz_i.h"
+
+#include <furi.h>
+#include <m-list.h>
+#include <lib/flipper_format/flipper_format.h>
+
+#define TAG "SubGhzSetting"
+
+#define SUBGHZ_SETTING_FILE_VERSION 1
+#define SUBGHZ_SETTING_FILE_TYPE "Flipper SubGhz Setting File"
+
+typedef enum {
+    SubGhzSettingStateNoLoad = 0,
+    SubGhzSettingStateLoadFrequencyDefault,
+    SubGhzSettingStateOkLoad,
+} SubGhzSettingState;
+
+static const uint32_t subghz_frequencies[] = {
+    /* 300 - 348 */
+    300000000,
+    303875000,
+    304250000,
+    315000000,
+    318000000,
+
+    /* 387 - 464 */
+    390000000,
+    418000000,
+    433075000, /* LPD433 first */
+    433420000,
+    433920000, /* LPD433 mid */
+    434420000,
+    434775000, /* LPD433 last channels */
+    438900000,
+
+    /* 779 - 928 */
+    868350000,
+    915000000,
+    925000000,
+    0,
+};
+static const uint32_t subghz_hopper_frequencies[] = {
+    315000000,
+    318000000,
+    390000000,
+    433920000,
+    868350000,
+    0,
+};
+static const uint32_t subghz_frequency_default_index = 9;
+
+static const uint32_t subghz_frequencies_region_eu_ru[] = {
+    /* 300 - 348 */
+    300000000,
+    303875000,
+    304250000,
+    315000000,
+    318000000,
+
+    /* 387 - 464 */
+    390000000,
+    418000000,
+    433075000, /* LPD433 first */
+    433420000,
+    433920000, /* LPD433 mid */
+    434420000,
+    434775000, /* LPD433 last channels */
+    438900000,
+
+    /* 779 - 928 */
+    868350000,
+    915000000,
+    925000000,
+    0,
+};
+static const uint32_t subghz_hopper_frequencies_region_eu_ru[] = {
+    315000000,
+    318000000,
+    390000000,
+    433920000,
+    868350000,
+    0,
+};
+static const uint32_t subghz_frequency_default_index_region_eu_ru = 9;
+
+static const uint32_t subghz_frequencies_region_us_ca_au[] = {
+    /* 300 - 348 */
+    300000000,
+    303875000,
+    304250000,
+    315000000,
+    318000000,
+
+    /* 387 - 464 */
+    390000000,
+    418000000,
+    433075000, /* LPD433 first */
+    433420000,
+    433920000, /* LPD433 mid */
+    434420000,
+    434775000, /* LPD433 last channels */
+    438900000,
+
+    /* 779 - 928 */
+    868350000,
+    915000000,
+    925000000,
+    0,
+};
+static const uint32_t subghz_hopper_frequencies_region_us_ca_au[] = {
+    315000000,
+    318000000,
+    390000000,
+    433920000,
+    868350000,
+    0,
+};
+static const uint32_t subghz_frequency_default_index_region_us_ca_au = 9;
+
+static const uint32_t subghz_frequencies_region_jp[] = {
+    /* 300 - 348 */
+    300000000,
+    303875000,
+    304250000,
+    315000000,
+    318000000,
+
+    /* 387 - 464 */
+    390000000,
+    418000000,
+    433075000, /* LPD433 first */
+    433420000,
+    433920000, /* LPD433 mid */
+    434420000,
+    434775000, /* LPD433 last channels */
+    438900000,
+
+    /* 779 - 928 */
+    868350000,
+    915000000,
+    925000000,
+    0,
+};
+static const uint32_t subghz_hopper_frequencies_region_jp[] = {
+    315000000,
+    318000000,
+    390000000,
+    433920000,
+    868350000,
+    0,
+};
+static const uint32_t subghz_frequency_default_index_region_jp = 9;
+
+LIST_DEF(frequencies_list, uint32_t)
+LIST_DEF(hopper_frequencies_list, uint32_t)
+
+struct SubGhzSetting {
+    frequencies_list_t frequencies;
+    hopper_frequencies_list_t hopper_frequencies;
+    size_t frequencies_count;
+    size_t hopper_frequencies_count;
+    uint32_t frequency_default_index;
+};
+
+SubGhzSetting* subghz_setting_alloc(void) {
+    SubGhzSetting* instance = malloc(sizeof(SubGhzSetting));
+    frequencies_list_init(instance->frequencies);
+    hopper_frequencies_list_init(instance->hopper_frequencies);
+    return instance;
+}
+
+void subghz_setting_free(SubGhzSetting* instance) {
+    furi_assert(instance);
+    frequencies_list_clear(instance->frequencies);
+    hopper_frequencies_list_clear(instance->hopper_frequencies);
+    free(instance);
+}
+
+void subghz_setting_load_default(
+    SubGhzSetting* instance,
+    const uint32_t frequencies[],
+    const uint32_t hopper_frequencies[],
+    const uint32_t frequency_default_index) {
+    furi_assert(instance);
+    size_t i = 0;
+    frequencies_list_clear(instance->frequencies);
+    hopper_frequencies_list_clear(instance->hopper_frequencies);
+    i = 0;
+    while(frequencies[i]) {
+        frequencies_list_push_back(instance->frequencies, frequencies[i]);
+        i++;
+    }
+    instance->frequencies_count = i;
+
+    i = 0;
+    while(hopper_frequencies[i]) {
+        hopper_frequencies_list_push_back(instance->hopper_frequencies, hopper_frequencies[i]);
+        i++;
+    }
+    instance->hopper_frequencies_count = i;
+
+    instance->frequency_default_index = frequency_default_index;
+}
+
+void subghz_setting_load(SubGhzSetting* instance, const char* file_path) {
+    furi_assert(instance);
+
+    frequencies_list_clear(instance->frequencies);
+    hopper_frequencies_list_clear(instance->hopper_frequencies);
+
+    Storage* storage = furi_record_open("storage");
+    FlipperFormat* fff_data_file = flipper_format_file_alloc(storage);
+
+    string_t temp_str;
+    string_init(temp_str);
+    uint32_t temp_data32;
+    SubGhzSettingState loading = SubGhzSettingStateNoLoad;
+    uint16_t i = 0;
+
+    if(file_path) {
+        do {
+            if(!flipper_format_file_open_existing(fff_data_file, file_path)) {
+                FURI_LOG_E(TAG, "Error open file %s", file_path);
+                break;
+            }
+
+            if(!flipper_format_read_header(fff_data_file, temp_str, &temp_data32)) {
+                FURI_LOG_E(TAG, "Missing or incorrect header");
+                break;
+            }
+
+            if((!strcmp(string_get_cstr(temp_str), SUBGHZ_SETTING_FILE_TYPE)) &&
+               temp_data32 == SUBGHZ_SETTING_FILE_VERSION) {
+            } else {
+                FURI_LOG_E(TAG, "Type or version mismatch");
+                break;
+            }
+
+            if(!flipper_format_rewind(fff_data_file)) {
+                FURI_LOG_E(TAG, "Rewind error");
+                break;
+            }
+            i = 0;
+            while(flipper_format_read_uint32(
+                fff_data_file, "Frequency", (uint32_t*)&temp_data32, 1)) {
+                if(furi_hal_subghz_is_frequency_valid(temp_data32)) {
+                    FURI_LOG_I(TAG, "Frequency loaded %lu", temp_data32);
+                    frequencies_list_push_back(instance->frequencies, temp_data32);
+                    i++;
+                } else {
+                    FURI_LOG_E(TAG, "Frequency not supported %lu", temp_data32);
+                }
+            }
+            instance->frequencies_count = i;
+
+            if(!flipper_format_rewind(fff_data_file)) {
+                FURI_LOG_E(TAG, "Rewind error");
+                break;
+            }
+            i = 0;
+            while(flipper_format_read_uint32(
+                fff_data_file, "Hopper_frequency", (uint32_t*)&temp_data32, 1)) {
+                if(furi_hal_subghz_is_frequency_valid(temp_data32)) {
+                    FURI_LOG_I(TAG, "Hopper frequency loaded %lu", temp_data32);
+                    hopper_frequencies_list_push_back(instance->hopper_frequencies, temp_data32);
+                    i++;
+                } else {
+                    FURI_LOG_E(TAG, "Hopper frequency not supported %lu", temp_data32);
+                }
+            }
+            instance->hopper_frequencies_count = i;
+
+            if(!flipper_format_rewind(fff_data_file)) {
+                FURI_LOG_E(TAG, "Rewind error");
+                break;
+            }
+            if(!flipper_format_read_uint32(
+                   fff_data_file, "Frequency_default", (uint32_t*)&temp_data32, 1)) {
+                FURI_LOG_E(TAG, "Frequency default missing");
+                break;
+            }
+
+            for(i = 0; i < instance->frequencies_count; i++) {
+                if(subghz_setting_get_frequency(instance, i) == temp_data32) {
+                    instance->frequency_default_index = i;
+                    FURI_LOG_I(TAG, "Frequency default index %lu", i);
+                    loading = SubGhzSettingStateLoadFrequencyDefault;
+                    break;
+                }
+            }
+
+            if(loading == SubGhzSettingStateLoadFrequencyDefault) {
+                loading = SubGhzSettingStateOkLoad;
+            } else {
+                FURI_LOG_E(TAG, "Frequency default index missing");
+            }
+        } while(false);
+    }
+    flipper_format_free(fff_data_file);
+    furi_record_close("storage");
+
+    if(loading != SubGhzSettingStateOkLoad) {
+        switch(furi_hal_version_get_hw_region()) {
+        case FuriHalVersionRegionEuRu:
+            subghz_setting_load_default(
+                instance,
+                subghz_frequencies_region_eu_ru,
+                subghz_hopper_frequencies_region_eu_ru,
+                subghz_frequency_default_index_region_eu_ru);
+            break;
+        case FuriHalVersionRegionUsCaAu:
+            subghz_setting_load_default(
+                instance,
+                subghz_frequencies_region_us_ca_au,
+                subghz_hopper_frequencies_region_us_ca_au,
+                subghz_frequency_default_index_region_us_ca_au);
+            break;
+        case FuriHalVersionRegionJp:
+            subghz_setting_load_default(
+                instance,
+                subghz_frequencies_region_jp,
+                subghz_hopper_frequencies_region_jp,
+                subghz_frequency_default_index_region_jp);
+            break;
+
+        default:
+            subghz_setting_load_default(
+                instance,
+                subghz_frequencies,
+                subghz_hopper_frequencies,
+                subghz_frequency_default_index);
+            break;
+        }
+    }
+}
+
+size_t subghz_setting_get_frequency_count(SubGhzSetting* instance) {
+    furi_assert(instance);
+    return instance->frequencies_count;
+}
+
+size_t subghz_setting_get_hopper_frequency_count(SubGhzSetting* instance) {
+    furi_assert(instance);
+    return instance->hopper_frequencies_count;
+}
+
+uint32_t subghz_setting_get_frequency(SubGhzSetting* instance, size_t idx) {
+    furi_assert(instance);
+    return *frequencies_list_get(instance->frequencies, idx);
+}
+
+uint32_t subghz_setting_get_hopper_frequency(SubGhzSetting* instance, size_t idx) {
+    furi_assert(instance);
+    return *hopper_frequencies_list_get(instance->hopper_frequencies, idx);
+}
+
+uint32_t subghz_setting_get_frequency_default_index(SubGhzSetting* instance) {
+    furi_assert(instance);
+    return instance->frequency_default_index;
+}

+ 17 - 0
applications/subghz/subghz_setting.h

@@ -0,0 +1,17 @@
+
+#pragma once
+
+#include <math.h>
+#include <furi.h>
+#include <furi_hal.h>
+
+typedef struct SubGhzSetting SubGhzSetting;
+
+SubGhzSetting* subghz_setting_alloc(void);
+void subghz_setting_free(SubGhzSetting* instance);
+void subghz_setting_load(SubGhzSetting* instance, const char* file_path);
+size_t subghz_setting_get_frequency_count(SubGhzSetting* instance);
+size_t subghz_setting_get_hopper_frequency_count(SubGhzSetting* instance);
+uint32_t subghz_setting_get_frequency(SubGhzSetting* instance, size_t idx);
+uint32_t subghz_setting_get_hopper_frequency(SubGhzSetting* instance, size_t idx);
+uint32_t subghz_setting_get_frequency_default_index(SubGhzSetting* instance);

+ 19 - 0
assets/resources/subghz/assets/setting_frequency_analyzer_user

@@ -0,0 +1,19 @@
+Filetype: Flipper SubGhz Setting File
+Version: 1
+Frequency_default: 433920000
+Frequency: 300000000
+Frequency: 303875000
+Frequency: 304250000
+Frequency: 315000000
+Frequency: 318000000
+Frequency: 390000000
+Frequency: 418000000
+Frequency: 433075000
+Frequency: 433420000
+Frequency: 433920000
+Frequency: 434420000
+Frequency: 434775000
+Frequency: 438900000
+Frequency: 868350000
+Frequency: 915000000
+Frequency: 925000000

+ 24 - 0
assets/resources/subghz/assets/setting_user

@@ -0,0 +1,24 @@
+Filetype: Flipper SubGhz Setting File
+Version: 1
+Frequency_default: 433920000
+Frequency: 300000000
+Frequency: 303875000
+Frequency: 304250000
+Frequency: 315000000
+Frequency: 318000000
+Frequency: 390000000
+Frequency: 418000000
+Frequency: 433075000
+Frequency: 433420000
+Frequency: 433920000
+Frequency: 434420000
+Frequency: 434775000
+Frequency: 438900000
+Frequency: 868350000
+Frequency: 915000000
+Frequency: 925000000
+Hopper_frequency: 315000000
+Hopper_frequency: 318000000
+Hopper_frequency: 390000000
+Hopper_frequency: 433920000
+Hopper_frequency: 868350000