Browse Source

Implement panic call handling for all modules (#3163)

Terry Ellison 3 years ago
parent
commit
1f2e5bba4a
58 changed files with 607 additions and 627 deletions
  1. 16 15
      .gdbinitlua
  2. 27 7
      app/lua/lauxlib.c
  3. 2 1
      app/lua/lauxlib.h
  4. 3 3
      app/lua/ldebug.c
  5. 5 3
      app/lua/lflash.c
  6. 24 16
      app/lua/lnodemcu.c
  7. 13 6
      app/lua/lnodemcu.h
  8. 1 1
      app/lua/lobject.h
  9. 12 4
      app/lua/lua.h
  10. 46 21
      app/lua53/lauxlib.c
  11. 2 0
      app/lua53/lauxlib.h
  12. 0 1
      app/lua53/llimits.h
  13. 6 4
      app/lua53/lnodemcu.c
  14. 4 51
      app/lua53/lnodemcu.h
  15. 31 41
      app/lua53/lua.c
  16. 7 7
      app/lua53/lua.h
  17. 42 0
      app/lua53/lundump.h
  18. 1 1
      app/modules/ads1115.c
  19. 1 1
      app/modules/bme280.c
  20. 1 1
      app/modules/bme680.c
  21. 1 1
      app/modules/cron.c
  22. 2 1
      app/modules/crypto.c
  23. 18 16
      app/modules/dcc.c
  24. 3 3
      app/modules/enduser_setup.c
  25. 2 1
      app/modules/file.c
  26. 3 5
      app/modules/gpio.c
  27. 1 1
      app/modules/gpio_pulse.c
  28. 1 1
      app/modules/http.c
  29. 1 1
      app/modules/hx711.c
  30. 52 7
      app/modules/node.c
  31. 1 1
      app/modules/pcm.c
  32. 1 1
      app/modules/pipe.c
  33. 1 1
      app/modules/rotary.c
  34. 2 2
      app/modules/sntp.c
  35. 1 1
      app/modules/softuart.c
  36. 1 1
      app/modules/somfy.c
  37. 1 1
      app/modules/switec.c
  38. 1 1
      app/modules/tcs34725.c
  39. 8 9
      app/modules/tls.c
  40. 98 192
      app/modules/tmr.c
  41. 4 4
      app/modules/websocket.c
  42. 5 6
      app/modules/wifi.c
  43. 1 1
      app/modules/wifi_eventmon.c
  44. 1 1
      app/modules/wifi_monitor.c
  45. 1 1
      app/modules/wps.c
  46. 6 4
      app/pcm/pcm_core.c
  47. 41 65
      app/platform/platform.c
  48. 1 1
      app/pm/pmSleep.c
  49. 81 93
      app/pm/swtimer.c
  50. 1 0
      app/spiffs/nodemcu_spiffs.h
  51. 1 1
      app/spiffs/spiffs.c
  52. 11 1
      app/spiffs/spiffs_nucleus.c
  53. 4 12
      app/spiffs/spiffs_nucleus.h
  54. 2 2
      app/u8g2lib/u8x8_d_fbrle.c
  55. 1 1
      lua_modules/liquidcrystal/lc-gpio4bit.lua
  56. 1 1
      lua_modules/liquidcrystal/lc-gpio8bit.lua
  57. 1 1
      lua_modules/liquidcrystal/lc-i2c4bit.lua
  58. 1 1
      lua_modules/liquidcrystal/liquidcrystal.lua

+ 16 - 15
.gdbinitlua

@@ -50,32 +50,25 @@ define prTV
        printf "Boolean: %u\n", $val.n
     end
     if $type == 2
-       # ROTable
-       printf "ROTable: %p\n", $val.p
-    end
-    if $type == 3
-        # Light Function
-       printf "Light Func: %p\n", $val.p
-    end
-    if $type == 4
        # Light User Data
        printf "Light Udata: %p\n", $val.p
     end
-    if $type == 5
+    if $type == 3
        # Number
        printf "Number: %u\n", $val.n
     end
-    if $type == 6
-      prTS $arg0
+    if $type == 4
+       # String
+       printf "String: %s\n", (char *)($val.p)+16
     end
-    if $type == 7
+    if $type == 5
       # Table
       set $o = &($val->gc.h)
       printf "Common header: next = %p, marked = 0x%01x\n", $o->next, $o->marked
       printf "Nodes: %4i  %p\n", 2<<($o->lsizenode), $o->node
       printf "Arry:  %4i  %p\n", $o->sizearray, $o->array
     end
-    if $type == 8
+    if $type == 6
       # Function
       set $o = &($val->gc.cl.c)
       printf "Common header: next = %p, marked = 0x%01x\n", $o->next, $o->marked
@@ -88,16 +81,24 @@ define prTV
           $o->nupvalues, $o->gclist, $o->env, $o->f
       end
     end
-    if $type == 9
+    if $type == 7
       # UserData
       set $o = &($val->gc.u.uv)
       printf "Common header: next = %p, marked = 0x%01x\n", $o->next, $o->marked
       printf "UD = %p Userdata: metatable = ", ($o+1))
       print ($o)->metatable
     end
-    if $type == 10
+    if $type == 8
       # Thread
     end
+   if $type == 21
+       # ROTable
+       printf "ROTable: %p\n", $val.p
+    end
+    if $type == 38
+        # Light Function
+       printf "Light Func: %p\n", $val.p
+    end
   end
 end
 

+ 27 - 7
app/lua/lauxlib.c

@@ -684,6 +684,26 @@ LUALIB_API void luaL_unref (lua_State *L, int t, int ref) {
 }
 
 
+LUALIB_API void (luaL_reref) (lua_State *L, int t, int *ref) {
+  int reft;
+/*
+ * If the ref is positive and the entry in table t exists then
+ * overwrite the value otherwise fall through to luaL_ref()
+ */   
+  if (ref) {
+    if (*ref >= 0) {
+      t = abs_index(L, t);
+      lua_rawgeti(L, t, *ref);
+      reft = lua_type(L, -1);
+      lua_pop(L, 1);
+      if (reft != LUA_TNIL) {
+        lua_rawseti(L, t, *ref);
+        return;
+      }
+    }
+    *ref = luaL_ref(L, t);
+  }
+}
 
 /*
 ** {======================================================
@@ -899,23 +919,23 @@ LUALIB_API void luaL_assertfail(const char *file, int line, const char *message)
  * is the option to exit the interactive session and start the Xtensa remote GDB
  * which will then sync up with the remote GDB client to allow forensics of the error.
  */
-#ifdef LUA_CROSS_COMPILER
-LUALIB_API void lua_debugbreak(void) {
-  puts(" lua_debugbreak ");  /* allows BT analysis of assert fails */
-}
-#else
 extern void gdbstub_init(void);
+extern void gdbstub_redirect_output(int);
 
-LUALIB_API void lua_debugbreak(void) {
+LUALIB_API void lua_debugbreak (void) {
+#ifdef LUA_CROSS_COMPILER
+  puts(" lua_debugbreak ");  /* allows gdb BT analysis of assert fails */
+#else
   static int repeat_entry = 0;
   if  (repeat_entry == 0) {
     dbg_printf("Start up the gdb stub if not already started\n");
     gdbstub_init();
+    gdbstub_redirect_output(1);
     repeat_entry = 1;
   }
   asm("break 0,0" ::);
-}
 #endif
+}
 #endif
 
 

+ 2 - 1
app/lua/lauxlib.h

@@ -73,6 +73,8 @@ LUALIB_API int (luaL_checkoption) (lua_State *L, int narg, const char *def,
 
 LUALIB_API int (luaL_ref) (lua_State *L, int t);
 LUALIB_API void (luaL_unref) (lua_State *L, int t, int ref);
+#define luaL_unref2(l,t,r) luaL_unref(L, (t), (r)); r = LUA_NOREF
+LUALIB_API void (luaL_reref) (lua_State *L, int t, int *ref);
 
 LUALIB_API int (luaL_loadfile) (lua_State *L, const char *filename);
 
@@ -160,7 +162,6 @@ LUALIB_API void (luaL_pushresult) (luaL_Buffer *B);
 
 /* }====================================================== */
 
-LUALIB_API int luaL_traceback (lua_State *L);
 LUALIB_API int luaL_pcallx (lua_State *L, int narg, int nres);
 LUALIB_API int luaL_posttask( lua_State* L, int prio );
 

+ 3 - 3
app/lua/ldebug.c

@@ -668,7 +668,7 @@ static int isinstack (CallInfo *ci, const TValue *o) {
 
 void luaG_typeerror (lua_State *L, const TValue *o, const char *op) {
   const char *name = NULL;
-  const char *t = luaT_typenames[ttype(o)];
+  const char *t = luaT_typenames[basettype(o)];
   const char *kind = (isinstack(L->ci, o)) ?
                          getobjname(L, L->ci, cast_int(o - L->base), &name) :
                          NULL;
@@ -696,8 +696,8 @@ void luaG_aritherror (lua_State *L, const TValue *p1, const TValue *p2) {
 
 
 int luaG_ordererror (lua_State *L, const TValue *p1, const TValue *p2) {
-  const char *t1 = luaT_typenames[ttype(p1)];
-  const char *t2 = luaT_typenames[ttype(p2)];
+  const char *t1 = luaT_typenames[basettype(p1)];
+  const char *t2 = luaT_typenames[basettype(p2)];
   if (t1[2] == t2[2])
     luaG_runerror(L, "attempt to compare two %s values", t1);
   else

+ 5 - 3
app/lua/lflash.c

@@ -138,7 +138,7 @@ static void flashErase(uint32_t start, uint32_t end){
 }
 
 /* =====================================================================================
- * luaN_init(), luaN_reload_reboot() and luaN_index() are exported via lflash.h.
+ * luaN_init() is exported via lflash.h.
  * The first is the startup hook used in lstate.c and the last two are
  * implementations of the node.flash API calls.
  */
@@ -194,10 +194,12 @@ static int loadLFS (lua_State *L);
 static int loadLFSgc (lua_State *L);
 static void procFirstPass (void);
 
+/* lua_lfsreload() and lua_lfsindex() are exported via lua.h */
+
 /*
  * Library function called by node.flashreload(filename).
  */
-LUALIB_API int luaN_reload_reboot (lua_State *L) {
+LUALIB_API int lua_lfsreload (lua_State *L) {
   const char *fn = lua_tostring(L, 1), *msg = "";
   int status;
 
@@ -266,7 +268,7 @@ LUALIB_API int luaN_reload_reboot (lua_State *L) {
  *  -  The base address and length of the LFS
  *  -  An array of the module names in the LFS
  */
-LUAI_FUNC int luaN_index (lua_State *L) {
+LUAI_FUNC int lua_lfsindex (lua_State *L) {
   int n = lua_gettop(L);
 
   /* Return nil + the LFS base address if the LFS size > 0 and it isn't loaded */

+ 24 - 16
app/lua/lnodemcu.c

@@ -26,27 +26,31 @@
 ** directly through the task interface the call is wrapped in a C closure with
 ** the error string as an Upval and this is posted to call the Lua reporter.
 */
-static int report_traceback (lua_State *L) {
-// **Temp**  lua_rawgeti(L, LUA_REGISTRYINDEX, G(L)->error_reporter);
-  lua_getglobal(L, "print");
+static int errhandler_aux (lua_State *L) {
+  lua_getfield(L, LUA_REGISTRYINDEX, "onerror");
+  if (!lua_isfunction(L, -1)) {
+    lua_pop(L, 1);
+    lua_getglobal(L, "print");
+  }
   lua_pushvalue(L, lua_upvalueindex(1));
   lua_call(L, 1, 0);  /* Using an error handler would cause an infinite loop! */
   return 0;
 }
 
 /*
-** Catch all error handler for CB calls.  This uses debug.traceback() to
-** generate a full Lua traceback.
+** Error handler for luaL_pcallx(), called from the lua_pcall() with a single 
+** argument -- the thrown error. This plus depth=2 is passed to debug.traceback()
+** to convert into an error message which it handles in a separate posted task.
 */
-LUALIB_API int luaL_traceback (lua_State *L) {
-  if (lua_isstring(L, 1)) {
-    lua_getglobal(L, "debug");
-    lua_getfield(L, -1, "traceback");
-    lua_remove(L, -2);
-    lua_pushvalue(L, 1);                                /* pass error message */
+static int errhandler (lua_State *L) {
+  lua_getglobal(L, "debug");
+  lua_getfield(L, -1, "traceback");
+  if (lua_isfunction(L, -1)) {
+    lua_insert(L, 1);                 /* insert tracback function above error */
+    lua_pop(L, 1);                              /* dump the debug table entry */
     lua_pushinteger(L, 2);                /* skip this function and traceback */
     lua_call(L, 2, 1);      /* call debug.traceback and return it as a string */
-    lua_pushcclosure(L, report_traceback, 1);     /* report with str as upval */
+    lua_pushcclosure(L, errhandler_aux, 1);       /* report with str as upval */
     luaL_posttask(L, LUA_TASK_HIGH);
   }
   return 0;
@@ -60,12 +64,16 @@ LUALIB_API int luaL_traceback (lua_State *L) {
 LUALIB_API int luaL_pcallx (lua_State *L, int narg, int nres) { // [-narg, +0, v]
   int status;
   int base = lua_gettop(L) - narg;
-  lua_pushcfunction(L, luaL_traceback);
+  lua_pushcfunction(L, errhandler);
   lua_insert(L, base);                                      /* put under args */
-  status = lua_pcall(L, narg, (nres < 0 ? LUA_MULTRET : nres), base);
+  status = lua_pcall(L, narg, nres, base);
   lua_remove(L, base);                           /* remove traceback function */
-  if (status && nres >=0)
-    lua_settop(L, base + nres);                 /* balance the stack on error */
+  if (status != LUA_OK && status != LUA_ERRRUN) {  
+    lua_gc(L, LUA_GCCOLLECT, 0);   /* call onerror directly if handler failed */
+    lua_pushliteral(L, "out of memory");
+    lua_pushcclosure(L, errhandler_aux, 1);            /* report EOM as upval */
+    luaL_posttask(L, LUA_TASK_HIGH);
+    }
   return status;
 }
 

+ 13 - 6
app/lua/lnodemcu.h

@@ -1,5 +1,6 @@
-/* Read-only tables for Lua */
-
+/*
+ * NodeMCU extensions to Lua 5.1 for readonly Flash memory support
+ */
 #ifndef lnodemcu_h
 #define lnodemcu_h
 
@@ -16,7 +17,8 @@
 #define LOCK_IN_SECTION(s) __attribute__((used,unused,section(".lua_" #s)))
 #endif
 
-/* Macros one can use to define rotable entries */
+/* Macros used to declare rotable entries */
+
 #define LRO_FUNCVAL(v)       {{.p = v}, LUA_TLIGHTFUNCTION}
 #define LRO_LUDATA(v)        {{.p = cast(void*,v)}, LUA_TLIGHTUSERDATA}
 #define LRO_NILVAL           {{.p = NULL}, LUA_TNIL}
@@ -25,7 +27,7 @@
 #define LRO_FLOATVAL(v)      LRO_NUMVAL(v)
 #define LRO_ROVAL(v)         {{.gc = cast(GCObject *, &(v ## _ROTable))}, LUA_TROTABLE}
 
-#define LROT_MARKED           0  //<<<<<<<<<<*** TBD *** >>>>>>>>>>>
+#define LROT_MARKED          0 //<<<<<<<<<<  *** TBD *** >>>>>>>>>>>
 
 #define LROT_FUNCENTRY(n,f)  {LRO_STRKEY(#n), LRO_FUNCVAL(f)},
 #define LROT_LUDENTRY(n,x)   {LRO_STRKEY(#n), LRO_LUDATA(x)},
@@ -38,9 +40,9 @@
 #define LROT_ENTRYREF(rt)    (rt ##_entries)
 #define LROT_TABLEREF(rt)    (&rt ##_ROTable)
 #define LROT_BEGIN(rt,mt,f)  LROT_TABLE(rt); \
-  static const ROTable_entry rt ## _entries[] = {
+  static ROTable_entry rt ## _entries[] = {
 #define LROT_ENTRIES_IN_SECTION(rt,s) \
-  static const ROTable_entry LOCK_IN_SECTION(s) rt ## _entries[] = {
+  static ROTable_entry LOCK_IN_SECTION(s) rt ## _entries[] = {
 #define LROT_END(rt,mt,f)    {NULL, LRO_NILVAL} }; \
   const ROTable rt ## _ROTable = { \
     (GCObject *)1, LUA_TROTABLE, LROT_MARKED, \
@@ -64,4 +66,9 @@
 #define LROT_MASK_GC_INDEX   (LROT_MASK_GC | LROT_MASK_INDEX)
 
 /* Maximum length of a rotable name and of a string key*/
+
+#ifdef LUA_CORE
+
 #endif
+#endif
+

+ 1 - 1
app/lua/lobject.h

@@ -410,7 +410,7 @@ typedef struct Table {
 GET_BYTE_FN(flags,Table,4,16)
 GET_BYTE_FN(lsizenode,Table,4,24)
 
-typedef struct ROTable_entry {
+typedef const struct ROTable_entry {
   const char *key;
   const TValue value;
 } ROTable_entry;

+ 12 - 4
app/lua/lua.h

@@ -383,10 +383,13 @@ struct lua_Debug {
 /* }====================================================================== */
 
 typedef struct ROTable ROTable;
-typedef struct ROTable_entry ROTable_entry;
+typedef const struct ROTable_entry ROTable_entry;
 
-LUA_API void  (lua_pushrotable) (lua_State *L, const ROTable *p);
-LUA_API void  (lua_createrotable) (lua_State *L, ROTable *t, const ROTable_entry *e, ROTable *mt);
+LUA_API void (lua_pushrotable) (lua_State *L, const ROTable *p);
+LUA_API void (lua_createrotable) (lua_State *L, ROTable *t, ROTable_entry *e, ROTable *mt);
+
+LUAI_FUNC int  lua_lfsreload (lua_State *L);
+LUAI_FUNC int  lua_lfsindex (lua_State *L);
 
 #define EGC_NOT_ACTIVE        0   // EGC disabled
 #define EGC_ON_ALLOC_FAILURE  1   // run EGC on allocation failure
@@ -413,7 +416,12 @@ LUA_API void lua_setegcmode(lua_State *L, int mode, int limit);
 #define dbg_printf printf
 
 #endif
-extern void lua_debugbreak(void);
+
+#ifdef DEVELOPMENT_USE_GDB
+LUALIB_API void (lua_debugbreak)(void);
+#else
+#define lua_debugbreak() (void)(0)
+#endif
 
 // EGC operations modes
 #define EGC_NOT_ACTIVE        0   // EGC disabled

+ 46 - 21
app/lua53/lauxlib.c

@@ -649,6 +649,28 @@ LUALIB_API void luaL_unref (lua_State *L, int t, int ref) {
   }
 }
 
+
+LUALIB_API void (luaL_reref) (lua_State *L, int t, int *ref) {
+  int reft;
+/*
+ * If the ref is positive and the entry in table t exists then
+ * overwrite the value otherwise fall through to luaL_ref()
+ */
+  if (ref) {
+    if (*ref >= 0) {
+      t = lua_absindex(L, t);
+      lua_rawgeti(L, t, *ref);
+      reft = lua_type(L, -1);
+      lua_pop(L, 1);
+      if (reft != LUA_TNIL) {
+        lua_rawseti(L, t, *ref);
+        return;
+      }
+    }
+    *ref = luaL_ref(L, t);
+  }
+}
+
 /* }====================================================== */
 
 
@@ -1127,26 +1149,23 @@ static int errhandler_aux (lua_State *L) {
   return 0;
 }
 
-
 /*
-** Error handler for luaL_pcallx()
+** Error handler for luaL_pcallx(), called from the lua_pcall() with a single 
+** argument -- the thrown error. This plus depth=2 is passed to debug.traceback()
+** to convert into an error message which it handles in a separate posted task.
 */
 static int errhandler (lua_State *L) {
-  if (lua_isnil(L, -1))
-    return 0;
-  if (lua_type(L, -1) != LUA_TSTRING) {     /* is error object not a string? */
-    if (luaL_callmeta(L, 1, "__tostring") &&     /* does it have a metamethod */
-        lua_type(L, -1) == LUA_TSTRING) {          /* that produces a string? */
-      lua_remove(L, 1);                              /* replace ToS with this */
-    } else if (!lua_isnil(L,-1)) {
-      lua_pushfstring(L, "(error object is a %s value)", luaL_typename(L, 1));
-      lua_remove(L, 1);        /* replace ToS with error object is type value */
-    }
+  lua_getglobal(L, "debug");
+  lua_getfield(L, -1, "traceback");
+  if (lua_isfunction(L, -1)) {
+    lua_insert(L, 1);                 /* insert tracback function above error */
+    lua_pop(L, 1);                              /* dump the debug table entry */
+    lua_pushinteger(L, 2);                /* skip this function and traceback */
+    lua_call(L, 2, 1);      /* call debug.traceback and return it as a string */
+    lua_pushcclosure(L, errhandler_aux, 1);       /* report with str as upval */
+    luaL_posttask(L, LUA_TASK_HIGH);
   }
-  luaL_traceback(L, L, lua_tostring(L, 1), 1); /* append a standard traceback */
-  lua_pushcclosure(L, errhandler_aux, 1);         /* report with str as upval */
-  luaL_posttask(L, LUA_TASK_HIGH);
-  return 1;  /* return the traceback */
+  return 0;
 }
 
 /*
@@ -1155,11 +1174,17 @@ static int errhandler (lua_State *L) {
 */
 LUALIB_API int luaL_pcallx (lua_State *L, int narg, int nres) {
   int status;
-  int base = lua_gettop(L) - narg;  /* function index */
-  lua_pushcfunction(L, errhandler);  /* push message handler */
-  lua_insert(L, base);  /* put it under function and args */
+  int base = lua_gettop(L) - narg;                          /* function index */
+  lua_pushcfunction(L, errhandler);                   /* push message handler */
+  lua_insert(L, base);                      /* put it under function and args */
   status = lua_pcall(L, narg, nres, base);
-  lua_remove(L, base);  /* remove message handler from the stack */
+  lua_remove(L, base);               /* remove message handler from the stack */
+  if (status != LUA_OK && status != LUA_ERRRUN) {  
+    lua_gc(L, LUA_GCCOLLECT, 0);   /* call onerror directly if handler failed */
+    lua_pushliteral(L, "out of memory");
+    lua_pushcclosure(L, errhandler_aux, 1);            /* report EOM as upval */
+    luaL_posttask(L, LUA_TASK_HIGH);
+    }
   return status;
 }
 
@@ -1180,7 +1205,7 @@ static void do_task (platform_task_param_t task_fn_ref, uint8_t prio) {
   luaL_checktype(L, -1, LUA_TFUNCTION);
   luaL_unref(L, LUA_REGISTRYINDEX, (int) task_fn_ref);
   lua_pushinteger(L, prio);
-  luaL_pcallx (L, 1, 0);
+  luaL_pcallx(L, 1, 0);
 }
 
 /*

+ 2 - 0
app/lua53/lauxlib.h

@@ -79,6 +79,8 @@ LUALIB_API int (luaL_execresult) (lua_State *L, int stat);
 
 LUALIB_API int (luaL_ref) (lua_State *L, int t);
 LUALIB_API void (luaL_unref) (lua_State *L, int t, int ref);
+#define luaL_unref2(l,t,r) luaL_unref(L, (t), (r)); r = LUA_NOREF
+LUALIB_API void (luaL_reref) (lua_State *L, int t, int *ref);
 
 LUALIB_API int (luaL_loadfilex) (lua_State *L, const char *filename,
                                                const char *mode);

+ 0 - 1
app/lua53/llimits.h

@@ -83,7 +83,6 @@ typedef LUAI_UACNUMBER l_uacNumber;
 typedef LUAI_UACINT l_uacInt;
 
 #if defined(DEVELOPMENT_USE_GDB) && !defined(lua_assert)
-extern void (lua_debugbreak)(void);
 # define lua_assert(c) ((c) ? (void) 0 : lua_debugbreak())
 #endif
 

+ 6 - 4
app/lua53/lnodemcu.c

@@ -202,8 +202,6 @@ LUALIB_API void lua_debugbreak(void) {
   asm("break 0,0" ::);
 #endif
 }
-#else
-#define lua_debugbreak() (void)(0)
 #endif
 
 //== NodeMCU lua.h API extensions ============================================//
@@ -567,7 +565,7 @@ LUAI_FUNC int luaN_init (lua_State *L) {
 #define getfield(L,t,f) \
   lua_getglobal(L, #t); luaL_getmetafield( L, 1, #f ); lua_remove(L, -2);
 
-LUAI_FUNC int  luaN_reload_reboot (lua_State *L) {
+LUAI_FUNC int  lua_lfsreload (lua_State *L) {
   int n = 0;
 #ifdef LUA_USE_ESP
   size_t l;
@@ -579,6 +577,10 @@ LUAI_FUNC int  luaN_reload_reboot (lua_State *L) {
 #endif
   lua_settop(L, 1);
   lua_getglobal(L, "file");
+  if (lua_isnil(L, 2)) {
+    lua_pushstring(L, "No file system mounted");
+    return 1;
+  }
   lua_getfield(L, 2, "exists");
   lua_pushstring(L, img + off);
   lua_call(L, 1, 1);
@@ -594,7 +596,7 @@ LUAI_FUNC int  luaN_reload_reboot (lua_State *L) {
   return 1;
 }
 
-LUAI_FUNC int  luaN_index (lua_State *L) {
+LUAI_FUNC int  lua_lfsindex (lua_State *L) {
   lua_settop(L,1);
   if (lua_isstring(L, 1)){
     lua_getglobal(L, "LFS");

+ 4 - 51
app/lua53/lnodemcu.h

@@ -1,5 +1,5 @@
 /*
- * NodeMCU extensions to Lua for readonly Flash memory support
+ * NodeMCU extensions to Lua 5.3 for readonly Flash memory support
  */
 #ifndef lnodemcu_h
 #define lnodemcu_h
@@ -27,7 +27,7 @@
 #define LRO_FLOATVAL(v)      {{.n = v}, LUA_TNUMFLT}
 #define LRO_ROVAL(v)         {{.gc = cast(GCObject *, &(v ## _ROTable))}, LUA_TTBLROF}
 
-#define LROT_MARKED          0 //<<<<<<<<<<*** TBD *** >>>>>>>>>>>
+#define LROT_MARKED          0 //<<<<<<<<<<  *** TBD *** >>>>>>>>>>>
 
 #define LROT_FUNCENTRY(n,f)  {LRO_STRKEY(#n), LRO_FUNCVAL(f)},
 #define LROT_LUDENTRY(n,x)   {LRO_STRKEY(#n), LRO_LUDATA(x)},
@@ -65,65 +65,18 @@
 #define LROT_MASK_NEWINDEX   LROT_MASK(NEWINDEX)
 #define LROT_MASK_GC_INDEX   (LROT_MASK_GC | LROT_MASK_INDEX)
 
-/* Maximum length of a rotable name and of a string key*/
-#define LUA_MAX_ROTABLE_NAME 32
+
+#define LUA_MAX_ROTABLE_NAME 32  /* Maximum length of a rotable name and of a string key*/
 
 #ifdef LUA_CORE
 
 #include "lstate.h"
 #include "lzio.h"
 
-typedef struct FlashHeader LFSHeader;
-/*
-** The LFSHeader uses offsets rather than pointers to avoid 32 vs 64 bit issues
-** during host compilation.  The offsets are in units of lu_int32's and NOT
-** size_t, though clearly any internal pointers are of the size_t for the
-** executing architectures: 4 or 8 byte.  Likewise recources are size_t aligned
-** so LFS regions built for 64-bit execution will have 4-byte alignment packing
-** between resources.
-*/
-struct FlashHeader{
-  lu_int32 flash_sig;     /* a standard fingerprint identifying an LFS image */
-  lu_int32 flash_size;    /* Size of LFS image in bytes */
-  lu_int32 seed;          /* random number seed used in LFS */
-  lu_int32 timestamp;     /* timestamp of LFS build */
-  lu_int32 nROuse;        /* number of elements in ROstrt */
-  lu_int32 nROsize;       /* size of ROstrt */
-  lu_int32 oROhash;       /* offset of TString ** ROstrt hash */
-  lu_int32 protoROTable;  /* offset of master ROTable for proto lookup */
-  lu_int32 protoHead;     /* offset of linked list of Protos in LFS */
-  lu_int32 shortTShead;   /* offset of linked list of short TStrings in LFS */
-  lu_int32 longTShead;    /* offset of linked list of long TStrings in LFS */
-  lu_int32 reserved;
-};
-
-#ifdef LUA_USE_HOST
-extern void *LFSregion;
-LUAI_FUNC void luaN_setabsolute(lu_int32 addr);
-#endif
-
-#define FLASH_FORMAT_VERSION ( 2 << 8)
-#define FLASH_SIG_B1          0x06
-#define FLASH_SIG_B2          0x02
-#define FLASH_SIG_PASS2       0x0F
-#define FLASH_FORMAT_MASK    0xF00
-#define FLASH_SIG_B2_MASK     0x04
-#define FLASH_SIG_ABSOLUTE    0x01
-#define FLASH_SIG_IN_PROGRESS 0x08
-#define FLASH_SIG  (0xfafaa050 | FLASH_FORMAT_VERSION)
-
-#define FLASH_FORMAT_MASK    0xF00
-
 LUAI_FUNC int luaN_init (lua_State *L);
-LUAI_FUNC int luaN_flashSetup (lua_State *L);
-
-LUAI_FUNC int  luaN_reload_reboot (lua_State *L);
-LUAI_FUNC int  luaN_index (lua_State *L);
-
 LUAI_FUNC void *luaN_writeFlash (void *data, const void *rec, size_t n);
 LUAI_FUNC void luaN_flushFlash (void *);
 LUAI_FUNC void luaN_setFlash (void *, unsigned int o);
 
-
 #endif
 #endif

+ 31 - 41
app/lua53/lua.c

@@ -64,9 +64,10 @@ static void l_print (lua_State *L, int n) {
     luaL_checkstack(L, LUA_MINSTACK, "too many results to print");
     lua_getglobal(L, "print");
     lua_insert(L, -n-1);
-    if (lua_pcall(L, n, 0, 0) != LUA_OK)
-       lua_writestringerror( "error calling 'print' (%s)\n",
-                            lua_tostring(L, -1));
+    if (lua_pcall(L, n, 0, 0) != LUA_OK) {
+      lua_writestringerror("error calling 'print' (%s)\n", lua_tostring(L, -1));
+      lua_settop(L, -n-1);
+    }
   }
 }
 
@@ -75,52 +76,43 @@ static void l_print (lua_State *L, int n) {
 ** Message handler is used with all chunks calls. Returns the traceback on ToS
 */
 static int msghandler (lua_State *L) {
-  const char *msg = lua_tostring(L, 1);
-  if (msg == NULL) {                         /* is error object not a string? */
-    if (luaL_callmeta(L, 1, "__tostring") &&     /* does it have a metamethod */
-        lua_type(L, -1) == LUA_TSTRING)            /* that produces a string? */
-      return 1;                                        /* that is the message */
-    msg = lua_pushfstring(L, "(error object is a %s value)",
-                               luaL_typename(L, 1));
-    lua_remove(L, -2);                 /* otherwise swap with printable error */
+  lua_getglobal(L, "debug");
+  lua_getfield(L, -1, "traceback");
+  if (lua_isfunction(L, -1)) {
+    lua_insert(L, 1);                 /* insert tracback function above error */
+    lua_pop(L, 1);                              /* dump the debug table entry */
+    lua_pushinteger(L, 2);                /* skip this function and traceback */
+    lua_call(L, 2, 1);      /* call debug.traceback and return it as a string */
   }
-#ifdef LUA_VERSION_51
-  lua_getglobal(L,"debug");
-  lua_getfield(L, -1,"traceback");
-  lua_insert(L, 1);                                     /* pass error message */
-  lua_pop(L, 1);
-  lua_pushinteger(L, 2);                  /* skip this function and traceback */
-  lua_call(L, 2, 1);                                  /* call debug.traceback */
-#else /* LUA_VERSION_53 */
-  luaL_traceback(L, L, msg, 1);                /* append a standard traceback */
-#endif
   return 1;  /* return the traceback */
 }
 
 
 /*
-** Interface to 'lua_pcall', which sets appropriate message function
-** and error handler. Used to run all chunks.
+** Interface to 'lua_pcall', which sets appropriate message function and error 
+** handler. Used to run all chunks.  Results or error traceback left on stack.
+** This function is interactive so unlike lua_pcallx(), the error is sent direct
+** to the print function and erroring does not trigger an on error restart. 
 */
-static int docall (lua_State *L, int narg, int nres) {
+static int docall (lua_State *L, int narg) {
   int status;
   int base = lua_gettop(L) - narg;                          /* function index */
   lua_pushcfunction(L, msghandler);                   /* push message handler */
   lua_insert(L, base);                         /* put it under chunk and args */
-  status = lua_pcall(L, narg, (nres ? 0 : LUA_MULTRET), base);
+  status = lua_pcall(L, narg, LUA_MULTRET, base);
   lua_remove(L, base);               /* remove message handler from the stack */
   /* force a complete garbage collection in case of errors */
   if (status != 0) lua_gc(L, LUA_GCCOLLECT, 0);
   return status;
 }
 
+
+#ifndef DISABLE_STARTUP_BANNER
 static void print_version (lua_State *L) {
-  #ifndef DISABLE_STARTUP_BANNER
   lua_writestringerror( "\n" NODE_VERSION " build " BUILD_DATE
                         " powered by " LUA_RELEASE " on SDK %s\n", SDK_VERSION);
-  #endif
 }
-
+#endif
 
 /*
 ** Returns the string to be used as a prompt by the interpreter.
@@ -164,7 +156,6 @@ static void l_create_stdin (lua_State *L);
 ** other C API and Lua functions might be executed as tasks between lines in
 ** a multiline, so a standard luaL_ref() registry entry is used instead.
 */
-//// TODO SHOLD this have an boot return false if pipe empty else nil
 static void dojob (lua_State *L) {
   static int MLref = LUA_NOREF;        /* Lua Reg entry for cached multi-line */
   int status;
@@ -204,21 +195,18 @@ static void dojob (lua_State *L) {
     }
     /* Execute the compiled chunk of successful */
     if (status == 0)
-      status = docall(L, 0, 0);
+      status = docall(L, 0);
     /* print any returned results or error message */
-    if (status && !lua_isnil(L, -1))
-      lua_writestringerror("Lua error: %s\n", lua_tostring(L, -1));
-    if (status == 0 && lua_gettop(L) - 1)
-      l_print(L, lua_gettop(L) - 1);
-
-    lua_settop(L, 2);
-    if (status != 0) lua_gc(L, LUA_GCCOLLECT, 0);
+    if (status && !lua_isnil(L, -1)) {
+      lua_pushliteral(L, "Lua error: ");
+      lua_insert(L , -2);
+    }
+    l_print(L, lua_gettop(L) - 1);        /* print error or results one stack */
   }
 
   prompt = get_prompt(L, MLref!= LUA_NOREF ? 0 : 1);
   input_setprompt(prompt);
   lua_writestring(prompt,strlen(prompt));
-  lua_pushnil(L);
 }
 
 
@@ -241,7 +229,9 @@ static int pmain (lua_State *L) {
   input_setup(LUA_MAXINPUT, get_prompt(L, 1));
   lua_input_string(" \n", 2);               /* queue CR to issue first prompt */
 
+#ifndef DISABLE_STARTUP_BANNER
   print_version(L);
+#endif
  /*
   * And last of all, kick off application initialisation.  Note that if
   * LUA_INIT_STRING is a file reference and the file system is uninitialised
@@ -252,7 +242,7 @@ static int pmain (lua_State *L) {
            luaL_loadfile(L, init+1) :
            luaL_loadbuffer(L, init, strlen(init), "=INIT");
   if (status == LUA_OK)
-    status = docall(L, 0, 0);
+    status = docall(L, 0);
   if (status != LUA_OK)
     l_print (L, 1);
   return 0;
@@ -274,7 +264,7 @@ int lua_main (void) {
   }
   globalL = L;
   lua_pushcfunction(L, pmain);
-  if (docall(L, 0, 0) != LUA_OK) {
+  if (docall(L, 0) != LUA_OK) {
     if (strstr(lua_tostring(L, -1),"!LFSrestart!")) {
       lua_close(L);
       return 1;                         /* non-zero return to flag LFS reload */
@@ -337,7 +327,7 @@ static int l_read_stdin (lua_State *L) {
     /* likewise if not CR terminated, then unread and ditto */
     lua_insert(L, 1);                   /* insert false return above the pipe */
     lua_getfield(L, 2, "unread");
-    lua_insert(L, 1);                    /* insert pipe.unread above the pipe */
+    lua_insert(L, 2);                    /* insert pipe.unread above the pipe */
     lua_call(L, 2, 0);                                   /* pobj:unread(line) */
     return 1;                                                 /* return false */
   }

+ 7 - 7
app/lua53/lua.h

@@ -467,16 +467,15 @@ typedef struct ROTable ROTable;
 typedef const struct ROTable_entry ROTable_entry;
 typedef size_t KeyCache;
 
-LUA_API void  (lua_pushrotable) (lua_State *L, const ROTable *p);
+LUA_API void (lua_pushrotable) (lua_State *L, const ROTable *p);
 LUA_API void (lua_createrotable) (lua_State *L, ROTable *t, const ROTable_entry *e, ROTable *mt);
 LUA_API lua_State *(lua_getstate) (void);
 LUA_API KeyCache *(lua_getcache) (int cl);
 LUA_API int (lua_getstrings) (lua_State *L, int opt);
 LUA_API int (lua_freeheap) (void);
 
-LUAI_FUNC int  luaN_flashSetup (lua_State *L);
-LUAI_FUNC int  luaN_reload_reboot (lua_State *L);
-LUAI_FUNC int  luaN_index (lua_State *L);
+LUAI_FUNC int  lua_lfsreload (lua_State *L);
+LUAI_FUNC int  lua_lfsindex (lua_State *L);
 
 #define luaN_freearray(L,a,l) luaM_freearray(L,a,l)
 
@@ -487,10 +486,11 @@ LUAI_FUNC int  luaN_index (lua_State *L);
 typedef struct Proto Proto;
 
 #ifdef DEVELOPMENT_USE_GDB
-LUALIB_API void lua_debugbreak(void);
-#define ASSERT(s) if (!(s)) {();}
+LUALIB_API void (lua_debugbreak)(void);
+#define ASSERT(s) if (!(s)) {lua_debugbreak();}
 #else
-#define ASSERT(s)
+#define lua_debugbreak() (void)(0)
+#define ASSERT(s) (void)(0)
 #endif
 
 LUAI_FUNC int luaG_stripdebug (lua_State *L, Proto *f, int level, int recv);

+ 42 - 0
app/lua53/lundump.h

@@ -48,4 +48,46 @@ LUAI_FUNC int luaU_DumpAllProtos(lua_State *L, const Proto *m, lua_Writer w,
                          void *data, int strip);
 
 LUAI_FUNC int luaU_undumpLFS(lua_State *L, ZIO *Z, int isabs);
+
+typedef struct FlashHeader LFSHeader;
+/*
+** The LFSHeader uses offsets rather than pointers to avoid 32 vs 64 bit issues
+** during host compilation.  The offsets are in units of lu_int32's and NOT
+** size_t, though clearly any internal pointers are of the size_t for the
+** executing architectures: 4 or 8 byte.  Likewise recources are size_t aligned
+** so LFS regions built for 64-bit execution will have 4-byte alignment packing
+** between resources.
+*/
+struct FlashHeader{
+  lu_int32 flash_sig;     /* a standard fingerprint identifying an LFS image */
+  lu_int32 flash_size;    /* Size of LFS image in bytes */
+  lu_int32 seed;          /* random number seed used in LFS */
+  lu_int32 timestamp;     /* timestamp of LFS build */
+  lu_int32 nROuse;        /* number of elements in ROstrt */
+  lu_int32 nROsize;       /* size of ROstrt */
+  lu_int32 oROhash;       /* offset of TString ** ROstrt hash */
+  lu_int32 protoROTable;  /* offset of master ROTable for proto lookup */
+  lu_int32 protoHead;     /* offset of linked list of Protos in LFS */
+  lu_int32 shortTShead;   /* offset of linked list of short TStrings in LFS */
+  lu_int32 longTShead;    /* offset of linked list of long TStrings in LFS */
+  lu_int32 reserved;
+};
+
+#ifdef LUA_USE_HOST
+extern void *LFSregion;
+LUAI_FUNC void luaN_setabsolute(lu_int32 addr);
+#endif
+
+#define FLASH_FORMAT_VERSION ( 2 << 8)
+#define FLASH_SIG_B1          0x06
+#define FLASH_SIG_B2          0x02
+#define FLASH_SIG_PASS2       0x0F
+#define FLASH_FORMAT_MASK    0xF00
+#define FLASH_SIG_B2_MASK     0x04
+#define FLASH_SIG_ABSOLUTE    0x01
+#define FLASH_SIG_IN_PROGRESS 0x08
+#define FLASH_SIG  (0xfafaa050 | FLASH_FORMAT_VERSION)
+
+#define FLASH_FORMAT_MASK    0xF00
+
 #endif

+ 1 - 1
app/modules/ads1115.c

@@ -499,7 +499,7 @@ static int ads1115_lua_readoutdone(void * param) {
     luaL_unref(L, LUA_REGISTRYINDEX, ads_ctrl->timer_ref);
     ads_ctrl->timer_ref = LUA_NOREF;
     read_common(ads_ctrl, raw, L);
-    lua_call(L, 4, 0);
+    luaL_pcallx(L, 4, 0);
 }
 
 // Read the conversion register from the ADC device

+ 1 - 1
app/modules/bme280.c

@@ -320,9 +320,9 @@ static void bme280_readoutdone (void *arg)
 	NODE_DBG("timer out\n");
 	lua_State *L = lua_getstate();
 	lua_rawgeti (L, LUA_REGISTRYINDEX, lua_connected_readout_ref);
-	lua_call (L, 0, 0);
 	luaL_unref (L, LUA_REGISTRYINDEX, lua_connected_readout_ref);
 	os_timer_disarm (&bme280_timer);
+	luaL_pcallx (L, 0, 0);
 }
 
 static int bme280_lua_startreadout(lua_State* L) {

+ 1 - 1
app/modules/bme680.c

@@ -430,9 +430,9 @@ static void bme280_readoutdone (void *arg)
 	NODE_DBG("timer out\n");
 	lua_State *L = lua_getstate();
 	lua_rawgeti (L, LUA_REGISTRYINDEX, lua_connected_readout_ref);
-	lua_call (L, 0, 0);
 	luaL_unref (L, LUA_REGISTRYINDEX, lua_connected_readout_ref);
 	os_timer_disarm (&bme680_timer);
+	luaL_pcallx (L, 0, 0);
 }
 
 static int bme680_lua_startreadout(lua_State* L) {

+ 1 - 1
app/modules/cron.c

@@ -200,7 +200,7 @@ static void cron_handle_time(uint8_t mon, uint8_t dom, uint8_t dow, uint8_t hour
     if ((ent->desc.min  & desc.min ) == 0) continue;
     lua_rawgeti(L, LUA_REGISTRYINDEX, ent->cb_ref);
     lua_rawgeti(L, LUA_REGISTRYINDEX, cronent_list[i]);
-    lua_call(L, 1, 0);
+    luaL_pcallx(L, 1, 0);
   }
 }
 

+ 2 - 1
app/modules/crypto.c

@@ -54,7 +54,8 @@ static int call_encoder( lua_State* L, const char *function ) {
   lua_getfield(L, -1, function);
   lua_insert(L, 1);    //move function below the argument
   lua_pop(L, 1);       //and dump the encoder rotable from stack.
-  lua_call(L,1,1);     // call encoder.xxx(string)
+  lua_call(L,1,1);     // Normal call encoder.xxx(string)
+                       // (errors thrown back to caller)
   return 1;
 }
 

+ 18 - 16
app/modules/dcc.c

@@ -53,13 +53,13 @@ void notifyDccReset(uint8_t hardReset ) {
   lua_State* L = lua_getstate();
   cbInit(L, DCC_RESET);
   cbAddFieldInteger(L, hardReset, "hardReset");
-  lua_call(L, 2, 0);
+  luaL_pcallx(L, 2, 0);
 }
 
 void notifyDccIdle(void) {
   lua_State* L = lua_getstate();
   cbInit(L, DCC_IDLE);
-  lua_call(L, 2, 0);
+  luaL_pcallx(L, 2, 0);
 }
 
 void notifyDccSpeed( uint16_t Addr, DCC_ADDR_TYPE AddrType, uint8_t Speed, DCC_DIRECTION Dir, DCC_SPEED_STEPS SpeedSteps ) {
@@ -70,7 +70,7 @@ void notifyDccSpeed( uint16_t Addr, DCC_ADDR_TYPE AddrType, uint8_t Speed, DCC_D
   cbAddFieldInteger(L, Speed, "Speed");
   cbAddFieldInteger(L, Dir, "Dir");
   cbAddFieldInteger(L, SpeedSteps, "SpeedSteps");
-  lua_call(L, 2, 0);
+  luaL_pcallx(L, 2, 0);
 }
 
 void notifyDccSpeedRaw( uint16_t Addr, DCC_ADDR_TYPE AddrType, uint8_t Raw) {
@@ -79,7 +79,7 @@ void notifyDccSpeedRaw( uint16_t Addr, DCC_ADDR_TYPE AddrType, uint8_t Raw) {
   cbAddFieldInteger(L, Addr, "Addr");
   cbAddFieldInteger(L, AddrType, "AddrType");
   cbAddFieldInteger(L, Raw, "Raw");
-  lua_call(L, 2, 0);
+  luaL_pcallx(L, 2, 0);
 }
 
 void notifyDccFunc( uint16_t Addr, DCC_ADDR_TYPE AddrType, FN_GROUP FuncGrp, uint8_t FuncState) {
@@ -89,7 +89,7 @@ void notifyDccFunc( uint16_t Addr, DCC_ADDR_TYPE AddrType, FN_GROUP FuncGrp, uin
   cbAddFieldInteger(L, AddrType, "AddrType");
   cbAddFieldInteger(L, FuncGrp, "FuncGrp");
   cbAddFieldInteger(L, FuncState, "FuncState");
-  lua_call(L, 2, 0);
+  luaL_pcallx(L, 2, 0);
 }
 
 void notifyDccAccTurnoutBoard( uint16_t BoardAddr, uint8_t OutputPair, uint8_t Direction, uint8_t OutputPower ) {
@@ -99,7 +99,7 @@ void notifyDccAccTurnoutBoard( uint16_t BoardAddr, uint8_t OutputPair, uint8_t D
   cbAddFieldInteger(L, OutputPair, "OutputPair");
   cbAddFieldInteger(L, Direction, "Direction");
   cbAddFieldInteger(L, OutputPower, "OutputPower");
-  lua_call(L, 2, 0);
+  luaL_pcallx(L, 2, 0);
 }
 
 void notifyDccAccTurnoutOutput( uint16_t Addr, uint8_t Direction, uint8_t OutputPower ) { 
@@ -108,28 +108,28 @@ void notifyDccAccTurnoutOutput( uint16_t Addr, uint8_t Direction, uint8_t Output
   cbAddFieldInteger(L, Addr, "Addr");
   cbAddFieldInteger(L, Direction, "Direction");
   cbAddFieldInteger(L, OutputPower, "OutputPower");
-  lua_call(L, 2, 0);
+  luaL_pcallx(L, 2, 0);
 }
 
 void notifyDccAccBoardAddrSet( uint16_t BoardAddr) { 
   lua_State* L = lua_getstate();
   cbInit(L, DCC_ACCESSORY);
   cbAddFieldInteger(L, BoardAddr, "BoardAddr");
-  lua_call(L, 2, 0);
+  luaL_pcallx(L, 2, 0);
 }
 
 void notifyDccAccOutputAddrSet( uint16_t Addr) { 
   lua_State* L = lua_getstate();
   cbInit(L, DCC_ACCESSORY);
   cbAddFieldInteger(L, Addr, "Addr");
-  lua_call(L, 2, 0);
+  luaL_pcallx(L, 2, 0);
 }
 
 void notifyDccSigOutputState( uint16_t Addr, uint8_t State) { 
   lua_State* L = lua_getstate();
   cbInit(L, DCC_ACCESSORY);
   cbAddFieldInteger(L, State, "State");
-  lua_call(L, 2, 0);
+  luaL_pcallx(L, 2, 0);
 }
 
 void notifyDccMsg( DCC_MSG * Msg ) { 
@@ -142,14 +142,14 @@ void notifyDccMsg( DCC_MSG * Msg ) {
     ets_sprintf(field, "Data%d", i);
     cbAddFieldInteger(L, Msg->Data[i], field);
   }
-  lua_call(L, 2, 0);
+  luaL_pcallx(L, 2, 0);
 }
 
 void notifyServiceMode(bool InServiceMode){ 
   lua_State* L = lua_getstate();
   cbInit(L, DCC_SERVICEMODE);
   cbAddFieldInteger(L, InServiceMode, "InServiceMode");
-  lua_call(L, 2, 0);
+  luaL_pcallx(L, 2, 0);
 }
 
 // CV handling
@@ -163,7 +163,8 @@ uint8_t notifyCVValid( uint16_t CV, uint8_t Writable ) {
   lua_newtable(L);
   cbAddFieldInteger(L, CV, "CV");
   cbAddFieldInteger(L, Writable, "Writable");
-  lua_call(L, 2, 1);
+  if (luaL_pcallx(L, 2, 1) != LUA_OK)
+    return 0;
   uint8 result = lua_tointeger(L, -1);
   lua_pop(L, 1);
   return result;
@@ -177,7 +178,8 @@ uint8_t notifyCVRead( uint16_t CV) {
   lua_pushinteger(L, CV_READ);
   lua_newtable(L);
   cbAddFieldInteger(L, CV, "CV");
-  lua_call(L, 2, 1);
+  if (luaL_pcallx(L, 2, 1) != LUA_OK)
+    return 0;;
   uint8 result = lua_tointeger(L, -1);
   lua_pop(L, 1);
   return result;
@@ -192,7 +194,7 @@ uint8_t notifyCVWrite( uint16_t CV, uint8_t Value) {
   lua_newtable(L);
   cbAddFieldInteger(L, CV, "CV");
   cbAddFieldInteger(L, Value, "Value");
-  lua_call(L, 2, 0);
+  luaL_pcallx(L, 2, 0);
   return Value;
 }
 
@@ -202,7 +204,7 @@ void notifyCVResetFactoryDefault(void) {
     return;
   lua_rawgeti(L, LUA_REGISTRYINDEX, CV_cb);
   lua_pushinteger(L, CV_RESET);
-  lua_call(L, 1, 0);
+  luaL_pcallx(L, 1, 0);
 }
 
 static int dcc_lua_setup(lua_State* L) {

+ 3 - 3
app/modules/enduser_setup.c

@@ -181,7 +181,7 @@ static void enduser_setup_debug(int line, const char *str)
   {
     lua_rawgeti(L, LUA_REGISTRYINDEX, state->lua_dbg_cb_ref);
     lua_pushfstring(L, "%d: \t%s", line, str);
-    lua_call(L, 1, 0);
+    luaL_pcallx(L, 1, 0);
   }
 }
 
@@ -196,7 +196,7 @@ static void enduser_setup_error(int line, const char *str, int err)
     lua_rawgeti (L, LUA_REGISTRYINDEX, state->lua_err_cb_ref);
     lua_pushnumber(L, err);
     lua_pushfstring(L, "%d: \t%s", line, str);
-    lua_call (L, 2, 0);
+    luaL_pcallx (L, 2, 0);
   }
 }
 
@@ -209,7 +209,7 @@ static void enduser_setup_connected_callback()
   if (state != NULL && state->lua_connected_cb_ref != LUA_NOREF)
   {
     lua_rawgeti(L, LUA_REGISTRYINDEX, state->lua_connected_cb_ref);
-    lua_call(L, 0, 0);
+    luaL_pcallx(L, 0, 0);
   }
 }
 

+ 2 - 1
app/modules/file.c

@@ -60,7 +60,8 @@ static sint32_t file_rtc_cb( vfs_time *tm )
     lua_State *L = lua_getstate();
 
     lua_rawgeti( L, LUA_REGISTRYINDEX, rtc_cb_ref );
-    lua_call( L, 0, 1 );
+    if (luaL_pcallx( L, 0, 1 ) != LUA_OK)
+      return res;
 
     if (lua_type( L, lua_gettop( L ) ) == LUA_TTABLE) {
       table2tm( L, tm );

+ 3 - 5
app/modules/gpio.c

@@ -77,7 +77,8 @@ static void gpio_intr_callback_task (task_param_t param, uint8 priority)
         then = system_get_time() & 0x7fffffff;
       }
 
-      lua_call(L, 3, 0);
+      if(luaL_pcallx(L, 3, 0) != LUA_OK)
+        return;
     }
 
     if (INTERRUPT_TYPE_IS_LEVEL(pin_int_type[pin])) {
@@ -237,10 +238,7 @@ static void seroutasync_done (task_param_t arg)
     lua_rawgeti (L, LUA_REGISTRYINDEX, serout.lua_done_ref);
     luaL_unref (L, LUA_REGISTRYINDEX, serout.lua_done_ref);
     serout.lua_done_ref = LUA_NOREF;
-    if (lua_pcall(L, 0, 0, 0)) {
-      // Uncaught Error. Print instead of sudden reset
-      luaL_error(L, "error: %s", lua_tostring(L, -1));
-    }
+    luaL_pcallx(L, 0, 0);
   }
 }
 

+ 1 - 1
app/modules/gpio_pulse.c

@@ -463,7 +463,7 @@ static void gpio_pulse_task(os_param_t param, uint8_t prio)
     active_pulser_ref = LUA_NOREF;
     luaL_unref(L, LUA_REGISTRYINDEX, pulser_ref);
 
-    lua_call(L, rc, 0);
+    luaL_pcallx(L, rc, 0);
   }
 }
 

+ 1 - 1
app/modules/http.c

@@ -100,7 +100,7 @@ static void http_callback( char * response, int http_status, char ** full_respon
     luaL_unref(L, LUA_REGISTRYINDEX, http_callback_registry);
     http_callback_registry = LUA_NOREF;
 
-    lua_call(L, 3, 0); // With 3 arguments and 0 result
+    luaL_pcallx(L, 3, 0); // With 3 arguments and 0 result
   }
 }
 

+ 1 - 1
app/modules/hx711.c

@@ -258,7 +258,7 @@ static void hx711_task(platform_task_param_t param, uint8_t prio)
 
     control->freed = param;
 
-    lua_call(L, 3, 0);
+    luaL_pcallx(L, 3, 0);
   }
 }
 #endif

+ 52 - 7
app/modules/node.c

@@ -21,6 +21,39 @@
 #define CPU80MHZ 80
 #define CPU160MHZ 160
 
+#define DELAY2SEC 2000
+
+static void restart_callback(void *arg) {
+  UNUSED(arg);
+  system_restart();
+}
+static int default_onerror(lua_State *L) {
+  static os_timer_t restart_timer = {0};
+  /* 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 */
+  if (!restart_timer.timer_func) {
+    os_timer_setfn(&restart_timer, restart_callback, NULL);
+		os_timer_arm(&restart_timer, DELAY2SEC, 0);
+  }
+  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: startupcommand(string)
 static int node_startupcommand( lua_State* L ) {
   size_t l, lrcr;
@@ -229,12 +262,17 @@ static int node_input( lua_State* L ) {
   lua_rawgeti(L, -1, 1);             /* get the pipe_write func from stdin[1] */
   lua_insert(L, -2);                           /* and move above the pipe ref */
   lua_pushvalue(L, 1);
-  lua_call(L, 2, 0);                                     /* stdin:write(line) */
+  lua_call(L, 2, 0);        /* stdin:write(line); errors are thrown to caller */
   return 0;
 }
 
 static int serial_debug = 1;
 
+/*
+** Output redirector. Note that panics in the output callback cannot be processed
+** using luaL_pcallx() as this would create an infinite error loop, so they are
+** reported direct to the UART.
+*/
 void output_redirect(const char *str, size_t l) {
   lua_State *L = lua_getstate();
   int n = lua_gettop(L);
@@ -247,8 +285,10 @@ void output_redirect(const char *str, size_t l) {
     lua_rawgeti(L, -1, 1);          /* get the pipe_write func from stdout[1] */
     lua_insert(L, -2);                         /* and move above the pipe ref */
     lua_pushlstring(L, str, l);
-    lua_call(L, 2, 0);                               /* Reg.stdout:write(str) */
-
+    if (lua_pcall(L, 2, 0, 0) != LUA_OK) {           /* Reg.stdout:write(str) */
+      lua_writestringerror("error calling stdout:write(%s)\n", lua_tostring(L, -1));
+      system_restart();
+    }
   } else { /* reg.stdout == nil */
     uart0_sendStrn(str, l);
   }
@@ -266,7 +306,7 @@ static int node_output( lua_State* L )
     lua_pushcfunction(L, pipe_create);
     lua_insert(L, 1);
     lua_pushinteger(L, LUA_TASK_MEDIUM);
-    lua_call(L, 2, 1);             /* T[1] = pipe.create(CB, medium_priority) */
+    lua_call(L, 2, 1);      /* Any pipe.create() errors thrown back to caller */
   } else {    // remove the stdout pipe
     lua_pop(L,1);
     lua_pushnil(L);                                             /* T[1] = nil */
@@ -786,8 +826,9 @@ LROT_BEGIN(node, NULL, 0)
   LROT_FUNCENTRY( heap, node_heap )
   LROT_FUNCENTRY( info, node_info )
   LROT_TABENTRY( task, node_task )
-  LROT_FUNCENTRY( flashreload, luaN_reload_reboot )
-  LROT_FUNCENTRY( flashindex, luaN_index )
+  LROT_FUNCENTRY( flashreload, lua_lfsreload )
+  LROT_FUNCENTRY( flashindex, lua_lfsindex )
+  LROT_FUNCENTRY( setonerror, node_setonerror )
   LROT_FUNCENTRY( startupcommand, node_startupcommand )
   LROT_FUNCENTRY( restart, node_restart )
   LROT_FUNCENTRY( dsleep, node_deepsleep )
@@ -831,5 +872,9 @@ LROT_BEGIN(node, NULL, 0)
 //  LROT_FUNCENTRY( dsleepsetoption, node_deepsleep_setoption )
 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);

+ 1 - 1
app/modules/pcm.c

@@ -27,7 +27,7 @@ static void dispatch_callback( lua_State *L, int self_ref, int cb_ref, int retur
   if (cb_ref != LUA_NOREF) {
     lua_rawgeti( L, LUA_REGISTRYINDEX, cb_ref );
     lua_rawgeti( L, LUA_REGISTRYINDEX, self_ref );
-    lua_call( L, 1, returns );
+    luaL_pcallx( L, 1, returns );
   }
 }
 

+ 1 - 1
app/modules/pipe.c

@@ -185,7 +185,7 @@ static int pipe_write_and_read_poster (lua_State *L) {
     lua_replace(L, UVstate);
     lua_pushvalue(L, UVfunc);                              /* Lua CB function */
     lua_pushvalue(L, UVpipe);                                   /* pipe table */
-    lua_call(L, 1, 1);
+    lua_call(L, 1, 1);                    /* Errors are thrown back to caller */
    /*
     * On return from the Lua CB, the task is never reposted if the pipe is empty.
     * If it is not empty then the Lua CB return status determines when reposting

+ 1 - 1
app/modules/rotary.c

@@ -114,7 +114,7 @@ static void callback_callOne(lua_State* L, int cb, int mask, int arg, uint32_t t
     lua_pushinteger(L, arg);
     lua_pushinteger(L, time);
 
-    lua_call(L, 3, 0);
+    luaL_pcallx(L, 3, 0);
   }
 }
 

+ 2 - 2
app/modules/sntp.c

@@ -211,7 +211,7 @@ static void handle_error (lua_State *L, ntp_err_t err, const char *msg)
     lua_pushinteger (L, err);
     lua_pushstring (L, msg);
     cleanup (L);
-    lua_call (L, 2, 0);
+    luaL_pcallx (L, 2, 0);
   }
   else
     cleanup (L);
@@ -319,7 +319,7 @@ static void sntp_handle_result(lua_State *L) {
 
   if (have_cb)
   {
-    lua_call (L, 4, 0);
+    luaL_pcallx (L, 4, 0);
   }
 }
 

+ 1 - 1
app/modules/softuart.c

@@ -287,7 +287,7 @@ static void softuart_rx_callback(task_param_t arg)
 	}
 	lua_pushlstring(L, softuart_rx_buffer, buffer_lenght);
 	softuart->armed = 1;
-	lua_call(L, 1, 0);
+	luaL_pcallx(L, 1, 0);
 }
 
 // Arguments: event name, minimum buffer filled to run callback, callback function

+ 1 - 1
app/modules/somfy.c

@@ -92,7 +92,7 @@ static void somfy_transmissionDone (task_param_t arg)
     lua_rawgeti (L, LUA_REGISTRYINDEX, lua_done_ref);
     luaL_unref (L, LUA_REGISTRYINDEX, lua_done_ref);
     lua_done_ref = LUA_NOREF;
-    lua_call (L, 0, 0);
+    luaL_pcallx (L, 0, 0);
 }
 
 static void ICACHE_RAM_ATTR sendCommand(os_param_t p) {

+ 1 - 1
app/modules/switec.c

@@ -47,7 +47,7 @@ static void callback_execute(lua_State* L, unsigned int id)
     lua_rawgeti(L, LUA_REGISTRYINDEX, callback);
     callback_free(L, id);
 
-    lua_call(L, 0, 0);
+    luaL_pcallx(L, 0, 0);
   }
 }
 

+ 1 - 1
app/modules/tcs34725.c

@@ -188,7 +188,7 @@ uint8_t tcs34725EnableDone()
 	lua_rawgeti(L, LUA_REGISTRYINDEX, cb_tcs_en); // Get the callback to call
 	luaL_unref(L, LUA_REGISTRYINDEX, cb_tcs_en); // Unregister the callback to avoid leak
 	cb_tcs_en = LUA_NOREF;
-	lua_call(L, 0, 0);
+	luaL_pcallx(L, 0, 0);
 
 	return 0;
 }

+ 8 - 9
app/modules/tls.c

@@ -10,8 +10,9 @@
 
 #include <string.h>
 #include <stddef.h>
-
 #include <stdint.h>
+#include <ctype.h>
+
 #include "mem.h"
 #include "lwip/ip_addr.h"
 #include "espconn.h"
@@ -463,12 +464,11 @@ static const char *fill_page_with_pem(lua_State *L, const unsigned char *flash_m
 static int tls_cert_auth(lua_State *L)
 {
   if (ssl_client_options.cert_auth_callback != LUA_NOREF) {
-    lua_unref(L, ssl_client_options.cert_auth_callback);
+    luaL_unref(L, LUA_REGISTRYINDEX, ssl_client_options.cert_auth_callback);
     ssl_client_options.cert_auth_callback = LUA_NOREF;
   }
-  if ((lua_type(L, 1) == LUA_TFUNCTION)
-      || (lua_type(L, 1) == LUA_TLIGHTFUNCTION)) {
-    ssl_client_options.cert_auth_callback = lua_ref(L, 1);
+  if (lua_type(L, 1) == LUA_TFUNCTION) {
+    ssl_client_options.cert_auth_callback = luaL_ref(L, LUA_REGISTRYINDEX);
     lua_pushboolean(L, true);
     return 1;
   }
@@ -518,12 +518,11 @@ static int tls_cert_auth(lua_State *L)
 static int tls_cert_verify(lua_State *L)
 {
   if (ssl_client_options.cert_verify_callback != LUA_NOREF) {
-    lua_unref(L, ssl_client_options.cert_verify_callback);
+    luaL_unref(L, LUA_REGISTRYINDEX, ssl_client_options.cert_verify_callback);
     ssl_client_options.cert_verify_callback = LUA_NOREF;
   }
-  if ((lua_type(L, 1) == LUA_TFUNCTION)
-      || (lua_type(L, 1) == LUA_TLIGHTFUNCTION)) {
-    ssl_client_options.cert_verify_callback = lua_ref(L, 1);
+  if (lua_type(L, 1) == LUA_TFUNCTION) {
+    ssl_client_options.cert_verify_callback = luaL_ref(L, LUA_REGISTRYINDEX);
     lua_pushboolean(L, true);
     return 1;
   }

+ 98 - 192
app/modules/tmr.c

@@ -5,48 +5,7 @@
 #pragma GCC diagnostic ignored "-Wunused-parameter"
 #endif
 
-/*-------------------------------------
-NEW TIMER API
----------------------------------------
-
-tmr.wdclr() -- not changed
-tmr.now()   -- not changed
-tmr.time()  -- not changed
-tmr.delay() -- not changed
-tmr.alarm() -- not changed
-tmr.stop()  -- changed, see below. use tmr.unregister for old functionality
-
-tmr.register(ref, interval, mode, function)
-	bind function with timer and set the interval in ms
-	the mode can be:
-		tmr.ALARM_SINGLE for a single run alarm
-		tmr.ALARM_SEMI for a multiple single run alarm
-		tmr.ALARM_AUTO for a repating alarm
-	tmr.register does NOT start the timer
-	tmr.alarm is a tmr.register & tmr.start macro
-tmr.unregister(ref)
-	stop alarm, unbind function and clean up memory
-	not needed for ALARM_SINGLE, as it unregisters itself
-tmr.start(ref)
-	ret: bool
-	start a alarm, returns true on success
-tmr.stop(ref)
-	ret: bool
-	stops a alarm, returns true on success
-	this call dose not free any memory, to do so use tmr.unregister
-	stopped alarms can be started with start
-tmr.interval(ref, interval)
-	set alarm interval, running alarm will be restarted
-tmr.state(ref)
-	ret: (bool, int) or nil
-	returns alarm status (true=started/false=stopped) and mode
-	nil if timer is unregistered
-tmr.softwd(int)
-	set a negative value to stop the timer
-	any other value starts the timer, when the
-	countdown reaches zero, the device restarts
-	the timer units are seconds
-*/
+/* See docs/modules/tmr.md for documentaiton o current API */
 
 #include "module.h"
 #include "lauxlib.h"
@@ -55,40 +14,40 @@ tmr.softwd(int)
 #include "user_interface.h"
 #include "pm/swtimer.h"
 
-#define TIMER_MODE_OFF 3
 #define TIMER_MODE_SINGLE 0
-#define TIMER_MODE_SEMI 2
-#define TIMER_MODE_AUTO 1
+#define TIMER_MODE_AUTO   1
+#define TIMER_MODE_SEMI   2
+#define TIMER_MODE_OFF    3
 #define TIMER_IDLE_FLAG (1<<7)
 
-
 #define STRINGIFY_VAL(x) #x
 #define STRINGIFY(x) STRINGIFY_VAL(x)
 
 // assuming system_timer_reinit() has *not* been called
-#define MAX_TIMEOUT_DEF 6870947  //SDK 1.5.3 limit (0x68D7A3)
+#define MAX_TIMEOUT_DEF 0x68D7A3  // SDK specfied limit
 
 static const uint32 MAX_TIMEOUT=MAX_TIMEOUT_DEF;
 static const char* MAX_TIMEOUT_ERR_STR = "Range: 1-"STRINGIFY(MAX_TIMEOUT_DEF);
 
 typedef struct{
 	os_timer_t os;
-	sint32_t lua_ref;	/* Reference to the callback function */
-	sint32_t self_ref;	/* Reference to this structure as userdata */
+	sint32_t lua_ref;	  /* Reference to registered callback function */
+	sint32_t self_ref;	/* Reference to UD registered slot */
 	uint32_t interval;
 	uint8_t mode;
-}timer_struct_t;
-typedef timer_struct_t* tmr_t;
+} tmr_t;
 
 // The previous implementation extended the rtc counter to 64 bits, and then
 // applied rtc2sec with the current calibration value to that 64 bit value.
-// This means that *ALL* clock ticks since bootup are counted with the *current*
-// clock period. In extreme cases (long uptime, sudden temperature change), this
-// could result in tmr.time() going backwards....
-// This implementation instead applies rtc2usec to short time intervals only (the
-// longest being around 1 second), and then accumulates the resulting microseconds
-// in a 64 bit counter. That's guaranteed to be monotonic, and should be a lot closer
-// to representing an actual uptime.
+// This means that *ALL* clock ticks since bootup are counted with the
+// *current* clock period. In extreme cases (long uptime, sudden temperature
+// change), this could result in tmr.time() going backwards....
+//
+// This implementation instead applies rtc2usec to short time intervals only
+// (the longest being around 1 second), and then accumulates the resulting
+// microseconds in a 64 bit counter. That's guaranteed to be monotonic, and
+// should be a lot closer to representing an actual uptime.
+
 static uint32_t rtc_time_cali=0;
 static uint32_t last_rtc_time=0;
 static uint64_t last_rtc_time_us=0;
@@ -97,180 +56,137 @@ static sint32_t soft_watchdog  = -1;
 static os_timer_t rtc_timer;
 
 static void alarm_timer_common(void* arg){
-	tmr_t tmr = (tmr_t)arg;
-	lua_State* L = lua_getstate();
-	if(tmr->lua_ref == LUA_NOREF)
-		return;
-	lua_rawgeti(L, LUA_REGISTRYINDEX, tmr->lua_ref);
-	lua_rawgeti(L, LUA_REGISTRYINDEX, tmr->self_ref);
-	//if the timer was set to single run we clean up after it
-	if(tmr->mode == TIMER_MODE_SINGLE){
-		luaL_unref(L, LUA_REGISTRYINDEX, tmr->lua_ref);
-		tmr->lua_ref = LUA_NOREF;
-		tmr->mode = TIMER_MODE_OFF;
-	}else if(tmr->mode == TIMER_MODE_SEMI){
-		tmr->mode |= TIMER_IDLE_FLAG;
-	}
-	if (tmr->mode != TIMER_MODE_AUTO && tmr->self_ref != LUA_REFNIL) {
-		luaL_unref(L, LUA_REGISTRYINDEX, tmr->self_ref);
-		tmr->self_ref = LUA_NOREF;
-	}
-	lua_call(L, 1, 0);
+  tmr_t *tmr = (tmr_t *) arg;
+  if(tmr->lua_ref > 0) {
+    lua_State* L = lua_getstate();
+    lua_rawgeti(L, LUA_REGISTRYINDEX, tmr->lua_ref);
+    lua_rawgeti(L, LUA_REGISTRYINDEX, tmr->self_ref);
+    if (tmr->mode != TIMER_MODE_AUTO) {
+    if(tmr->mode == TIMER_MODE_SINGLE) {
+      luaL_unref2(L, LUA_REGISTRYINDEX, tmr->lua_ref);
+      luaL_unref2(L, LUA_REGISTRYINDEX, tmr->self_ref);
+      tmr->mode = TIMER_MODE_OFF;
+    } else if (tmr->mode == TIMER_MODE_SEMI) {
+      tmr->mode |= TIMER_IDLE_FLAG;
+      luaL_unref2(L, LUA_REGISTRYINDEX, tmr->self_ref);
+      }
+    }
+    luaL_pcallx(L, 1, 0);
+  }
 }
 
 // Lua: tmr.delay( us )
 static int tmr_delay( lua_State* L ){
 	sint32_t us = luaL_checkinteger(L, 1);
-	if(us <= 0)
-		return luaL_error(L, "wrong arg range");
-	while(us >= 1000000){
-		us -= 1000000;
-		os_delay_us(1000000);
-		system_soft_wdt_feed ();
+  luaL_argcheck(L, us>0, 1, "wrong arg range");
+	while(us > 0){
+	  os_delay_us(us >= 1000000 ? 1000000 : us);
+	  system_soft_wdt_feed ();
+	  us -= 1000000;
 	}
-	if(us>0){
-		os_delay_us(us);
-		system_soft_wdt_feed ();
-	}
-	return 0;
+  return 0;
 }
 
 // Lua: tmr.now() , return system timer in us
 static int tmr_now(lua_State* L){
-	uint32_t now = 0x7FFFFFFF & system_get_time();
-	lua_pushinteger(L, now);
+	lua_pushinteger(L, (uint32_t) (0x7FFFFFFF & system_get_time()));
 	return 1;
 }
 
 // Lua: tmr.ccount() , returns CCOUNT register
-static int tmr_ccount( lua_State* L )
-{
+static int tmr_ccount(lua_State* L){
 	lua_pushinteger(L, CCOUNT_REG);
 	return 1;
 }
 
-static tmr_t tmr_get( lua_State *L, int stack ) {
-	tmr_t t = (tmr_t)luaL_checkudata(L, stack, "tmr.timer");
-	if (t == NULL)
-		return (tmr_t)luaL_error(L, "timer object expected");
-	return t;
-}
-
-// Lua: tmr.register( ref, interval, mode, function )
-static int tmr_register(lua_State* L){
-	tmr_t tmr = tmr_get(L, 1);
+/*
+** Health warning: this is also called DIRECTLY from alarm() which assumes that the Lua
+** stack is preserved for the following start(), so the stack MUST be balanced here.
+*/
 
+// Lua: t:register( interval, mode, function )
+static int tmr_register(lua_State* L) {
+	tmr_t *tmr = (tmr_t *) luaL_checkudata(L, 1, "tmr.timer");
 	uint32_t interval = luaL_checkinteger(L, 2);
 	uint8_t mode = luaL_checkinteger(L, 3);
 
 	luaL_argcheck(L, (interval > 0 && interval <= MAX_TIMEOUT), 2, MAX_TIMEOUT_ERR_STR);
 	luaL_argcheck(L, (mode == TIMER_MODE_SINGLE || mode == TIMER_MODE_SEMI || mode == TIMER_MODE_AUTO), 3, "Invalid mode");
 	luaL_argcheck(L, lua_isfunction(L, 4), 4, "Must be function");
+
 	//get the lua function reference
 	lua_pushvalue(L, 4);
-	sint32_t ref = luaL_ref(L, LUA_REGISTRYINDEX);
 	if(!(tmr->mode & TIMER_IDLE_FLAG) && tmr->mode != TIMER_MODE_OFF)
 		os_timer_disarm(&tmr->os);
-	//there was a bug in this part, the second part of the following condition was missing
-	if(tmr->lua_ref != LUA_NOREF && tmr->lua_ref != ref)
-		luaL_unref(L, LUA_REGISTRYINDEX, tmr->lua_ref);
-	tmr->lua_ref = ref;
+	luaL_reref(L, LUA_REGISTRYINDEX, &tmr->lua_ref);
 	tmr->mode = mode|TIMER_IDLE_FLAG;
 	tmr->interval = interval;
 	os_timer_setfn(&tmr->os, alarm_timer_common, tmr);
 	return 0;
 }
 
-// Lua: tmr.start( id / ref )
+// Lua: t:start()
 static int tmr_start(lua_State* L){
-	tmr_t tmr = tmr_get(L, 1);
+	tmr_t *tmr = (tmr_t *) luaL_checkudata(L, 1, "tmr.timer");
+  int idle = tmr->mode & TIMER_IDLE_FLAG;
 
-	if (tmr->self_ref == LUA_NOREF) {
-		lua_pushvalue(L, 1);
+  lua_settop(L, 1);  /* ignore any args after the userdata */
+	if (tmr->self_ref == LUA_NOREF)
 		tmr->self_ref = luaL_ref(L, LUA_REGISTRYINDEX);
-	}
 
-	//we return false if the timer is not idle
-	if(!(tmr->mode&TIMER_IDLE_FLAG)){
-		lua_pushboolean(L, 0);
-	}else{
-		tmr->mode &= ~TIMER_IDLE_FLAG;
+	if(idle) {
+  	tmr->mode &= ~TIMER_IDLE_FLAG;
 		os_timer_arm(&tmr->os, tmr->interval, tmr->mode==TIMER_MODE_AUTO);
-		lua_pushboolean(L, 1);
-	}
+  }
+	lua_pushboolean(L, !idle); /* false if the timer is not idle */
 	return 1;
 }
 
-// Lua: tmr.alarm( id / ref, interval, repeat, function )
+// Lua: t:alarm( interval, repeat, function )
 static int tmr_alarm(lua_State* L){
 	tmr_register(L);
 	return tmr_start(L);
 }
 
-// Lua: tmr.stop( id / ref )
+// Lua: t:stop()
 static int tmr_stop(lua_State* L){
-	tmr_t tmr = tmr_get(L, 1);
+	tmr_t *tmr = (tmr_t *) luaL_checkudata(L, 1, "tmr.timer");
+  int idle = tmr->mode == TIMER_MODE_OFF || (tmr->mode & TIMER_IDLE_FLAG);
+  luaL_unref2(L, LUA_REGISTRYINDEX, tmr->self_ref);
 
-	if (tmr->self_ref != LUA_REFNIL) {
-		luaL_unref(L, LUA_REGISTRYINDEX, tmr->self_ref);
-		tmr->self_ref = LUA_NOREF;
-	}
-
-	//we return false if the timer is idle (of not registered)
-	if(!(tmr->mode & TIMER_IDLE_FLAG) && tmr->mode != TIMER_MODE_OFF){
-		tmr->mode |= TIMER_IDLE_FLAG;
+	if(!idle)
 		os_timer_disarm(&tmr->os);
-		lua_pushboolean(L, 1);
-	}else{
-		lua_pushboolean(L, 0);
-	}
+	tmr->mode |= TIMER_IDLE_FLAG;
+	lua_pushboolean(L, !idle);  /* return false if the timer is idle (or not registered) */
 	return 1;
 }
 
 #ifdef TIMER_SUSPEND_ENABLE
 
 #define TMR_SUSPEND_REMOVED_MSG "This feature has been removed, we apologize for any inconvenience this may have caused."
-static int tmr_suspend(lua_State* L){
+#define tmr_suspend     tmr_suspend_removed
+#define tmr_resume      tmr_suspend_removed
+#define tmr_suspend_all tmr_suspend_removed
+#define tmr_resume_all  tmr_suspend_removed
+static int tmr_suspend_removed(lua_State* L){
   return luaL_error(L, TMR_SUSPEND_REMOVED_MSG);
 }
-
-static int tmr_resume(lua_State* L){
-  return luaL_error(L, TMR_SUSPEND_REMOVED_MSG);
-}
-
-static int tmr_suspend_all (lua_State *L){
-  return luaL_error(L, TMR_SUSPEND_REMOVED_MSG);
-}
-
-static int tmr_resume_all (lua_State *L){
-  return luaL_error(L, TMR_SUSPEND_REMOVED_MSG);
-}
-
-
 #endif
 
-// Lua: tmr.unregister( id / ref )
+// Lua: t:unregister()
 static int tmr_unregister(lua_State* L){
-	tmr_t tmr = tmr_get(L, 1);
-
-	if (tmr->self_ref != LUA_REFNIL) {
-		luaL_unref(L, LUA_REGISTRYINDEX, tmr->self_ref);
-		tmr->self_ref = LUA_NOREF;
-	}
-
+	tmr_t *tmr =  (tmr_t *) luaL_checkudata(L, 1, "tmr.timer");
+  luaL_unref2(L, LUA_REGISTRYINDEX, tmr->self_ref);
+	luaL_unref2(L, LUA_REGISTRYINDEX, tmr->lua_ref);
 	if(!(tmr->mode & TIMER_IDLE_FLAG) && tmr->mode != TIMER_MODE_OFF)
 		os_timer_disarm(&tmr->os);
-	if(tmr->lua_ref != LUA_NOREF)
-		luaL_unref(L, LUA_REGISTRYINDEX, tmr->lua_ref);
-	tmr->lua_ref = LUA_NOREF;
 	tmr->mode = TIMER_MODE_OFF;
 	return 0;
 }
 
-// Lua: tmr.interval( id / ref, interval )
+// Lua: t:interval( interval )
 static int tmr_interval(lua_State* L){
-	tmr_t tmr = tmr_get(L, 1);
-
+	tmr_t *tmr = (tmr_t *) luaL_checkudata(L, 1, "tmr.timer");
 	uint32_t interval = luaL_checkinteger(L, 2);
 	luaL_argcheck(L, (interval > 0 && interval <= MAX_TIMEOUT), 2, MAX_TIMEOUT_ERR_STR);
 	if(tmr->mode != TIMER_MODE_OFF){
@@ -283,10 +199,9 @@ static int tmr_interval(lua_State* L){
 	return 0;
 }
 
-// Lua: tmr.state( id / ref )
+// Lua: t:state()
 static int tmr_state(lua_State* L){
-	tmr_t tmr = tmr_get(L, 1);
-
+	tmr_t *tmr = (tmr_t *) luaL_checkudata(L, 1, "tmr.timer");
 	if(tmr->mode == TIMER_MODE_OFF){
 		lua_pushnil(L);
 		return 1;
@@ -297,28 +212,21 @@ static int tmr_state(lua_State* L){
 	return 2;
 }
 
-/*I left the led comments 'couse I don't know
-why they are here*/
-
-// extern void update_key_led();
 // Lua: tmr.wdclr()
 static int tmr_wdclr( lua_State* L ){
 	system_soft_wdt_feed ();
-	// update_key_led();
 	return 0;
 }
 
-//system_rtc_clock_cali_proc() returns
-//a fixed point value (12 bit fraction part)
-//it tells how many rtc clock ticks represent 1us.
-//the high 64 bits of the uint64_t multiplication
-//are unnedded (I did the math)
+// The on ESP8266 system_rtc_clock_cali_proc() returns a fixed point value 
+// (12 bit fraction part), giving how many rtc clock ticks represent 1us.
+//  The high 64 bits of the uint64_t multiplication are not needed)
 static uint32_t rtc2usec(uint64_t rtc){
 	return (rtc*rtc_time_cali)>>12;
 }
 
-// This returns the number of microseconds uptime. Note that it relies on the rtc clock,
-// which is notoriously temperature dependent
+// This returns the number of microseconds uptime. Note that it relies on
+// the rtc clock, which is notoriously temperature dependent
 inline static uint64_t rtc_timer_update(bool do_calibration){
 	if (do_calibration || rtc_time_cali==0)
 		rtc_time_cali=system_rtc_clock_cali_proc();
@@ -330,8 +238,7 @@ inline static uint64_t rtc_timer_update(bool do_calibration){
 
 	// Only update if at least 100ms has passed since we last updated.
 	// This prevents the rounding errors in rtc2usec from accumulating
-	if (us_since_last>=100000)
-	{
+	if (us_since_last>=100000){
 		last_rtc_time=current;
 		last_rtc_time_us=now;
 	}
@@ -356,20 +263,18 @@ static int tmr_time( lua_State* L ){
 
 // Lua: tmr.softwd( value )
 static int tmr_softwd( lua_State* L ){
-	soft_watchdog = luaL_checkinteger(L, 1);
+	int t = luaL_checkinteger(L, 1);
+	luaL_argcheck(L, t>0 , 2, "invalid time");
+  soft_watchdog = t;
 	return 0;
 }
 
 // Lua: tmr.create()
 static int tmr_create( lua_State *L ) {
-	tmr_t ud = (tmr_t)lua_newuserdata(L, sizeof(timer_struct_t));
-	if (!ud) return luaL_error(L, "not enough memory");
+	tmr_t *ud = (tmr_t *)lua_newuserdata(L, sizeof(*ud));
 	luaL_getmetatable(L, "tmr.timer");
 	lua_setmetatable(L, -2);
-	ud->lua_ref = LUA_NOREF;
-	ud->self_ref = LUA_NOREF;
-	ud->mode = TIMER_MODE_OFF;
-	os_timer_disarm(&ud->os);
+	*ud = (tmr_t) {{0}, LUA_NOREF, LUA_NOREF, 0, TIMER_MODE_OFF};
 	return 1;
 }
 
@@ -422,13 +327,14 @@ int luaopen_tmr( lua_State *L ){
 	os_timer_setfn(&rtc_timer, rtc_callback, NULL);
 	os_timer_arm(&rtc_timer, 1000, 1);
 
+  // The function rtc_callback calls the a function that calibrates the SoftRTC
+  // for drift in the esp8266's clock.  My guess: after the duration of light_sleep
+  // there is bound to be some drift in the clock, so a calibration is due.
   SWTIMER_REG_CB(rtc_callback, SWTIMER_RESUME);
-  //The function rtc_callback calls the a function that calibrates the SoftRTC for drift in the esp8266's clock.
-  //My guess: after the duration of light_sleep there's bound to be some drift in the clock, so a calibration is due.
-  SWTIMER_REG_CB(alarm_timer_common, SWTIMER_RESUME);
-  //The function alarm_timer_common handles timers created by the developer via tmr.create().
-  //No reason not to resume the timers, so resume em'.
 
+  // The function alarm_timer_common handles timers created by the developer via
+  // tmr.create().  No reason not to resume the timers, so resume em'.
+  SWTIMER_REG_CB(alarm_timer_common, SWTIMER_RESUME);
 
 	return 0;
 }

+ 4 - 4
app/modules/websocket.c

@@ -41,7 +41,7 @@ static void websocketclient_onConnectionCallback(ws_info *ws) {
   if (data->onConnection != LUA_NOREF) {
     lua_rawgeti(L, LUA_REGISTRYINDEX, data->onConnection); // load the callback function
     lua_rawgeti(L, LUA_REGISTRYINDEX, data->self_ref);  // pass itself, #1 callback argument
-    lua_call(L, 1, 0);
+    luaL_pcallx(L, 1, 0);
   }
 }
 
@@ -61,7 +61,7 @@ static void websocketclient_onReceiveCallback(ws_info *ws, int len, char *messag
     lua_rawgeti(L, LUA_REGISTRYINDEX, data->self_ref);  // pass itself, #1 callback argument
     lua_pushlstring(L, message, len); // #2 callback argument
     lua_pushnumber(L, opCode); // #3 callback argument
-    lua_call(L, 3, 0);
+    luaL_pcallx(L, 3, 0);
   }
 }
 
@@ -80,7 +80,7 @@ static void websocketclient_onCloseCallback(ws_info *ws, int errorCode) {
     lua_rawgeti(L, LUA_REGISTRYINDEX, data->onClose); // load the callback function
     lua_rawgeti(L, LUA_REGISTRYINDEX, data->self_ref);  // pass itself, #1 callback argument
     lua_pushnumber(L, errorCode); // pass the error code, #2 callback argument
-    lua_call(L, 2, 0);
+    luaL_pcallx(L, 2, 0);
   }
 
   // free self-reference to allow gc (no futher callback will be called until next ws:connect())
@@ -285,7 +285,7 @@ static int websocketclient_gc(lua_State *L) {
       lua_rawgeti(L, LUA_REGISTRYINDEX, data->onClose);
 
       lua_pushnumber(L, -100);
-      lua_call(L, 1, 0);
+      luaL_pcallx(L, 1, 0);
     }
     luaL_unref(L, LUA_REGISTRYINDEX, data->onClose);
   }

+ 5 - 6
app/modules/wifi.c

@@ -46,7 +46,7 @@ static void wifi_smart_succeed_cb(sc_status status, void *pdata){
 
   lua_State* L = (lua_State *)arg;
   lua_rawgeti(L, LUA_REGISTRYINDEX, wifi_smart_succeed);
-  lua_call(L, 0, 0);
+  luaL_pcallx(L, 0, 0);
 
 #else
 
@@ -64,9 +64,8 @@ static void wifi_smart_succeed_cb(sc_status status, void *pdata){
 
     lua_pushstring(L, sta_conf->ssid);
     lua_pushstring(L, sta_conf->password);
-    lua_call(L, 2, 0);
-
     unregister_lua_cb(L, &wifi_smart_succeed);
+    luaL_pcallx(L, 2, 0);
   }
 
 #endif // defined( NODE_SMART_OLDSTYLE )
@@ -125,8 +124,8 @@ static void wifi_scan_done(void *arg, STATUS status)
   {
     lua_newtable( L );
   }
-  lua_call(L, 1, 0);
   unregister_lua_cb(L, &wifi_scan_succeed);
+  luaL_pcallx(L, 1, 0);
 }
 
 #ifdef WIFI_SMART_ENABLE
@@ -438,9 +437,9 @@ void wifi_pmSleep_suspend_CB(void)
   {
     lua_State* L = lua_getstate(); // Get main Lua thread pointer
     lua_rawgeti(L, LUA_REGISTRYINDEX, wifi_suspend_cb_ref); // Push suspend callback onto stack
-    luaL_unref(L, LUA_REGISTRYINDEX, wifi_suspend_cb_ref); // remove suspend callback from LUA_REGISTRY
+    luaL_unref(L, wifi_suspend_cb_ref); // remove suspend callback from LUA_REGISTRY
     wifi_suspend_cb_ref = LUA_NOREF; // Update variable since reference is no longer valid
-    lua_call(L, 0, 0); // Execute suspend callback
+    luaL_pcallx(L, 0, 0); // Execute suspend callback
   }
   else
   {

+ 1 - 1
app/modules/wifi_eventmon.c

@@ -242,7 +242,7 @@ static void wifi_event_monitor_process_event_queue(task_param_t param, uint8 pri
   luaL_unref(L, LUA_REGISTRYINDEX, event_ref); //the userdata containing event info is no longer needed
   event_ref = LUA_NOREF;
 
-  lua_call(L, 1, 0); //execute user's callback and pass Lua table
+  luaL_pcallx(L, 1, 0); //execute user's callback and pass Lua table
   return;
 }
 

+ 1 - 1
app/modules/wifi_monitor.c

@@ -321,7 +321,7 @@ static void monitor_task(os_param_t param, uint8_t prio)
 
     free(input);
 
-    lua_call(L, 1, 0);
+    luaL_pcallx(L, 1, 0);
   } else {
     free(input);
   }

+ 1 - 1
app/modules/wps.c

@@ -30,7 +30,7 @@ LOCAL void ICACHE_FLASH_ATTR user_wps_status_cb(int status)
   if (wps_callback_ref != LUA_NOREF) {
     lua_rawgeti(L, LUA_REGISTRYINDEX, wps_callback_ref);
     lua_pushinteger(L, status);
-    lua_call(L, 1, 0);
+    luaL_pcallx(L, 1, 0);
   }
 }
 

+ 6 - 4
app/pcm/pcm_core.c

@@ -18,13 +18,14 @@
 
 #include "pcm.h"
 
-static void dispatch_callback( lua_State *L, int self_ref, int cb_ref, int returns )
+static int dispatch_callback( lua_State *L, int self_ref, int cb_ref, int returns )
 {
   if (cb_ref != LUA_NOREF) {
     lua_rawgeti( L, LUA_REGISTRYINDEX, cb_ref );
     lua_rawgeti( L, LUA_REGISTRYINDEX, self_ref );
-    lua_call( L, 1, returns );
+    return luaL_pcallx( L, 1, returns );
   }
+  return LUA_OK;
 }
 
 void pcm_data_vu( task_param_t param, uint8 prio )
@@ -36,7 +37,7 @@ void pcm_data_vu( task_param_t param, uint8 prio )
     lua_rawgeti( L, LUA_REGISTRYINDEX, cfg->cb_vu_ref );
     lua_rawgeti( L, LUA_REGISTRYINDEX, cfg->self_ref );
     lua_pushnumber( L, (LUA_NUMBER)(cfg->vu_peak) );
-    lua_call( L, 2, 0 );
+    luaL_pcallx( L, 2, 0 );
   }
 }
 
@@ -52,7 +53,8 @@ void pcm_data_play( task_param_t param, uint8 prio )
   // retrieve new data from callback
   if ((cfg->isr_throttled >= 0) &&
       (cfg->cb_data_ref != LUA_NOREF)) {
-    dispatch_callback( L, cfg->self_ref, cfg->cb_data_ref, 1 );
+    if(dispatch_callback( L, cfg->self_ref, cfg->cb_data_ref, 1 ) != LUA_OK)
+      return;
     need_pop = TRUE;
 
     if (lua_type( L, -1 ) == LUA_TSTRING) {

+ 41 - 65
app/platform/platform.c

@@ -26,12 +26,12 @@ struct gpio_hook_entry {
   uint32_t               bits;
 };
 struct gpio_hook {
-  struct gpio_hook_entry *entry;
   uint32_t                all_bits;
   uint32_t                count;
+  struct gpio_hook_entry  entry[1];
 };
 
-static struct gpio_hook platform_gpio_hook;
+static struct gpio_hook *platform_gpio_hook;
 #endif
 #endif
 
@@ -218,10 +218,10 @@ static void ICACHE_RAM_ATTR platform_gpio_intr_dispatcher (void *dummy){
   UNUSED(dummy);
 
 #ifdef GPIO_INTERRUPT_HOOK_ENABLE
-  if (gpio_status & platform_gpio_hook.all_bits) {
-    for (j = 0; j < platform_gpio_hook.count; j++) {
-       if (gpio_status & platform_gpio_hook.entry[j].bits)
-         gpio_status = (platform_gpio_hook.entry[j].func)(gpio_status);
+  if (gpio_status & platform_gpio_hook->all_bits) {
+    for (j = 0; j < platform_gpio_hook->count; j++) {
+       if (gpio_status & platform_gpio_hook->entry[j].bits)
+         gpio_status = (platform_gpio_hook->entry[j].func)(gpio_status);
     }
   }
 #endif
@@ -266,7 +266,8 @@ static void ICACHE_RAM_ATTR platform_gpio_intr_dispatcher (void *dummy){
 void platform_gpio_init( platform_task_handle_t gpio_task )
 {
   gpio_task_handle = gpio_task;
-
+  // No error handling but this is called at startup when there is a lot of free RAM
+  platform_gpio_hook =  calloc (1, sizeof(*platform_gpio_hook) - sizeof(struct gpio_hook_entry));
   ETS_GPIO_INTR_ATTACH(platform_gpio_intr_dispatcher, NULL);
 }
 
@@ -281,78 +282,53 @@ void platform_gpio_init( platform_task_handle_t gpio_task )
  */
 int platform_gpio_register_intr_hook(uint32_t bits, platform_hook_function hook)
 {
-  struct gpio_hook nh, oh = platform_gpio_hook;
-  int i, j;
+  struct gpio_hook *oh = platform_gpio_hook;
+  int i, j, cur = -1;
 
-  if (!hook) {
-    // Cannot register or unregister null hook
+  if (!hook)   // Cannot register or unregister null hook
     return 0;
-  }
 
-  int delete_slot = -1;
-
-  // If hook already registered, just update the bits
-  for (i=0; i<oh.count; i++) {
-    if (hook == oh.entry[i].func) {
-      if (!bits) {
-	// Unregister if move to zero bits
-	delete_slot = i;
-	break;
-      }
-      if (bits & (oh.all_bits & ~oh.entry[i].bits)) {
-	// Attempt to hook an already hooked bit
-	return 0;
-      }
-      // Update the hooked bits (in the right order)
-      uint32_t old_bits = oh.entry[i].bits;
-      *(volatile uint32_t *) &oh.entry[i].bits = bits;
-      *(volatile uint32_t *) &oh.all_bits = (oh.all_bits & ~old_bits) | bits;
-      ETS_GPIO_INTR_DISABLE();
-      // This is a structure copy, so interrupts need to be disabled
-      platform_gpio_hook = oh;
-      ETS_GPIO_INTR_ENABLE();
-      return 1;
+  // Is the hook already registered?
+  for (i=0; i<oh->count; i++) {
+    if (hook == oh->entry[i].func) {
+      cur = i;
+      break;
     }
   }
 
-  // This must be the register new hook / delete old hook
-
-  if (delete_slot < 0) {
-    if (bits & oh.all_bits) {
-      return 0;   // Attempt to hook already hooked bits
-    }
-    nh.count = oh.count + 1;    // register a new hook
-  } else {
-    nh.count = oh.count - 1;    // unregister an old hook
-  }
+  // return error status if there is a bits clash
+  if (oh->all_bits & ~(cur < 0 ? 0 : oh->entry[cur].bits) & bits)
+    return 0;
 
-  // These return NULL if the count = 0 so only error check if > 0)
-  nh.entry = malloc( nh.count * sizeof(*(nh.entry)) );
-  if (nh.count && !(nh.entry)) {
-    return 0;  // Allocation failure
-  }
+  // Allocate replacement hook block and return 0 on alloc failure
+  int count = oh->count + (cur < 0 ? 1 : (bits == 0 ? -1 : 0)); 
+  struct gpio_hook *nh = malloc (sizeof *oh + (count -1)*sizeof(struct gpio_hook_entry));
+  if (!oh)
+    return 0;
 
-  for (i=0, j=0; i<oh.count; i++) {
-    // Don't copy if this is the entry to delete
-    if (i != delete_slot) {
-      nh.entry[j++]   = oh.entry[i];
-    }
-  }
+  nh->all_bits = 0;
+  nh->count = count;
 
-  if (delete_slot < 0) { // for a register add the hook to the tail and set the all bits
-    nh.entry[j].bits  = bits;
-    nh.entry[j].func  = hook;
-    nh.all_bits = oh.all_bits | bits;
-  } else {    // for an unregister clear the matching all bits
-    nh.all_bits = oh.all_bits & (~oh.entry[delete_slot].bits);
+  for (i=0, j=0; i<oh->count; i++) {
+    if (i == cur && !bits) 
+      continue;  /* unregister entry is a no-op */
+    nh->entry[j] = oh->entry[i];  /* copy existing entry */
+    if (i == cur)
+      nh->entry[j].bits = bits;   /* update bits if this is a replacement */
+    nh->all_bits |= nh->entry[j++].bits;
   }
-
+ 
+  if (cur < 0) {                  /* append new hook entry */
+    nh->entry[j].func = hook;
+    nh->entry[j].bits = bits;
+    nh->all_bits |= bits;
+  } 
+       
   ETS_GPIO_INTR_DISABLE();
-  // This is a structure copy, so interrupts need to be disabled
   platform_gpio_hook = nh;
   ETS_GPIO_INTR_ENABLE();
 
-  free(oh.entry);
+  free(oh);
   return 1;
 }
 #endif // GPIO_INTERRUPT_HOOK_ENABLE

+ 1 - 1
app/pm/pmSleep.c

@@ -162,7 +162,7 @@ void pmSleep_execute_lua_cb(int* cb_ref){
     lua_rawgeti(L, LUA_REGISTRYINDEX, *cb_ref); // Push resume callback onto the stack
     lua_unref(L, *cb_ref); // Remove resume callback from registry
     *cb_ref = LUA_NOREF; // Update variable since reference is no longer valid
-    lua_call(L, 0, 0); // Execute resume callback
+    luaL_pcallx(L, 0, 0); // Execute resume callback
   }
 
 }

+ 81 - 93
app/pm/swtimer.c

@@ -8,33 +8,33 @@
  * The SDK software timer API executes in a task. The priority of this task in relation to the
  * application level tasks is unknown (at time of writing).
  *
- *
  * To determine when a timer's callback should be executed, the respective timer's `timer_expire`
- *  variable is compared to the hardware counter(FRC2), then, if the timer's `timer_expire` is
- *  less than the current FRC2 count, the timer's callback is fired.
+ * variable is compared to the hardware counter(FRC2), then, if the timer's `timer_expire` is
+ * less than the current FRC2 count, the timer's callback is fired.
  *
  * The timers in this list are organized in an ascending order starting with the timer
  * with the lowest timer_expire.
  *
- * When a timer expires that has a timer_period greater than 0, timer_expire is changed to
- * current FRC2 + timer_period, then the timer is inserted back in to the list in the correct position.
+ * When a timer expires that has a timer_period greater than 0, timer_expire is changed to current
+ * FRC2 + timer_period, then the timer is inserted back in to the list in the correct position.
  *
- * when using millisecond(default) timers, FRC2 resolution is 312.5 ticks per millisecond.
+ * When using millisecond(default) timers, FRC2 resolution is 312.5 ticks per millisecond.
  *
  *
  * TIMER SUSPEND API INFO:
  *
- * Timer suspension is achieved by first finding any non-SDK timers by comparing the timer function callback pointer
- * of each timer in "timer_list" to a list of registered timer callback pointers stored in the Lua registry.
- * If a timer with a corresponding registered callback pointer is found, the timer's timer_expire field is is compared
- * to the current FRC2 count and the difference is saved along with the other timer parameters to temporary variables.
- * The timer is then disarmed and the parameters are copied back, the timer pointer is then
- * added to a separate linked list of which the head pointer is stored as a lightuserdata in the lua registry.
+ * Timer suspension is achieved by first finding any non-SDK timers by comparing the timer function
+ * callback pointer of each timer in "timer_list" to a list of registered timer callback pointers
+ * stored in the Lua registry.  If a timer with a corresponding registered callback pointer is found,
+ * the timer's timer_expire field is is compared to the current FRC2 count and the difference is
+ * saved along with the other timer parameters to temporary variables.  The timer is then disarmed
+ * and the parameters are copied back, the timer pointer is then added to a separate linked list of 
+ * which the head pointer is stored as a lightuserdata in the lua registry.
  *
- * Resuming the timers is achieved by first retrieving the lightuserdata holding the suspended timer list head pointer.
- * Then, starting with the beginning of the list the current FRC2 count is added back to the timer's timer_expire, then
- * the timer is manually added back to "timer_list" in an ascending order.
- * Once there are no more suspended timers, the function returns
+ * Resuming the timers is achieved by first retrieving the lightuserdata holding the suspended timer
+ * list head pointer.  Then, starting with the beginning of the list the current FRC2 count is added
+ * back to the timer's timer_expire, then the timer is manually added back to "timer_list" in an
+ * ascending order.  Once there are no more suspended timers, the function returns.
  *
  *
  */
@@ -57,17 +57,24 @@
  #define SWTMR_DEBUG
 #endif
 
-//this section specifies which lua registry to use. LUA_GLOBALSINDEX or LUA_REGISTRYINDEX
+// The SWTMR table is either normally stored in the Lua registry, but at _G.SWTMR_registry_key
+// when in debug.  THe CB and suspend lists have different names depending of debugging mode.
+// Also
 #ifdef SWTMR_DEBUG
 #define SWTMR_DBG(fmt, ...) dbg_printf("\n SWTMR_DBG(%s): "fmt"\n", __FUNCTION__, ##__VA_ARGS__)
-#define L_REGISTRY LUA_GLOBALSINDEX
 #define CB_LIST_STR "timer_cb_ptrs"
 #define SUSP_LIST_STR "suspended_tmr_LL_head"
+#define get_swtmr_registry(L) lua_getglobal(L, "SWTMR_registry_key")
+#define set_swtmr_registry(L) lua_setglobal(L, "SWTMR_registry_key")
 #else
 #define SWTMR_DBG(...)
-#define L_REGISTRY LUA_REGISTRYINDEX
 #define CB_LIST_STR "cb"
 #define SUSP_LIST_STR "st"
+#define get_swtmr_registry(L) lua_pushlightuserdata(L, &register_queue); \
+                              lua_rawget(L, LUA_REGISTRYINDEX)
+#define set_swtmr_registry(L) lua_pushlightuserdata(L, &register_queue); \
+                              lua_insert(L, -2); \
+                              lua_rawset(L, LUA_REGISTRYINDEX)
 #endif
 
 
@@ -92,32 +99,21 @@ static task_handle_t cb_register_task_id = 0; //variable to hold task id for tas
 static void add_to_reg_queue(void* timer_cb_ptr, uint8 suspend_policy);
 static void process_cb_register_queue(task_param_t param, uint8 priority);
 
-
-#ifdef SWTMR_DEBUG
-#define push_swtmr_registry_key(L) lua_pushstring(L, "SWTMR_registry_key")
-#else
-#define push_swtmr_registry_key(L) lua_pushlightuserdata(L, &register_queue);
-#endif
-
 #include <pm/swtimer.h>
 
 void swtmr_suspend_timers(){
   lua_State* L = lua_getstate();
 
   //get swtimer table
-  push_swtmr_registry_key(L);
-  lua_rawget(L, L_REGISTRY);
+  get_swtmr_registry(L);
+  if(!lua_istable(L, -1)) {lua_pop(L, 1); return;}
 
   //get cb_list table
   lua_pushstring(L, CB_LIST_STR);
   lua_rawget(L, -2);
 
-  //check for existence of the swtimer table and the cb_list table, return if not found
-  if(!lua_istable(L, -2) || !lua_istable(L, -1)){
-    // not necessarily an error maybe there are legitimately no timers to suspend
-    lua_pop(L, 2);
-    return;
-  }
+  //check for existence of the cb_list table, return if not found
+  if(!lua_istable(L, -1)) {lua_pop(L, 2); return;}
 
   os_timer_t* suspended_timer_list_head = NULL;
   os_timer_t* suspended_timer_list_tail = NULL;
@@ -168,10 +164,10 @@ void swtmr_suspend_timers(){
   uint32 period_temp = 0;
   void* arg_temp = NULL;
 
-  /* In this section, the SDK's timer_list is traversed to find any timers that have a registered callback pointer.
-   * If a registered callback is found, the timer is suspended by saving the difference
-   * between frc2_count and timer_expire then the timer is disarmed and placed into suspended_timer_list
-   * so it can later be resumed.
+  /* In this section, the SDK's timer_list is traversed to find any timers that have a registered
+   * callback pointer. If a registered callback is found, the timer is suspended by saving the
+   * difference between frc2_count and timer_expire then the timer is disarmed and placed into
+   * suspended_timer_list so it can later be resumed.
    */
   while(timer_ptr != NULL){
     os_timer_t* next_timer = (os_timer_t*)0xffffffff;
@@ -190,7 +186,8 @@ void swtmr_suspend_timers(){
         arg_temp = timer_ptr->timer_arg;
 
         if(timer_ptr->timer_period == 0 && cb_reg_array[i]->suspend_policy == SWTIMER_RESTART){
-          SWTMR_DBG("Warning: suspend_policy(RESTART) is not compatible with single-shot timer(%p), changing suspend_policy to (RESUME)", timer_ptr);
+          SWTMR_DBG("Warning: suspend_policy(RESTART) is not compatible with single-shot timer(%p), "
+                    "changing suspend_policy to (RESUME)", timer_ptr);
           cb_reg_array[i]->suspend_policy = SWTIMER_RESUME;
         }
 
@@ -260,19 +257,15 @@ void swtmr_resume_timers(){
   lua_State* L = lua_getstate();
 
   //get swtimer table
-  push_swtmr_registry_key(L);
-  lua_rawget(L, L_REGISTRY);
+  get_swtmr_registry(L);
+  if(!lua_istable(L, -1)) {lua_pop(L, 1); return;}
 
   //get suspended_timer_list lightuserdata
   lua_pushstring(L, SUSP_LIST_STR);
   lua_rawget(L, -2);
 
-  //check for existence of swtimer table and the suspended_timer_list pointer userdata, return if not found
-  if(!lua_istable(L, -2) || !lua_isuserdata(L, -1)){
-    // not necessarily an error maybe there are legitimately no timers to resume
-    lua_pop(L, 2);
-    return;
-  }
+  //check for existence of the suspended_timer_list pointer userdata, return if not found
+  if(!lua_isuserdata(L, -1)) {lua_pop(L, 2); return;}
 
   os_timer_t* suspended_timer_list_ptr = lua_touserdata(L, -1);
   lua_pop(L, 1); //pop suspended timer list userdata from stack
@@ -339,25 +332,21 @@ void swtmr_resume_timers(){
 void swtmr_cb_register(void* timer_cb_ptr, uint8 suspend_policy){
   lua_State* L = lua_getstate();
   if(!L){
-    //Lua has not started yet, therefore L_REGISTRY is not available.
-    //add timer cb to queue for later processing after Lua has started
+    // If Lua has not started yet, then add timer cb to queue for later processing after Lua has started
     add_to_reg_queue(timer_cb_ptr, suspend_policy);
     return;
   }
   if(timer_cb_ptr){
     size_t cb_list_last_idx = 0;
 
-    push_swtmr_registry_key(L);
-    lua_rawget(L, L_REGISTRY);
+    get_swtmr_registry(L);
 
     if(!lua_istable(L, -1)){
-      //swtmr does not exist, create and add to registry
+      //swtmr does not exist, create and add to registry and leave table as ToS
       lua_pop(L, 1);
-      lua_newtable(L);//push new table for swtmr.timer_cb_list
-      // add swtimer table to L_REGISTRY
-      push_swtmr_registry_key(L);
-      lua_pushvalue(L, -2);
-      lua_rawset(L, L_REGISTRY);
+      lua_newtable(L);
+      lua_pushvalue(L, -1);
+      set_swtmr_registry(L);
     }
 
     lua_pushstring(L, CB_LIST_STR);
@@ -439,41 +428,40 @@ static void process_cb_register_queue(task_param_t param, uint8 priority)
 
 #ifdef SWTMR_DEBUG
 int print_timer_list(lua_State* L){
-   push_swtmr_registry_key(L);
-   lua_rawget(L, L_REGISTRY);
-   lua_pushstring(L, CB_LIST_STR);
-   lua_rawget(L, -2);
-   if(!lua_istable(L, -2) || !lua_istable(L, -1)){
-     lua_pop(L, 2);
-     return 0;
+  get_swtmr_registry(L);
+  if(!lua_istable(L, -1)) {lua_pop(L, 1); return 0;}
+
+  lua_pushstring(L, CB_LIST_STR);
+  lua_rawget(L, -2);
+  if(!lua_istable(L, -1)) {lua_pop(L, 2); return 0;}
+
+  os_timer_t* suspended_timer_list_head = NULL;
+  os_timer_t* suspended_timer_list_tail = NULL;
+  lua_pushstring(L, SUSP_LIST_STR);
+  lua_rawget(L, -3);
+  if(lua_isuserdata(L, -1)){
+   suspended_timer_list_head = suspended_timer_list_tail = lua_touserdata(L, -1);
+   while(suspended_timer_list_tail->timer_next != NULL){
+     suspended_timer_list_tail = suspended_timer_list_tail->timer_next;
    }
-   os_timer_t* suspended_timer_list_head = NULL;
-   os_timer_t* suspended_timer_list_tail = NULL;
-   lua_pushstring(L, SUSP_LIST_STR);
-   lua_rawget(L, -3);
+  }
+  lua_pop(L, 1);
+  size_t registered_cb_qty = lua_objlen(L, -1);
+  cb_registry_item_t** cb_reg_array = calloc(1,sizeof(cb_registry_item_t*)*registered_cb_qty);
+  if(!cb_reg_array){
+   luaL_error(L, "%s: unable to suspend timers, out of memory!", __func__);
+   return 0;
+  }
+  uint8 index = 0;
+  lua_pushnil(L);
+  while(lua_next(L, -2) != 0){
    if(lua_isuserdata(L, -1)){
-     suspended_timer_list_head = suspended_timer_list_tail = lua_touserdata(L, -1);
-     while(suspended_timer_list_tail->timer_next != NULL){
-       suspended_timer_list_tail = suspended_timer_list_tail->timer_next;
-     }
-   }
-   lua_pop(L, 1);
-   size_t registered_cb_qty = lua_objlen(L, -1);
-   cb_registry_item_t** cb_reg_array = calloc(1,sizeof(cb_registry_item_t*)*registered_cb_qty);
-   if(!cb_reg_array){
-     luaL_error(L, "%s: unable to suspend timers, out of memory!", __func__);
-     return 0;
-   }
-   uint8 index = 0;
-   lua_pushnil(L);
-   while(lua_next(L, -2) != 0){
-     if(lua_isuserdata(L, -1)){
-       cb_reg_array[index] = lua_touserdata(L, -1);
-     }
-     lua_pop(L, 1);
-     index++;
+     cb_reg_array[index] = lua_touserdata(L, -1);
    }
    lua_pop(L, 1);
+   index++;
+  }
+  lua_pop(L, 1);
 
 
   os_timer_t* timer_list_ptr = timer_list;
@@ -499,9 +487,7 @@ int print_timer_list(lua_State* L){
 }
 
 int print_susp_timer_list(lua_State* L){
-  push_swtmr_registry_key(L);
-  lua_rawget(L, L_REGISTRY);
-
+  get_swtmr_registry(L);
   if(!lua_istable(L, -1)){
     return luaL_error(L, "swtmr table not found!");
   }
@@ -516,7 +502,9 @@ int print_susp_timer_list(lua_State* L){
   os_timer_t* susp_timer_list_ptr = lua_touserdata(L, -1);
   dbg_printf("\n\tsuspended_timer_list:\n");
   while(susp_timer_list_ptr != NULL){
-    dbg_printf("\tptr:%p\tcb:%p\texpire:%8u\tperiod:%8u\tnext:%p\n",susp_timer_list_ptr, susp_timer_list_ptr->timer_func, susp_timer_list_ptr->timer_expire, susp_timer_list_ptr->timer_period, susp_timer_list_ptr->timer_next);
+    dbg_printf("\tptr:%p\tcb:%p\texpire:%8u\tperiod:%8u\tnext:%p\n",susp_timer_list_ptr, 
+               susp_timer_list_ptr->timer_func, susp_timer_list_ptr->timer_expire, 
+               susp_timer_list_ptr->timer_period, susp_timer_list_ptr->timer_next);
     susp_timer_list_ptr = susp_timer_list_ptr->timer_next;
   }
   return 0;
@@ -542,5 +530,5 @@ LROT_END(test_swtimer_debug, NULL, 0)
 
 NODEMCU_MODULE(SWTMR_DBG, "SWTMR_DBG", test_swtimer_debug, NULL);
 
-#endif
+#endif /* SWTMR_DEBUG */
 

+ 1 - 0
app/spiffs/nodemcu_spiffs.h

@@ -22,4 +22,5 @@
 // Reduce the chance of returning disk full
 #define SPIFFS_GC_MAX_RUNS          256
 
+#define SPIFFS_SECURE_ERASE         0
 #endif

+ 1 - 1
app/spiffs/spiffs.c

@@ -93,7 +93,7 @@ static bool myspiffs_set_cfg(spiffs_config *cfg, bool force_create) {
  
   if (cfg->phys_size < MIN_BLOCKS_FS * LOG_BLOCK_SIZE_SMALL_FS) {
     return FALSE;
-  } else if (cfg->phys_size < MIN_BLOCKS_FS * LOG_BLOCK_SIZE_SMALL_FS) {
+  } else if (cfg->phys_size < MIN_BLOCKS_FS * LOG_BLOCK_SIZE) {
     cfg->log_block_size = LOG_BLOCK_SIZE_SMALL_FS;
   } else  {
     cfg->log_block_size = LOG_BLOCK_SIZE;

+ 11 - 1
app/spiffs/spiffs_nucleus.c

@@ -889,6 +889,16 @@ s32_t spiffs_page_delete(
   fs->stats_p_deleted++;
   fs->stats_p_allocated--;
 
+#if SPIFFS_SECURE_ERASE
+  // Secure erase
+  unsigned char data[SPIFFS_CFG_LOG_PAGE_SZ(fs) - sizeof(spiffs_page_header)];
+  bzero(data, sizeof(data));
+  res = _spiffs_wr(fs,  SPIFFS_OP_T_OBJ_DA | SPIFFS_OP_C_DELE,
+      0,
+      SPIFFS_PAGE_TO_PADDR(fs, pix) + sizeof(spiffs_page_header), sizeof(data), data);
+  SPIFFS_CHECK_RES(res);
+#endif
+
   // mark deleted in source page
   u8_t flags = 0xff;
 #if SPIFFS_NO_BLIND_WRITES
@@ -2030,7 +2040,7 @@ s32_t spiffs_object_read(
     // remaining data in page
     len_to_read = MIN(len_to_read, SPIFFS_DATA_PAGE_SIZE(fs) - (cur_offset % SPIFFS_DATA_PAGE_SIZE(fs)));
     // remaining data in file
-    len_to_read = MIN(len_to_read, fd->size);
+    len_to_read = MIN(len_to_read, fd->size - cur_offset);
     SPIFFS_DBG("read: offset:"_SPIPRIi" rd:"_SPIPRIi" data spix:"_SPIPRIsp" is data_pix:"_SPIPRIpg" addr:"_SPIPRIad"\n", cur_offset, len_to_read, data_spix, data_pix,
         (u32_t)(SPIFFS_PAGE_TO_PADDR(fs, data_pix) + sizeof(spiffs_page_header) + (cur_offset % SPIFFS_DATA_PAGE_SIZE(fs))));
     if (len_to_read <= 0) {

+ 4 - 12
app/spiffs/spiffs_nucleus.h

@@ -262,8 +262,8 @@
 #define SPIFFS_FH_OFFS(fs, fh)   ((fh) != 0 ? ((fh) + (fs)->cfg.fh_ix_offset) : 0)
 #define SPIFFS_FH_UNOFFS(fs, fh) ((fh) != 0 ? ((fh) - (fs)->cfg.fh_ix_offset) : 0)
 #else
-#define SPIFFS_FH_OFFS(fs, fh)   (fh)
-#define SPIFFS_FH_UNOFFS(fs, fh) (fh)
+#define SPIFFS_FH_OFFS(fs, fh)   ((spiffs_file)(fh))
+#define SPIFFS_FH_UNOFFS(fs, fh) ((spiffs_file)(fh))
 #endif
 
 
@@ -476,9 +476,6 @@ typedef struct {
 
 
 // object structs
-#ifdef _MSC_VER
-#pragma pack ( push, 1 )
-#endif
 
 // page header, part of each page except object lookup pages
 // NB: this is always aligned when the data page is an object index,
@@ -496,10 +493,9 @@ typedef struct SPIFFS_PACKED {
 typedef struct SPIFFS_PACKED
 #if SPIFFS_ALIGNED_OBJECT_INDEX_TABLES
 #ifdef _MSC_VER
-//Note: the following needs to track the sizeof(spiffs_page_ix), which is defined in spiffs_config.h
-                __declspec( align( 2 ) )
+                 __declspec( align( 2 ) ) // must track sizeof(spiffs_page_ix) in spiffs_config.h
 #else
-                __attribute(( aligned(sizeof(spiffs_page_ix)) ))
+                 __attribute(( aligned(sizeof(spiffs_page_ix)) ))
 #endif
 #endif
 {
@@ -525,10 +521,6 @@ typedef struct SPIFFS_PACKED {
  u8_t _align[4 - ((sizeof(spiffs_page_header)&3)==0 ? 4 : (sizeof(spiffs_page_header)&3))];
 } spiffs_page_object_ix;
 
-#ifdef _MSC_VER
-#pragma pack ( pop )
-#endif
-
 // callback func for object lookup visitor
 typedef s32_t (*spiffs_visitor_f)(spiffs *fs, spiffs_obj_id id, spiffs_block_ix bix, int ix_entry,
     const void *user_const_p, void *user_var_p);

+ 2 - 2
app/u8g2lib/u8x8_d_fbrle.c

@@ -86,7 +86,7 @@ static uint8_t u8x8_d_fbrle(u8x8_t *u8x8, uint8_t msg, uint8_t arg_int, void *ar
         lua_State *L = lua_getstate();
         lua_rawgeti( L, LUA_REGISTRYINDEX, ext_u8g2->overlay.rfb_cb_ref );
         lua_pushnil( L );
-        lua_call( L, 1, 0 );
+        luaL_pcallx( L, 1, 0 );
       }
       // and note ongoing framebuffer update
       ext_u8g2->overlay.fb_update_ongoing = 1;
@@ -143,7 +143,7 @@ static uint8_t u8x8_d_fbrle(u8x8_t *u8x8, uint8_t msg, uint8_t arg_int, void *ar
 
           lua_rawgeti( L, LUA_REGISTRYINDEX, ext_u8g2->overlay.rfb_cb_ref );
           lua_pushlstring( L, (const char *)fbrle_line, fbrle_line_size );
-          lua_call( L, 1, 0 );
+          luaL_pcallx( L, 1, 0 );
         }
       }
 

+ 1 - 1
lua_modules/liquidcrystal/lc-gpio4bit.lua

@@ -1,4 +1,4 @@
-local gpio, bit = gpio, bit
+local gpio, bit = gpio, bit --luacheck: read globals gpio bit
 
 return function(bus_args)
    local rs = bus_args.rs or 0

+ 1 - 1
lua_modules/liquidcrystal/lc-gpio8bit.lua

@@ -1,4 +1,4 @@
-local gpio, bit = gpio, bit
+local gpio, bit = gpio, bit --luacheck: read globals gpio bit
 
 return function(bus_args)
    local rs = bus_args.rs or 0

+ 1 - 1
lua_modules/liquidcrystal/lc-i2c4bit.lua

@@ -1,4 +1,4 @@
-local i2c, bit = i2c, bit
+local i2c, bit = i2c, bit --luacheck: read globals i2c bit
 
 return function(bus_args)
    local busid = bus_args.id or 0

+ 1 - 1
lua_modules/liquidcrystal/liquidcrystal.lua

@@ -1,4 +1,4 @@
-local bit = bit
+local bit = bit --luacheck: read globals bit
 -- metatable
 local LiquidCrystal = {}
 LiquidCrystal.__index = LiquidCrystal