SDK สำหรับ TESAIoT Dev Kit
คู่มืออ้างอิง API และ Tutorial (MTB & µPython)
Loading...
Searching...
No Matches
แกนกลางและการส่งของ NUS

Functions

bool ble_nus_init (const ble_nus_config_t *cfg)
 เริ่มต้น AIROC stack แล้วเริ่มการกระจายสัญญาณ (advertising) การเรียกในช่วงบูตต้องเป็นของ task จ่ายไฟชิป
int ble_nus_send (const uint8_t *data, size_t len)
 ส่งไบต์ออกทาง NUS TX คืน -1 เว้นแต่เชื่อมต่ออยู่และเปิด notification ไว้ มี weak stub อยู่แล้วตั้งแต่ก่อน init
ble_nus_state_t ble_nus_get_state (void)
 อ่านค่า state โดยไม่ต้องจับ lock ปลอดภัยตั้งแต่ก่อน init เฟิร์มแวร์เลือกวนถามค่านี้แทนการเชื่อ callback
void ble_nus_deinit (void)
 หยุดแบบ soft: ยุติการกระจายสัญญาณและตัด link แต่คง AIROC host stack ไว้ให้ทำงานต่อ
void ble_nus_rearm_advertising (void)
 เปิดการกระจายสัญญาณใหม่บน stack ที่ init ไว้แล้ว เป็น idempotent จากทุกสถานะ
const char * ble_nus_get_adv_name (void)
 พอยน์เตอร์ไปยังชื่อ Bento-XXXX สำหรับการกระจายสัญญาณซึ่งเป็น static คืน NULL จนกว่าจะคลี่ค่า BD address ได้
void ble_nus_passkey_cb (const char *passkey_6_digits)
 callback แบบ weak สำหรับ passkey ของการจับคู่ (pairing) ยังไม่ถูกเรียกใช้ในบิลด์นี้
void ble_nus_get_diagnostics (ble_nus_diag_t *out)
 เติม snapshot ข้อมูลวินิจฉัย (diagnostics) ที่ผู้เรียกเป็นเจ้าของ — คือบล็อก _diag.ble ของ bento.fw.query

Detailed Description

8 ฟังก์ชัน: transport ble_nus_* ได้แก่ init, deinit, send, get_state, get_adv_name, rearm_advertising, passkey_cb และ get_diagnostics คอมไพล์เฉพาะเมื่อ ENABLE_PAGE_BENTO_BUDDY=1 (ค่าตั้งต้นคือ 0, proj_cm33_ns/Makefile:64, :305) ให้ build ใหม่หลังจาก make getlibs — ดู Flag gate (อ่านก่อน)

การประกาศ: ble_nus.h ส่วนการนำไปสร้างจริงใน ble_nus.c ถูกเก็บไว้ใน libbento_secure.a 2 ฟังก์ชันในกลุ่มนี้มี weak stub อยู่ใน ble_nus.c (ble_nus_send ที่ :65 และ ble_nus_get_state ที่ :68) เพื่อให้ translation unit อ้างถึงได้ก่อนที่ stack จะมีอยู่ นิยามตัวจริง (:878, :964) จะชนะเมื่อคอมโพเนนต์ BLE ถูกลิงก์เข้ามา

variant ที่ใช้ได้
mtb-mpy และ mtb-only

Function Documentation

◆ ble_nus_init()

bool ble_nus_init ( const ble_nus_config_t * cfg)

เริ่มต้น AIROC stack แล้วเริ่มการกระจายสัญญาณ (advertising) การเรียกในช่วงบูตต้องเป็นของ task จ่ายไฟชิป

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

ข้อกำหนดการเรียกใช้
เริ่มต้น AIROC host stack ลงทะเบียนบริการ NUS GATT (ข้อมูล GATT) แล้วเริ่มการกระจายสัญญาณ คืน false เมื่อ init ของ stack ล้มเหลว เจ้าของการเรียกในช่วงบูต ต้องเป็น 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) โค้ดของแอปพลิเคชันไม่เรียกฟังก์ชันนี้เอง แต่ไปผ่านทาง bento_buddy_request_start() on_rx ทำงานใน task context ของ BLE โดยที่ payload ไม่ปิดท้ายด้วย NUL (ให้คัดลอกออกไปก่อนคืนค่า) call site ในเทมเพลต: 0 ผู้เรียกจริงใน archive มีแห่งเดียวคือ ble_nus_lazy.c:210 (bool ok = ble_nus_init(&cfg); ภายใน bento_buddy_request_start คอมไพล์รวมอยู่ใน libbento_secure.a ไม่ได้ส่งมอบมาเป็นซอร์ส)
variant ที่ใช้ได้
mtb-mpy และ mtb-only

◆ ble_nus_send()

int ble_nus_send ( const uint8_t * data,
size_t len )

ส่งไบต์ออกทาง NUS TX คืน -1 เว้นแต่เชื่อมต่ออยู่และเปิด notification ไว้ มี weak stub อยู่แล้วตั้งแต่ก่อน 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.

ข้อกำหนดการเรียกใช้
ต้องเรียก ble_nus_init() สำเร็จมาก่อน และ state ต้องเป็น BLE_NUS_STATE_CONNECTED และ ฝั่งตรงข้ามต้องเปิด notification ของ TX ไว้ มิฉะนั้นคืนค่า -1 (ble_nus.c:878-885) ผู้เรียกที่มีอยู่จริงในของที่ส่งมอบถือว่าความล้มเหลวไม่ร้ายแรง ((void)) มี weak stub อยู่ที่ ble_nus.c:65 symbol นี้จึงอ้างถึงได้อย่างปลอดภัยตั้งแต่ก่อน init — stub เพียงคืนค่าที่หมายถึงความล้มเหลว ผู้เรียกเป็นฝ่ายประกอบเฟรมของ payload เอง (ปิดท้ายด้วย LF, 0x0A) และตรวจขอบเขตผลของ snprintf ก่อน (w > 0 && w < sizeof(tx)) เพราะการเรียกรับความยาวมาอย่างชัดเจน ไม่ใช่สตริงที่ปิดท้ายด้วย NUL การแบ่งชิ้นเป็นเรื่องภายใน: ชิ้นละ MTU-3 จำกัดไว้ที่ 180 ไบต์ (:893-894) ไลบรารีทำ pvPortMalloc แล้ว memcpy บัฟเฟอร์ให้ บัฟเฟอร์บน stack จึงใช้ได้ (:899-905) เข้าถึงได้ผ่านมาโคร NUS_SEND ด้วย (nus_commands.c:72, :76) — fan-in จริงอยู่ที่ราว 80 จุด นอกเหนือจากผู้เรียกในเทมเพลต 4 ราย
variant ที่ใช้ได้
mtb-mpy และ mtb-only
call site ในเทมเพลต
(bento_libs/claw/common/ble_nus/bento_time.c:222-228 มี extern ประกาศในไฟล์นั้นเองที่ :27 มีอยู่ในทั้งสอง zip):
/* ...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 )

อ่านค่า state โดยไม่ต้องจับ lock ปลอดภัยตั้งแต่ก่อน init เฟิร์มแวร์เลือกวนถามค่านี้แทนการเชื่อ callback

Current connection state (polled from UI layer).

ข้อกำหนดการเรียกใช้
อ่านตัวแปร state ตรง ๆ โดยไม่ต้องจับ lock (ble_nus.c:964-967) — เรียกได้จากทุก task ไม่ต้องใช้ mutex ปลอดภัยตั้งแต่ ก่อน init: weak stub ที่ ble_nus.c:68 คืนค่า BLE_NUS_STATE_OFF ให้คอมไพล์ผู้เรียกภายใต้ BENTO_HAS_BLE_NUS == 1 เฟิร์มแวร์ตั้งใจ วนถาม ค่านี้แทนการเชื่อ callback on_state: พบว่าเส้นทาง fast-resume ของคู่ที่ผูกอุปกรณ์ (bonding) ไว้แล้วพลาด GATT_CONNECTION_STATUS_EVT (2026-05-10) รูปแบบที่ส่งมอบจริงจึงเป็นการวนถามทุก 500 ms พร้อมตรวจจับขอบสัญญาณ
variant ที่ใช้ได้
mtb-mpy และ mtb-only
call site ในเทมเพลต
(bento_libs/claw/common/mpy/sensor_auto_task.c:1440-1459 มีอยู่ในทั้งสอง zip):
/* ...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: ยุติการกระจายสัญญาณและตัด link แต่คง AIROC host stack ไว้ให้ทำงานต่อ

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).

ข้อกำหนดการเรียกใช้
หยุดแบบ soft: หยุดการกระจายสัญญาณและตัด GATT link ที่มีอยู่ แต่คง AIROC host stack ไว้ให้ทำงานต่อ ใช้คู่กับ ble_nus_rearm_advertising() เพื่อสลับสถานะของ link โดยไม่ต้องผ่านรอบ init/deinit ที่หนัก ซึ่งเคยทำให้ CM33 หยุดทำงานเมื่อรันจาก task IPC RX (ISSUE-029 คอมเมนต์ใน header) โค้ดของแอปพลิเคชันเข้าถึงฟังก์ชันนี้ผ่าน bento_buddy_request_stop() call site ในเทมเพลต: 0 call site ใน archive อยู่ที่ ble_nus_lazy.c:243 (อยู่ในบล็อกคอมเมนต์เรื่องการหยุดแบบ soft ที่ :239 "advertising via ble_nus_rearm_advertising — keeping the ..." คอมไพล์รวมอยู่ใน libbento_secure.a ไม่ได้ส่งมอบมาเป็นซอร์ส)
variant ที่ใช้ได้
mtb-mpy และ mtb-only

◆ ble_nus_rearm_advertising()

void ble_nus_rearm_advertising ( void )

เปิดการกระจายสัญญาณใหม่บน stack ที่ init ไว้แล้ว เป็น idempotent จากทุกสถานะ

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).

ข้อกำหนดการเรียกใช้
เปิดการกระจายสัญญาณแบบ undirected-high ใหม่บน stack ที่เคยผ่าน ble_nus_init() มาแล้วครั้งหนึ่ง เป็น idempotent (เรียกซ้ำแล้วผลเหมือนเดิม) — ปลอดภัยจากทุกสถานะ (ข้ามการเรียก start เว้นแต่สถานะเป็น OFF) นี่คือเส้นทางเริ่มใหม่ที่การเรียก bento_buddy_request_start() ครั้งที่สองใช้หลังการหยุดแบบ soft call site ในเทมเพลต: 0 call site ใน archive อยู่ที่ ble_nus_lazy.c:176 (ble_nus_rearm_advertising(); คอมไพล์รวมอยู่ใน libbento_secure.a ไม่ได้ส่งมอบมาเป็นซอร์ส)
variant ที่ใช้ได้
mtb-mpy และ mtb-only

◆ ble_nus_get_adv_name()

const char * ble_nus_get_adv_name ( void )

พอยน์เตอร์ไปยังชื่อ Bento-XXXX สำหรับการกระจายสัญญาณซึ่งเป็น static คืน NULL จนกว่าจะคลี่ค่า BD address ได้

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.

ข้อกำหนดการเรียกใช้
พอยน์เตอร์ไปยังชื่อ Bento-XXXX สำหรับการกระจายสัญญาณซึ่งเป็น static (อายุการใช้งาน = ตลอดโพรเซส เป็นเลข hex 4 หลักท้ายของ MAC) คืนค่า NULL จนกว่า ble_nus_init() จะคลี่ค่า BD address ของเครื่องได้ — ต้องตรวจค่า null ก่อนใช้ call site ในเทมเพลต: 0 call site ใน archive อยู่ที่ ble_nus_lazy.c:74 (const char *name = ble_nus_get_adv_name(); คอมไพล์รวมอยู่ใน libbento_secure.a ไม่ได้ส่งมอบมาเป็นซอร์ส)
variant ที่ใช้ได้
mtb-mpy และ mtb-only

◆ ble_nus_passkey_cb()

void ble_nus_passkey_cb ( const char * passkey_6_digits)

callback แบบ weak สำหรับ passkey ของการจับคู่ (pairing) ยังไม่ถูกเรียกใช้ในบิลด์นี้

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.

ข้อกำหนดการเรียกใช้
บิลด์ที่ส่งมอบจริงไม่เคยเรียก callback นี้ และไม่เคยแสดง passkey เพราะ ble_nus.c ตั้งค่าการจับคู่เป็นแบบ Just Works ด้วย local_io_cap = BTM_IO_CAPABILITIES_NONE และ auth_req = BTM_LE_AUTH_REQ_SC_BOND (ไม่มีบิต MITM) การจับคู่แบบ NoInputNoOutput จะไม่ทำให้ stack ยก BTM_PASSKEY_NOTIFICATION_EVT ขึ้นมาเลย เส้นทาง DisplayOnly พร้อม passkey 6 หลักที่ callback นี้เขียนขึ้นมารองรับนั้นยังไม่ได้พัฒนา hook นี้จึงถูกเก็บไว้เพื่อรองรับเมื่อพัฒนาเสร็จ ค่าตั้งต้นเป็น no-op ชั้นเชื่อมระบบเขียนทับ (override) มันเพื่อส่งรหัสนั้นต่อไปยังหน้าจอ CM55 โค้ดของแอปพลิเคชันไม่เรียกฟังก์ชันนี้ call site ในเทมเพลต: 0 การเรียกใน archive อยู่ที่ ble_nus.c:714 (ble_nus_passkey_cb(buf);) นิยามแบบ weak อยู่ที่ ble_nus.c:117 และการเขียนทับฝั่ง UI อยู่ที่ ble_nus_lazy.c:121 — ครึ่งที่ควรบันทึกไว้ในเอกสารคือการเขียนทับ (ทั้งหมดคอมไพล์รวมอยู่ใน libbento_secure.a ไม่ได้ส่งมอบมาเป็นซอร์ส)
variant ที่ใช้ได้
mtb-mpy และ mtb-only

◆ ble_nus_get_diagnostics()

void ble_nus_get_diagnostics ( ble_nus_diag_t * out)

เติม snapshot ข้อมูลวินิจฉัย (diagnostics) ที่ผู้เรียกเป็นเจ้าของ — คือบล็อก _diag.ble ของ bento.fw.query

ข้อกำหนดการเรียกใช้
เติม snapshot ที่ผู้เรียกเป็นเจ้าของ ค่านี้ถูกส่งออกไปยังฝั่ง desktop เป็นบล็อก _diag.ble ของคำตอบ bento.fw.query last_advert_restart_result เป็นไปตาม wiced_result_t (0 = สำเร็จ) call site ในเทมเพลต: 0 call site ใน archive อยู่ที่ bento_fw.c:189 (ble_nus_get_diagnostics(&bd); คอมไพล์รวมอยู่ใน libbento_secure.a ไม่ได้ส่งมอบมาเป็นซอร์ส)
variant ที่ใช้ได้
mtb-mpy และ mtb-only