Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/ClusterM/flipperzero-firmware.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorgornekich <44112859+gornekich@users.noreply.github.com>2021-03-11 12:31:07 +0300
committerGitHub <noreply@github.com>2021-03-11 12:31:07 +0300
commit0af6c9882e31bce95d4d852ad5b4217bf8b313d9 (patch)
treef1d3f6c4105fa458c449d7298a588c668379650a /applications/bt
parentb920248693b79d3beb2afb3dcf2fe593b72e9a8b (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.c188
-rw-r--r--applications/bt/bt.h2
-rw-r--r--applications/bt/bt_i.h27
-rw-r--r--applications/bt/bt_types.h58
-rw-r--r--applications/bt/bt_views.c188
-rw-r--r--applications/bt/bt_views.h48
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);