unleashed-firmware/applications/plugins/wifi_scanner/wifi_scanner.c
MX 28eb4d1060
Massive plugins refactoring
Not full refactoring, only small issues is fixed and moved all plugins to furi mutex instead of valuemutex

Many small issues was found and fixed due mutex upgrade

OFW removed 60 lines of code and it was painful
2023-03-08 00:18:23 +03:00

858 lines
30 KiB
C

#include <furi.h>
#include <furi_hal_console.h>
#include <furi_hal_gpio.h>
#include <furi_hal_power.h>
#include <furi_hal_uart.h>
#include <gui/canvas_i.h>
#include <gui/gui.h>
#include <input/input.h>
#include <math.h>
#include <notification/notification.h>
#include <notification/notification_messages.h>
#include <stdlib.h>
#include <u8g2.h>
#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 {
FuriString* m_accessPointName;
int16_t m_rssi;
FuriString* m_secType;
FuriString* 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 {
FuriMutex* mutex;
Gui* m_gui;
FuriThread* m_worker_thread;
NotificationApp* m_notification;
FuriStreamBuffer* 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;
app->m_currentAccesspointDescription.m_accessPointName = furi_string_alloc();
furi_string_set(app->m_currentAccesspointDescription.m_accessPointName, "N/A\n");
app->m_currentAccesspointDescription.m_channel = 0;
app->m_currentAccesspointDescription.m_bssid = furi_string_alloc();
furi_string_set(app->m_currentAccesspointDescription.m_bssid, "N/A\n");
app->m_currentAccesspointDescription.m_secType = furi_string_alloc();
furi_string_set(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) {
furi_assert(ctx);
SWiFiScannerApp* app = ctx;
furi_mutex_acquire(app->mutex, FuriWaitForever);
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)" :
furi_string_get_cstr(app->m_currentAccesspointDescription.m_accessPointName));
offsetY += fontHeight;
canvas_draw_str(
canvas,
offsetX,
offsetY,
furi_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];
snprintf(
string, sizeof(string), "RSSI: %d", app->m_currentAccesspointDescription.m_rssi);
canvas_draw_str(canvas, offsetX, offsetY, string);
offsetY += fontHeight + 1;
snprintf(
string, sizeof(string), "CHNL: %d", app->m_currentAccesspointDescription.m_channel);
canvas_draw_str(canvas, offsetX, offsetY, string);
offsetY += fontHeight + 1;
snprintf(
string,
sizeof(string),
"ENCR: %s",
furi_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);
snprintf(
string,
sizeof(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];
snprintf(rssi, sizeof(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,
furi_string_get_cstr(app->m_currentAccesspointDescription.m_accessPointName));
offsetY += fontHeight + 2;
canvas_draw_str(
canvas,
offsetX,
offsetY,
furi_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;
}
}
furi_mutex_release(app->mutex);
}
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);
SWiFiScannerApp* app = context;
WIFI_APP_LOG_I("uart_echo_on_irq_cb");
if(ev == UartIrqEventRXNE) {
WIFI_APP_LOG_I("ev == UartIrqEventRXNE");
furi_stream_buffer_send(app->m_rx_stream, &data, 1, 0);
furi_thread_flags_set(furi_thread_get_id(app->m_worker_thread), WorkerEventRx);
}
}
static int32_t uart_worker(void* context) {
furi_assert(context);
SWiFiScannerApp* app = context;
furi_mutex_acquire(app->mutex, FuriWaitForever);
if(app == NULL) {
return 1;
}
FuriStreamBuffer* rx_stream = app->m_rx_stream;
furi_mutex_release(app->mutex);
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;
FuriString* receivedString;
receivedString = furi_string_alloc();
do {
uint8_t data[64];
length = furi_stream_buffer_receive(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++) {
furi_string_push_back(receivedString, data[i]);
}
//notification_message(app->notification, &sequence_set_only_red_255);
}
} while(length > 0);
if(furi_string_size(receivedString) > 0) {
FuriString* chunk;
chunk = furi_string_alloc();
size_t begin = 0;
size_t end = 0;
size_t stringSize = furi_string_size(receivedString);
WIFI_APP_LOG_I("Received string: %s", furi_string_get_cstr(receivedString));
FuriString* chunksArray[EChunkArrayData_ENUM_MAX];
for(uint8_t i = 0; i < EChunkArrayData_ENUM_MAX; ++i) {
chunksArray[i] = furi_string_alloc();
}
uint8_t index = 0;
do {
end = furi_string_search_char(receivedString, '+', begin);
if(end == FURI_STRING_FAILURE) {
end = stringSize;
}
WIFI_APP_LOG_I("size: %i, begin: %i, end: %i", stringSize, begin, end);
furi_string_set_strn(
chunk, &furi_string_get_cstr(receivedString)[begin], end - begin);
WIFI_APP_LOG_I("String chunk: %s", furi_string_get_cstr(chunk));
furi_string_set(chunksArray[index++], chunk);
begin = end + 1;
} while(end < stringSize);
furi_string_free(chunk);
app = context;
furi_mutex_acquire(app->mutex, FuriWaitForever);
if(app == NULL) {
return 1;
}
if(!app->m_wifiModuleInitialized) {
if(furi_string_cmp_str(
chunksArray[EChunkArrayData_Context], MODULE_CONTEXT_INITIALIZATION) ==
0) {
app->m_wifiModuleInitialized = true;
app->m_context = ScanAnimation;
}
} else {
if(furi_string_cmp_str(
chunksArray[EChunkArrayData_Context], MODULE_CONTEXT_MONITOR) == 0) {
app->m_context = MonitorMode;
} else if(
furi_string_cmp_str(
chunksArray[EChunkArrayData_Context], MODULE_CONTEXT_SCAN) == 0) {
app->m_context = ScanMode;
} else if(
furi_string_cmp_str(
chunksArray[EChunkArrayData_Context], MODULE_CONTEXT_SCAN_ANIMATION) ==
0) {
app->m_context = ScanAnimation;
} else if(
furi_string_cmp_str(
chunksArray[EChunkArrayData_Context],
MODULE_CONTEXT_MONITOR_ANIMATION) == 0) {
app->m_context = MonitorAnimation;
}
if(app->m_context == MonitorMode || app->m_context == ScanMode) {
furi_string_set(
app->m_currentAccesspointDescription.m_accessPointName,
chunksArray[EChunkArrayData_SSID]);
furi_string_set(
app->m_currentAccesspointDescription.m_secType,
chunksArray[EChunkArrayData_EncryptionType]);
app->m_currentAccesspointDescription.m_rssi =
atoi(furi_string_get_cstr(chunksArray[EChunkArrayData_RSSI]));
furi_string_set(
app->m_currentAccesspointDescription.m_bssid,
chunksArray[EChunkArrayData_BSSID]);
app->m_currentAccesspointDescription.m_channel =
atoi(furi_string_get_cstr(chunksArray[EChunkArrayData_Channel]));
app->m_currentAccesspointDescription.m_isHidden =
atoi(furi_string_get_cstr(chunksArray[EChunkArrayData_IsHidden]));
app->m_currentIndexAccessPoint = atoi(
furi_string_get_cstr(chunksArray[EChunkArrayData_CurrentAPIndex]));
app->m_totalAccessPoints =
atoi(furi_string_get_cstr(chunksArray[EChunkArrayData_TotalAps]));
}
}
furi_mutex_release(app->mutex);
// Clear string array
for(index = 0; index < EChunkArrayData_ENUM_MAX; ++index) {
furi_string_free(chunksArray[index]);
}
}
furi_string_free(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
app->mutex = furi_mutex_alloc(FuriMutexTypeNormal);
if(!app->mutex) {
WIFI_APP_LOG_E("cannot create mutex\r\n");
free(app);
return 255;
}
WIFI_APP_LOG_I("Mutex created");
app->m_notification = furi_record_open(RECORD_NOTIFICATION);
ViewPort* view_port = view_port_alloc();
view_port_draw_callback_set(view_port, wifi_module_render_callback, app);
view_port_input_callback_set(view_port, wifi_module_input_callback, event_queue);
// Open GUI and register view_port
Gui* gui = furi_record_open(RECORD_GUI);
gui_add_view_port(gui, view_port, GuiLayerFullscreen);
//notification_message(app->notification, &sequence_set_only_blue_255);
app->m_rx_stream = furi_stream_buffer_alloc(1 * 1024, 1);
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);
furi_thread_set_callback(app->m_worker_thread, uart_worker);
furi_thread_start(app->m_worker_thread);
WIFI_APP_LOG_I("UART thread allocated");
// 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");
// 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);
furi_mutex_acquire(app->mutex, FuriWaitForever);
#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;
}
}
}
}
}
#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);
furi_mutex_release(app->mutex);
}
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");
// Reset GPIO pins to default state
furi_hal_gpio_init(&gpio_ext_pc0, GpioModeAnalog, GpioPullNo, GpioSpeedLow);
#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(RECORD_GUI);
furi_record_close(RECORD_NOTIFICATION);
app->m_gui = NULL;
view_port_free(view_port);
furi_message_queue_free(event_queue);
furi_stream_buffer_free(app->m_rx_stream);
furi_mutex_free(app->mutex);
// Free rest
free(app);
WIFI_APP_LOG_I("App freed");
#if ENABLE_MODULE_POWER
furi_hal_power_disable_otg();
#endif
return 0;
}