Fix web OTA upload and isolate OTA sessions across firmware and goTool.
Split ESP-NOW into core/master/slave modules, block non-OTA UART traffic during updates, and hold the host serial port exclusively so dashboard polling cannot interleave with firmware uploads. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
+30
-12
@@ -41,7 +41,7 @@ flowchart TB
|
||||
| Rolle | UART | ESP-NOW | Zentrale Datenhaltung |
|
||||
|-------|------|---------|------------------------|
|
||||
| **Master** | Ja — einziger Befehlseingang von außen | Discover (Broadcast), Unicast zu Slaves | `client_registry.c` |
|
||||
| **Slave** | Nein | Antwort auf Discover, Heartbeat, Events zum Master | Lokaler Zustand in `esp_now_comm.c` |
|
||||
| **Slave** | Nein | Antwort auf Discover, Heartbeat, Events zum Master | `esp_now_slave.c` |
|
||||
|
||||
**Einstieg:** `main/powerpod.c` → `app_main()`.
|
||||
|
||||
@@ -158,11 +158,26 @@ flowchart LR
|
||||
| Protobuf + Framing | `main/uart_proto.c` | `uart_send_uart_message()` |
|
||||
| XOR-Rahmen | `main/uart.c` | `uart_send_framed()` |
|
||||
|
||||
### 4.3 Interner Befehlspost (ohne UART)
|
||||
### 4.3 OTA-Sperre (keine parallelen Befehle)
|
||||
|
||||
`msg_post(id, data, len)` in `cmd_handler.c` legt dieselbe `generic_msg_t`-Struktur in `cmd_queue` — für zukünftige Firmware-interne Quellen (z. B. ESP-NOW → PC-Pfad). Aktuell ist **UART der einzige Produktiv-Eingang**.
|
||||
Während einer OTA-Session (`ota_uart_is_active()` oder `ota_espnow_distribution_active()`) lehnt der Dispatcher alle UART-Befehle ab **außer**:
|
||||
|
||||
### 4.4 Bridge-Muster: UART-Befehl → ESP-NOW
|
||||
- `OTA_START`, `OTA_PAYLOAD`, `OTA_END`, `OTA_START_ESPNOW`, `OTA_SLAVE_PROGRESS`
|
||||
|
||||
Implementierung: `ota_session.c`, Prüfung in `vCmdDispatcherTask` vor Handler-Aufruf.
|
||||
|
||||
Auf dem **Slave** verarbeitet `espnow_recv_cb` während `ota_uart_is_active()` nur noch OTA-Nachrichten vom joined Master (kein Discover, Stream, LED, …).
|
||||
|
||||
Auf dem **Master** während ESP-NOW-Verteilung nur noch `ESPNOW_OTA_STATUS` aus dem recv_cb.
|
||||
|
||||
### 4.4 UART-Request-Decode (strikt)
|
||||
|
||||
| Regel | Beispiele |
|
||||
|-------|-----------|
|
||||
| Leerer protobuf-Body (`len == 0`) erlaubt | `VERSION`, `CLIENT_INFO`, `CACHE_STATUS`, `BATTERY_STATUS` (Defaults) |
|
||||
| `len > 0` → Decode muss gelingen, `which_payload` muss passen | `ACCEL_STREAM`, `TAP_NOTIFY`, `RESTART`, OTA, … |
|
||||
|
||||
### 4.5 Bridge-Muster: UART-Befehl → ESP-NOW
|
||||
|
||||
Viele Master-Handler folgen demselben Muster:
|
||||
|
||||
@@ -237,13 +252,12 @@ Queue-Größe Master: **64** Einträge (`powerpod.c`); volle Queue → Warnung,
|
||||
|
||||
### 6.1 Stack-Initialisierung
|
||||
|
||||
`esp_now_comm_init()` (`main/esp_now_comm.c`):
|
||||
`esp_now_comm_init()` (`esp_now_comm.c`) → `esp_now_core` (WiFi/Radio/Send) + Rolle:
|
||||
|
||||
1. `client_registry_init()`
|
||||
2. WiFi STA, Kanal = `network` (1–13)
|
||||
3. `esp_now_init()`, `esp_now_register_recv_cb(espnow_recv_cb)`
|
||||
4. **Master:** Broadcast-Peer, Tasks `espnow_disc` (500 ms), `espnow_mon` (1 s)
|
||||
5. **Slave:** `slave_tx_task`, `slave_heartbeat_task`, `slave_accel_stream_task` (16 ms)
|
||||
1. `client_registry_init()` (Master)
|
||||
2. `esp_now_init()`, recv_cb leitet an `esp_now_master_on_recv` / `esp_now_slave_on_recv`
|
||||
3. **Master** (`esp_now_master.c`): `espnow_disc`, `espnow_mon`
|
||||
4. **Slave** (`esp_now_slave.c`): `espnow_stx`, `espnow_hb`, `espnow_accel`, `ota_slave_work`
|
||||
|
||||
**Codec:** Rohes Paket = ein nanopb `EspNowMessage` — **kein** zusätzliches Framing (`main/esp_now_proto.c`).
|
||||
|
||||
@@ -276,6 +290,10 @@ sequenceDiagram
|
||||
|
||||
**Master-Verlust:** Slave setzt Join zurück, wenn **5 s** kein Discover vom gleichen Master (`SLAVE_MASTER_LOST_MS`).
|
||||
|
||||
**Join-Policy:** Master→Slave-Steuerung (Stream, Tap, LED, OTA, `UNICAST_TEST`, `SET_ACCEL_DEADZONE`, …) nur bei `s_slave_joined` und `src_addr == s_master_mac`. Während `ota_uart_is_active()` auf dem Slave verarbeitet der recv_cb nur OTA vom joined Master.
|
||||
|
||||
**Slave OTA:** Payload/End/Status-Sends laufen über `ota_slave_work_task` (Queue), nicht im ESP-NOW-recv_cb.
|
||||
|
||||
### 6.3 Master → Slave (Unicast)
|
||||
|
||||
Alle Master-Sends laufen über `send_message()` / `send_message_ex()`:
|
||||
@@ -361,7 +379,7 @@ flowchart TB
|
||||
| UART Handler-Boilerplate | `main/uart_cmd.c`, `main/uart_cmd.h` | Decode, Register, Send |
|
||||
| Command Dispatch | `main/cmd/cmd_handler.c` | Queue, Dispatcher |
|
||||
| UART Commands | `main/cmd/cmd_*.c` | Pro Befehl ein Modul |
|
||||
| ESP-NOW Kern | `main/esp_now_comm.c`, `.h` | WiFi, Tasks, recv/send, Slave-Join |
|
||||
| ESP-NOW | `esp_now_comm.c` Init/Router; `esp_now_core.c` Send/Peer; `esp_now_master.c` / `esp_now_slave.c` Rollenlogik |
|
||||
| ESP-NOW Codec | `main/esp_now_proto.c` | nanopb encode/decode |
|
||||
| Registry | `main/client_registry.c` | Slave-Tabelle + Caches |
|
||||
| BMA456 | `main/bosch456.c` | Sensor, Tap, Deadzone-Filter |
|
||||
@@ -386,7 +404,7 @@ Regenerierung: `make proto_generate` (siehe `DOCUMENTATION.md`).
|
||||
|
||||
## 11. Design-Entscheidungen
|
||||
|
||||
1. **Eine Queue für alle Commands** — einheitliches Modell für UART und künftige interne Quellen.
|
||||
1. **Eine Queue für UART-Commands** — einziger Eingang vom Host; während OTA nur OTA-Befehle.
|
||||
2. **Kein ESP-NOW-Send im `recv_cb`** — Slave antwortet auf Discover über `slave_tx_task` (Deadlock-/Stack-Risiko vermeiden).
|
||||
3. **Registry-MAC = ESP-NOW-Quelladresse** — Protobuf-MAC ist optional/informativ.
|
||||
4. **Gleiches Binary** — Konfiguration nur Hardware (DIP + Expander); reduziert Release-Komplexität.
|
||||
|
||||
@@ -140,8 +140,9 @@ Implementierung: `uart.c` — `parse_uart_byte()`, `uart_send_framed()`.
|
||||
1. `uart_read_task` parst Frames → `uart_enqueue_packet`
|
||||
2. `generic_msg_t` → `cmd_queue`
|
||||
3. `vCmdDispatcherTask` → registrierter `msg_callback_t`
|
||||
4. Handler: `uart_cmd_decode(data, len, &msg)` — `data` ist **nur** protobuf-Teil
|
||||
5. Antwort: `uart_cmd_init_response()` + Felder setzen + `uart_cmd_send()`
|
||||
4. Handler: `uart_cmd_decode(data, len, &msg)` — `data` ist **nur** protobuf-Teil; bei `len > 0` strikt (Decode/`which_payload`-Fehler → Fehlerantwort)
|
||||
5. `ota_session_uart_cmd_allowed()` — während OTA nur OTA-Befehle
|
||||
6. Antwort: `uart_cmd_init_response()` + Felder setzen + `uart_cmd_send()`
|
||||
|
||||
Hilfsmakro für Request-Felder:
|
||||
|
||||
@@ -404,7 +405,10 @@ Includes in generierten `.pb.c` müssen `"uart_messages.pb.h"` heißen (nicht `m
|
||||
|
||||
| Datei | Rolle |
|
||||
|-------|--------|
|
||||
| `esp_now_comm.c` | WiFi, ESP-NOW, alle Rollen-Tasks |
|
||||
| `esp_now_comm.c` | Init, recv-Router |
|
||||
| `esp_now_core.c` | WiFi, Peer, gemeinsamer Send |
|
||||
| `esp_now_master.c` | Master-Tasks, Registry, Unicast send |
|
||||
| `esp_now_slave.c` | Join, Heartbeat, Accel/Tap send |
|
||||
| `esp_now_proto.c` | nanopb für EspNowMessage |
|
||||
| `client_registry.c` | Slave-Tabelle + Telemetrie-Cache |
|
||||
|
||||
@@ -436,7 +440,8 @@ Includes in generierten `.pb.c` müssen `"uart_messages.pb.h"` heißen (nicht `m
|
||||
| `pod_settings.c` | NVS |
|
||||
| `pod_reboot.c` | Verzögerter Restart |
|
||||
| `ota_uart.c` | Flash-Puffer UART-OTA |
|
||||
| `ota_espnow.c` | OTA an Slaves |
|
||||
| `ota_espnow.c` | OTA Master/Slave; Slave-Work-Queue |
|
||||
| `ota_session.c` | OTA-Sperre für UART-Dispatcher |
|
||||
|
||||
### 14.5 Protobuf
|
||||
|
||||
|
||||
Reference in New Issue
Block a user