Fix uart.on('data') on console

With the switch to use the IDF's stdin for feeding the Lua VM, we
unintentionally lost the ability to use uart.on('data') on the console uart.
This is since we no longer install the nodemcu uart driver on said uart.
In order to resolve this shortcoming, this commit refactors the uart.on('data')
delimiter handling and moves it away from platform.c into uart.c where it
really belongs. A new function, uart_feed_data(), is introduced, which is used
both by the nodemcu uart driver task as well as the nodemcu console driver
task (assuming the console is in fact a uart).

The linebuffer allocation/freeing is still in response to
uart.start()/uart.stop(), but it is now in uart.c rather than
platform.c.

The whole uart integration is still too tightly coupled between the platform
component and the module component's uart.c, but this makes it slightly
better at least.
This commit is contained in:
Jade Mattsson 2024-05-12 19:49:33 +10:00 committed by Jade Mattsson
parent 6036c8a6ed
commit 53da95b5ae
4 changed files with 116 additions and 79 deletions

View File

@ -148,11 +148,29 @@ static void nodemcu_init(void)
}
static bool have_console_on_data_cb(void)
{
#if CONFIG_ESP_CONSOLE_UART_DEFAULT || CONFIG_ESP_CONSOLE_UART_CUSTOM
return uart_has_on_data_cb(CONFIG_ESP_CONSOLE_UART_NUM);
#else
return false;
#endif
}
static void console_nodemcu_task(task_param_t param, task_prio_t prio)
{
(void)prio;
char c = (char)param;
feed_lua_input(&c, 1);
if (run_input)
feed_lua_input(&c, 1);
#if CONFIG_ESP_CONSOLE_UART_DEFAULT || CONFIG_ESP_CONSOLE_UART_CUSTOM
if (have_console_on_data_cb())
uart_feed_data(CONFIG_ESP_CONSOLE_UART_NUM, &c, 1);
#endif
// The IDF doesn't seem to honor setvbuf(stdout, NULL, _IONBF, 0) :(
fsync(fileno(stdout));
}
@ -168,7 +186,7 @@ static void console_task(void *)
*/
char c;
ssize_t n = read(fileno(stdin), &c, 1);
if (n > 0 && run_input)
if (n > 0 && (run_input || have_console_on_data_cb()))
{
if (!task_post_block_high(lua_feed_task, (task_param_t)c))
{

View File

@ -4,48 +4,93 @@
#include "lauxlib.h"
#include "platform.h"
#include "linput.h"
#include "lmem.h"
#include <stdint.h>
#include <string.h>
typedef struct {
int receive_rf;
int error_rf;
char *line_buffer;
size_t line_position;
uint16_t need_len;
int16_t end_char;
} uart_cb_cfg_t;
static lua_State *gL = NULL;
static uart_cb_cfg_t uart_cb_cfg[NUM_UART];
bool uart_has_on_data_cb(unsigned id){
return uart_status[id].receive_rf != LUA_NOREF;
}
bool uart_on_data_cb(unsigned id, const char *buf, size_t len){
static bool uart_on_data_cb(unsigned id, const char *buf, size_t len){
if(!buf || len==0)
return false;
if(uart_status[id].receive_rf == LUA_NOREF)
if(uart_cb_cfg[id].receive_rf == LUA_NOREF)
return false;
if(!gL)
return false;
int top = lua_gettop(gL);
lua_rawgeti(gL, LUA_REGISTRYINDEX, uart_status[id].receive_rf);
lua_rawgeti(gL, LUA_REGISTRYINDEX, uart_cb_cfg[id].receive_rf);
lua_pushlstring(gL, buf, len);
luaL_pcallx(gL, 1, 0);
lua_settop(gL, top);
return !run_input;
}
bool uart_on_error_cb(unsigned id, const char *buf, size_t len){
if(!buf || len==0)
return false;
if(uart_status[id].error_rf == LUA_NOREF)
if(uart_cb_cfg[id].error_rf == LUA_NOREF)
return false;
if(!gL)
return false;
int top = lua_gettop(gL);
lua_rawgeti(gL, LUA_REGISTRYINDEX, uart_status[id].error_rf);
lua_rawgeti(gL, LUA_REGISTRYINDEX, uart_cb_cfg[id].error_rf);
lua_pushlstring(gL, buf, len);
luaL_pcallx(gL, 1, 0);
lua_settop(gL, top);
return true;
}
bool uart_has_on_data_cb(unsigned id){
return uart_cb_cfg[id].receive_rf != LUA_NOREF;
}
void uart_feed_data(unsigned id, const char *buf, size_t len)
{
if (id >= NUM_UART)
return;
uart_cb_cfg_t *cfg = &uart_cb_cfg[id];
if (!cfg->line_buffer)
return;
for (unsigned i = 0; i < len; ++i)
{
char ch = buf[i];
cfg->line_buffer[cfg->line_position] = ch;
cfg->line_position++;
uint16_t need_len = cfg->need_len;
int16_t end_char = cfg->end_char;
size_t max_wanted =
(end_char >= 0 && need_len == 0) ? LUA_MAXINPUT : need_len;
bool at_end = (cfg->line_position >= max_wanted);
bool end_char_found =
(end_char >= 0 && (uint8_t)ch == (uint8_t)end_char);
if (at_end || end_char_found) {
uart_on_data_cb(id, cfg->line_buffer, cfg->line_position);
cfg->line_position = 0;
}
}
}
// Lua: uart.on([id], "method", [number/char], function, [run_input])
static int uart_on( lua_State* L )
{
@ -54,14 +99,15 @@ static int uart_on( lua_State* L )
int32_t run = 1;
uint8_t stack = 1;
const char *method;
uart_status_t *us;
if( lua_isnumber( L, stack ) ) {
id = ( unsigned )luaL_checkinteger( L, stack );
MOD_CHECK_ID( uart, id );
stack++;
}
us = & uart_status[id];
uart_cb_cfg_t *cfg = &uart_cb_cfg[id];
method = luaL_checklstring( L, stack, &sl );
stack++;
if (method == NULL)
@ -69,11 +115,12 @@ static int uart_on( lua_State* L )
if( lua_type( L, stack ) == LUA_TNUMBER )
{
us->need_len = ( uint16_t )luaL_checkinteger( L, stack );
cfg->need_len = (uint16_t)luaL_checkinteger(L, stack);
stack++;
us->end_char = -1;
if( us->need_len > 255 ){
us->need_len = 255;
cfg->end_char = -1;
if(cfg->need_len > 255)
{
cfg->need_len = 255;
return luaL_error( L, "wrong arg range" );
}
}
@ -84,8 +131,8 @@ static int uart_on( lua_State* L )
if(el!=1){
return luaL_error( L, "wrong arg range" );
}
us->end_char = (int16_t)end[0];
us->need_len = 0;
cfg->end_char = (int16_t)end[0];
cfg->need_len = 0;
}
if (lua_isfunction(L, stack)) {
@ -99,12 +146,12 @@ static int uart_on( lua_State* L )
if(sl == 4 && strcmp(method, "data") == 0){
if(id == CONFIG_ESP_CONSOLE_UART_NUM)
run_input = true;
if(us->receive_rf != LUA_NOREF){
luaL_unref(L, LUA_REGISTRYINDEX, us->receive_rf);
us->receive_rf = LUA_NOREF;
if(cfg->receive_rf != LUA_NOREF){
luaL_unref(L, LUA_REGISTRYINDEX, cfg->receive_rf);
cfg->receive_rf = LUA_NOREF;
}
if(!lua_isnil(L, -1)){
us->receive_rf = luaL_ref(L, LUA_REGISTRYINDEX);
cfg->receive_rf = luaL_ref(L, LUA_REGISTRYINDEX);
gL = L;
if(id == CONFIG_ESP_CONSOLE_UART_NUM && run==0)
run_input = false;
@ -112,12 +159,12 @@ static int uart_on( lua_State* L )
lua_pop(L, 1);
}
} else if(sl == 5 && strcmp(method, "error") == 0){
if(us->error_rf != LUA_NOREF){
luaL_unref(L, LUA_REGISTRYINDEX, us->error_rf);
us->error_rf = LUA_NOREF;
if(cfg->error_rf != LUA_NOREF){
luaL_unref(L, LUA_REGISTRYINDEX, cfg->error_rf);
cfg->error_rf = LUA_NOREF;
}
if(!lua_isnil(L, -1)){
us->error_rf = luaL_ref(L, LUA_REGISTRYINDEX);
cfg->error_rf = luaL_ref(L, LUA_REGISTRYINDEX);
gL = L;
} else {
lua_pop(L, 1);
@ -183,7 +230,7 @@ static int uart_setup( lua_State* L )
static int uart_setmode(lua_State* L)
{
unsigned id, mode;
id = luaL_checkinteger( L, 1 );
MOD_CHECK_ID( uart, id );
mode = luaL_checkinteger( L, 2 );
@ -230,6 +277,11 @@ static int uart_stop( lua_State* L )
id = luaL_checkinteger( L, 1 );
MOD_CHECK_ID( uart, id );
platform_uart_stop( id );
if (uart_cb_cfg[id].line_buffer)
{
luaM_freemem(L, uart_cb_cfg[id].line_buffer, LUA_MAXINPUT);
uart_cb_cfg[id].line_buffer = NULL;
}
return 0;
}
@ -240,6 +292,8 @@ static int uart_start( lua_State* L )
int err;
id = luaL_checkinteger( L, 1 );
MOD_CHECK_ID( uart, id );
if (!uart_cb_cfg[id].line_buffer)
uart_cb_cfg[id].line_buffer = luaM_malloc(L, LUA_MAXINPUT);
err = platform_uart_start( id );
lua_pushboolean( L, err == 0 );
return 1;
@ -303,21 +357,23 @@ LROT_BEGIN(uart, NULL, 0)
LROT_NUMENTRY( FLOWCTRL_NONE, PLATFORM_UART_FLOW_NONE )
LROT_NUMENTRY( FLOWCTRL_CTS, PLATFORM_UART_FLOW_CTS )
LROT_NUMENTRY( FLOWCTRL_RTS, PLATFORM_UART_FLOW_RTS )
LROT_NUMENTRY( MODE_UART, PLATFORM_UART_MODE_UART )
LROT_NUMENTRY( MODE_UART, PLATFORM_UART_MODE_UART )
LROT_NUMENTRY( MODE_RS485_COLLISION_DETECT, PLATFORM_UART_MODE_RS485_COLLISION_DETECT )
LROT_NUMENTRY( MODE_RS485_APP_CONTROL, PLATFORM_UART_MODE_RS485_APP_CONTROL )
LROT_NUMENTRY( MODE_RS485_HALF_DUPLEX, PLATFORM_UART_MODE_HALF_DUPLEX )
LROT_NUMENTRY( MODE_IRDA, PLATFORM_UART_MODE_IRDA )
LROT_NUMENTRY( MODE_IRDA, PLATFORM_UART_MODE_IRDA )
LROT_END(uart, NULL, 0)
int luaopen_uart( lua_State *L ) {
uart_status_t *us;
for(int id = 0; id < NUM_UART; id++) {
us = & uart_status[id];
us->receive_rf = LUA_NOREF;
us->error_rf = LUA_NOREF;
us->need_len = 0;
us->end_char = -1;
for(int id = 0; id < sizeof(uart_cb_cfg)/sizeof(uart_cb_cfg[0]); id++)
{
uart_cb_cfg_t *cfg = &uart_cb_cfg[id];
cfg->receive_rf = LUA_NOREF;
cfg->error_rf = LUA_NOREF;
cfg->line_buffer = NULL;
cfg->line_position = 0;
cfg->need_len = 0;
cfg->end_char = -1;
}
return 0;
}

View File

@ -91,15 +91,12 @@ typedef struct {
typedef struct {
QueueHandle_t queue;
TaskHandle_t taskHandle;
int receive_rf;
int error_rf;
char *line_buffer;
size_t line_position;
uint16_t need_len;
int16_t end_char;
} uart_status_t;
extern uart_status_t uart_status[NUM_UART];
// We have a bit of legacy spaghetti - these point into modules/uart.c
extern bool uart_has_on_data_cb(unsigned id);
extern void uart_feed_data(unsigned id, const char *buf, size_t len);
extern bool uart_on_error_cb(unsigned id, const char *buf, size_t len);
// Flow control types (this is a bit mask, one can specify PLATFORM_UART_FLOW_RTS | PLATFORM_UART_FLOW_CTS )
#define PLATFORM_UART_FLOW_NONE 0

View File

@ -83,14 +83,9 @@ uart_status_t uart_status[NUM_UART];
task_handle_t uart_event_task_id = 0;
SemaphoreHandle_t sem = NULL;
extern bool uart_has_on_data_cb(unsigned id);
extern bool uart_on_data_cb(unsigned id, const char *buf, size_t len);
extern bool uart_on_error_cb(unsigned id, const char *buf, size_t len);
void uart_event_task( task_param_t param, task_prio_t prio ) {
uart_event_post_t *post = (uart_event_post_t *)param;
unsigned id = post->id;
uart_status_t *us = &uart_status[id];
xSemaphoreGive(sem);
if(post->type == PLATFORM_UART_EVENT_DATA) {
if (id == CONFIG_ESP_CONSOLE_UART_NUM && run_input) {
@ -101,28 +96,9 @@ void uart_event_task( task_param_t param, task_prio_t prio ) {
i += used;
}
}
if (uart_has_on_data_cb(id)) {
size_t i = 0;
while (i < post->size)
{
char ch = post->data[i];
us->line_buffer[us->line_position] = ch;
us->line_position++;
if (uart_has_on_data_cb(id))
uart_feed_data(id, post->data, post->size);
uint16_t need_len = us->need_len;
int16_t end_char = us->end_char;
size_t max_wanted =
(end_char >= 0 && need_len == 0) ? LUA_MAXINPUT : need_len;
bool at_end = (us->line_position >= max_wanted);
bool end_char_found =
(end_char >= 0 && (uint8_t)ch == (uint8_t)end_char);
if (at_end || end_char_found) {
uart_on_data_cb(id, us->line_buffer, us->line_position);
us->line_position = 0;
}
++i;
}
}
free(post->data);
} else {
const char *err;
@ -353,20 +329,12 @@ int platform_uart_start( unsigned id )
if(ret != ESP_OK) {
return -1;
}
us->line_buffer = malloc(LUA_MAXINPUT);
us->line_position = 0;
if(us->line_buffer == NULL) {
uart_driver_delete(id);
return -1;
}
char pcName[6];
snprintf( pcName, 6, "uart%d", id );
pcName[5] = '\0';
if(xTaskCreate(task_uart, pcName, 2048, (void*)id, ESP_TASK_MAIN_PRIO + 1, & us->taskHandle) != pdPASS) {
uart_driver_delete(id);
free(us->line_buffer);
us->line_buffer = NULL;
return -1;
}
@ -381,8 +349,6 @@ void platform_uart_stop( unsigned id )
#endif
uart_status_t *us = & uart_status[id];
uart_driver_delete(id);
if(us->line_buffer) free(us->line_buffer);
us->line_buffer = NULL;
if(us->taskHandle) vTaskDelete(us->taskHandle);
us->taskHandle = NULL;
}