elemes/docs/05-velxio-ble-deployer.md

17 KiB

05. Velxio BLE Deployer — Dokumentasi Implementasi

Versi: 3.1 Tanggal: 4 Juli 2026 Status: SELESAI + ENHANCEMENT — Re-deploy fix, serial terminal always visible, baud dropdown, LED patterns, event-based Arduino detection


1. Ringkasan

Integrasi fitur BLE Deployer ke LMS untuk memungkinkan siswa melakukan flashing firmware Arduino langsung dari browser Chrome Android via Web Bluetooth API, menggunakan ESP32-S3 sebagai bridge BLE-to-USB.

Arsitektur:

Webapp (Chrome Android)
  |  Web Bluetooth API (GATT)
  v
ESP32-S3 N16R8 (BLE Peripheral + USB Host)
  |  STK500v1 over USB CDC (115200 baud)
  v
Arduino Uno/Nano (Target)
  |
  |  USB CDC read (9600 baud — match Serial.begin(9600))
  v
ESP32-S3 (Serial Bridge via BLE Notify)
  |
  v
Webapp Serial Monitor

2. Status Implementasi Aktual

Area Status Keterangan
Frontend SvelteKit Selesai DeployTab.svelte, ble-deployer.ts, deployer.ts — UUID branded, chunks 240, MTU 255
Backend Flask Selesai /velxio-compile return hex_content (tidak berubah)
Firmware BLE + Parser Selesai Advertising OK, UUID branded, MTU 255, protocol ACK benar
Firmware USB Host Selesai CherryUSB 1.6.1 host CDC, RX claim/unclaim, dual baud rate
Firmware STK500v1 Selesai Flash Arduino via STK500v1 (get_sync, signature, prog_page, dll)
Firmware Serial Bridge Selesai USB CDC ↔ BLE Notify, throttle 20 pkt/s, flush 500ms
Serial Monitor (data flow) SELESAI Data "LED ON"/"LED OFF" dari Arduino tampil di webapp
Integrasi Webapp Selesai DeployTab ter-render saat active_tabs include velxio
Debug tools Selesai debug-capture.sh — capture CDP + firmware log simultan
Re-deploy fix (v3.1) FIXED Stuck di INIT setelah deploy pertama — root cause: ACK sent before serial_bridge_stop + CCCD left at 0
Serial terminal inline (v3.1) DONE Serial terminal tampil saat paired (bukan gated di deploy success) + baud dropdown (9600/19200/38400)
LED blink patterns (v3.1) DONE IDLE slow blink (1s), RECEIVING medium (200ms), FLASHING fast (100ms)
Arduino event-based (v3.1) DONE Hybrid event+1s poll via CherryUSB usbh_event_handler_t — disconnect ~200ms

3. Keputusan Desain Final

Keputusan Pilihan Alasan
UUID BLE Branded hex 56454C58-494F-0000-... UUID lama 0000VELX-IO00 invalid
ESP-IDF v6.0 v5.1 tidak support ESP32-S3 USB Host dengan benar
USB Host CherryUSB 1.6.1 TinyUSB tidak stabil di S3
USB Native GPIO19/20 = Host ke Arduino Debug log via UART0
Chunk size 240 byte Field Len 1-byte (max 255), MTU 255
MTU 255 requestMTU(255) di frontend
ACK timing END Setelah STK500v1 flash sukses Bukan sebelum flash
Adv name "Velxio" (shortened) Max 31 byte adv data
Baud STK500 115200 Arduino bootloader sync (get_sync)
Baud Serial Bridge 9600 Match Serial.begin(9600) di sketch siswa

4. UUID BLE (Final)

Service UUID:        56454c58-494f-0000-0000-000000000001
Flashing Char UUID:  56454c58-494f-0000-0000-000000000002
Serial Char UUID:    56454c58-494f-0000-0000-000000000003

Firmware (C, little-endian NimBLE BLE_UUID128_INIT):

// 56454C58-494F-0000-0000-000000000001
static const ble_uuid128_t service_uuid = BLE_UUID128_INIT(
    0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x4F, 0x49, 0x58, 0x4C, 0x45, 0x56
);

Frontend (TypeScript):

export const BLE_SERVICE_UUID = '56454c58-494f-0000-0000-000000000001';

5. Protokol BLE

Payload: [CMD:1][Index:2 LE][Len:1][Data:N][CRC32:4 LE]

CMD_INIT (0x01) — mulai transfer, data = total CRC (4 byte)
CMD_DATA (0x02) — chunk binary, data = raw hex bytes
CMD_END  (0x03) — selesai transfer, data = total CRC (4 byte)
CMD_ACK  (0x04) — respon sukses dari firmware
CMD_ERR  (0x05) — respon error dari firmware

Alur ACK (Final)

Webapp → INIT → ESP32 kirim ACK segera
Webapp → DATA chunk 0 → ESP32 kirim ACK
Webapp → DATA chunk 1 → ESP32 kirim ACK
...
Webapp → DATA chunk N → ESP32 kirim ACK
Webapp → END → ESP32 TIDAK kirim ACK langsung
              ESP32 verifikasi CRC buffer
              ESP32 flash via STK500v1
              Jika sukses → kirim ACK
              Jika gagal → kirim ERR

6. Firmware ESP32-S3

Lokasi: elemes/velxio-deployer-firmware/

6.1 Persyaratan

  • ESP-IDF v6.0 (/home/a2nr/Downloads/lms-c/esp-idf-v6)
  • Target: esp32s3
  • CherryUSB 1.6.1 (managed component)

6.2 Build & Flash

source /home/a2nr/Downloads/lms-c/esp-idf-v6/export.sh
cd elemes/velxio-deployer-firmware
idf.py build
idf.py -p /dev/ttyACM0 flash

6.3 Konfigurasi (sdkconfig.defaults)

Key Value Keterangan
CONFIG_IDF_TARGET esp32s3 Target chip
CONFIG_BT_NIMBLE_SVC_GAP_DEVICE_NAME Velxio-Deployer Nama BLE
CONFIG_BT_NIMBLE_ATT_PREFERRED_MTU 255 MTU
CONFIG_SPIRAM y PSRAM enabled
CONFIG_SPIRAM_MODE_OCT y Octal mode (N16R8)
CONFIG_ESPTOOLPY_FLASHSIZE_16MB y Flash 16MB
CONFIG_CHERRYUSB_HOST_CDC_ACM y USB Host CDC
CONFIG_ESP_CONSOLE_UART_DEFAULT y Debug via UART0

6.4 Dual Baud Rate Strategy (Kunci Fix Serial Monitor)

Dua baud rate berbeda untuk dua mode operasi:

Mode Baud Fungsi File
STK500 flashing 115200 Arduino bootloader sync usb_host.c:make_stk500_termios()
Serial bridge 9600 Match Serial.begin(9600) sketch usb_host.c:make_termios()

Switch terjadi otomatis via usb_host_rx_claim() / usb_host_rx_release():

  • Saat flashing: rx_claim() set 115200 → STK500 sync sukses
  • Setelah flash: rx_release() set 9600 → serial bridge cocok dengan baud sketch Arduino

6.5 File Firmware

File Fungsi
main.c Entry point, BLE command router
ble_service.c BLE GATT server, advertising, 2 karakteristik
binary_parser.c Parse INIT/DATA/END, CRC32 verification
state_machine.c IDLE → RECEIVING → VERIFYING → FLASHING → SERIAL_BRIDGE → ERROR
stk500v1.c STK500v1 protocol (get_sync, prog_page, dll)
usb_host.c CherryUSB host CDC, RX claim/release, DTR pulse, dual baud
serial_bridge.c USB CDC ↔ BLE Notify bridge, throttle 20 pkt/s
led_button.c LED RGB + tombol Retry

6.6 Debug

  • Serial log: UART0 (CH343 USB-to-UART devkit) via /dev/ttyACM0
  • Tag logging: BLE_SVC, BIN_PARSER, SM, USB, STK500, SERIAL_BRIDGE
  • CDP remote debug Chrome Android via ADB + debug-capture.sh

7. Frontend SvelteKit

7.1 File Terkait

File Fungsi
src/lib/types/deployer.ts UUID, constants, types
src/lib/services/ble-deployer.ts BLEHardwareDeployer class
src/routes/lesson/[slug]/DeployTab.svelte UI tab Deploy + Serial Monitor
src/lib/components/WorkspaceHeader.svelte Tab button "Deploy"
src/lib/components/Footer.svelte Version badge

7.2 Konstanta

export const BLE_SERVICE_UUID = '56454c58-494f-0000-0000-000000000001';
export const BLE_CHAR_FLASHING_UUID = '56454c58-494f-0000-0000-000000000002';
export const BLE_CHAR_SERIAL_UUID = '56454c58-494f-0000-0000-000000000003';
export const CHUNK_SIZE = 240;
export const BLE_TIMEOUT_MS = 5000;
export const MAX_RETRIES = 3;

7.3 Fitur Serial Monitor

  • Subscribe karakteristik serial (CCCD notify)
  • TextDecoder().decode() untuk BLE notify → terminal UI
  • Auto-scroll ke bawah
  • Input form untuk kirim data ke Arduino (Write Without Response)
  • Throttle BLE notify dari firmware (max 20 pkt/s)
  • Stop serial monitor via unsubscribe

7.4 Build & Deploy

cd elemes/frontend
npm run build
cd elemes
# via container:
./elemes.sh runbuild

8. Debug & Testing Tools

8.1 debug-capture.sh

Lokasi: elemes/debug-capture.sh

Script multifungsi untuk capture log simultan:

Command Fungsi
setup Auto-detect ADB + CDP WebSocket URL
cdp Stream console.log Chrome Android via CDP
firmware Source IDF + idf.py monitor pada /dev/ttyACM0
all CDP + firmware simultan
sw-clear Unregister SW + clear caches + reload page
eval '<js>' Evaluate JS expression di device
status Cek ADB, CDP, ACM0, IDF, Node

8.2 Remote Debug Chrome Android

# Setup koneksi ADB via Tailscale
adb forward tcp:9222 localabstract:chrome_devtools_remote

# Capture log
./debug-capture.sh setup
./debug-capture.sh all

# On-demand inspect
./debug-capture.sh eval 'document.querySelector(".serial-line")?.textContent'

9. Bug Fix yang Sudah Dilakukan

Firmware

# File Bug Fix Sesi
B1 ble_service.c UUID invalid (encode "LEXV") Branded hex little-endian 22/6
B2 main.c CMD_INIT tidak kirim ACK binary_parser_process_packet kirim ACK 22/6
B3 binary_parser.c END ACK dikirim sebelum flash Hapus ACK dari parser, kirim setelah flash 23/6
B4 stk500v1.c expect_resp() cuma delay Rewrite send_and_expect baca response 23/6
B5 usb_host.c RX serial bridge vs STK500 bentrok RX claim/unclaim mechanism 23/6
B6 ble_service.c conn_handle tidak disimpan Simpan dari BLE_GAP_EVENT_CONNECT 22/6
B7 ble_service.c MTU tidak dinegosiasi ble_att_set_preferred_mtu(255) 22/6
B8 ble_service.c serial_char_access_cb kosong Forward ke serial_bridge_on_ble_write() 22/6
B9 ble_service.c Adv tanpa nama Build explicit adv data 22/6
B10 usb_host.c TIOCMSET deref alamat 0x6 crash Pointer ke uint32_t flags 23/6
B11 main.c CMD_END skip parser → buffer_size=0 Panggil binary_parser_process_packet 23/6
B12 binary_parser.c CRC check duplikat → ERR ganda Hapus CRC check di parser 23/6
B13 usb_host.c Baud rate 115200 → null bytes di serial Dual baud: 115200 STK500 + 9600 serial bridge 4/7

Frontend

# File Bug Fix Sesi
F1 deployer.ts UUID invalid Branded hex 22/6
F2 ble-deployer.ts CHUNK_SIZE=512 tanpa requestMTU() 240 + requestMTU(255) 22/6
F3 ble-deployer.ts payload[3]=data.length overflow Aman karena chunk ≤ 240 22/6
F4 ble-deployer.ts pair() tidak panggil requestMTU Tambah requestMTU(255) 22/6
F5 ble-deployer.ts Race condition ACK ackResolver BEFORE write 23/6
F6 ble-deployer.ts DataView byteOffset bug new Uint8Array(buffer, byteOffset, byteLength) 23/6
F7 ble-deployer.ts END timeout 5s terlalu pendek 30s + write fallback 23/6
F8 DeployTab.svelte Svelte 5 reactivity isConnected $state isPaired flag 22/6
F9 sw.js Service Worker cache stale Bump cache version 22/6
F10 Dockerfile Tidak copy package-lock.json npm ci + copy lock 22/6
F11 ble-deployer.ts CCCD subscription hilang saat idle Refresh stop+startNotifications sebelum END 30/6
F12 ble-deployer.ts Poll loop resolve di deadline Hapus resolve() — biarkan ackPromise yg tentukan 30/6

10. Enhancement v3.1 (4 Juli 2026)

10.1 Fix Re-Deploy Stuck di INIT

Root cause: Dua faktor:

  1. Firmwaremain.c:29-30: ACK INIT dikirim oleh binary_parser_process_packet() (binary_parser.c:70) SEBELUM serial_bridge_stop() dipanggil. Saat re-deploy dari SERIAL_BRIDGE, serial bridge masih aktif dan NimBLE mbuf pool (12 blocks) bisa habis → ACK silent drop di ble_service.c:277-278.
  2. Webappble-deployer.ts:278-284: END flow melakukan stopNotifications() (CCCD=0) lalu startNotifications() fire-and-forget yang race dengan readValue() poll → bisa gagal → CCCD tertinggal 0 → ACK INIT notification tidak di-deliver.

Fix:

  • main.c: serial_bridge_stop() SEBELUM binary_parser_process_packet()
  • ble_service.c: Retry mbuf alloc 3x + error log jika gagal
  • sdkconfig.defaults: CONFIG_BT_NIMBLE_MSYS_1_BLOCK_COUNT=24
  • serial_bridge.c: vTaskDelay(1) yield di large-packet loop
  • binary_parser.c: Hapus memset(hex_buffer, 0, MAX_HEX_SIZE) — block BLE thread ~25ms tidak perlu
  • ble-deployer.ts: deployHex() stop serial monitor + await startNotifications() ensure CCCD=1 sebelum INIT
  • ble-deployer.ts: Hapus stopNotifications() dari END flow (penyebab CCCD=0)
  • DeployTab.svelte: serialActive = false di handleDeploy

10.2 Serial Terminal Always Visible + Baud Dropdown

  • Serial terminal render saat isPaired (bukan deployState === 'success')
  • Toggle "Mulai"/"Tutup" di serial header
  • Auto-start serial setelah deploy success
  • Dropdown baud: 9600 / 19200 / 38400 (hanya rate yang aman untuk BLE throughput ~4800 B/s)
  • CMD_SET_BAUD (0x06) — dikirim via serialChar (writeWithoutResponse)
  • Firmware ble_service.c intercept CMD_SET_BAUD → usb_host_set_baud_rate(baud)
  • usb_host.c: configurable baud via serial_baud static variable
State Pattern Interval
IDLE (advertising) LED_BLUE_BLINK_SLOW 1000ms
RECEIVING chunks LED_BLUE_BLINK 200ms
VERIFYING CRC LED_BLUE solid
FLASHING STK500 LED_BLUE_BLINK_FAST 100ms
SERIAL_BRIDGE LED_GREEN solid
ERROR_CHECKSUM LED_RED_BLINK 100ms
ERROR_TARGET LED_RED solid

10.4 Arduino Connect/Disconnect Event-Based

  • CherryUSB usbh_event_handler_t callback di usbh_initialize(0, ESP_USB_FS0_BASE, usb_event_handler)
  • Listen USBH_EVENT_INTERFACE_START (connect) + USBH_EVENT_DEVICE_DISCONNECTED
  • Forward via xTaskNotifyGive ke usb_monitor_task
  • vTaskDelay(1000)xTaskNotifyWait(0, 0, NULL, pdMS_TO_TICKS(1000)) — event OR 1s poll fallback
  • Constraint: handler jalan di hub thread, hanya boleh notify, tidak boleh usbh_serial_*

11. Sisa Pekerjaan (Improvement, Bukan Blocker)

Task Prioritas Keterangan
OTA firmware update Rencana V2 Update firmware ESP32 via webapp
Multi-board support Rencana V2 Target MCU selain ATmega328P
Baud rate auto-detect Rencana V2 Deteksi baud dari kode siswa

12. Alur Data End-to-End (Final)

Webapp                          ESP32-S3                        Arduino
  |                                |                              |
  |-- BLE INIT (total CRC) ------->|                              |
  |<-- BLE ACK --------------------|                              |
  |-- BLE DATA (chunk 0..N) ------>|                              |
  |<-- BLE ACK (per chunk) --------|                              |
  |-- BLE END -------------------->|                              |
  |                                |-- STK500 (115200) --------->|
  |                                |  get_sync → signature →      |
  |                                |  enter_prog → load_addr →    |
  |                                |  prog_page → leave_prog     |
  |                                |<-- flash OK -----------------|
  |<-- BLE ACK END (flash OK) -----|                              |
  |                                |-- switch to 9600 baud        |
  |                                |                              |
  |== Serial Monitor mode ==       |                              |
  |<-- BLE serial Notify ----------|<-- Serial.print (9600) ------|
  |-- BLE serial Write ----------->|-- USB CDC write ------------>|

Verifikasi End-to-End (4 Juli 2026)

Dari capture simultan firmware + CDP:

FW: SERIAL_BRIDGE: USB->BLE: 8 bytes, buf=0/240
FW: NimBLE: att_handle=19
CDP: [BLE-SERIAL] RX: 8B "LED ON"
FW: SERIAL_BRIDGE: USB->BLE: 9 bytes, buf=0/240
FW: NimBLE: att_handle=19
CDP: [BLE-SERIAL] RX: 9B "LED OFF"

Data "LED ON" / "LED OFF" dari Arduino muncul di webapp setiap ~1 detik.


13. Catatan Teknis

  • Advertising data max 31 byte. Nama "Velxio-Deployer" (15 char) + UUID128 (18 byte) + flags (3 byte) = 38 byte → overflow. Solusi: shorten ke "Velxio" (6 char) → total ~21 byte.
  • Frontend scan pakai namePrefix: 'Velxio' → tetap match.
  • Web Bluetooth types (BluetoothRemoteGATTServer, dll) tidak ada di TypeScript default — perlu @types/web-bluetooth atau ignore. Error ini pre-existing.
  • Lesson terkunci (prerequisite belum selesai) → backend kosongkan initial_code_arduino, tapi active_tabs tetap di-return. Tab Deploy tetap muncul.
  • idf.py monitor membutuhkan TTY — tidak bisa dijalankan di bash tool non-interaktif. Alternatif: Python serial raw (python3 -c "import serial; ...").
  • Debug-capture.sh menggunakan Node.js inline (WebSocket native) untuk CDP — tidak perlu dependency eksternal.