Add command queue dispatcher and VERSION UART handler.

Centralize command dispatch over a FreeRTOS queue so UART and future
ESP-NOW transports can register handlers; implement the protobuf VERSION
command with framed nanopb responses including build git hash.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-05-18 21:46:51 +02:00
co-authored by Cursor
parent 6b7ccb4256
commit 43a85ce697
12 changed files with 422 additions and 12 deletions
+114
View File
@@ -0,0 +1,114 @@
# Command handler
Generic command dispatch for Powerpod. Transports (UART today, ESP-NOW later) enqueue messages on a shared FreeRTOS queue; a dispatcher task invokes registered callbacks by message ID.
## Architecture
```
UART / ESP-NOW → generic_msg_t queue → vCmdDispatcherTask → registered handler
```
- **`cmd_handler`** — queue, registration, dispatcher task
- **`uart`** — framed serial input, converts packets to `generic_msg_t`
- **`powerpod.c`** — creates the queue, calls `init_cmdHandler()` then `init_uart()`
Initialize the command handler **before** UART so the dispatcher is running when packets arrive.
```c
cmd_queue = xQueueCreate(10, sizeof(generic_msg_t));
init_cmdHandler(cmd_queue);
init_uart(cmd_queue);
```
## UART frame format
Packets on UART1 (921600 baud, pins TX=2 / RX=3):
| Field | Value |
|-----------|--------------------------------------------|
| Start | `0xAA` |
| Length | 1 byte, payload size (1252), non-zero |
| Payload | `length` bytes |
| Checksum | XOR of all payload bytes |
| Stop | `0xCC` |
**Payload layout for the command handler:**
| Offset | Meaning |
|--------|----------------------------------|
| 0 | Command ID (`msg_id`, uint8/16) |
| 1… | Arguments (passed to handler) |
Example: command `0x01` with arguments `0x02 0x03` → payload `01 02 03`, length = 3.
The dispatcher strips the first byte; handlers receive only the argument bytes.
## API
### `msg_register_handler(uint16_t id, msg_callback_t cb)`
Register a callback for a command ID. Up to 32 handlers. Re-registering the same ID updates the callback.
```c
static void on_ping(const uint8_t *data, size_t len) {
ESP_LOGI("app", "ping, %u bytes", (unsigned)len);
}
msg_register_handler(0x01, on_ping);
```
Callback signature:
```c
typedef void (*msg_callback_t)(const uint8_t *data, size_t len);
```
### `msg_post(uint16_t id, const uint8_t *data, size_t len)`
Enqueue a command from firmware (e.g. ESP-NOW receive path) without UART. Copies `data` into heap memory; the dispatcher frees it after the handler returns.
```c
uint8_t args[] = {0x02, 0x03};
msg_post(0x01, args, sizeof(args));
```
Returns `ESP_OK`, `ESP_ERR_NO_MEM`, `ESP_ERR_TIMEOUT` (queue full), or `ESP_ERR_INVALID_STATE`.
## Adding a new command
1. Pick a command ID (first byte of UART payload).
2. Implement a handler in `powerpod.c` (or a dedicated module).
3. Call `msg_register_handler()` after `init_cmdHandler()`.
4. From a host tool, send a framed UART packet with that ID in byte 0.
## VERSION command (`MessageType.VERSION` = 3)
Implemented in `cmd_version.c`. Request is a UART frame with payload `03` (command byte only).
Response frame payload:
| Byte 0 | Bytes 1… |
|--------|----------|
| `0x03` | nanopb-encoded `UartMessage` with `type = VERSION` and `version_response` set |
`VersionResponse` fields:
- `version``POWERPOD_FW_VERSION` (default `1`, override at compile time)
- `git_hash` — short git hash from build (`POWERPOD_GIT_HASH`, from `git rev-parse`)
Register additional proto commands the same way: handler + `uart_send_uart_message()` for replies.
## ESP-NOW (planned)
Parse incoming ESP-NOW data in the Wi-Fi layer and call `msg_post()` with the same ID + payload layout as UART (ID separate, arguments in `data`). No changes to `cmd_handler` required.
## Files
| File | Role |
|-----------------|-------------------------------------------|
| `cmd_handler.h` | Types and public API |
| `cmd_handler.c` | Queue dispatch, registration, `msg_post` |
| `uart.c` | Framed UART parser → queue |
| `powerpod.c` | Queue creation and init order |
| `cmd_version.c` | VERSION command handler |
| `uart_proto.c` | Encode/send `UartMessage` over UART |