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:
jonny_jr9
2023-08-30 17:58:01 +02:00
parent 1e544613ee
commit 446c246f43
8 changed files with 197 additions and 55 deletions

View File

@@ -26,7 +26,7 @@ extern "C"
//=========================
//only run uart test code at the end
//disables other functionality
#define UART_TEST_ONLY
//#define UART_TEST_ONLY
//tag for logging
@@ -35,7 +35,6 @@ static const char * TAG = "main";
#ifndef UART_TEST_ONLY
#include "control.hpp"
//====================================
//========== motorctl task ===========
//====================================

View File

@@ -1,49 +1,58 @@
#include "uart.hpp"
#include "config.hpp"
#include "types.hpp"
#include "uart_common.hpp"
//===== uart board MOTORCTL =====
static const char * TAG = "uart";
//handle received payload from uart
void handleMessage(uint8_t *receivedData, int len) {
ESP_LOGI(TAG, "complete message received len=%d", len);
//local variables
uartData_test_t dataTest;
motorCommands_t dataMotorCommands;
//assign data to struct
switch (len){
case sizeof(uartData_test_t):
dataTest = serialData2Struct<uartData_test_t>(receivedData);
ESP_LOGW(TAG, "received uartDataStruct len=%d DATA: timestamp=%d, id=%d, value=%.1f", len, dataTest.timestamp, dataTest.id, dataTest.value);
break;
case sizeof(motorCommands_t):
dataMotorCommands = serialData2Struct<motorCommands_t>(receivedData);
ESP_LOGI(TAG, "received motorCommands struct len=%d left=%.2f%% right=%.2f%%, update target...", len, dataMotorCommands.left.duty, dataMotorCommands.right.duty);
//update target motor state and duty
motorLeft.setTarget(dataMotorCommands.left.state,
dataMotorCommands.left.duty);
motorRight.setTarget(dataMotorCommands.right.state,
dataMotorCommands.right.duty);
break;
//TODO add other received structs here
default:
ESP_LOGE(TAG, "received data len=%d cant be associated with configures struct", len);
break;
}
}
//==============================
//====== task_uartReceive ======
//==============================
//TODO duplicate code, same task in both boards, only handleMessage function has to be passed -> move to uart_common
void task_uartReceive(void *arg){
ESP_LOGW(TAG, "receive task started");
//receive data from uart, detect associated struct and copy/handle the data
//TODO use queue instead of check interval?
uartData_test_t dataTest;
motorCommands_t dataMotorCommands;
uint8_t receivedData[1024-1];
while(1){
//note: check has to be more frequent than pause time between sending
vTaskDelay(50 / portTICK_PERIOD_MS);
//read bytes (max 1023) until 20ms pause is happening
int len = uart_read_bytes(UART_NUM_1, receivedData, sizeof(receivedData), 20 / portTICK_PERIOD_MS);
uart_flush_input(UART_NUM_1);
if (len < 1) continue;
switch (len){
case sizeof(uartData_test_t):
dataTest = serialData2Struct<uartData_test_t>(receivedData);
ESP_LOGW(TAG, "received uartDataStruct len=%d DATA: timestamp=%d, id=%d, value=%.1f", len, dataTest.timestamp, dataTest.id, dataTest.value);
break;
case sizeof(motorCommands_t):
dataMotorCommands = serialData2Struct<motorCommands_t>(receivedData);
ESP_LOGI(TAG, "received motorCommands struct len=%d left=%.2f%% right=%.2f%%, update target...", len, dataMotorCommands.left.duty, dataMotorCommands.right.duty);
//update target motor state and duty
motorLeft.setTarget(dataMotorCommands.left.state,
dataMotorCommands.left.duty);
motorRight.setTarget(dataMotorCommands.right.state,
dataMotorCommands.right.duty);
break;
//TODO add other received structs here
default:
ESP_LOGE(TAG, "received data len=%d cant be associated with configures struct", len);
break;
while (1) {
uint8_t byte;
//read 1 byte TODO: use uart queue here? data might get lost when below function takes longer than data arrives
int len = uart_read_bytes(UART_NUM_1, &byte, 1, portMAX_DELAY);
if (len > 0) {
//process received byte
uart_processReceivedByte(byte, handleMessage);
}
}
}

View File

@@ -1,7 +1,6 @@
#pragma once
#include "uart_common.hpp"
//===== uart board MOTORCTL =====
void task_uartReceive(void *arg);