diff --git a/docs/superpowers/specs/2026-07-30-ble-ota-design.md b/docs/superpowers/specs/2026-07-30-ble-ota-design.md new file mode 100644 index 0000000..96df1ae --- /dev/null +++ b/docs/superpowers/specs/2026-07-30-ble-ota-design.md @@ -0,0 +1,116 @@ +# BLE OTA Firmware Updates — Design + +## Doel + +Firmware-updates via Bluetooth LE vanaf de Android app, zonder USB-kabel. Doelgroep: lichamelijk gehandicapten — zelfstandigheid zonder fysieke kabels. + +## Huidige situatie + +- Firmware: ~523KB (534.793 bytes, 16% van 3.2MB beschikbaar) +- BLE: NimBLE-Arduino, service "SylDa_Server" actief +- Partitietabel: al OTA-capable — `ota_0` (0x10000, 3264K), `ota_1` (0x340000, 3264K), `otadata` (0xe000, 8K) +- Android app: flasht nu via USB CDC (EspFlasher) + +## Architectuur + +### ESP32-S3 kant (`src/main.cpp`) + +- Nieuwe BLE characteristic `OTA_UUID = "beb5483e-36e1-4688-b7f5-ea07361f26da"` — WRITE + NOTIFY +- Bestaande relay service blijft volledig intact, aparte characteristic in dezelfde service +- OTA-modus flag: tijdens OTA worden relay-commando's genegeerd, relais vallen UIT +- Gebruikt ESP-IDF `esp_ota_*` API: + - `esp_ota_get_next_update_partition()` — vindt target partitie + - `esp_ota_begin()` — initialiseert schrijfactie + - `esp_ota_write()` — schrijft chunk naar flash + - `esp_ota_end()` — valideert image, markeert als bootable + - `esp_ota_set_boot_partition()` — zet target als actieve bootpartitie + - `esp_restart()` — herstart naar nieuwe firmware + +### Android kant + +- Nieuwe `OtaManager.kt` — leest firmware, hasht, chunkt, verstuurt +- OTA-knop in `SettingsActivity.kt` met progress UI +- Firmware bron: `assets/firmware.bin` (of bestandskiezer voor handmatige update) +- SHA-256 hash berekening voor eindverificatie + +## BLE OTA Protocol + +``` +OTA Characteristic UUID: beb5483e-36e1-4688-b7f5-ea07361f26da +Properties: WRITE, NOTIFY +MTU: 512 bytes (negotiated) +Chunk nuttige data: 488 bytes (512 - 24 header overhead) +``` + +### Write commando's (App → ESP) + +| Byte(s) | Commando | +|---|---| +| `0x10` + 4 bytes size (LE) | OTA_START — firmware grootte in bytes | +| 4 bytes seq (LE) + data[...] | OTA_DATA — chunk met sequentienummer | +| 32 bytes SHA-256 | OTA_END — hash van volledige firmware | + +### Notificatie commando's (ESP → App) + +| Byte(s) | Betekenis | +|---|---| +| `0x00` + 4 bytes seq (LE) | ACK — chunk ontvangen en geschreven | +| `0x01` + 4 bytes seq (LE) | NAK — chunk opnieuw versturen | +| `0x10` | OTA_COMPLETE — succes, reboot volgt | +| `0x11` + 1 byte error | OTA_ERROR | + +### Flow + +1. App stuurt `OTA_START` met firmware-grootte +2. ESP roept `esp_ota_begin()` aan, zet OTA-modus, relais UIT +3. App stuurt chunks sequentieel (seq 0, 1, 2, ...) +4. ESP schrijft elke chunk met `esp_ota_write()`, stuurt ACK terug +5. Bij timeout of corruptie: ESP stuurt NAK met seq-nummer, app herhaalt chunk +6. App stuurt `OTA_END` met SHA-256 +7. ESP: `esp_ota_end()`, `esp_ota_set_boot_partition()`, stuurt `OTA_COMPLETE` +8. ESP: `esp_restart()` + +## LED-signalering + +| Patroon | Betekenis | +|---|---| +| Snel knipperen (100ms) | OTA bezig | +| Continu aan | OTA voltooid, reboot imminent | +| Langzaam knipperen (500ms) | OTA fout | + +## Beveiliging + +- Firmware wordt NIET per chunk met HMAC geauthenticeerd (snelheid) +- SHA-256 hash van volledige firmware meegezonden in OTA_END +- `esp_ota_end()` verifieert image header checksum automatisch +- Alleen een BLE-verbonden device kan OTA starten (BLE link-level encryptie via pairing) +- Bestaande HMAC-key voor relay-commando's blijft ongewijzigd + +## Foutafhandeling + +| Scenario | Afhandeling | +|---|---| +| Verbinding verbroken tijdens OTA | Huidige firmware blijft actief, `ota_1` ongeldig | +| Corrupte chunk | ESP stuurt NAK, app herhaalt chunk | +| SHA-256 mismatch in OTA_END | `esp_ota_end()` faalt, huidige firmware blijft | +| ESP crash tijdens OTA | Bootloader boot `ota_0`, `otadata` wijst er nog naar | +| Nieuwe firmware boot niet (8x bootloop) | ESP-IDF rollback: schakelt terug naar `ota_0` | + +## Edge cases + +- **OTA opnieuw starten:** `ota_1` wordt overschreven, geen clean-up nodig +- **Versie-check:** App kan firmwareversie opvragen voordat OTA start +- **Tijdens actieve relais:** relais vallen UIT bij start OTA (rust-toestand) + +## Transfertijd schatting + +523KB bij ~30 KB/s effectieve BLE throughput ≈ 17 seconden. Bij slechtere verbinding ~45-60 seconden. Acceptabel. + +## Bestanden + +| Bestand | Wijziging | +|---|---| +| `src/main.cpp` | OTA characteristic, `esp_ota_*` logica, OTA-modus flag, LED-signalering | +| `platformio.ini` | Geen wijziging (partitietabel al OTA-ready) | +| `android/.../OtaManager.kt` | Nieuw: firmware lezen, hashen, chunken, BLE verzenden | +| `android/.../SettingsActivity.kt` | OTA-knop + progress UI |