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:
parent
6b7ccb4256
commit
43a85ce697
@ -1,2 +1,26 @@
|
|||||||
idf_component_register(SRCS "powerpod.c" "led_ring.c" "uart.c"
|
execute_process(
|
||||||
INCLUDE_DIRS ".")
|
COMMAND git -C ${CMAKE_CURRENT_LIST_DIR}/.. rev-parse --short=8 HEAD
|
||||||
|
OUTPUT_VARIABLE POWERPOD_GIT_HASH
|
||||||
|
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||||
|
ERROR_QUIET)
|
||||||
|
if(NOT POWERPOD_GIT_HASH)
|
||||||
|
set(POWERPOD_GIT_HASH "unknown")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
idf_component_register(
|
||||||
|
SRCS
|
||||||
|
"powerpod.c"
|
||||||
|
"led_ring.c"
|
||||||
|
"uart.c"
|
||||||
|
"uart_proto.c"
|
||||||
|
"cmd_handler.c"
|
||||||
|
"cmd_version.c"
|
||||||
|
"proto/uart_messages.pb.c"
|
||||||
|
"proto/pb_encode.c"
|
||||||
|
"proto/pb_common.c"
|
||||||
|
INCLUDE_DIRS
|
||||||
|
"."
|
||||||
|
"proto")
|
||||||
|
|
||||||
|
target_compile_definitions(${COMPONENT_LIB}
|
||||||
|
PRIVATE "POWERPOD_GIT_HASH=\"${POWERPOD_GIT_HASH}\"")
|
||||||
|
|||||||
114
main/README.md
Normal file
114
main/README.md
Normal 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 (1–252), 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 |
|
||||||
80
main/cmd_handler.c
Normal file
80
main/cmd_handler.c
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
#include "cmd_handler.h"
|
||||||
|
#include "esp_err.h"
|
||||||
|
#include "esp_log.h"
|
||||||
|
#include "freertos/FreeRTOS.h"
|
||||||
|
#include "freertos/idf_additions.h"
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#define MAX_HANDLERS 32
|
||||||
|
|
||||||
|
static const char *TAG = "[CMDH]";
|
||||||
|
static QueueHandle_t cmd_queue;
|
||||||
|
static msg_binding_t handlers[MAX_HANDLERS];
|
||||||
|
static int handler_count;
|
||||||
|
|
||||||
|
void init_cmdHandler(QueueHandle_t queue) {
|
||||||
|
cmd_queue = queue;
|
||||||
|
xTaskCreate(vCmdDispatcherTask, "cmd_dispatch", 4096, NULL, 5, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t msg_register_handler(uint16_t id, msg_callback_t cb) {
|
||||||
|
if (cb == NULL) {
|
||||||
|
return ESP_ERR_INVALID_ARG;
|
||||||
|
}
|
||||||
|
for (int i = 0; i < handler_count; i++) {
|
||||||
|
if (handlers[i].msg_id == id) {
|
||||||
|
handlers[i].callback = cb;
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (handler_count >= MAX_HANDLERS) {
|
||||||
|
return ESP_ERR_NO_MEM;
|
||||||
|
}
|
||||||
|
handlers[handler_count].msg_id = id;
|
||||||
|
handlers[handler_count].callback = cb;
|
||||||
|
handler_count++;
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t msg_post(uint16_t id, const uint8_t *data, size_t len) {
|
||||||
|
if (cmd_queue == NULL) {
|
||||||
|
return ESP_ERR_INVALID_STATE;
|
||||||
|
}
|
||||||
|
|
||||||
|
generic_msg_t msg = {.msg_id = id, .len = len, .payload = NULL};
|
||||||
|
if (len > 0) {
|
||||||
|
msg.payload = malloc(len);
|
||||||
|
if (msg.payload == NULL) {
|
||||||
|
return ESP_ERR_NO_MEM;
|
||||||
|
}
|
||||||
|
memcpy(msg.payload, data, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (xQueueSend(cmd_queue, &msg, pdMS_TO_TICKS(100)) != pdPASS) {
|
||||||
|
free(msg.payload);
|
||||||
|
return ESP_ERR_TIMEOUT;
|
||||||
|
}
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
void vCmdDispatcherTask(void *param) {
|
||||||
|
generic_msg_t msg;
|
||||||
|
while (1) {
|
||||||
|
if (xQueueReceive(cmd_queue, &msg, portMAX_DELAY) == pdPASS) {
|
||||||
|
bool handled = false;
|
||||||
|
for (int i = 0; i < handler_count; i++) {
|
||||||
|
if (handlers[i].msg_id == msg.msg_id) {
|
||||||
|
handlers[i].callback(msg.payload, msg.len);
|
||||||
|
handled = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!handled) {
|
||||||
|
ESP_LOGW(TAG, "no handler for msg_id 0x%04x (%u bytes)", msg.msg_id,
|
||||||
|
(unsigned)msg.len);
|
||||||
|
}
|
||||||
|
free(msg.payload);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
26
main/cmd_handler.h
Normal file
26
main/cmd_handler.h
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
#ifndef CMD_HANDLER_H
|
||||||
|
#define CMD_HANDLER_H
|
||||||
|
|
||||||
|
#include "esp_err.h"
|
||||||
|
#include "freertos/idf_additions.h"
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint16_t msg_id;
|
||||||
|
uint8_t *payload;
|
||||||
|
size_t len;
|
||||||
|
} generic_msg_t;
|
||||||
|
|
||||||
|
typedef void (*msg_callback_t)(const uint8_t *data, size_t len);
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint16_t msg_id;
|
||||||
|
msg_callback_t callback;
|
||||||
|
} msg_binding_t;
|
||||||
|
|
||||||
|
void init_cmdHandler(QueueHandle_t queue);
|
||||||
|
void vCmdDispatcherTask(void *param);
|
||||||
|
|
||||||
|
esp_err_t msg_register_handler(uint16_t id, msg_callback_t cb);
|
||||||
|
esp_err_t msg_post(uint16_t id, const uint8_t *data, size_t len);
|
||||||
|
|
||||||
|
#endif
|
||||||
58
main/cmd_version.c
Normal file
58
main/cmd_version.c
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
#include "cmd_handler.h"
|
||||||
|
#include "cmd_version.h"
|
||||||
|
#include "esp_log.h"
|
||||||
|
#include "pb_encode.h"
|
||||||
|
#include "uart_messages.pb.h"
|
||||||
|
#include "uart_proto.h"
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#ifndef POWERPOD_FW_VERSION
|
||||||
|
#define POWERPOD_FW_VERSION 1u
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef POWERPOD_GIT_HASH
|
||||||
|
#define POWERPOD_GIT_HASH "unknown"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static const char *TAG = "[VERSION]";
|
||||||
|
|
||||||
|
static bool encode_git_hash(pb_ostream_t *stream, const pb_field_t *field,
|
||||||
|
void *const *arg) {
|
||||||
|
const char *str = (const char *)*arg;
|
||||||
|
if (str == NULL) {
|
||||||
|
str = "";
|
||||||
|
}
|
||||||
|
if (!pb_encode_tag_for_field(stream, field)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return pb_encode_string(stream, (const pb_byte_t *)str, strlen(str));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void handle_version(const uint8_t *data, size_t len) {
|
||||||
|
(void)data;
|
||||||
|
(void)len;
|
||||||
|
|
||||||
|
alox_UartMessage response = alox_UartMessage_init_zero;
|
||||||
|
response.type = alox_MessageType_VERSION;
|
||||||
|
response.which_payload = alox_UartMessage_version_response_tag;
|
||||||
|
response.payload.version_response.version = POWERPOD_FW_VERSION;
|
||||||
|
response.payload.version_response.git_hash.funcs.encode = encode_git_hash;
|
||||||
|
response.payload.version_response.git_hash.arg = (void *)POWERPOD_GIT_HASH;
|
||||||
|
|
||||||
|
esp_err_t err = uart_send_uart_message(&response);
|
||||||
|
if (err != ESP_OK) {
|
||||||
|
ESP_LOGE(TAG, "failed to send version response: %s", esp_err_to_name(err));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ESP_LOGI(TAG, "version=%u git=%s", (unsigned)POWERPOD_FW_VERSION,
|
||||||
|
POWERPOD_GIT_HASH);
|
||||||
|
}
|
||||||
|
|
||||||
|
void cmd_version_register(void) {
|
||||||
|
esp_err_t err =
|
||||||
|
msg_register_handler(alox_MessageType_VERSION, handle_version);
|
||||||
|
if (err != ESP_OK) {
|
||||||
|
ESP_LOGE(TAG, "register failed: %s", esp_err_to_name(err));
|
||||||
|
}
|
||||||
|
}
|
||||||
6
main/cmd_version.h
Normal file
6
main/cmd_version.h
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
#ifndef CMD_VERSION_H
|
||||||
|
#define CMD_VERSION_H
|
||||||
|
|
||||||
|
void cmd_version_register(void);
|
||||||
|
|
||||||
|
#endif
|
||||||
@ -1,4 +1,6 @@
|
|||||||
#include "powerpod.h"
|
#include "powerpod.h"
|
||||||
|
#include "cmd_handler.h"
|
||||||
|
#include "cmd_version.h"
|
||||||
#include "driver/gpio.h"
|
#include "driver/gpio.h"
|
||||||
#include "driver/i2c_master.h"
|
#include "driver/i2c_master.h"
|
||||||
#include "driver/i2c_types.h"
|
#include "driver/i2c_types.h"
|
||||||
@ -7,9 +9,11 @@
|
|||||||
#include "esp_log.h"
|
#include "esp_log.h"
|
||||||
#include "esp_ota_ops.h"
|
#include "esp_ota_ops.h"
|
||||||
#include "freertos/FreeRTOS.h"
|
#include "freertos/FreeRTOS.h"
|
||||||
|
#include "freertos/idf_additions.h"
|
||||||
#include "led_ring.h"
|
#include "led_ring.h"
|
||||||
#include "nvs.h"
|
#include "nvs.h"
|
||||||
#include "nvs_flash.h"
|
#include "nvs_flash.h"
|
||||||
|
#include "uart.h"
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
enum MASTER_STATES {
|
enum MASTER_STATES {
|
||||||
@ -47,6 +51,8 @@ static i2c_master_dev_handle_t io_expander;
|
|||||||
|
|
||||||
static struct app_config_t App_Config;
|
static struct app_config_t App_Config;
|
||||||
|
|
||||||
|
static QueueHandle_t cmd_queue;
|
||||||
|
|
||||||
uint8_t reverse_high_nibble_lut(uint8_t n) {
|
uint8_t reverse_high_nibble_lut(uint8_t n) {
|
||||||
static const uint8_t lookup[] = {0x0, 0x8, 0x4, 0xC, 0x2, 0xA, 0x6, 0xE,
|
static const uint8_t lookup[] = {0x0, 0x8, 0x4, 0xC, 0x2, 0xA, 0x6, 0xE,
|
||||||
0x1, 0x9, 0x5, 0xD, 0x3, 0xB, 0x7, 0xF};
|
0x1, 0x9, 0x5, 0xD, 0x3, 0xB, 0x7, 0xF};
|
||||||
@ -123,15 +129,18 @@ void app_main(void) {
|
|||||||
|
|
||||||
led_ring_init();
|
led_ring_init();
|
||||||
|
|
||||||
|
cmd_queue = xQueueCreate(10, sizeof(generic_msg_t));
|
||||||
|
init_cmdHandler(cmd_queue);
|
||||||
|
init_uart(cmd_queue);
|
||||||
|
cmd_version_register();
|
||||||
|
|
||||||
uint8_t current_digit = 10;
|
uint8_t current_digit = 10;
|
||||||
while (1) {
|
while (1) {
|
||||||
led_command_t cmd = {
|
led_command_t cmd = {.mode = LED_CMD_SET_DIGIT,
|
||||||
.mode = LED_CMD_SET_DIGIT,
|
.value = current_digit,
|
||||||
.value = current_digit,
|
.r = 5,
|
||||||
.r = 5,
|
.g = 5,
|
||||||
.g = 5,
|
.b = 0};
|
||||||
.b = 0
|
|
||||||
};
|
|
||||||
|
|
||||||
led_ring_send_command(&cmd);
|
led_ring_send_command(&cmd);
|
||||||
current_digit = (current_digit + 1) % 11;
|
current_digit = (current_digit + 1) % 11;
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
/* Automatically generated nanopb constant definitions */
|
/* Automatically generated nanopb constant definitions */
|
||||||
/* Generated by nanopb-1.0.0-dev */
|
/* Generated by nanopb-1.0.0-dev */
|
||||||
|
|
||||||
#include "main/proto/uart_messages.pb.h"
|
#include "uart_messages.pb.h"
|
||||||
#if PB_PROTO_HEADER_VERSION != 40
|
#if PB_PROTO_HEADER_VERSION != 40
|
||||||
#error Regenerate this file with the current version of nanopb generator.
|
#error Regenerate this file with the current version of nanopb generator.
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
62
main/uart.c
62
main/uart.c
@ -1,3 +1,4 @@
|
|||||||
|
#include "cmd_handler.h"
|
||||||
#include "driver/uart.h"
|
#include "driver/uart.h"
|
||||||
#include "driver/gpio.h"
|
#include "driver/gpio.h"
|
||||||
#include "esp_log.h"
|
#include "esp_log.h"
|
||||||
@ -8,11 +9,40 @@
|
|||||||
#include "portmacro.h"
|
#include "portmacro.h"
|
||||||
#include "uart.h"
|
#include "uart.h"
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
static const char *TAG = "[UART]";
|
static const char *TAG = "[UART]";
|
||||||
static QueueHandle_t uart_cmd_queue;
|
static QueueHandle_t uart_cmd_queue;
|
||||||
|
|
||||||
|
static bool uart_enqueue_packet(const uart_packet_t *packet) {
|
||||||
|
if (packet->len == 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
generic_msg_t msg = {
|
||||||
|
.msg_id = packet->payload[0],
|
||||||
|
.len = packet->len > 1 ? packet->len - 1 : 0,
|
||||||
|
.payload = NULL,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (msg.len > 0) {
|
||||||
|
msg.payload = malloc(msg.len);
|
||||||
|
if (msg.payload == NULL) {
|
||||||
|
ESP_LOGE(TAG, "failed to allocate command payload");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
memcpy(msg.payload, &packet->payload[1], msg.len);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (xQueueSend(uart_cmd_queue, &msg, 0) != pdPASS) {
|
||||||
|
free(msg.payload);
|
||||||
|
ESP_LOGW(TAG, "command queue full");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
void init_uart(QueueHandle_t cmd_queue) {
|
void init_uart(QueueHandle_t cmd_queue) {
|
||||||
uart_cmd_queue = cmd_queue;
|
uart_cmd_queue = cmd_queue;
|
||||||
uart_config_t uart_config = {// .baud_rate = 115200, // 921600, 115200
|
uart_config_t uart_config = {// .baud_rate = 115200, // 921600, 115200
|
||||||
@ -44,8 +74,9 @@ void uart_read_task(void *param) {
|
|||||||
if (len > 0) {
|
if (len > 0) {
|
||||||
for (int i = 0; i < len; ++i) {
|
for (int i = 0; i < len; ++i) {
|
||||||
if (parse_uart_byte(data[i], &packet)) {
|
if (parse_uart_byte(data[i], &packet)) {
|
||||||
ESP_LOGI("UART", "Paket empfangen! Länge: %d", packet.len);
|
ESP_LOGI(TAG, "packet received, len=%d, cmd=0x%02x", packet.len,
|
||||||
xQueueSend(uart_cmd_queue, &packet, 0);
|
packet.len > 0 ? packet.payload[0] : 0);
|
||||||
|
uart_enqueue_packet(&packet);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
last_byte_time = xTaskGetTickCount();
|
last_byte_time = xTaskGetTickCount();
|
||||||
@ -107,3 +138,30 @@ bool parse_uart_byte(uint8_t byte, uart_packet_t *p) {
|
|||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
esp_err_t uart_send_framed(const uint8_t *payload, size_t len) {
|
||||||
|
if (payload == NULL || len == 0 || len > MAX_PAYLOAD_SIZE) {
|
||||||
|
return ESP_ERR_INVALID_ARG;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t checksum = 0;
|
||||||
|
for (size_t i = 0; i < len; i++) {
|
||||||
|
checksum ^= payload[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t frame[4 + MAX_PAYLOAD_SIZE];
|
||||||
|
size_t pos = 0;
|
||||||
|
frame[pos++] = START_MARKER;
|
||||||
|
frame[pos++] = (uint8_t)len;
|
||||||
|
memcpy(&frame[pos], payload, len);
|
||||||
|
pos += len;
|
||||||
|
frame[pos++] = checksum;
|
||||||
|
frame[pos++] = STOP_MARKER;
|
||||||
|
|
||||||
|
int written =
|
||||||
|
uart_write_bytes(UART_NUM, frame, pos);
|
||||||
|
if (written < 0 || (size_t)written != pos) {
|
||||||
|
return ESP_FAIL;
|
||||||
|
}
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|||||||
@ -1,8 +1,10 @@
|
|||||||
#ifndef UART_H
|
#ifndef UART_H
|
||||||
#define UART_H
|
#define UART_H
|
||||||
|
|
||||||
|
#include "esp_err.h"
|
||||||
#include "freertos/idf_additions.h"
|
#include "freertos/idf_additions.h"
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
|
#include <stddef.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#define UART_NUM UART_NUM_1
|
#define UART_NUM UART_NUM_1
|
||||||
#define UART_BUF_SIZE 256
|
#define UART_BUF_SIZE 256
|
||||||
@ -38,5 +40,6 @@ typedef struct {
|
|||||||
void init_uart(QueueHandle_t cmd_queue);
|
void init_uart(QueueHandle_t cmd_queue);
|
||||||
void uart_read_task(void *param);
|
void uart_read_task(void *param);
|
||||||
bool parse_uart_byte(uint8_t byte, uart_packet_t *p);
|
bool parse_uart_byte(uint8_t byte, uart_packet_t *p);
|
||||||
|
esp_err_t uart_send_framed(const uint8_t *payload, size_t len);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
23
main/uart_proto.c
Normal file
23
main/uart_proto.c
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
#include "uart_proto.h"
|
||||||
|
#include "pb_encode.h"
|
||||||
|
#include "uart.h"
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
esp_err_t uart_send_uart_message(const alox_UartMessage *msg) {
|
||||||
|
uint8_t pb_buf[MAX_PAYLOAD_SIZE];
|
||||||
|
pb_ostream_t stream = pb_ostream_from_buffer(pb_buf, sizeof(pb_buf));
|
||||||
|
|
||||||
|
if (!pb_encode(&stream, alox_UartMessage_fields, msg)) {
|
||||||
|
return ESP_FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t payload[MAX_PAYLOAD_SIZE];
|
||||||
|
if (stream.bytes_written + 1 > sizeof(payload)) {
|
||||||
|
return ESP_ERR_NO_MEM;
|
||||||
|
}
|
||||||
|
|
||||||
|
payload[0] = (uint8_t)msg->type;
|
||||||
|
memcpy(&payload[1], pb_buf, stream.bytes_written);
|
||||||
|
|
||||||
|
return uart_send_framed(payload, stream.bytes_written + 1);
|
||||||
|
}
|
||||||
9
main/uart_proto.h
Normal file
9
main/uart_proto.h
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
#ifndef UART_PROTO_H
|
||||||
|
#define UART_PROTO_H
|
||||||
|
|
||||||
|
#include "esp_err.h"
|
||||||
|
#include "uart_messages.pb.h"
|
||||||
|
|
||||||
|
esp_err_t uart_send_uart_message(const alox_UartMessage *msg);
|
||||||
|
|
||||||
|
#endif
|
||||||
Loading…
x
Reference in New Issue
Block a user