Add message framing (start, end, escape bytes) to UART
Same functionality as previous commit. But way more stable and clean. Previous proof of concept approach had issues with random partial or too large messages due to time based method Rework send and receive functions to work more stable - send: encode data with frame (start, end byte) - receive: read each byte one after the other, assemble message, handle actual data in handle function - add semaphore to write operation to prevent parallel write of different data when called from other tasks
This commit is contained in:
@@ -1,6 +1,9 @@
|
||||
#include "uart_common.hpp"
|
||||
|
||||
static const char * TAG = "uart-common";
|
||||
SemaphoreHandle_t uart_semaphoreSend = NULL;
|
||||
bool uart_isInitialized = false;
|
||||
|
||||
|
||||
|
||||
//===========================
|
||||
@@ -9,6 +12,8 @@ static const char * TAG = "uart-common";
|
||||
//initial struct on given pins
|
||||
//TODO add pins and baud rate to config?
|
||||
void uart_init(void){
|
||||
ESP_LOGW(TAG, "initializing uart1...");
|
||||
uart_semaphoreSend = xSemaphoreCreateBinary();
|
||||
uart_config_t uart1_config = {
|
||||
.baud_rate = 115198,
|
||||
.data_bits = UART_DATA_8_BITS,
|
||||
@@ -22,8 +27,91 @@ void uart_init(void){
|
||||
ESP_ERROR_CHECK(uart_set_pin(UART_NUM_1, 23, 22, 0, 0));
|
||||
ESP_LOGI(TAG, "init...");
|
||||
ESP_ERROR_CHECK(uart_driver_install(UART_NUM_1, 1024, 1024, 10, NULL, 0));
|
||||
uart_isInitialized = true;
|
||||
xSemaphoreGive(uart_semaphoreSend);
|
||||
}
|
||||
|
||||
|
||||
|
||||
//===========================
|
||||
//===== uart_sendBinary ====
|
||||
//===========================
|
||||
//encode byte array to message (start, stop, escape pattern)
|
||||
//and send it via uart
|
||||
//note: use sendStruct template in uart_common.hpp
|
||||
void uart_sendBinary(uint8_t *data, int length) {
|
||||
const uint8_t startPattern = START_PATTERN;
|
||||
const uint8_t endPattern = END_PATTERN;
|
||||
const uint8_t escapeByte = ESCAPE_BYTE;
|
||||
ESP_LOGI(TAG, "encoding and sending bytes len=%d", length);
|
||||
//wait for last message to finish sending
|
||||
if (xSemaphoreTake(uart_semaphoreSend, UART_WRITE_WAIT_TIMEOUT / portTICK_PERIOD_MS) == pdTRUE){
|
||||
//send start byte
|
||||
uart_write_bytes(UART_NUM_1, &startPattern, 1);
|
||||
|
||||
for (int i = 0; i < length; i++) {
|
||||
if (data[i] == START_PATTERN || data[i] == END_PATTERN || data[i] == ESCAPE_BYTE) {
|
||||
//add escape byte if next byte is special pattern by accident
|
||||
uart_write_bytes(UART_NUM_1, &escapeByte, 1);
|
||||
}
|
||||
uart_write_bytes(UART_NUM_1, (const char *)&data[i], 1);
|
||||
}
|
||||
//send end byte
|
||||
uart_write_bytes(UART_NUM_1, &endPattern, 1);
|
||||
vTaskDelay(UART_WRITE_MIN_DELAY / portTICK_PERIOD_MS);
|
||||
xSemaphoreGive(uart_semaphoreSend);
|
||||
} else ESP_LOGE(TAG, "timeout waiting for uart write semaphore! dropping data");
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
//================================
|
||||
//=== uart_processReceivedByte ===
|
||||
//================================
|
||||
//decode received message to byte array (start, stop, escape pattern)
|
||||
//has to be run for each receibed byte
|
||||
//runs provided messageHandler function when complete message received
|
||||
uint8_t receivedData[MAX_MESSAGE_LENGTH];
|
||||
int dataIndex = 0;
|
||||
bool insideMessage = false;
|
||||
bool escaped = false;
|
||||
|
||||
void uart_processReceivedByte(uint8_t data, messageHandleFunc_t messageHandler){
|
||||
ESP_LOGD(TAG, "process byte %x", data);
|
||||
if (escaped) {
|
||||
//this byte is actual data, no end/start byte
|
||||
escaped = false;
|
||||
receivedData[dataIndex++] = data;
|
||||
if (dataIndex >= MAX_MESSAGE_LENGTH) {
|
||||
insideMessage = false;
|
||||
dataIndex = 0;
|
||||
}
|
||||
} else if (data == START_PATTERN) {
|
||||
//start of message
|
||||
insideMessage = true;
|
||||
dataIndex = 0;
|
||||
} else if (insideMessage) {
|
||||
if (data == ESCAPE_BYTE) {
|
||||
ESP_LOGI(TAG, "escape byte received");
|
||||
//escape next byte
|
||||
escaped = true;
|
||||
} else if (data == END_PATTERN) {
|
||||
//end of message
|
||||
insideMessage = false;
|
||||
//call provided function that handles actual messages
|
||||
messageHandler(receivedData, dataIndex);
|
||||
dataIndex = 0;
|
||||
} else if (dataIndex < MAX_MESSAGE_LENGTH - 2) {
|
||||
//normal byte - append byte to data
|
||||
receivedData[dataIndex++] = data;
|
||||
} else {
|
||||
//message too long / buffer exceeded
|
||||
insideMessage = false;
|
||||
dataIndex = 0;
|
||||
ESP_LOGE(TAG, "received message too long! dropped");
|
||||
}
|
||||
} else ESP_LOGE(TAG, "start pattern missing! ignoreing byte");
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -11,12 +11,31 @@ extern "C"
|
||||
#include "esp_log.h"
|
||||
#include "sdkconfig.h"
|
||||
#include <string.h>
|
||||
#include "freertos/semphr.h"
|
||||
|
||||
#include "freertos/queue.h"
|
||||
#include "driver/uart.h"
|
||||
}
|
||||
#include "types.hpp"
|
||||
|
||||
//idea with time gap dropped, replaced with encoding
|
||||
//#define UART_RECEIVE_DURATION 10
|
||||
//receive interval may not miss messages but also may not read multiple messages
|
||||
//has to be significantly smaller than WRITE_MNIN_DELAY but larger than longest message
|
||||
//semaphore for assuring min delay between sent data
|
||||
//extern SemaphoreHandle_t uart_semaphoreSend;
|
||||
|
||||
//==== parameters message frame ====
|
||||
#define START_PATTERN 0xAA
|
||||
#define END_PATTERN 0xBB
|
||||
#define MAX_MESSAGE_LENGTH 1024
|
||||
#define ESCAPE_BYTE 0xCC
|
||||
//min delay after each message (no significant effect)
|
||||
#define UART_WRITE_MIN_DELAY 50
|
||||
#define UART_WRITE_WAIT_TIMEOUT 2000
|
||||
|
||||
extern bool uart_isInitialized;
|
||||
|
||||
//struct for testing uart
|
||||
typedef struct {
|
||||
uint32_t timestamp;
|
||||
@@ -32,23 +51,57 @@ typedef struct {
|
||||
} uartData_motorCommands_t;
|
||||
|
||||
|
||||
//function that handles receieved messages as byte array
|
||||
//-> do something with received data
|
||||
typedef void (*messageHandleFunc_t)(uint8_t *data, int length);
|
||||
|
||||
|
||||
|
||||
//===== uart_init =====
|
||||
//should be run once at startup
|
||||
void uart_init(void);
|
||||
|
||||
|
||||
//===== uart_processReceivedByte =====
|
||||
//decode received message to byte array (start, stop, escape pattern)
|
||||
//has to be run for each receibed byte
|
||||
//runs provided messageHandler function when complete message received
|
||||
void uart_processReceivedByte(uint8_t data, messageHandleFunc_t messageHandler);
|
||||
|
||||
|
||||
//===== uart_sendBinary =====
|
||||
//encode byte array to message (start, stop, escape pattern)
|
||||
//and send it via uart
|
||||
void uart_sendBinary(uint8_t *data, int length);
|
||||
|
||||
|
||||
|
||||
//============================
|
||||
//====== uart_sendStruct =====
|
||||
//============================
|
||||
//send and struct via uart
|
||||
//waits when another struct was sent less than UART_WRITE_MIN_DELAY ago
|
||||
template <typename T> void uart_sendStruct(T dataStruct) {
|
||||
static const char * TAG = "uart-common";
|
||||
//TODO check if initialized?
|
||||
//check if uart is initialized
|
||||
if (!uart_isInitialized){
|
||||
ESP_LOGE(TAG, "uart not initialized! droping data");
|
||||
return;
|
||||
}
|
||||
uint8_t dataSerial[sizeof(T)];
|
||||
//struct to serial bytes
|
||||
memcpy(dataSerial, &dataStruct, sizeof(T));
|
||||
uart_write_bytes(UART_NUM_1, (const char *)dataSerial, sizeof(T));
|
||||
ESP_LOGW(TAG, "sent data struct with len %d", sizeof(T));
|
||||
//ESP_LOGW(TAG, "sent DATA: timestamp=%d, id=%d, value=%.1f", data.timestamp, data.id, data.value);
|
||||
// //wait for min delay after last write DROPPED
|
||||
// if (xSemaphoreTake(uart_semaphoreSend, UART_WRITE_WAIT_TIMEOUT / portTICK_PERIOD_MS) == pdTRUE){
|
||||
// //send bytes
|
||||
// uart_write_bytes(UART_NUM_1, (const char *)dataSerial, sizeof(T));
|
||||
// ESP_LOGW(TAG, "sent data struct with len %d", sizeof(T));
|
||||
// } else ESP_LOGE(TAG, "timeout waiting for uart write semaphore! dropping data");
|
||||
// //wait min delay before next write is allowed
|
||||
// vTaskDelay(UART_WRITE_MIN_DELAY / portTICK_PERIOD_MS);
|
||||
// xSemaphoreGive(uart_semaphoreSend);
|
||||
|
||||
uart_sendBinary(dataSerial, sizeof(T));
|
||||
}
|
||||
|
||||
|
||||
@@ -62,7 +115,7 @@ template <typename T> T serialData2Struct(uint8_t *dataSerial){
|
||||
static const char * TAG = "uart-common";
|
||||
T dataStruct;
|
||||
memcpy(&dataStruct, dataSerial, sizeof(T));
|
||||
ESP_LOGW(TAG, "converted serial data len=%d to struct", sizeof(T));
|
||||
ESP_LOGI(TAG, "converted serial data len=%d to struct", sizeof(T));
|
||||
return dataStruct;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user