diff --git a/ReadMe.md b/ReadMe.md index 2efedb830..fc442e926 100644 --- a/ReadMe.md +++ b/ReadMe.md @@ -48,6 +48,8 @@ See changelog in releases for latest updates! ### Community apps included: +- ESP8266 Deauther plugin [(By SequoiaSan)](https://github.com/SequoiaSan/FlipperZero-Wifi-ESP8266-Deauther-Module) +- WiFi Scanner plugin [(By SequoiaSan)](https://github.com/SequoiaSan/FlipperZero-WiFi-Scanner_Module) - UPC-A Barcode generator plugin [(By McAzzaMan)](https://github.com/McAzzaMan/flipperzero-firmware/tree/UPC-A_Barcode_Generator/applications/barcode_generator) - GPIO: Sentry Safe plugin [(By H4ckd4ddy)](https://github.com/H4ckd4ddy/flipperzero-sentry-safe-plugin) - ESP32: WiFi Marauder companion plugin [(By 0xchocolate)](https://github.com/0xchocolate/flipperzero-firmware-with-wifi-marauder-companion) @@ -79,17 +81,27 @@ See changelog in releases for latest updates! ## [- BadUSB: how to use different keyboard layouts](https://github.com/Eng1n33r/flipperzero-firmware/blob/dev/documentation/BadUSBLayouts.md) -**Plugins** +### **Plugins** ## [- Configure UniversalRF Remix App](https://github.com/Eng1n33r/flipperzero-firmware/blob/dev/documentation/UniRFRemix.md) -## [- How to use: NRF24 plugins](https://github.com/Eng1n33r/flipperzero-firmware/blob/dev/documentation/NRF24.md) +## [- Barcode Generator](https://github.com/Eng1n33r/flipperzero-firmware/blob/dev/documentation/BarcodeGenerator.md) -## [- How to use: SentrySafe plugin](https://github.com/Eng1n33r/flipperzero-firmware/blob/dev/documentation/SentrySafe.md) +### **Plugins that works with external hardware** -## [- Barcode Generator readme](https://github.com/Eng1n33r/flipperzero-firmware/blob/dev/documentation/BarcodeGenerator.md) +## [- How to use: [NRF24] plugins](https://github.com/Eng1n33r/flipperzero-firmware/blob/dev/documentation/NRF24.md) -**SubGHz** +## [- How to use: [WiFi] Scanner](https://github.com/SequoiaSan/FlipperZero-WiFi-Scanner_Module#readme) + +## [- How to use: [ESP8266] Deauther](https://github.com/SequoiaSan/FlipperZero-Wifi-ESP8266-Deauther-Module#readme) + +## [- How to use: [ESP32] WiFi Marauder](https://github.com/UberGuidoZ/Flipper/tree/main/Wifi_DevBoard) + +## [- Windows: How to Upload .bin to ESP32/ESP8266](https://github.com/SequoiaSan/Guide-How-To-Upload-bin-to-ESP8266-ESP32) + +## [- How to use: [GPIO] SentrySafe plugin](https://github.com/Eng1n33r/flipperzero-firmware/blob/dev/documentation/SentrySafe.md) + +### **SubGHz** ## [- Transmission is blocked? - How to extend SubGHz frequency range](https://github.com/Eng1n33r/flipperzero-firmware/blob/dev/documentation/DangerousSettings.md) diff --git a/applications/esp8266_deauth/FlipperZeroWiFiDeauthModuleDefines.h b/applications/esp8266_deauth/FlipperZeroWiFiDeauthModuleDefines.h new file mode 100644 index 000000000..a8e0e8d61 --- /dev/null +++ b/applications/esp8266_deauth/FlipperZeroWiFiDeauthModuleDefines.h @@ -0,0 +1,7 @@ +#define WIFI_MODULE_INIT_VERSION "WFDM_0.1" + +#define MODULE_CONTEXT_INITIALIZATION WIFI_MODULE_INIT_VERSION + +#define FLIPPERZERO_SERIAL_BAUD 230400 + +#define NA 0 diff --git a/applications/esp8266_deauth/application.fam b/applications/esp8266_deauth/application.fam new file mode 100644 index 000000000..7f66c75a4 --- /dev/null +++ b/applications/esp8266_deauth/application.fam @@ -0,0 +1,10 @@ +App( + appid="esp8266_deauth", + name="[ESP8266] Deauther", + apptype=FlipperAppType.PLUGIN, + entry_point="esp8266_deauth_app", + cdefines=["APP_ESP8266_deauth"], + requires=["gui"], + stack_size=2 * 1024, + order=100, +) diff --git a/applications/esp8266_deauth/esp8266_deauth.c b/applications/esp8266_deauth/esp8266_deauth.c new file mode 100644 index 000000000..8e706b958 --- /dev/null +++ b/applications/esp8266_deauth/esp8266_deauth.c @@ -0,0 +1,573 @@ +#include +#include +#include +#include +#include +#include +#include +#include +//#include +//#include +//#include +//#include +//#include +#include +#include + +#include "FlipperZeroWiFiDeauthModuleDefines.h" + +#define DEAUTH_APP_DEBUG 0 + +#if DEAUTH_APP_DEBUG +#define APP_NAME_TAG "WiFi_Scanner" +#define DEAUTH_APP_LOG_I(format, ...) FURI_LOG_I(APP_NAME_TAG, format, ##__VA_ARGS__) +#define DEAUTH_APP_LOG_D(format, ...) FURI_LOG_D(APP_NAME_TAG, format, ##__VA_ARGS__) +#define DEAUTH_APP_LOG_E(format, ...) FURI_LOG_E(APP_NAME_TAG, format, ##__VA_ARGS__) +#else +#define DEAUTH_APP_LOG_I(format, ...) +#define DEAUTH_APP_LOG_D(format, ...) +#define DEAUTH_APP_LOG_E(format, ...) +#endif // WIFI_APP_DEBUG + +#define DISABLE_CONSOLE !DEAUTH_APP_DEBUG +#define ENABLE_MODULE_POWER 1 +#define ENABLE_MODULE_DETECTION 1 + +typedef enum EEventType // app internally defined event types +{ + EventTypeKey // flipper input.h type +} EEventType; + +typedef struct SPluginEvent +{ + EEventType m_type; + InputEvent m_input; +} SPluginEvent; + +typedef enum EAppContext +{ + Undefined, + WaitingForModule, + Initializing, + ModuleActive, +} EAppContext; + +typedef enum EWorkerEventFlags +{ + WorkerEventReserved = (1 << 0), // Reserved for StreamBuffer internal event + WorkerEventStop = (1 << 1), + WorkerEventRx = (1 << 2), +} EWorkerEventFlags; + +typedef struct SGpioButtons +{ + GpioPin const* pinButtonUp; + GpioPin const* pinButtonDown; + GpioPin const* pinButtonOK; + GpioPin const* pinButtonBack; +} SGpioButtons; + +typedef struct SWiFiDeauthApp +{ + Gui* m_gui; + FuriThread* m_worker_thread; + //NotificationApp* m_notification; + StreamBufferHandle_t m_rx_stream; + SGpioButtons m_GpioButtons; + + bool m_wifiDeauthModuleInitialized; + bool m_wifiDeauthModuleAttached; + + EAppContext m_context; + + uint8_t m_backBuffer[128 * 8 * 8]; + //uint8_t m_renderBuffer[128 * 8 * 8]; + + uint8_t* m_backBufferPtr; + //uint8_t* m_m_renderBufferPtr; + + //uint8_t* m_originalBuffer; + //uint8_t** m_originalBufferLocation; + size_t m_canvasSize; + + bool m_needUpdateGUI; +} SWiFiDeauthApp; + +/////// INIT STATE /////// +static void esp8266_deauth_app_init(SWiFiDeauthApp* const app) +{ + app->m_context = Undefined; + + app->m_canvasSize = 128 * 8 * 8; + memset(app->m_backBuffer, DEAUTH_APP_DEBUG ? 0xFF : 0x00, app->m_canvasSize); + //memset(app->m_renderBuffer, DEAUTH_APP_DEBUG ? 0xFF : 0x00, app->m_canvasSize); + + //app->m_originalBuffer = NULL; + //app->m_originalBufferLocation = NULL; + + //app->m_m_renderBufferPtr = app->m_renderBuffer; + app->m_backBufferPtr = app->m_backBuffer; + + app->m_GpioButtons.pinButtonUp = &gpio_ext_pc3; + app->m_GpioButtons.pinButtonDown = &gpio_ext_pb2; + app->m_GpioButtons.pinButtonOK = &gpio_ext_pb3; + app->m_GpioButtons.pinButtonBack = &gpio_ext_pa4; + + app->m_needUpdateGUI = false; + +#if ENABLE_MODULE_POWER + app->m_wifiDeauthModuleInitialized = false; +#else + app->m_wifiDeauthModuleInitialized = true; +#endif // ENABLE_MODULE_POWER + +#if ENABLE_MODULE_DETECTION + app->m_wifiDeauthModuleAttached = false; +#else + app->m_wifiDeauthModuleAttached = true; +#endif +} + +static void esp8266_deauth_module_render_callback(Canvas* const canvas, void* ctx) +{ + SWiFiDeauthApp* app = acquire_mutex((ValueMutex*)ctx, 25); + if (app == NULL) + { + return; + } + + //if(app->m_needUpdateGUI) + //{ + // app->m_needUpdateGUI = false; + + // //app->m_canvasSize = canvas_get_buffer_size(canvas); + // //app->m_originalBuffer = canvas_get_buffer(canvas); + // //app->m_originalBufferLocation = &u8g2_GetBufferPtr(&canvas->fb); + // //u8g2_GetBufferPtr(&canvas->fb) = app->m_m_renderBufferPtr; + //} + + //uint8_t* exchangeBuffers = app->m_m_renderBufferPtr; + //app->m_m_renderBufferPtr = app->m_backBufferPtr; + //app->m_backBufferPtr = exchangeBuffers; + + //if(app->m_needUpdateGUI) + //{ + // //memcpy(app->m_renderBuffer, app->m_backBuffer, app->m_canvasSize); + // app->m_needUpdateGUI = false; + //} + + switch (app->m_context) + { + case Undefined: + { + canvas_clear(canvas); + canvas_set_font(canvas, FontPrimary); + + const char* strInitializing = "Something wrong"; + canvas_draw_str(canvas, (u8g2_GetDisplayWidth(&canvas->fb) / 2) - (canvas_string_width(canvas, strInitializing) / 2), (u8g2_GetDisplayHeight(&canvas->fb) / 2)/* - (u8g2_GetMaxCharHeight(&canvas->fb) / 2)*/, strInitializing); + } + break; + case WaitingForModule: +#if ENABLE_MODULE_DETECTION + furi_assert(!app->m_wifiDeauthModuleAttached); + if (!app->m_wifiDeauthModuleAttached) + { + canvas_clear(canvas); + canvas_set_font(canvas, FontSecondary); + + const char* strInitializing = "Attach WiFi scanner module"; + canvas_draw_str(canvas, (u8g2_GetDisplayWidth(&canvas->fb) / 2) - (canvas_string_width(canvas, strInitializing) / 2), (u8g2_GetDisplayHeight(&canvas->fb) / 2)/* - (u8g2_GetMaxCharHeight(&canvas->fb) / 2)*/, strInitializing); + } +#endif + break; + case Initializing: +#if ENABLE_MODULE_POWER + { + furi_assert(!app->m_wifiDeauthModuleInitialized); + if (!app->m_wifiDeauthModuleInitialized) + { + canvas_set_font(canvas, FontPrimary); + + const char* strInitializing = "Initializing..."; + canvas_draw_str(canvas, (u8g2_GetDisplayWidth(&canvas->fb) / 2) - (canvas_string_width(canvas, strInitializing) / 2), (u8g2_GetDisplayHeight(&canvas->fb) / 2) - (u8g2_GetMaxCharHeight(&canvas->fb) / 2), strInitializing); + } + } +#endif // ENABLE_MODULE_POWER + break; + case ModuleActive: + { + uint8_t* buffer = canvas_get_buffer(canvas); + app->m_canvasSize = canvas_get_buffer_size(canvas); + memcpy(buffer, app->m_backBuffer, app->m_canvasSize); + } + break; + default: + break; + } + + release_mutex((ValueMutex*)ctx, app); +} + +static void esp8266_deauth_module_input_callback(InputEvent* input_event, FuriMessageQueue* event_queue) { + furi_assert(event_queue); + + SPluginEvent event = {.m_type = EventTypeKey, .m_input = *input_event}; + furi_message_queue_put(event_queue, &event, FuriWaitForever); +} + +static void uart_on_irq_cb(UartIrqEvent ev, uint8_t data, void* context) { + furi_assert(context); + BaseType_t xHigherPriorityTaskWoken = pdFALSE; + + SWiFiDeauthApp* app = context; + + DEAUTH_APP_LOG_I("uart_echo_on_irq_cb"); + + if(ev == UartIrqEventRXNE) { + DEAUTH_APP_LOG_I("ev == UartIrqEventRXNE"); + xStreamBufferSendFromISR(app->m_rx_stream, &data, 1, &xHigherPriorityTaskWoken); + furi_thread_flags_set(furi_thread_get_id(app->m_worker_thread), WorkerEventRx); + portYIELD_FROM_ISR(xHigherPriorityTaskWoken); + } + + +} + +static int32_t uart_worker(void* context) { + furi_assert(context); + DEAUTH_APP_LOG_I("[UART] Worker thread init"); + + SWiFiDeauthApp* app = acquire_mutex((ValueMutex*)context, 25); + if (app == NULL) + { + return 1; + } + + StreamBufferHandle_t rx_stream = app->m_rx_stream; + + release_mutex((ValueMutex*)context, app); + +#if ENABLE_MODULE_POWER + bool initialized = false; + + string_t receivedString; + string_init(receivedString); +#endif // ENABLE_MODULE_POWER + + while(true) + { + uint32_t events = furi_thread_flags_wait(WorkerEventStop | WorkerEventRx, FuriFlagWaitAny, FuriWaitForever); + furi_check((events & FuriFlagError) == 0); + + if(events & WorkerEventStop) break; + if(events & WorkerEventRx) + { + DEAUTH_APP_LOG_I("[UART] Received data"); + SWiFiDeauthApp* app = acquire_mutex((ValueMutex*)context, 25); + if (app == NULL) + { + return 1; + } + + size_t dataReceivedLength = 0; + int index = 0; + do + { + const uint8_t dataBufferSize = 64; + uint8_t dataBuffer[dataBufferSize]; + dataReceivedLength = xStreamBufferReceive(rx_stream, dataBuffer, dataBufferSize, 25); + if (dataReceivedLength > 0) + { +#if ENABLE_MODULE_POWER + if (!initialized) + { + if(!(dataReceivedLength > strlen(MODULE_CONTEXT_INITIALIZATION))) + { + DEAUTH_APP_LOG_I("[UART] Found possible init candidate"); + for (uint16_t i = 0; i < dataReceivedLength; i++) + { + string_push_back(receivedString, dataBuffer[i]); + } + } + } + else +#endif // ENABLE_MODULE_POWER + { + DEAUTH_APP_LOG_I("[UART] Data copied to backbuffer"); + memcpy(app->m_backBuffer + index, dataBuffer, dataReceivedLength); + index += dataReceivedLength; + app->m_needUpdateGUI = true; + } + } + + } while(dataReceivedLength > 0); + +#if ENABLE_MODULE_POWER + if (!app->m_wifiDeauthModuleInitialized) + { + if (string_cmp_str(receivedString, MODULE_CONTEXT_INITIALIZATION) == 0) + { + DEAUTH_APP_LOG_I("[UART] Initialized"); + initialized = true; + app->m_wifiDeauthModuleInitialized = true; + app->m_context = ModuleActive; + string_clear(receivedString); + } + else + { + DEAUTH_APP_LOG_I("[UART] Not an initialization command"); + string_reset(receivedString); + } + } +#endif // ENABLE_MODULE_POWER + + release_mutex((ValueMutex*)context, app); + } + } + + return 0; +} + +int32_t esp8266_deauth_app(void* p) +{ + UNUSED(p); + + DEAUTH_APP_LOG_I("Init"); + + // FuriTimer* timer = furi_timer_alloc(blink_test_update, FuriTimerTypePeriodic, event_queue); + // furi_timer_start(timer, furi_kernel_get_tick_frequency()); + + FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(SPluginEvent)); + + SWiFiDeauthApp* app = malloc(sizeof(SWiFiDeauthApp)); + + esp8266_deauth_app_init(app); + + furi_hal_gpio_init_simple(app->m_GpioButtons.pinButtonUp, GpioModeOutputPushPull); + furi_hal_gpio_init_simple(app->m_GpioButtons.pinButtonDown, GpioModeOutputPushPull); + furi_hal_gpio_init_simple(app->m_GpioButtons.pinButtonOK, GpioModeOutputPushPull); + furi_hal_gpio_init_simple(app->m_GpioButtons.pinButtonBack, GpioModeOutputPushPull); + + furi_hal_gpio_write(app->m_GpioButtons.pinButtonUp, true); + furi_hal_gpio_write(app->m_GpioButtons.pinButtonDown, true); + furi_hal_gpio_write(app->m_GpioButtons.pinButtonOK, true); + furi_hal_gpio_write(app->m_GpioButtons.pinButtonBack, false); // GPIO15 - Boot fails if pulled HIGH + +#if ENABLE_MODULE_DETECTION + furi_hal_gpio_init(&gpio_ext_pc0, GpioModeInput, GpioPullUp, GpioSpeedLow); // Connect to the Flipper's ground just to be sure + //furi_hal_gpio_add_int_callback(pinD0, input_isr_d0, this); + app->m_context = WaitingForModule; +#else +#if ENABLE_MODULE_POWER + app->m_context = Initializing; + furi_hal_power_enable_otg(); +#else + app->m_context = ModuleActive; +#endif +#endif // ENABLE_MODULE_DETECTION + + ValueMutex app_data_mutex; + if (!init_mutex(&app_data_mutex, app, sizeof(SWiFiDeauthApp))) { + DEAUTH_APP_LOG_E("cannot create mutex\r\n"); + free(app); + return 255; + } + + DEAUTH_APP_LOG_I("Mutex created"); + + app->m_rx_stream = xStreamBufferCreate(1 * 1024, 1); + + //app->m_notification = furi_record_open("notification"); + + ViewPort* view_port = view_port_alloc(); + view_port_draw_callback_set(view_port, esp8266_deauth_module_render_callback, &app_data_mutex); + view_port_input_callback_set(view_port, esp8266_deauth_module_input_callback, event_queue); + + // Open GUI and register view_port + Gui* gui = furi_record_open("gui"); + gui_add_view_port(gui, view_port, GuiLayerFullscreen); + + //notification_message(app->notification, &sequence_set_only_blue_255); + + // Enable uart listener +#if DISABLE_CONSOLE + furi_hal_console_disable(); +#endif + furi_hal_uart_set_br(FuriHalUartIdUSART1, FLIPPERZERO_SERIAL_BAUD); + furi_hal_uart_set_irq_cb(FuriHalUartIdUSART1, uart_on_irq_cb, app); + DEAUTH_APP_LOG_I("UART Listener created"); + + app->m_worker_thread = furi_thread_alloc(); + furi_thread_set_name(app->m_worker_thread, "WiFiDeauthModuleUARTWorker"); + furi_thread_set_stack_size(app->m_worker_thread, 1 * 1024); + furi_thread_set_context(app->m_worker_thread, &app_data_mutex); + furi_thread_set_callback(app->m_worker_thread, uart_worker); + furi_thread_start(app->m_worker_thread); + DEAUTH_APP_LOG_I("UART thread allocated"); + + SPluginEvent event; + for(bool processing = true; processing;) + { + FuriStatus event_status = furi_message_queue_get(event_queue, &event, 100); + SWiFiDeauthApp* app = (SWiFiDeauthApp*)acquire_mutex_block(&app_data_mutex); + +#if ENABLE_MODULE_DETECTION + if(!app->m_wifiDeauthModuleAttached) + { + if(furi_hal_gpio_read(&gpio_ext_pc0) == false) + { + DEAUTH_APP_LOG_I("Module Attached"); + app->m_wifiDeauthModuleAttached = true; +#if ENABLE_MODULE_POWER + app->m_context = Initializing; + furi_hal_power_enable_otg(); +#else + app->m_context = ModuleActive; +#endif + } + } +#endif // ENABLE_MODULE_DETECTION + + if(event_status == FuriStatusOk) + { + if(event.m_type == EventTypeKey) + { + if (app->m_wifiDeauthModuleInitialized) + { + if (app->m_context == ModuleActive) + { + switch (event.m_input.key) + { + case InputKeyUp: + if (event.m_input.type == InputTypePress) + { + DEAUTH_APP_LOG_I("Up Press"); + furi_hal_gpio_write(app->m_GpioButtons.pinButtonUp, false); + } + else if (event.m_input.type == InputTypeRelease) + { + DEAUTH_APP_LOG_I("Up Release"); + furi_hal_gpio_write(app->m_GpioButtons.pinButtonUp, true); + } + break; + case InputKeyDown: + if (event.m_input.type == InputTypePress) + { + DEAUTH_APP_LOG_I("Down Press"); + furi_hal_gpio_write(app->m_GpioButtons.pinButtonDown, false); + } + else if (event.m_input.type == InputTypeRelease) + { + DEAUTH_APP_LOG_I("Down Release"); + furi_hal_gpio_write(app->m_GpioButtons.pinButtonDown, true); + } + break; + case InputKeyOk: + if (event.m_input.type == InputTypePress) + { + DEAUTH_APP_LOG_I("OK Press"); + furi_hal_gpio_write(app->m_GpioButtons.pinButtonOK, false); + } + else if (event.m_input.type == InputTypeRelease) + { + DEAUTH_APP_LOG_I("OK Release"); + furi_hal_gpio_write(app->m_GpioButtons.pinButtonOK, true); + } + break; + case InputKeyBack: + if (event.m_input.type == InputTypePress) + { + DEAUTH_APP_LOG_I("Back Press"); + furi_hal_gpio_write(app->m_GpioButtons.pinButtonBack, false); + } + else if (event.m_input.type == InputTypeRelease) + { + DEAUTH_APP_LOG_I("Back Release"); + furi_hal_gpio_write(app->m_GpioButtons.pinButtonBack, true); + } + else if (event.m_input.type == InputTypeLong) + { + DEAUTH_APP_LOG_I("Back Long"); + processing = false; + } + break; + default: + break; + } + } + } + else + { + if(event.m_input.key == InputKeyBack) + { + if(event.m_input.type == InputTypeShort || event.m_input.type == InputTypeLong) + { + processing = false; + } + } + } + } + } + else + { + DEAUTH_APP_LOG_D("osMessageQueue: event timeout"); + } + +#if ENABLE_MODULE_DETECTION + if(app->m_wifiDeauthModuleAttached && furi_hal_gpio_read(&gpio_ext_pc0) == true) + { + DEAUTH_APP_LOG_D("Module Disconnected - Exit"); + processing = false; + app->m_wifiDeauthModuleAttached = false; + app->m_wifiDeauthModuleInitialized = false; + } +#endif + + view_port_update(view_port); + release_mutex(&app_data_mutex, app); + } + + DEAUTH_APP_LOG_I("Start exit app"); + + furi_thread_flags_set(furi_thread_get_id(app->m_worker_thread), WorkerEventStop); + furi_thread_join(app->m_worker_thread); + furi_thread_free(app->m_worker_thread); + + DEAUTH_APP_LOG_I("Thread Deleted"); + +#if DISABLE_CONSOLE + furi_hal_console_enable(); +#endif + + //*app->m_originalBufferLocation = app->m_originalBuffer; + + view_port_enabled_set(view_port, false); + + gui_remove_view_port(gui, view_port); + + // Close gui record + furi_record_close("gui"); + furi_record_close("notification"); + app->m_gui = NULL; + + view_port_free(view_port); + + furi_message_queue_free(event_queue); + + vStreamBufferDelete(app->m_rx_stream); + + delete_mutex(&app_data_mutex); + + + + // Free rest + free(app); + + DEAUTH_APP_LOG_I("App freed"); + +#if ENABLE_MODULE_POWER + furi_hal_power_disable_otg(); +#endif + + return 0; +} diff --git a/applications/meta/application.fam b/applications/meta/application.fam index 14f106287..9409a33b1 100644 --- a/applications/meta/application.fam +++ b/applications/meta/application.fam @@ -74,5 +74,7 @@ App( "nrf_sniff", "sentry_safe", "wifi_marauder", + "esp8266_deauth", + "wifi_scanner", ], ) \ No newline at end of file diff --git a/applications/wifi_scanner/FlipperZeroWiFiModuleDefines.h b/applications/wifi_scanner/FlipperZeroWiFiModuleDefines.h new file mode 100644 index 000000000..7b28ebd64 --- /dev/null +++ b/applications/wifi_scanner/FlipperZeroWiFiModuleDefines.h @@ -0,0 +1,17 @@ +#define WIFI_MODULE_INIT_VERSION "WFSM_0.1" + +#define MODULE_CONTEXT_INITIALIZATION WIFI_MODULE_INIT_VERSION +#define MODULE_CONTEXT_MONITOR "monitor" +#define MODULE_CONTEXT_SCAN "scan" +#define MODULE_CONTEXT_SCAN_ANIMATION "scan_anim" +#define MODULE_CONTEXT_MONITOR_ANIMATION "monitor_anim" + +#define MODULE_CONTROL_COMMAND_NEXT 'n' +#define MODULE_CONTROL_COMMAND_PREVIOUS 'p' +#define MODULE_CONTROL_COMMAND_SCAN 's' +#define MODULE_CONTROL_COMMAND_MONITOR 'm' +#define MODULE_CONTROL_COMMAND_RESTART 'r' + +#define FLIPPERZERO_SERIAL_BAUD 115200 + +#define NA 0 diff --git a/applications/wifi_scanner/application.fam b/applications/wifi_scanner/application.fam new file mode 100644 index 000000000..3d42a0d4f --- /dev/null +++ b/applications/wifi_scanner/application.fam @@ -0,0 +1,10 @@ +App( + appid="wifi_scanner", + name="[WiFi] Scanner", + apptype=FlipperAppType.PLUGIN, + entry_point="wifi_scanner_app", + cdefines=["APP_WIFI_SCANNER"], + requires=["gui"], + stack_size=2 * 1024, + order=110, +) diff --git a/applications/wifi_scanner/wifi_scanner.c b/applications/wifi_scanner/wifi_scanner.c new file mode 100644 index 000000000..e8f964819 --- /dev/null +++ b/applications/wifi_scanner/wifi_scanner.c @@ -0,0 +1,848 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "FlipperZeroWiFiModuleDefines.h" + +#define WIFI_APP_DEBUG 0 + +#if WIFI_APP_DEBUG +#define APP_NAME_TAG "WiFi_Scanner" +#define WIFI_APP_LOG_I(format, ...) FURI_LOG_I(APP_NAME_TAG, format, ##__VA_ARGS__) +#define WIFI_APP_LOG_D(format, ...) FURI_LOG_D(APP_NAME_TAG, format, ##__VA_ARGS__) +#define WIFI_APP_LOG_E(format, ...) FURI_LOG_E(APP_NAME_TAG, format, ##__VA_ARGS__) +#else +#define WIFI_APP_LOG_I(format, ...) +#define WIFI_APP_LOG_D(format, ...) +#define WIFI_APP_LOG_E(format, ...) +#endif // WIFI_APP_DEBUG + +#define DISABLE_CONSOLE !WIFI_APP_DEBUG + +#define ENABLE_MODULE_POWER 1 +#define ENABLE_MODULE_DETECTION 1 + +#define ANIMATION_TIME 350 + +typedef enum EChunkArrayData { + EChunkArrayData_Context = 0, + EChunkArrayData_SSID, + EChunkArrayData_EncryptionType, + EChunkArrayData_RSSI, + EChunkArrayData_BSSID, + EChunkArrayData_Channel, + EChunkArrayData_IsHidden, + EChunkArrayData_CurrentAPIndex, + EChunkArrayData_TotalAps, + EChunkArrayData_ENUM_MAX +} EChunkArrayData; + +typedef enum EEventType // app internally defined event types +{ EventTypeKey // flipper input.h type +} EEventType; + +typedef struct SPluginEvent { + EEventType m_type; + InputEvent m_input; +} SPluginEvent; + +typedef struct EAccessPointDesc { + string_t m_accessPointName; + int16_t m_rssi; + string_t m_secType; + string_t m_bssid; + unsigned short m_channel; + bool m_isHidden; +} EAccessPointDesc; + +typedef enum EAppContext { + Undefined, + WaitingForModule, + Initializing, + ScanMode, + MonitorMode, + ScanAnimation, + MonitorAnimation +} EAppContext; + +typedef enum EWorkerEventFlags { + WorkerEventReserved = (1 << 0), // Reserved for StreamBuffer internal event + WorkerEventStop = (1 << 1), + WorkerEventRx = (1 << 2), +} EWorkerEventFlags; + +typedef struct SWiFiScannerApp { + Gui* m_gui; + FuriThread* m_worker_thread; + NotificationApp* m_notification; + StreamBufferHandle_t m_rx_stream; + + bool m_wifiModuleInitialized; + bool m_wifiModuleAttached; + + EAppContext m_context; + + EAccessPointDesc m_currentAccesspointDescription; + + unsigned short m_totalAccessPoints; + unsigned short m_currentIndexAccessPoint; + + uint32_t m_prevAnimationTime; + uint32_t m_animationTime; + uint8_t m_animtaionCounter; +} SWiFiScannerApp; + +/////// INIT STATE /////// +static void wifi_scanner_app_init(SWiFiScannerApp* const app) { + app->m_context = Undefined; + + app->m_totalAccessPoints = 0; + app->m_currentIndexAccessPoint = 0; + + string_init(app->m_currentAccesspointDescription.m_accessPointName); + string_set_str(app->m_currentAccesspointDescription.m_accessPointName, "N/A\n"); + app->m_currentAccesspointDescription.m_channel = 0; + string_init(app->m_currentAccesspointDescription.m_bssid); + string_set_str(app->m_currentAccesspointDescription.m_bssid, "N/A\n"); + string_init(app->m_currentAccesspointDescription.m_secType); + string_set_str(app->m_currentAccesspointDescription.m_secType, "N/A\n"); + app->m_currentAccesspointDescription.m_rssi = 0; + app->m_currentAccesspointDescription.m_isHidden = false; + + app->m_prevAnimationTime = 0; + app->m_animationTime = ANIMATION_TIME; + app->m_animtaionCounter = 0; + + app->m_wifiModuleInitialized = false; + +#if ENABLE_MODULE_DETECTION + app->m_wifiModuleAttached = false; +#else + app->m_wifiModuleAttached = true; +#endif +} + +int16_t dBmtoPercentage(int16_t dBm) { + const int16_t RSSI_MAX = -50; // define maximum strength of signal in dBm + const int16_t RSSI_MIN = -100; // define minimum strength of signal in dBm + + int16_t quality; + if(dBm <= RSSI_MIN) { + quality = 0; + } else if(dBm >= RSSI_MAX) { + quality = 100; + } else { + quality = 2 * (dBm + 100); + } + + return quality; +} + +void DrawSignalStrengthBar(Canvas* canvas, int rssi, int x, int y, int width, int height) { + int16_t percents = dBmtoPercentage(rssi); + + u8g2_DrawHLine(&canvas->fb, x, y, width); + u8g2_DrawHLine(&canvas->fb, x, y + height, width); + + if(rssi != NA && height > 0) { + uint8_t barHeight = floor((height / 100.f) * percents); + canvas_draw_box(canvas, x, y + height - barHeight, width, barHeight); + } +} + +static void wifi_module_render_callback(Canvas* const canvas, void* ctx) { + SWiFiScannerApp* app = acquire_mutex((ValueMutex*)ctx, 25); + if(app == NULL) { + return; + } + + canvas_clear(canvas); + + { + switch(app->m_context) { + case Undefined: { + canvas_set_font(canvas, FontPrimary); + + const char* strError = "Something wrong"; + canvas_draw_str( + canvas, + (u8g2_GetDisplayWidth(&canvas->fb) / 2) - + (canvas_string_width(canvas, strError) / 2), + (u8g2_GetDisplayHeight(&canvas->fb) / + 2) /* - (u8g2_GetMaxCharHeight(&canvas->fb) / 2)*/, + strError); + } break; + case WaitingForModule: +#if ENABLE_MODULE_DETECTION + furi_assert(!app->m_wifiModuleAttached); + if(!app->m_wifiModuleAttached) { + canvas_set_font(canvas, FontSecondary); + + const char* strConnectModule = "Attach WiFi scanner module"; + canvas_draw_str( + canvas, + (u8g2_GetDisplayWidth(&canvas->fb) / 2) - + (canvas_string_width(canvas, strConnectModule) / 2), + (u8g2_GetDisplayHeight(&canvas->fb) / + 2) /* - (u8g2_GetMaxCharHeight(&canvas->fb) / 2)*/, + strConnectModule); + } +#endif + break; + case Initializing: { + furi_assert(!app->m_wifiModuleInitialized); + if(!app->m_wifiModuleInitialized) { + canvas_set_font(canvas, FontPrimary); + + const char* strInitializing = "Initializing..."; + canvas_draw_str( + canvas, + (u8g2_GetDisplayWidth(&canvas->fb) / 2) - + (canvas_string_width(canvas, strInitializing) / 2), + (u8g2_GetDisplayHeight(&canvas->fb) / 2) - + (u8g2_GetMaxCharHeight(&canvas->fb) / 2), + strInitializing); + } + } break; + case ScanMode: { + uint8_t offsetY = 0; + uint8_t offsetX = 0; + canvas_draw_frame( + canvas, + 0, + 0, + u8g2_GetDisplayWidth(&canvas->fb), + u8g2_GetDisplayHeight(&canvas->fb)); + + //canvas_set_font(canvas, FontPrimary); + u8g2_SetFont(&canvas->fb, u8g2_font_7x13B_tr); + uint8_t fontHeight = u8g2_GetMaxCharHeight(&canvas->fb); + + offsetX += 5; + offsetY += fontHeight; + canvas_draw_str( + canvas, + offsetX, + offsetY, + app->m_currentAccesspointDescription.m_isHidden ? + "(Hidden SSID)" : + string_get_cstr(app->m_currentAccesspointDescription.m_accessPointName)); + + offsetY += fontHeight; + + canvas_draw_str( + canvas, + offsetX, + offsetY, + string_get_cstr(app->m_currentAccesspointDescription.m_bssid)); + + canvas_set_font(canvas, FontSecondary); + //u8g2_SetFont(&canvas->fb, u8g2_font_tinytim_tf); + fontHeight = u8g2_GetMaxCharHeight(&canvas->fb); + + offsetY += fontHeight + 1; + + char string[15]; + sprintf(string, "RSSI: %d", app->m_currentAccesspointDescription.m_rssi); + canvas_draw_str(canvas, offsetX, offsetY, string); + + offsetY += fontHeight + 1; + + sprintf(string, "CHNL: %d", app->m_currentAccesspointDescription.m_channel); + canvas_draw_str(canvas, offsetX, offsetY, string); + + offsetY += fontHeight + 1; + + sprintf( + string, + "ENCR: %s", + string_get_cstr(app->m_currentAccesspointDescription.m_secType)); + canvas_draw_str(canvas, offsetX, offsetY, string); + + offsetY += fontHeight; + offsetY -= fontHeight; + + u8g2_SetFont(&canvas->fb, u8g2_font_courB08_tn); + sprintf(string, "%d/%d", app->m_currentIndexAccessPoint, app->m_totalAccessPoints); + offsetX = u8g2_GetDisplayWidth(&canvas->fb) - canvas_string_width(canvas, string) - 5; + canvas_draw_str(canvas, offsetX, offsetY, string); + + canvas_draw_frame( + canvas, + offsetX - 6, + offsetY - u8g2_GetMaxCharHeight(&canvas->fb) - 3, + u8g2_GetDisplayWidth(&canvas->fb), + u8g2_GetDisplayHeight(&canvas->fb)); + + u8g2_SetFont(&canvas->fb, u8g2_font_open_iconic_arrow_2x_t); + if(app->m_currentIndexAccessPoint != app->m_totalAccessPoints) { + //canvas_draw_triangle(canvas, offsetX - 5 - 20, offsetY + 5, 4, 4, CanvasDirectionBottomToTop); + canvas_draw_str(canvas, offsetX - 0 - 35, offsetY + 5, "\x4C"); + } + + if(app->m_currentIndexAccessPoint != 1) { + //canvas_draw_triangle(canvas, offsetX - 6 - 35, offsetY + 5, 4, 4, CanvasDirectionTopToBottom); + canvas_draw_str(canvas, offsetX - 4 - 20, offsetY + 5, "\x4F"); + } + } break; + case MonitorMode: { + uint8_t offsetY = 0; + uint8_t offsetX = 0; + + canvas_draw_frame( + canvas, + 0, + 0, + u8g2_GetDisplayWidth(&canvas->fb), + u8g2_GetDisplayHeight(&canvas->fb)); + + //canvas_set_font(canvas, FontBigNumbers); + u8g2_SetFont(&canvas->fb, u8g2_font_inb27_mr); + uint8_t fontHeight = u8g2_GetMaxCharHeight(&canvas->fb); + uint8_t fontWidth = u8g2_GetMaxCharWidth(&canvas->fb); + + if(app->m_currentAccesspointDescription.m_rssi == NA) { + offsetX += floor(u8g2_GetDisplayWidth(&canvas->fb) / 2) - fontWidth - 10; + offsetY += fontHeight - 5; + + canvas_draw_str(canvas, offsetX, offsetY, "N/A"); + } else { + offsetX += floor(u8g2_GetDisplayWidth(&canvas->fb) / 2) - 2 * fontWidth; + offsetY += fontHeight - 5; + + char rssi[8]; + sprintf(rssi, "%d", app->m_currentAccesspointDescription.m_rssi); + canvas_draw_str(canvas, offsetX, offsetY, rssi); + } + + //canvas_set_font(canvas, FontPrimary); + u8g2_SetFont(&canvas->fb, u8g2_font_7x13B_tr); + fontHeight = u8g2_GetMaxCharHeight(&canvas->fb); + fontWidth = u8g2_GetMaxCharWidth(&canvas->fb); + + offsetX = 5; + offsetY = u8g2_GetDisplayHeight(&canvas->fb) - 7 - fontHeight; + canvas_draw_str( + canvas, + offsetX, + offsetY, + string_get_cstr(app->m_currentAccesspointDescription.m_accessPointName)); + + offsetY += fontHeight + 2; + + canvas_draw_str( + canvas, + offsetX, + offsetY, + string_get_cstr(app->m_currentAccesspointDescription.m_bssid)); + + DrawSignalStrengthBar( + canvas, app->m_currentAccesspointDescription.m_rssi, 5, 5, 12, 25); + DrawSignalStrengthBar( + canvas, + app->m_currentAccesspointDescription.m_rssi, + u8g2_GetDisplayWidth(&canvas->fb) - 5 - 12, + 5, + 12, + 25); + } break; + case ScanAnimation: { + uint32_t currentTime = furi_get_tick(); + if(currentTime - app->m_prevAnimationTime > app->m_animationTime) { + app->m_prevAnimationTime = currentTime; + app->m_animtaionCounter += 1; + app->m_animtaionCounter = app->m_animtaionCounter % 3; + } + + uint8_t offsetX = 10; + uint8_t mutliplier = 2; + + for(uint8_t i = 0; i < 3; ++i) { + canvas_draw_disc( + canvas, + offsetX + 30 + 25 * i, + u8g2_GetDisplayHeight(&canvas->fb) / 2 - 7, + 5 * (app->m_animtaionCounter == i ? mutliplier : 1)); + } + + u8g2_SetFont(&canvas->fb, u8g2_font_7x13B_tr); + //canvas_set_font(canvas, FontPrimary); + const char* message = "Scanning"; + canvas_draw_str( + canvas, + u8g2_GetDisplayWidth(&canvas->fb) / 2 - canvas_string_width(canvas, message) / 2, + 55, + message); + } break; + case MonitorAnimation: { + uint32_t currentTime = furi_get_tick(); + if(currentTime - app->m_prevAnimationTime > app->m_animationTime) { + app->m_prevAnimationTime = currentTime; + app->m_animtaionCounter += 1; + app->m_animtaionCounter = app->m_animtaionCounter % 2; + } + + uint8_t offsetX = 10; + uint8_t mutliplier = 2; + + canvas_draw_disc( + canvas, + offsetX + 30, + u8g2_GetDisplayHeight(&canvas->fb) / 2 - 7, + 5 * (app->m_animtaionCounter == 0 ? mutliplier : 1)); + canvas_draw_disc( + canvas, + offsetX + 55, + u8g2_GetDisplayHeight(&canvas->fb) / 2 - 7, + 5 * (app->m_animtaionCounter == 1 ? mutliplier : 1)); + canvas_draw_disc( + canvas, + offsetX + 80, + u8g2_GetDisplayHeight(&canvas->fb) / 2 - 7, + 5 * (app->m_animtaionCounter == 0 ? mutliplier : 1)); + + u8g2_SetFont(&canvas->fb, u8g2_font_7x13B_tr); + //canvas_set_font(canvas, FontPrimary); + const char* message = "Monitor Mode"; + canvas_draw_str( + canvas, + u8g2_GetDisplayWidth(&canvas->fb) / 2 - canvas_string_width(canvas, message) / 2, + 55, + message); + } break; + default: + break; + } + } + release_mutex((ValueMutex*)ctx, app); +} + +static void wifi_module_input_callback(InputEvent* input_event, FuriMessageQueue* event_queue) { + furi_assert(event_queue); + + SPluginEvent event = {.m_type = EventTypeKey, .m_input = *input_event}; + furi_message_queue_put(event_queue, &event, FuriWaitForever); +} + +static void uart_on_irq_cb(UartIrqEvent ev, uint8_t data, void* context) { + furi_assert(context); + BaseType_t xHigherPriorityTaskWoken = pdFALSE; + + SWiFiScannerApp* app = context; + + WIFI_APP_LOG_I("uart_echo_on_irq_cb"); + + if(ev == UartIrqEventRXNE) { + WIFI_APP_LOG_I("ev == UartIrqEventRXNE"); + xStreamBufferSendFromISR(app->m_rx_stream, &data, 1, &xHigherPriorityTaskWoken); + furi_thread_flags_set(furi_thread_get_id(app->m_worker_thread), WorkerEventRx); + portYIELD_FROM_ISR(xHigherPriorityTaskWoken); + } +} + +static int32_t uart_worker(void* context) { + furi_assert(context); + + SWiFiScannerApp* app = acquire_mutex((ValueMutex*)context, 25); + if(app == NULL) { + return 1; + } + + StreamBufferHandle_t rx_stream = app->m_rx_stream; + + release_mutex((ValueMutex*)context, app); + + while(true) { + uint32_t events = furi_thread_flags_wait( + WorkerEventStop | WorkerEventRx, FuriFlagWaitAny, FuriWaitForever); + furi_check((events & FuriFlagError) == 0); + + if(events & WorkerEventStop) break; + if(events & WorkerEventRx) { + size_t length = 0; + string_t receivedString; + string_init(receivedString); + do { + uint8_t data[64]; + length = xStreamBufferReceive(rx_stream, data, 64, 25); + if(length > 0) { + WIFI_APP_LOG_I("Received Data - length: %i", length); + + for(uint16_t i = 0; i < length; i++) { + string_push_back(receivedString, data[i]); + } + + //notification_message(app->notification, &sequence_set_only_red_255); + } + } while(length > 0); + if(string_size(receivedString) > 0) { + string_t chunk; + string_init(chunk); + size_t begin = 0; + size_t end = 0; + size_t stringSize = string_size(receivedString); + + WIFI_APP_LOG_I("Received string: %s", string_get_cstr(receivedString)); + + string_t chunksArray[EChunkArrayData_ENUM_MAX]; + for(uint8_t i = 0; i < EChunkArrayData_ENUM_MAX; ++i) { + string_init(chunksArray[i]); + } + + uint8_t index = 0; + do { + end = string_search_char(receivedString, '+', begin); + + if(end == STRING_FAILURE) { + end = stringSize; + } + + WIFI_APP_LOG_I("size: %i, begin: %i, end: %i", stringSize, begin, end); + + string_set_strn(chunk, &string_get_cstr(receivedString)[begin], end - begin); + + WIFI_APP_LOG_I("String chunk: %s", string_get_cstr(chunk)); + + string_set(chunksArray[index++], chunk); + + begin = end + 1; + } while(end < stringSize); + string_clear(chunk); + + app = acquire_mutex((ValueMutex*)context, 25); + if(app == NULL) { + return 1; + } + + if(!app->m_wifiModuleInitialized) { + if(string_cmp_str( + chunksArray[EChunkArrayData_Context], MODULE_CONTEXT_INITIALIZATION) == + 0) { + app->m_wifiModuleInitialized = true; + app->m_context = ScanAnimation; + } + + } else { + if(string_cmp_str( + chunksArray[EChunkArrayData_Context], MODULE_CONTEXT_MONITOR) == 0) { + app->m_context = MonitorMode; + } else if( + string_cmp_str( + chunksArray[EChunkArrayData_Context], MODULE_CONTEXT_SCAN) == 0) { + app->m_context = ScanMode; + } else if( + string_cmp_str( + chunksArray[EChunkArrayData_Context], MODULE_CONTEXT_SCAN_ANIMATION) == + 0) { + app->m_context = ScanAnimation; + } else if( + string_cmp_str( + chunksArray[EChunkArrayData_Context], + MODULE_CONTEXT_MONITOR_ANIMATION) == 0) { + app->m_context = MonitorAnimation; + } + + if(app->m_context == MonitorMode || app->m_context == ScanMode) { + string_set( + app->m_currentAccesspointDescription.m_accessPointName, + chunksArray[EChunkArrayData_SSID]); + string_set( + app->m_currentAccesspointDescription.m_secType, + chunksArray[EChunkArrayData_EncryptionType]); + app->m_currentAccesspointDescription.m_rssi = + atoi(string_get_cstr(chunksArray[EChunkArrayData_RSSI])); + string_set( + app->m_currentAccesspointDescription.m_bssid, + chunksArray[EChunkArrayData_BSSID]); + app->m_currentAccesspointDescription.m_channel = + atoi(string_get_cstr(chunksArray[EChunkArrayData_Channel])); + app->m_currentAccesspointDescription.m_isHidden = + atoi(string_get_cstr(chunksArray[EChunkArrayData_IsHidden])); + + app->m_currentIndexAccessPoint = + atoi(string_get_cstr(chunksArray[EChunkArrayData_CurrentAPIndex])); + app->m_totalAccessPoints = + atoi(string_get_cstr(chunksArray[EChunkArrayData_TotalAps])); + } + } + + release_mutex((ValueMutex*)context, app); + + // Clear string array + for(index = 0; index < EChunkArrayData_ENUM_MAX; ++index) { + string_clear(chunksArray[index]); + } + } + string_clear(receivedString); + } + } + + return 0; +} + +typedef enum ESerialCommand { + ESerialCommand_Next, + ESerialCommand_Previous, + ESerialCommand_Scan, + ESerialCommand_MonitorMode, + ESerialCommand_Restart +} ESerialCommand; + +void send_serial_command(ESerialCommand command) { +#if !DISABLE_CONSOLE + return; +#endif + + uint8_t data[1] = {0}; + + switch(command) { + case ESerialCommand_Next: + data[0] = MODULE_CONTROL_COMMAND_NEXT; + break; + case ESerialCommand_Previous: + data[0] = MODULE_CONTROL_COMMAND_PREVIOUS; + break; + case ESerialCommand_Scan: + data[0] = MODULE_CONTROL_COMMAND_SCAN; + break; + case ESerialCommand_MonitorMode: + data[0] = MODULE_CONTROL_COMMAND_MONITOR; + break; + case ESerialCommand_Restart: + data[0] = MODULE_CONTROL_COMMAND_RESTART; + break; + default: + return; + }; + + furi_hal_uart_tx(FuriHalUartIdUSART1, data, 1); +} + +int32_t wifi_scanner_app(void* p) { + UNUSED(p); + + WIFI_APP_LOG_I("Init"); + + // FuriTimer* timer = furi_timer_alloc(blink_test_update, FuriTimerTypePeriodic, event_queue); + // furi_timer_start(timer, furi_kernel_get_tick_frequency()); + + FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(SPluginEvent)); + + SWiFiScannerApp* app = malloc(sizeof(SWiFiScannerApp)); + + wifi_scanner_app_init(app); + +#if ENABLE_MODULE_DETECTION + furi_hal_gpio_init( + &gpio_ext_pc0, + GpioModeInput, + GpioPullUp, + GpioSpeedLow); // Connect to the Flipper's ground just to be sure + //furi_hal_gpio_add_int_callback(pinD0, input_isr_d0, this); + app->m_context = WaitingForModule; +#else + app->m_context = Initializing; +#if ENABLE_MODULE_POWER + furi_hal_power_enable_otg(); +#endif // ENABLE_MODULE_POWER +#endif // ENABLE_MODULE_DETECTION + + ValueMutex app_data_mutex; + if(!init_mutex(&app_data_mutex, app, sizeof(SWiFiScannerApp))) { + WIFI_APP_LOG_E("cannot create mutex\r\n"); + free(app); + return 255; + } + + WIFI_APP_LOG_I("Mutex created"); + + app->m_rx_stream = xStreamBufferCreate(1 * 1024, 1); + + app->m_notification = furi_record_open("notification"); + + ViewPort* view_port = view_port_alloc(); + view_port_draw_callback_set(view_port, wifi_module_render_callback, &app_data_mutex); + view_port_input_callback_set(view_port, wifi_module_input_callback, event_queue); + + // Open GUI and register view_port + Gui* gui = furi_record_open("gui"); + gui_add_view_port(gui, view_port, GuiLayerFullscreen); + + //notification_message(app->notification, &sequence_set_only_blue_255); + + // Enable uart listener +#if DISABLE_CONSOLE + furi_hal_console_disable(); +#endif + furi_hal_uart_set_br(FuriHalUartIdUSART1, FLIPPERZERO_SERIAL_BAUD); + furi_hal_uart_set_irq_cb(FuriHalUartIdUSART1, uart_on_irq_cb, app); + WIFI_APP_LOG_I("UART Listener created"); + + app->m_worker_thread = furi_thread_alloc(); + furi_thread_set_name(app->m_worker_thread, "WiFiModuleUARTWorker"); + furi_thread_set_stack_size(app->m_worker_thread, 1024); + furi_thread_set_context(app->m_worker_thread, &app_data_mutex); + furi_thread_set_callback(app->m_worker_thread, uart_worker); + furi_thread_start(app->m_worker_thread); + WIFI_APP_LOG_I("UART thread allocated"); + + // Because we assume that module was on before we launched the app. We need to ensure that module will be in initial state on app start + send_serial_command(ESerialCommand_Restart); + + SPluginEvent event; + for(bool processing = true; processing;) { + FuriStatus event_status = furi_message_queue_get(event_queue, &event, 100); + SWiFiScannerApp* app = (SWiFiScannerApp*)acquire_mutex_block(&app_data_mutex); + +#if ENABLE_MODULE_DETECTION + if(!app->m_wifiModuleAttached) { + if(furi_hal_gpio_read(&gpio_ext_pc0) == false) { + WIFI_APP_LOG_I("Module Attached"); + app->m_wifiModuleAttached = true; + app->m_context = Initializing; +#if ENABLE_MODULE_POWER + furi_hal_power_enable_otg(); +#endif + } + } +#endif // ENABLE_MODULE_DETECTION + + if(event_status == FuriStatusOk) { + if(event.m_type == EventTypeKey) { + if(app->m_wifiModuleInitialized) { + if(app->m_context == ScanMode) { + switch(event.m_input.key) { + case InputKeyUp: + case InputKeyLeft: + if(event.m_input.type == InputTypeShort) { + WIFI_APP_LOG_I("Previous"); + send_serial_command(ESerialCommand_Previous); + } else if(event.m_input.type == InputTypeRepeat) { + WIFI_APP_LOG_I("Previous Repeat"); + send_serial_command(ESerialCommand_Previous); + } + break; + case InputKeyDown: + case InputKeyRight: + if(event.m_input.type == InputTypeShort) { + WIFI_APP_LOG_I("Next"); + send_serial_command(ESerialCommand_Next); + } else if(event.m_input.type == InputTypeRepeat) { + WIFI_APP_LOG_I("Next Repeat"); + send_serial_command(ESerialCommand_Next); + } + break; + default: + break; + } + } + + switch(event.m_input.key) { + case InputKeyOk: + if(event.m_input.type == InputTypeShort) { + if(app->m_context == ScanMode) { + WIFI_APP_LOG_I("Monitor Mode"); + send_serial_command(ESerialCommand_MonitorMode); + } + } else if(event.m_input.type == InputTypeLong) { + WIFI_APP_LOG_I("Scan"); + send_serial_command(ESerialCommand_Scan); + } + break; + case InputKeyBack: + if(event.m_input.type == InputTypeShort) { + switch(app->m_context) { + case MonitorMode: + send_serial_command(ESerialCommand_Scan); + break; + case ScanMode: + processing = false; + break; + default: + break; + } + } else if(event.m_input.type == InputTypeLong) { + processing = false; + } + break; + default: + break; + } + } else { + if(event.m_input.key == InputKeyBack) { + if(event.m_input.type == InputTypeShort || + event.m_input.type == InputTypeLong) { + processing = false; + } + } + } + } + } else { + WIFI_APP_LOG_D("osMessageQueue: event timeout"); + } + +#if ENABLE_MODULE_DETECTION + if(app->m_wifiModuleAttached && furi_hal_gpio_read(&gpio_ext_pc0) == true) { + WIFI_APP_LOG_D("Module Disconnected - Exit"); + processing = false; + app->m_wifiModuleAttached = false; + app->m_wifiModuleInitialized = false; + } +#endif + + view_port_update(view_port); + release_mutex(&app_data_mutex, app); + } + + WIFI_APP_LOG_I("Start exit app"); + + furi_thread_flags_set(furi_thread_get_id(app->m_worker_thread), WorkerEventStop); + furi_thread_join(app->m_worker_thread); + furi_thread_free(app->m_worker_thread); + + WIFI_APP_LOG_I("Thread Deleted"); + +#if DISABLE_CONSOLE + furi_hal_console_enable(); +#endif + + view_port_enabled_set(view_port, false); + + gui_remove_view_port(gui, view_port); + + // Close gui record + furi_record_close("gui"); + furi_record_close("notification"); + app->m_gui = NULL; + + view_port_free(view_port); + + furi_message_queue_free(event_queue); + + vStreamBufferDelete(app->m_rx_stream); + + delete_mutex(&app_data_mutex); + + // Free rest + free(app); + + WIFI_APP_LOG_I("App freed"); + +#if ENABLE_MODULE_POWER + furi_hal_power_disable_otg(); +#endif + + return 0; +} \ No newline at end of file