Single pump loop becomes the only reader.read() caller, feeding
rxBuffer. Serial monitor + readExact/drain will consume from the
buffer (next tasks). Build passes.
Test runner setup (vitest) plus deadline-based FIFO buffer that
becomes the single sink for serial bytes. readExact keeps
partial bytes buffered on timeout — the exact case that dropped
optiboot INSYNC/OK replies under the orphaned-reader race.
7 unit tests pass.
- Update lesson_service.py to parse markdown link format [title](path)
- Convert all lesson files to use markdown link format for prerequisites
- Backend now extracts slug directly from link path
- Supports both plain text and markdown link formats
- Add device_name module: NVS-persisted custom name with MAC-suffix
fallback (Velxio-XXXX) so every device is uniquely identifiable
out-of-the-box in BLE pairing dialog
- Add wifi_ap module: first-boot SoftAP (Velxio-Setup-XXXX / velxio123)
with embedded HTTP config portal at 192.168.4.1 for renaming
- main.c: enter AP setup mode when no custom name is set
- ble_service.c: use dynamic device name from device_name_get(),
with adv-packet length guard
- led_button.c: BOOT button long-press (5s) factory reset (erase NVS)
- sdkconfig.defaults: enable WiFi stack + set NimBLE default name
Frontend deviceName display already committed in 01f68d3.
v3.1 enhancements (4 Jul 2026):
- Baud dropdown (9600/19200/38400) + CMD_SET_BAUD
- LED blink patterns per-state (IDLE/RECEIVING/FLASHING/SERIAL_BRIDGE)
- Arduino event-based connect/disconnect (CherryUSB)
- Serial terminal always visible saat paired
- Re-deploy fix: serial_bridge_stop before parser
- Dual baud: 115200 STK500 + 9600 serial bridge
- Footer v15, SW cache v14
This is a checkpoint commit. Next cycle: port Deploy from
SvelteKit (DeployTab.svelte) to Velxio React app (native integration).
DeployTab.svelte will be deprecated after port complete.
cad90eb merutekan DATA melalui sendCommand() dengan readPoll state===1
yang selalu true — menyebabkan chunk lambat 5s/kirim saat ACK di-drop.
Kembalikan ke direct writeValueWithResponse + waitForAck.
Selama transfer aktif tidak ada idle gap (Qualcomm >8s), jadi ACK notify
tidak terpengaruh idle-drop.
Perubahan:
- sendCommandWithRetry: build payload sendiri, pakai chunkCRC param,
write+waitForAck langsung (tidak lewat sendCommand)
- sendCommand: hanya handle INIT dan END (DATA dihapus dari blok)
- Hapus writeSucceeded flag (tidak lagi dipakai)
- Hapus readPoll fallback untuk DATA
- Bump v12 (Footer + SW cache)
Akar masalah:
- 4a6ef8c menambah binary_parser_reset() di handler re-deploy SERIAL_BRIDGE
yang menghapus expected_total_crc setelah INIT — menyebabkan CRC mismatch
di END. Source 1d3a489 sudah menghapus reset-nya, tapi device butuh rebuild.
- cad90eb merutekan DATA melalui sendCommand() dengan readPoll state===1
yang selalu true saat DATA — write gagal dilaporkan sukses.
Perbaikan:
1. readPoll pintar untuk DATA: state===1 tidak lagi dianggap sukses.
readPoll hanya fast-fail di state>=5; deadline fallback hanya ok
jika writeValueWithResponse sukses (GATT write response = bukti delivery).
2. INIT_ACK_INDEX=0xFFFE (Fase 3): inisialisasi INIT dengan index unik
menghilangkan tabrakan dengan DATA[0] (index 0). binary_parser.c echo
index recv di ACK INIT. Guard handleFlashingNotification diperluas.
3. pendingAcks.clear() di awal deployHex mencegah poisoning antar deploy.
4. Track firmware inti ke git: binary_parser.c, checksum.c, stk500v1.c,
usb_host.c + headers + CMakeLists + sdkconfig + partitions.
Build: ESP-IDF v6, ESP32-S3, flash via /dev/ttyACM0
sendCommandWithRetry now delegates to sendCommand() instead of
using raw writeValueWithResponse + await ackPromise. This ensures
DATA chunks also benefit from the readValue() state poll fallback
when Android BLE notify is unreliable after idle.
serial_bridge.c + .h:
- Implement 20pkt/s throttle with 240-byte buffer
- serial_bridge_on_usb_data() for buffering USB serial data
- Flush every 50ms or 500ms stale timeout
led_button.c + .h:
- Per-color LED blink toggle in led_button_tick()
- BLUE_BLINK 200ms, RED_BLINK 100ms, GREEN_BLINK 200ms
- Fix: was set_led(true,true,true) for all blink patterns
main.c:
- Route USB serial data through serial_bridge_on_usb_data
- Add led_button_tick() call in main loop
state_machine.c:
- Add binary_parser_reset() on SERIAL_BRIDGE re-deploy
Firmware (state_machine.c):
- Add EVENT_BLE_INIT handler to SERIAL_BRIDGE state
-> transition back to RECEIVING for re-deploy
- Add EVENT_BLE_INIT handler to ERROR states
-> allows re-deploy from error without pressing Retry
Frontend (ble-deployer.ts):
- Add readValue() state poll for INIT and DATA commands
(same Android notify idle-drop bug affects these too)
- INIT success: firmware state >= 1 (RECEIVING+)
- DATA success: firmware state == 1 (RECEIVING)
- Error on state >= 5
- Race ackPromise against a polling loop that yields to the event loop
every 200ms (vs a single blocking await that can miss notifications)
- bump SW cache v5→v6