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:
@@ -9,7 +9,8 @@ services and characteristics: read, write, subscribe, and log it all.
|
|||||||
> Flipper's radio coprocessor ships with ST's "BLE Light" stack, which can only
|
> Flipper's radio coprocessor ships with ST's "BLE Light" stack, which can only
|
||||||
> advertise (peripheral role). It cannot scan for or connect to other devices.
|
> advertise (peripheral role). It cannot scan for or connect to other devices.
|
||||||
> BT Inspector is therefore delivered as a **custom firmware build** that swaps
|
> BT Inspector is therefore delivered as a **custom firmware build** that swaps
|
||||||
> in ST's full BLE stack and compiles the app in. `build.sh` does all of this.
|
> in ST's full BLE stack and exports the BLE command API to apps. The app itself
|
||||||
|
> is a normal `.fap`. `build.sh` does all of this.
|
||||||
> See [Why a custom firmware build](#why-a-custom-firmware-build).
|
> See [Why a custom firmware build](#why-a-custom-firmware-build).
|
||||||
|
|
||||||
## Features
|
## Features
|
||||||
@@ -62,19 +63,34 @@ Requirements: Linux or macOS with `git`, `python3` and a few GB of disk. The
|
|||||||
firmware build system downloads its own ARM toolchain.
|
firmware build system downloads its own ARM toolchain.
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
./build.sh # clone firmware 1.4.3, add the app, build with the full BLE stack
|
./build.sh # clone firmware 1.4.3, export the BLE API, build firmware + .fap
|
||||||
./build.sh flash # same, then flash over USB (Flipper connected, qFlipper closed)
|
./build.sh flash # same, then flash over USB (Flipper connected, qFlipper closed)
|
||||||
|
./build.sh fap # rebuild only the .fap after editing the app
|
||||||
```
|
```
|
||||||
|
|
||||||
Options via environment variables: `FW_TAG` (firmware tag, default `1.4.3`),
|
Options via environment variables: `FW_TAG` (firmware tag, default `1.4.3`),
|
||||||
`FW_DIR` (where to clone), `JOBS` (parallel compile jobs, default 4).
|
`FW_DIR` (where to clone), `JOBS` (parallel compile jobs, default 4),
|
||||||
|
`BLE_API` (which ST API to export, see below).
|
||||||
|
|
||||||
The result is a standard update package in
|
The result is a standard update package in
|
||||||
`flipperzero-firmware/dist/f7-C/f7-update-local/`. To install without USB
|
`flipperzero-firmware/dist/f7-C/f7-update-local/`. It contains the firmware,
|
||||||
flashing, copy that folder to the SD card (for example `SD:/update/`) and open
|
the **full radio stack**, and the app as `apps/Bluetooth/bt_inspector.fap` in
|
||||||
it in the Flipper's file browser: the updater installs the firmware **and the
|
its resources. To install without USB flashing, copy that folder to the SD card
|
||||||
full radio stack**. Installing an official firmware later restores the light
|
(for example `SD:/update/`) and open it in the Flipper's file browser.
|
||||||
stack the same way.
|
Installing an official firmware later restores the light stack the same way.
|
||||||
|
|
||||||
|
The `.fap` alone is at `flipperzero-firmware/build/f7-firmware-C/.extapps/`;
|
||||||
|
after the firmware is installed once, app updates are just a file copy to
|
||||||
|
`SD:/apps/Bluetooth/`. It only loads on this firmware (API version 87.2 with
|
||||||
|
the exported symbols); the stock loader rejects it with an API mismatch.
|
||||||
|
|
||||||
|
To build the app with `ufbt` instead of the firmware tree, point `ufbt` at the
|
||||||
|
SDK the build produces:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
ufbt update --local=flipperzero-firmware/dist/f7-C/flipper-z-f7-sdk-local.zip
|
||||||
|
cd bt_inspector && ufbt # -> dist/bt_inspector.fap ; `ufbt launch` installs it
|
||||||
|
```
|
||||||
|
|
||||||
The first build takes 5-15 minutes; later builds are incremental.
|
The first build takes 5-15 minutes; later builds are incremental.
|
||||||
|
|
||||||
@@ -128,12 +144,26 @@ Three facts about the Flipper Zero drive the design:
|
|||||||
firmware build system already supports it (`COPRO_STACK_TYPE=ble_full`) and
|
firmware build system already supports it (`COPRO_STACK_TYPE=ble_full`) and
|
||||||
the firmware recognises it at runtime (`FuriHalBtStackFull`).
|
the firmware recognises it at runtime (`FuriHalBtStackFull`).
|
||||||
2. External `.fap` apps can only call functions listed in the firmware's API
|
2. External `.fap` apps can only call functions listed in the firmware's API
|
||||||
table. The ST `aci_*` / `hci_*` command functions are not in it, so a `.fap`
|
table. The ST `aci_*` / `hci_*` command functions are not in it, so a stock
|
||||||
cannot start a scan or a connection. Compiling the app into the firmware
|
`.fap` cannot start a scan or a connection. `build.sh` adds ST's command
|
||||||
(`FlipperAppType.APP`, added with `--extra-int-apps`) gives it access.
|
headers to the SDK (`lib/stm32wb.scons`), adds `extern "C"` guards to them
|
||||||
3. The full stack is 34 KB larger and loads at 0x080CE000 instead of
|
(the API table is C++), lets `fbt` regenerate `api_symbols.csv`, and enables
|
||||||
0x080D7000. The built image still leaves 12 flash pages (52 KB) for the
|
the selected functions. The API minor version becomes 87.2.
|
||||||
Flipper's internal storage (stock leaves 27).
|
3. Every exported function is kept in the firmware image, so `BLE_API` trades
|
||||||
|
generality for flash. The full stack loads at 0x080CE000 (light: 0x080D7000);
|
||||||
|
the space between the firmware and the stack is the Flipper's internal
|
||||||
|
storage:
|
||||||
|
|
||||||
|
| `BLE_API` | exported | firmware | pages left, full stack | pages left, extended stack |
|
||||||
|
|---|---|---|---|---|
|
||||||
|
| `min` (app's own 15 imports) | 15 | 770 KB | 18 | 9 |
|
||||||
|
| `hci,gatt` (default) | 127 | 779 KB | 15 | 6 |
|
||||||
|
| `all` | 211 | ~788 KB | 13 | 4 |
|
||||||
|
| stock light-stack firmware | 0 | 768 KB | 27 | n/a |
|
||||||
|
|
||||||
|
Any comma list of `hci,gap,gatt,hal,l2cap` works. `extended` refers to
|
||||||
|
`stm32wb5x_BLE_Stack_full_extended_fw.bin` (BLE 5 extended advertising,
|
||||||
|
Coded PHY), which loads at 0x080C5000; it is not selected by this script.
|
||||||
|
|
||||||
`build.sh` also widens the GAP roles the firmware initialises
|
`build.sh` also widens the GAP roles the firmware initialises
|
||||||
(`GAP_PERIPHERAL_ROLE | GAP_CENTRAL_ROLE | GAP_OBSERVER_ROLE`) in
|
(`GAP_PERIPHERAL_ROLE | GAP_CENTRAL_ROLE | GAP_OBSERVER_ROLE`) in
|
||||||
@@ -182,8 +212,12 @@ wire overlay).
|
|||||||
Install the package produced by `build.sh`, which includes `radio.bin` with
|
Install the package produced by `build.sh`, which includes `radio.bin` with
|
||||||
the full stack.
|
the full stack.
|
||||||
* **Build killed / out of memory**: `JOBS=2 ./build.sh`.
|
* **Build killed / out of memory**: `JOBS=2 ./build.sh`.
|
||||||
* **Updater link errors about `bt`/`rpc`**: the app manifest must not `require`
|
* **App refuses to load ("API mismatch" / unresolved symbol)**: the firmware on
|
||||||
the `bt` service (the same app list is linked into the tiny updater image).
|
the device is not the one from `build.sh`, or it was built with a smaller
|
||||||
|
`BLE_API` than the app needs. Reinstall the update package.
|
||||||
|
* **"API version is still WIP"** during a build: `fbt` found symbols the script
|
||||||
|
did not classify. Open `targets/f7/api_symbols.csv`, replace the remaining
|
||||||
|
`?` marks with `+` or `-`, rerun.
|
||||||
* **Connect failed: Timeout**: the device may not be connectable (see the
|
* **Connect failed: Timeout**: the device may not be connectable (see the
|
||||||
advertising type on the device page), or it is out of range. Random-address
|
advertising type on the device page), or it is out of range. Random-address
|
||||||
devices that rotate their address may need re-selecting from the list.
|
devices that rotate their address may need re-selecting from the list.
|
||||||
@@ -191,7 +225,7 @@ wire overlay).
|
|||||||
## Layout
|
## Layout
|
||||||
|
|
||||||
```
|
```
|
||||||
bt_inspector/ app sources + application.fam
|
bt_inspector/ app sources, application.fam, icon.png
|
||||||
build.sh firmware build / flash script
|
build.sh firmware build / flash script
|
||||||
flipperzero-firmware/ created by build.sh (git-ignored)
|
flipperzero-firmware/ created by build.sh (git-ignored)
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -1,12 +1,16 @@
|
|||||||
# Built-in (firmware-linked) app: it needs the ST GAP/GATT/HCI command API,
|
# External app. Needs firmware built by ../build.sh: full BLE stack plus the
|
||||||
# which the firmware does not export to .fap apps. See ../build.sh.
|
# ST aci_*/hci_* command API exported to apps.
|
||||||
App(
|
App(
|
||||||
appid="bt_inspector",
|
appid="bt_inspector",
|
||||||
name="BT Inspector",
|
name="BT Inspector",
|
||||||
apptype=FlipperAppType.APP,
|
apptype=FlipperAppType.EXTERNAL,
|
||||||
entry_point="bt_inspector_app",
|
entry_point="bt_inspector_app",
|
||||||
requires=["gui", "storage"],
|
requires=["gui", "storage"],
|
||||||
stack_size=4 * 1024,
|
stack_size=4 * 1024,
|
||||||
icon="A_Plugins_14",
|
fap_category="Bluetooth",
|
||||||
order=25,
|
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",
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,13 +1,37 @@
|
|||||||
#include "ble_central.h"
|
#include "ble_central.h"
|
||||||
#include <furi_hal.h>
|
#include <furi_hal.h>
|
||||||
#include <furi_ble/event_dispatcher.h>
|
#include <furi_ble/event_dispatcher.h>
|
||||||
#include <ble/ble.h>
|
#include <ble/core/ble_defs.h>
|
||||||
#include <interface/patterns/ble_thread/tl/tl.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 <string.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
|
||||||
#define TAG "BtInspector"
|
#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 OWN_ADDR_TYPE GAP_PUBLIC_ADDR // firmware programs a public identity address
|
||||||
#define PROC_TIMEOUT_MS 5000
|
#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) {
|
static BleEventAckStatus bc_event_handler(void* event, void* context) {
|
||||||
BleCentral* bc = 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) {
|
switch(pkt->evt) {
|
||||||
case HCI_LE_META_EVT_CODE: {
|
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) {
|
if(meta->subevent == HCI_LE_ADVERTISING_REPORT_SUBEVT_CODE) {
|
||||||
handle_adv_report(bc, meta->data, pkt->plen - 1);
|
handle_adv_report(bc, meta->data, pkt->plen - 1);
|
||||||
return BleEventAckFlowEnable;
|
return BleEventAckFlowEnable;
|
||||||
@@ -173,7 +197,7 @@ static BleEventAckStatus bc_event_handler(void* event, void* context) {
|
|||||||
return BleEventNotAck;
|
return BleEventNotAck;
|
||||||
}
|
}
|
||||||
case HCI_VENDOR_SPECIFIC_DEBUG_EVT_CODE: {
|
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
|
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;
|
if(!bc->connected || ch != bc->conn_handle) return BleEventNotAck;
|
||||||
|
|
||||||
@@ -306,9 +330,6 @@ void bc_scan_stop(BleCentral* bc) {
|
|||||||
bc->scanning = false;
|
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 bc_snapshot(BleCentral* bc, BcDevice* out, size_t max) {
|
||||||
size_t n = 0;
|
size_t n = 0;
|
||||||
@@ -325,7 +346,15 @@ size_t bc_snapshot(BleCentral* bc, BcDevice* out, size_t max) {
|
|||||||
d->logged = true;
|
d->logged = true;
|
||||||
}
|
}
|
||||||
furi_mutex_release(bc->mutex);
|
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;
|
return n;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -10,7 +10,7 @@
|
|||||||
#include <gui/modules/text_input.h>
|
#include <gui/modules/text_input.h>
|
||||||
#include <gui/modules/number_input.h>
|
#include <gui/modules/number_input.h>
|
||||||
#include <storage/storage.h>
|
#include <storage/storage.h>
|
||||||
#include <ble/ble.h>
|
#include <ble/core/ble_defs.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <stdarg.h>
|
#include <stdarg.h>
|
||||||
#include "ble_central.h"
|
#include "ble_central.h"
|
||||||
|
|||||||
Binary file not shown.
|
After Width: | Height: | Size: 95 B |
@@ -6,11 +6,16 @@
|
|||||||
# broadcaster only. Scanning and connecting as a central need the "full"
|
# broadcaster only. Scanning and connecting as a central need the "full"
|
||||||
# stack (COPRO_STACK_TYPE=ble_full, already supported by fbt).
|
# stack (COPRO_STACK_TYPE=ble_full, already supported by fbt).
|
||||||
# * The firmware does not export the ST GAP/GATT/HCI command API to external
|
# * The firmware does not export the ST GAP/GATT/HCI command API to external
|
||||||
# apps, so the app is compiled in as a built-in ("--extra-int-apps").
|
# apps. This script adds those headers to the SDK and accepts the new
|
||||||
|
# aci_*/hci_* symbols, so the app builds as a normal .fap (bumps the API
|
||||||
|
# minor version: the .fap only loads on this firmware).
|
||||||
#
|
#
|
||||||
# Usage:
|
# Usage:
|
||||||
# ./build.sh build -> flipperzero-firmware/dist/f7-C/f7-update-*/ (update package)
|
# ./build.sh build -> flipperzero-firmware/dist/f7-C/f7-update-*/ (update package,
|
||||||
|
# includes the .fap under apps/Bluetooth) + SDK zip for ufbt
|
||||||
# ./build.sh flash build and flash over USB (Flipper connected, qFlipper closed)
|
# ./build.sh flash build and flash over USB (Flipper connected, qFlipper closed)
|
||||||
|
# ./build.sh fap rebuild only the .fap (firmware already built once)
|
||||||
|
# BLE_API=all|min|hci,gap,gatt,hal,l2cap which ST API to export (default hci,gatt)
|
||||||
# FW_TAG=1.4.3 ./build.sh pin a different firmware tag (default 1.4.3)
|
# FW_TAG=1.4.3 ./build.sh pin a different firmware tag (default 1.4.3)
|
||||||
# JOBS=2 ./build.sh limit parallel compile jobs (default 4)
|
# JOBS=2 ./build.sh limit parallel compile jobs (default 4)
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
@@ -36,6 +41,48 @@ if grep -q "^ GAP_PERIPHERAL_ROLE,$" "$GAP_C"; then
|
|||||||
echo ">> patched $GAP_C: GAP roles = peripheral|central|observer"
|
echo ">> patched $GAP_C: GAP roles = peripheral|central|observer"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# Export the ST BLE command API (aci_*/hci_*) to external apps.
|
||||||
|
WB_SCONS=lib/stm32wb.scons
|
||||||
|
python3 - "$WB_SCONS" <<'PY'
|
||||||
|
import re, sys
|
||||||
|
p = sys.argv[1]
|
||||||
|
s = open(p).read()
|
||||||
|
block = """# BT Inspector: expose the ST BLE command API to .fap apps
|
||||||
|
env.Append(
|
||||||
|
SDK_HEADERS=[
|
||||||
|
File("stm32wb_copro/wpan/ble/core/auto/ble_hci_le.h"),
|
||||||
|
File("stm32wb_copro/wpan/ble/core/auto/ble_gap_aci.h"),
|
||||||
|
File("stm32wb_copro/wpan/ble/core/auto/ble_gatt_aci.h"),
|
||||||
|
File("stm32wb_copro/wpan/ble/core/auto/ble_hal_aci.h"),
|
||||||
|
File("stm32wb_copro/wpan/ble/core/auto/ble_l2cap_aci.h"),
|
||||||
|
File("stm32wb_copro/wpan/ble/core/auto/ble_vs_codes.h"),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
"""
|
||||||
|
s = re.sub(r"\n*# BT Inspector: expose.*?\n\)\n", "\n", s, flags=re.S) # drop any earlier copy
|
||||||
|
if block not in s:
|
||||||
|
s = s.replace('Return("lib")', block + 'Return("lib")', 1) # must run before Return
|
||||||
|
open(p, "w").write(s)
|
||||||
|
print(">> patched " + p + ": ST BLE headers added to SDK")
|
||||||
|
PY
|
||||||
|
|
||||||
|
# ST's generated headers have no extern "C" guards; the API table is C++.
|
||||||
|
python3 - <<'PY'
|
||||||
|
import re
|
||||||
|
for name in ["ble_hci_le", "ble_gap_aci", "ble_gatt_aci", "ble_hal_aci", "ble_l2cap_aci"]:
|
||||||
|
p = f"lib/stm32wb_copro/wpan/ble/core/auto/{name}.h"
|
||||||
|
s = open(p).read()
|
||||||
|
if "__cplusplus" in s:
|
||||||
|
continue
|
||||||
|
guard = re.search(r"#define \w+_H__\n", s).group(0)
|
||||||
|
s = s.replace(guard, guard + '#ifdef __cplusplus\nextern "C" {\n#endif\n', 1)
|
||||||
|
i = s.rstrip().rfind("#endif")
|
||||||
|
s = s[:i] + "#ifdef __cplusplus\n}\n#endif\n" + s[i:]
|
||||||
|
open(p, "w").write(s)
|
||||||
|
print(">> patched " + p + ': extern "C" guards')
|
||||||
|
PY
|
||||||
|
|
||||||
# Link (or refresh) the app into the user apps directory.
|
# Link (or refresh) the app into the user apps directory.
|
||||||
mkdir -p applications_user
|
mkdir -p applications_user
|
||||||
rm -rf applications_user/bt_inspector
|
rm -rf applications_user/bt_inspector
|
||||||
@@ -45,10 +92,58 @@ FBT_ARGS=(
|
|||||||
COMPACT=1 DEBUG=0 # release build, same as official images
|
COMPACT=1 DEBUG=0 # release build, same as official images
|
||||||
COPRO_STACK_TYPE=ble_full
|
COPRO_STACK_TYPE=ble_full
|
||||||
COPRO_STACK_BIN=stm32wb5x_BLE_Stack_full_fw.bin
|
COPRO_STACK_BIN=stm32wb5x_BLE_Stack_full_fw.bin
|
||||||
--extra-int-apps=bt_inspector
|
|
||||||
)
|
)
|
||||||
|
|
||||||
echo ">> building update package (full BLE stack + BT Inspector)"
|
API_CSV=targets/f7/api_symbols.csv
|
||||||
|
# BLE_API selects which ST command headers are exported to apps (each exported
|
||||||
|
# function is kept in the firmware image):
|
||||||
|
# hci,gatt scanning, connections, GATT client/server (~12 KB, default)
|
||||||
|
# all also gap, hal, l2cap (~20 KB)
|
||||||
|
# min only the functions BT Inspector imports (~1.5 KB)
|
||||||
|
BLE_API="${BLE_API:-hci,gatt}"
|
||||||
|
if grep -q "^Function,?," "$API_CSV" || ! grep -qE "^Header,\+,.*ble_hci_le\.h" "$API_CSV"; then
|
||||||
|
# fbt notices the new headers, rewrites the csv with '?' entries and stops.
|
||||||
|
echo ">> syncing API symbol table (expected to stop once)"
|
||||||
|
./fbt -j"${JOBS:-4}" "${FBT_ARGS[@]}" api_check || true
|
||||||
|
fi
|
||||||
|
python3 - "$API_CSV" "$BLE_API" "$APP_SRC" <<'PY'
|
||||||
|
import re, sys, glob
|
||||||
|
csv_path, mode, app_src = sys.argv[1:4]
|
||||||
|
hdrs = {"hci": "ble_hci_le", "gap": "ble_gap_aci", "gatt": "ble_gatt_aci", "hal": "ble_hal_aci", "l2cap": "ble_l2cap_aci"}
|
||||||
|
owner = {}
|
||||||
|
for key, name in hdrs.items():
|
||||||
|
src = open(f"lib/stm32wb_copro/wpan/ble/core/auto/{name}.h").read()
|
||||||
|
for fn in re.findall(r"^tBleStatus (\w+)\(", src, re.M):
|
||||||
|
owner[fn] = key
|
||||||
|
if mode == "all":
|
||||||
|
selected = set(owner)
|
||||||
|
elif mode == "min":
|
||||||
|
selected = set()
|
||||||
|
for f in glob.glob(app_src + "/*.c"):
|
||||||
|
selected |= set(re.findall(r"\b((?:aci_|hci_)\w+)\(", open(f).read()))
|
||||||
|
else:
|
||||||
|
want = set(mode.split(","))
|
||||||
|
selected = {fn for fn, key in owner.items() if key in want}
|
||||||
|
out, n_on = [], 0
|
||||||
|
for line in open(csv_path).read().splitlines():
|
||||||
|
parts = line.split(",")
|
||||||
|
if len(parts) >= 3 and parts[0] == "Function" and parts[2] in owner:
|
||||||
|
parts[1] = "+" if parts[2] in selected else "-"
|
||||||
|
n_on += parts[1] == "+"
|
||||||
|
elif len(parts) >= 2 and parts[1] == "?":
|
||||||
|
parts[1] = "-" if parts[0] == "Variable" else "+"
|
||||||
|
out.append(",".join(parts))
|
||||||
|
open(csv_path, "w").write("\n".join(out) + "\n")
|
||||||
|
print(f">> BLE API export ({mode}): {n_on} of {len(owner)} aci_*/hci_* functions enabled")
|
||||||
|
PY
|
||||||
|
|
||||||
|
if [ "${1:-}" = "fap" ]; then
|
||||||
|
./fbt -j"${JOBS:-4}" "${FBT_ARGS[@]}" fap_bt_inspector
|
||||||
|
echo ">> $(ls build/f7-firmware-C/.extapps/bt_inspector.fap)"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo ">> building update package (full BLE stack + BT Inspector .fap)"
|
||||||
./fbt -j"${JOBS:-4}" "${FBT_ARGS[@]}" updater_package
|
./fbt -j"${JOBS:-4}" "${FBT_ARGS[@]}" updater_package
|
||||||
|
|
||||||
PKG=$(ls -d dist/f7-C/f7-update-* 2>/dev/null | head -1)
|
PKG=$(ls -d dist/f7-C/f7-update-* 2>/dev/null | head -1)
|
||||||
@@ -58,6 +153,9 @@ echo ">> radio stack in package:"
|
|||||||
grep -E "^Radio" "$PKG/update.fuf" || true
|
grep -E "^Radio" "$PKG/update.fuf" || true
|
||||||
echo ">> firmware size:"
|
echo ">> firmware size:"
|
||||||
ls -l "$PKG/firmware.dfu"
|
ls -l "$PKG/firmware.dfu"
|
||||||
|
echo ">> app (also inside the package's resources, installed to apps/Bluetooth/):"
|
||||||
|
ls -l build/f7-firmware-C/.extapps/bt_inspector.fap
|
||||||
|
echo ">> SDK for building the app with ufbt: $(ls dist/f7-C/flipper-z-f7-sdk-*.zip)"
|
||||||
echo
|
echo
|
||||||
echo "Install: copy '$PKG' to the SD card (e.g. /ext/update/) and run it from"
|
echo "Install: copy '$PKG' to the SD card (e.g. /ext/update/) and run it from"
|
||||||
echo "the file browser, or use: ./build.sh flash"
|
echo "the file browser, or use: ./build.sh flash"
|
||||||
|
|||||||
Reference in New Issue
Block a user