├── application.fam ├── fbs.h ├── LICENSE ├── README.md └── fbs.c /application.fam: -------------------------------------------------------------------------------- 1 | App( 2 | appid="fbs", 3 | name="Flipper BT Serial App", 4 | apptype=FlipperAppType.EXTERNAL, 5 | entry_point="fbs_app", 6 | stack_size=1 * 1024, 7 | requires=[ 8 | "bt", 9 | "gui", 10 | ], 11 | ) -------------------------------------------------------------------------------- /fbs.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #define TAG "FlipperBTSerial" 11 | 12 | typedef struct { 13 | Bt* bt; 14 | bool bt_connected; 15 | 16 | char* display_text; 17 | 18 | ViewPort* view_port; 19 | Gui* gui; 20 | 21 | FuriMutex* app_mutex; 22 | FuriMessageQueue* event_queue; 23 | } FBS; -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Roman Beltiukov 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Flipper Zero BT Serial Example App 2 | 3 | UPDATE: this code might not working with the current version of FW because of API changes. WIP to update this repo. 4 | 5 | This is a very simple Serial-over-Bluetooth app for Flipper Zero. You can use it (but better not to :D) as a reference app for building your own solutions. 6 | 7 | ## What does it do? 8 | This application hijacks the default Serial-over-Bluetooth connection of a Flipper Zero device (that is usually used for communication with mobile app) and set a custom callback on data received over the connection. Therefore, by modifying this app you can implement your own custom logic on using Serial connection over Bluetooth. As an example - see this repository: https://github.com/maybe-hello-world/flipper-bp 9 | 10 | ## How to use? 11 | 0. Optional: enable debug log on the Flipper device: System - Log level - Debug 12 | 1. Compile and upload the app on the flipper. 13 | 2. Enable bluetooth and pair your device with the flipper. 14 | 3. Run the application on the flipper (optionally: connect flipper via usb and use `fbt cli -> log` to observe the logs) 15 | 4. On your device launch an app that supports serial profile over bluetooth (e.g., for Linux: bluetoothctl) and use these channels for connection: 16 | ``` 17 | tx: 19ed82ae-ed21-4c9d-4145-228e62fe0000 18 | rx: 19ed82ae-ed21-4c9d-4145-228e61fe0000 19 | ``` 20 | Specifically, try to write into `tx` channel some bytes. 21 | 5. Observe bytes received on the Flipper's side. 22 | 23 | ## How to modify it for my own app? 24 | 0. Optional: find a normal Flipper Bluetooth Serial reference app instead of this junk. If it's impossible - go to step 1. 25 | 1. The application substites the default serial callback of the Flipper RPC connection. In the fbs.c modify the `bt_serial_event_callback` function to introduce your own custom logic of processing the data received (or sent) over Bluetooth. 26 | 27 | ## How to build? 28 | See [this](https://github.com/flipperdevices/flipperzero-firmware/blob/dev/documentation/AppsOnSDCard.md) official documentation on how to build external applications for Flipper Zero. 29 | 30 | 31 | -------------------------------------------------------------------------------- /fbs.c: -------------------------------------------------------------------------------- 1 | #include "fbs.h" 2 | 3 | const uint16_t BT_SERIAL_BUFFER_SIZE = 128; 4 | 5 | void draw_callback(Canvas* canvas, void* ctx) { 6 | FBS* app = ctx; 7 | furi_check(furi_mutex_acquire(app->app_mutex, FuriWaitForever) == FuriStatusOk); 8 | 9 | canvas_draw_str_aligned(canvas, 64, 32, AlignCenter, AlignCenter, (char*)app->display_text); 10 | 11 | furi_mutex_release(app->app_mutex); 12 | } 13 | 14 | void input_callback(InputEvent* input, void* ctx) { 15 | FBS* app = ctx; 16 | furi_message_queue_put(app->event_queue, input, FuriWaitForever); 17 | } 18 | 19 | FBS* fbs_alloc() { 20 | FBS* app = malloc(sizeof(FBS)); 21 | app->app_mutex = furi_mutex_alloc(FuriMutexTypeNormal); 22 | app->event_queue = furi_message_queue_alloc(8, sizeof(InputEvent)); 23 | 24 | app->gui = furi_record_open(RECORD_GUI); 25 | app->view_port = view_port_alloc(); 26 | view_port_draw_callback_set(app->view_port, draw_callback, app); 27 | view_port_input_callback_set(app->view_port, input_callback, app); 28 | gui_add_view_port(app->gui, app->view_port, GuiLayerFullscreen); 29 | 30 | app->bt_connected = false; 31 | app->bt = furi_record_open(RECORD_BT); 32 | return app; 33 | } 34 | 35 | void fbs_free(FBS* app) { 36 | view_port_enabled_set(app->view_port, false); 37 | gui_remove_view_port(app->gui, app->view_port); 38 | furi_record_close(RECORD_GUI); 39 | app->gui = NULL; 40 | view_port_free(app->view_port); 41 | free(app->display_text); 42 | 43 | furi_mutex_free(app->app_mutex); 44 | furi_message_queue_free(app->event_queue); 45 | 46 | furi_record_close(RECORD_BT); 47 | app->bt = NULL; 48 | 49 | free(app); 50 | } 51 | 52 | static uint16_t bt_serial_event_callback(SerialServiceEvent event, void* context) { 53 | furi_assert(context); 54 | Bt* bt = context; 55 | UNUSED(bt); 56 | uint16_t ret = 0; 57 | 58 | if(event.event == SerialServiceEventTypeDataReceived) { 59 | FURI_LOG_D(TAG, "SerialServiceEventTypeDataReceived"); 60 | FURI_LOG_D(TAG, "Size: %u", event.data.size); 61 | FURI_LOG_D(TAG, "Data: "); 62 | for (size_t i = 0; i < event.data.size; i++) 63 | { 64 | printf("%X ", event.data.buffer[i]); 65 | } 66 | printf("\r\n"); 67 | } else if(event.event == SerialServiceEventTypeDataSent) { 68 | FURI_LOG_D(TAG, "SerialServiceEventTypeDataSent"); 69 | FURI_LOG_D(TAG, "Size: %u", event.data.size); 70 | FURI_LOG_D(TAG, "Data: "); 71 | for (size_t i = 0; i < event.data.size; i++) 72 | { 73 | printf("%X ", event.data.buffer[i]); 74 | } 75 | printf("\r\n"); 76 | } 77 | return ret; 78 | } 79 | 80 | int32_t fbs_app(void* p) { 81 | UNUSED(p); 82 | FBS* app = fbs_alloc(); 83 | 84 | if (furi_hal_bt_is_active()) { 85 | FURI_LOG_D(TAG, "BT is working, hijacking the serial connection..."); 86 | furi_hal_bt_serial_set_event_callback(BT_SERIAL_BUFFER_SIZE, bt_serial_event_callback, app); 87 | furi_hal_bt_start_advertising(); 88 | } else { 89 | FURI_LOG_D(TAG, "Please, enable the Bluetooth and restart the app"); 90 | } 91 | 92 | InputEvent event; 93 | for(bool processing = true; processing;) { 94 | int status = furi_message_queue_get(app->event_queue, &event, 100); 95 | furi_check(furi_mutex_acquire(app->app_mutex, FuriWaitForever) == FuriStatusOk); 96 | if(status == FuriStatusOk && event.type == InputTypePress && event.key == InputKeyBack) { 97 | processing = false; 98 | } 99 | furi_mutex_release(app->app_mutex); 100 | view_port_update(app->view_port); 101 | } 102 | 103 | furi_hal_bt_serial_set_event_callback(0, NULL, NULL); 104 | 105 | fbs_free(app); 106 | FURI_LOG_D(TAG, "Released everything"); 107 | return 0; 108 | } --------------------------------------------------------------------------------