From 185855b69af3d3775e14f230366a94f2d0eb660e Mon Sep 17 00:00:00 2001 From: Johny Mattsson Date: Thu, 26 Aug 2021 11:33:42 +1000 Subject: [PATCH] Brought over node.setonerror() functionality. Now properly triggering restart on non-interactive errors. --- components/modules/node.c | 44 ++++++++++++++++++++++++++++++++++++++- docs/modules/node.md | 26 +++++++++++++++++++++++ 2 files changed, 69 insertions(+), 1 deletion(-) diff --git a/components/modules/node.c b/components/modules/node.c index a39afcab..fc56c074 100644 --- a/components/modules/node.c +++ b/components/modules/node.c @@ -15,6 +15,42 @@ #include "lnodeaux.h" #include "lpanic.h" #include "rom/rtc.h" +#include "freertos/FreeRTOS.h" +#include "freertos/timers.h" + +static void restart_callback(TimerHandle_t timer) { + (void)timer; + esp_restart(); +} + +static int default_onerror(lua_State *L) { + /* Use Lua print to print the ToS */ + lua_settop(L, 1); + lua_getglobal(L, "print"); + lua_insert(L, 1); + lua_pcall(L, 1, 0, 0); + /* One first time through set automatic restart after 2s delay */ + static TimerHandle_t restart_timer; + if (!restart_timer) { + restart_timer = xTimerCreate( + "error_restart", pdMS_TO_TICKS(2000), pdFALSE, NULL, restart_callback); + if (xTimerStart(restart_timer, portMAX_DELAY) != pdPASS) + esp_restart(); // should never happen, but Justin Case fallback + } + return 0; +} + +// Lua: setonerror([function]) +static int node_setonerror( lua_State* L ) { + lua_settop(L, 1); + if (!lua_isfunction(L, 1)) { + lua_pop(L, 1); + lua_pushcfunction(L, default_onerror); + } + lua_setfield(L, LUA_REGISTRYINDEX, "onerror"); + return 0; +} + // Lua: node.bootreason() static int node_bootreason( lua_State *L) @@ -808,6 +844,7 @@ LROT_BEGIN(node, NULL, 0) LROT_FUNCENTRY( osoutput, node_osoutput ) LROT_FUNCENTRY( osprint, node_osprint ) LROT_FUNCENTRY( restart, node_restart ) + LROT_FUNCENTRY( setonerror, node_setonerror ) LROT_FUNCENTRY( sleep, node_sleep ) LROT_FUNCENTRY( stripdebug, node_stripdebug ) LROT_TABENTRY ( task, node_task ) @@ -815,5 +852,10 @@ LROT_BEGIN(node, NULL, 0) LROT_TABENTRY ( wakeup, node_wakeup ) LROT_END(node, NULL, 0) +int luaopen_node(lua_State *L) +{ + lua_settop(L, 0); + return node_setonerror(L); /* set default onerror action */ +} -NODEMCU_MODULE(NODE, "node", node, NULL); +NODEMCU_MODULE(NODE, "node", node, luaopen_node); diff --git a/docs/modules/node.md b/docs/modules/node.md index 4286c914..82d36d5d 100644 --- a/docs/modules/node.md +++ b/docs/modules/node.md @@ -486,6 +486,32 @@ node.setcpufreq(node.CPU80MHZ) ``` +## node.setonerror() + +Overrides the default crash handling which always restarts the system. It can be used to e.g. write an error to a logfile or to secure connected hardware before restarting. + +!!! attention + + It is strongly advised to ensure that the callback ends with a restart. Something has gone quite wrong and it is probably not safe to just wait for the next event (e.g., timer tick) and hope everything works out. + +#### Syntax +`node.setonerror(function)` + +#### Parameters +`function` a callback function to be executed when an error occurs, gets the error string as an argument, remember to **trigger a restart** at the end of the callback + +#### Returns +`nil` + +#### Example +```lua +node.setonerror(function(s) + print("Error: "..s) + node.restart() + end) +``` + + ## node.sleep() Enters light sleep mode, which saves power without losing state. The state of the CPU and peripherals is preserved during light sleep and is resumed once the processor wakes up. When the processor wakes back up depends on the supplied `options`. Wake up from light sleep can be triggered by a time period, or when a GPIO (or GPIOs) change level, when a touchpad event occurs, when data is received on a UART, or by the ULP (ultra low power processor, generally not used by NodeMCU). If multiple different wakeup sources are specified, the processor will wake when any of them occur. The return value of the function can be used to determine which source caused the wakeup. The function does not return until a wakeup occurs (and therefore may not return at all if a wakeup trigger never happens).