unleashed-firmware/applications/plugins/wifi_scanner/wifi_scanner.c

854 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 {
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) {
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)" :
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;
}
}
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);
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 = acquire_mutex((ValueMutex*)context, 25);
if(app == NULL) {
return 1;
}
FuriStreamBuffer* 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;
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 = acquire_mutex((ValueMutex*)context, 25);
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]));
}
}
release_mutex((ValueMutex*)context, app);
// 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
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 = furi_stream_buffer_alloc(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(RECORD_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(RECORD_GUI);
furi_record_close("notification");
app->m_gui = NULL;
view_port_free(view_port);
furi_message_queue_free(event_queue);
furi_stream_buffer_free(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;
}