Add ESP firmware architecture and reference documentation.
Document UART command flow, ESP-NOW data paths, and module map for developers without covering goTool; link from main/README. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
parent
35b39fce46
commit
0cbc4d0644
398
docs/ARCHITECTURE.md
Normal file
398
docs/ARCHITECTURE.md
Normal file
@ -0,0 +1,398 @@
|
|||||||
|
# Powerpod — Architektur-Spezifikation
|
||||||
|
|
||||||
|
Dieses Dokument beschreibt die Firmware-Architektur des ESP32-S3-Projekts: Rollen, Schichten, Datenflüsse und die wichtigsten Implementierungsstellen. **goTool** (Host-CLI) ist bewusst ausgeschlossen — es nutzt nur das UART-Protokoll des Masters.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. Systemüberblick
|
||||||
|
|
||||||
|
Master und Slave laufen mit **demselben Binary**. Die Rolle wird beim Boot per DIP-Schalter und I2C-IO-Expander festgelegt; danach verzweigt die Initialisierung.
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
flowchart TB
|
||||||
|
subgraph host["Externer Host (UART)"]
|
||||||
|
PC[PC / beliebiger UART-Client]
|
||||||
|
end
|
||||||
|
|
||||||
|
subgraph master["Master ESP32-S3"]
|
||||||
|
UART_RX[uart_read_task]
|
||||||
|
Q[cmd_queue]
|
||||||
|
DISP[vCmdDispatcherTask]
|
||||||
|
HAND[cmd/*.c Handler]
|
||||||
|
REG[client_registry]
|
||||||
|
ENOW_M[esp_now_comm Master]
|
||||||
|
end
|
||||||
|
|
||||||
|
subgraph slaves["Slave ESP32-S3 × N"]
|
||||||
|
ENOW_S[esp_now_comm Slave]
|
||||||
|
BMA[BMA456 + LED Ring]
|
||||||
|
end
|
||||||
|
|
||||||
|
PC <-->|UART1 921600 framed + protobuf| UART_RX
|
||||||
|
UART_RX --> Q --> DISP --> HAND
|
||||||
|
HAND --> REG
|
||||||
|
HAND --> ENOW_M
|
||||||
|
ENOW_M <-->|ESP-NOW nanopb| ENOW_S
|
||||||
|
ENOW_S --> BMA
|
||||||
|
ENOW_S -->|Accel/Tap/Battery| ENOW_M
|
||||||
|
ENOW_M --> REG
|
||||||
|
```
|
||||||
|
|
||||||
|
| 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` |
|
||||||
|
|
||||||
|
**Einstieg:** `main/powerpod.c` → `app_main()`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. Boot und Konfiguration
|
||||||
|
|
||||||
|
### 2.1 Ablauf
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
sequenceDiagram
|
||||||
|
participant AM as app_main
|
||||||
|
participant NVS as pod_settings
|
||||||
|
participant I2C as I2C / IO-Expander
|
||||||
|
participant BMA as bosch456
|
||||||
|
participant EN as esp_now_comm
|
||||||
|
participant LR as led_ring
|
||||||
|
participant BI as board_input
|
||||||
|
participant CMD as cmd_handler + uart
|
||||||
|
|
||||||
|
AM->>NVS: pod_settings_init()
|
||||||
|
AM->>AM: GPIO DIP_MASTER → master/slave
|
||||||
|
AM->>I2C: Bus + Expander 0x20 → network 1–8
|
||||||
|
AM->>BMA: init_bma456() (Master + Slave)
|
||||||
|
AM->>AM: app_config_t füllen
|
||||||
|
AM->>BI: board_input_init()
|
||||||
|
AM->>EN: esp_now_comm_init(&app_config)
|
||||||
|
AM->>LR: led_ring_init()
|
||||||
|
alt master == true
|
||||||
|
AM->>CMD: Queue, Dispatcher, UART, Handler registrieren
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2.2 `app_config_t`
|
||||||
|
|
||||||
|
| Feld | Quelle | Bedeutung |
|
||||||
|
|------|--------|-----------|
|
||||||
|
| `master` | `DIP_MASTER` (GPIO 4): Low = Master | Steuert UART + Registry-Nutzung |
|
||||||
|
| `network` | IO-Expander, Bits 5–8 (nibble reversed) | 1–8 → WiFi/ESP-NOW-Kanal |
|
||||||
|
| `running_partition` | `esp_ota_get_running_partition()` | Aktives OTA-Label (`ota_0` / `ota_1`) |
|
||||||
|
|
||||||
|
**Dateien:** `main/app_config.h`, `main/powerpod.c`, `main/powerpod.h` (Pins).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. Schichtenmodell
|
||||||
|
|
||||||
|
```
|
||||||
|
┌─────────────────────────────────────────────────────────────┐
|
||||||
|
│ Befehlshandler (main/cmd/cmd_*.c) │
|
||||||
|
│ Decode/Encode: uart_cmd.c, Antwort: uart_proto.c │
|
||||||
|
├─────────────────────────────────────────────────────────────┤
|
||||||
|
│ Dispatch: cmd_handler.c (Queue + vCmdDispatcherTask) │
|
||||||
|
├─────────────────────────────────────────────────────────────┤
|
||||||
|
│ Transport UART: uart.c (Framing) │ ESP-NOW: esp_now_comm │
|
||||||
|
├─────────────────────────────────────────────────────────────┤
|
||||||
|
│ Protokoll: uart_messages.proto │ esp_now_messages.proto │
|
||||||
|
│ Codec: nanopb (proto/*.pb.c) │ esp_now_proto.c │
|
||||||
|
├─────────────────────────────────────────────────────────────┤
|
||||||
|
│ Domäne: client_registry, bosch456, led_ring, ota_*, board │
|
||||||
|
├─────────────────────────────────────────────────────────────┤
|
||||||
|
│ ESP-IDF: UART, WiFi, ESP-NOW, I2C, ADC, OTA, NVS, FreeRTOS │
|
||||||
|
└─────────────────────────────────────────────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. Datenfluss: Commands (Befehle)
|
||||||
|
|
||||||
|
Commands sind **asynchron über eine FreeRTOS-Queue** entkoppelt; der UART-Reader blockiert nicht auf Handler-Logik.
|
||||||
|
|
||||||
|
### 4.1 Eingang (UART → Handler)
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
flowchart LR
|
||||||
|
A[Bytes UART1] --> B[parse_uart_byte]
|
||||||
|
B --> C{Frame vollständig?}
|
||||||
|
C -->|ja| D[uart_enqueue_packet]
|
||||||
|
D --> E["generic_msg_t<br/>msg_id = payload[0]<br/>payload = Rest"]
|
||||||
|
E --> F[cmd_queue xQueueSend]
|
||||||
|
F --> G[vCmdDispatcherTask]
|
||||||
|
G --> H{msg_register_handler}
|
||||||
|
H --> I[cmd_*.c callback]
|
||||||
|
I --> J[free payload]
|
||||||
|
```
|
||||||
|
|
||||||
|
**Wichtige Stellen:**
|
||||||
|
|
||||||
|
| Schritt | Datei | Funktion |
|
||||||
|
|---------|--------|----------|
|
||||||
|
| Byte-Parser + Timeout 50 ms | `main/uart.c` | `parse_uart_byte()`, `uart_read_task()` |
|
||||||
|
| Queue-Eintrag | `main/uart.c` | `uart_enqueue_packet()` |
|
||||||
|
| Dispatcher | `main/cmd/cmd_handler.c` | `vCmdDispatcherTask()`, `msg_register_handler()` |
|
||||||
|
| Registrierung Master | `main/powerpod.c` | `cmd_*_register()` nach `init_uart()` |
|
||||||
|
|
||||||
|
**Nachrichten-ID:** Byte 0 des UART-Payloads = `alox_MessageType` (siehe `main/proto/uart_messages.proto`). Bytes 1… = nanopb-kodiertes `UartMessage` **ohne** wiederholtes Type-Feld — Handler erhalten nur den protobuf-Teil (`msg.payload` ab Offset 1).
|
||||||
|
|
||||||
|
### 4.2 Ausgang (Handler → UART)
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
flowchart LR
|
||||||
|
H[Handler baut alox_UartMessage] --> U[uart_cmd_send]
|
||||||
|
U --> P[uart_send_uart_message]
|
||||||
|
P --> E[pb_encode + Byte0 = type]
|
||||||
|
E --> F[uart_send_framed]
|
||||||
|
F --> W[uart_write_bytes]
|
||||||
|
```
|
||||||
|
|
||||||
|
**Wichtige Stellen:**
|
||||||
|
|
||||||
|
| Schritt | Datei | Funktion |
|
||||||
|
|---------|--------|----------|
|
||||||
|
| Handler-Hilfen | `main/uart_cmd.c` | `uart_cmd_decode()`, `uart_cmd_init_response()`, `uart_cmd_send()` |
|
||||||
|
| Protobuf + Framing | `main/uart_proto.c` | `uart_send_uart_message()` |
|
||||||
|
| XOR-Rahmen | `main/uart.c` | `uart_send_framed()` |
|
||||||
|
|
||||||
|
### 4.3 Interner Befehlspost (ohne UART)
|
||||||
|
|
||||||
|
`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**.
|
||||||
|
|
||||||
|
### 4.4 Bridge-Muster: UART-Befehl → ESP-NOW
|
||||||
|
|
||||||
|
Viele Master-Handler folgen demselben Muster:
|
||||||
|
|
||||||
|
1. `uart_cmd_decode()` → Request aus `UartMessage`
|
||||||
|
2. Lokale Aktion und/oder `client_registry_*` aktualisieren
|
||||||
|
3. `esp_now_comm_send_*()` mit MAC aus Registry
|
||||||
|
4. `uart_cmd_send()` mit Response
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
sequenceDiagram
|
||||||
|
participant Host
|
||||||
|
participant UART as uart.c
|
||||||
|
participant CMD as cmd_accel_deadzone.c
|
||||||
|
participant REG as client_registry
|
||||||
|
participant EN as esp_now_comm.c
|
||||||
|
participant SL as Slave
|
||||||
|
|
||||||
|
Host->>UART: Frame ACCEL_DEADZONE
|
||||||
|
UART->>CMD: generic_msg_t
|
||||||
|
CMD->>REG: set_accel_deadzone / lookup MAC
|
||||||
|
CMD->>EN: esp_now_comm_send_accel_deadzone
|
||||||
|
EN->>SL: ESPNOW_SET_ACCEL_DEADZONE
|
||||||
|
SL->>SL: bma456 + NVS
|
||||||
|
CMD->>Host: uart_cmd_send Response
|
||||||
|
```
|
||||||
|
|
||||||
|
**Beispiel-Implementierung:** `main/cmd/cmd_accel_deadzone.c`
|
||||||
|
**Weitere Bridge-Handler:** `cmd_accel_stream.c`, `cmd_tap_notify.c`, `cmd_led_ring.c`, `cmd_espnow_find_me.c`, `cmd_restart.c`, `cmd_espnow_unicast_test.c`, `cmd/cmd_ota.c` (OTA + `ota_espnow.c`).
|
||||||
|
|
||||||
|
**Nur Master / nur Cache (kein Slave-Roundtrip):** `cmd_client_info.c`, `cmd_battery.c`, `cmd_cache_status.c`, `cmd_version.c`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. Datenfluss: UART
|
||||||
|
|
||||||
|
### 5.1 Rahmenformat (Transport)
|
||||||
|
|
||||||
|
Unabhängig von Protobuf — reines Bytestream-Framing:
|
||||||
|
|
||||||
|
| Feld | Wert |
|
||||||
|
|------|------|
|
||||||
|
| Start | `0xAA` |
|
||||||
|
| Länge | 1 Byte (1–252) |
|
||||||
|
| Payload | `length` Bytes |
|
||||||
|
| Prüfsumme | XOR aller Payload-Bytes |
|
||||||
|
| Stopp | `0xCC` |
|
||||||
|
|
||||||
|
**Parameter:** `main/uart.h` — `UART_NUM_1`, `921600` Baud, TX GPIO **2**, RX GPIO **3**, `MAX_PAYLOAD_SIZE` 248.
|
||||||
|
|
||||||
|
### 5.2 Nutzlast (Anwendung)
|
||||||
|
|
||||||
|
```
|
||||||
|
Payload[0] = MessageType (enum, 1 Byte)
|
||||||
|
Payload[1…] = nanopb UartMessage (Felder ab type/payload oneof)
|
||||||
|
```
|
||||||
|
|
||||||
|
**Schema:** `main/proto/uart_messages.proto`
|
||||||
|
**Generiert:** `main/proto/uart_messages.pb.c/h` (`make proto_generate_uart`)
|
||||||
|
|
||||||
|
### 5.3 Tasks und Prioritäten
|
||||||
|
|
||||||
|
| Task | Stack | Priorität | Datei |
|
||||||
|
|------|-------|-----------|--------|
|
||||||
|
| `uart_rx` | 4096 | 5 | `uart.c` |
|
||||||
|
| `cmd_dispatch` | 8192 | 5 | `cmd_handler.c` |
|
||||||
|
|
||||||
|
Queue-Größe Master: **64** Einträge (`powerpod.c`); volle Queue → Warnung, Payload wird freigegeben.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 6. Datenfluss: ESP-NOW
|
||||||
|
|
||||||
|
### 6.1 Stack-Initialisierung
|
||||||
|
|
||||||
|
`esp_now_comm_init()` (`main/esp_now_comm.c`):
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
|
**Codec:** Rohes Paket = ein nanopb `EspNowMessage` — **kein** zusätzliches Framing (`main/esp_now_proto.c`).
|
||||||
|
|
||||||
|
### 6.2 Discovery und Join (Slave)
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
sequenceDiagram
|
||||||
|
participant M as Master espnow_disc
|
||||||
|
participant S as Slave recv_cb
|
||||||
|
participant TX as slave_tx_task
|
||||||
|
|
||||||
|
loop alle 500 ms
|
||||||
|
M->>M: ESPNOW_DISCOVER → FF:FF:…:FF
|
||||||
|
end
|
||||||
|
S->>S: handle_discover (network match)
|
||||||
|
S->>S: s_slave_joined, s_master_mac
|
||||||
|
Note over S,TX: Kein Send aus recv_cb!
|
||||||
|
S->>TX: SLAVE_TX_SLAVE_INFO
|
||||||
|
TX->>M: ESPNOW_SLAVE_INFO
|
||||||
|
S->>TX: SLAVE_TX_BATTERY
|
||||||
|
TX->>M: ESPNOW_BATTERY_REPORT
|
||||||
|
loop alle 1 s
|
||||||
|
S->>M: ESPNOW_HEARTBEAT
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
|
**Registry-Schlüssel:** immer `recv_info.src_addr` (WiFi-MAC des Senders), **nicht** optionales `mac`-Feld in der Protobuf-Nachricht.
|
||||||
|
|
||||||
|
**Slave-ID:** `mac[5]` (letztes Oktett) — kann kollidieren; eindeutig ist die volle MAC in der Registry.
|
||||||
|
|
||||||
|
**Master-Verlust:** Slave setzt Join zurück, wenn **5 s** kein Discover vom gleichen Master (`SLAVE_MASTER_LOST_MS`).
|
||||||
|
|
||||||
|
### 6.3 Master → Slave (Unicast)
|
||||||
|
|
||||||
|
Alle Master-Sends laufen über `send_message()` / `send_message_ex()`:
|
||||||
|
|
||||||
|
1. `esp_now_proto_encode()`
|
||||||
|
2. `ensure_peer(dest_mac)`
|
||||||
|
3. `esp_now_send()`
|
||||||
|
|
||||||
|
Öffentliche API: `main/esp_now_comm.h` (`esp_now_comm_send_*`).
|
||||||
|
|
||||||
|
**Empfang Slave:** `espnow_recv_cb()` → Switch auf `which_payload` → Handler (`handle_slave_*`).
|
||||||
|
|
||||||
|
### 6.4 Slave → Master (Events)
|
||||||
|
|
||||||
|
| Nachricht | Task / Trigger | Master-Ziel |
|
||||||
|
|-----------|----------------|-------------|
|
||||||
|
| `ESPNOW_ACCEL_SAMPLE` | `slave_accel_stream_task` 16 ms | `client_registry_update_accel()` |
|
||||||
|
| `ESPNOW_TAP_EVENT` | BMA456-IRQ → `on_bma456_tap` | `client_registry_update_tap()` |
|
||||||
|
| `ESPNOW_BATTERY_REPORT` | Heartbeat + nach Join | `client_registry_update_battery()` |
|
||||||
|
| `ESPNOW_SLAVE_INFO` / `HEARTBEAT` | `slave_tx_task` / Heartbeat-Task | `client_registry_heartbeat()` |
|
||||||
|
| `ESPNOW_OTA_STATUS` | `ota_espnow_slave_*` | `ota_espnow_master_on_status()` |
|
||||||
|
|
||||||
|
**Master recv:** `espnow_recv_cb()` — Presence, Accel, Tap, Battery, OTA-Status.
|
||||||
|
|
||||||
|
### 6.5 OTA über ESP-NOW
|
||||||
|
|
||||||
|
Nach erfolgreichem UART-OTA auf dem Master (oder `OTA_START_ESPNOW`): `ota_espnow.c` liest gestagte Partition, sendet `OTA_START` / `PAYLOAD` (≤200 B, `send_message_ex` mit ACK-Semaphore) / `OTA_END` an alle **available** Slaves.
|
||||||
|
|
||||||
|
Gemeinsamer Flash-Puffer: `ota_uart.c` (4 KiB Blockgröße).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 7. Client Registry (Master-Datenhub)
|
||||||
|
|
||||||
|
Die Registry ist die **zentrale Brücke** zwischen ESP-NOW-Echtzeitdaten und UART-Abfragen.
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
flowchart TB
|
||||||
|
EN_RX[espnow_recv_cb] --> REG[client_registry]
|
||||||
|
CMD_R[cmd_client_info / battery / cache_status] --> REG
|
||||||
|
CMD_W[cmd_* write paths] --> REG
|
||||||
|
REG --> CMD_R
|
||||||
|
EN_TX[esp_now_comm_send_*] --> MAC[MAC aus Registry]
|
||||||
|
```
|
||||||
|
|
||||||
|
| Daten | Aktualisiert durch | Gelesen durch UART |
|
||||||
|
|-------|-------------------|-------------------|
|
||||||
|
| Presence, Version | HEARTBEAT / SLAVE_INFO | `CLIENT_INFO` |
|
||||||
|
| Accel-Stream-Samples | `ESPNOW_ACCEL_SAMPLE` | `CACHE_STATUS` |
|
||||||
|
| Tap-Events (16 ms Cache) | `ESPNOW_TAP_EVENT` | `CACHE_STATUS` |
|
||||||
|
| Batterie | `ESPNOW_BATTERY_REPORT` + Master-ADC | `BATTERY_STATUS` |
|
||||||
|
| Konfig-Flags | Handler + ESP-NOW | `CLIENT_INFO`, diverse GET |
|
||||||
|
|
||||||
|
**Datei:** `main/client_registry.c`, `main/client_registry.h` (max. 16 Clients, Timeout 3 s ohne Heartbeat).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 8. FreeRTOS-Tasks (Übersicht)
|
||||||
|
|
||||||
|
| Task | Rolle | Intervall / Trigger |
|
||||||
|
|------|-------|---------------------|
|
||||||
|
| `uart_rx` | Master | UART read 20 ms timeout |
|
||||||
|
| `cmd_dispatch` | Master | Queue blocking |
|
||||||
|
| `espnow_disc` | Master | 500 ms Discover |
|
||||||
|
| `espnow_mon` | Master | 1 s Timeout + Master-Battery |
|
||||||
|
| `espnow_stx` | Slave | Queue: SLAVE_INFO, Battery nach Join |
|
||||||
|
| `espnow_hb` | Slave | 1 s Heartbeat + 30 s Battery |
|
||||||
|
| `espnow_accel` | Slave | 16 ms Accel wenn Stream an |
|
||||||
|
| `bma456_poll` | Beide | 10 Hz (`bosch456.c`) |
|
||||||
|
| `vTaskLedRing` | Beide | LED-Ring-Befehle |
|
||||||
|
|
||||||
|
`app_main` endet in `while(1) vTaskDelay(portMAX_DELAY)` — alle Arbeit läuft in Tasks.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 9. Implementierungskarte (wichtige Dateien)
|
||||||
|
|
||||||
|
| Bereich | Pfad | Verantwortung |
|
||||||
|
|---------|------|----------------|
|
||||||
|
| Einstieg / Init | `main/powerpod.c` | Boot-Reihenfolge, Master-Handler-Register |
|
||||||
|
| UART Transport | `main/uart.c`, `main/uart.h` | Framing RX/TX |
|
||||||
|
| UART Protobuf TX | `main/uart_proto.c` | `uart_send_uart_message` |
|
||||||
|
| 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 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 |
|
||||||
|
| LED | `main/led_ring.c` | 95 LEDs, Digit-Maps |
|
||||||
|
| OTA UART | `main/ota_uart.c`, `cmd/cmd_ota.c` | A/B-Partition Upload |
|
||||||
|
| OTA ESP-NOW | `main/ota_espnow.c` | Verteilung an Slaves |
|
||||||
|
| Einstellungen NVS | `main/pod_settings.c` | Accel-Deadzone persistent |
|
||||||
|
| Board | `main/board_input.c` | Taster, LiPo-ADC |
|
||||||
|
| Protobuf Schemas | `main/proto/*.proto` | Vertrag UART / ESP-NOW |
|
||||||
|
| Vendor BMA456 | `components/bma456/` | Nur `bma4.c` + `bma456h.c` gelinkt |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 10. Protokoll-Verträge (Kurzreferenz)
|
||||||
|
|
||||||
|
- **UART:** `main/proto/uart_messages.proto` — Host ↔ Master
|
||||||
|
- **ESP-NOW:** `main/proto/esp_now_messages.proto` — Master ↔ Slave
|
||||||
|
|
||||||
|
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.
|
||||||
|
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.
|
||||||
|
5. **Nanopb statt vollem protobuf-c** — passend für ESP-RAM/Flash.
|
||||||
|
6. **Master aggregiert Sensor-Daten** — Slaves streamen; Host pollt Cache per UART (`CACHE_STATUS`), kein Durchreichen jedes Accel-Samples über UART.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
Weitere Details (Befehlsliste, GPIO, Build, BMA456): [`DOCUMENTATION.md`](DOCUMENTATION.md).
|
||||||
481
docs/DOCUMENTATION.md
Normal file
481
docs/DOCUMENTATION.md
Normal file
@ -0,0 +1,481 @@
|
|||||||
|
# Powerpod ESP-Firmware — Dokumentation
|
||||||
|
|
||||||
|
Vollständige Referenz für das ESP-IDF-Projekt unter `main/` und `components/`. Diese Doku beschreibt **nur die Firmware** — kein goTool, keine Host-Implementierung.
|
||||||
|
|
||||||
|
- **Architektur & Datenflüsse:** [`ARCHITECTURE.md`](ARCHITECTURE.md)
|
||||||
|
- **Neues Feature end-to-end:** [`adding-a-feature.md`](adding-a-feature.md)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Inhaltsverzeichnis
|
||||||
|
|
||||||
|
1. [Projektziel](#1-projektziel)
|
||||||
|
2. [Hardware und Pins](#2-hardware-und-pins)
|
||||||
|
3. [Build und Flash](#3-build-und-flash)
|
||||||
|
4. [Boot-Konfiguration](#4-boot-konfiguration)
|
||||||
|
5. [UART-Protokoll](#5-uart-protokoll)
|
||||||
|
6. [ESP-NOW-Protokoll](#6-esp-now-protokoll)
|
||||||
|
7. [Befehlsreferenz (UART)](#7-befehlsreferenz-uart)
|
||||||
|
8. [OTA](#8-ota)
|
||||||
|
9. [BMA456 Beschleunigungssensor](#9-bma456-beschleunigungssensor)
|
||||||
|
10. [LED-Ring](#10-led-ring)
|
||||||
|
11. [Board-Input und Batterie](#11-board-input-und-batterie)
|
||||||
|
12. [Persistenz (NVS)](#12-persistenz-nvs)
|
||||||
|
13. [Protobuf und Code-Generierung](#13-protobuf-und-code-generierung)
|
||||||
|
14. [Modulreferenz](#14-modulreferenz)
|
||||||
|
15. [Logging-Tags](#15-logging-tags)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. Projektziel
|
||||||
|
|
||||||
|
**Powerpod** ist Firmware für ESP32-S3-Knoten in einem verteilten System:
|
||||||
|
|
||||||
|
- Ein **Master** spricht per **UART** mit einem externen Host und verwaltet bis zu **16 Slaves** über **ESP-NOW**.
|
||||||
|
- **Slaves** haben keinen UART-Befehlspfad; sie joinen über periodisches **Discover**, senden Heartbeats und liefern Sensor-/Statusdaten.
|
||||||
|
- Master und Slave nutzen **identisches Firmware-Image**; Rolle und Funknetz werden beim Boot erkannt.
|
||||||
|
|
||||||
|
Zielbild für den Host: Befehle an den Master senden; der Master steuert Slaves und **cached** deren Telemetrie (`client_registry`) für schnelle UART-Abfragen.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. Hardware und Pins
|
||||||
|
|
||||||
|
> Pinbelegung ist in `powerpod.h` als vorläufig markiert — mit Schaltplan abgleichen.
|
||||||
|
|
||||||
|
| Signal | GPIO | Datei / Modul |
|
||||||
|
|--------|------|----------------|
|
||||||
|
| DIP Master/Slave | 4 | `powerpod.h` — Low = Master |
|
||||||
|
| I2C SCL / SDA | 5 / 6 | IO-Expander `0x20`, BMA456 `0x18` |
|
||||||
|
| UART1 TX / RX | **2 / 3** | `uart.h` (Adapter: ESP-TX → Host-RX) |
|
||||||
|
| LED-Ring (WS2812) | 7 | `led_ring.c` |
|
||||||
|
| BMA456 Interrupt | 10 | `bosch456.c` |
|
||||||
|
| Taster | 12 | `board_input.c` |
|
||||||
|
| LiPo ADC 1 | 1 | `board_input.c` |
|
||||||
|
| LiPo ADC 2 | 12 | Entfällt wenn = Taster-GPIO |
|
||||||
|
|
||||||
|
**UART:** `UART_NUM_1`, **921600** Baud, 8N1, kein Flow-Control.
|
||||||
|
|
||||||
|
**I2C:** 100 kHz, interne Pull-ups, gemeinsamer Bus für Expander und BMA456H.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. Build und Flash
|
||||||
|
|
||||||
|
**Ziel-Chip:** ESP32-S3 (ESP-IDF).
|
||||||
|
|
||||||
|
```bash
|
||||||
|
source ~/esp/esp-idf/export.sh # oder export.fish
|
||||||
|
cd /pfad/zu/powerpod
|
||||||
|
idf.py build
|
||||||
|
idf.py -p /dev/ttyUSB0 flash monitor
|
||||||
|
```
|
||||||
|
|
||||||
|
- **Git-Hash** wird beim Build in `POWERPOD_GIT_HASH` eingebettet (`main/CMakeLists.txt`).
|
||||||
|
- **Firmware-Version:** `POWERPOD_FW_VERSION` (Default `1` in `esp_now_comm.c` / `cmd_version.c`).
|
||||||
|
|
||||||
|
**Komponenten:**
|
||||||
|
|
||||||
|
| Pfad | Inhalt |
|
||||||
|
|------|--------|
|
||||||
|
| `main/` | Anwendungslogik |
|
||||||
|
| `components/bma456/` | Bosch BMA456H-Treiber (Vendor) |
|
||||||
|
| `libs/nanopb/` | Protobuf-Codec für Embedded |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. Boot-Konfiguration
|
||||||
|
|
||||||
|
### 4.1 Initialisierungsreihenfolge (`powerpod.c`)
|
||||||
|
|
||||||
|
1. `pod_settings_init()` — NVS
|
||||||
|
2. DIP → `app_config.master`
|
||||||
|
3. I2C + IO-Expander → `app_config.network` (1–8)
|
||||||
|
4. `init_bma456()` — optional, bei Fehler weiter ohne Sensor
|
||||||
|
5. `pod_settings_apply_accel_deadzone()` wenn Sensor da
|
||||||
|
6. OTA-Partition → `app_config.running_partition`
|
||||||
|
7. `board_input_init()`
|
||||||
|
8. `esp_now_comm_init(&app_config)` — **immer** (Master + Slave)
|
||||||
|
9. `led_ring_init()`
|
||||||
|
10. **Nur Master:** `cmd_queue` → `init_cmdHandler` → `init_uart` → alle `cmd_*_register()`
|
||||||
|
|
||||||
|
### 4.2 Master vs. Slave
|
||||||
|
|
||||||
|
| Funktion | Master | Slave |
|
||||||
|
|----------|--------|-------|
|
||||||
|
| UART Command-Handler | Ja | Nein |
|
||||||
|
| ESP-NOW Discover senden | Ja (Broadcast) | Nein |
|
||||||
|
| ESP-NOW auf Discover reagieren | Nein | Ja |
|
||||||
|
| `client_registry` für Fremdknoten | Ja | Nein (nur eigener Join-State) |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. UART-Protokoll
|
||||||
|
|
||||||
|
*(Transport + Anwendung — Datenfluss-Diagramme in [`ARCHITECTURE.md`](ARCHITECTURE.md) §4–5.)*
|
||||||
|
|
||||||
|
### 5.1 Rahmen
|
||||||
|
|
||||||
|
| Byte | Inhalt |
|
||||||
|
|------|--------|
|
||||||
|
| 0 | `0xAA` Start |
|
||||||
|
| 1 | Länge N (1–252) |
|
||||||
|
| 2…N+1 | Payload |
|
||||||
|
| N+2 | XOR-Checksum über Payload |
|
||||||
|
| N+3 | `0xCC` Stopp |
|
||||||
|
|
||||||
|
Implementierung: `uart.c` — `parse_uart_byte()`, `uart_send_framed()`.
|
||||||
|
|
||||||
|
### 5.2 Anwendungs-Payload
|
||||||
|
|
||||||
|
| Offset | Bedeutung |
|
||||||
|
|--------|-----------|
|
||||||
|
| 0 | `MessageType` (siehe Enum in `uart_messages.proto`) |
|
||||||
|
| 1… | nanopb `UartMessage` (ohne separates type-Feld im protobuf-Teil) |
|
||||||
|
|
||||||
|
**Antworten** nutzen dieselbe Struktur: Byte 0 = Response-Typ, Rest = kodierte `UartMessage`.
|
||||||
|
|
||||||
|
### 5.3 Handler-Pipeline
|
||||||
|
|
||||||
|
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()`
|
||||||
|
|
||||||
|
Hilfsmakro für Request-Felder:
|
||||||
|
|
||||||
|
```c
|
||||||
|
UART_CMD_REQ(&uart_msg, alox_UartMessage_accel_deadzone_request_tag, accel_deadzone_request)
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 6. ESP-NOW-Protokoll
|
||||||
|
|
||||||
|
*(Join, Tasks, Registry — [`ARCHITECTURE.md`](ARCHITECTURE.md) §6.)*
|
||||||
|
|
||||||
|
### 6.1 Grundlagen
|
||||||
|
|
||||||
|
- WiFi **STA**, keine AP-Verbindung.
|
||||||
|
- **Kanal** = `app_config.network` (1–13).
|
||||||
|
- Payload = ein `EspNowMessage` (nanopb), max. `ESP_NOW_MAX_DATA_LEN`.
|
||||||
|
- Schema: `main/proto/esp_now_messages.proto`.
|
||||||
|
|
||||||
|
### 6.2 Nachrichtentypen
|
||||||
|
|
||||||
|
| Type | Richtung | Payload | Zweck |
|
||||||
|
|------|----------|---------|--------|
|
||||||
|
| `ESPNOW_DISCOVER` | M→Broadcast | `discover.network` | Slaves finden Master |
|
||||||
|
| `ESPNOW_SLAVE_INFO` | S→M | `slave_info` | Erste Registrierung |
|
||||||
|
| `ESPNOW_HEARTBEAT` | S→M | `heartbeat` | Keepalive 1 s |
|
||||||
|
| `ESPNOW_SET_ACCEL_DEADZONE` | M→S | `accel_deadzone` | Deadzone LSB |
|
||||||
|
| `ESPNOW_SET_ACCEL_STREAM` | M→S | `accel_stream` | Stream ~16 ms |
|
||||||
|
| `ESPNOW_ACCEL_SAMPLE` | S→M | `accel_sample` | x/y/z Roh-LSB |
|
||||||
|
| `ESPNOW_SET_TAP_NOTIFY` | M→S | `tap_notify` | Tap-Arten filtern |
|
||||||
|
| `ESPNOW_TAP_EVENT` | S→M | `tap_event` | kind 1/2/3 |
|
||||||
|
| `ESPNOW_BATTERY_QUERY` | M→S | `battery_query` | On-demand |
|
||||||
|
| `ESPNOW_BATTERY_REPORT` | S→M | `battery_report` | mV LiPo 1/2 |
|
||||||
|
| `ESPNOW_LED_RING` | M→S | `led_ring` | Wie UART LED_RING |
|
||||||
|
| `ESPNOW_FIND_ME` | M→S | `find_me` | LED-Locate |
|
||||||
|
| `ESPNOW_RESTART` | M→S | `restart` | Reboot Slave |
|
||||||
|
| `ESPNOW_UNICAST_TEST` | M→S | `unicast_test` | Link-Test |
|
||||||
|
| `ESPNOW_OTA_*` | M↔S | `ota_*` | Firmware-Verteilung |
|
||||||
|
|
||||||
|
### 6.3 Zeitkonstanten (`esp_now_comm.c`)
|
||||||
|
|
||||||
|
| Konstante | Wert |
|
||||||
|
|-----------|------|
|
||||||
|
| Discover-Intervall | 500 ms |
|
||||||
|
| Heartbeat | 1000 ms |
|
||||||
|
| Client-Timeout (Master) | 3 × Heartbeat = 3 s → `available=false` |
|
||||||
|
| Master-Verlust (Slave) | 5 s ohne Discover |
|
||||||
|
| Accel-Stream | 16 ms |
|
||||||
|
| Batterie-Report | 30 s (+ einmal 150 ms nach Join) |
|
||||||
|
|
||||||
|
### 6.4 `EspNowSlavePresence`
|
||||||
|
|
||||||
|
Felder: `network`, `mac` (6 B), `version`, `slave_id`, `available`, `used`.
|
||||||
|
|
||||||
|
- `slave_id` = letztes Oktett der STA-MAC.
|
||||||
|
- Registry auf dem Master indexiert über **Sender-MAC** der ESP-NOW-Callback.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 7. Befehlsreferenz (UART)
|
||||||
|
|
||||||
|
Nur auf dem **Master** registriert (`powerpod.c`). IDs aus `MessageType` in `uart_messages.proto`.
|
||||||
|
|
||||||
|
| ID | Name | Modul | Kurzbeschreibung |
|
||||||
|
|----|------|-------|------------------|
|
||||||
|
| 1 | ACK | — | Reserviert |
|
||||||
|
| 2 | ECHO | — | Reserviert |
|
||||||
|
| 3 | VERSION | `cmd_version.c` | FW-Version, Git-Hash, OTA-Partition |
|
||||||
|
| 4 | CLIENT_INFO | `cmd_client_info.c` | Liste `client_registry` |
|
||||||
|
| 5 | CLIENT_INPUT | — | Geplant |
|
||||||
|
| 6 | ACCEL_DEADZONE | `cmd_accel_deadzone.c` | Get/Set Deadzone lokal + ESP-NOW |
|
||||||
|
| 7 | ESPNOW_UNICAST_TEST | `cmd_espnow_unicast_test.c` | Link-Test zu Slave |
|
||||||
|
| 8 | LED_RING | `cmd_led_ring.c` | Ring lokal / ESP-NOW |
|
||||||
|
| 16 | OTA_START | `cmd_ota.c` | UART-OTA beginnen |
|
||||||
|
| 17 | OTA_PAYLOAD | `cmd_ota.c` | Chunk ≤200 B |
|
||||||
|
| 18 | OTA_END | `cmd_ota.c` | Abschluss + ESP-NOW-Verteilung |
|
||||||
|
| 19 | OTA_STATUS | `cmd_ota.c` | Gerät → Host Status |
|
||||||
|
| 20 | OTA_START_ESPNOW | `cmd_ota.c` | Nur ESP-NOW aus Staging |
|
||||||
|
| 21 | OTA_SLAVE_PROGRESS | `cmd_ota_slave_progress.c` | Fortschritt pro Slave |
|
||||||
|
| 22 | FIND_ME | `cmd_espnow_find_me.c` | LED Locate |
|
||||||
|
| 23 | RESTART | `cmd_restart.c` | Master oder Slave reboot |
|
||||||
|
| 25 | ACCEL_STREAM | `cmd_accel_stream.c` | ESP-NOW Accel-Stream an/aus |
|
||||||
|
| 26 | BATTERY_STATUS | `cmd_battery.c` | Cache LiPo Master + Slaves |
|
||||||
|
| 27 | TAP_NOTIFY | `cmd_tap_notify.c` | Tap-Weiterleitung konfigurieren |
|
||||||
|
| 29 | CACHE_STATUS | `cmd_cache_status.c` | Accel + Tap Cache (ein Round-Trip) |
|
||||||
|
|
||||||
|
### 7.1 VERSION (3)
|
||||||
|
|
||||||
|
- **Request:** nur Typ-Byte `0x03` oder leerer protobuf-Body.
|
||||||
|
- **Response:** `version`, `git_hash`, `running_partition`.
|
||||||
|
|
||||||
|
### 7.2 CLIENT_INFO (4)
|
||||||
|
|
||||||
|
- **Response:** wiederholtes `ClientInfo` pro Registry-Eintrag: `id`, `mac`, `version`, `available`, `used`, `last_ping`, `last_success_ping`, Tap-/Stream-Flags.
|
||||||
|
|
||||||
|
### 7.3 ACCEL_DEADZONE (6)
|
||||||
|
|
||||||
|
- **Request:** `write`, `deadzone`, `client_id` (0=lokal), `all_clients`.
|
||||||
|
- **Write Master:** Registry + `esp_now_comm_send_accel_deadzone` pro Slave; lokal `bma456` + NVS.
|
||||||
|
- **Slave-Empfang:** `handle_slave_accel_deadzone` in `esp_now_comm.c`.
|
||||||
|
|
||||||
|
### 7.4 ACCEL_STREAM (25)
|
||||||
|
|
||||||
|
- Master setzt `client_registry_set_accel_stream` und sendet `ESPNOW_SET_ACCEL_STREAM`.
|
||||||
|
- Slave-Task `slave_accel_stream_task` sendet `ESPNOW_ACCEL_SAMPLE` alle 16 ms.
|
||||||
|
|
||||||
|
### 7.5 TAP_NOTIFY (27)
|
||||||
|
|
||||||
|
- Konfiguriert auf Slave welche Tap-Arten `ESPNOW_TAP_EVENT` auslösen.
|
||||||
|
- Tap-Quelle: BMA456-Interrupt → `on_bma456_tap` in `esp_now_comm.c`.
|
||||||
|
|
||||||
|
### 7.6 CACHE_STATUS (29)
|
||||||
|
|
||||||
|
- **Request:** leer.
|
||||||
|
- **Response:** pro Slave mit Stream und/oder Tap-Notify: gecachte Accel-Werte (`age_ms`) und konsumierte Tap-Events (`client_registry_take_tap`).
|
||||||
|
|
||||||
|
### 7.7 BATTERY_STATUS (26)
|
||||||
|
|
||||||
|
- Liest **nur Cache** — keine ESP-NOW-Roundtrip-Pflicht pro Abfrage.
|
||||||
|
- Master-Batterie: `client_registry_set_master_battery` (Monitor-Task).
|
||||||
|
|
||||||
|
### 7.8 LED_RING (8)
|
||||||
|
|
||||||
|
| mode | Bedeutung |
|
||||||
|
|------|-----------|
|
||||||
|
| 0 | Clear |
|
||||||
|
| 1 | Progress 0–100 % |
|
||||||
|
| 2 | Digit 0–10 |
|
||||||
|
| 3 | Blink |
|
||||||
|
| 4 | Find-me (RGB-Sequenz) |
|
||||||
|
| 5 | Solid color |
|
||||||
|
|
||||||
|
`client_id=0` nur Master-Ring; `>0` ESP-NOW; `all_clients` Broadcast über Registry.
|
||||||
|
|
||||||
|
### 7.9 FIND_ME (22) / RESTART (23)
|
||||||
|
|
||||||
|
- `client_id=0`: lokaler Master (`led_ring_find_me` / `pod_schedule_restart`).
|
||||||
|
- `client_id>0`: ESP-NOW Unicast zum Slave.
|
||||||
|
|
||||||
|
### 7.10 ESPNOW_UNICAST_TEST (7)
|
||||||
|
|
||||||
|
Minimaler Master→Slave-Ping; Slave loggt `UNICAST TEST OK`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 8. OTA
|
||||||
|
|
||||||
|
### 8.1 UART-OTA (nur Master)
|
||||||
|
|
||||||
|
Implementierung: `ota_uart.c`, Steuerung `cmd/cmd_ota.c`.
|
||||||
|
|
||||||
|
| Phase | Host → Master | Master → Host |
|
||||||
|
|-------|---------------|---------------|
|
||||||
|
| Start | `OTA_START` + `total_size` | `OTA_STATUS` preparing → ready |
|
||||||
|
| Daten | `OTA_PAYLOAD` ≤200 B, `seq` | `block_ack` je 4096 B Flash |
|
||||||
|
| Ende | `OTA_END` | success/failed; startet ESP-NOW-Verteilung |
|
||||||
|
|
||||||
|
- Inaktive Partition: `esp_ota_get_next_update_partition()`.
|
||||||
|
- LED-Ring zeigt Fortschritt (blau Schreiben, grün Verteilung).
|
||||||
|
|
||||||
|
### 8.2 ESP-NOW-OTA (Master → Slaves)
|
||||||
|
|
||||||
|
`ota_espnow.c` — gleiche Status-Codes wie UART.
|
||||||
|
|
||||||
|
1. `ESPNOW_OTA_START` + `total_size` → Slave `ota_espnow_slave_on_start`
|
||||||
|
2. `ESPNOW_OTA_PAYLOAD` bis 200 B → 4 KiB Buffer auf Slave
|
||||||
|
3. `ESPNOW_OTA_END` → Boot-Partition setzen
|
||||||
|
4. Slave antwortet `ESPNOW_OTA_STATUS` (mit `send_message_ex` + Semaphore auf Master)
|
||||||
|
|
||||||
|
Nach Erfolg: **alle Knoten neu starten**.
|
||||||
|
|
||||||
|
### 8.3 OTA_SLAVE_PROGRESS (21)
|
||||||
|
|
||||||
|
Abfrage laufender oder letzter ESP-NOW-Verteilung: pro Slave `bytes_written`, `status`, `error`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 9. BMA456 Beschleunigungssensor
|
||||||
|
|
||||||
|
| Thema | Detail |
|
||||||
|
|-------|--------|
|
||||||
|
| Wrapper | `bosch456.c` / `bosch456.h` |
|
||||||
|
| Chip-Variante | BMA456**H** (`bma456h.c` im Component) |
|
||||||
|
| I2C | Adresse 0x18, 100 kHz |
|
||||||
|
| Polling | Task ~10 Hz |
|
||||||
|
| Interrupt GPIO 10 | Single/Double/Triple Tap |
|
||||||
|
| Deadzone | Software-Filter für Logs/Stream (Default 100 LSB) |
|
||||||
|
| API | `bma456_read_accel`, `bma456_set_accel_deadzone`, `bma456_set_tap_handler` |
|
||||||
|
|
||||||
|
Ohne Sensor: `bma456_is_ready() == false`, Firmware läuft weiter.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 10. LED-Ring
|
||||||
|
|
||||||
|
- **95 LEDs**, WS2812 über RMT (`led_ring.c`).
|
||||||
|
- Segment-Karten für Ziffern 0–10 in `digit_lookup[]`.
|
||||||
|
- **Kein** lokaler Demo-Loop — Anzeige nur über UART (`cmd_led_ring.c`) oder ESP-NOW `ESPNOW_LED_RING`.
|
||||||
|
- Helligkeit: `intensity` 0 → Default ~5 % (`LED_RING_DEFAULT_INTENSITY`).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 11. Board-Input und Batterie
|
||||||
|
|
||||||
|
`board_input.c`:
|
||||||
|
|
||||||
|
- **Taster** GPIO 12 — Logging bei Druck.
|
||||||
|
- **LiPo-ADC** GPIO 1 (und optional 2, wenn nicht Taster).
|
||||||
|
- Master: `master_monitor_task` aktualisiert Master-Batterie alle 30 s.
|
||||||
|
- Slave: `slave_send_battery_report_to_master` nach Join, Heartbeat und Query.
|
||||||
|
|
||||||
|
Spannungen in Millivolt in Registry und `BATTERY_STATUS`-UART.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 12. Persistenz (NVS)
|
||||||
|
|
||||||
|
`pod_settings.c` — Namespace `powerpod`:
|
||||||
|
|
||||||
|
| Key | Inhalt |
|
||||||
|
|-----|--------|
|
||||||
|
| `accel_dz` | Accel-Deadzone LSB |
|
||||||
|
|
||||||
|
Gespeichert bei lokalem Set (UART `client_id=0`, ESP-NOW auf Slave). Geladen nach `init_bma456()`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 13. Protobuf und Code-Generierung
|
||||||
|
|
||||||
|
```bash
|
||||||
|
make proto_generate # beide Schemas
|
||||||
|
make proto_generate_uart # nur uart_messages.proto
|
||||||
|
make proto_generate_espnow # nur esp_now_messages.proto
|
||||||
|
```
|
||||||
|
|
||||||
|
**Ausgabe:** `main/proto/*.pb.c`, `*.pb.h`
|
||||||
|
**Optionen:** `main/proto/uart_messages.options` (nanopb max_size etc.)
|
||||||
|
|
||||||
|
Includes in generierten `.pb.c` müssen `"uart_messages.pb.h"` heißen (nicht `main/proto/...`).
|
||||||
|
|
||||||
|
**Paketname protobuf:** `alox` → C-Prefix `alox_`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 14. Modulreferenz
|
||||||
|
|
||||||
|
### 14.1 Kern
|
||||||
|
|
||||||
|
| Datei | Rolle |
|
||||||
|
|-------|--------|
|
||||||
|
| `powerpod.c` | `app_main`, Init, Master-Register |
|
||||||
|
| `app_config.h` | Laufzeit-Konfiguration |
|
||||||
|
| `cmd_handler.c` | Queue + Dispatcher |
|
||||||
|
| `uart.c` | Framing |
|
||||||
|
| `uart_proto.c` | UartMessage send |
|
||||||
|
| `uart_cmd.c` | Handler-Hilfen |
|
||||||
|
|
||||||
|
### 14.2 Kommunikation
|
||||||
|
|
||||||
|
| Datei | Rolle |
|
||||||
|
|-------|--------|
|
||||||
|
| `esp_now_comm.c` | WiFi, ESP-NOW, alle Rollen-Tasks |
|
||||||
|
| `esp_now_proto.c` | nanopb für EspNowMessage |
|
||||||
|
| `client_registry.c` | Slave-Tabelle + Telemetrie-Cache |
|
||||||
|
|
||||||
|
### 14.3 Befehle (`main/cmd/`)
|
||||||
|
|
||||||
|
| Datei | UART-Typ |
|
||||||
|
|-------|----------|
|
||||||
|
| `cmd_version.c` | VERSION |
|
||||||
|
| `cmd_client_info.c` | CLIENT_INFO |
|
||||||
|
| `cmd_accel_deadzone.c` | ACCEL_DEADZONE |
|
||||||
|
| `cmd_accel_stream.c` | ACCEL_STREAM |
|
||||||
|
| `cmd_tap_notify.c` | TAP_NOTIFY |
|
||||||
|
| `cmd_cache_status.c` | CACHE_STATUS |
|
||||||
|
| `cmd_battery.c` | BATTERY_STATUS |
|
||||||
|
| `cmd_led_ring.c` | LED_RING |
|
||||||
|
| `cmd_espnow_find_me.c` | FIND_ME |
|
||||||
|
| `cmd_restart.c` | RESTART |
|
||||||
|
| `cmd_espnow_unicast_test.c` | ESPNOW_UNICAST_TEST |
|
||||||
|
| `cmd_ota.c` | OTA_* |
|
||||||
|
| `cmd_ota_slave_progress.c` | OTA_SLAVE_PROGRESS |
|
||||||
|
|
||||||
|
### 14.4 Domäne
|
||||||
|
|
||||||
|
| Datei | Rolle |
|
||||||
|
|-------|--------|
|
||||||
|
| `bosch456.c` | Accelerometer + Tap |
|
||||||
|
| `led_ring.c` | LED-Anzeige |
|
||||||
|
| `board_input.c` | Taster, ADC |
|
||||||
|
| `pod_settings.c` | NVS |
|
||||||
|
| `pod_reboot.c` | Verzögerter Restart |
|
||||||
|
| `ota_uart.c` | Flash-Puffer UART-OTA |
|
||||||
|
| `ota_espnow.c` | OTA an Slaves |
|
||||||
|
|
||||||
|
### 14.5 Protobuf
|
||||||
|
|
||||||
|
| Datei | Rolle |
|
||||||
|
|-------|--------|
|
||||||
|
| `proto/uart_messages.proto` | UART-Vertrag |
|
||||||
|
| `proto/esp_now_messages.proto` | ESP-NOW-Vertrag |
|
||||||
|
| `proto/pb_encode.c`, `pb_decode.c`, `pb_common.c` | nanopb Runtime |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 15. Logging-Tags
|
||||||
|
|
||||||
|
| Tag | Modul |
|
||||||
|
|-----|--------|
|
||||||
|
| `[Main]` | powerpod.c |
|
||||||
|
| `[UART]` | uart.c |
|
||||||
|
| `[CMDH]` | cmd_handler.c |
|
||||||
|
| `[UART_CMD]` | uart_cmd.c |
|
||||||
|
| `[ESPNOW]` | esp_now_comm.c |
|
||||||
|
| `[CLIENTS]` | client_registry.c |
|
||||||
|
| `[BMA456]` | bosch456.c |
|
||||||
|
| `[VERSION]` etc. | jeweiliger cmd_* |
|
||||||
|
|
||||||
|
Typischer Ablauf bei UART-Befehl:
|
||||||
|
|
||||||
|
```
|
||||||
|
[UART] received message cmd=0x06 len=…
|
||||||
|
[CMDH] trigger command ACCEL_DEADZONE (0x06)
|
||||||
|
[ACCEL_DZ] …
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Anhang: Neues UART-Kommando hinzufügen
|
||||||
|
|
||||||
|
1. `uart_messages.proto` erweitern → `make proto_generate_uart`
|
||||||
|
2. `cmd/cmd_neu.c` mit `handle_*` + `uart_cmd_register()`
|
||||||
|
3. In `powerpod.c` `cmd_neu_register()` aufrufen (nur Master-Zweig)
|
||||||
|
4. Bei Slave-Bedarf: `esp_now_messages.proto` + `esp_now_comm_send_*` + Slave-Zweig in `espnow_recv_cb`
|
||||||
|
|
||||||
|
Siehe [`adding-a-feature.md`](adding-a-feature.md) für ein vollständiges Beispiel (Find Me).
|
||||||
@ -2,6 +2,8 @@
|
|||||||
|
|
||||||
ESP32-S3 firmware for Powerpod nodes. Master and slave devices run the **same binary**; role and ESP-NOW network are selected at boot via DIP switches and an I2C IO expander.
|
ESP32-S3 firmware for Powerpod nodes. Master and slave devices run the **same binary**; role and ESP-NOW network are selected at boot via DIP switches and an I2C IO expander.
|
||||||
|
|
||||||
|
**Architektur & ESP-only Doku (ohne goTool):** [`../docs/ARCHITECTURE.md`](../docs/ARCHITECTURE.md) (Datenflüsse UART/Commands/ESP-NOW), [`../docs/DOCUMENTATION.md`](../docs/DOCUMENTATION.md) (vollständige Referenz).
|
||||||
|
|
||||||
## System overview
|
## System overview
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user