128 lines
5.4 KiB
Markdown
128 lines
5.4 KiB
Markdown
# 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 `VERSION_UUID = "beb5483e-36e1-4688-b7f5-ea07361f26db"` — READ, exposeert firmwareversie als string
|
|
- 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
|
|
- Bij BLE connect: leest firmwareversie van ESP via `VERSION_UUID` characteristic, vergelijkt met versie in app. Toont toast bij beschikbare update.
|
|
- Firmware bron: `assets/firmware.bin` (of bestandskiezer voor handmatige update)
|
|
- SHA-256 hash berekening voor eindverificatie
|
|
- Bundled firmwareversie (uit `assets/`) vergeleken met ESP-versie bij connect
|
|
|
|
## Versie-check bij BLE connect
|
|
|
|
Bij elke BLE-verbinding leest de Android app de `VERSION_UUID` READ characteristic van de ESP. De ESP retourneert de stringwaarde van `FIRMWARE_VERSION`. De app vergelijkt met de versie van de gebundelde `firmware.bin` in assets. Als de ESP een lagere versie heeft toont de app een toast: "Firmware-update beschikbaar" — de gebruiker kan dan in SettingsActivity de OTA starten.
|
|
|
|
| VERSION_UUID | "beb5483e-36e1-4688-b7f5-ea07361f26db" |
|
|
|---|---|
|
|
| Properties | READ |
|
|
| Waarde | String, bijv. `"1"` |
|
|
|
|
## 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:** Bij BLE connect leest de app de firmwareversie van de ESP (`VERSION_UUID` characteristic). Bij mismatch met gebundelde firmware toont app een toast "Firmware-update beschikbaar" en activeert de OTA-knop.
|
|
- **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` | VERSION characteristic, 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 |
|