|
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 ส่วนข้อความตัดตอน (excerpt) ที่กำกับว่ามาจาก archive นั้นคัดมาตามตัวอักษรจากซอร์สเหล่านั้น และแต่ละชิ้นระบุที่มาของตัวเองไว้ (file:lines — คอมไพล์เข้าไปใน libbento_secure.a ไม่ได้ส่งมอบเป็นซอร์ส)
การประกอบเฟรมเพื่อส่ง ตัวอย่างที่มีอยู่จริงในของที่ส่งมอบคือ ack ของการซิงก์เวลา: ผู้เรียกประกอบ JSON โดยปิดท้ายด้วย \n ตรวจขอบเขตของ snprintf ส่งความยาวไปอย่างชัดแจ้ง และแคสต์ผลลัพธ์เป็น (void):
ble_nus_send คืนค่า −1 เว้นแต่ init สำเร็จ สถานะเป็น CONNECTED และอุปกรณ์คู่เปิดการแจ้งเตือน TX แล้ว (ble_nus.c:878-885) การแบ่งชิ้นข้อมูลจัดการอยู่ภายใน (MTU−3 จำกัดสูงสุดที่ 180 B) ไลบรารีจองหน่วยความจำแล้วคัดลอกข้อมูลเอง บัฟเฟอร์บน stack จึงใช้ได้ (:893-905) stub ชนิด weak ที่ ble_nus.c:65 ทำให้อ้างถึง symbol นี้ได้อย่างปลอดภัยก่อน init จำนวนผู้เรียกจริงของมันคือราว 80 ขึ้นไปผ่านมาโคร NUS_SEND
การวนถามสถานะ เฟิร์มแวร์เลือกวนถามโดยเจตนา แทนที่จะไว้ใจ 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 ตัวแรกที่เชื่อมต่อสำเร็จ (ble_nus_lazy.c:99-109) ผ่าน TX ที่ยังไม่ได้เข้ารหัส
นอกจากนี้ secret มาจาก xorshift32 ที่ seed ด้วย tick ของ FreeRTOS, แอดเดรสบน stack และค่าคงที่หนึ่งค่า (bento_devmode.c:54-66) จึงเป็นเอนโทรปีระดับต่ำกว่า 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[] เท่านั้น, nus_gatt_db.c:83), 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 (ตัวฟังก์ชันอยู่ที่ nus_agent.c:122-133 ซึ่งเป็นนิยาม ไม่ใช่การเรียก), 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 แพ็กเกจ |