From 18a8e496fa490259f039bc449b4a03aec793a93e Mon Sep 17 00:00:00 2001 From: "Calvin M.T." Date: Sat, 22 Mar 2025 17:58:40 +0100 Subject: [PATCH 01/13] Add descriptive comments to each function --- radio_scanner_app.c | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/radio_scanner_app.c b/radio_scanner_app.c index 97f36ac..f8ddb78 100644 --- a/radio_scanner_app.c +++ b/radio_scanner_app.c @@ -18,6 +18,10 @@ #define SUBGHZ_FREQUENCY_STEP 10000 #define SUBGHZ_DEVICE_NAME "cc1101_int" +/** + * Draw callback for updating the canvas UI. + * Displays the current frequency, RSSI, sensitivity, and scanning status. + */ static void radio_scanner_draw_callback(Canvas* canvas, void* context) { furi_assert(canvas); furi_assert(context); @@ -49,6 +53,10 @@ static void radio_scanner_draw_callback(Canvas* canvas, void* context) { #endif } +/** + * Input callback for handling button events. + * Passes input events into the event queue for processing. + */ static void radio_scanner_input_callback(InputEvent* input_event, void* context) { furi_assert(context); #ifdef FURI_DEBUG @@ -60,6 +68,10 @@ static void radio_scanner_input_callback(InputEvent* input_event, void* context) FURI_LOG_D(TAG, "Exit radio_scanner_input_callback"); } +/** + * RX callback triggered on radio packet reception. + * Currently unused beyond debug logging. + */ static void radio_scanner_rx_callback(const void* data, size_t size, void* context) { UNUSED(data); UNUSED(context); @@ -70,6 +82,9 @@ static void radio_scanner_rx_callback(const void* data, size_t size, void* conte #endif } +/** + * Updates the RSSI (signal strength) value from the radio device. + */ static void radio_scanner_update_rssi(RadioScannerApp* app) { furi_assert(app); #ifdef FURI_DEBUG @@ -89,6 +104,10 @@ static void radio_scanner_update_rssi(RadioScannerApp* app) { #endif } +/** + * Initializes the SubGHz radio device with appropriate settings. + * Sets frequency, loads preset, and begins asynchronous reception. + */ static bool radio_scanner_init_subghz(RadioScannerApp* app) { furi_assert(app); #ifdef FURI_DEBUG @@ -148,6 +167,10 @@ static bool radio_scanner_init_subghz(RadioScannerApp* app) { return true; } +/** + * Core logic for scanning radio frequencies. + * Adjusts frequency up/down and checks for valid signal above sensitivity threshold. + */ static void radio_scanner_process_scanning(RadioScannerApp* app) { furi_assert(app); #ifdef FURI_DEBUG @@ -234,6 +257,10 @@ static void radio_scanner_process_scanning(RadioScannerApp* app) { #endif } +/** + * Allocates and initializes a new instance of the RadioScannerApp. + * Sets up GUI components, state variables, and input handlers. + */ RadioScannerApp* radio_scanner_app_alloc() { #ifdef FURI_DEBUG FURI_LOG_D(TAG, "Enter radio_scanner_app_alloc"); @@ -290,6 +317,10 @@ RadioScannerApp* radio_scanner_app_alloc() { return app; } +/** + * Frees all resources allocated by the RadioScannerApp instance. + * Cleans up GUI, radio device, event queue, and memory. + */ void radio_scanner_app_free(RadioScannerApp* app) { furi_assert(app); #ifdef FURI_DEBUG @@ -347,6 +378,10 @@ void radio_scanner_app_free(RadioScannerApp* app) { #endif } +/** + * Main entry point for the radio scanner app. + * Handles main loop, input processing, scanning logic, and cleanup. + */ int32_t radio_scanner_app(void* p) { UNUSED(p); FURI_LOG_I(TAG, "Enter radio_scanner_app"); From 578f8ba965b4963f08d40d38da27fc74caf2eaae Mon Sep 17 00:00:00 2001 From: "Calvin M.T." Date: Sun, 23 Mar 2025 09:24:43 +0100 Subject: [PATCH 02/13] Move internal functions to separate file --- radio_scanner_app.c | 254 +----------------------------------------- radio_scanner_app.h | 28 ----- radio_scanner_app_i.c | 240 +++++++++++++++++++++++++++++++++++++++ radio_scanner_app_i.h | 43 +++++++ 4 files changed, 285 insertions(+), 280 deletions(-) delete mode 100644 radio_scanner_app.h create mode 100644 radio_scanner_app_i.c create mode 100644 radio_scanner_app_i.h diff --git a/radio_scanner_app.c b/radio_scanner_app.c index f8ddb78..42280b5 100644 --- a/radio_scanner_app.c +++ b/radio_scanner_app.c @@ -1,4 +1,5 @@ -#include "radio_scanner_app.h" +#include "radio_scanner_app_i.h" + #include #include #include @@ -6,257 +7,6 @@ #include #include -#define TAG "RadioScannerApp" - -#define RADIO_SCANNER_DEFAULT_FREQ 433920000 -#define RADIO_SCANNER_DEFAULT_RSSI (-100.0f) -#define RADIO_SCANNER_DEFAULT_SENSITIVITY (-85.0f) -#define RADIO_SCANNER_BUFFER_SZ 32 - -#define SUBGHZ_FREQUENCY_MIN 300000000 -#define SUBGHZ_FREQUENCY_MAX 928000000 -#define SUBGHZ_FREQUENCY_STEP 10000 -#define SUBGHZ_DEVICE_NAME "cc1101_int" - -/** - * Draw callback for updating the canvas UI. - * Displays the current frequency, RSSI, sensitivity, and scanning status. - */ -static void radio_scanner_draw_callback(Canvas* canvas, void* context) { - furi_assert(canvas); - furi_assert(context); -#ifdef FURI_DEBUG - FURI_LOG_D(TAG, "Enter radio_scanner_draw_callback"); -#endif - RadioScannerApp* app = (RadioScannerApp*)context; - canvas_clear(canvas); - canvas_set_font(canvas, FontPrimary); - canvas_draw_str_aligned(canvas, 64, 2, AlignCenter, AlignTop, "Radio Scanner"); - - canvas_set_font(canvas, FontSecondary); - char freq_str[RADIO_SCANNER_BUFFER_SZ + 1] = {0}; - snprintf(freq_str, RADIO_SCANNER_BUFFER_SZ, "Freq: %.2f MHz", (double)app->frequency / 1000000); - canvas_draw_str_aligned(canvas, 64, 18, AlignCenter, AlignTop, freq_str); - - char rssi_str[RADIO_SCANNER_BUFFER_SZ + 1] = {0}; - snprintf(rssi_str, RADIO_SCANNER_BUFFER_SZ, "RSSI: %.2f", (double)app->rssi); - canvas_draw_str_aligned(canvas, 64, 30, AlignCenter, AlignTop, rssi_str); - - char sensitivity_str[RADIO_SCANNER_BUFFER_SZ + 1] = {0}; - snprintf(sensitivity_str, RADIO_SCANNER_BUFFER_SZ, "Sens: %.2f", (double)app->sensitivity); - canvas_draw_str_aligned(canvas, 64, 42, AlignCenter, AlignTop, sensitivity_str); - - canvas_draw_str_aligned( - canvas, 64, 54, AlignCenter, AlignTop, app->scanning ? "Scanning..." : "Locked"); -#ifdef FURI_DEBUG - FURI_LOG_D(TAG, "Exit radio_scanner_draw_callback"); -#endif -} - -/** - * Input callback for handling button events. - * Passes input events into the event queue for processing. - */ -static void radio_scanner_input_callback(InputEvent* input_event, void* context) { - furi_assert(context); -#ifdef FURI_DEBUG - FURI_LOG_D(TAG, "Enter radio_scanner_input_callback"); - FURI_LOG_D(TAG, "Input event: type=%d, key=%d", input_event->type, input_event->key); -#endif - FuriMessageQueue* event_queue = context; - furi_message_queue_put(event_queue, input_event, FuriWaitForever); - FURI_LOG_D(TAG, "Exit radio_scanner_input_callback"); -} - -/** - * RX callback triggered on radio packet reception. - * Currently unused beyond debug logging. - */ -static void radio_scanner_rx_callback(const void* data, size_t size, void* context) { - UNUSED(data); - UNUSED(context); -#ifdef FURI_DEBUG - FURI_LOG_D(TAG, "radio_scanner_rx_callback called with size: %zu", size); -#else - UNUSED(size); -#endif -} - -/** - * Updates the RSSI (signal strength) value from the radio device. - */ -static void radio_scanner_update_rssi(RadioScannerApp* app) { - furi_assert(app); -#ifdef FURI_DEBUG - FURI_LOG_D(TAG, "Enter radio_scanner_update_rssi"); -#endif - if(app->radio_device) { - app->rssi = subghz_devices_get_rssi(app->radio_device); -#ifdef FURI_DEBUG - FURI_LOG_D(TAG, "Updated RSSI: %f", (double)app->rssi); -#endif - } else { - FURI_LOG_E(TAG, "Radio device is NULL"); - app->rssi = RADIO_SCANNER_DEFAULT_RSSI; - } -#ifdef FURI_DEBUG - FURI_LOG_D(TAG, "Exit radio_scanner_update_rssi"); -#endif -} - -/** - * Initializes the SubGHz radio device with appropriate settings. - * Sets frequency, loads preset, and begins asynchronous reception. - */ -static bool radio_scanner_init_subghz(RadioScannerApp* app) { - furi_assert(app); -#ifdef FURI_DEBUG - FURI_LOG_D(TAG, "Enter radio_scanner_init_subghz"); -#endif - subghz_devices_init(); -#ifdef FURI_DEBUG - FURI_LOG_D(TAG, "SubGHz devices initialized"); -#endif - - const SubGhzDevice* device = subghz_devices_get_by_name(SUBGHZ_DEVICE_NAME); - if(!device) { - FURI_LOG_E(TAG, "Failed to get SubGhzDevice"); - return false; - } - FURI_LOG_I(TAG, "SubGhzDevice obtained: %s", subghz_devices_get_name(device)); - - app->radio_device = device; - - subghz_devices_begin(device); - subghz_devices_reset(device); -#ifdef FURI_DEBUG - FURI_LOG_D(TAG, "SubGhzDevice begun"); -#endif - if(!subghz_devices_is_frequency_valid(device, app->frequency)) { - FURI_LOG_E(TAG, "Invalid frequency: %lu", app->frequency); - return false; - } -#ifdef FURI_DEBUG - FURI_LOG_D(TAG, "Frequency is valid: %lu", app->frequency); -#endif - subghz_devices_load_preset(device, FuriHalSubGhzPreset2FSKDev238Async, NULL); -#ifdef FURI_DEBUG - FURI_LOG_D(TAG, "Preset loaded"); -#endif - subghz_devices_set_frequency(device, app->frequency); -#ifdef FURI_DEBUG - FURI_LOG_D(TAG, "Frequency set to %lu", app->frequency); -#endif - subghz_devices_start_async_rx(device, radio_scanner_rx_callback, app); -#ifdef FURI_DEBUG - FURI_LOG_D(TAG, "Asynchronous RX started"); -#endif - if(furi_hal_speaker_acquire(30)) { - app->speaker_acquired = true; - subghz_devices_set_async_mirror_pin(device, &gpio_speaker); -#ifdef FURI_DEBUG - FURI_LOG_D(TAG, "Speaker acquired and async mirror pin set"); -#endif - } else { - app->speaker_acquired = false; - FURI_LOG_E(TAG, "Failed to acquire speaker"); - } -#ifdef FURI_DEBUG - FURI_LOG_D(TAG, "Exit radio_scanner_init_subghz"); -#endif - return true; -} - -/** - * Core logic for scanning radio frequencies. - * Adjusts frequency up/down and checks for valid signal above sensitivity threshold. - */ -static void radio_scanner_process_scanning(RadioScannerApp* app) { - furi_assert(app); -#ifdef FURI_DEBUG - FURI_LOG_D(TAG, "Enter radio_scanner_process_scanning"); -#endif - radio_scanner_update_rssi(app); -#ifdef FURI_DEBUG - FURI_LOG_D(TAG, "RSSI after update: %f", (double)app->rssi); -#endif - bool signal_detected = (app->rssi > app->sensitivity); -#ifdef FURI_DEBUG - FURI_LOG_D(TAG, "Signal detected: %d", signal_detected); -#endif - - if(signal_detected) { - if(app->scanning) { - app->scanning = false; -#ifdef FURI_DEBUG - FURI_LOG_D(TAG, "Scanning stopped"); -#endif - } - } else { - if(!app->scanning) { - app->scanning = true; -#ifdef FURI_DEBUG - FURI_LOG_D(TAG, "Scanning started"); -#endif - } - } - - if(!app->scanning) { -#ifdef FURI_DEBUG - FURI_LOG_D(TAG, "Exit radio_scanner_process_scanning"); - return; -#endif - } - uint32_t new_frequency = (app->scan_direction == ScanDirectionUp) ? - app->frequency + SUBGHZ_FREQUENCY_STEP : - app->frequency - SUBGHZ_FREQUENCY_STEP; - - if(!subghz_devices_is_frequency_valid(app->radio_device, new_frequency)) { - if(app->scan_direction == ScanDirectionUp) { - if(new_frequency < 387000000) { - new_frequency = 387000000; - } else if(new_frequency < 779000000) { - new_frequency = 779000000; - } else if(new_frequency > SUBGHZ_FREQUENCY_MAX) { - new_frequency = SUBGHZ_FREQUENCY_MIN; - } - } else { - if(new_frequency > 464000000) { - new_frequency = 464000000; - } else if(new_frequency > 348000000) { - new_frequency = 348000000; - } else if(new_frequency < SUBGHZ_FREQUENCY_MIN) { - new_frequency = SUBGHZ_FREQUENCY_MAX; - } - } -#ifdef FURI_DEBUG - FURI_LOG_D(TAG, "Adjusted frequency to next valid range: %lu", new_frequency); -#endif - } - - subghz_devices_flush_rx(app->radio_device); - subghz_devices_stop_async_rx(app->radio_device); -#ifdef FURI_DEBUG - FURI_LOG_D(TAG, "Asynchronous RX stopped"); -#endif - - subghz_devices_idle(app->radio_device); -#ifdef FURI_DEBUG - FURI_LOG_D(TAG, "Device set to idle"); -#endif - app->frequency = new_frequency; - subghz_devices_set_frequency(app->radio_device, app->frequency); -#ifdef FURI_DEBUG - FURI_LOG_D(TAG, "Frequency set to %lu", app->frequency); -#endif - - subghz_devices_start_async_rx(app->radio_device, radio_scanner_rx_callback, app); -#ifdef FURI_DEBUG - FURI_LOG_D(TAG, "Asynchronous RX restarted"); - FURI_LOG_D(TAG, "Exit radio_scanner_process_scanning"); -#endif -} - /** * Allocates and initializes a new instance of the RadioScannerApp. * Sets up GUI components, state variables, and input handlers. diff --git a/radio_scanner_app.h b/radio_scanner_app.h deleted file mode 100644 index 70b03a3..0000000 --- a/radio_scanner_app.h +++ /dev/null @@ -1,28 +0,0 @@ -#pragma once - -#include -#include -#include - -typedef enum { - ScanDirectionUp, - ScanDirectionDown, -} ScanDirection; - -typedef struct { - Gui* gui; - ViewPort* view_port; - FuriMessageQueue* event_queue; - bool running; - uint32_t frequency; - float rssi; - float sensitivity; - bool scanning; - ScanDirection scan_direction; - const SubGhzDevice* radio_device; - bool speaker_acquired; -} RadioScannerApp; - -RadioScannerApp* radio_scanner_app_alloc(void); -void radio_scanner_app_free(RadioScannerApp* app); -int32_t radio_scanner_app(void* p); diff --git a/radio_scanner_app_i.c b/radio_scanner_app_i.c new file mode 100644 index 0000000..7fc95b9 --- /dev/null +++ b/radio_scanner_app_i.c @@ -0,0 +1,240 @@ +#include "radio_scanner_app_i.h" + +/** + * Draw callback for updating the canvas UI. + * Displays the current frequency, RSSI, sensitivity, and scanning status. + */ +void radio_scanner_draw_callback(Canvas* canvas, void* context) { + furi_assert(canvas); + furi_assert(context); +#ifdef FURI_DEBUG + FURI_LOG_D(TAG, "Enter radio_scanner_draw_callback"); +#endif + RadioScannerApp* app = (RadioScannerApp*)context; + canvas_clear(canvas); + canvas_set_font(canvas, FontPrimary); + canvas_draw_str_aligned(canvas, 64, 2, AlignCenter, AlignTop, "Radio Scanner"); + + canvas_set_font(canvas, FontSecondary); + char freq_str[RADIO_SCANNER_BUFFER_SZ + 1] = {0}; + snprintf(freq_str, RADIO_SCANNER_BUFFER_SZ, "Freq: %.2f MHz", (double)app->frequency / 1000000); + canvas_draw_str_aligned(canvas, 64, 18, AlignCenter, AlignTop, freq_str); + + char rssi_str[RADIO_SCANNER_BUFFER_SZ + 1] = {0}; + snprintf(rssi_str, RADIO_SCANNER_BUFFER_SZ, "RSSI: %.2f", (double)app->rssi); + canvas_draw_str_aligned(canvas, 64, 30, AlignCenter, AlignTop, rssi_str); + + char sensitivity_str[RADIO_SCANNER_BUFFER_SZ + 1] = {0}; + snprintf(sensitivity_str, RADIO_SCANNER_BUFFER_SZ, "Sens: %.2f", (double)app->sensitivity); + canvas_draw_str_aligned(canvas, 64, 42, AlignCenter, AlignTop, sensitivity_str); + + canvas_draw_str_aligned( + canvas, 64, 54, AlignCenter, AlignTop, app->scanning ? "Scanning..." : "Locked"); +#ifdef FURI_DEBUG + FURI_LOG_D(TAG, "Exit radio_scanner_draw_callback"); +#endif +} + +/** + * Input callback for handling button events. + * Passes input events into the event queue for processing. + */ +void radio_scanner_input_callback(InputEvent* input_event, void* context) { + furi_assert(context); +#ifdef FURI_DEBUG + FURI_LOG_D(TAG, "Enter radio_scanner_input_callback"); + FURI_LOG_D(TAG, "Input event: type=%d, key=%d", input_event->type, input_event->key); +#endif + FuriMessageQueue* event_queue = context; + furi_message_queue_put(event_queue, input_event, FuriWaitForever); + FURI_LOG_D(TAG, "Exit radio_scanner_input_callback"); +} + +/** + * RX callback triggered on radio packet reception. + * Currently unused beyond debug logging. + */ +void radio_scanner_rx_callback(const void* data, size_t size, void* context) { + UNUSED(data); + UNUSED(context); +#ifdef FURI_DEBUG + FURI_LOG_D(TAG, "radio_scanner_rx_callback called with size: %zu", size); +#else + UNUSED(size); +#endif +} + +/** + * Updates the RSSI (signal strength) value from the radio device. + */ +void radio_scanner_update_rssi(RadioScannerApp* app) { + furi_assert(app); +#ifdef FURI_DEBUG + FURI_LOG_D(TAG, "Enter radio_scanner_update_rssi"); +#endif + if(app->radio_device) { + app->rssi = subghz_devices_get_rssi(app->radio_device); +#ifdef FURI_DEBUG + FURI_LOG_D(TAG, "Updated RSSI: %f", (double)app->rssi); +#endif + } else { + FURI_LOG_E(TAG, "Radio device is NULL"); + app->rssi = RADIO_SCANNER_DEFAULT_RSSI; + } +#ifdef FURI_DEBUG + FURI_LOG_D(TAG, "Exit radio_scanner_update_rssi"); +#endif +} + +/** + * Initializes the SubGHz radio device with appropriate settings. + * Sets frequency, loads preset, and begins asynchronous reception. + */ +bool radio_scanner_init_subghz(RadioScannerApp* app) { + furi_assert(app); +#ifdef FURI_DEBUG + FURI_LOG_D(TAG, "Enter radio_scanner_init_subghz"); +#endif + subghz_devices_init(); +#ifdef FURI_DEBUG + FURI_LOG_D(TAG, "SubGHz devices initialized"); +#endif + + const SubGhzDevice* device = subghz_devices_get_by_name(SUBGHZ_DEVICE_NAME); + if(!device) { + FURI_LOG_E(TAG, "Failed to get SubGhzDevice"); + return false; + } + FURI_LOG_I(TAG, "SubGhzDevice obtained: %s", subghz_devices_get_name(device)); + + app->radio_device = device; + + subghz_devices_begin(device); + subghz_devices_reset(device); +#ifdef FURI_DEBUG + FURI_LOG_D(TAG, "SubGhzDevice begun"); +#endif + if(!subghz_devices_is_frequency_valid(device, app->frequency)) { + FURI_LOG_E(TAG, "Invalid frequency: %lu", app->frequency); + return false; + } +#ifdef FURI_DEBUG + FURI_LOG_D(TAG, "Frequency is valid: %lu", app->frequency); +#endif + subghz_devices_load_preset(device, FuriHalSubGhzPreset2FSKDev238Async, NULL); +#ifdef FURI_DEBUG + FURI_LOG_D(TAG, "Preset loaded"); +#endif + subghz_devices_set_frequency(device, app->frequency); +#ifdef FURI_DEBUG + FURI_LOG_D(TAG, "Frequency set to %lu", app->frequency); +#endif + subghz_devices_start_async_rx(device, radio_scanner_rx_callback, app); +#ifdef FURI_DEBUG + FURI_LOG_D(TAG, "Asynchronous RX started"); +#endif + if(furi_hal_speaker_acquire(30)) { + app->speaker_acquired = true; + subghz_devices_set_async_mirror_pin(device, &gpio_speaker); +#ifdef FURI_DEBUG + FURI_LOG_D(TAG, "Speaker acquired and async mirror pin set"); +#endif + } else { + app->speaker_acquired = false; + FURI_LOG_E(TAG, "Failed to acquire speaker"); + } +#ifdef FURI_DEBUG + FURI_LOG_D(TAG, "Exit radio_scanner_init_subghz"); +#endif + return true; +} + +/** + * Core logic for scanning radio frequencies. + * Adjusts frequency up/down and checks for valid signal above sensitivity threshold. + */ +void radio_scanner_process_scanning(RadioScannerApp* app) { + furi_assert(app); +#ifdef FURI_DEBUG + FURI_LOG_D(TAG, "Enter radio_scanner_process_scanning"); +#endif + radio_scanner_update_rssi(app); +#ifdef FURI_DEBUG + FURI_LOG_D(TAG, "RSSI after update: %f", (double)app->rssi); +#endif + bool signal_detected = (app->rssi > app->sensitivity); +#ifdef FURI_DEBUG + FURI_LOG_D(TAG, "Signal detected: %d", signal_detected); +#endif + + if(signal_detected) { + if(app->scanning) { + app->scanning = false; +#ifdef FURI_DEBUG + FURI_LOG_D(TAG, "Scanning stopped"); +#endif + } + } else { + if(!app->scanning) { + app->scanning = true; +#ifdef FURI_DEBUG + FURI_LOG_D(TAG, "Scanning started"); +#endif + } + } + + if(!app->scanning) { +#ifdef FURI_DEBUG + FURI_LOG_D(TAG, "Exit radio_scanner_process_scanning"); + return; +#endif + } + uint32_t new_frequency = (app->scan_direction == ScanDirectionUp) ? + app->frequency + SUBGHZ_FREQUENCY_STEP : + app->frequency - SUBGHZ_FREQUENCY_STEP; + + if(!subghz_devices_is_frequency_valid(app->radio_device, new_frequency)) { + if(app->scan_direction == ScanDirectionUp) { + if(new_frequency < 387000000) { + new_frequency = 387000000; + } else if(new_frequency < 779000000) { + new_frequency = 779000000; + } else if(new_frequency > SUBGHZ_FREQUENCY_MAX) { + new_frequency = SUBGHZ_FREQUENCY_MIN; + } + } else { + if(new_frequency > 464000000) { + new_frequency = 464000000; + } else if(new_frequency > 348000000) { + new_frequency = 348000000; + } else if(new_frequency < SUBGHZ_FREQUENCY_MIN) { + new_frequency = SUBGHZ_FREQUENCY_MAX; + } + } +#ifdef FURI_DEBUG + FURI_LOG_D(TAG, "Adjusted frequency to next valid range: %lu", new_frequency); +#endif + } + + subghz_devices_flush_rx(app->radio_device); + subghz_devices_stop_async_rx(app->radio_device); +#ifdef FURI_DEBUG + FURI_LOG_D(TAG, "Asynchronous RX stopped"); +#endif + + subghz_devices_idle(app->radio_device); +#ifdef FURI_DEBUG + FURI_LOG_D(TAG, "Device set to idle"); +#endif + app->frequency = new_frequency; + subghz_devices_set_frequency(app->radio_device, app->frequency); +#ifdef FURI_DEBUG + FURI_LOG_D(TAG, "Frequency set to %lu", app->frequency); +#endif + + subghz_devices_start_async_rx(app->radio_device, radio_scanner_rx_callback, app); +#ifdef FURI_DEBUG + FURI_LOG_D(TAG, "Asynchronous RX restarted"); + FURI_LOG_D(TAG, "Exit radio_scanner_process_scanning"); +#endif +} diff --git a/radio_scanner_app_i.h b/radio_scanner_app_i.h new file mode 100644 index 0000000..837d9f3 --- /dev/null +++ b/radio_scanner_app_i.h @@ -0,0 +1,43 @@ +#pragma once + +#include +#include +#include + +#define TAG "RadioScannerApp" + +#define RADIO_SCANNER_DEFAULT_FREQ 433920000 +#define RADIO_SCANNER_DEFAULT_RSSI (-100.0f) +#define RADIO_SCANNER_DEFAULT_SENSITIVITY (-85.0f) +#define RADIO_SCANNER_BUFFER_SZ 32 + +#define SUBGHZ_FREQUENCY_MIN 300000000 +#define SUBGHZ_FREQUENCY_MAX 928000000 +#define SUBGHZ_FREQUENCY_STEP 10000 +#define SUBGHZ_DEVICE_NAME "cc1101_int" + +typedef enum { + ScanDirectionUp, + ScanDirectionDown, +} ScanDirection; + +typedef struct { + Gui* gui; + ViewPort* view_port; + FuriMessageQueue* event_queue; + bool running; + uint32_t frequency; + float rssi; + float sensitivity; + bool scanning; + ScanDirection scan_direction; + const SubGhzDevice* radio_device; + bool speaker_acquired; +} RadioScannerApp; + +void radio_scanner_draw_callback(Canvas* canvas, void* context); +void radio_scanner_input_callback(InputEvent* input_event, void* context); +void radio_scanner_rx_callback(const void* data, size_t size, void* context); +void radio_scanner_update_rssi(RadioScannerApp* app); +bool radio_scanner_init_subghz(RadioScannerApp* app); +void radio_scanner_process_scanning(RadioScannerApp* app); From 68eb28dbe70755ebf503c70c940c3b99a6c709e0 Mon Sep 17 00:00:00 2001 From: "Calvin M.T." Date: Sun, 23 Mar 2025 11:45:03 +0100 Subject: [PATCH 03/13] Remove debug logs --- radio_scanner_app.c | 42 ++---------------------------------------- 1 file changed, 2 insertions(+), 40 deletions(-) diff --git a/radio_scanner_app.c b/radio_scanner_app.c index 42280b5..3e80abb 100644 --- a/radio_scanner_app.c +++ b/radio_scanner_app.c @@ -20,19 +20,10 @@ RadioScannerApp* radio_scanner_app_alloc() { FURI_LOG_E(TAG, "Failed to allocate RadioScannerApp"); return NULL; } -#ifdef FURI_DEBUG - FURI_LOG_D(TAG, "RadioScannerApp allocated"); -#endif app->view_port = view_port_alloc(); -#ifdef FURI_DEBUG - FURI_LOG_D(TAG, "ViewPort allocated"); -#endif app->event_queue = furi_message_queue_alloc(8, sizeof(InputEvent)); -#ifdef FURI_DEBUG - FURI_LOG_D(TAG, "Event queue allocated"); -#endif app->running = true; app->frequency = RADIO_SCANNER_DEFAULT_FREQ; @@ -44,26 +35,15 @@ RadioScannerApp* radio_scanner_app_alloc() { app->radio_device = NULL; view_port_draw_callback_set(app->view_port, radio_scanner_draw_callback, app); -#ifdef FURI_DEBUG - FURI_LOG_D(TAG, "Draw callback set"); -#endif - view_port_input_callback_set(app->view_port, radio_scanner_input_callback, app->event_queue); -#ifdef FURI_DEBUG - FURI_LOG_D(TAG, "Input callback set"); - FURI_LOG_D(TAG, "Exit radio_scanner_app_alloc"); -#endif app->gui = furi_record_open(RECORD_GUI); -#ifdef FURI_DEBUG - FURI_LOG_D(TAG, "GUI record opened"); -#endif gui_add_view_port(app->gui, app->view_port, GuiLayerFullscreen); + #ifdef FURI_DEBUG - FURI_LOG_D(TAG, "ViewPort added to GUI"); + FURI_LOG_D(TAG, "Exit radio_scanner_app_alloc"); #endif - return app; } @@ -100,27 +80,12 @@ void radio_scanner_app_free(RadioScannerApp* app) { } subghz_devices_deinit(); -#ifdef FURI_DEBUG - FURI_LOG_D(TAG, "SubGHz devices de-initialized"); -#endif gui_remove_view_port(app->gui, app->view_port); -#ifdef FURI_DEBUG - FURI_LOG_D(TAG, "ViewPort removed from GUI"); -#endif view_port_free(app->view_port); -#ifdef FURI_DEBUG - FURI_LOG_D(TAG, "ViewPort freed"); -#endif furi_message_queue_free(app->event_queue); -#ifdef FURI_DEBUG - FURI_LOG_D(TAG, "Event queue freed"); -#endif furi_record_close(RECORD_GUI); -#ifdef FURI_DEBUG - FURI_LOG_D(TAG, "GUI record closed"); -#endif free(app); #ifdef FURI_DEBUG @@ -168,9 +133,6 @@ int32_t radio_scanner_app(void* p) { radio_scanner_update_rssi(app); } -#ifdef FURI_DEBUG - FURI_LOG_D(TAG, "Checking for input events"); -#endif if(furi_message_queue_get(app->event_queue, &event, 10) == FuriStatusOk) { #ifdef FURI_DEBUG FURI_LOG_D(TAG, "Input event received: type=%d, key=%d", event.type, event.key); From 096ff96883114e3e10686714bfd2e23e756c9fb9 Mon Sep 17 00:00:00 2001 From: "Calvin M.T." Date: Sun, 23 Mar 2025 11:52:44 +0100 Subject: [PATCH 04/13] Refactor input key conditions to switch case --- radio_scanner_app.c | 46 +++++++++++++++++++++++++++------------------ 1 file changed, 28 insertions(+), 18 deletions(-) diff --git a/radio_scanner_app.c b/radio_scanner_app.c index 3e80abb..a79c77b 100644 --- a/radio_scanner_app.c +++ b/radio_scanner_app.c @@ -138,24 +138,34 @@ int32_t radio_scanner_app(void* p) { FURI_LOG_D(TAG, "Input event received: type=%d, key=%d", event.type, event.key); #endif if(event.type == InputTypeShort) { - if(event.key == InputKeyOk) { - app->scanning = !app->scanning; - FURI_LOG_I(TAG, "Toggled scanning: %d", app->scanning); - } else if(event.key == InputKeyUp) { - app->sensitivity += 1.0f; - FURI_LOG_I(TAG, "Increased sensitivity: %f", (double)app->sensitivity); - } else if(event.key == InputKeyDown) { - app->sensitivity -= 1.0f; - FURI_LOG_I(TAG, "Decreased sensitivity: %f", (double)app->sensitivity); - } else if(event.key == InputKeyLeft) { - app->scan_direction = ScanDirectionDown; - FURI_LOG_I(TAG, "Scan direction set to down"); - } else if(event.key == InputKeyRight) { - app->scan_direction = ScanDirectionUp; - FURI_LOG_I(TAG, "Scan direction set to up"); - } else if(event.key == InputKeyBack) { - app->running = false; - FURI_LOG_I(TAG, "Exiting app"); + switch(event.key) { + case InputKeyOk: + app->scanning = !app->scanning; + FURI_LOG_I(TAG, "Toggled scanning: %d", app->scanning); + break; + case InputKeyUp: + app->sensitivity += 1.0f; + FURI_LOG_I(TAG, "Increased sensitivity: %f", (double)app->sensitivity); + break; + case InputKeyDown: + app->sensitivity -= 1.0f; + FURI_LOG_I(TAG, "Decreased sensitivity: %f", (double)app->sensitivity); + break; + case InputKeyLeft: + app->scan_direction = ScanDirectionDown; + FURI_LOG_I(TAG, "Scan direction set to down"); + break; + case InputKeyRight: + app->scan_direction = ScanDirectionUp; + FURI_LOG_I(TAG, "Scan direction set to up"); + break; + case InputKeyBack: + app->running = false; + FURI_LOG_I(TAG, "Exiting app"); + break; + default: + FURI_LOG_I(TAG, "Unknown input"); + break; } } } From 19ec305ec463ba1686a5c66e2eb096adf6e46a7d Mon Sep 17 00:00:00 2001 From: "Calvin M.T." Date: Thu, 27 Mar 2025 19:53:52 +0100 Subject: [PATCH 05/13] Refactor UI architecture using view dispatcher and scene manager --- helpers/scanner_event.h | 14 +++ radio_scanner_app.c | 129 +++++++++----------- radio_scanner_app_i.c | 90 ++++++-------- radio_scanner_app_i.h | 34 +++++- scenes/radio_scanner_scene.c | 38 ++++++ scenes/radio_scanner_scene.h | 40 ++++++ scenes/radio_scanner_scene_config.h | 5 + scenes/scanner.c | 131 ++++++++++++++++++++ views/scanner.c | 182 ++++++++++++++++++++++++++++ views/scanner.h | 44 +++++++ 10 files changed, 582 insertions(+), 125 deletions(-) create mode 100644 helpers/scanner_event.h create mode 100644 scenes/radio_scanner_scene.c create mode 100644 scenes/radio_scanner_scene.h create mode 100644 scenes/radio_scanner_scene_config.h create mode 100644 scenes/scanner.c create mode 100644 views/scanner.c create mode 100644 views/scanner.h diff --git a/helpers/scanner_event.h b/helpers/scanner_event.h new file mode 100644 index 0000000..d40a38c --- /dev/null +++ b/helpers/scanner_event.h @@ -0,0 +1,14 @@ +#pragma once + +/** + * Enumeration of scanner events. + */ +typedef enum { + // Scanning + ScannerEventScanDirectionDown, + ScannerEventScanDirectionUp, + ScannerEventToggleScanning, + // Sensitivity + ScannerEventDecreaseSensitivity, + ScannerEventIncreaseSensitivity, +} ScannerEvent; diff --git a/radio_scanner_app.c b/radio_scanner_app.c index a79c77b..54d3a10 100644 --- a/radio_scanner_app.c +++ b/radio_scanner_app.c @@ -7,6 +7,36 @@ #include #include +/** + * Custom event callback for the radio scanner app. + * Passes custom events to the scene manager for handling. + */ +static bool radio_scanner_app_custom_event_callback(void* context, uint32_t event) { + furi_assert(context); + RadioScannerApp* app = context; + return scene_manager_handle_custom_event(app->scene_manager, event); +} + +/** + * Back event callback for the radio scanner app. + * Processes back navigation events via the scene manager. + */ +static bool radio_scanner_app_back_event_callback(void* context) { + furi_assert(context); + RadioScannerApp* app = context; + return scene_manager_handle_back_event(app->scene_manager); +} + +/** + * Tick event callback for the radio scanner app. + * Invokes the scene manager to handle periodic tick events. + */ +static void radio_scanner_app_tick_event_callback(void* context) { + furi_assert(context); + RadioScannerApp* app = context; + scene_manager_handle_tick_event(app->scene_manager); +} + /** * Allocates and initializes a new instance of the RadioScannerApp. * Sets up GUI components, state variables, and input handlers. @@ -21,11 +51,27 @@ RadioScannerApp* radio_scanner_app_alloc() { return NULL; } - app->view_port = view_port_alloc(); + // GUI + app->gui = furi_record_open(RECORD_GUI); + + // SceneManager + app->scene_manager = scene_manager_alloc(&radio_scanner_scene_handlers, app); + + // ViewDispatcher + app->view_dispatcher = view_dispatcher_alloc(); + + view_dispatcher_set_event_callback_context(app->view_dispatcher, app); + view_dispatcher_set_custom_event_callback(app->view_dispatcher, radio_scanner_app_custom_event_callback); + view_dispatcher_set_navigation_event_callback(app->view_dispatcher, radio_scanner_app_back_event_callback); + view_dispatcher_set_tick_event_callback(app->view_dispatcher, radio_scanner_app_tick_event_callback, 100); - app->event_queue = furi_message_queue_alloc(8, sizeof(InputEvent)); + view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen); - app->running = true; + // Scanner + app->scanner = scanner_view_alloc(); + view_dispatcher_add_view(app->view_dispatcher, RadioScannerViewScanner, scanner_view_get_view(app->scanner)); + + // Init app state app->frequency = RADIO_SCANNER_DEFAULT_FREQ; app->rssi = RADIO_SCANNER_DEFAULT_RSSI; app->sensitivity = RADIO_SCANNER_DEFAULT_SENSITIVITY; @@ -34,12 +80,7 @@ RadioScannerApp* radio_scanner_app_alloc() { app->speaker_acquired = false; app->radio_device = NULL; - view_port_draw_callback_set(app->view_port, radio_scanner_draw_callback, app); - view_port_input_callback_set(app->view_port, radio_scanner_input_callback, app->event_queue); - - app->gui = furi_record_open(RECORD_GUI); - - gui_add_view_port(app->gui, app->view_port, GuiLayerFullscreen); + scene_manager_next_scene(app->scene_manager, RadioScannerSceneScanner); #ifdef FURI_DEBUG FURI_LOG_D(TAG, "Exit radio_scanner_app_alloc"); @@ -80,10 +121,16 @@ void radio_scanner_app_free(RadioScannerApp* app) { } subghz_devices_deinit(); - gui_remove_view_port(app->gui, app->view_port); - view_port_free(app->view_port); - furi_message_queue_free(app->event_queue); + // Scanner + view_dispatcher_remove_view(app->view_dispatcher, RadioScannerViewScanner); + scanner_view_free(app->scanner); + + // ViewDispatcher + view_dispatcher_free(app->view_dispatcher); + + // SceneManager + scene_manager_free(app->scene_manager); furi_record_close(RECORD_GUI); @@ -116,63 +163,7 @@ int32_t radio_scanner_app(void* p) { FURI_LOG_D(TAG, "SubGHz initialized successfully"); #endif - InputEvent event; - while(app->running) { -#ifdef FURI_DEBUG - FURI_LOG_D(TAG, "Main loop iteration"); -#endif - if(app->scanning) { -#ifdef FURI_DEBUG - FURI_LOG_D(TAG, "Scanning is active"); -#endif - radio_scanner_process_scanning(app); - } else { -#ifdef FURI_DEBUG - FURI_LOG_D(TAG, "Scanning is inactive, updating RSSI"); -#endif - radio_scanner_update_rssi(app); - } - - if(furi_message_queue_get(app->event_queue, &event, 10) == FuriStatusOk) { -#ifdef FURI_DEBUG - FURI_LOG_D(TAG, "Input event received: type=%d, key=%d", event.type, event.key); -#endif - if(event.type == InputTypeShort) { - switch(event.key) { - case InputKeyOk: - app->scanning = !app->scanning; - FURI_LOG_I(TAG, "Toggled scanning: %d", app->scanning); - break; - case InputKeyUp: - app->sensitivity += 1.0f; - FURI_LOG_I(TAG, "Increased sensitivity: %f", (double)app->sensitivity); - break; - case InputKeyDown: - app->sensitivity -= 1.0f; - FURI_LOG_I(TAG, "Decreased sensitivity: %f", (double)app->sensitivity); - break; - case InputKeyLeft: - app->scan_direction = ScanDirectionDown; - FURI_LOG_I(TAG, "Scan direction set to down"); - break; - case InputKeyRight: - app->scan_direction = ScanDirectionUp; - FURI_LOG_I(TAG, "Scan direction set to up"); - break; - case InputKeyBack: - app->running = false; - FURI_LOG_I(TAG, "Exiting app"); - break; - default: - FURI_LOG_I(TAG, "Unknown input"); - break; - } - } - } - - view_port_update(app->view_port); - furi_delay_ms(10); - } + view_dispatcher_run(app->view_dispatcher); radio_scanner_app_free(app); #ifdef FURI_DEBUG diff --git a/radio_scanner_app_i.c b/radio_scanner_app_i.c index 7fc95b9..638dc00 100644 --- a/radio_scanner_app_i.c +++ b/radio_scanner_app_i.c @@ -1,55 +1,5 @@ #include "radio_scanner_app_i.h" -/** - * Draw callback for updating the canvas UI. - * Displays the current frequency, RSSI, sensitivity, and scanning status. - */ -void radio_scanner_draw_callback(Canvas* canvas, void* context) { - furi_assert(canvas); - furi_assert(context); -#ifdef FURI_DEBUG - FURI_LOG_D(TAG, "Enter radio_scanner_draw_callback"); -#endif - RadioScannerApp* app = (RadioScannerApp*)context; - canvas_clear(canvas); - canvas_set_font(canvas, FontPrimary); - canvas_draw_str_aligned(canvas, 64, 2, AlignCenter, AlignTop, "Radio Scanner"); - - canvas_set_font(canvas, FontSecondary); - char freq_str[RADIO_SCANNER_BUFFER_SZ + 1] = {0}; - snprintf(freq_str, RADIO_SCANNER_BUFFER_SZ, "Freq: %.2f MHz", (double)app->frequency / 1000000); - canvas_draw_str_aligned(canvas, 64, 18, AlignCenter, AlignTop, freq_str); - - char rssi_str[RADIO_SCANNER_BUFFER_SZ + 1] = {0}; - snprintf(rssi_str, RADIO_SCANNER_BUFFER_SZ, "RSSI: %.2f", (double)app->rssi); - canvas_draw_str_aligned(canvas, 64, 30, AlignCenter, AlignTop, rssi_str); - - char sensitivity_str[RADIO_SCANNER_BUFFER_SZ + 1] = {0}; - snprintf(sensitivity_str, RADIO_SCANNER_BUFFER_SZ, "Sens: %.2f", (double)app->sensitivity); - canvas_draw_str_aligned(canvas, 64, 42, AlignCenter, AlignTop, sensitivity_str); - - canvas_draw_str_aligned( - canvas, 64, 54, AlignCenter, AlignTop, app->scanning ? "Scanning..." : "Locked"); -#ifdef FURI_DEBUG - FURI_LOG_D(TAG, "Exit radio_scanner_draw_callback"); -#endif -} - -/** - * Input callback for handling button events. - * Passes input events into the event queue for processing. - */ -void radio_scanner_input_callback(InputEvent* input_event, void* context) { - furi_assert(context); -#ifdef FURI_DEBUG - FURI_LOG_D(TAG, "Enter radio_scanner_input_callback"); - FURI_LOG_D(TAG, "Input event: type=%d, key=%d", input_event->type, input_event->key); -#endif - FuriMessageQueue* event_queue = context; - furi_message_queue_put(event_queue, input_event, FuriWaitForever); - FURI_LOG_D(TAG, "Exit radio_scanner_input_callback"); -} - /** * RX callback triggered on radio packet reception. * Currently unused beyond debug logging. @@ -238,3 +188,43 @@ void radio_scanner_process_scanning(RadioScannerApp* app) { FURI_LOG_D(TAG, "Exit radio_scanner_process_scanning"); #endif } + +/** + * Retrieves the current frequency value as a string. + */ +void radio_scanner_get_frequency_str(RadioScannerApp* app, FuriString* frequency_str) { + furi_assert(app); + if(frequency_str != NULL) { + furi_string_printf(frequency_str, "%.2f", (double)app->frequency / 1000000); + } +} + +/** + * Retrieves the current RSSI value as a string. + */ +void radio_scanner_get_rssi_str(RadioScannerApp* app, FuriString* rssi_str) { + furi_assert(app); + if(rssi_str != NULL) { + furi_string_printf(rssi_str, "%.2f", (double)app->rssi); + } +} + +/** + * Retrieves the current sensitivity value as a string. + */ +void radio_scanner_get_sensitivity_str(RadioScannerApp* app, FuriString* sensitivity_str) { + furi_assert(app); + if(sensitivity_str != NULL) { + furi_string_printf(sensitivity_str, "%.2f", (double)app->sensitivity); + } +} + +/** + * Retrieves the scanning status as a string. + */ +void radio_scanner_get_scanning_str(RadioScannerApp* app, FuriString* scanning_str) { + furi_assert(app); + if(scanning_str != NULL) { + furi_string_printf(scanning_str, app->scanning ? "Scanning..." : "Locked"); + } +} diff --git a/radio_scanner_app_i.h b/radio_scanner_app_i.h index 837d9f3..cbb1ff1 100644 --- a/radio_scanner_app_i.h +++ b/radio_scanner_app_i.h @@ -1,7 +1,13 @@ #pragma once +#include "scenes/radio_scanner_scene.h" +#include "views/scanner.h" + #include -#include +#include +#include +#include +#include #include #define TAG "RadioScannerApp" @@ -16,28 +22,44 @@ #define SUBGHZ_FREQUENCY_STEP 10000 #define SUBGHZ_DEVICE_NAME "cc1101_int" +/** + * Enumeration of view types used in the radio scanner app. + */ +typedef enum { + RadioScannerViewScanner, +} RadioScannerView; + +/** + * Enumeration representing the scanning direction. + */ typedef enum { ScanDirectionUp, ScanDirectionDown, } ScanDirection; +/** + * Main structure for the radio scanner app. + */ typedef struct { Gui* gui; - ViewPort* view_port; - FuriMessageQueue* event_queue; - bool running; uint32_t frequency; float rssi; + SceneManager* scene_manager; float sensitivity; bool scanning; ScanDirection scan_direction; + Scanner* scanner; const SubGhzDevice* radio_device; bool speaker_acquired; + ViewDispatcher* view_dispatcher; } RadioScannerApp; -void radio_scanner_draw_callback(Canvas* canvas, void* context); -void radio_scanner_input_callback(InputEvent* input_event, void* context); void radio_scanner_rx_callback(const void* data, size_t size, void* context); void radio_scanner_update_rssi(RadioScannerApp* app); bool radio_scanner_init_subghz(RadioScannerApp* app); void radio_scanner_process_scanning(RadioScannerApp* app); + +void radio_scanner_get_frequency_str(RadioScannerApp* app, FuriString* frequency_str); +void radio_scanner_get_rssi_str(RadioScannerApp* app, FuriString* rssi_str); +void radio_scanner_get_sensitivity_str(RadioScannerApp* app, FuriString* sensitivity_str); +void radio_scanner_get_scanning_str(RadioScannerApp* app, FuriString* scanning_str); diff --git a/scenes/radio_scanner_scene.c b/scenes/radio_scanner_scene.c new file mode 100644 index 0000000..1bd3e43 --- /dev/null +++ b/scenes/radio_scanner_scene.c @@ -0,0 +1,38 @@ +#include "../radio_scanner_app_i.h" + +/** + * Generate array of on_enter handler functions for each scene. + */ +#define ADD_SCENE(name, id) name##_scene_on_enter, +void (*const radio_scanner_scene_on_enter_handlers[])(void*) = { +#include "radio_scanner_scene_config.h" +}; +#undef ADD_SCENE + +/** + * Generate array of on_event handler functions for each scene. + */ +#define ADD_SCENE(name, id) name##_scene_on_event, +bool (*const radio_scanner_scene_on_event_handlers[])(void* context, SceneManagerEvent event) = { +#include "radio_scanner_scene_config.h" +}; +#undef ADD_SCENE + +/** + * Generate array of on_exit handler functions for each scene. + */ +#define ADD_SCENE(name, id) name##_scene_on_exit, +void (*const radio_scanner_scene_on_exit_handlers[])(void* context) = { +#include "radio_scanner_scene_config.h" +}; +#undef ADD_SCENE + +/** + * Scene manager handlers configuration structure for the radio scanner app. + */ +const SceneManagerHandlers radio_scanner_scene_handlers = { + .on_enter_handlers = radio_scanner_scene_on_enter_handlers, + .on_event_handlers = radio_scanner_scene_on_event_handlers, + .on_exit_handlers = radio_scanner_scene_on_exit_handlers, + .scene_num = RadioScannerSceneNum, +}; diff --git a/scenes/radio_scanner_scene.h b/scenes/radio_scanner_scene.h new file mode 100644 index 0000000..95d4a66 --- /dev/null +++ b/scenes/radio_scanner_scene.h @@ -0,0 +1,40 @@ +#pragma once + +#include + +/** + * Generate enumeration of radio scanner scenes and total number of scenes. + */ +#define ADD_SCENE(name, id) RadioScannerScene##id, +typedef enum { +#include "radio_scanner_scene_config.h" + RadioScannerSceneNum, +} RadioScannerScene; +#undef ADD_SCENE + +/** + * Scene manager handlers for the radio scanner app. + */ +extern const SceneManagerHandlers radio_scanner_scene_handlers; + +/** + * Generate declarations for scene on_enter handler functions. + */ +#define ADD_SCENE(name, id) void name##_scene_on_enter(void*); +#include "radio_scanner_scene_config.h" +#undef ADD_SCENE + +/** + * Generate declarations for scene on_event handler functions. + */ +#define ADD_SCENE(name, id) \ + bool name##_scene_on_event(void* context, SceneManagerEvent event); +#include "radio_scanner_scene_config.h" +#undef ADD_SCENE + +/** + * Generate declarations for scene on_exit handler functions. + */ +#define ADD_SCENE(name, id) void name##_scene_on_exit(void* context); +#include "radio_scanner_scene_config.h" +#undef ADD_SCENE diff --git a/scenes/radio_scanner_scene_config.h b/scenes/radio_scanner_scene_config.h new file mode 100644 index 0000000..4b04fbc --- /dev/null +++ b/scenes/radio_scanner_scene_config.h @@ -0,0 +1,5 @@ +/** + * Configuration for radio scanner scene handlers. + * Defines the available scenes and their corresponding handler identifiers. + */ +ADD_SCENE(scanner, Scanner) diff --git a/scenes/scanner.c b/scenes/scanner.c new file mode 100644 index 0000000..45d99bc --- /dev/null +++ b/scenes/scanner.c @@ -0,0 +1,131 @@ +#include "../radio_scanner_app_i.h" +#include "../views/scanner.h" + +/** + * Receiver callback for scanner scene events. + * Sends the received scanner event to the view dispatcher. + */ +void scanner_scene_receiver_callback(ScannerEvent event, void* context) { + furi_assert(context); + RadioScannerApp* app = context; + view_dispatcher_send_custom_event(app->view_dispatcher, event); +} + +/** + * Updates the scanner scene by fetching the latest frequency, RSSI, sensitivity, + * and scanning status, then refreshing the scanner view. + */ +static void scanner_scene_update(void* context) { + RadioScannerApp* app = context; + + FuriString* frequency_str; + FuriString* rssi_str; + FuriString* sensitivity_str; + FuriString* scanning_str; + + frequency_str = furi_string_alloc(); + rssi_str = furi_string_alloc(); + sensitivity_str = furi_string_alloc(); + scanning_str = furi_string_alloc(); + + radio_scanner_get_frequency_str(app, frequency_str); + radio_scanner_get_rssi_str(app, rssi_str); + radio_scanner_get_sensitivity_str(app, sensitivity_str); + radio_scanner_get_scanning_str(app, scanning_str); + + scanner_view_update( + app->scanner, + furi_string_get_cstr(frequency_str), + furi_string_get_cstr(rssi_str), + furi_string_get_cstr(sensitivity_str), + furi_string_get_cstr(scanning_str) + ); + + furi_string_free(frequency_str); + furi_string_free(rssi_str); + furi_string_free(sensitivity_str); + furi_string_free(scanning_str); +} + +/** + * Handler called when entering the scanner scene. + * Sets the scanner view callback and switches the view to the scanner view. + */ +void scanner_scene_on_enter(void* context) { + RadioScannerApp* app = context; + + scanner_view_set_callback(app->scanner, scanner_scene_receiver_callback, app); + + view_dispatcher_switch_to_view(app->view_dispatcher, RadioScannerViewScanner); +} + +/** + * Handles events for the scanner scene. + * Processes custom events and tick events. + */ +bool scanner_scene_on_event(void* context, SceneManagerEvent event) { + UNUSED(event); + RadioScannerApp* app = context; + + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + switch(event.event) { + // Scanning + case ScannerEventScanDirectionDown: + app->scan_direction = ScanDirectionDown; + FURI_LOG_I(TAG, "Scan direction set to down"); + consumed = true; + break; + case ScannerEventScanDirectionUp: + app->scan_direction = ScanDirectionUp; + FURI_LOG_I(TAG, "Scan direction set to up"); + consumed = true; + break; + case ScannerEventToggleScanning: + app->scanning = !app->scanning; + FURI_LOG_I(TAG, "Toggled scanning: %d", app->scanning); + consumed = true; + break; + // Sensitivity + case ScannerEventDecreaseSensitivity: + app->sensitivity -= 1.0f; + FURI_LOG_I(TAG, "Decreased sensitivity: %f", (double)app->sensitivity); + consumed = true; + break; + case ScannerEventIncreaseSensitivity: + app->sensitivity += 1.0f; + FURI_LOG_I(TAG, "Increased sensitivity: %f", (double)app->sensitivity); + consumed = true; + break; + default: + FURI_LOG_I(TAG, "Unknown event"); + break; + } + } else if(event.type == SceneManagerEventTypeTick) { + if(app->scanning) { + #ifdef FURI_DEBUG + FURI_LOG_D(TAG, "Scanning is active"); + #endif + radio_scanner_process_scanning(app); + } else { + #ifdef FURI_DEBUG + FURI_LOG_D(TAG, "Scanning is inactive, updating RSSI"); + #endif + radio_scanner_update_rssi(app); + } + + scanner_scene_update(app); + + consumed = true; + } + + return consumed; +} + +/** + * Handler called when exiting the scanner scene. + */ +void scanner_scene_on_exit(void* context) { + UNUSED(context); +} diff --git a/views/scanner.c b/views/scanner.c new file mode 100644 index 0000000..9c0025f --- /dev/null +++ b/views/scanner.c @@ -0,0 +1,182 @@ +#include "scanner.h" +#include "../radio_scanner_app_i.h" + +#include + +/** + * Sets the callback function for scanner view events. + */ +void scanner_view_set_callback(Scanner* scanner, ScannerCallback callback, void* context) { + furi_assert(scanner); + furi_assert(callback); + scanner->callback = callback; + scanner->context = context; +} + +/** + * Retrieves the view associated with the scanner. + */ +View* scanner_view_get_view(Scanner* scanner) { + furi_assert(scanner); + return scanner->view; +} + +/** + * Updates the scanner view with new frequency, RSSI, sensitivity, and scanning status strings. + */ +void scanner_view_update(Scanner* scanner, const char* frequency_str, const char* rssi_str, const char* sensitivity_str, const char* scanning_str) { + furi_assert(scanner); + with_view_model( + scanner->view, + ScannerModel* model, + { + furi_string_set_str(model->frequency_str, frequency_str); + furi_string_set_str(model->rssi_str, rssi_str); + furi_string_set_str(model->sensitivity_str, sensitivity_str); + furi_string_set_str(model->scanning_str, scanning_str); + }, + true); +} + +/** + * Draw callback for updating the canvas UI. + * Displays the current frequency, RSSI, sensitivity, and scanning status. + */ +void scanner_view_draw(Canvas* canvas, ScannerModel* model) { + furi_assert(canvas); + furi_assert(model); +#ifdef FURI_DEBUG + FURI_LOG_D(TAG, "Enter scanner_view_draw"); +#endif + canvas_clear(canvas); + canvas_set_font(canvas, FontPrimary); + canvas_draw_str_aligned(canvas, 64, 2, AlignCenter, AlignTop, "Radio Scanner"); + + canvas_set_font(canvas, FontSecondary); + char freq_str[RADIO_SCANNER_BUFFER_SZ + 1] = {0}; + snprintf(freq_str, RADIO_SCANNER_BUFFER_SZ, "Freq: %s MHz", furi_string_get_cstr(model->frequency_str)); + canvas_draw_str_aligned(canvas, 64, 18, AlignCenter, AlignTop, freq_str); + + char rssi_str[RADIO_SCANNER_BUFFER_SZ + 1] = {0}; + snprintf(rssi_str, RADIO_SCANNER_BUFFER_SZ, "RSSI: %s", furi_string_get_cstr(model->rssi_str)); + canvas_draw_str_aligned(canvas, 64, 30, AlignCenter, AlignTop, rssi_str); + + char sensitivity_str[RADIO_SCANNER_BUFFER_SZ + 1] = {0}; + snprintf(sensitivity_str, RADIO_SCANNER_BUFFER_SZ, "Sens: %s", furi_string_get_cstr(model->sensitivity_str)); + canvas_draw_str_aligned(canvas, 64, 42, AlignCenter, AlignTop, sensitivity_str); + + canvas_draw_str_aligned(canvas, 64, 54, AlignCenter, AlignTop, furi_string_get_cstr(model->scanning_str)); +#ifdef FURI_DEBUG + FURI_LOG_D(TAG, "Exit scanner_view_draw"); +#endif +} + +/** + * Input callback for handling button events. + * Passes input events into the event queue for processing. + */ +bool scanner_view_input(InputEvent* event, void* context) { + furi_assert(context); + Scanner* scanner = context; +#ifdef FURI_DEBUG + FURI_LOG_D(TAG, "Enter scanner_view_input"); + FURI_LOG_D(TAG, "Input event: type=%d, key=%d", event->type, event->key); +#endif + bool consumed = false; + if(event->type == InputTypeShort) { + switch(event->key) { + case InputKeyOk: + scanner->callback(ScannerEventToggleScanning, scanner->context); + consumed = true; + break; + case InputKeyUp: + scanner->callback(ScannerEventIncreaseSensitivity, scanner->context); + consumed = true; + break; + case InputKeyDown: + scanner->callback(ScannerEventDecreaseSensitivity, scanner->context); + consumed = true; + break; + case InputKeyLeft: + scanner->callback(ScannerEventScanDirectionDown, scanner->context); + consumed = true; + break; + case InputKeyRight: + scanner->callback(ScannerEventScanDirectionUp, scanner->context); + consumed = true; + break; + default: + FURI_LOG_I(TAG, "Unknown input"); + break; + } + } + FURI_LOG_D(TAG, "Exit scanner_view_input"); + return consumed; +} + +/** + * Called when entering the scanner view. + */ +void scanner_view_enter(void* context) { + furi_assert(context); +} + +/** + * Called when exiting the scanner view. + */ +void scanner_view_exit(void* context) { + furi_assert(context); +} + +/** + * Allocates and initializes a new Scanner instance. + * Sets up the view, model, and callbacks. + */ +Scanner* scanner_view_alloc() { + Scanner* scanner = malloc(sizeof(Scanner)); + + scanner->view = view_alloc(); + + view_allocate_model(scanner->view, ViewModelTypeLocking, sizeof(ScannerModel)); + view_set_context(scanner->view, scanner); + view_set_draw_callback(scanner->view, (ViewDrawCallback)scanner_view_draw); + view_set_input_callback(scanner->view, scanner_view_input); + view_set_enter_callback(scanner->view, scanner_view_enter); + view_set_exit_callback(scanner->view, scanner_view_exit); + + with_view_model( + scanner->view, + ScannerModel* model, + { + model->frequency_str = furi_string_alloc(); + model->rssi_str = furi_string_alloc(); + model->sensitivity_str = furi_string_alloc(); + model->scanning_str = furi_string_alloc(); + }, + true + ); + + return scanner; +} + +/** + * Frees the resources associated with the Scanner instance. + */ +void scanner_view_free(Scanner* scanner) { + furi_assert(scanner); + + with_view_model( + scanner->view, + ScannerModel * model, + { + furi_string_free(model->frequency_str); + furi_string_free(model->rssi_str); + furi_string_free(model->sensitivity_str); + furi_string_free(model->scanning_str); + }, + false); + + view_free(scanner->view); + + free(scanner); +} diff --git a/views/scanner.h b/views/scanner.h new file mode 100644 index 0000000..5ce10f7 --- /dev/null +++ b/views/scanner.h @@ -0,0 +1,44 @@ +#pragma once + +#include "../helpers/scanner_event.h" + +#include + +/** + * Forward declaration for the Scanner structure. + * Represents a scanner view with its associated callbacks and context. + */ +typedef struct Scanner Scanner; + +/** + * Function pointer type for scanner event callbacks. + */ +typedef void (*ScannerCallback)(ScannerEvent event, void* context); + +/** + * Structure representing the scanner view. + */ +struct Scanner { + View* view; + ScannerCallback callback; + void* context; +}; + +/** + * Data model for the scanner view UI. + */ +typedef struct { + FuriString* frequency_str; + FuriString* rssi_str; + FuriString* sensitivity_str; + FuriString* scanning_str; +} ScannerModel; + +void scanner_view_set_callback(Scanner* scanner, ScannerCallback callback, void* context); + +View* scanner_view_get_view(Scanner* scanner); + +void scanner_view_update(Scanner* scanner, const char* frequency_str, const char* rssi_str, const char* sensitivity_str, const char* scanning_str); + +Scanner* scanner_view_alloc(); +void scanner_view_free(Scanner* scanner); From d9014ab686a9a946fc611fb6787ce5d26e1937c9 Mon Sep 17 00:00:00 2001 From: "Calvin M.T." Date: Tue, 1 Apr 2025 22:05:08 +0200 Subject: [PATCH 06/13] Feature config scene --- helpers/scanner_event.h | 2 ++ radio_scanner_app.c | 8 +++++++ radio_scanner_app_i.h | 3 +++ scenes/config.c | 37 +++++++++++++++++++++++++++++ scenes/radio_scanner_scene_config.h | 1 + scenes/scanner.c | 5 ++++ views/scanner.c | 5 +++- 7 files changed, 60 insertions(+), 1 deletion(-) create mode 100644 scenes/config.c diff --git a/helpers/scanner_event.h b/helpers/scanner_event.h index d40a38c..8ffe1d9 100644 --- a/helpers/scanner_event.h +++ b/helpers/scanner_event.h @@ -11,4 +11,6 @@ typedef enum { // Sensitivity ScannerEventDecreaseSensitivity, ScannerEventIncreaseSensitivity, + // Views + ScannerEventViewConfig, } ScannerEvent; diff --git a/radio_scanner_app.c b/radio_scanner_app.c index 54d3a10..c7f724b 100644 --- a/radio_scanner_app.c +++ b/radio_scanner_app.c @@ -67,6 +67,10 @@ RadioScannerApp* radio_scanner_app_alloc() { view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen); + // Config + app->config = variable_item_list_alloc(); + view_dispatcher_add_view(app->view_dispatcher, RadioScannerViewConfig, variable_item_list_get_view(app->config)); + // Scanner app->scanner = scanner_view_alloc(); view_dispatcher_add_view(app->view_dispatcher, RadioScannerViewScanner, scanner_view_get_view(app->scanner)); @@ -122,6 +126,10 @@ void radio_scanner_app_free(RadioScannerApp* app) { subghz_devices_deinit(); + // Config + view_dispatcher_remove_view(app->view_dispatcher, RadioScannerViewConfig); + variable_item_list_free(app->config); + // Scanner view_dispatcher_remove_view(app->view_dispatcher, RadioScannerViewScanner); scanner_view_free(app->scanner); diff --git a/radio_scanner_app_i.h b/radio_scanner_app_i.h index cbb1ff1..fa983d4 100644 --- a/radio_scanner_app_i.h +++ b/radio_scanner_app_i.h @@ -3,6 +3,7 @@ #include "scenes/radio_scanner_scene.h" #include "views/scanner.h" +#include #include #include #include @@ -26,6 +27,7 @@ * Enumeration of view types used in the radio scanner app. */ typedef enum { + RadioScannerViewConfig, RadioScannerViewScanner, } RadioScannerView; @@ -41,6 +43,7 @@ typedef enum { * Main structure for the radio scanner app. */ typedef struct { + VariableItemList* config; Gui* gui; uint32_t frequency; float rssi; diff --git a/scenes/config.c b/scenes/config.c new file mode 100644 index 0000000..3966aeb --- /dev/null +++ b/scenes/config.c @@ -0,0 +1,37 @@ +#include "../radio_scanner_app_i.h" + +/** + * Enumeration of configuration indices used for accessing specific settings. + */ +enum ConfigIndex { + null +}; + +/** + * Handler called when entering the config scene. + * Sets the config view callback and switches the view to the config view. + */ +void config_scene_on_enter(void* context) { + RadioScannerApp* app = context; + + view_dispatcher_switch_to_view(app->view_dispatcher, RadioScannerViewConfig); +} + +/** + * Handles events for the config scene. + * Processes custom events. + */ +bool config_scene_on_event(void* context, SceneManagerEvent event) { + UNUSED(context); + UNUSED(event); + return false; +} + +/** + * Handler called when exiting the config scene. + */ +void config_scene_on_exit(void* context) { + RadioScannerApp* app = context; + variable_item_list_set_selected_item(app->config, 0); + variable_item_list_reset(app->config); +} diff --git a/scenes/radio_scanner_scene_config.h b/scenes/radio_scanner_scene_config.h index 4b04fbc..deab509 100644 --- a/scenes/radio_scanner_scene_config.h +++ b/scenes/radio_scanner_scene_config.h @@ -2,4 +2,5 @@ * Configuration for radio scanner scene handlers. * Defines the available scenes and their corresponding handler identifiers. */ +ADD_SCENE(config, Config) ADD_SCENE(scanner, Scanner) diff --git a/scenes/scanner.c b/scenes/scanner.c index 45d99bc..06f1428 100644 --- a/scenes/scanner.c +++ b/scenes/scanner.c @@ -98,6 +98,11 @@ bool scanner_scene_on_event(void* context, SceneManagerEvent event) { FURI_LOG_I(TAG, "Increased sensitivity: %f", (double)app->sensitivity); consumed = true; break; + // Views + case ScannerEventViewConfig: + scene_manager_next_scene(app->scene_manager, RadioScannerSceneConfig); + consumed = true; + break; default: FURI_LOG_I(TAG, "Unknown event"); break; diff --git a/views/scanner.c b/views/scanner.c index 9c0025f..922143c 100644 --- a/views/scanner.c +++ b/views/scanner.c @@ -50,6 +50,9 @@ void scanner_view_draw(Canvas* canvas, ScannerModel* model) { #endif canvas_clear(canvas); canvas_set_font(canvas, FontPrimary); + + elements_button_left(canvas, "Config"); + canvas_draw_str_aligned(canvas, 64, 2, AlignCenter, AlignTop, "Radio Scanner"); canvas_set_font(canvas, FontSecondary); @@ -98,7 +101,7 @@ bool scanner_view_input(InputEvent* event, void* context) { consumed = true; break; case InputKeyLeft: - scanner->callback(ScannerEventScanDirectionDown, scanner->context); + scanner->callback(ScannerEventViewConfig, scanner->context); consumed = true; break; case InputKeyRight: From dc0fb31e42402fb68439105f45ca6501b744e70b Mon Sep 17 00:00:00 2001 From: "Calvin M.T." Date: Thu, 3 Apr 2025 21:35:56 +0200 Subject: [PATCH 07/13] Feature sound state config --- helpers/radio_scanner_states.h | 6 +++ radio_scanner_app.c | 1 + radio_scanner_app_i.c | 6 ++- radio_scanner_app_i.h | 2 + scenes/config.c | 68 +++++++++++++++++++++++++++++++++- 5 files changed, 81 insertions(+), 2 deletions(-) create mode 100644 helpers/radio_scanner_states.h diff --git a/helpers/radio_scanner_states.h b/helpers/radio_scanner_states.h new file mode 100644 index 0000000..d8e6e23 --- /dev/null +++ b/helpers/radio_scanner_states.h @@ -0,0 +1,6 @@ +// Enumeration of the possible states of sound, ending with the number of sound states. +typedef enum { + SoundStateOFF, + SoundStateON, + SoundStateNum +} SoundState; diff --git a/radio_scanner_app.c b/radio_scanner_app.c index c7f724b..6938a7e 100644 --- a/radio_scanner_app.c +++ b/radio_scanner_app.c @@ -81,6 +81,7 @@ RadioScannerApp* radio_scanner_app_alloc() { app->sensitivity = RADIO_SCANNER_DEFAULT_SENSITIVITY; app->scanning = true; app->scan_direction = ScanDirectionUp; + app->sound_state = SoundStateOFF; app->speaker_acquired = false; app->radio_device = NULL; diff --git a/radio_scanner_app_i.c b/radio_scanner_app_i.c index 638dc00..99512b5 100644 --- a/radio_scanner_app_i.c +++ b/radio_scanner_app_i.c @@ -85,7 +85,11 @@ bool radio_scanner_init_subghz(RadioScannerApp* app) { #endif if(furi_hal_speaker_acquire(30)) { app->speaker_acquired = true; - subghz_devices_set_async_mirror_pin(device, &gpio_speaker); + if(app->sound_state == SoundStateON) { + subghz_devices_set_async_mirror_pin(device, &gpio_speaker); + } else { + subghz_devices_set_async_mirror_pin(device, NULL); + } #ifdef FURI_DEBUG FURI_LOG_D(TAG, "Speaker acquired and async mirror pin set"); #endif diff --git a/radio_scanner_app_i.h b/radio_scanner_app_i.h index fa983d4..adbe1d1 100644 --- a/radio_scanner_app_i.h +++ b/radio_scanner_app_i.h @@ -1,5 +1,6 @@ #pragma once +#include "helpers/radio_scanner_states.h" #include "scenes/radio_scanner_scene.h" #include "views/scanner.h" @@ -53,6 +54,7 @@ typedef struct { ScanDirection scan_direction; Scanner* scanner; const SubGhzDevice* radio_device; + SoundState sound_state; bool speaker_acquired; ViewDispatcher* view_dispatcher; } RadioScannerApp; diff --git a/scenes/config.c b/scenes/config.c index 3966aeb..7bb656d 100644 --- a/scenes/config.c +++ b/scenes/config.c @@ -4,15 +4,81 @@ * Enumeration of configuration indices used for accessing specific settings. */ enum ConfigIndex { - null + ConfigIndexSound, +}; + +// Text labels for the sound state values. +const char* const sound_state_text[SoundStateNum] = { + "OFF", + "ON", +}; + +// Corresponding enum/int values for sound states. +const uint32_t sound_state_value[SoundStateNum] = { + SoundStateOFF, + SoundStateON, }; +/** + * Callback to update the sound state when changed in the config UI. + */ +static void config_scene_set_sound_state(VariableItem* item) { + RadioScannerApp* app = variable_item_get_context(item); + uint8_t index = variable_item_get_current_value_index(item); + + variable_item_set_current_value_text(item, sound_state_text[index]); + + app->sound_state = sound_state_value[index]; + + if(app->speaker_acquired) { + // Stop RX before changing the mirror pin + subghz_devices_stop_async_rx(app->radio_device); + // Update the mirror pin based on the sound state + if(app->sound_state == SoundStateON) { + subghz_devices_set_async_mirror_pin(app->radio_device, &gpio_speaker); + } else { + subghz_devices_set_async_mirror_pin(app->radio_device, NULL); + } + // Restart RX + subghz_devices_start_async_rx(app->radio_device, radio_scanner_rx_callback, app); + } +} + +/** + * Utility function to get the index of a sound state value. + */ +uint8_t config_scene_sound_value_index(const uint32_t value, const uint32_t values[], uint8_t values_count, void* context) { + furi_assert(context); + UNUSED(values_count); + UNUSED(context); + + if(value == values[0]) { + return 0; + } else { + return 1; + } +} + /** * Handler called when entering the config scene. * Sets the config view callback and switches the view to the config view. */ void config_scene_on_enter(void* context) { RadioScannerApp* app = context; + VariableItem* item; + uint8_t value_index; + + // Sound + item = variable_item_list_add( + app->config, + "Sound:", + SoundStateNum, + config_scene_set_sound_state, + app + ); + value_index = config_scene_sound_value_index(app->sound_state, sound_state_value, SoundStateNum, app); + variable_item_set_current_value_index(item, value_index); + variable_item_set_current_value_text(item, sound_state_text[value_index]); view_dispatcher_switch_to_view(app->view_dispatcher, RadioScannerViewConfig); } From 3572f6fd8c3d69b4b05115c2991d06b6e24dcd19 Mon Sep 17 00:00:00 2001 From: "Calvin M.T." Date: Thu, 10 Apr 2025 20:43:55 +0200 Subject: [PATCH 08/13] Feature sensitivity config --- scenes/config.c | 51 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/scenes/config.c b/scenes/config.c index 7bb656d..61a45e3 100644 --- a/scenes/config.c +++ b/scenes/config.c @@ -5,6 +5,7 @@ */ enum ConfigIndex { ConfigIndexSound, + ConfigIndexSensitivity, }; // Text labels for the sound state values. @@ -59,6 +60,44 @@ uint8_t config_scene_sound_value_index(const uint32_t value, const uint32_t valu } } +/** + * Helper function to update the sensitivity display text. + */ +static void config_scene_set_sensitivity_display_text(VariableItem* item, float sensitivity) { + char buf[8]; + snprintf(buf, sizeof(buf), "%.1f", (double)sensitivity); + variable_item_set_current_value_text(item, buf); +} + +/** + * Helper function to compute the configuration index based on the current sensitivity value. + * + * Sensitivity is a float where 0.0 corresponds to index 0 and -127.0 corresponds to index 127. + * This function clamps the value to the allowed range and returns the proper index. + */ +static uint8_t config_scene_sensitivity_value_index(float sensitivity) { + if(sensitivity > 0.0f) { + sensitivity = 0.0f; + } else if(sensitivity < -127.0f) { + sensitivity = -127.0f; + } + return (uint8_t)(sensitivity + 127.0f); +} + +/** + * Callback to update the sensitivity value when changed in the config UI. + */ +static void config_scene_set_sensitivity(VariableItem* item) { + RadioScannerApp* app = variable_item_get_context(item); + uint8_t index = variable_item_get_current_value_index(item); + + // Calculate sensitivity: index 0 = -127.0, index 1 = -126.0, ..., index 127 = 0.0 + float sensitivity = -127.0f + (float)index; + app->sensitivity = sensitivity; + + config_scene_set_sensitivity_display_text(item, sensitivity); +} + /** * Handler called when entering the config scene. * Sets the config view callback and switches the view to the config view. @@ -80,6 +119,18 @@ void config_scene_on_enter(void* context) { variable_item_set_current_value_index(item, value_index); variable_item_set_current_value_text(item, sound_state_text[value_index]); + // Sensitivity + item = variable_item_list_add( + app->config, + "Sensitivity:", + 128, + config_scene_set_sensitivity, + app + ); + uint8_t sensitivity_index = config_scene_sensitivity_value_index(app->sensitivity); + variable_item_set_current_value_index(item, sensitivity_index); + config_scene_set_sensitivity_display_text(item, app->sensitivity); + view_dispatcher_switch_to_view(app->view_dispatcher, RadioScannerViewConfig); } From c019d120db2d4b876694d93d08945dbb1481cb00 Mon Sep 17 00:00:00 2001 From: "Calvin M.T." Date: Thu, 10 Apr 2025 20:45:09 +0200 Subject: [PATCH 09/13] Remove sensitivity input key events --- helpers/scanner_event.h | 3 --- scenes/scanner.c | 11 ----------- views/scanner.c | 8 -------- 3 files changed, 22 deletions(-) diff --git a/helpers/scanner_event.h b/helpers/scanner_event.h index 8ffe1d9..44abc64 100644 --- a/helpers/scanner_event.h +++ b/helpers/scanner_event.h @@ -8,9 +8,6 @@ typedef enum { ScannerEventScanDirectionDown, ScannerEventScanDirectionUp, ScannerEventToggleScanning, - // Sensitivity - ScannerEventDecreaseSensitivity, - ScannerEventIncreaseSensitivity, // Views ScannerEventViewConfig, } ScannerEvent; diff --git a/scenes/scanner.c b/scenes/scanner.c index 06f1428..22bea2a 100644 --- a/scenes/scanner.c +++ b/scenes/scanner.c @@ -87,17 +87,6 @@ bool scanner_scene_on_event(void* context, SceneManagerEvent event) { FURI_LOG_I(TAG, "Toggled scanning: %d", app->scanning); consumed = true; break; - // Sensitivity - case ScannerEventDecreaseSensitivity: - app->sensitivity -= 1.0f; - FURI_LOG_I(TAG, "Decreased sensitivity: %f", (double)app->sensitivity); - consumed = true; - break; - case ScannerEventIncreaseSensitivity: - app->sensitivity += 1.0f; - FURI_LOG_I(TAG, "Increased sensitivity: %f", (double)app->sensitivity); - consumed = true; - break; // Views case ScannerEventViewConfig: scene_manager_next_scene(app->scene_manager, RadioScannerSceneConfig); diff --git a/views/scanner.c b/views/scanner.c index 922143c..86beb09 100644 --- a/views/scanner.c +++ b/views/scanner.c @@ -92,14 +92,6 @@ bool scanner_view_input(InputEvent* event, void* context) { scanner->callback(ScannerEventToggleScanning, scanner->context); consumed = true; break; - case InputKeyUp: - scanner->callback(ScannerEventIncreaseSensitivity, scanner->context); - consumed = true; - break; - case InputKeyDown: - scanner->callback(ScannerEventDecreaseSensitivity, scanner->context); - consumed = true; - break; case InputKeyLeft: scanner->callback(ScannerEventViewConfig, scanner->context); consumed = true; From 6bf2939b710cb4b5c889c16683b50ef78cc47848 Mon Sep 17 00:00:00 2001 From: "Calvin M.T." Date: Thu, 10 Apr 2025 20:58:19 +0200 Subject: [PATCH 10/13] Update scan direction input key events --- views/scanner.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/views/scanner.c b/views/scanner.c index 86beb09..1984f08 100644 --- a/views/scanner.c +++ b/views/scanner.c @@ -96,10 +96,14 @@ bool scanner_view_input(InputEvent* event, void* context) { scanner->callback(ScannerEventViewConfig, scanner->context); consumed = true; break; - case InputKeyRight: + case InputKeyUp: scanner->callback(ScannerEventScanDirectionUp, scanner->context); consumed = true; break; + case InputKeyDown: + scanner->callback(ScannerEventScanDirectionDown, scanner->context); + consumed = true; + break; default: FURI_LOG_I(TAG, "Unknown input"); break; From 48f91890edc98f7f767237abc359ed1e5e3fedaf Mon Sep 17 00:00:00 2001 From: "Calvin M.T." Date: Fri, 11 Apr 2025 19:30:59 +0200 Subject: [PATCH 11/13] Feature squelch sound state --- helpers/radio_scanner_states.h | 1 + radio_scanner_app.c | 2 +- radio_scanner_app_i.c | 26 ++++++++++++++++++++++++++ radio_scanner_app_i.h | 1 + scenes/config.c | 13 ++++++++----- scenes/scanner.c | 7 +++++++ 6 files changed, 44 insertions(+), 6 deletions(-) diff --git a/helpers/radio_scanner_states.h b/helpers/radio_scanner_states.h index d8e6e23..fc3afab 100644 --- a/helpers/radio_scanner_states.h +++ b/helpers/radio_scanner_states.h @@ -2,5 +2,6 @@ typedef enum { SoundStateOFF, SoundStateON, + SoundStateSquelch, SoundStateNum } SoundState; diff --git a/radio_scanner_app.c b/radio_scanner_app.c index 6938a7e..6bd085c 100644 --- a/radio_scanner_app.c +++ b/radio_scanner_app.c @@ -81,7 +81,7 @@ RadioScannerApp* radio_scanner_app_alloc() { app->sensitivity = RADIO_SCANNER_DEFAULT_SENSITIVITY; app->scanning = true; app->scan_direction = ScanDirectionUp; - app->sound_state = SoundStateOFF; + app->sound_state = SoundStateSquelch; app->speaker_acquired = false; app->radio_device = NULL; diff --git a/radio_scanner_app_i.c b/radio_scanner_app_i.c index 99512b5..15b8940 100644 --- a/radio_scanner_app_i.c +++ b/radio_scanner_app_i.c @@ -193,6 +193,32 @@ void radio_scanner_process_scanning(RadioScannerApp* app) { #endif } +/** + * Updates the squelch state by checking the current RSSI against the configured sensitivity threshold. + */ +void radio_scanner_update_squelch(RadioScannerApp* app) { + if(app->speaker_acquired && app->sound_state == SoundStateSquelch) { + bool signal_detected = (app->rssi > app->sensitivity); + + if(signal_detected) { + subghz_devices_stop_async_rx(app->radio_device); + subghz_devices_set_async_mirror_pin(app->radio_device, &gpio_speaker); + subghz_devices_start_async_rx(app->radio_device, radio_scanner_rx_callback, app); +#ifdef FURI_DEBUG + FURI_LOG_D(TAG, "Squelch activated: enabling audio output"); +#endif + } + else { + subghz_devices_stop_async_rx(app->radio_device); + subghz_devices_set_async_mirror_pin(app->radio_device, NULL); + subghz_devices_start_async_rx(app->radio_device, radio_scanner_rx_callback, app); +#ifdef FURI_DEBUG + FURI_LOG_D(TAG, "Squelch deactivated: disabling audio output"); +#endif + } + } +} + /** * Retrieves the current frequency value as a string. */ diff --git a/radio_scanner_app_i.h b/radio_scanner_app_i.h index adbe1d1..1531eb3 100644 --- a/radio_scanner_app_i.h +++ b/radio_scanner_app_i.h @@ -63,6 +63,7 @@ void radio_scanner_rx_callback(const void* data, size_t size, void* context); void radio_scanner_update_rssi(RadioScannerApp* app); bool radio_scanner_init_subghz(RadioScannerApp* app); void radio_scanner_process_scanning(RadioScannerApp* app); +void radio_scanner_update_squelch(RadioScannerApp* app); void radio_scanner_get_frequency_str(RadioScannerApp* app, FuriString* frequency_str); void radio_scanner_get_rssi_str(RadioScannerApp* app, FuriString* rssi_str); diff --git a/scenes/config.c b/scenes/config.c index 61a45e3..27e9e02 100644 --- a/scenes/config.c +++ b/scenes/config.c @@ -11,12 +11,14 @@ enum ConfigIndex { // Text labels for the sound state values. const char* const sound_state_text[SoundStateNum] = { "OFF", + "SQLCH", "ON", }; // Corresponding enum/int values for sound states. const uint32_t sound_state_value[SoundStateNum] = { SoundStateOFF, + SoundStateSquelch, SoundStateON, }; @@ -50,14 +52,15 @@ static void config_scene_set_sound_state(VariableItem* item) { */ uint8_t config_scene_sound_value_index(const uint32_t value, const uint32_t values[], uint8_t values_count, void* context) { furi_assert(context); - UNUSED(values_count); UNUSED(context); - if(value == values[0]) { - return 0; - } else { - return 1; + for(uint8_t i = 0; i < values_count; i++){ + if(values[i] == value) { + return i; + } } + + return 0; } /** diff --git a/scenes/scanner.c b/scenes/scanner.c index 22bea2a..192621f 100644 --- a/scenes/scanner.c +++ b/scenes/scanner.c @@ -109,6 +109,13 @@ bool scanner_scene_on_event(void* context, SceneManagerEvent event) { radio_scanner_update_rssi(app); } + if(app->sound_state == SoundStateSquelch) { + #ifdef FURI_DEBUG + FURI_LOG_D(TAG, "Squelch is active, updating sound output"); + #endif + radio_scanner_update_squelch(app); + } + scanner_scene_update(app); consumed = true; From 8c6e225edf0a47b12c599d6aa007bf98e665b081 Mon Sep 17 00:00:00 2001 From: "Calvin M.T." Date: Fri, 11 Apr 2025 21:43:26 +0200 Subject: [PATCH 12/13] Add scan direction string to scanner model --- radio_scanner_app_i.c | 10 ++++++++++ radio_scanner_app_i.h | 1 + scenes/scanner.c | 5 +++++ views/scanner.c | 5 ++++- views/scanner.h | 3 ++- 5 files changed, 22 insertions(+), 2 deletions(-) diff --git a/radio_scanner_app_i.c b/radio_scanner_app_i.c index 15b8940..412fe8c 100644 --- a/radio_scanner_app_i.c +++ b/radio_scanner_app_i.c @@ -249,6 +249,16 @@ void radio_scanner_get_sensitivity_str(RadioScannerApp* app, FuriString* sensiti } } +/** + * Retrieves the scan direction as a string. + */ +void radio_scanner_get_scan_direction_str(RadioScannerApp* app, FuriString* scan_direction_str) { + furi_assert(app); + if(scan_direction_str != NULL) { + furi_string_printf(scan_direction_str, app->scan_direction == ScanDirectionUp ? "+" : "-"); + } +} + /** * Retrieves the scanning status as a string. */ diff --git a/radio_scanner_app_i.h b/radio_scanner_app_i.h index 1531eb3..03d8cdc 100644 --- a/radio_scanner_app_i.h +++ b/radio_scanner_app_i.h @@ -68,4 +68,5 @@ void radio_scanner_update_squelch(RadioScannerApp* app); void radio_scanner_get_frequency_str(RadioScannerApp* app, FuriString* frequency_str); void radio_scanner_get_rssi_str(RadioScannerApp* app, FuriString* rssi_str); void radio_scanner_get_sensitivity_str(RadioScannerApp* app, FuriString* sensitivity_str); +void radio_scanner_get_scan_direction_str(RadioScannerApp* app, FuriString* scan_direction_str); void radio_scanner_get_scanning_str(RadioScannerApp* app, FuriString* scanning_str); diff --git a/scenes/scanner.c b/scenes/scanner.c index 192621f..81a8ee8 100644 --- a/scenes/scanner.c +++ b/scenes/scanner.c @@ -21,16 +21,19 @@ static void scanner_scene_update(void* context) { FuriString* frequency_str; FuriString* rssi_str; FuriString* sensitivity_str; + FuriString* scan_direction_str; FuriString* scanning_str; frequency_str = furi_string_alloc(); rssi_str = furi_string_alloc(); sensitivity_str = furi_string_alloc(); + scan_direction_str = furi_string_alloc(); scanning_str = furi_string_alloc(); radio_scanner_get_frequency_str(app, frequency_str); radio_scanner_get_rssi_str(app, rssi_str); radio_scanner_get_sensitivity_str(app, sensitivity_str); + radio_scanner_get_scan_direction_str(app, scan_direction_str); radio_scanner_get_scanning_str(app, scanning_str); scanner_view_update( @@ -38,12 +41,14 @@ static void scanner_scene_update(void* context) { furi_string_get_cstr(frequency_str), furi_string_get_cstr(rssi_str), furi_string_get_cstr(sensitivity_str), + furi_string_get_cstr(scan_direction_str), furi_string_get_cstr(scanning_str) ); furi_string_free(frequency_str); furi_string_free(rssi_str); furi_string_free(sensitivity_str); + furi_string_free(scan_direction_str); furi_string_free(scanning_str); } diff --git a/views/scanner.c b/views/scanner.c index 1984f08..a70d4b3 100644 --- a/views/scanner.c +++ b/views/scanner.c @@ -24,7 +24,7 @@ View* scanner_view_get_view(Scanner* scanner) { /** * Updates the scanner view with new frequency, RSSI, sensitivity, and scanning status strings. */ -void scanner_view_update(Scanner* scanner, const char* frequency_str, const char* rssi_str, const char* sensitivity_str, const char* scanning_str) { +void scanner_view_update(Scanner* scanner, const char* frequency_str, const char* rssi_str, const char* sensitivity_str, const char* scan_direction_str, const char* scanning_str) { furi_assert(scanner); with_view_model( scanner->view, @@ -33,6 +33,7 @@ void scanner_view_update(Scanner* scanner, const char* frequency_str, const char furi_string_set_str(model->frequency_str, frequency_str); furi_string_set_str(model->rssi_str, rssi_str); furi_string_set_str(model->sensitivity_str, sensitivity_str); + furi_string_set_str(model->scan_direction_str, scan_direction_str); furi_string_set_str(model->scanning_str, scanning_str); }, true); @@ -150,6 +151,7 @@ Scanner* scanner_view_alloc() { model->frequency_str = furi_string_alloc(); model->rssi_str = furi_string_alloc(); model->sensitivity_str = furi_string_alloc(); + model->scan_direction_str = furi_string_alloc(); model->scanning_str = furi_string_alloc(); }, true @@ -171,6 +173,7 @@ void scanner_view_free(Scanner* scanner) { furi_string_free(model->frequency_str); furi_string_free(model->rssi_str); furi_string_free(model->sensitivity_str); + furi_string_free(model->scan_direction_str); furi_string_free(model->scanning_str); }, false); diff --git a/views/scanner.h b/views/scanner.h index 5ce10f7..b31cd27 100644 --- a/views/scanner.h +++ b/views/scanner.h @@ -31,6 +31,7 @@ typedef struct { FuriString* frequency_str; FuriString* rssi_str; FuriString* sensitivity_str; + FuriString* scan_direction_str; FuriString* scanning_str; } ScannerModel; @@ -38,7 +39,7 @@ void scanner_view_set_callback(Scanner* scanner, ScannerCallback callback, void* View* scanner_view_get_view(Scanner* scanner); -void scanner_view_update(Scanner* scanner, const char* frequency_str, const char* rssi_str, const char* sensitivity_str, const char* scanning_str); +void scanner_view_update(Scanner* scanner, const char* frequency_str, const char* rssi_str, const char* sensitivity_str, const char* scan_direction_str, const char* scanning_str); Scanner* scanner_view_alloc(); void scanner_view_free(Scanner* scanner); From d991253a62873f34340df4f30d4bc4a48e2ae719 Mon Sep 17 00:00:00 2001 From: "Calvin M.T." Date: Fri, 11 Apr 2025 21:44:16 +0200 Subject: [PATCH 13/13] Update scanner main display --- radio_scanner_app_i.c | 4 ++-- views/scanner.c | 20 +++++++++++--------- 2 files changed, 13 insertions(+), 11 deletions(-) diff --git a/radio_scanner_app_i.c b/radio_scanner_app_i.c index 412fe8c..d8a7d97 100644 --- a/radio_scanner_app_i.c +++ b/radio_scanner_app_i.c @@ -235,7 +235,7 @@ void radio_scanner_get_frequency_str(RadioScannerApp* app, FuriString* frequency void radio_scanner_get_rssi_str(RadioScannerApp* app, FuriString* rssi_str) { furi_assert(app); if(rssi_str != NULL) { - furi_string_printf(rssi_str, "%.2f", (double)app->rssi); + furi_string_printf(rssi_str, "%.1f", (double)app->rssi); } } @@ -245,7 +245,7 @@ void radio_scanner_get_rssi_str(RadioScannerApp* app, FuriString* rssi_str) { void radio_scanner_get_sensitivity_str(RadioScannerApp* app, FuriString* sensitivity_str) { furi_assert(app); if(sensitivity_str != NULL) { - furi_string_printf(sensitivity_str, "%.2f", (double)app->sensitivity); + furi_string_printf(sensitivity_str, "%.1f", (double)app->sensitivity); } } diff --git a/views/scanner.c b/views/scanner.c index a70d4b3..b45bc4b 100644 --- a/views/scanner.c +++ b/views/scanner.c @@ -58,18 +58,20 @@ void scanner_view_draw(Canvas* canvas, ScannerModel* model) { canvas_set_font(canvas, FontSecondary); char freq_str[RADIO_SCANNER_BUFFER_SZ + 1] = {0}; - snprintf(freq_str, RADIO_SCANNER_BUFFER_SZ, "Freq: %s MHz", furi_string_get_cstr(model->frequency_str)); - canvas_draw_str_aligned(canvas, 64, 18, AlignCenter, AlignTop, freq_str); + snprintf(freq_str, RADIO_SCANNER_BUFFER_SZ, "Freq. (%s):", furi_string_get_cstr(model->scan_direction_str)); + canvas_draw_str_aligned(canvas, 8, 18, AlignLeft, AlignTop, freq_str); + char freq_value_str[RADIO_SCANNER_BUFFER_SZ + 1] = {0}; + snprintf(freq_value_str, RADIO_SCANNER_BUFFER_SZ, "%s MHz", furi_string_get_cstr(model->frequency_str)); + canvas_draw_str_aligned(canvas, 120, 18, AlignRight, AlignTop, freq_value_str); char rssi_str[RADIO_SCANNER_BUFFER_SZ + 1] = {0}; - snprintf(rssi_str, RADIO_SCANNER_BUFFER_SZ, "RSSI: %s", furi_string_get_cstr(model->rssi_str)); - canvas_draw_str_aligned(canvas, 64, 30, AlignCenter, AlignTop, rssi_str); + snprintf(rssi_str, RADIO_SCANNER_BUFFER_SZ, "RSSI (%s):", furi_string_get_cstr(model->sensitivity_str)); + canvas_draw_str_aligned(canvas, 8, 30, AlignLeft, AlignTop, rssi_str); + char rssi_value_str[RADIO_SCANNER_BUFFER_SZ + 1] = {0}; + snprintf(rssi_value_str, RADIO_SCANNER_BUFFER_SZ, "%s dBm", furi_string_get_cstr(model->rssi_str)); + canvas_draw_str_aligned(canvas, 120, 30, AlignRight, AlignTop, rssi_value_str); - char sensitivity_str[RADIO_SCANNER_BUFFER_SZ + 1] = {0}; - snprintf(sensitivity_str, RADIO_SCANNER_BUFFER_SZ, "Sens: %s", furi_string_get_cstr(model->sensitivity_str)); - canvas_draw_str_aligned(canvas, 64, 42, AlignCenter, AlignTop, sensitivity_str); - - canvas_draw_str_aligned(canvas, 64, 54, AlignCenter, AlignTop, furi_string_get_cstr(model->scanning_str)); + canvas_draw_str_aligned(canvas, 64, 42, AlignCenter, AlignTop, furi_string_get_cstr(model->scanning_str)); #ifdef FURI_DEBUG FURI_LOG_D(TAG, "Exit scanner_view_draw"); #endif