Просмотр исходного кода

[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>
gornekich 4 лет назад
Родитель
Сommit
0af6c9882e

+ 183 - 5
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;
 }

+ 2 - 0
applications/bt/bt.h

@@ -1 +1,3 @@
 #pragma once
+
+typedef struct Bt Bt;

+ 25 - 2
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);

+ 58 - 0
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;

+ 188 - 0
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;
+}

+ 48 - 0
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);

+ 21 - 0
firmware/targets/api-hal-include/api-hal-bt.h

@@ -10,6 +10,9 @@ extern "C" {
 /* Initialize */
 void api_hal_bt_init();
 
+/* Start BLE app */
+bool api_hal_bt_start_app();
+
 /* Get BT/BLE system component state */
 void api_hal_bt_dump_state(string_t buffer);
 
@@ -24,6 +27,24 @@ bool api_hal_bt_lock_flash();
 /* Unlock shared access to flash controller */
 void api_hal_bt_unlock_flash();
 
+/* Start ble tone tx at given channel and power */
+void api_hal_bt_start_tone_tx(uint8_t tx_channel, uint8_t power);
+
+/* Stop ble tone tx */
+void api_hal_bt_stop_tone_tx();
+
+/* Start sending ble packets at a given frequency and datarate */
+void api_hal_bt_start_packet_tx(uint8_t frequency, uint8_t datarate);
+
+/* Stop sending ble packets */
+void api_hal_bt_stop_packet_tx();
+
+/* Set up the RF to listen to a given RF channel */
+void api_hal_bt_start_rx(uint8_t frequency);
+
+/* Stop RF listenning */
+void api_hal_bt_stop_rx();
+
 #ifdef __cplusplus
 }
 #endif

+ 31 - 0
firmware/targets/f4/api-hal/api-hal-bt.c

@@ -4,6 +4,7 @@
 #include <stm32wbxx.h>
 #include <shci.h>
 #include <cmsis_os2.h>
+#include <app_ble.h>
 
 void api_hal_bt_init() {
     // Explicitly tell that we are in charge of CLK48 domain
@@ -12,6 +13,10 @@ void api_hal_bt_init() {
     APPE_Init();
 }
 
+bool api_hal_bt_start_app() {
+    return APP_BLE_Start();
+}
+
 void api_hal_bt_dump_state(string_t buffer) {
     BleGlueStatus status = APPE_Status();
     if (status == BleGlueStatusStarted) {
@@ -76,3 +81,29 @@ void api_hal_bt_unlock_flash() {
         HAL_HSEM_Release(CFG_HW_FLASH_SEMID, HSEM_CPU1_COREID);
     }
 }
+
+void api_hal_bt_start_tone_tx(uint8_t tx_channel, uint8_t power) {
+    aci_hal_set_tx_power_level(0, power);
+    aci_hal_tone_start(tx_channel, 0);
+}
+
+void api_hal_bt_stop_tone_tx() {
+    aci_hal_tone_stop();
+}
+
+void api_hal_bt_start_packet_tx(uint8_t frequency, uint8_t datarate) {
+    hci_le_enhanced_transmitter_test(frequency, 0x25, 2, datarate);
+}
+
+void api_hal_bt_stop_packet_tx() {
+    uint16_t num_of_packets;
+    hci_le_test_end(&num_of_packets);
+}
+
+void api_hal_bt_start_rx(uint8_t frequency) {
+    aci_hal_rx_start(frequency);
+}
+
+void api_hal_bt_stop_rx() {
+    aci_hal_rx_stop();
+}

+ 6 - 1
firmware/targets/f4/ble-glue/app_ble.c

@@ -1,5 +1,6 @@
 #include "main.h"
 
+#include "app_entry.h"
 #include "app_common.h"
 #include "dbg_trace.h"
 #include "ble.h"
@@ -158,7 +159,11 @@ bool APP_BLE_Init() {
   // Register the hci transport layer to handle BLE User Asynchronous Events
   HciUserEvtProcessId = osThreadNew(HciUserEvtProcess, NULL, &HciUserEvtProcess_attr);
   // Starts the BLE Stack on CPU2
-  if (SHCI_C2_BLE_Init( &ble_init_cmd_packet ) != SHCI_Success) {
+  return (SHCI_C2_BLE_Init( &ble_init_cmd_packet ) == SHCI_Success);
+}
+
+bool APP_BLE_Start() {
+  if (APPE_Status() != BleGlueStatusStarted) {
     return false;
   }
   // Initialization of HCI & GATT & GAP layer

+ 1 - 0
firmware/targets/f4/ble-glue/app_ble.h

@@ -18,6 +18,7 @@ typedef enum {
 } APP_BLE_ConnStatus_t;
 
 bool APP_BLE_Init();
+bool APP_BLE_Start();
 
 APP_BLE_ConnStatus_t APP_BLE_Get_Server_Connection_Status();
 

+ 31 - 0
firmware/targets/f5/api-hal/api-hal-bt.c

@@ -4,6 +4,7 @@
 #include <stm32wbxx.h>
 #include <shci.h>
 #include <cmsis_os2.h>
+#include <app_ble.h>
 
 void api_hal_bt_init() {
     // Explicitly tell that we are in charge of CLK48 domain
@@ -12,6 +13,10 @@ void api_hal_bt_init() {
     APPE_Init();
 }
 
+bool api_hal_bt_start_app() {
+    return APP_BLE_Start();
+}
+
 void api_hal_bt_dump_state(string_t buffer) {
     BleGlueStatus status = APPE_Status();
     if (status == BleGlueStatusStarted) {
@@ -76,3 +81,29 @@ void api_hal_bt_unlock_flash() {
         HAL_HSEM_Release(CFG_HW_FLASH_SEMID, HSEM_CPU1_COREID);
     }
 }
+
+void api_hal_bt_start_tone_tx(uint8_t tx_channel, uint8_t power) {
+    aci_hal_set_tx_power_level(0, power);
+    aci_hal_tone_start(tx_channel, 0);
+}
+
+void api_hal_bt_stop_tone_tx() {
+    aci_hal_tone_stop();
+}
+
+void api_hal_bt_start_packet_tx(uint8_t frequency, uint8_t datarate) {
+    hci_le_enhanced_transmitter_test(frequency, 0x25, 2, datarate);
+}
+
+void api_hal_bt_stop_packet_tx() {
+    uint16_t num_of_packets;
+    hci_le_test_end(&num_of_packets);
+}
+
+void api_hal_bt_start_rx(uint8_t frequency) {
+    aci_hal_rx_start(frequency);
+}
+
+void api_hal_bt_stop_rx() {
+    aci_hal_rx_stop();
+}

+ 6 - 1
firmware/targets/f5/ble-glue/app_ble.c

@@ -1,5 +1,6 @@
 #include "main.h"
 
+#include "app_entry.h"
 #include "app_common.h"
 #include "dbg_trace.h"
 #include "ble.h"
@@ -158,7 +159,11 @@ bool APP_BLE_Init() {
   // Register the hci transport layer to handle BLE User Asynchronous Events
   HciUserEvtProcessId = osThreadNew(HciUserEvtProcess, NULL, &HciUserEvtProcess_attr);
   // Starts the BLE Stack on CPU2
-  if (SHCI_C2_BLE_Init( &ble_init_cmd_packet ) != SHCI_Success) {
+  return (SHCI_C2_BLE_Init( &ble_init_cmd_packet ) == SHCI_Success);
+}
+
+bool APP_BLE_Start() {
+  if (APPE_Status() != BleGlueStatusStarted) {
     return false;
   }
   // Initialization of HCI & GATT & GAP layer

+ 1 - 0
firmware/targets/f5/ble-glue/app_ble.h

@@ -18,6 +18,7 @@ typedef enum {
 } APP_BLE_ConnStatus_t;
 
 bool APP_BLE_Init();
+bool APP_BLE_Start();
 
 APP_BLE_ConnStatus_t APP_BLE_Get_Server_Connection_Status();