Add LiPo battery monitoring with ESP-NOW cache and dashboard API.

Slaves report pack voltages every 30s; the master caches them for fast
BATTERY_STATUS reads. goTool exposes REST/WebSocket and shows values in
the dashboard, with a nanopb fix so optional lipo submessages encode.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-05-29 20:14:28 +02:00
co-authored by Cursor
parent eb67a46158
commit 3cb0b5bbe9
30 changed files with 1618 additions and 150 deletions
+14
View File
@@ -116,6 +116,7 @@ Schema: `proto/esp_now_messages.proto`. Encode/decode: `esp_now_proto.c`. The ES
| `ESPNOW_FIND_ME` | Master → slave | `EspNowFindMe` (`client_id` filter) — LED locate sequence |
| `ESPNOW_RESTART` | Master → slave | `EspNowRestart` (`client_id` filter) — reboot slave |
| `ESPNOW_ACCEL_SAMPLE` | Slave → master | `EspNowAccelSample` (`slave_id`, `x`, `y`, `z` raw LSB) — ~every 16 ms |
| `ESPNOW_BATTERY_REPORT` | Slave → master | `EspNowBatteryReport` (`client_id`, `lipo1/2` mV) — ~every 30 s; cached in `client_registry` |
| `ESPNOW_OTA_START` | Master → slave (unicast) | `EspNowOtaStart` (`total_size`) |
| `ESPNOW_OTA_PAYLOAD` | Master → slave | `EspNowOtaPayload` (`seq`, up to 200 B `data`) |
| `ESPNOW_OTA_END` | Master → slave | `EspNowOtaEnd` |
@@ -210,6 +211,7 @@ Host and master speak nanopb-encoded `UartMessage` inside UART frames (byte 0 =
| 6 | `ACCEL_DEADZONE` | Implemented (`cmd/cmd_accel_deadzone.c`) — get/set accel filter LSB |
| 7 | `ESPNOW_UNICAST_TEST` | Implemented (`cmd/cmd_espnow_unicast_test.c`) |
| 8 | `LED_RING` | Implemented (`cmd/cmd_led_ring.c`) — ring progress bar (0100 %, RGB, intensity) |
| 26 | `BATTERY_STATUS` | Implemented (`cmd/cmd_battery.c`) — cached LiPo 1/2 per pod from `client_registry` (UART read, no slave round-trip) |
| 16 | `OTA_START` | Implemented (`cmd/cmd_ota.c`) — begin UART OTA on inactive slot |
| 17 | `OTA_PAYLOAD` | Implemented — up to 200 B per frame; device buffers 4 KiB |
| 18 | `OTA_END` | Implemented — flush, `esp_ota_end`, push image to slaves via ESP-NOW, set boot |
@@ -370,6 +372,18 @@ go run . -port /dev/ttyUSB0 restart
go run . -port /dev/ttyUSB0 restart -client 16
```
### BATTERY_STATUS command
Read **cached** LiPo ADC values on the **master** (master local + one entry per registered slave). Slaves push `ESPNOW_BATTERY_REPORT` every **30 s**; the master stores them in `client_registry` (`lipo1/2_valid`, `lipo1/2_mv`, `battery_updated_at`). The master refreshes its own pack on the same interval in `master_monitor_task`.
**Request:** framed `26` + optional `battery_status_request` (`client_id`, `all_clients`).
**Response:** `battery_status_response` with `samples[]` (`client_id`, `lipo1`, `lipo2`, `age_ms`).
```bash
# Host / goTool: all_clients returns master (id 0) + slaves from cache
```
### LED_RING command
Control the 95-LED ring from the host. The firmware **does not** animate digits locally; only UART updates the display.