diff --git a/docs/superpowers/specs/2026-07-30-ota-improvements-design.md b/docs/superpowers/specs/2026-07-30-ota-improvements-design.md new file mode 100644 index 0000000..e446175 --- /dev/null +++ b/docs/superpowers/specs/2026-07-30-ota-improvements-design.md @@ -0,0 +1,90 @@ +# OTA Improvements — post code review + +Datum: 2026-07-30 +Branch: `feature/ble-ota` + +## Doel + +Zes code review aanbevelingen implementeren voor robuustere OTA firmware update, in volgorde van prioriteit. + +--- + +## 1. ESP SHA256 verificatie + +### ESP (`src/main.cpp`) + +In `onWrite()` voor de BLE OTA characteristic: + +- Android stuurt SHA256 hash van `firmware.bin` als laatste chunk (32 bytes, `chunkIndex == totalChunks`) +- ESP herkent laatste chunk aan `dataLen == 32` in combinatie met `chunkIndex >= totalChunks` +- Bij ontvangst: `esp_ota_end(update_handle)` afronden, dan `esp_ota_get_app_elf_sha256(update_handle, computedSha)` aanroepen +- `memcmp(computedSha, expectedSha, 32)` — bij mismatch: + - abortOta(update_handle) → bestaande partitie blijft actief + - NAK-notificatie sturen naar Android (0x01) +- Bij match: + - ACK-notificatie sturen (0x00) + - `esp_ota_set_boot_partition(update_handle)` + - `esp_restart()` + +### Android (`OtaManager.kt`) + +- Na laatste data-chunk: SHA256 van `firmware.bin` berekenen met `java.security.MessageDigest.getInstance("SHA-256")` +- 32-byte hash als aparte chunk sturen met `chunkIndex = totalChunks + 1` +- Wachten op ACK/NAK response van ESP (punt 2) + +--- + +## 2. OTA notification handling (Android) + +### `BleRelayManager.kt` + +- `onCharacteristicChanged()` in OTA callback doorsturen naar `OtaManager.otaNotificationHandler(bytes: ByteArray)` + +### `OtaManager.kt` + +- `otaNotificationHandler(bytes)`: leest `bytes[0]` — `0x00`=ACK, `0x01`=NAK +- ACK → `sendNextChunk()`, update progress +- NAK → retry huidige chunk (max 3x per chunk) +- Timeout: na versturen chunk, 2s timer — geen antwoord = retry (max 3x) +- Bij overschrijding max retries: OTA abort met foutmelding in dialog + +--- + +## 3+4. Dynamische chunk size + setMTU(512) + +### ESP + +- `NimBLEDevice::setMTU(512)` aanroepen in `setupBLE()` + +### Android + +- `OTA_CHUNK_DATA_SIZE` vervangen door `chunkDataSize` variabele +- `onMtuChanged(mtu: Int)` callback → `chunkDataSize = mtu - 3 - 4` (ATT header + chunk header) +- Default: 248 (valt terug als MTU nog niet onderhandeld is) +- Chunk header blijft 4 bytes: `[chunkIndex:2][totalChunks:2]` + +--- + +## 5. writeCharacteristic return check + +### `OtaManager.kt` + +In `sendOtaData()`: + +```kotlin +val success = bleRelayManager.writeOtaCharacteristic(chunk) +if (!success) { + retriesLeft-- + if (retriesLeft <= 0) abortOta() + else retryCurrentChunk() +} +``` + +--- + +## 6. otaStatusText widget verwijderen + +- `otaStatusText` verwijderen uit `activity_settings.xml` +- `otaStatusText` property verwijderen uit `SettingsActivity.kt` + +De progress bar in de OTA dialog toont al percentage — aparte statuswidget is overbodig.