diff options
author | gornekich <44112859+gornekich@users.noreply.github.com> | 2021-03-11 12:31:07 +0300 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-03-11 12:31:07 +0300 |
commit | 0af6c9882e31bce95d4d852ad5b4217bf8b313d9 (patch) | |
tree | f1d3f6c4105fa458c449d7298a588c668379650a /applications/bt | |
parent | b920248693b79d3beb2afb3dcf2fe593b72e9a8b (diff) |
[FL-580] Prepare BLE for certification (#376)
* api-hal-bt: separate bt init to core init and app start
* bt: add continuous TX and RX tests
* bt: finish rx test on Back button click
* api-hal-bt: check core 2 started, same f4 and f5 implementation
* bt: refactoring, move hopping test logic to main thread
Co-authored-by: あく <alleteam@gmail.com>
Diffstat (limited to 'applications/bt')
-rw-r--r-- | applications/bt/bt.c | 188 | ||||
-rw-r--r-- | applications/bt/bt.h | 2 | ||||
-rw-r--r-- | applications/bt/bt_i.h | 27 | ||||
-rw-r--r-- | applications/bt/bt_types.h | 58 | ||||
-rw-r--r-- | applications/bt/bt_views.c | 188 | ||||
-rw-r--r-- | applications/bt/bt_views.h | 48 |
6 files changed, 504 insertions, 7 deletions
diff --git a/applications/bt/bt.c b/applications/bt/bt.c index 87e8f547..b4624090 100644 --- a/applications/bt/bt.c +++ b/applications/bt/bt.c @@ -1,13 +1,41 @@ #include "bt_i.h" +uint32_t bt_view_exit(void* context) { + (void)context; + return VIEW_NONE; +} + +void bt_update_statusbar(void* arg) { + furi_assert(arg); + Bt* bt = arg; + BtMessage m = {.type = BtMessageTypeUpdateStatusbar}; + furi_check(osMessageQueuePut(bt->message_queue, &m, 0, osWaitForever) == osOK); +} + +void bt_switch_freq(void* arg) { + furi_assert(arg); + Bt* bt = arg; + BtMessage m = {.type = BtMessageTypeStartTestToneTx}; + furi_check(osMessageQueuePut(bt->message_queue, &m, 0, osWaitForever) == osOK); +} + Bt* bt_alloc() { Bt* bt = furi_alloc(sizeof(Bt)); + bt->message_queue = osMessageQueueNew(8, sizeof(BtMessage), NULL); + bt->update_status_timer = osTimerNew(bt_update_statusbar, osTimerPeriodic, bt, NULL); + osTimerStart(bt->update_status_timer, 4000); + bt->hopping_mode_timer = osTimerNew(bt_switch_freq, osTimerPeriodic, bt, NULL); bt->cli = furi_record_open("cli"); cli_add_command(bt->cli, "bt_info", bt_cli_info, bt); bt->gui = furi_record_open("gui"); bt->menu = furi_record_open("menu"); + bt->state.type = BtStatusReady; + bt->state.param.channel = BtChannel2402; + bt->state.param.power = BtPower0dB; + bt->state.param.datarate = BtDateRate1M; + bt->statusbar_view_port = view_port_alloc(); view_port_set_width(bt->statusbar_view_port, 5); view_port_draw_callback_set(bt->statusbar_view_port, bt_draw_statusbar_callback, bt); @@ -16,9 +44,48 @@ Bt* bt_alloc() { bt->menu_icon = assets_icons_get(A_Bluetooth_14); bt->menu_item = menu_item_alloc_menu("Bluetooth", bt->menu_icon); + menu_item_subitem_add( + bt->menu_item, menu_item_alloc_function("Test tone TX", NULL, bt_menu_test_tone_tx, bt)); + menu_item_subitem_add( + bt->menu_item, + menu_item_alloc_function("Test packet TX", NULL, bt_menu_test_packet_tx, bt)); + menu_item_subitem_add( + bt->menu_item, menu_item_alloc_function("Test tone RX", NULL, bt_menu_test_tone_rx, bt)); + menu_item_subitem_add( + bt->menu_item, menu_item_alloc_function("Start app", NULL, bt_menu_start_app, bt)); + + bt->view_test_tone_tx = view_alloc(); + view_set_context(bt->view_test_tone_tx, bt); + view_set_draw_callback(bt->view_test_tone_tx, bt_view_test_tone_tx_draw); + view_allocate_model( + bt->view_test_tone_tx, ViewModelTypeLocking, sizeof(BtViewTestToneTxModel)); + view_set_input_callback(bt->view_test_tone_tx, bt_view_test_tone_tx_input); + bt->view_test_packet_tx = view_alloc(); + view_set_context(bt->view_test_packet_tx, bt); + view_set_draw_callback(bt->view_test_packet_tx, bt_view_test_packet_tx_draw); + view_allocate_model( + bt->view_test_packet_tx, ViewModelTypeLocking, sizeof(BtViewTestPacketTxModel)); + view_set_input_callback(bt->view_test_packet_tx, bt_view_test_packet_tx_input); + bt->view_test_tone_rx = view_alloc(); + view_set_context(bt->view_test_tone_rx, bt); + view_set_draw_callback(bt->view_test_tone_rx, bt_view_test_tone_rx_draw); + view_allocate_model(bt->view_test_tone_rx, ViewModelTypeLocking, sizeof(BtViewTestRxModel)); + view_set_input_callback(bt->view_test_tone_rx, bt_view_test_tone_rx_input); + bt->view_start_app = view_alloc(); + view_set_context(bt->view_start_app, bt); + view_set_draw_callback(bt->view_start_app, bt_view_app_draw); + view_set_previous_callback(bt->view_start_app, bt_view_exit); + bt->view_dispatcher = view_dispatcher_alloc(); + view_dispatcher_add_view(bt->view_dispatcher, BtViewTestToneTx, bt->view_test_tone_tx); + view_dispatcher_add_view(bt->view_dispatcher, BtViewTestPacketTx, bt->view_test_packet_tx); + view_dispatcher_add_view(bt->view_dispatcher, BtViewTestToneRx, bt->view_test_tone_rx); + view_dispatcher_add_view(bt->view_dispatcher, BtViewStartApp, bt->view_start_app); + + Gui* gui = furi_record_open("gui"); + view_dispatcher_attach_to_gui(bt->view_dispatcher, gui, ViewDispatcherTypeFullscreen); + with_value_mutex( bt->menu, (Menu * menu) { menu_item_add(menu, bt->menu_item); }); - return bt; } @@ -26,6 +93,47 @@ void bt_draw_statusbar_callback(Canvas* canvas, void* context) { canvas_draw_icon_name(canvas, 0, 0, I_Bluetooth_5x8); } +void bt_menu_test_tone_tx(void* context) { + furi_assert(context); + Bt* bt = context; + bt->state.type = BtStatusToneTx; + BtMessage message = { + .type = BtMessageTypeStartTestToneTx, + .param.channel = bt->state.param.channel, + .param.power = bt->state.param.power}; + furi_check(osMessageQueuePut(bt->message_queue, &message, 0, osWaitForever) == osOK); +} + +void bt_menu_test_packet_tx(void* context) { + furi_assert(context); + Bt* bt = context; + bt->state.type = BtStatusPacketSetup; + BtMessage message = { + .type = BtMessageTypeSetupTestPacketTx, + .param.channel = bt->state.param.channel, + .param.datarate = bt->state.param.datarate}; + furi_check(osMessageQueuePut(bt->message_queue, &message, 0, osWaitForever) == osOK); +} + +void bt_menu_test_tone_rx(void* context) { + furi_assert(context); + Bt* bt = context; + bt->state.type = BtStatusToneRx; + BtMessage message = { + .type = BtMessageTypeStartTestRx, + .param.channel = bt->state.param.channel, + .param.power = bt->state.param.power}; + furi_check(osMessageQueuePut(bt->message_queue, &message, 0, osWaitForever) == osOK); +} + +void bt_menu_start_app(void* context) { + furi_assert(context); + Bt* bt = context; + bt->state.type = BtStatusStartedApp; + BtMessage message = {.type = BtMessageTypeStartApp}; + furi_check(osMessageQueuePut(bt->message_queue, &message, 0, osWaitForever) == osOK); +} + void bt_cli_info(string_t args, void* context) { string_t buffer; string_init(buffer); @@ -40,11 +148,81 @@ int32_t bt_task() { furi_record_create("bt", bt); api_hal_bt_init(); - + BtMessage message; while(1) { - view_port_enabled_set(bt->statusbar_view_port, api_hal_bt_is_alive()); - osDelay(1024); + furi_check(osMessageQueueGet(bt->message_queue, &message, NULL, osWaitForever) == osOK); + if(message.type == BtMessageTypeStartTestToneTx) { + // Start test tx + api_hal_bt_stop_tone_tx(); + if(bt->state.type == BtStatusToneTx) { + api_hal_bt_start_tone_tx(message.param.channel, message.param.power); + } else { + bt->state.param.channel = + bt_switch_channel(InputKeyRight, bt->state.param.channel); + bt->state.param.power = BtPower6dB; + api_hal_bt_start_tone_tx(bt->state.param.channel, bt->state.param.power); + } + with_view_model( + bt->view_test_tone_tx, (BtViewTestToneTxModel * model) { + model->type = bt->state.type; + model->channel = bt->state.param.channel; + model->power = bt->state.param.power; + return true; + }); + view_dispatcher_switch_to_view(bt->view_dispatcher, BtViewTestToneTx); + } else if(message.type == BtMessageTypeStopTestToneTx) { + // Stop test tone tx + api_hal_bt_stop_tone_tx(); + bt->state.type = BtStatusReady; + } else if(message.type == BtMessageTypeSetupTestPacketTx) { + // Update packet test setup + api_hal_bt_stop_packet_tx(); + with_view_model( + bt->view_test_packet_tx, (BtViewTestPacketTxModel * model) { + model->type = bt->state.type; + model->channel = bt->state.param.channel; + model->datarate = bt->state.param.datarate; + return true; + }); + view_dispatcher_switch_to_view(bt->view_dispatcher, BtViewTestPacketTx); + } else if(message.type == BtMessageTypeStartTestPacketTx) { + // Start sending packets + api_hal_bt_start_packet_tx(message.param.channel, message.param.datarate); + with_view_model( + bt->view_test_packet_tx, (BtViewTestPacketTxModel * model) { + model->type = bt->state.type; + model->channel = bt->state.param.channel; + model->datarate = bt->state.param.datarate; + return true; + }); + view_dispatcher_switch_to_view(bt->view_dispatcher, BtViewTestPacketTx); + } else if(message.type == BtMessageTypeStopTestPacketTx) { + // Stop test packet tx + api_hal_bt_stop_packet_tx(); + bt->state.type = BtStatusReady; + } else if(message.type == BtMessageTypeStartTestRx) { + // Start test rx + api_hal_bt_start_rx(message.param.channel); + with_view_model( + bt->view_test_tone_rx, (BtViewTestRxModel * model) { + model->channel = bt->state.param.channel; + return true; + }); + view_dispatcher_switch_to_view(bt->view_dispatcher, BtViewTestToneRx); + } else if(message.type == BtMessageTypeStopTestRx) { + // Stop test rx + api_hal_bt_stop_rx(); + bt->state.type = BtStatusReady; + } else if(message.type == BtMessageTypeStartApp) { + // Start app + view_dispatcher_switch_to_view(bt->view_dispatcher, BtViewStartApp); + if(api_hal_bt_start_app()) { + bt->state.type = BtStatusStartedApp; + } + } else if(message.type == BtMessageTypeUpdateStatusbar) { + // Update statusbar + view_port_enabled_set(bt->statusbar_view_port, api_hal_bt_is_alive()); + } } - return 0; } diff --git a/applications/bt/bt.h b/applications/bt/bt.h index 6f70f09b..b163fa69 100644 --- a/applications/bt/bt.h +++ b/applications/bt/bt.h @@ -1 +1,3 @@ #pragma once + +typedef struct Bt Bt; diff --git a/applications/bt/bt_i.h b/applications/bt/bt_i.h index 26d47def..f7213c7f 100644 --- a/applications/bt/bt_i.h +++ b/applications/bt/bt_i.h @@ -1,6 +1,8 @@ #pragma once #include "bt.h" +#include "bt_views.h" +#include "bt_types.h" #include <furi.h> #include <api-hal.h> @@ -9,11 +11,17 @@ #include <gui/gui.h> #include <gui/view_port.h> +#include <gui/view.h> +#include <gui/view_dispatcher.h> #include <menu/menu.h> #include <menu/menu_item.h> -typedef struct { +struct Bt { + osMessageQueueId_t message_queue; + BtState state; + osTimerId_t update_status_timer; + osTimerId_t hopping_mode_timer; Cli* cli; Gui* gui; ValueMutex* menu; @@ -22,12 +30,27 @@ typedef struct { // Menu Icon* menu_icon; MenuItem* menu_item; -} Bt; + View* view_test_tone_tx; + View* view_test_packet_tx; + View* view_test_tone_rx; + View* view_start_app; + ViewDispatcher* view_dispatcher; +}; Bt* bt_alloc(); void bt_draw_statusbar_callback(Canvas* canvas, void* context); +BtTestChannel bt_switch_channel(InputKey key, BtTestChannel inst_chan); + void bt_cli_info(string_t args, void* context); void bt_draw_statusbar_callback(Canvas* canvas, void* context); + +void bt_menu_test_tone_tx(void* context); + +void bt_menu_test_packet_tx(void* context); + +void bt_menu_test_tone_rx(void* context); + +void bt_menu_start_app(void* context); diff --git a/applications/bt/bt_types.h b/applications/bt/bt_types.h new file mode 100644 index 00000000..723a7b76 --- /dev/null +++ b/applications/bt/bt_types.h @@ -0,0 +1,58 @@ +#pragma once + +typedef enum { + BtMessageTypeStartTestToneTx, + BtMessageTypeHoppingTx, + BtMessageTypeStopTestToneTx, + BtMessageTypeSetupTestPacketTx, + BtMessageTypeStartTestPacketTx, + BtMessageTypeStopTestPacketTx, + BtMessageTypeStartTestRx, + BtMessageTypeStopTestRx, + BtMessageTypeStartApp, + BtMessageTypeUpdateStatusbar, +} BtMessageType; + +typedef enum { + BtStatusReady, + BtStatusToneTx, + BtStatusHoppingTx, + BtStatusToneRx, + BtStatusPacketSetup, + BtStatusPacketTx, + BtStatusStartedApp, +} BtStateType; + +typedef enum { + BtChannel2402 = 0, + BtChannel2440 = 19, + BtChannel2480 = 39, +} BtTestChannel; + +typedef enum { + BtPower0dB = 0x19, + BtPower2dB = 0x1B, + BtPower4dB = 0x1D, + BtPower6dB = 0x1F, +} BtTestPower; + +typedef enum { + BtDateRate1M = 1, + BtDateRate2M = 2, +} BtTestDataRate; + +typedef struct { + BtTestChannel channel; + BtTestPower power; + BtTestDataRate datarate; +} BtTestParam; + +typedef struct { + BtMessageType type; + BtTestParam param; +} BtMessage; + +typedef struct { + BtStateType type; + BtTestParam param; +} BtState; diff --git a/applications/bt/bt_views.c b/applications/bt/bt_views.c new file mode 100644 index 00000000..01eb4b62 --- /dev/null +++ b/applications/bt/bt_views.c @@ -0,0 +1,188 @@ +#include "bt_views.h" + +void bt_view_test_tone_tx_draw(Canvas* canvas, void* model) { + BtViewTestToneTxModel* m = model; + canvas_clear(canvas); + canvas_set_font(canvas, FontSecondary); + canvas_draw_str(canvas, 0, 12, "Performing continous TX test"); + if(m->type == BtStatusToneTx) { + canvas_draw_str(canvas, 0, 24, "Manual control mode"); + } else { + canvas_draw_str(canvas, 0, 24, "Hopping mode"); + } + char buffer[32]; + snprintf(buffer, sizeof(buffer), "Channel:%d MHz", m->channel * 2 + 2402); + canvas_draw_str(canvas, 0, 36, buffer); + snprintf(buffer, sizeof(buffer), "Power:%d dB", m->power - BtPower0dB); + canvas_draw_str(canvas, 0, 48, buffer); +} + +void bt_view_test_tone_rx_draw(Canvas* canvas, void* model) { + BtViewTestRxModel* m = model; + canvas_clear(canvas); + canvas_set_font(canvas, FontSecondary); + canvas_draw_str(canvas, 0, 12, "Performing continous RX test"); + char buffer[32]; + snprintf(buffer, sizeof(buffer), "Channel:%d MHz", m->channel * 2 + 2402); + canvas_draw_str(canvas, 0, 24, buffer); +} + +void bt_view_test_packet_tx_draw(Canvas* canvas, void* model) { + BtViewTestPacketTxModel* m = model; + canvas_clear(canvas); + canvas_set_font(canvas, FontSecondary); + canvas_draw_str(canvas, 0, 12, "Packets send TX test"); + if(m->type == BtStatusPacketSetup) { + canvas_draw_str(canvas, 0, 24, "Setup parameters"); + canvas_draw_str(canvas, 0, 36, "Press OK to send packets"); + } else { + canvas_draw_str(canvas, 0, 24, "Sending packets"); + canvas_draw_str(canvas, 0, 36, "Packets parameters:"); + } + char buffer[32]; + snprintf(buffer, sizeof(buffer), "Channel:%d MHz", m->channel * 2 + 2402); + canvas_draw_str(canvas, 0, 48, buffer); + snprintf(buffer, sizeof(buffer), "Daterate:%d Mbps", m->datarate); + canvas_draw_str(canvas, 0, 60, buffer); +} + +void bt_view_app_draw(Canvas* canvas, void* model) { + canvas_clear(canvas); + canvas_set_font(canvas, FontSecondary); + canvas_draw_str(canvas, 0, 12, "Start BLE app"); +} + +BtTestChannel bt_switch_channel(InputKey key, BtTestChannel inst_chan) { + uint8_t pos = 0; + BtTestChannel arr[] = {BtChannel2402, BtChannel2440, BtChannel2480}; + for(pos = 0; pos < sizeof(arr); pos++) { + if(arr[pos] == inst_chan) { + break; + } + } + if(key == InputKeyRight) { + pos = (pos + 1) % sizeof(arr); + return arr[pos]; + } else if(key == InputKeyLeft) { + if(pos) { + return arr[pos - 1]; + } else { + return arr[sizeof(arr) - 1]; + } + } + return arr[0]; +} + +bool bt_view_test_tone_tx_input(InputEvent* event, void* context) { + furi_assert(event); + furi_assert(context); + Bt* bt = context; + if(event->type == InputTypeShort) { + if(event->key == InputKeyBack) { + if(osTimerIsRunning(bt->hopping_mode_timer)) { + osTimerStop(bt->hopping_mode_timer); + } + BtMessage m = {.type = BtMessageTypeStopTestToneTx}; + furi_check(osMessageQueuePut(bt->message_queue, &m, 0, osWaitForever) == osOK); + view_dispatcher_switch_to_view(bt->view_dispatcher, VIEW_NONE); + return true; + } else { + if(event->key == InputKeyRight || event->key == InputKeyLeft) { + bt->state.param.channel = bt_switch_channel(event->key, bt->state.param.channel); + } else if(event->key == InputKeyUp) { + if(bt->state.param.power < BtPower6dB) { + bt->state.param.power += 2; + } + } else if(event->key == InputKeyDown) { + if(bt->state.param.power > BtPower0dB) { + bt->state.param.power -= 2; + } + } else if(event->key == InputKeyOk) { + if(bt->state.type == BtStatusToneTx) { + bt->state.type = BtStatusHoppingTx; + osTimerStart(bt->hopping_mode_timer, 2000); + } else { + bt->state.type = BtStatusToneTx; + osTimerStop(bt->hopping_mode_timer); + } + } + BtMessage m = { + .type = BtMessageTypeStartTestToneTx, + .param.channel = bt->state.param.channel, + .param.power = bt->state.param.power}; + furi_check(osMessageQueuePut(bt->message_queue, &m, 0, osWaitForever) == osOK); + return true; + } + } + return false; +} + +bool bt_view_test_tone_rx_input(InputEvent* event, void* context) { + furi_assert(event); + furi_assert(context); + Bt* bt = context; + if(event->type == InputTypeShort) { + if(event->key == InputKeyRight || event->key == InputKeyLeft) { + bt->state.param.channel = bt_switch_channel(event->key, bt->state.param.channel); + BtMessage m = { + .type = BtMessageTypeStartTestRx, .param.channel = bt->state.param.channel}; + furi_check(osMessageQueuePut(bt->message_queue, &m, 0, osWaitForever) == osOK); + return true; + } else if(event->key == InputKeyBack) { + BtMessage m = {.type = BtMessageTypeStopTestRx}; + furi_check(osMessageQueuePut(bt->message_queue, &m, 0, osWaitForever) == osOK); + view_dispatcher_switch_to_view(bt->view_dispatcher, VIEW_NONE); + return true; + } else { + return false; + } + } + return false; +} + +bool bt_view_test_packet_tx_input(InputEvent* event, void* context) { + furi_assert(event); + furi_assert(context); + Bt* bt = context; + if(event->type == InputTypeShort) { + if(event->key < InputKeyOk) { + // Process InputKeyUp, InputKeyDown, InputKeyLeft, InputKeyRight + if(event->key == InputKeyRight || event->key == InputKeyLeft) { + bt->state.param.channel = bt_switch_channel(event->key, bt->state.param.channel); + } else if(event->key == InputKeyUp) { + if(bt->state.param.datarate < BtDateRate2M) { + bt->state.param.datarate += 1; + } + } else if(event->key == InputKeyDown) { + if(bt->state.param.datarate > BtDateRate1M) { + bt->state.param.datarate -= 1; + } + } + bt->state.type = BtStatusPacketSetup; + BtMessage m = { + .type = BtMessageTypeSetupTestPacketTx, + .param.channel = bt->state.param.channel, + .param.datarate = bt->state.param.datarate, + }; + furi_check(osMessageQueuePut(bt->message_queue, &m, 0, osWaitForever) == osOK); + return true; + } else if(event->key == InputKeyOk) { + bt->state.type = BtStatusPacketTx; + BtMessage m = { + .type = BtMessageTypeStartTestPacketTx, + .param.channel = bt->state.param.channel, + .param.datarate = bt->state.param.datarate, + }; + furi_check(osMessageQueuePut(bt->message_queue, &m, 0, osWaitForever) == osOK); + return true; + } else if(event->key == InputKeyBack) { + BtMessage m = { + .type = BtMessageTypeStopTestPacketTx, + }; + furi_check(osMessageQueuePut(bt->message_queue, &m, 0, osWaitForever) == osOK); + view_dispatcher_switch_to_view(bt->view_dispatcher, VIEW_NONE); + return true; + } + } + return false; +} diff --git a/applications/bt/bt_views.h b/applications/bt/bt_views.h new file mode 100644 index 00000000..260ab39e --- /dev/null +++ b/applications/bt/bt_views.h @@ -0,0 +1,48 @@ +#pragma once + +#include "bt_i.h" +#include "bt_types.h" + +#include <stdint.h> +#include <stdbool.h> +#include <gui/canvas.h> +#include <furi.h> +#include <gui/view_dispatcher.h> +#include <gui/view.h> + +typedef enum { + BtViewTestToneTx, + BtViewTestPacketTx, + BtViewTestToneRx, + BtViewStartApp, +} BtView; + +typedef struct { + BtStateType type; + BtTestChannel channel; + BtTestPower power; +} BtViewTestToneTxModel; + +typedef struct { + BtStateType type; + BtTestChannel channel; + BtTestDataRate datarate; +} BtViewTestPacketTxModel; + +typedef struct { + BtTestChannel channel; +} BtViewTestRxModel; + +void bt_view_test_tone_tx_draw(Canvas* canvas, void* model); + +bool bt_view_test_tone_tx_input(InputEvent* event, void* context); + +void bt_view_test_packet_tx_draw(Canvas* canvas, void* model); + +bool bt_view_test_packet_tx_input(InputEvent* event, void* context); + +void bt_view_test_tone_rx_draw(Canvas* canvas, void* model); + +bool bt_view_test_tone_rx_input(InputEvent* event, void* context); + +void bt_view_app_draw(Canvas* canvas, void* model); |