From 45283374ec77b9fb2ec5f48ef1890db27d40dc43 Mon Sep 17 00:00:00 2001 From: Jade Mattsson Date: Sun, 12 May 2024 19:49:33 +1000 Subject: [PATCH 1/4] 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. --- components/base_nodemcu/user_main.c | 22 ++++- components/modules/uart.c | 124 ++++++++++++++++++------- components/platform/include/platform.h | 11 +-- components/platform/platform.c | 40 +------- 4 files changed, 117 insertions(+), 80 deletions(-) diff --git a/components/base_nodemcu/user_main.c b/components/base_nodemcu/user_main.c index 01a2dde22..c72cc6543 100644 --- a/components/base_nodemcu/user_main.c +++ b/components/base_nodemcu/user_main.c @@ -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)) { diff --git a/components/modules/uart.c b/components/modules/uart.c index 107e28849..6a738f0ad 100644 --- a/components/modules/uart.c +++ b/components/modules/uart.c @@ -4,48 +4,93 @@ #include "lauxlib.h" #include "platform.h" #include "linput.h" +#include "lmem.h" #include #include +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; } diff --git a/components/platform/include/platform.h b/components/platform/include/platform.h index 2706a9de4..42ec933a6 100644 --- a/components/platform/include/platform.h +++ b/components/platform/include/platform.h @@ -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 diff --git a/components/platform/platform.c b/components/platform/platform.c index 2d8141bdb..86606df8c 100644 --- a/components/platform/platform.c +++ b/components/platform/platform.c @@ -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++; - - 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; - } - } + if (uart_has_on_data_cb(id)) + uart_feed_data(id, post->data, post->size); + 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; } From 3324ce137dbdcec84697e9f2a1035c393b74c59c Mon Sep 17 00:00:00 2001 From: Jade Mattsson Date: Sun, 12 May 2024 20:00:24 +1000 Subject: [PATCH 2/4] Disable CR/LF translation by default. With the change to use the IDF's stdin to feed the Lua VM, the default IDF CR/LF translation appears to cause issues with some IDEs. --- sdkconfig.defaults | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/sdkconfig.defaults b/sdkconfig.defaults index 617c1c319..3475ee402 100644 --- a/sdkconfig.defaults +++ b/sdkconfig.defaults @@ -32,3 +32,7 @@ CONFIG_LWIP_TCP_MSL=5000 # Disable esp-idf's bluetooth component by default. # The bthci module is also disabled and will enable bt when selected CONFIG_BT_ENABLED=n + +# Disable CR/LF translation, for legacy compatibility +CONFIG_NEWLIB_STDIN_LINE_ENDING_LF=y +CONFIG_NEWLIB_STDOUT_LINE_ENDING_LF=y From e1a0a627ff8fa2a1ef1bff9ae46139339e97c8ec Mon Sep 17 00:00:00 2001 From: Jade Mattsson Date: Sun, 12 May 2024 20:08:20 +1000 Subject: [PATCH 3/4] Added node.chipmodel(). Updated and removed incorrect node module documentation. --- components/modules/node.c | 8 ++++++ docs/modules/node.md | 54 ++++++++++----------------------------- 2 files changed, 22 insertions(+), 40 deletions(-) diff --git a/components/modules/node.c b/components/modules/node.c index a46350899..3a1a3c5da 100644 --- a/components/modules/node.c +++ b/components/modules/node.c @@ -174,6 +174,13 @@ static int node_chipid( lua_State *L ) } #endif +// Lua: node.chipmodel() +static int node_chipmodel(lua_State *L) +{ + lua_pushstring(L, CONFIG_IDF_TARGET); + return 1; +} + // Lua: node.heap() static int node_heap( lua_State* L ) { @@ -868,6 +875,7 @@ LROT_BEGIN(node, NULL, 0) #if defined(CONFIG_IDF_TARGET_ESP32) LROT_FUNCENTRY( chipid, node_chipid ) #endif + LROT_FUNCENTRY( chipmodel, node_chipmodel ) LROT_FUNCENTRY( compile, node_compile ) LROT_FUNCENTRY( dsleep, node_dsleep ) #if defined(CONFIG_LUA_VERSION_51) diff --git a/docs/modules/node.md b/docs/modules/node.md index eefd87209..a86b814d6 100644 --- a/docs/modules/node.md +++ b/docs/modules/node.md @@ -71,7 +71,7 @@ if reset_reason == 0 then print("Power UP!") end ## node.chipid() -Returns the ESP chip ID. +Returns the ESP chip ID. Only available on the base ESP32 model. #### Syntax `node.chipid()` @@ -85,6 +85,19 @@ chip ID (string) Note that due to the chip id being a much larger value on the ESP32, it is reported as a string now. E.g. `"0x1818fe346a88"`. +## node.chipmodel() + +Returns the model of the ESP chip. + +#### Syntax +`node.chipmodel()` + +#### Parameters +none + +#### Returns +The chip model as a string, e.g. "esp32c3". This is the string corresponding to the IDF's Kconfig parameter `IDF_TARGET`. + ## node.compile() Compiles a Lua text file into Lua bytecode, and saves it as .lc file. @@ -153,20 +166,6 @@ node.dsleep({ gpio = 13, level = 0, pull = true }) - [`node.sleep()`](#nodesleep) -## node.flashid() - -Returns the flash chip ID. - -#### Syntax -`node.flashid()` - -#### Parameters -none - -#### Returns -flash ID (number) - - ## node.flashindex() --deprecated Deprecated synonym for [`node.LFS.get()`](#nodelfsget) to return an LFS function reference. @@ -191,31 +190,6 @@ none #### Returns system heap size left in bytes (number) -## node.info() - -Returns NodeMCU version, chipid, flashid, flash size, flash mode, flash speed. - -#### Syntax -`node.info()` - -#### Parameters -none - -#### Returns - - `majorVer` (number) - - `minorVer` (number) - - `devVer` (number) - - `chipid` (number) - - `flashid` (number) - - `flashsize` (number) - - `flashmode` (number) - - `flashspeed` (number) - -#### Example -```lua -majorVer, minorVer, devVer, chipid, flashid, flashsize, flashmode, flashspeed = node.info() -print("NodeMCU "..majorVer.."."..minorVer.."."..devVer) -``` ## node.input() From cd71a9c63f8c0bb17cdce6a6b5bcbe13bee2e4a8 Mon Sep 17 00:00:00 2001 From: Jade Mattsson Date: Mon, 20 May 2024 15:54:52 +1000 Subject: [PATCH 4/4] Actually honour Kconfig line-endings settings. --- components/base_nodemcu/user_main.c | 30 +++++++++++++++++++++++------ 1 file changed, 24 insertions(+), 6 deletions(-) diff --git a/components/base_nodemcu/user_main.c b/components/base_nodemcu/user_main.c index c72cc6543..0a70e58b6 100644 --- a/components/base_nodemcu/user_main.c +++ b/components/base_nodemcu/user_main.c @@ -36,6 +36,24 @@ #define SIG_LUA 0 #define SIG_UARTINPUT 1 +// Line ending config from Kconfig +#if CONFIG_NEWLIB_STDIN_LINE_ENDING_CRLF +# define RX_LINE_ENDINGS_CFG ESP_LINE_ENDINGS_CRLF +#elif CONFIG_NEWLIB_STDIN_LINE_ENDING_CR +# define RX_LINE_ENDINGS_CFG ESP_LINE_ENDINGS_CR +#else +# define RX_LINE_ENDINGS_CFG ESP_LINE_ENDINGS_LF +#endif + +#if CONFIG_NEWLIB_STDOUT_LINE_ENDING_CRLF +# define TX_LINE_ENDINGS_CFG ESP_LINE_ENDINGS_CRLF +#elif CONFIG_NEWLIB_STDOUT_LINE_ENDING_CR +# define TX_LINE_ENDINGS_CFG ESP_LINE_ENDINGS_CR +#else +# define TX_LINE_ENDINGS_CFG ESP_LINE_ENDINGS_LF +#endif + + // We don't get argument size data from the esp_event dispatch, so it's // not possible to copy and forward events from the default event queue // to one running within our task context. To cope with this, we instead @@ -214,9 +232,9 @@ static void console_init(void) /* Based on console/advanced example */ esp_vfs_dev_uart_port_set_rx_line_endings( - CONFIG_ESP_CONSOLE_UART_NUM, ESP_LINE_ENDINGS_CR); + CONFIG_ESP_CONSOLE_UART_NUM, RX_LINE_ENDINGS_CFG); esp_vfs_dev_uart_port_set_tx_line_endings( - CONFIG_ESP_CONSOLE_UART_NUM, ESP_LINE_ENDINGS_CRLF); + CONFIG_ESP_CONSOLE_UART_NUM, TX_LINE_ENDINGS_CFG); /* Configure UART. Note that REF_TICK is used so that the baud rate remains * correct while APB frequency is changing in light sleep mode. @@ -242,8 +260,8 @@ static void console_init(void) #elif CONFIG_ESP_CONSOLE_USB_SERIAL_JTAG /* Based on @pjsg's work */ - esp_vfs_dev_usb_serial_jtag_set_rx_line_endings(ESP_LINE_ENDINGS_CR); - esp_vfs_dev_usb_serial_jtag_set_tx_line_endings(ESP_LINE_ENDINGS_CRLF); + esp_vfs_dev_usb_serial_jtag_set_rx_line_endings(RX_LINE_ENDINGS_CFG); + esp_vfs_dev_usb_serial_jtag_set_tx_line_endings(TX_LINE_ENDINGS_CFG); usb_serial_jtag_driver_config_t usb_serial_jtag_config = USB_SERIAL_JTAG_DRIVER_CONFIG_DEFAULT(); @@ -254,8 +272,8 @@ static void console_init(void) #elif CONFIG_ESP_CONSOLE_USB_CDC /* Based on console/advanced_usb_cdc */ - esp_vfs_dev_cdcacm_set_rx_line_endings(ESP_LINE_ENDINGS_CR); - esp_vfs_dev_cdcacm_set_tx_line_endings(ESP_LINE_ENDINGS_CRLF); + esp_vfs_dev_cdcacm_set_rx_line_endings(RX_LINE_ENDINGS_CFG); + esp_vfs_dev_cdcacm_set_tx_line_endings(TX_LINE_ENDINGS_CFG); #else # error "Unsupported console type" #endif