Reworked Message Parsing and UART Protkol with Tests

This commit is contained in:
2025-07-22 14:29:41 +02:00
parent b4d9f24f0e
commit c564fedf65
10 changed files with 744 additions and 276 deletions
+1 -1
View File
@@ -1,4 +1,4 @@
idf_component_register(SRCS "main.c" "uart_handler.c" "communication_handler.c" "uart_prot.c" "client_handler.c" "message_parser.c" "message_builder.c" "message_handler.c"
idf_component_register(SRCS "main.c" "uart_handler.c" "communication_handler.c" "client_handler.c" "message_parser.c" "message_builder.c" "message_handler.c"
INCLUDE_DIRS ".")
# Get the short Git commit hash of the current HEAD.
+62
View File
@@ -0,0 +1,62 @@
#include "message_handler.h"
#include "esp_log.h"
#include "freertos/idf_additions.h"
#include "uart_handler.h"
static struct MessageBroker mr;
static char *TAG = "ALOX - Message Handler";
void InitMessageBroker() {
mr.num_direct_callbacks = 0;
mr.num_task_callbacks = 0;
return;
}
void RegisterCallback(uint8_t msgid, RegisterFunctionCallback callback) {
mr.FunctionList[mr.num_direct_callbacks].MSGID = msgid;
mr.FunctionList[mr.num_direct_callbacks].callback = callback;
mr.num_direct_callbacks++;
return;
}
void RegisterTask(uint8_t msgid, RegisterTaskCallback callback) {
mr.TaskList[mr.num_task_callbacks].MSGID = msgid;
mr.TaskList[mr.num_task_callbacks].task = callback;
mr.num_task_callbacks++;
return;
}
void MessageBrokerTask(void *param) {
ParsedMessage_t received_msg;
QueueHandle_t msg_queue = *(QueueHandle_t *)param;
if (msg_queue == NULL) {
ESP_LOGE(TAG, "Message queue not initialized. Terminating task.");
vTaskDelete(NULL);
}
ESP_LOGI(TAG, "Message broker task started.");
while (1) {
if (xQueueReceive(msg_queue, &received_msg, portMAX_DELAY)) {
ESP_LOGI(TAG, "Received message from queue: MSGID=0x%02X, Length=%u",
received_msg.msgid, received_msg.payload_len);
for (int i = 0; i < mr.num_direct_callbacks; i++) {
if (mr.FunctionList[i].MSGID == received_msg.msgid) {
mr.FunctionList[i].callback(received_msg.msgid, received_msg.data,
received_msg.payload_len);
}
}
for (int i = 0; i < mr.num_direct_callbacks; i++) {
if (mr.FunctionList[i].MSGID == received_msg.msgid) {
// TODO: Not yet implemented
// Only send data to task, task should be created beforhead and wait
// for new data in the queue.
}
}
}
}
}
void SendMessage(const uint8_t *buffer, size_t length);
+38
View File
@@ -0,0 +1,38 @@
#ifndef _MESSAGE_HANDLER_HEADER
#define _MESSAGE_HANDLER_HEADER
#include "freertos/idf_additions.h"
#include <stddef.h>
#include <stdint.h>
typedef void (*RegisterFunctionCallback)(uint8_t msgid, const uint8_t *payload,
size_t payload_len);
typedef void (*RegisterTaskCallback)(uint8_t msgid, const uint8_t *payload,
size_t payload_len);
struct RegisterdFunction {
uint8_t MSGID;
RegisterFunctionCallback callback;
};
struct RegisterdTask {
uint8_t MSGID;
RegisterTaskCallback task;
};
struct MessageBroker {
struct RegisterdFunction FunctionList[64];
uint8_t num_direct_callbacks;
struct RegisterdTask TaskList[64];
uint8_t num_task_callbacks;
};
typedef void (*SendMessageHookCallback)(const uint8_t *buffer, size_t length);
void InitMessageBroker();
void RegisterCallback(uint8_t msgid, RegisterFunctionCallback callback);
void RegisterTask(uint8_t msgid, RegisterTaskCallback callback);
void SendMessage(const uint8_t *buffer, size_t length);
void MessageBrokerTask(void *param);
#endif
+112
View File
@@ -0,0 +1,112 @@
#include "message_parser.h"
#include <stdint.h>
#include <string.h>
MessageReceivedCallback on_message_received = NULL;
MessageFailCallback on_message_fail = NULL;
struct MessageReceive InitMessageReceive() {
struct MessageReceive mr = {
.state = WaitingForStartByte, // Startzustand des Parsers
.error = NoError, // Kein Fehler zu Beginn
.messageid = 0, // MSGID auf Standardwert setzen
// .message Array muss nicht explizit initialisiert werden, da es bei
// jedem Start geleert wird
.index = 0, // Index für das Nachrichten-Array initialisieren
.checksum = 0 // Checksumme initialisieren
};
return mr;
}
// Registrierungsfunktionen für die Callbacks
void register_message_callback(MessageReceivedCallback callback) {
on_message_received = callback;
}
void register_message_fail_callback(MessageFailCallback callback) {
on_message_fail = callback;
}
void parse_byte(struct MessageReceive *mr, uint8_t pbyte) {
switch (mr->state) {
case WaitingForStartByte:
if (pbyte == StartByte) {
mr->index = 0;
mr->checksum = 0;
mr->state = GetMessageType;
}
break;
case EscapedMessageType:
mr->messageid = pbyte;
mr->checksum ^= pbyte;
mr->state = InPayload;
break;
case GetMessageType:
if (pbyte == EscapeByte) {
mr->state = EscapedMessageType;
return;
}
if (pbyte == StartByte || pbyte == EndByte) {
mr->state = WaitingForStartByte;
mr->error = UnexpectedCommandByte;
if (on_message_received) {
on_message_fail(mr->messageid, mr->message, mr->index, mr->error);
}
return;
}
mr->messageid = pbyte;
mr->checksum ^= pbyte;
mr->state = InPayload;
break;
case EscapePayloadByte:
mr->message[mr->index++] = pbyte;
mr->checksum ^= pbyte;
mr->state = InPayload;
break;
case InPayload:
if (pbyte == EscapeByte) {
mr->state = EscapePayloadByte;
return;
}
if (pbyte == StartByte) {
mr->state = WaitingForStartByte;
mr->error = UnexpectedCommandByte;
if (on_message_received) {
on_message_fail(mr->messageid, mr->message, mr->index, mr->error);
}
return;
}
if (pbyte == EndByte) {
if (mr->checksum != 0x00) {
// Checksum failure
// The Checksum gets treated like a normal byte until the end byte
// accours. Therefore the last byte xor'ed to the checksum ist the
// checksum so the checksum must be Zero.
mr->state = WaitingForStartByte;
mr->error = WrongCheckSum;
if (on_message_received) {
on_message_fail(mr->messageid, mr->message, mr->index, mr->error);
}
return;
}
if (on_message_received) {
on_message_received(mr->messageid, mr->message,
mr->index - 1); // remove checksum byte by just
// setting the length of the message
}
mr->state = WaitingForStartByte;
}
if (mr->index < MAX_TOTAL_CONTENT_LENGTH) {
mr->message[mr->index++] = pbyte;
mr->checksum ^= pbyte;
} else {
mr->state = WaitingForStartByte;
mr->error = MessageToLong;
if (on_message_received) {
on_message_fail(mr->messageid, mr->message, mr->index, mr->error);
}
return;
}
break;
}
}
+52
View File
@@ -0,0 +1,52 @@
#ifndef _MESSAGE_PARSER_HEADER
#define _MESSAGE_PARSER_HEADER
#include <stddef.h>
#include <stdint.h>
#define MAX_MESSAGE_PAYLOAD_LENGTH 128
#define MAX_TOTAL_CONTENT_LENGTH (MAX_MESSAGE_PAYLOAD_LENGTH + 1)
enum ParserState {
WaitingForStartByte,
GetMessageType,
EscapedMessageType,
EscapePayloadByte,
InPayload,
};
enum ParserError {
NoError,
WrongCheckSum,
MessageToLong,
UnexpectedCommandByte,
};
typedef enum {
StartByte = 0xAA,
EscapeByte = 0xBB,
EndByte = 0xCC,
} MessageBytes;
struct MessageReceive {
enum ParserState state;
enum ParserError error;
uint8_t messageid;
uint8_t message[MAX_MESSAGE_PAYLOAD_LENGTH];
uint8_t index;
uint8_t checksum;
};
typedef void (*MessageReceivedCallback)(uint8_t msgid, const uint8_t *payload,
size_t payload_len);
typedef void (*MessageFailCallback)(uint8_t msgid, const uint8_t *payload,
size_t payload_len, enum ParserError error);
struct MessageReceive InitMessageReceive();
void register_message_callback(MessageReceivedCallback callback);
void register_message_fail_callback(MessageFailCallback callback);
void parse_byte(struct MessageReceive *mr, uint8_t pbyte);
#endif
+43 -23
View File
@@ -1,19 +1,23 @@
#include "driver/gpio.h"
#include "driver/uart.h"
#include "esp_log.h"
#include "esp_log_buffer.h"
#include "freertos/idf_additions.h"
#include "hal/uart_types.h"
#include "message_handler.h"
#include "message_parser.h"
#include "nvs_flash.h"
#include "portmacro.h"
#include <stdbool.h>
#include <string.h>
#include "message_parser.h"
#include "uart_handler.h"
#include "uart_prot.h"
static const char *TAG = "ALOX - UART";
static QueueHandle_t parsed_message_queue;
void init_uart() {
void init_uart(QueueHandle_t msg_queue_handle) {
uart_config_t uart_config = {.baud_rate = 115200,
.data_bits = UART_DATA_8_BITS,
.parity = UART_PARITY_DISABLE,
@@ -25,12 +29,16 @@ void init_uart() {
uart_set_pin(MASTER_UART, TXD_PIN, RXD_PIN, UART_PIN_NO_CHANGE,
UART_PIN_NO_CHANGE);
message_handler_init();
parsed_message_queue = msg_queue_handle;
register_message_callback(HandleMessageReceivedCallback);
register_message_fail_callback(HandleMessageFailCallback);
xTaskCreate(uart_read_task, "Read Uart", 4096, NULL, 1, NULL);
}
void uart_read_task(void *param) {
QueueHandle_t inputQueue = message_handler_get_input_queue();
// Send all Input from Uart to the Message Handler for Parsing
struct MessageReceive mr = InitMessageReceive();
uint8_t *data = (uint8_t *)malloc(BUF_SIZE);
int len = 0;
while (1) {
@@ -39,32 +47,44 @@ void uart_read_task(void *param) {
uart_read_bytes(MASTER_UART, data, BUF_SIZE, (20 / portTICK_PERIOD_MS));
if (len > 0) {
for (int i = 0; i < len; ++i) {
BaseType_t res = xQueueSend(inputQueue, &data[i], 0);
if (res == errQUEUE_FULL) {
ESP_LOGW(TAG, "inputQueue full");
}
parse_byte(&mr, data[i]);
}
}
}
}
void uart_status_task(void *param) {
while (1) {
uart_write_bytes(MASTER_UART, "c1,status,0\n\r", sizeof("c1,status,0\n\r"));
vTaskDelay(1000 / portTICK_PERIOD_MS);
// TODO: Remove this? or handle message sending in any other way reduce
// abstraction hell
void send_message_hook(const uint8_t *buffer, size_t length) {
uart_write_bytes(MASTER_UART, buffer, length);
}
void HandleMessageReceivedCallback(uint8_t msgid, const uint8_t *payload,
size_t payload_len) {
ESP_LOGI(TAG, "GOT UART MESSAGE MSGID: %02X, Len: %u bytes \nMSG: ", msgid,
payload_len, payload);
ESP_LOG_BUFFER_HEX(TAG, payload, payload_len);
ParsedMessage_t msg_to_send;
msg_to_send.msgid = msgid;
msg_to_send.payload_len = payload_len;
memcpy(msg_to_send.data, payload, payload_len);
if (xQueueSend(parsed_message_queue, &msg_to_send, portMAX_DELAY) != pdPASS) {
// Fehlerbehandlung: Queue voll oder Senden fehlgeschlagen
ESP_LOGE(TAG, "Failed to send parsed message to queue.");
}
return;
}
void send_client_info(int clientid, bool isAvailable, TickType_t lastPing) {
char buf[128];
snprintf(buf, sizeof(buf), "c%d,status,2,%d,%u\r\n", clientid,
isAvailable ? 1 : 0, (unsigned int)lastPing);
uart_write_bytes(MASTER_UART, buf, strlen(buf));
}
void HandleMessageFailCallback(uint8_t msgid, const uint8_t *payload,
size_t payload_len, enum ParserError error) {
ESP_LOGE(
TAG,
"UART MESSAGE Parsing Failed MSGID: %02X, Len: %u, ERROR: %X, \nMSG: ",
msgid, payload_len, error);
ESP_LOG_BUFFER_HEX(TAG, payload, payload_len);
void esp_send_message_hook(ESPTOPCBaseMessage *msg) {
// serialize + send via UART
uint8_t buffer[128];
uart_write_bytes(UART_NUM_1, (const char *)buffer,
sizeof(ESPTOPCBaseMessage));
return;
}
+18 -4
View File
@@ -1,16 +1,30 @@
#ifndef UART_HANDLER_H
#define UART_HANDLER_H
#include "freertos/idf_additions.h"
#include "message_parser.h"
#include <stddef.h>
#include <stdint.h>
#define MASTER_UART UART_NUM_1
#define TXD_PIN (GPIO_NUM_1)
#define RXD_PIN (GPIO_NUM_2)
#define BUF_SIZE (1024)
#define BUF_SIZE (256)
void init_uart();
typedef struct {
uint8_t msgid;
size_t payload_len;
uint8_t data[MAX_MESSAGE_PAYLOAD_LENGTH];
} ParsedMessage_t;
void init_uart(QueueHandle_t msg_queue_handle);
void uart_read_task(void *param);
void uart_status_task(void *param);
void uart_send_task(void *param);
void send_client_info(int clientid, bool isAvailable, TickType_t lastPing);
void HandleMessageReceivedCallback(uint8_t msgid, const uint8_t *payload,
size_t payload_len);
void HandleMessageFailCallback(uint8_t msgid, const uint8_t *payload,
size_t payload_len, enum ParserError error);
#endif
-97
View File
@@ -1,97 +0,0 @@
#include "uart_prot.h"
#include <string.h>
#define MSG_QUEUE_LEN 64
static QueueHandle_t input_queue;
static QueueHandle_t output_queue;
QueueHandle_t message_handler_get_input_queue(void) {
return input_queue;
}
QueueHandle_t message_handler_get_output_queue(void) {
return output_queue;
}
void message_handler_init(void) {
input_queue = xQueueCreate(MSG_QUEUE_LEN, sizeof(uint8_t));
output_queue = xQueueCreate(MSG_QUEUE_LEN, sizeof(uint8_t));
}
void message_handler_task(void *param) {
uint8_t byte;
while (1) {
if (xQueueReceive(input_queue, &byte, portMAX_DELAY)) {
// handle byte, check message recieve with start and stop byte length and crc
}
}
}
// Message Dispatcher
void dispatch_message(uint8_t msg_id, void *payload) {
switch (msg_id) {
case RequestPing:
if (on_request_ping)
on_request_ping((RequestPingPayload *)payload);
break;
case RequestStatus:
if (on_request_status)
on_request_status((RequestStatusPayload *)payload);
break;
case PrepareFirmwareUpdate:
if (on_prepare_firmware_update)
on_prepare_firmware_update((PrepareFirmwareUpdatePayload *)payload);
break;
case FirmwareUpdateLine:
if (on_firmware_update_line)
on_firmware_update_line((FirmwareUpdateLinePayload *)payload);
break;
default:
// Unknown message
break;
}
}
// Generic Send Function
void send_message(ESP_TO_PC_MESSAGE_IDS msgid, PayloadUnion *payload) {
ESPTOPCBaseMessage mes;
mes.Version = 1;
mes.MessageID = msgid;
mes.Payload = *payload;
esp_send_message_hook(&mes);
}
// Sepzific Send Functions
void send_clients(uint8_t clientCount, uint32_t clientAvaiableBitMask) {
ClientsPayload payload;
// Payload-Daten zuweisen
payload.clientCount = clientCount;
payload.clientAvaiableBitMask = clientAvaiableBitMask;
// Nachricht senden
send_message(Clients, (PayloadUnion *)&payload);
}
void send_status(uint8_t clientId, uint8_t *mac) {
StatusPayload payload;
// Payload-Daten zuweisen
payload.clientId = clientId;
memcpy(payload.mac, mac, 6);
// Nachricht senden
send_message(Status, (PayloadUnion *)&payload);
}
void send_pong(uint8_t clientId, uint32_t ping) {
PongPayload payload;
// Payload-Daten zuweisen
payload.clientId = clientId;
payload.ping = ping;
// Nachricht senden
send_message(Pong, (PayloadUnion *)&payload);
}
-100
View File
@@ -1,100 +0,0 @@
#ifndef _PROTO_HEADER
#define _PROTO_HEADER
#include "freertos/idf_additions.h"
#include <stdint.h>
void message_handler_init(void);
QueueHandle_t message_handler_get_input_queue(void);
QueueHandle_t message_handler_get_output_queue(void);
// MessageIDs
typedef enum {
RequestPing = 0xE1,
RequestStatus = 0xE2,
PrepareFirmwareUpdate = 0xF1,
FirmwareUpdateLine = 0xF2,
} PC_TO_ESP_MESSAGE_IDS;
typedef enum {
Clients = 0xE1,
Status = 0xE2,
Pong = 0xD1,
} ESP_TO_PC_MESSAGE_IDS;
// Payloads for single Messages
typedef struct {
uint8_t clientId;
} RequestPingPayload;
typedef struct {
uint8_t clientId;
} RequestStatusPayload;
typedef struct {
// empty payload
} PrepareFirmwareUpdatePayload;
typedef struct {
uint8_t data[240];
} FirmwareUpdateLinePayload;
typedef struct {
uint8_t clientCount;
uint32_t clientAvaiableBitMask;
} ClientsPayload;
typedef struct {
uint8_t clientId;
uint8_t mac[6];
} StatusPayload;
typedef struct {
uint8_t clientId;
uint32_t ping;
} PongPayload;
// Union for all the Payloads
typedef union {
RequestPingPayload request_ping;
RequestStatusPayload request_status;
PrepareFirmwareUpdatePayload prepare_firmware_update;
FirmwareUpdateLinePayload firmware_update_line;
ClientsPayload clients;
StatusPayload status;
PongPayload pong;
} PayloadUnion;
// Base Message that can hold all Payloads
typedef struct {
uint8_t Version;
PC_TO_ESP_MESSAGE_IDS MessageID;
uint8_t Length;
PayloadUnion Payload;
} PCTOESPBaseMessage;
typedef struct {
uint8_t Version;
ESP_TO_PC_MESSAGE_IDS MessageID;
uint8_t Length;
PayloadUnion Payload;
} ESPTOPCBaseMessage;
// deklarierte Hook-Signatur
void esp_send_message_hook(ESPTOPCBaseMessage *msg);
// Generic Send Function Prototype
void send_message(ESP_TO_PC_MESSAGE_IDS msgid, PayloadUnion *payload);
// Spezific Send Functions Prototype
void send_clients(uint8_t clientCount, uint32_t clientAvaiableBitMask);
void send_status(uint8_t clientId, uint8_t *mac);
void send_pong(uint8_t clientId, uint32_t ping);
// Prototypes for Message Recieve Handler to be set in user code
void (*on_request_ping)(RequestPingPayload *);
void (*on_request_status)(RequestStatusPayload *);
void (*on_prepare_firmware_update)(PrepareFirmwareUpdatePayload *);
void (*on_firmware_update_line)(FirmwareUpdateLinePayload *);
#endif