BLE bonding/encryption removed — HMAC-SHA256 on application level provides authentication + replay protection. Eliminates bonding state bugs, write queue issues, and GATT_INSUFFICIENT_AUTHENTICATION complexity. Firmware: - Remove BLESecurity, setRespEncryptionKey, ESP_GATT_PERM_WRITE_ENCRYPTED - Characteristic permission: ESP_GATT_PERM_WRITE (no encryption required) - Characteristic property: PROPERTY_WRITE_NR (Write Without Response) Enables direct ON/OFF sequences without ACK wait time Android: - Remove bondReceiver, createBond, bonding state handling - Remove write queue / pendingRelayState / writeInProgress - Use WRITE_TYPE_NO_RESPONSE for minimal latency - Post-flash dialog auto-dismisses on USB detach (currentAction = NONE) - Separate progressDialog (flash progress) from dialog (post-flash info) - Detach during active action now calls cleanupAction() Build number: - BuildConfig.BUILD_NUMBER in build.gradle as yyyyMMddHHmmss timestamp - buildFeatures.buildConfig enabled - Displayed in SettingsActivity bottom (12sp, secondary color) Gradle: - Wrapper upgraded 8.5 → 8.14 - minSdk 24 → 26 (Android 8+) README updated to reflect HMAC-only security model.
398 lines
No EOL
19 KiB
Markdown
398 lines
No EOL
19 KiB
Markdown
# Sylvia Touch Relay
|
||
|
||
BLE-gestuurde relais-switch voor de M5Stack Atom VoiceS3R (ESP32-S3) met een bijbehorende Android-app. Het relais kan worden bediend via een capacitieve touch-sensor (open-drain) óf via Bluetooth Low Energy vanaf een Android-telefoon.
|
||
|
||
De BLE-verbinding is beveiligd met **HMAC-SHA256 authenticatie** (application-level handtekening met replay-protectie). BLE-link encryptie/bonding is bewust niet toegepast — HMAC biedt authenticatie en replay-bescherming op applicatieniveau, voldoende voor een relais-aan/uit commando.
|
||
|
||
## Hardware
|
||
|
||
| Component | Details |
|
||
|---|---|
|
||
| Board | M5Stack Atom VoiceS3R (SKU: C126-ECHO) |
|
||
| SoC | ESP32-S3-PICO-1-N8R8 (8MB Flash, 8MB PSRAM) |
|
||
| Touch sensor | Open-drain sensor op **G38** (3.3V, interne pullup) |
|
||
| Relais / LED | Op **G1** (3.3V GPIO output) |
|
||
| Status LED | Op **G46** (knippert 3x bij opstarten) |
|
||
|
||
### Pinout (Atom VoiceS3R onderste header)
|
||
|
||
```
|
||
G38 G39 3.3V GND 5V G5 G6 G7 G8
|
||
```
|
||
|
||
- **G38** — Touch sensor input (active LOW, interne pullup ingeschakeld)
|
||
- **G1** — Relais output (HIGH = aan, LOW = uit)
|
||
- **G46** — Status LED (3x knipperen bij opstarten, daarna uit)
|
||
|
||
### Aansluiting
|
||
|
||
```
|
||
Sensor GND → GND
|
||
Sensor OUT → G38
|
||
Sensor VCC → 3.3V
|
||
|
||
Relais + → G1
|
||
Relais - → GND
|
||
```
|
||
|
||
> **Let op:** De ESP32-S3 interne pullup is ~45kΩ. Bij lange draden of elektrische ruis een externe 4.7kΩ–10kΩ pullup naar 3.3V plaatsen.
|
||
|
||
## Beveiliging
|
||
|
||
De relay is beveiligd tegen ongeautoriseerde toegang via HMAC-SHA256 authenticatie op applicatieniveau.
|
||
|
||
### HMAC-SHA256 Authenticatie (application-level)
|
||
|
||
- Zowel de ESP firmware als de Android app delen een **32-byte shared secret** (HMAC key), die via USB provisioning op beide apparaten terechtkomt
|
||
- Elk commando wordt ondertekend met `HMAC-SHA256(key, command || sequence)`
|
||
- De ESP verifieert de handtekening voordat het relais schakelt
|
||
- Een **sequence counter** voorkomt replay-aanvallen: elk packet moet een strikt oplopend volgnummer hebben
|
||
|
||
#### Packet formaat (37 bytes)
|
||
|
||
```
|
||
Offset Grootte Veld Beschrijving
|
||
0 1 byte command 0x01 = relais aan, 0x00 = relais uit
|
||
1 4 bytes sequence Big-endian uint32, strikt oplopend (start bij 1)
|
||
5 32 bytes hmac HMAC-SHA256(key, command || sequence)
|
||
```
|
||
|
||
De sequence counter reset naar 0 bij elke (re)connectie op de ESP. De app start bij 1 om te zorgen dat het eerste packet altijd geaccepteerd wordt (`1 > 0`). Replay van een eerder packet wordt geweerd omdat `seq <= lastSeq`.
|
||
|
||
#### Key management
|
||
|
||
- De HMAC key wordt **niet meegecompileerd** — de ESP start zonder key en wordt geprovisioneerd via USB
|
||
- De key wordt opgeslagen in **NVS** (Non-Volatile Storage) op de ESP via de Arduino `Preferences` library (survives reboots en power-cycles)
|
||
- De app slaat de key op in **SharedPreferences** (overleeft app-herstarts)
|
||
- **USB auto-provisioning:** Sluit de ESP via USB-OTG aan op de telefoon. De app stuurt `KEY?\n`:
|
||
- Als de ESP al een key heeft → ESP antwoordt `HMAC_KEY:<hex>` → app slaat op lokaal op
|
||
- Als de ESP nog geen key heeft → ESP antwoordt `NO_KEY` → app genereert willekeurige 32-byte key via `SecureRandom` → stuurt `SET_KEY:<hex>\n` → ESP slaat op in NVS en antwoordt `OK` → app slaat op lokaal op
|
||
- Beide paden tonen dezelfde toast: **"Beveiligingssleutel gelezen"**
|
||
- Zo kan dezelfde app gebruikt worden voor meerdere ESP modules — each met een eigen key
|
||
|
||
#### USB Serial Protocol
|
||
|
||
```
|
||
App → ESP: KEY?\n
|
||
ESP → App: HMAC_KEY:C3F17B...AF\n (als key bekend)
|
||
ESP → App: NO_KEY\n (als ESP nog geen key heeft)
|
||
|
||
App → ESP: SET_KEY:C3F17B...AF\n (nieuwe key schrijven)
|
||
ESP → App: OK\n (bevestiging)
|
||
```
|
||
|
||
De ESP luistert in `loop()` voor USB serial data. Bij `KEY?\n` antwoordt hij met de hex key (`HMAC_KEY:<hex>`) of `NO_KEY`. Bij `SET_KEY:<64-hex>\n` slaat hij de key op in NVS en antwoordt `OK`.
|
||
|
||
#### Provisioning flow (auto)
|
||
|
||
```
|
||
Android app ESP32 (USB CDC)
|
||
─────────── ───────────────
|
||
USB device attached ──────────►
|
||
Request USB permission ◄────── Android dialog
|
||
Open CDC-ACM connection
|
||
Send KEY?\n ──────────────────►
|
||
┌─ if key in NVS ──► HMAC_KEY:<hex>\n
|
||
│ App saves to prefs → toast
|
||
│
|
||
└─ if no key ──────► NO_KEY\n
|
||
Generate 32 random bytes │
|
||
Send SET_KEY:<hex>\n ─────────► (store in NVS)
|
||
◄──────────────────────────── OK\n
|
||
App saves to prefs → toast
|
||
```
|
||
|
||
### Beveiligingsanalyse
|
||
|
||
| Aanval | Hoe geweerd |
|
||
|---|---|
|
||
| **Replay** | Sequence counter — zelfde packet werkt niet 2x (`seq <= lastSeq` wordt geweerd) |
|
||
| **Ongeautoriseerd device** | HMAC vereist geheime sleutel (32-byte random via USB provisioning) |
|
||
| **Commando vervalsing** | HMAC dekt command byte + sequence — wijzigen maakt handtekening ongeldig |
|
||
| **Brute-force key** | 32-byte key = 2^256 mogelijkheden — onhaalbaar |
|
||
| **Sniffen** | Pakketten zijn onversleuteld; geen confidentiality. Alleen commando's (aan/uit) zijn zichtbaar, niet de key. |
|
||
| **Reverse engineering van key** | Sleutel staat niet in de APK — wordt pas bij USB provisioning gegenereerd en in SharedPreferences opgeslagen. Extractie vereist fysieke toegang tot de telefoon én root. |
|
||
| **Key extractie via USB** | Fysieke USB-toegang tot de ESP geeft `KEY?\n` de huidige key terug. Bescherming: fysieke toegang tot het board beperken. |
|
||
| **NVS wiping** | `pio run -t erase` wist alle flash waaronder NVS — ESP is daarna leeg en kan opnieuw geprovisioneerd worden. |
|
||
|
||
### Waarom geen BLE-link encryptie?
|
||
|
||
BLE-link encryptie (bonding/Just Works) vereist complexe pairing flows die op Android sliding bugs veroorzaken (write queues, `GATT_INSUFFICIENT_AUTHENTICATION` handling, state leaks). HMAC biedt authenticatie en replay-bescherming op applicatieniveau — voldoende voor een relais. Het enige wat link-encryptie extra biedt is confidentiality (afluisteren), wat voor een aan/uit commando irrelevant is. De HMAC key wordt fysiek via USB uitgewisseld, nooit via BLE.
|
||
|
||
## Firmware
|
||
|
||
De firmware is geschreven in C++ met behulp van PlatformIO en de Arduino-framework. BLE wordt verzorgd door de ESP32 BLE library, HMAC door mbedtls (ingebouwd in ESP-IDF).
|
||
|
||
### Logica
|
||
|
||
Het relais gaat aan als **één van beide** bronnen actief is:
|
||
|
||
| Bron | Voorwaarde | Resultaat |
|
||
|---|---|---|
|
||
| Touch sensor | G38 = LOW (aanraken) | Relais aan |
|
||
| BLE command | App stuurt HMAC-ondertekend `0x01` | Relais aan |
|
||
| Geen van beide | G38 = HIGH + geen BLE command | Relais uit |
|
||
|
||
De touch sensor en BLE werken onafhankelijk. Als de sensor losgelaten wordt en de BLE-knop niet ingedrukt is, gaat het relais uit.
|
||
|
||
### BLE GATT
|
||
|
||
| Eigenschap | UUID |
|
||
|---|---|
|
||
| Service | `4fafc201-1fb5-459e-8fcc-c5c9c331914b` |
|
||
| Characteristic | `beb5483e-36e1-4688-b7f5-ea07361f26d8` |
|
||
|
||
De characteristic ondersteunt `WRITE_NR` (Write Without Response) en `NOTIFY`. Writes worden geaccepteerd zonder bonding — de HMAC-handtekening op applicatieniveau verzorgt de authenticatie. `WRITE_NR` zorgt voor minimale latency: de app kan direct na ON ook OFF sturen zonder te wachten op een ACK.
|
||
|
||
### Key provisioning
|
||
|
||
De firmware wordt zonder HMAC key gecompileerd. Bij de eerste opstart print de ESP:
|
||
|
||
```
|
||
No HMAC key provisioned — connect USB and run SET_KEY
|
||
```
|
||
|
||
Sluit de ESP via USB aan op de telefoon met de app geïnstalleerd. De app genereert automatisch een willekeurige key en stuurt deze naar de ESP. Na provisioning print de ESP bij opstarten:
|
||
|
||
```
|
||
HMAC key loaded from NVS
|
||
HMAC Key: C3F17B...AF
|
||
```
|
||
|
||
**Key wissen (factory reset):** Om de ESP terug te zetten naar ongeprovisioneerd:
|
||
|
||
```bash
|
||
pio run -t erase
|
||
```
|
||
|
||
Dit wist alle flash inclusief NVS. Na opnieuw flashen start de ESP weer zonder key.
|
||
|
||
### Serial monitor
|
||
|
||
Baudrate: **115200** (USB CDC). De firmware stuurt heartbeat-statussen elke 5 seconden, logt statuswijzigingen, en print de HMAC key (of de afwezigheid ervan) bij opstarten.
|
||
|
||
**Met geprovisioneerde key:**
|
||
```
|
||
=== Atom VoiceS3R BLE Relay ===
|
||
HMAC key loaded from NVS
|
||
LED blink done, starting BLE...
|
||
BLE: advertising started as 'AtomS3R-Relay'
|
||
HMAC Key: C3F17B612B93E82C56B055FA9B04E84664ACE1EC4E889F2763F271B65A5B54AF
|
||
Setup done.
|
||
[heartbeat] relay=OFF touch=no ble=disconnected
|
||
BLE: device connected
|
||
Relay ON (touch=YES, ble=connected)
|
||
BLE: relay ON (seq=1)
|
||
BLE: relay OFF (seq=2)
|
||
```
|
||
|
||
**Zonder key (nieuwe/gewiste ESP):**
|
||
```
|
||
=== Atom VoiceS3R BLE Relay ===
|
||
No HMAC key provisioned — connect USB and run SET_KEY
|
||
LED blink done, starting BLE...
|
||
BLE: advertising started as 'AtomS3R-Relay'
|
||
No HMAC key — use USB SET_KEY to provision
|
||
Setup done.
|
||
```
|
||
|
||
In deze staat accepteert de ESP wel BLE-verbindingen, maar elk HMAC-packet faalt verificatie omdat er geen key is om mee te vergelijken.
|
||
|
||
### Uploaden
|
||
|
||
> **Download-mode:** Houd de reset-knop ~2 seconden ingedrukt tot de groene LED aangaat, laat dan los. Het board is nu in download-mode.
|
||
|
||
```bash
|
||
# Flash firmware (behoudt NVS/key)
|
||
pio run --target upload
|
||
|
||
# Volledig wissen (incl. NVS/key) + opnieuw flashen
|
||
pio run -t erase && pio run -t upload
|
||
```
|
||
|
||
Na het uploaden treedt er een harde reset op. Als het board in download-mode blijft, USB eruit/erin of kort op reset tikken om in run-mode te komen.
|
||
|
||
### Firmware flashen via de app
|
||
|
||
De firmware kan ook rechtstreeks via de Android-app worden geflashed, zonder computer:
|
||
|
||
1. Zet de ESP in download-modus (houd reset ~2s in tot groene LED)
|
||
2. Sluit de ESP via USB-OTG aan op de telefoon
|
||
3. Open de app → Instellingen → Firmware Flashen
|
||
4. De app flasht bootloader, partitions, en firmware via USB
|
||
5. Dialog verdwijnt automatisch wanneer de USB wordt losgekoppeld
|
||
6. Sluit de ESP opnieuw aan → Sleutel Uitwisselen
|
||
|
||
De firmware binaries worden via `tools/copy_firmware.sh` naar de app assets gekopieerd.
|
||
|
||
## Android App
|
||
|
||
### Features
|
||
|
||
- **Auto-connect:** De app scant automatisch bij opstarten naar het BLE-apparaat "AtomS3R-Relay"
|
||
- **Auto-reconnect:** Bij wegvallen van de verbinding wordt na 2 seconden opnieuw gescant
|
||
- **Hold-to-activate:** Houd de knop ingedrukt voor relais aan, loslaten voor relais uit (zelfde gedrag als de sensor)
|
||
- **Write Without Response:** Gebruikt `WRITE_TYPE_NO_RESPONSE` voor minimale latency — ON en OFF kunnen direct achter elkaar worden verstuurd zonder ACK-wachttijd
|
||
- **MTU negotiatie:** Vraagt MTU 256 aan na service discovery (voor 37-byte HMAC packets)
|
||
- **HMAC authenticatie:** Elk commando wordt ondertekend met HMAC-SHA256 + sequence counter
|
||
- **USB auto-provisioning:** Bij aansluiten van de ESP via USB-OTG leest de app de bestaande key uit, of genereert een nieuwe key als de ESP er nog geen heeft. Beide scenario's tonen de toast _"Beveiligingssleutel gelezen"_
|
||
- **Firmware flashen via app:** De app kan de ESP32 firmware rechtstreeks via USB flashen (download-modus), geen computer nodig
|
||
- **Instellingenscherm:** Losse Activity voor sleutel uitwisseling, firmware flashen, en buildnummer weergave
|
||
- **Permissies:** Vraagt BLUETOOTH_SCAN, BLUETOOTH_CONNECT en ACCESS_FINE_LOCATION aan (Android 12+)
|
||
|
||
### Instellingenscherm
|
||
|
||
1. Open de app
|
||
2. Tik op het **tandwiel-icoon** rechtsboven
|
||
3. De app toont twee acties: **Sleutel Uitwisselen** en **Firmware Flashen**
|
||
4. **Sleutel Uitwisselen:** Sluit de ESP via USB-OTG aan. De app leest de key uit (als de ESP er al een heeft) of genereert een nieuwe key en provisioneert de ESP. Toast: _"Beveiligingssleutel gelezen"_
|
||
5. **Firmware Flashen:** Zet de ESP in download-modus (houd reset 2s in tot groene LED). Sluit aan via USB. De app flasht de firmware. Dialog verdwijnt automatisch bij loskoppelen USB.
|
||
6. Onderaan staat het **Buildnummer** (timestamp van de build)
|
||
|
||
De key wordt opgeslagen in SharedPreferences en overleeft app-herstarts. Bij geen key toont de hoofdapp "Geen HMAC sleutel - open Instellingen" en wordt er niet gescant.
|
||
|
||
### Hoofdscherm UI
|
||
|
||
- **Tandwiel-icoon** rechtsboven → opent instellingenscherm
|
||
- **HOLD-knop** centrum → houd ingedrukt voor relais aan
|
||
- **Status tekst** linksonder (14sp) → toont verbindingsstatus
|
||
|
||
### Bouwen
|
||
|
||
Vereisten:
|
||
- Android SDK (API 34+)
|
||
- Java 17+ (Java 26 werkt niet met Gradle — gebruik Java 21)
|
||
- Gradle 8.14 (via wrapper)
|
||
- USB host support (raw Android USB API, geen externe serial library)
|
||
- PlatformIO (voor firmware compilatie)
|
||
|
||
```bash
|
||
# Firmware compileren en naar app assets kopiëren
|
||
pio run
|
||
bash tools/copy_firmware.sh
|
||
|
||
# App bouwen en installeren
|
||
cd android
|
||
JAVA_HOME=/pad/naar/jdk21 ./gradlew installDebug
|
||
```
|
||
|
||
De APK staat na build in `android/app/build/outputs/apk/debug/app-debug.apk`.
|
||
|
||
### Installeren
|
||
|
||
```bash
|
||
adb install -r android/app/build/outputs/apk/debug/app-debug.apk
|
||
```
|
||
|
||
Of via draadloze foutopsporing:
|
||
|
||
```bash
|
||
adb connect <ip>:<poort>
|
||
adb install -r android/app/build/outputs/apk/debug/app-debug.apk
|
||
```
|
||
|
||
## Eerste keer setup
|
||
|
||
1. **Firmware compileren:**
|
||
```bash
|
||
pio run
|
||
bash tools/copy_firmware.sh
|
||
```
|
||
|
||
2. **App op de telefoon installeren:**
|
||
```bash
|
||
cd android
|
||
JAVA_HOME=/pad/naar/jdk21 ./gradlew installDebug
|
||
```
|
||
|
||
3. **Firmware flashen (kies één):**
|
||
|
||
**Optie A — Via de app (aanbevolen, geen computer nodig na eerste installatie):**
|
||
- Zet de ESP in download-modus (houd reset ~2s in tot groene LED)
|
||
- Sluit de ESP via USB-OTG aan op de telefoon
|
||
- Open de app → Instellingen → Firmware Flashen
|
||
- Dialog verdwijnt automatisch bij loskoppelen USB
|
||
|
||
**Optie B — Via computer:**
|
||
```bash
|
||
pio run -t upload
|
||
```
|
||
|
||
4. **Sleutel uitwisselen:**
|
||
- Sluit de ESP via USB-OTG aan op de telefoon (niet in download-modus)
|
||
- Open de app → Instellingen → Sleutel Uitwisselen
|
||
- De app leest de bestaande key uit of genereert een nieuwe
|
||
- Toast: _"Beveiligingssleutel gelezen"_
|
||
|
||
5. **Gebruiken:** De app scant automatisch, verbindt, en toont "Connected". Houd de HOLD-knop ingedrukt om het relais te schakelen.
|
||
|
||
### Nieuwe ESP, bestaande telefoon
|
||
|
||
- Sluit de nieuwe ESP via USB aan → app genereert nieuwe key (overschrijft vorige)
|
||
- Flash eerst de firmware (download-modus → Firmware Flashen), daarna sleutel uitwisselen
|
||
|
||
### Bestaande ESP, nieuwe telefoon
|
||
|
||
- Sluit de ESP via USB-OTG aan op de nieuwe telefoon
|
||
- De app leest de bestaande key uit de ESP en slaat lokaal op
|
||
- Toast: _"Beveiligingssleutel gelezen"_
|
||
|
||
## Projectstructuur
|
||
|
||
```
|
||
sylvia-touch-relay/
|
||
├── platformio.ini # PlatformIO configuratie (ESP32-S3)
|
||
├── src/
|
||
│ └── main.cpp # Firmware (BLE + HMAC + NVS + USB serial + sensor + relais)
|
||
├── tools/
|
||
│ └── copy_firmware.sh # Kopieert firmware binaries naar app assets
|
||
├── android/
|
||
│ ├── build.gradle # Root Gradle config
|
||
│ ├── settings.gradle # Gradle settings
|
||
│ ├── gradle.properties
|
||
│ ├── gradlew # Gradle wrapper
|
||
│ ├── gradle/wrapper/
|
||
│ │ └── gradle-wrapper.properties
|
||
│ └── app/
|
||
│ ├── build.gradle # App module config (esp32-flash-lib, minSdk 26)
|
||
│ └── src/main/
|
||
│ ├── AndroidManifest.xml
|
||
│ ├── assets/firmware/ # GeCompileerde firmware binaries
|
||
│ │ ├── bootloader.bin
|
||
│ │ ├── partitions.bin
|
||
│ │ ├── firmware.bin
|
||
│ │ └── boot_app0.bin
|
||
│ ├── java/com/eddy/atomrelay/
|
||
│ │ ├── MainActivity.kt # BLE client + HMAC signing
|
||
│ │ ├── SettingsActivity.kt # Key exchange + firmware flash UI
|
||
│ │ ├── EspFlasher.kt # ESP32 firmware flasher via USB
|
||
│ │ ├── UsbSerialTransport.kt # USB serial transport voor flasher
|
||
│ │ ├── UsbUtils.kt # Gedeelde USB utility functions
|
||
│ │ └── BuildConfig # Buildnummer (timestamp)
|
||
│ └── res/
|
||
│ ├── drawable/
|
||
│ │ ├── ic_eye_open.xml # Oog open icoon
|
||
│ │ ├── ic_eye_closed.xml # Oog dicht icoon
|
||
│ │ └── ic_settings.xml # Tandwiel icoon
|
||
│ ├── layout/
|
||
│ │ ├── activity_main.xml # Hoofd UI (FrameLayout)
|
||
│ │ └── activity_settings.xml # Key exchange + flash UI
|
||
│ └── values/strings.xml
|
||
├── .gitignore
|
||
└── README.md
|
||
```
|
||
|
||
## Technische notities
|
||
|
||
- De Atom VoiceS3R gebruikt de ES8311 audio codec op G45/G0/G48/G4/G3/G17/G11, IR TX op G47, en de knop op G41. Deze pinnen zijn niet beschikbaar voor eigen gebruik.
|
||
- G1 en G2 zitten op de HY2.0-4P Grove poort. G5-G8, G38, G39 zitten op de onderste expantie-header.
|
||
- De firmware gebruikt geen M5Unified library — alleen Arduino core + ESP32 BLE + mbedtls. Dit maakt de serial output betrouwbaarder via USB CDC.
|
||
- `ARDUINO_USB_CDC_ON_BOOT=1` is ingeschakeld voor virtuele serial via USB.
|
||
- De HMAC key wordt opgeslagen in NVS (Non-Volatile Storage) via de Arduino `Preferences` library. De key wordt niet meegecompileerd maar via USB provisioning door de Android app op de ESP gezet. Sleuteloverdracht werkt in beide richtingen: de app kan een key naar de ESP schrijven (nieuwe ESP) of een bestaande key uitlezen (bestaande ESP op nieuwe telefoon).
|
||
- De app gebruikt `WRITE_TYPE_NO_RESPONSE` (Write Without Response) voor minimale latency. De ESP characteristic heeft `PROPERTY_WRITE_NR` zodat writes zonder ACK worden geaccepteerd. Dit maakt directe ON/OFF sequenties mogelijk zonder write queue.
|
||
- De sequence counter start bij 1 (niet 0) om te voorkomen dat het eerste packet na connectie als replay wordt geweerd (`0 <= 0` is true in de `seq <= lastSeq` check).
|
||
- Voor meerdere ESP modules met verschillende keys: sluit elke ESP één voor één aan op de telefoon. De app genereert per ESP een eigen key en slaat deze op. De app ondersteunt momenteel slechts één key tegelijk (opgeslagen in SharedPreferences). Bij wisselen van ESP: wis app data of sluit de nieuwe ESP aan (overschrijft vorige key).
|
||
- De app gebruikt raw Android USB API (`UsbDeviceConnection.bulkTransfer`) in plaats van een externe serial library. Dit vermijdt compatibiliteitsproblemen met de ESP32-S3 USB-JTAG/serial controller die geen CDC `get_status` requests ondersteunt.
|
||
- CDC control interface setup: line coding (115200 8N1) via SET_LINE_CODING request + DTR/RTS via SET_CONTROL_LINE_STATE. Dit activeert de data-flow op de ESP32-S3 CDC-ACM interface voordat commando's worden gestuurd.
|
||
- Het buildnummer wordt via `BuildConfig.BUILD_NUMBER` in `build.gradle` gegenereerd als timestamp (`yyyyMMddHHmmss`). Elke build produceert een uniek buildnummer dat onderaan het instellingenscherm staat.
|
||
- `minSdk` is 26 (Android 8.0). Android 7 en lager worden niet ondersteund. |