Reworked Message Parsing and UART Protkol with Tests
This commit is contained in:
parent
b4d9f24f0e
commit
c564fedf65
@ -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 ".")
|
INCLUDE_DIRS ".")
|
||||||
|
|
||||||
# Get the short Git commit hash of the current HEAD.
|
# Get the short Git commit hash of the current HEAD.
|
||||||
|
|||||||
62
main/message_handler.c
Normal file
62
main/message_handler.c
Normal 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
main/message_handler.h
Normal file
38
main/message_handler.h
Normal 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
main/message_parser.c
Normal file
112
main/message_parser.c
Normal 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
main/message_parser.h
Normal file
52
main/message_parser.h
Normal 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
|
||||||
@ -1,19 +1,23 @@
|
|||||||
#include "driver/gpio.h"
|
#include "driver/gpio.h"
|
||||||
#include "driver/uart.h"
|
#include "driver/uart.h"
|
||||||
#include "esp_log.h"
|
#include "esp_log.h"
|
||||||
|
#include "esp_log_buffer.h"
|
||||||
#include "freertos/idf_additions.h"
|
#include "freertos/idf_additions.h"
|
||||||
#include "hal/uart_types.h"
|
#include "hal/uart_types.h"
|
||||||
|
#include "message_handler.h"
|
||||||
|
#include "message_parser.h"
|
||||||
#include "nvs_flash.h"
|
#include "nvs_flash.h"
|
||||||
#include "portmacro.h"
|
#include "portmacro.h"
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "message_parser.h"
|
||||||
#include "uart_handler.h"
|
#include "uart_handler.h"
|
||||||
#include "uart_prot.h"
|
|
||||||
|
|
||||||
static const char *TAG = "ALOX - UART";
|
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,
|
uart_config_t uart_config = {.baud_rate = 115200,
|
||||||
.data_bits = UART_DATA_8_BITS,
|
.data_bits = UART_DATA_8_BITS,
|
||||||
.parity = UART_PARITY_DISABLE,
|
.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_set_pin(MASTER_UART, TXD_PIN, RXD_PIN, UART_PIN_NO_CHANGE,
|
||||||
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);
|
xTaskCreate(uart_read_task, "Read Uart", 4096, NULL, 1, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
void uart_read_task(void *param) {
|
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);
|
uint8_t *data = (uint8_t *)malloc(BUF_SIZE);
|
||||||
int len = 0;
|
int len = 0;
|
||||||
while (1) {
|
while (1) {
|
||||||
@ -39,32 +47,44 @@ void uart_read_task(void *param) {
|
|||||||
uart_read_bytes(MASTER_UART, data, BUF_SIZE, (20 / portTICK_PERIOD_MS));
|
uart_read_bytes(MASTER_UART, data, BUF_SIZE, (20 / portTICK_PERIOD_MS));
|
||||||
if (len > 0) {
|
if (len > 0) {
|
||||||
for (int i = 0; i < len; ++i) {
|
for (int i = 0; i < len; ++i) {
|
||||||
BaseType_t res = xQueueSend(inputQueue, &data[i], 0);
|
parse_byte(&mr, data[i]);
|
||||||
if (res == errQUEUE_FULL) {
|
|
||||||
ESP_LOGW(TAG, "inputQueue full");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void uart_status_task(void *param) {
|
// TODO: Remove this? or handle message sending in any other way reduce
|
||||||
while (1) {
|
// abstraction hell
|
||||||
uart_write_bytes(MASTER_UART, "c1,status,0\n\r", sizeof("c1,status,0\n\r"));
|
void send_message_hook(const uint8_t *buffer, size_t length) {
|
||||||
vTaskDelay(1000 / portTICK_PERIOD_MS);
|
uart_write_bytes(MASTER_UART, buffer, length);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void send_client_info(int clientid, bool isAvailable, TickType_t lastPing) {
|
void HandleMessageReceivedCallback(uint8_t msgid, const uint8_t *payload,
|
||||||
char buf[128];
|
size_t payload_len) {
|
||||||
snprintf(buf, sizeof(buf), "c%d,status,2,%d,%u\r\n", clientid,
|
ESP_LOGI(TAG, "GOT UART MESSAGE MSGID: %02X, Len: %u bytes \nMSG: ", msgid,
|
||||||
isAvailable ? 1 : 0, (unsigned int)lastPing);
|
payload_len, payload);
|
||||||
uart_write_bytes(MASTER_UART, buf, strlen(buf));
|
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.");
|
||||||
}
|
}
|
||||||
|
|
||||||
void esp_send_message_hook(ESPTOPCBaseMessage *msg) {
|
return;
|
||||||
// serialize + send via UART
|
}
|
||||||
uint8_t buffer[128];
|
|
||||||
uart_write_bytes(UART_NUM_1, (const char *)buffer,
|
void HandleMessageFailCallback(uint8_t msgid, const uint8_t *payload,
|
||||||
sizeof(ESPTOPCBaseMessage));
|
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);
|
||||||
|
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,16 +1,30 @@
|
|||||||
#ifndef UART_HANDLER_H
|
#ifndef UART_HANDLER_H
|
||||||
#define 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 MASTER_UART UART_NUM_1
|
||||||
#define TXD_PIN (GPIO_NUM_1)
|
#define TXD_PIN (GPIO_NUM_1)
|
||||||
#define RXD_PIN (GPIO_NUM_2)
|
#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_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
|
#endif
|
||||||
|
|||||||
@ -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
main/uart_prot.h
100
main/uart_prot.h
@ -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
|
|
||||||
@ -1,85 +1,452 @@
|
|||||||
#include "Unity/src/unity.h"
|
#include "Unity/src/unity.h"
|
||||||
#include "message_parser.h"
|
#include "message_parser.h" // Stellt sicher, dass deine Header-Datei message_parser.h korrekt ist
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
#include <string.h> // Für memcpy
|
||||||
|
|
||||||
|
// Globale Variablen für Callback-Überprüfung
|
||||||
|
static uint8_t received_msgid = 0xFF;
|
||||||
|
static uint8_t
|
||||||
|
received_payload[MAX_TOTAL_CONTENT_LENGTH]; // Muss groß genug sein
|
||||||
|
static size_t received_payload_len = 0;
|
||||||
|
static enum ParserError received_error = NoError;
|
||||||
|
static bool message_received_flag = false;
|
||||||
|
static bool message_fail_flag = false;
|
||||||
|
|
||||||
|
// Mock-Implementierungen für die Callbacks
|
||||||
|
void mock_on_message_received(uint8_t msgid, const uint8_t *payload,
|
||||||
|
size_t payload_len) {
|
||||||
|
received_msgid = msgid;
|
||||||
|
received_payload_len = payload_len;
|
||||||
|
// Sicherstellen, dass der Puffer nicht überläuft
|
||||||
|
memcpy(received_payload, payload,
|
||||||
|
(payload_len < MAX_TOTAL_CONTENT_LENGTH) ? payload_len
|
||||||
|
: MAX_TOTAL_CONTENT_LENGTH);
|
||||||
|
message_received_flag = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void mock_on_message_fail(uint8_t msgid, const uint8_t *payload,
|
||||||
|
size_t payload_len, enum ParserError error) {
|
||||||
|
received_msgid = msgid; // Auch bei Fehlern kann die ID relevant sein
|
||||||
|
received_payload_len = payload_len;
|
||||||
|
// Auch hier, um sicherzustellen, dass wir den Zustand des Puffers beim Fehler
|
||||||
|
// sehen können
|
||||||
|
memcpy(received_payload, payload,
|
||||||
|
(payload_len < MAX_TOTAL_CONTENT_LENGTH) ? payload_len
|
||||||
|
: MAX_TOTAL_CONTENT_LENGTH);
|
||||||
|
received_error = error;
|
||||||
|
message_fail_flag = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- UNITY SETUP/TEARDOWN ---
|
||||||
|
void setUp(void) {
|
||||||
|
// Reset der globalen Variablen vor jedem Test
|
||||||
|
received_msgid = 0xFF;
|
||||||
|
received_payload_len = 0;
|
||||||
|
received_error = NoError;
|
||||||
|
message_received_flag = false;
|
||||||
|
message_fail_flag = false;
|
||||||
|
memset(received_payload, 0, MAX_TOTAL_CONTENT_LENGTH);
|
||||||
|
|
||||||
|
// Registrierung der Mock-Callbacks (muss vor den Tests erfolgen)
|
||||||
|
register_message_callback(mock_on_message_received);
|
||||||
|
register_message_fail_callback(mock_on_message_fail);
|
||||||
|
}
|
||||||
|
|
||||||
void setUp(void) {} // optional
|
|
||||||
void tearDown(void) {} // optional
|
void tearDown(void) {} // optional
|
||||||
|
|
||||||
// Gültige Nachricht mit Payload
|
// --- Hilfsfunktion zur Checksummenberechnung (für Tests) ---
|
||||||
void test_valid_message_parses_correctly(void) {
|
// Berechnet die Checksumme für MSGID + Payload + Checksummen-Byte
|
||||||
struct MessageRecieve mr = {
|
// Ergibt 0x00, wenn die gesamte Kette XORiert wird
|
||||||
.state = WaitingForStartByte,
|
uint8_t calculate_test_checksum_final(uint8_t msgid, const uint8_t *payload,
|
||||||
.messageid = 0,
|
size_t payload_len,
|
||||||
.index = 0,
|
uint8_t actual_checksum_byte) {
|
||||||
.checksum = 0,
|
uint8_t cs = msgid;
|
||||||
|
for (size_t i = 0; i < payload_len; ++i) {
|
||||||
|
cs ^= payload[i];
|
||||||
|
}
|
||||||
|
cs ^=
|
||||||
|
actual_checksum_byte; // Das gesendete Checksummen-Byte wird auch XORiert
|
||||||
|
return cs;
|
||||||
|
}
|
||||||
|
// Hilfsfunktion zur Berechnung des *zu sendenden* Checksummen-Bytes
|
||||||
|
// Dies ist der Wert, der im Frame an der Checksummen-Position steht,
|
||||||
|
// sodass die finale XOR-Summe der Nutzdaten (MSGID + Payload + dieses Byte)
|
||||||
|
// 0x00 ergibt.
|
||||||
|
uint8_t calculate_payload_checksum_byte(uint8_t msgid, const uint8_t *payload,
|
||||||
|
size_t payload_len) {
|
||||||
|
uint8_t cs = msgid;
|
||||||
|
for (size_t i = 0; i < payload_len; ++i) {
|
||||||
|
cs ^= payload[i];
|
||||||
|
}
|
||||||
|
return cs; // Dies ist der Wert, der gesendet werden muss, damit die finale
|
||||||
|
// XOR-Summe 0x00 wird
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test 1: Gültige Nachricht mit Payload
|
||||||
|
void test_1_valid_message_parses_correctly(void) {
|
||||||
|
struct MessageReceive mr = InitMessageReceive();
|
||||||
|
|
||||||
|
uint8_t msgid = 0x01;
|
||||||
|
uint8_t payload[] = {0x01, 0x02, 0x03};
|
||||||
|
size_t payload_len = sizeof(payload);
|
||||||
|
|
||||||
|
// Berechne das Checksummen-Byte, das gesendet werden muss
|
||||||
|
uint8_t checksum_byte_to_send =
|
||||||
|
calculate_payload_checksum_byte(msgid, payload, payload_len);
|
||||||
|
|
||||||
|
uint8_t full_message[] = {
|
||||||
|
StartByte, // 0xAA
|
||||||
|
msgid, // 0x01
|
||||||
|
0x01,
|
||||||
|
0x02,
|
||||||
|
0x03, // Payload
|
||||||
|
checksum_byte_to_send, // Das Checksummen-Byte
|
||||||
|
EndByte // 0xCC
|
||||||
};
|
};
|
||||||
|
|
||||||
uint8_t msg[] = {0xAA, 0x01, 0x03, 0x01, 0x02, 0x03, 0x00};
|
for (uint8_t i = 0; i < sizeof(full_message); ++i) {
|
||||||
msg[6] = msg[1] ^ msg[2] ^ msg[3] ^ msg[4] ^ msg[5]; // korrekte Checksumme
|
parse_byte(&mr, full_message[i]);
|
||||||
|
|
||||||
for (uint8_t i = 0; i < sizeof(msg); ++i)
|
|
||||||
parse_byte(&mr, msg[i]);
|
|
||||||
|
|
||||||
TEST_ASSERT_EQUAL_UINT8(1, mr.messageid);
|
|
||||||
TEST_ASSERT_EQUAL_UINT8(3, mr.length);
|
|
||||||
uint8_t expected[] = {1, 2, 3};
|
|
||||||
TEST_ASSERT_EQUAL_UINT8_ARRAY(expected, mr.message, 3);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ungültige Checksumme – Zustand wird zurückgesetzt
|
TEST_ASSERT_TRUE(message_received_flag);
|
||||||
void test_invalid_checksum_resets_state(void) {
|
TEST_ASSERT_FALSE(message_fail_flag);
|
||||||
struct MessageRecieve mr = {
|
TEST_ASSERT_EQUAL_UINT8(msgid, received_msgid);
|
||||||
.state = WaitingForStartByte,
|
TEST_ASSERT_EQUAL_UINT8(payload_len, received_payload_len); // Payload-Länge
|
||||||
.messageid = 0,
|
TEST_ASSERT_EQUAL_UINT8_ARRAY(payload, received_payload, payload_len);
|
||||||
.index = 0,
|
TEST_ASSERT_EQUAL_UINT8(WaitingForStartByte, mr.state);
|
||||||
.checksum = 0,
|
TEST_ASSERT_EQUAL_UINT8(NoError, mr.error);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test 2: Ungültige Checksumme – Fehler gemeldet und Zustand zurückgesetzt
|
||||||
|
void test_2_invalid_checksum_resets_state(void) {
|
||||||
|
struct MessageReceive mr = InitMessageReceive();
|
||||||
|
|
||||||
|
uint8_t msgid = 0x02;
|
||||||
|
uint8_t payload[] = {0x10, 0x20};
|
||||||
|
size_t payload_len = sizeof(payload);
|
||||||
|
uint8_t wrong_checksum_byte = 0x01; // Absichtlich falsche Checksumme
|
||||||
|
|
||||||
|
uint8_t full_message[] = {StartByte,
|
||||||
|
msgid,
|
||||||
|
0x10,
|
||||||
|
0x20,
|
||||||
|
wrong_checksum_byte, // Falsches Checksummen-Byte
|
||||||
|
EndByte};
|
||||||
|
|
||||||
|
for (uint8_t i = 0; i < sizeof(full_message); ++i) {
|
||||||
|
parse_byte(&mr, full_message[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_ASSERT_FALSE(message_received_flag);
|
||||||
|
TEST_ASSERT_TRUE(message_fail_flag);
|
||||||
|
TEST_ASSERT_EQUAL_UINT8(msgid, received_msgid);
|
||||||
|
// received_payload_len sollte die Länge der Daten sein, die bis zum Fehler
|
||||||
|
// empfangen wurden, d.h., Payload-Länge + das falsche Checksummen-Byte.
|
||||||
|
TEST_ASSERT_EQUAL_UINT8(payload_len + 1, received_payload_len);
|
||||||
|
TEST_ASSERT_EQUAL_UINT8(WrongCheckSum, received_error);
|
||||||
|
TEST_ASSERT_EQUAL_UINT8(WaitingForStartByte, mr.state);
|
||||||
|
TEST_ASSERT_EQUAL_UINT8(WrongCheckSum, mr.error);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test 3: Gültige Nachricht ohne Payload (Länge 0)
|
||||||
|
void test_3_zero_length_message(void) {
|
||||||
|
struct MessageReceive mr = InitMessageReceive();
|
||||||
|
|
||||||
|
uint8_t msgid = 0x03;
|
||||||
|
uint8_t payload[] = {}; // Leerer Payload
|
||||||
|
size_t payload_len = sizeof(payload);
|
||||||
|
|
||||||
|
uint8_t checksum_byte_to_send =
|
||||||
|
calculate_payload_checksum_byte(msgid, payload, payload_len);
|
||||||
|
|
||||||
|
uint8_t full_message[] = {
|
||||||
|
StartByte, msgid,
|
||||||
|
checksum_byte_to_send, // Checksummen-Byte (hier gleich MSGID, da Payload
|
||||||
|
// leer)
|
||||||
|
EndByte};
|
||||||
|
|
||||||
|
for (uint8_t i = 0; i < sizeof(full_message); ++i) {
|
||||||
|
parse_byte(&mr, full_message[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_ASSERT_TRUE(message_received_flag);
|
||||||
|
TEST_ASSERT_FALSE(message_fail_flag);
|
||||||
|
TEST_ASSERT_EQUAL_UINT8(msgid, received_msgid);
|
||||||
|
TEST_ASSERT_EQUAL_UINT8(0, received_payload_len); // Payload-Länge ist 0
|
||||||
|
TEST_ASSERT_EQUAL_UINT8(WaitingForStartByte, mr.state);
|
||||||
|
TEST_ASSERT_EQUAL_UINT8(NoError, mr.error);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test 4: Escapete MSGID (0xAA im MSGID-Feld)
|
||||||
|
void test_4_escaped_message_id(void) {
|
||||||
|
struct MessageReceive mr = InitMessageReceive();
|
||||||
|
|
||||||
|
uint8_t msgid = 0xAA; // MSGID ist ein Steuerzeichen, muss escapet werden
|
||||||
|
uint8_t payload[] = {0x42};
|
||||||
|
size_t payload_len = sizeof(payload);
|
||||||
|
|
||||||
|
uint8_t checksum_byte_to_send =
|
||||||
|
calculate_payload_checksum_byte(msgid, payload, payload_len);
|
||||||
|
|
||||||
|
uint8_t full_message[] = {StartByte,
|
||||||
|
EscapeByte,
|
||||||
|
msgid, // Escapete MSGID (0xBB 0xAA)
|
||||||
|
0x42, // Payload
|
||||||
|
checksum_byte_to_send,
|
||||||
|
EndByte};
|
||||||
|
|
||||||
|
for (uint8_t i = 0; i < sizeof(full_message); ++i) {
|
||||||
|
parse_byte(&mr, full_message[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_ASSERT_TRUE(message_received_flag);
|
||||||
|
TEST_ASSERT_FALSE(message_fail_flag);
|
||||||
|
TEST_ASSERT_EQUAL_UINT8(msgid, received_msgid);
|
||||||
|
TEST_ASSERT_EQUAL_UINT8(payload_len, received_payload_len);
|
||||||
|
TEST_ASSERT_EQUAL_UINT8_ARRAY(payload, received_payload, payload_len);
|
||||||
|
TEST_ASSERT_EQUAL_UINT8(WaitingForStartByte, mr.state);
|
||||||
|
TEST_ASSERT_EQUAL_UINT8(NoError, mr.error);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test 5: Escapetes Payload-Byte (z.B. ein StartByte im Payload)
|
||||||
|
void test_5_escaped_payload_byte(void) {
|
||||||
|
struct MessageReceive mr = InitMessageReceive();
|
||||||
|
|
||||||
|
uint8_t msgid = 0x05;
|
||||||
|
uint8_t payload[] = {
|
||||||
|
0x11, StartByte}; // StartByte (0xAA) im Payload, muss escapet werden
|
||||||
|
size_t payload_len = sizeof(payload);
|
||||||
|
|
||||||
|
uint8_t checksum_byte_to_send =
|
||||||
|
calculate_payload_checksum_byte(msgid, payload, payload_len);
|
||||||
|
|
||||||
|
uint8_t full_message[] = {
|
||||||
|
StartByte,
|
||||||
|
msgid,
|
||||||
|
0x11,
|
||||||
|
EscapeByte,
|
||||||
|
StartByte, // Escaptes StartByte (0xBB 0xAA) im Payload
|
||||||
|
checksum_byte_to_send,
|
||||||
|
EndByte};
|
||||||
|
|
||||||
|
for (uint8_t i = 0; i < sizeof(full_message); ++i) {
|
||||||
|
parse_byte(&mr, full_message[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_ASSERT_TRUE(message_received_flag);
|
||||||
|
TEST_ASSERT_FALSE(message_fail_flag);
|
||||||
|
TEST_ASSERT_EQUAL_UINT8(msgid, received_msgid);
|
||||||
|
TEST_ASSERT_EQUAL_UINT8(payload_len, received_payload_len);
|
||||||
|
TEST_ASSERT_EQUAL_UINT8_ARRAY(payload, received_payload, payload_len);
|
||||||
|
TEST_ASSERT_EQUAL_UINT8(WaitingForStartByte, mr.state);
|
||||||
|
TEST_ASSERT_EQUAL_UINT8(NoError, mr.error);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test 6: Escapetes Checksummen-Byte (z.B. ein EndByte als Checksumme)
|
||||||
|
void test_6_escaped_checksum_byte(void) {
|
||||||
|
struct MessageReceive mr = InitMessageReceive();
|
||||||
|
|
||||||
|
uint8_t msgid = 0x01;
|
||||||
|
uint8_t payload[] = {
|
||||||
|
0xCD}; // payload[0] ^ msgid = 0xCD ^ 0x01 = 0xCC (EndByte)
|
||||||
|
size_t payload_len = sizeof(payload);
|
||||||
|
|
||||||
|
uint8_t checksum_byte_to_send = calculate_payload_checksum_byte(
|
||||||
|
msgid, payload, payload_len); // Dies ist 0xCC
|
||||||
|
|
||||||
|
uint8_t full_message[] = {
|
||||||
|
StartByte,
|
||||||
|
msgid,
|
||||||
|
payload[0], // Payload
|
||||||
|
EscapeByte,
|
||||||
|
checksum_byte_to_send, // Escaptes 0xCC als Checksummen-Byte (0xBB 0xCC)
|
||||||
|
EndByte};
|
||||||
|
|
||||||
|
for (uint8_t i = 0; i < sizeof(full_message); ++i) {
|
||||||
|
parse_byte(&mr, full_message[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_ASSERT_TRUE(message_received_flag);
|
||||||
|
TEST_ASSERT_FALSE(message_fail_flag);
|
||||||
|
TEST_ASSERT_EQUAL_UINT8(msgid, received_msgid);
|
||||||
|
TEST_ASSERT_EQUAL_UINT8(payload_len, received_payload_len);
|
||||||
|
TEST_ASSERT_EQUAL_UINT8_ARRAY(payload, received_payload, payload_len);
|
||||||
|
TEST_ASSERT_EQUAL_UINT8(WaitingForStartByte, mr.state);
|
||||||
|
TEST_ASSERT_EQUAL_UINT8(NoError, mr.error);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test 7: Nachricht zu lang (Pufferüberlauf)
|
||||||
|
void test_7_message_too_long(void) {
|
||||||
|
struct MessageReceive mr =
|
||||||
|
InitMessageReceive(); // mr.max_total_content_length wird auf
|
||||||
|
// MAX_TOTAL_CONTENT_LENGTH gesetzt
|
||||||
|
|
||||||
|
uint8_t msgid = 0x07;
|
||||||
|
// Dieser Payload ist absichtlich 1 Byte zu lang für
|
||||||
|
// MAX_MESSAGE_PAYLOAD_LENGTH. D.h., der gesamte Inhalt für mr.message[]
|
||||||
|
// (Payload + Checksumme) ist MAX_TOTAL_CONTENT_LENGTH + 1 Bytes lang. Dadurch
|
||||||
|
// wird der Puffer definitiv überlaufen.
|
||||||
|
uint8_t oversized_data_for_buffer[MAX_TOTAL_CONTENT_LENGTH +
|
||||||
|
1]; // Ein Byte zu viel für mr.message[]
|
||||||
|
for (size_t i = 0; i < sizeof(oversized_data_for_buffer); ++i) {
|
||||||
|
oversized_data_for_buffer[i] = (uint8_t)(i + 1); // Beliebige Daten
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wir brauchen eine korrekte Checksumme, auch wenn die Nachricht zu lang ist,
|
||||||
|
// da der Sender diese theoretisch senden würde.
|
||||||
|
// Die Berechnung erfolgt über die tatsächlich gesendeten Payload-Daten (die
|
||||||
|
// zu lang sind).
|
||||||
|
uint8_t checksum_byte_for_oversized_msg = calculate_payload_checksum_byte(
|
||||||
|
msgid, oversized_data_for_buffer, MAX_MESSAGE_PAYLOAD_LENGTH + 1);
|
||||||
|
|
||||||
|
// Simuliere den Versand der Nachricht Byte für Byte
|
||||||
|
parse_byte(&mr, StartByte); // mr.state = GetMessageType
|
||||||
|
parse_byte(&mr, msgid); // mr.state = InPayload, mr.messageid = 0x07
|
||||||
|
|
||||||
|
// Sende MAX_TOTAL_CONTENT_LENGTH Bytes, die den Puffer `mr.message`
|
||||||
|
// vollständig füllen. Index läuft von 0 bis MAX_TOTAL_CONTENT_LENGTH-1.
|
||||||
|
for (size_t i = 0; i < MAX_TOTAL_CONTENT_LENGTH; ++i) {
|
||||||
|
// Hier schicken wir die ersten MAX_TOTAL_CONTENT_LENGTH Bytes des
|
||||||
|
// übergroßen Payloads (inklusive dem eigentlichen Checksummen-Byte an
|
||||||
|
// Position MAX_MESSAGE_PAYLOAD_LENGTH). Das wird den Puffer anfüllen, aber
|
||||||
|
// noch keinen Overflow melden.
|
||||||
|
parse_byte(&mr, oversized_data_for_buffer[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// An diesem Punkt sollte mr.index = MAX_TOTAL_CONTENT_LENGTH sein.
|
||||||
|
// Der Puffer `mr.message` ist jetzt voll.
|
||||||
|
TEST_ASSERT_EQUAL_UINT8(MAX_TOTAL_CONTENT_LENGTH,
|
||||||
|
mr.index); // Der Puffer ist genau gefüllt.
|
||||||
|
TEST_ASSERT_EQUAL_UINT8(InPayload, mr.state); // Noch im Payload-Zustand.
|
||||||
|
|
||||||
|
// Das nächste Byte (das letzte Byte von oversized_data_for_buffer)
|
||||||
|
// wird den Overflow auslösen, da mr->index dann mr->max_total_content_length
|
||||||
|
// überschreitet.
|
||||||
|
parse_byte(&mr, oversized_data_for_buffer[MAX_TOTAL_CONTENT_LENGTH]);
|
||||||
|
|
||||||
|
TEST_ASSERT_FALSE(message_received_flag);
|
||||||
|
TEST_ASSERT_TRUE(message_fail_flag);
|
||||||
|
TEST_ASSERT_EQUAL_UINT8(msgid, received_msgid); // MSGID ist korrekt gesetzt
|
||||||
|
// received_payload_len sollte die max. Puffergröße sein, bis der Fehler
|
||||||
|
// auftrat
|
||||||
|
TEST_ASSERT_EQUAL_UINT8(MAX_TOTAL_CONTENT_LENGTH, received_payload_len);
|
||||||
|
TEST_ASSERT_EQUAL_UINT8(MessageToLong, received_error);
|
||||||
|
TEST_ASSERT_EQUAL_UINT8(WaitingForStartByte, mr.state);
|
||||||
|
TEST_ASSERT_EQUAL_UINT8(MessageToLong, mr.error);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test 8: Unerwartetes StartByte mitten im Frame
|
||||||
|
void test_8_unexpected_start_byte_in_payload(void) {
|
||||||
|
struct MessageReceive mr = InitMessageReceive();
|
||||||
|
|
||||||
|
uint8_t msgid = 0x08;
|
||||||
|
|
||||||
|
uint8_t full_message[] = {
|
||||||
|
StartByte, msgid,
|
||||||
|
0x10, // Teil des Payloads
|
||||||
|
StartByte, // Unerwartetes StartByte mitten im Payload
|
||||||
|
0x20, // Dies würde danach kommen
|
||||||
|
0x00, // Dummy-Checksumme
|
||||||
|
EndByte // Dummy-EndByte
|
||||||
};
|
};
|
||||||
|
|
||||||
uint8_t msg[] = {
|
parse_byte(&mr, full_message[0]); // StartByte
|
||||||
0xAA, 0x02, 0x02,
|
parse_byte(&mr, full_message[1]); // MSGID
|
||||||
0x10, 0x20, 0x00}; // falsche Checksumme (korr. wäre 0x10 ^ 0x20 ^ 2 ^ 2)
|
parse_byte(&mr, full_message[2]); // Payload 0x10
|
||||||
|
|
||||||
for (uint8_t i = 0; i < sizeof(msg); ++i)
|
// Hier kommt das unerwartete StartByte, sollte den Fehler auslösen und
|
||||||
parse_byte(&mr, msg[i]);
|
// resetten
|
||||||
|
parse_byte(&mr, full_message[3]);
|
||||||
|
|
||||||
|
TEST_ASSERT_FALSE(message_received_flag);
|
||||||
|
TEST_ASSERT_TRUE(message_fail_flag);
|
||||||
|
TEST_ASSERT_EQUAL_UINT8(msgid, received_msgid);
|
||||||
|
// received_payload_len sollte die Länge der Daten sein, die vor dem Fehler
|
||||||
|
// empfangen wurden.
|
||||||
|
TEST_ASSERT_EQUAL_UINT8(1, received_payload_len); // Nur 0x10 empfangen
|
||||||
|
TEST_ASSERT_EQUAL_UINT8_ARRAY(((uint8_t[]){0x10}), received_payload, 1);
|
||||||
|
TEST_ASSERT_EQUAL_UINT8(UnexpectedCommandByte, received_error);
|
||||||
TEST_ASSERT_EQUAL_UINT8(WaitingForStartByte, mr.state);
|
TEST_ASSERT_EQUAL_UINT8(WaitingForStartByte, mr.state);
|
||||||
|
TEST_ASSERT_EQUAL_UINT8(UnexpectedCommandByte, mr.error);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Kein Startbyte – Nachricht ignorieren
|
// Test 9: Unerwartetes EndByte an der Position der MSGID
|
||||||
void test_no_startbyte_ignored(void) {
|
void test_9_unexpected_end_byte_at_msgid(void) {
|
||||||
struct MessageRecieve mr = {
|
struct MessageReceive mr = InitMessageReceive();
|
||||||
.state = WaitingForStartByte, .index = 0, .checksum = 0};
|
|
||||||
|
|
||||||
uint8_t msg[] = {2, 0x10, 0x20, 0x30}; // kein Startbyte
|
uint8_t full_message[] = {StartByte, EndByte, // EndByte anstelle von MSGID
|
||||||
|
0x01, 0x02, 0x03, 0x04, EndByte};
|
||||||
|
|
||||||
for (uint8_t i = 0; i < sizeof(msg); ++i)
|
parse_byte(&mr, full_message[0]); // StartByte
|
||||||
parse_byte(&mr, msg[i]);
|
parse_byte(&mr, full_message[1]); // EndByte anstelle MSGID
|
||||||
|
|
||||||
|
TEST_ASSERT_FALSE(message_received_flag);
|
||||||
|
TEST_ASSERT_TRUE(message_fail_flag);
|
||||||
|
TEST_ASSERT_EQUAL_UINT8(
|
||||||
|
0x00, received_msgid); // MSGID ist noch 0, da keine empfangen
|
||||||
|
TEST_ASSERT_EQUAL_UINT8(0, received_payload_len); // Noch kein Payload
|
||||||
|
TEST_ASSERT_EQUAL_UINT8(UnexpectedCommandByte, received_error);
|
||||||
TEST_ASSERT_EQUAL_UINT8(WaitingForStartByte, mr.state);
|
TEST_ASSERT_EQUAL_UINT8(WaitingForStartByte, mr.state);
|
||||||
TEST_ASSERT_EQUAL_UINT8(0, mr.index);
|
TEST_ASSERT_EQUAL_UINT8(UnexpectedCommandByte, mr.error);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Länge 0 – gültige Nachricht ohne Payload
|
// Test 10: Kein StartByte zu Beginn der Sequenz (Parser sollte ignorieren)
|
||||||
void test_zero_length_message(void) {
|
void test_10_no_startbyte_at_beginning_ignored(void) {
|
||||||
struct MessageRecieve mr = {
|
struct MessageReceive mr = InitMessageReceive();
|
||||||
.state = WaitingForStartByte, .index = 0, .checksum = 0};
|
|
||||||
|
|
||||||
uint8_t msg[] = {0xAA, 3, 0, 0}; // Länge = 0, Checksumme = 3 ^ 0 = 3
|
uint8_t msg[] = {0x01, 0x02, 0x03, 0x04}; // Beginnt nicht mit StartByte
|
||||||
msg[3] = msg[1] ^ msg[2];
|
|
||||||
|
|
||||||
for (uint8_t i = 0; i < sizeof(msg); ++i)
|
for (uint8_t i = 0; i < sizeof(msg); ++i) {
|
||||||
parse_byte(&mr, msg[i]);
|
parse_byte(&mr, msg[i]);
|
||||||
|
}
|
||||||
|
|
||||||
TEST_ASSERT_EQUAL_UINT8(0, mr.index);
|
TEST_ASSERT_FALSE(message_received_flag);
|
||||||
TEST_ASSERT_EQUAL_UINT8(3, mr.messageid);
|
TEST_ASSERT_FALSE(
|
||||||
|
message_fail_flag); // Sollte keinen Fehler melden, nur ignorieren
|
||||||
|
TEST_ASSERT_EQUAL_UINT8(WaitingForStartByte,
|
||||||
|
mr.state); // Sollte im Wartezustand bleiben
|
||||||
|
TEST_ASSERT_EQUAL_UINT8(0, mr.index); // Index sollte 0 bleiben
|
||||||
|
TEST_ASSERT_EQUAL_UINT8(NoError, mr.error); // Kein Fehler gemeldet
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test 11: Ungültige Länge (z.B. EndByte kommt zu früh, ohne Checksumme)
|
||||||
|
// Angenommen, das Protokoll erwartet immer mindestens MSGID + Checksumme.
|
||||||
|
// Ein leeres Payload ist OK (MSGID + Checksumme + EndByte), aber nur MSGID +
|
||||||
|
// EndByte ist Fehler.
|
||||||
|
void test_11_frame_too_short_no_checksum(void) {
|
||||||
|
struct MessageReceive mr = InitMessageReceive();
|
||||||
|
|
||||||
|
uint8_t msgid = 0x09;
|
||||||
|
uint8_t full_message[] = {
|
||||||
|
StartByte, msgid, EndByte // Kein Payload, keine Checksumme
|
||||||
|
};
|
||||||
|
|
||||||
|
for (uint8_t i = 0; i < sizeof(full_message); ++i) {
|
||||||
|
parse_byte(&mr, full_message[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_ASSERT_FALSE(message_received_flag);
|
||||||
|
TEST_ASSERT_TRUE(message_fail_flag);
|
||||||
|
TEST_ASSERT_EQUAL_UINT8(msgid, received_msgid); // MSGID ist bekannt
|
||||||
|
TEST_ASSERT_EQUAL_UINT8(0, received_payload_len); // Payload-Puffer ist leer
|
||||||
|
TEST_ASSERT_EQUAL_UINT8(WrongCheckSum,
|
||||||
|
received_error); // Checksumme kann nicht 0x00 sein
|
||||||
TEST_ASSERT_EQUAL_UINT8(WaitingForStartByte, mr.state);
|
TEST_ASSERT_EQUAL_UINT8(WaitingForStartByte, mr.state);
|
||||||
|
TEST_ASSERT_EQUAL_UINT8(WrongCheckSum, mr.error);
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(void) {
|
int main(void) {
|
||||||
UNITY_BEGIN();
|
UNITY_BEGIN();
|
||||||
RUN_TEST(test_valid_message_parses_correctly);
|
RUN_TEST(test_1_valid_message_parses_correctly);
|
||||||
RUN_TEST(test_invalid_checksum_resets_state);
|
RUN_TEST(test_2_invalid_checksum_resets_state);
|
||||||
RUN_TEST(test_no_startbyte_ignored);
|
RUN_TEST(test_3_zero_length_message);
|
||||||
RUN_TEST(test_zero_length_message);
|
RUN_TEST(test_4_escaped_message_id);
|
||||||
|
RUN_TEST(test_5_escaped_payload_byte);
|
||||||
|
RUN_TEST(test_6_escaped_checksum_byte);
|
||||||
|
RUN_TEST(test_7_message_too_long);
|
||||||
|
RUN_TEST(test_8_unexpected_start_byte_in_payload);
|
||||||
|
RUN_TEST(test_9_unexpected_end_byte_at_msgid);
|
||||||
|
RUN_TEST(test_10_no_startbyte_at_beginning_ignored);
|
||||||
|
RUN_TEST(test_11_frame_too_short_no_checksum);
|
||||||
return UNITY_END();
|
return UNITY_END();
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user