SDK for TESAIoT Dev Kit
API reference & tutorials (ModusToolbox)
Loading...
Searching...
No Matches

Functions

bool ble_nus_init (const ble_nus_config_t *cfg)
 Initialise the AIROC stack and start advertising; the chip-power task must own the boot call.
int ble_nus_send (const uint8_t *data, size_t len)
 Send bytes over NUS TX; -1 unless connected with notifications on. Weak stub exists pre-init.
ble_nus_state_t ble_nus_get_state (void)
 Lock-free state read, safe before init; the firmware polls it rather than trusting the callback.
void ble_nus_deinit (void)
 Soft-stop: drops advertising and the link, keeps the AIROC host stack alive.
void ble_nus_rearm_advertising (void)
 Re-arm advertising on an already-initialised stack; idempotent from any state.
const char * ble_nus_get_adv_name (void)
 Pointer to the static Bento-XXXX advertising name; NULL until the BD address resolves.
void ble_nus_passkey_cb (const char *passkey_6_digits)
 Weak pairing-passkey callback — not reached in this build.
void ble_nus_get_diagnostics (ble_nus_diag_t *out)
 Fill a caller-owned diagnostics snapshot — the _diag.ble block of bento.fw.query.

Detailed Description

Eight functions: the ble_nus_* transport — init, deinit, send, get_state, get_adv_name, rearm_advertising, passkey_cb, get_diagnostics. Compiled only with ENABLE_PAGE_BENTO_BUDDY=1 (default 0, proj_cm33_ns/Makefile:64, :305); rebuild after make getlibs — Flag gate (read first).

Declarations: ble_nus.h. Implementation ble_nus.c is archived in libbento_secure.a. Two of these functions have weak stubs in ble_nus.c (ble_nus_send at :65, ble_nus_get_state at :68) so a translation unit may reference them before the stack exists; the real definitions (:878, :964) win when the BLE component links.

Variant
mtb-mpy and mtb-only

Function Documentation

◆ ble_nus_init()

bool ble_nus_init ( const ble_nus_config_t * cfg)

Initialise the AIROC stack and start advertising; the chip-power task must own the boot call.

Initialize the AIROC BLE host stack, register NUS GATT service, and start advertising. Returns false on stack init failure (check UART log).

Contract
Initialises the AIROC host stack, registers the NUS GATT service (GATT data) and starts advertising; false on stack init failure. The boot-time owner must be the dedicated chip-power task: "calling ble_nus_init from any other context (notably the radio_scheduler worker) fails with state=ERROR even with the same 3-s delay — the original task's stack/priority is what the AIROC HCI bring-up actually needs" (main.c:326-334). Application code does not call it; it goes through bento_buddy_request_start(). on_rx runs in BLE task context with a payload that is not NUL-terminated (copy before returning). Template call sites: 0; the one real archived caller is ble_nus_lazy.c:210 (bool ok = ble_nus_init(&cfg); inside bento_buddy_request_start — compiled into libbento_secure.a, not shipped as source).
Variant
mtb-mpy and mtb-only

◆ ble_nus_send()

int ble_nus_send ( const uint8_t * data,
size_t len )

Send bytes over NUS TX; -1 unless connected with notifications on. Weak stub exists pre-init.

Send a notification on NUS TX characteristic. Fragmented into MTU-sized chunks internally. Thread-safe (enqueues to BLE task). Returns bytes queued, or -1 if disconnected / not initialized.

Contract
Requires ble_nus_init() to have succeeded and state == BLE_NUS_STATE_CONNECTED and the peer to have enabled TX notifications; otherwise returns -1 (ble_nus.c:878-885). Shipped callers treat failure as non-fatal ((void)). A weak stub exists at ble_nus.c:65, so the symbol is safe to reference before init — it simply returns the failure value. The caller frames the payload itself (trailing LF, 0x0A) and bounds-checks snprintf first (w > 0 && w < sizeof(tx)) because the call takes an explicit length, not a NUL-terminated string. Fragmentation is internal: chunks of MTU-3, capped at 180 bytes (:893-894); the library pvPortMalloc+memcpys the buffer, so a stack buffer is legal (:899-905). Also reached through the NUS_SEND macro (nus_commands.c:72, :76) — true fan-in is roughly 80 sites beyond the 4 template callers.
Variant
mtb-mpy and mtb-only
Template call site
(bento_libs/claw/common/ble_nus/bento_time.c:222-228, local extern at :27; ships in both zips):
/* ...context: inside the bento.time.sync ack emitter ... */
int w = snprintf(tx, sizeof(tx),
"{\"ack\":\"bento.time.sync\",\"ok\":true,\"n\":0,"
"\"synced\":true,\"boot_epoch_ms\":%s,\"uptime_ms_at_sync\":%s}\n",
boot_buf, up_buf);
if (w > 0 && w < (int)sizeof(tx)) {
(void)ble_nus_send((const uint8_t *)tx, (size_t)w);
}

◆ ble_nus_get_state()

ble_nus_state_t ble_nus_get_state ( void )

Lock-free state read, safe before init; the firmware polls it rather than trusting the callback.

Current connection state (polled from UI layer).

Contract
Lock-free plain read of the state variable (ble_nus.c:964-967) — safe from any task, no mutex. Safe before init: the weak stub at ble_nus.c:68 returns BLE_NUS_STATE_OFF. Compile the caller under BENTO_HAS_BLE_NUS == 1. The firmware deliberately polls this rather than trusting the on_state callback: the bonded-pair fast-resume path was observed to miss GATT_CONNECTION_STATUS_EVT (2026-05-10), so a 500 ms poll with edge detection is the shipped pattern.
Variant
mtb-mpy and mtb-only
Template call site
(bento_libs/claw/common/mpy/sensor_auto_task.c:1440-1459; ships in both zips):
/* ...context: inside the sensor auto-push loop ... */
#if defined(BENTO_HAS_BLE_NUS) && (BENTO_HAS_BLE_NUS == 1)
/* BLE NUS host-link state poll (every 5th cycle = ~500ms).
* Why poll instead of using on_state callback: the callback
* registration window depends on the AIROC stack delivering
* GATT_CONNECTION_STATUS_EVT, which we observed was missing on
* the bonded-pair fast-resume path on 2026-05-10 — the desktop
* was actively serving fw.query verbs over the GATT link but
* ble_nus_get_state() still read ADVERTISING. Polling closes
* the loop deterministically: whatever the stack actually
* thinks the state is, the LCD topbar will reflect it within
* 500 ms of any change. */
if ((now_ms - last_ble_ms) >= 500u) {
last_ble_ms = now_ms;
static int8_t last_pushed_ble = -1; /* −1 = uninitialized */
int8_t now_connected = (ble_nus_get_state() == BLE_NUS_STATE_CONNECTED) ? 1 : 0;
if (now_connected != last_pushed_ble) {
sensor_auto_push_ble_state(now_connected != 0);
last_pushed_ble = now_connected;
}
}

◆ ble_nus_deinit()

void ble_nus_deinit ( void )

Soft-stop: drops advertising and the link, keeps the AIROC host stack alive.

Soft-stop: stop advertising + drop any active GATT link, but keep the AIROC host stack alive. Pair with ble_nus_rearm_advertising to toggle the link without re-running the heavy stack init/deinit cycle (which crashed CM33 when invoked from the IPC RX task — see ISSUE-029).

Contract
Soft-stop: stops advertising and drops any GATT link but keeps the AIROC host stack alive. Pair with ble_nus_rearm_advertising() to toggle the link without the heavy init/deinit cycle, which crashed CM33 when run from the IPC RX task (ISSUE-029, header comment). Application code reaches it through bento_buddy_request_stop(). Template call sites: 0; archived call site ble_nus_lazy.c:243 (inside the soft-stop comment block, :239 "advertising via ble_nus_rearm_advertising — keeping the ..."; compiled into libbento_secure.a, not shipped as source).
Variant
mtb-mpy and mtb-only

◆ ble_nus_rearm_advertising()

void ble_nus_rearm_advertising ( void )

Re-arm advertising on an already-initialised stack; idempotent from any state.

Re-arm undirected-high advertising on a stack that's already been initialized once via ble_nus_init. Idempotent — safe to call from any state (skips the start_advertisements call if state isn't OFF).

Contract
Re-arms undirected-high advertising on a stack already initialised once via ble_nus_init(). Idempotent — safe from any state (skips the start call unless state is OFF). This is the restart path a second bento_buddy_request_start() takes after a soft stop. Template call sites: 0; archived call site ble_nus_lazy.c:176 (ble_nus_rearm_advertising(); — compiled into libbento_secure.a, not shipped as source).
Variant
mtb-mpy and mtb-only

◆ ble_nus_get_adv_name()

const char * ble_nus_get_adv_name ( void )

Pointer to the static Bento-XXXX advertising name; NULL until the BD address resolves.

Pointer to the static "Bento-XXXX" advertising name (lifetime = process). Returns NULL until ble_nus_init has resolved the local BD address and built the suffix.

Contract
Pointer to the static Bento-XXXX advertising name (lifetime = process; four hex digits of the MAC suffix). Returns NULL until ble_nus_init() has resolved the local BD address — null-check it. Template call sites: 0; archived call site ble_nus_lazy.c:74 (const char *name = ble_nus_get_adv_name(); — compiled into libbento_secure.a, not shipped as source).
Variant
mtb-mpy and mtb-only

◆ ble_nus_passkey_cb()

void ble_nus_passkey_cb ( const char * passkey_6_digits)

Weak pairing-passkey callback — not reached in this build.

Weak callback for a pairing passkey notification.

NOT REACHED IN THIS BUILD, and no passkey is ever shown. Pairing is configured as Just Works: ble_nus.c sets local_io_cap = BTM_IO_CAPABILITIES_NONE (NoInputNoOutput) auth_req = BTM_LE_AUTH_REQ_SC_BOND (no MITM bit) and a NoInputNoOutput association never raises BTM_PASSKEY_NOTIFICATION_EVT, so this callback is not invoked.

The DisplayOnly + 6-digit passkey flow it was written for is NOT YET IMPLEMENTED. The hook is kept so the integration layer (e.g. bento_buddy_task.c) can forward the code to the CM55 UI once that flow ships. The default implementation is a no-op.

Contract
The shipped build never invokes this callback and never displays a passkey.** ble_nus.c configures Just Works pairing — local_io_cap = BTM_IO_CAPABILITIES_NONE and auth_req = BTM_LE_AUTH_REQ_SC_BOND, no MITM bit — and a NoInputNoOutput association never makes the stack raise BTM_PASSKEY_NOTIFICATION_EVT. The DisplayOnly + 6-digit passkey flow this hook was written for is not yet implemented; the hook is kept so it is ready when that flow ships. The default is a no-op; the integration layer overrides it to forward the code to the CM55 screen. Application code does not call it. Template call sites: 0; archived invocation ble_nus.c:714 (ble_nus_passkey_cb(buf);), weak definition ble_nus.c:117, UI override ble_nus_lazy.c:121 — the override is the doc-worthy half (all compiled into libbento_secure.a, not shipped as source).
Variant
mtb-mpy and mtb-only

◆ ble_nus_get_diagnostics()

void ble_nus_get_diagnostics ( ble_nus_diag_t * out)

Fill a caller-owned diagnostics snapshot — the _diag.ble block of bento.fw.query.

Contract
Fills a caller-owned snapshot; surfaced to the desktop as the _diag.ble block of the bento.fw.query answer. last_advert_restart_result follows wiced_result_t (0 = success). Template call sites: 0; archived call site bento_fw.c:189 (ble_nus_get_diagnostics(&bd); — compiled into libbento_secure.a, not shipped as source).
Variant
mtb-mpy and mtb-only