Need advice to improve UART command parser logic
Posted: Fri May 26, 2023 10:41 am
Hey. Every time I work on a project the first thing I do is write a simple debug UART parser. This allows me to send/receive commands via any Serial terminal software such as termite,mobaxterm and etc. This is very helpful for testing different things on your device.
I have been using the following approach for a long time:
In my UART.c component:
And in my main.c I simply create a task for UART0:
The method for parsing UART0 command shown above will work just fine, however, I dont think its very convenient.
I have come up with a little different approach:
In my UART.h:
The CMD macro allows me to pass only 1 parameter that contains command and length. This is simply more clean. Alternative would be to pass command and length of command in 2 seperate variables.
In my UART.c:
As you can see, in this method, I have a structure of commands that contains command, length and function pointer that must be executed if a known command is received.
This example seems to be working fine, however, there is one problem that I cannot wrap my head arround:
If a function that needs to be executed does not contain any parameters such as test_function1, test_function2 and test_function3, everything will be okay. However, I might want to use some functions that contain one or more parameters, for example:
or
How can I modify my UART parser to accept any function pointer regardless of how many parameters it contains?
I have been using the following approach for a long time:
In my UART.c component:
Code: Select all
void UART0_setup() {
uart_config_t uart_config = {
.baud_rate = 115200,
.data_bits = UART_DATA_8_BITS,
.parity = UART_PARITY_DISABLE,
.stop_bits = UART_STOP_BITS_1,
.flow_ctrl = UART_HW_FLOWCTRL_DISABLE,
.source_clk = UART_SCLK_DEFAULT,
};
ESP_ERROR_CHECK(uart_param_config(UART_NUM_0, &uart_config));
ESP_ERROR_CHECK(uart_driver_install(UART_NUM_0, UART0_COMMAND_LINE_MAX_SIZE, 0, 0, NULL, 0));
}
void UART0_task(void *argument)
{
UART0_setup();
unsigned int char_received=EOF;
unsigned int char_counter=0;
char command_line[UART0_COMMAND_LINE_MAX_SIZE];
for (;;)
{
int len = uart_read_bytes(UART_NUM_0, command_line, (UART0_COMMAND_LINE_MAX_SIZE - 1), 20 / portTICK_PERIOD_MS);
if (len) {
command_line[len] = 0;
ParseSystemCmd(command_line, len); // Line is complete. Execute it!
memset(&command_line, 0, sizeof(command_line));
}
vTaskDelay(10/portTICK_PERIOD_MS);
}
}
bool ParseSystemCmd(char *line, uint16_t cmd_size)
{
if (!strncmp("ping", line,4))
{
printf("pong\n");
return true;
}
if (!strncmp("command1", line,8))
{
printf("command 1 response\n");
return true;
}
return 0;
}
Code: Select all
xTaskCreate(UART0_task,"UART0_task",10000,NULL,5,NULL); // receiving commands from main uart
The method for parsing UART0 command shown above will work just fine, however, I dont think its very convenient.
I have come up with a little different approach:
In my UART.h:
Code: Select all
#define CMD(_cmd) _cmd, sizeof(_cmd) - 1
In my UART.c:
Code: Select all
struct commandsTableDesc_s{
char* command;
uint16_t length;
void (*function_pointer)();
};
struct commandsTableDesc_s CMD_table[3] = {
{ CMD("command1"),&test_function1},
{ CMD("command2"),&test_function2},
{ CMD("command3"),&test_function3},
};
void test_function1(){
printf("command1 response \n");
}
void test_function2(){
printf("command2 response \n");
}
void test_function3(){
printf("command3 response \n");
}
void UART0_setup() {
uart_config_t uart_config = {
.baud_rate = 115200,
.data_bits = UART_DATA_8_BITS,
.parity = UART_PARITY_DISABLE,
.stop_bits = UART_STOP_BITS_1,
.flow_ctrl = UART_HW_FLOWCTRL_DISABLE,
.source_clk = UART_SCLK_DEFAULT,
};
ESP_ERROR_CHECK(uart_param_config(UART_NUM_0, &uart_config));
ESP_ERROR_CHECK(uart_driver_install(UART_NUM_0, UART0_COMMAND_LINE_MAX_SIZE, 0, 0, NULL, 0));
}
void UART0_task(void *argument)
{
UART0_setup();
char command_line[UART0_COMMAND_LINE_MAX_SIZE];
for (;;)
{
int len = uart_read_bytes(UART_NUM_0, command_line, (UART0_COMMAND_LINE_MAX_SIZE - 1), 20 / portTICK_PERIOD_MS);
if (len) {
command_line[len] = 0;
for(uint8_t i=0; i < (sizeof(CMD_table)/sizeof(CMD_table[0])) ; i++) {
if(strncmp(command_line, CMD_table[i].command, CMD_table[i].length) == 0) {
printf("valid command detected = %s \n",command_line);
execute_command(CMD_table[i].function_pointer);
}
}
}
vTaskDelay(10/portTICK_PERIOD_MS);
}
}
This example seems to be working fine, however, there is one problem that I cannot wrap my head arround:
If a function that needs to be executed does not contain any parameters such as test_function1, test_function2 and test_function3, everything will be okay. However, I might want to use some functions that contain one or more parameters, for example:
Code: Select all
void control_motor(uint8_t speed, uint8_t time);
Code: Select all
void random_function(bool parameter1, bool parameter2, bool parameter3);