Convert to external .fap; export ST BLE command API from the firmware

build.sh now adds ST's aci_*/hci_* headers to the SDK (with extern "C"
guards, the API table is C++), lets fbt regenerate api_symbols.csv and
enables the selected functions (BLE_API=hci,gatt default; min; all).
The app builds as a normal .fap shipped inside the update package and
loadable from SD, saving ~12 KB of firmware flash versus the built-in
(min mode: ~22 KB). qsort replaced with an insertion sort since libc's
qsort is not exported to apps. README documents modes and flash budget.
This commit is contained in:
2026-04-15 23:57:20 +01:00
parent b1c879d305
commit d943328ad3
6 changed files with 201 additions and 36 deletions
+9 -5
View File
@@ -1,12 +1,16 @@
# Built-in (firmware-linked) app: it needs the ST GAP/GATT/HCI command API,
# which the firmware does not export to .fap apps. See ../build.sh.
# External app. Needs firmware built by ../build.sh: full BLE stack plus the
# ST aci_*/hci_* command API exported to apps.
App(
appid="bt_inspector",
name="BT Inspector",
apptype=FlipperAppType.APP,
apptype=FlipperAppType.EXTERNAL,
entry_point="bt_inspector_app",
requires=["gui", "storage"],
stack_size=4 * 1024,
icon="A_Plugins_14",
order=25,
fap_category="Bluetooth",
fap_icon="icon.png",
fap_version="1.0",
fap_description="BLE scanner and GATT explorer (full-stack firmware)",
fap_author="andydixon",
fap_weburl="https://github.com/andydixon/flipper-bt-inspector",
)
+38 -9
View File
@@ -1,13 +1,37 @@
#include "ble_central.h"
#include <furi_hal.h>
#include <furi_ble/event_dispatcher.h>
#include <ble/ble.h>
#include <interface/patterns/ble_thread/tl/tl.h>
#include <ble/core/ble_defs.h>
#include <ble/core/ble_std.h>
#include <ble/core/auto/ble_types.h>
#include <ble/core/auto/ble_vs_codes.h>
#include <ble/core/auto/ble_hci_le.h>
#include <ble/core/auto/ble_gatt_aci.h>
#include <string.h>
#include <stdlib.h>
#define TAG "BtInspector"
// Wire layout of the packets handed to dispatcher handlers (from ST's tl.h,
// redeclared here so the app only needs the exported SDK headers).
typedef struct __attribute__((packed)) {
uint8_t type;
uint8_t data[1];
} BcHciPkt;
typedef struct __attribute__((packed)) {
uint8_t evt;
uint8_t plen;
uint8_t data[1];
} BcHciEvent;
typedef struct __attribute__((packed)) {
uint8_t subevent;
uint8_t data[1];
} BcLeMetaEvent;
typedef struct __attribute__((packed)) {
uint16_t ecode;
uint8_t data[1];
} BcAciEvent;
#define OWN_ADDR_TYPE GAP_PUBLIC_ADDR // firmware programs a public identity address
#define PROC_TIMEOUT_MS 5000
@@ -134,11 +158,11 @@ static void handle_adv_report(BleCentral* bc, const uint8_t* p, size_t len) {
static BleEventAckStatus bc_event_handler(void* event, void* context) {
BleCentral* bc = context;
hci_event_pckt* pkt = (hci_event_pckt*)((hci_uart_pckt*)event)->data;
BcHciEvent* pkt = (BcHciEvent*)((BcHciPkt*)event)->data;
switch(pkt->evt) {
case HCI_LE_META_EVT_CODE: {
evt_le_meta_event* meta = (evt_le_meta_event*)pkt->data;
BcLeMetaEvent* meta = (BcLeMetaEvent*)pkt->data;
if(meta->subevent == HCI_LE_ADVERTISING_REPORT_SUBEVT_CODE) {
handle_adv_report(bc, meta->data, pkt->plen - 1);
return BleEventAckFlowEnable;
@@ -173,7 +197,7 @@ static BleEventAckStatus bc_event_handler(void* event, void* context) {
return BleEventNotAck;
}
case HCI_VENDOR_SPECIFIC_DEBUG_EVT_CODE: {
evt_blecore_aci* aci = (evt_blecore_aci*)pkt->data;
BcAciEvent* aci = (BcAciEvent*)pkt->data;
uint16_t ch = aci->data[0] | (aci->data[1] << 8); // every event below starts with it
if(!bc->connected || ch != bc->conn_handle) return BleEventNotAck;
@@ -306,9 +330,6 @@ void bc_scan_stop(BleCentral* bc) {
bc->scanning = false;
}
static int cmp_rssi(const void* a, const void* b) {
return ((const BcDevice*)b)->rssi - ((const BcDevice*)a)->rssi;
}
size_t bc_snapshot(BleCentral* bc, BcDevice* out, size_t max) {
size_t n = 0;
@@ -325,7 +346,15 @@ size_t bc_snapshot(BleCentral* bc, BcDevice* out, size_t max) {
d->logged = true;
}
furi_mutex_release(bc->mutex);
qsort(out, n, sizeof(BcDevice), cmp_rssi);
for(size_t i = 1; i < n; i++) { // insertion sort by RSSI, strongest first (n <= 32)
BcDevice d = out[i];
size_t j = i;
while(j > 0 && out[j - 1].rssi < d.rssi) {
out[j] = out[j - 1];
j--;
}
out[j] = d;
}
return n;
}
+1 -1
View File
@@ -10,7 +10,7 @@
#include <gui/modules/text_input.h>
#include <gui/modules/number_input.h>
#include <storage/storage.h>
#include <ble/ble.h>
#include <ble/core/ble_defs.h>
#include <string.h>
#include <stdarg.h>
#include "ble_central.h"
Binary file not shown.

After

Width:  |  Height:  |  Size: 95 B