|
SDK สำหรับ TESAIoT Dev Kit
คู่มืออ้างอิง API และ Tutorial (MTB & µPython)
|
ส่ง payload ที่ประกอบเป็นเฟรมแล้ว ปล่อย event ตอบรับคำสั่ง ข้ามไปยัง CM55 ผ่านสะพานหน่วยความจำร่วม และรู้ว่าส่วนใดของ API ที่ส่งออกมานั้นใช้งานได้จริงและส่วนใดตายแล้ว symbol ที่ส่งออก 77 จาก 87 ตัวไม่มีผู้เรียกใน template/ และมีไฟล์ .c ของ ble_nus เพียง 3 ไฟล์ที่ส่งมอบเป็นซอร์ส (bento_time.c, character_lottie.c, character_lottie_default.c) ที่เหลือทั้งหมดอยู่ใน libbento_secure.a ผู้เรียกที่อยู่ใน archive จะอธิบายเป็นข้อความ เพราะซอร์สนั้นไม่ได้ส่งมอบมา
การประกอบเฟรมเพื่อส่ง ตัวอย่างที่มีอยู่จริงในของที่ส่งมอบคือ ack ของการซิงก์เวลา: ผู้เรียกประกอบ JSON โดยปิดท้ายด้วย \n ตรวจขอบเขตของ snprintf ส่งความยาวไปอย่างชัดแจ้ง และแคสต์ผลลัพธ์เป็น (void):
ble_nus_send คืนค่า −1 เว้นแต่ init สำเร็จ สถานะเป็น CONNECTED และอุปกรณ์คู่เปิดการแจ้งเตือน TX แล้ว (ประกาศใน ble_nus.h) การแบ่งชิ้นข้อมูลจัดการอยู่ภายใน (MTU−3 จำกัดสูงสุดที่ 180 B) ไลบรารีจองหน่วยความจำแล้วคัดลอกข้อมูลเอง บัฟเฟอร์บน stack จึงใช้ได้ stub ชนิด weak ภายใน archive ทำให้อ้างถึง symbol นี้ได้อย่างปลอดภัยก่อน init จำนวนผู้เรียกจริงของมันคือราว 80 ขึ้นไปผ่านมาโครส่งข้อมูล
การวนถามสถานะ เฟิร์มแวร์เลือกวนถามโดยเจตนา แทนที่จะไว้ใจ callback on_state — พบว่าเส้นทางกลับมาทำงานเร็วของคู่อุปกรณ์ที่ผูกไว้แล้วพลาด GATT_CONNECTION_STATUS_EVT (2026-05-10):
ble_nus_get_state() เป็นการอ่านธรรมดาแบบไม่ใช้ lock ปลอดภัยจากทุก task และปลอดภัยก่อน init (stub ชนิด weak คืน BLE_NUS_STATE_OFF)
การปล่อย event nus_emit_event(json) รับ JSON ที่ปิดท้ายด้วย NUL และ ไม่มีการขึ้นบรรทัดใหม่ต่อท้าย (ตัวมันเติมให้เอง) ต้องมี strlen(json) + 2 <= 256 มิฉะนั้นคืน −1 แล้ว ทิ้ง ข้อมูลนั้นไม่มีการตัดสั้น ค่า −1 ยังครอบคลุมกรณี NULL และลิงก์ล่มด้วย ผู้เรียกที่มีอยู่จริงในของที่ส่งมอบ (mod_dualband.c:196-213 ซอร์สฝั่ง mtb-mpy ประกาศผ่าน extern ในไฟล์ที่ :44-46) จัดรูป {"evt":"bento.net.down","n":lu,"reason":"s"} ตรวจขอบเขตของ snprintf แล้วโยน OSError("nus_emit_event failed (d)") เมื่อค่าที่ได้ไม่เป็นศูนย์
การตอบรับคำสั่ง — ผู้เรียกที่เก็บอยู่ใน archive หนีบช่วงเวลาของ sensor_stream_start ไว้ที่ [10, 5000]
ตัวคำสั่งถูกตัดตายตัวที่ 32 อักขระ ack_cmd_len == 0 หมายถึงให้ใช้ strlen ค่า ok เป็นค่าที่คำนวณขึ้น (ถ้าเป็น NULL ทั้งคู่ ⇒ true ส่วน error ที่ไม่ใช่ NULL จะพลิกเป็น false และต่อท้ายด้วย "error") เป็น void — ความล้มเหลวในการส่งจึงมองไม่เห็น และทำงานบน task ที่กระจายงาน NUS RX ไม่ใช่ context ใดก็ได้
การข้ามไปยัง CM55 ipc_bento_buddy_send เขียนข้อความ .cy_shared_socmem แบบ static เพียงชุดเดียว — ไม่ reentrant ค่า data_len ถูกหนีบเป็น IPC_DATA_MAX_LEN อย่างเงียบ ๆ มีการลองใหม่แบบมีขอบเขต 50 × 1 ms จึง เรียกได้จาก task เท่านั้น ห้ามจาก ISR ให้ส่ง len+1 เพื่อให้ฝั่งรับได้ NUL ไปด้วย และได้จำนวนที่ไม่รวม NUL ใน value
สถานะวิทยุบนสาย — radio_mode_str คืนค่าเป็นสตริงคงที่แบบ static (คืน "invalid" เมื่ออยู่นอกช่วง s จึงปลอดภัยเสมอ) คำศัพท์ที่ใช้คือ unknown | ble_adv | ble_paired | switching_to_wifi | wifi_active | switching_to_ble | wifi_failed
Devmode ค่า secret อยู่ใน RAM เท่านั้นและสร้างใหม่ทุกครั้งที่บูต เครื่องเดสก์ท็อปจึงต้องจัดเตรียม (provisioning) ใหม่หลังการรีบูตทุกครั้ง bento_devmode_init เป็น idempotent และจุดเข้าใช้งานสาธารณะทุกจุดเรียกมันเอง
ขอบเขตที่ challenge/response นี้ปกป้องได้จริง ขั้นตอน HMAC-SHA256 ถูกนำไปใช้อย่างถูกต้อง (nonce มี TTL ใช้ได้ครั้งเดียว และมีตัวจำกัดอัตราการลองผิด) แต่ตัว secret เองถูกส่งออกไปในรูป plaintext: bento_devmode_emit_provision() ส่งเฟรม {"evt":"bento.devmode.provision","secret":"<64 hex>"} ให้ peer ตัวแรกที่เชื่อมต่อสำเร็จ (เรียกเมื่อสถานะเปลี่ยนเป็น CONNECTED ดู bento_devmode.h) ผ่าน TX ที่ยังไม่ได้เข้ารหัส
นอกจากนี้ secret มาจาก xorshift32 ที่ seed ด้วย tick ของ FreeRTOS, แอดเดรสบน stack และค่าคงที่หนึ่งค่า จึงเป็นเอนโทรปีระดับต่ำกว่า 32 บิต ไม่ใช่ค่าจาก RNG ของ OPTIGA ผลรวมคือ gate นี้กัน peer ที่มาทีหลังได้ แต่กัน peer ที่เชื่อมต่อเป็นรายแรกไม่ได้ ให้ถือว่าเป็นเครื่องกีดขวาง ไม่ใช่ขอบเขตการบังคับใช้สิทธิ์
สถานะของ agent คือคำถามที่กำลังดำเนินอยู่เพียงรายการเดียว ซึ่ง NUS RX task เดียวเป็นเจ้าของ
เชื่อมต่อจาก Bento Desktop Buddy การโฆษณา (advertising) เปิดรับ central ทุกตัว จึงเชื่อมต่อได้โดยไม่ต้องจับคู่ก่อน หากเครื่องเดสก์ท็อปขอจับคู่ จะเป็นแบบ Just Works (BTM_IO_CAPABILITIES_NONE + BTM_LE_AUTH_REQ_SC_BOND ไม่มีบิต MITM) ไม่มี passkey แสดงบนหน้าจอ และไม่มีการป้องกัน MITM ส่วนหน้าจอ passkey 6 หลักแบบ DisplayOnly นั้นยังไม่ได้พัฒนา
สิ่งที่ควรสังเกต glyph BLE บนแถบบนเปลี่ยนภายใน 500 ms หลังการเชื่อมต่อ — นั่นคือจังหวะการวนถามที่กล่าวไว้ข้างต้น
การจับคู่เป็นทางเลือก ไม่ใช่ข้อบังคับ ฐานข้อมูล GATT ที่ส่งมอบจริงไม่ได้ตั้งบิต GATTDB_PERM_AUTH_* บน RX, TX หรือ CCCD เลย (ตรวจใน libbento_secure.a สมาชิก bento_core_11.o เมื่อ 2026-08-29 ดู ข้อมูล GATT) อุปกรณ์คู่ที่ยังไม่ได้ผูกจึงเขียน RX และเปิด notification ได้ตามปกติ และจะไม่ได้รับ GATT_INSUF_AUTHENTICATION ให้ปฏิบัติต่อ link นี้เสมือนยังไม่ผ่านการพิสูจน์ตัวตน
ให้เครื่องเดสก์ท็อปส่ง bento.time.sync
สิ่งที่ควรสังเกต บนเครื่องเดสก์ท็อป: บรรทัด JSON {"ack":"bento.time.sync","ok":true,"n":0,"synced":true,"boot_epoch_ms":…,"uptime_ms_at_sync":…} — เป็นเฟรมที่ snippet สร้างขึ้นพอดี
กระตุ้นให้เปลี่ยนโหมด (bento_buddy.stop() บน mtb-mpy หรือปุ่มบนหน้า CM55)
สิ่งที่ควรสังเกต บนเครื่องเดสก์ท็อป: {"event":"bento.radio.state","mode":"…","ssid":"…","ipv4":"…","ble_paired":…,"wifi_fail_count":…} โดย mode มาจากคำศัพท์ที่ระบุไว้ข้างต้น
ส่ง bento.sensor.stream.start พร้อม id ที่ไม่รู้จัก
สิ่งที่ควรสังเกต ack ที่มี "ok":false,"error":"unknown_id" ส่วนกรณีที่ id ถูกต้องและ interval_ms อยู่นอกช่วง [10, 5000] สตรีมจะทำงานที่ช่วงเวลาที่หนีบไว้แล้ว
รายการต่อไปนี้ส่งออกจาก libbento_secure.a โดย ไม่มีผู้เรียกในซอร์สทั้ง 2 ชุด ตัวอย่างของแต่ละรายการเขียนขึ้นเองเพื่อเอกสารนี้ (ไม่มี call site ในของที่ส่งมอบ) และกำกับไว้เช่นนั้น ได้แก่ NUS_UUID_CHAR_TX, NUS_UUID_CHAR_RX (ปรากฏเป็นไบต์ดิบภายใน nus_gatt_database[] เท่านั้น), voice_capture_is_running, radio_scheduler_set_boot_mode, radio_scheduler_get_boot_mode, nus_protocol_tick, nus_protocol_send_permission, nus_fp_is_active, nus_events_pending_ack_count, nus_agent_note_ask (ใน archive มีเพียงนิยามของมัน ไม่มีการเรียก), nus_agent_buffer_len, fw_hash_prefix8, bento_buddy_auto_start_install (ถูกแทนที่ไปแล้ว main.c:344-347) หน้าใดที่แสดงรายการเหล่านี้เป็นส่วนหนึ่งของเส้นทางโปรโตคอลที่ส่งมอบจริงคือหน้าที่ผิด
symbol 5 ตัวใน overridable.txt เป็นกรณีตรงข้าม — เป็น hook ชนิด WEAK ที่ผู้ใช้ไลบรารี ต้องนิยามเอง และนิยามไว้ใน wifi_init.c: :193 app_wifi_connect_direct, :248 app_wifi_disconnect, :256 app_wifi_get_ipv4, :261 lfs_save_wifi_creds, :304 lfs_load_wifi_creds:
| mtb-mpy | mtb-only | |
|---|---|---|
| handler ของโปรโตคอล | เหมือนกัน (อยู่ใน archive) | เหมือนกัน |
| ผู้เรียก nus_emit_event ที่แสดงไว้ | mod_dualband.c (ส่งมอบในแพ็กเกจนี้) | ไม่ได้ส่งมอบ แต่ข้อกำหนดการเรียกใช้เหมือนกัน |
| การดันสถานะขึ้นแถบบน | sensor_auto_task.c:1440-1459 | ไฟล์เดียวกันนี้ส่งมอบในทั้ง 2 แพ็กเกจ |