- Generalize _read_md_cached() for generic file caching
- Add find_sub_home_for_lesson() and get_sub_home_data() helpers
- Add /api/bab/<folder> endpoint
- Detect sub-home in api_lesson for nav context
- Skip sub-home.md in find_lesson_file() and _parse_lesson_links()
- Add title to bab frontend page data
- Update generate_tokens.py & content_parser.py to skip sub-home.md
WARN-1: pumpRunning now reset to false when the pump loop exits via
{done} or error, so future startPump() isn't a silent no-op.
WARN-3: readExact clears rxBuffer on timeout to avoid stale bytes
leaking into the next non-retried STK500 command.
readExact and drainStray now consume the FIFO buffer fed by the pump
instead of calling reader.read() directly. No orphaned reader.read()
promise can swallow optiboot INSYNC/OK replies anymore. This is the
core fix for "get_sync failed after N attempts".
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.
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.
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