From 32e161b131400a0cd215fddd50733d26dd98ff93 Mon Sep 17 00:00:00 2001 From: Terry Ellison Date: Tue, 10 Dec 2019 21:27:06 +0000 Subject: [PATCH] UART fixes and lua.c merge --- app/driver/input.c | 10 +- app/lua/lapi.c | 4 +- app/lua/lauxlib.c | 6 +- app/lua/lstate.c | 3 - app/lua/lua.c | 311 +------------------------------------------ app/lua/lua.h | 11 +- app/lua/luaconf.h | 28 ++-- app/lua53/lnodemcu.c | 3 +- app/lua53/lstate.c | 4 - app/lua53/lua.c | 300 ++++++++++++++++++++++------------------- app/modules/adc.c | 32 +++-- app/modules/uart.c | 17 ++- app/user/user_main.c | 5 +- docs/modules/uart.md | 2 +- 14 files changed, 222 insertions(+), 514 deletions(-) mode change 100644 => 120000 app/lua/lua.c diff --git a/app/driver/input.c b/app/driver/input.c index c6beedcbba..4d4f1a8378 100644 --- a/app/driver/input.c +++ b/app/driver/input.c @@ -115,8 +115,8 @@ void input_setprompt (const char *prompt) { ** the bool ins.run_input. ** - TRUE: it clears the UART FIFO up to EOL, doing any callback and sending ** the line to Lua. -** - FALSE: it clears the UART FIFO doing callbacks according to the data_len / -** end_char break. +** - FALSE: it clears the UART FIFO doing callbacks according to the data_len +** or end_char break. */ extern void lua_input_string (const char *line, int len); @@ -180,9 +180,9 @@ static bool input_readline(void) { } else { while (uart_getc(&ch)) { ins.data[ins.line_pos++] = ch; - if( ins.line_pos >= ins.len || - (ins.data_len > 0 && ins.line_pos >= ins.data_len) || - ch == ins.end_char ) { + if( ins.line_pos >= ins.len || + (ins.data_len >= 0 && ins.line_pos >= ins.data_len) || + (ins.data_len < 0 && ch == ins.end_char )) { ins.uart_cb(ins.data, ins.line_pos); ins.line_pos = 0; } diff --git a/app/lua/lapi.c b/app/lua/lapi.c index a10786b1a2..b1e384bd68 100644 --- a/app/lua/lapi.c +++ b/app/lua/lapi.c @@ -530,9 +530,9 @@ LUA_API void lua_pushrotable (lua_State *L, const ROTable *t) { lua_unlock(L); } -LUA_API void lua_pushlightfunction(lua_State *L, void *p) { +LUA_API void lua_pushlightfunction(lua_State *L, lua_CFunction f) { lua_lock(L); - setfvalue(L->top, p); + setfvalue(L->top, f); api_incr_top(L); lua_unlock(L); } diff --git a/app/lua/lauxlib.c b/app/lua/lauxlib.c index 78d0755d25..aaa0f99b7c 100644 --- a/app/lua/lauxlib.c +++ b/app/lua/lauxlib.c @@ -900,13 +900,13 @@ LUALIB_API void luaL_assertfail(const char *file, int line, const char *message) * which will then sync up with the remote GDB client to allow forensics of the error. */ #ifdef LUA_CROSS_COMPILER -LUALIB_API void luaL_dbgbreak(void) { - puts("debug break"); /* allows BT analysis of assert fails */ +LUALIB_API void lua_debugbreak(void) { + puts(" lua_debugbreak "); /* allows BT analysis of assert fails */ } #else extern void gdbstub_init(void); -LUALIB_API void luaL_dbgbreak(void) { +LUALIB_API void lua_debugbreak(void) { static int repeat_entry = 0; if (repeat_entry == 0) { dbg_printf("Start up the gdb stub if not already started\n"); diff --git a/app/lua/lstate.c b/app/lua/lstate.c index 5e497d3d57..1edfd5c68f 100644 --- a/app/lua/lstate.c +++ b/app/lua/lstate.c @@ -229,9 +229,6 @@ lua_State *lua_open(void) { return lua_crtstate; } -lua_State *lua_getstate(void) { - return lua_crtstate; -} LUA_API void lua_close (lua_State *L) { #ifndef LUA_CROSS_COMPILER lua_sethook( L, NULL, 0, 0 ); diff --git a/app/lua/lua.c b/app/lua/lua.c deleted file mode 100644 index a7e5b25c1a..0000000000 --- a/app/lua/lua.c +++ /dev/null @@ -1,310 +0,0 @@ -/* -** NodeMCU Lua 5.1 main initiator and comand interpreter -** See Copyright Notice in lua.h -** -** Note this is largely a backport of some new Lua 5.3 version but -** with API changes for Lua 5.1 compatability -*/ - -#include -#include -#include -#include "user_version.h" -#include "driver/input.h" - -#define lua_c - -#include "lua.h" - -#include "lauxlib.h" -#include "lualib.h" -#include "llimits.h" -#include "os_type.h" - -#include "platform.h" - -extern int pipe_create(lua_State *L); -extern int pipe_read(lua_State *L); -extern int pipe_unread(lua_State *L); - -#ifndef LUA_INIT_STRING -#define LUA_INIT_STRING "@init.lua" -#endif - -static int MLref = LUA_NOREF; -lua_State *globalL = NULL; - -static int pmain (lua_State *L); -static int dojob (lua_State *L); - -static void l_message (const char *msg) { - lua_writestringerror("%s\n", msg); -} - -static int report (lua_State *L, int status) { - if (status && !lua_isnil(L, -1)) { - const char *msg = lua_tostring(L, -1); - if (msg == NULL) msg = "(error object is not a string)"; - l_message(msg); - lua_pop(L, 1); - } - return status; -} - -static void l_print(lua_State *L, int n) { - lua_getglobal(L, "print"); - lua_insert(L, -n-1); - if (lua_pcall(L, n, 0, 0) != 0) - l_message(lua_pushfstring(L, "error calling " LUA_QL("print") " (%s)", - lua_tostring(L, -1))); -} - -static int 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 */ - lua_pushinteger(L, 2); /* skip this function and traceback */ - lua_call(L, 2, 1); /* call debug.traceback */ - } - lua_settop(L, 1); - return 1; -} - -static int docall (lua_State *L, int narg, int nres) { - int status; - int base = lua_gettop(L) - narg; /* function index */ - lua_pushlightfunction(L, &traceback); /* push traceback function */ - lua_insert(L, base); /* put it under chunk and args */ - status = lua_pcall(L, narg, (nres ? 0 : LUA_MULTRET), base); - lua_remove(L, base); /* remove traceback function */ - /* force a complete garbage collection in case of errors */ - if (status != 0) lua_gc(L, LUA_GCCOLLECT, 0); - return status; -} - -static void print_version (lua_State *L) { - lua_pushliteral (L, "\n" NODE_VERSION " build " BUILD_DATE - " powered by " LUA_RELEASE " on SDK "); - lua_pushstring (L, SDK_VERSION); - lua_concat (L, 2); - const char *msg = lua_tostring (L, -1); - l_message (msg); - lua_pop (L, 1); -} - -static int dofile (lua_State *L, const char *name) { - int status = luaL_loadfile(L, name) || docall(L, 0, 1); - return report(L, status); -} - -static int dostring (lua_State *L, const char *s, const char *name) { - int status = luaL_loadbuffer(L, s, strlen(s), name) || docall(L, 0, 1); - return report(L, status); -} - -static const char *get_prompt (lua_State *L, int firstline) { - const char *p; - lua_getglobal(L, firstline ? "_PROMPT" : "_PROMPT2"); - p = lua_tostring(L, -1); - if (p == NULL) p = (firstline ? LUA_PROMPT : LUA_PROMPT2); - lua_pop(L, 1); /* remove global */ - return p; -} - -#define EOFMARK LUA_QL("") -static int incomplete (lua_State *L, int status) { - if (status == LUA_ERRSYNTAX) { - size_t lmsg; - const char *msg = lua_tolstring(L, -1, &lmsg); - if (!strcmp(msg+lmsg-sizeof(EOFMARK)+1, EOFMARK)) { - lua_pop(L, 1); - return 1; - } - } - return 0; -} - -/* -** dojob is the CB reader for the input pipe and follows the calling convention -** for pipe reader CBs. It has one argument: the stdin pipe that it is reading. -*/ -static int dojob (lua_State *L) { - size_t l; - int status; - const char *prompt; - -//dbg_printf("dojob entered\n"); - lua_settop(L, 1); /* pipe obj at S[1] */ - lua_pushlightfunction(L, &pipe_read); /* pobj:read at S[2] */ - lua_pushvalue(L, 1); /* dup pobj to S[3] */ - lua_pushliteral(L, "\n+"); /* S[4] = "\n+" */ - lua_call(L, 2, 1); /* S[2] = pobj:read("\n+") */ - const char* b = lua_tolstring(L, 2, &l); /* b = NULL if S[2] is nil */ - - if ((lua_isnil(L, 2) || l == 0)) { - /* If the pipe is empty then return false to suppress automatic reposting */ -//dbg_printf("stdin empty\n"); - lua_pushboolean(L, false); - return 1; /* return false */ - } - - if (b[l-1] != '\n') { -//dbg_printf("unreading part line\n"); - /* likewise if not CR terminated, then unread and ditto */ - lua_pushlightfunction(L, &pipe_unread); /* pobj:read at S[1] */ - lua_insert(L, 1); - lua_call(L, 2, 0); /* pobj:unread(line) */ - lua_pushboolean(L, false); - return 1; /* return false */ - } else { -//dbg_printf("popping CR terminated string(%d) %s", l-1, b); - } - /* - * Now we can process a proper CR terminated line - */ - lua_pushlstring(L, b, --l); /* remove end CR */ - lua_remove(L, 2); - b = lua_tostring(L, 2); - - if (MLref != LUA_NOREF) { - /* processing multiline */ - lua_rawgeti(L, LUA_REGISTRYINDEX, MLref); /* insert prev lines(s) */ - lua_pushliteral(L, "\n"); /* insert CR */ - lua_pushvalue(L, 2); /* dup new line */ - lua_concat(L, 3); /* concat all 3 */ - lua_remove(L, 2); /* and shift down to S[2] */ - } else if (b[0] == '=') { - /* If firstline and of the format = */ - lua_pushfstring(L, "return %s", b+1); - lua_remove(L, 2); - } - - /* ToS is at S[2] which contains the putative chunk to be compiled */ - - status = luaL_loadbuffer(L, lua_tostring(L, 2), lua_strlen(L, 2), "=stdin"); - - if (incomplete(L, status)) { - /* Store line back in the Reg mlref sot */ - if (MLref == LUA_NOREF) { - MLref = luaL_ref(L, LUA_REGISTRYINDEX); - } else { - lua_rawseti(L, LUA_REGISTRYINDEX, MLref); - } - } else { - /* compile finished OK or with hard error */ - lua_remove(L, 2); /* remove line because now redundant */ - if (MLref!= LUA_NOREF) { /* also remove multiline if it exists */ - luaL_unref(L, LUA_REGISTRYINDEX, MLref); - MLref= LUA_NOREF; - } - /* Execute the compiled chunk of successful */ - if (status == 0) { - status = docall(L, 0, 0); - } - /* print any returned results or error message */ - if (status && !lua_isnil(L, -1)) - l_print(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); - } - - prompt = get_prompt(L, MLref!= LUA_NOREF ? 0 : 1); - input_setprompt(prompt); - puts(prompt); - - lua_pushnil(L); - return 1; /* return nil will retask if pipe not empty */ -} - - -/* - * Kick off library and UART input handling before opening the module - * libraries. Note that as this is all done within a Lua task, so error - * handling is left to the Lua task traceback mechanism. - */ -extern void luaL_dbgbreak(void); - -static int pmain (lua_State *L) { - const char *RCRinit = NULL; - uint32_t n = platform_rcr_read(PLATFORM_RCR_INITSTR, (void**) &RCRinit); - const char *init = RCRinit ? RCRinit : LUA_INIT_STRING; - globalL = L; - UNUSED(n); - -//*DEBUG*/luaL_dbgbreak(); - lua_gc(L, LUA_GCSTOP, 0); /* stop GC during initialization */ - luaL_openlibs(L); /* open libraries */ - lua_gc(L, LUA_GCRESTART, 0); /* restart GC and set EGC mode */ - lua_setegcmode( L, EGC_ALWAYS, 4096 ); - lua_settop(L, 0); - - lua_pushliteral(L, "stdin"); - lua_pushlightfunction(L, &pipe_create); - lua_pushlightfunction(L, &dojob); - lua_pushinteger(L, LUA_TASK_LOW); - lua_call(L, 2, 1); /* ToS = pipe.create(dojob, low_priority) */ - lua_rawset(L, LUA_REGISTRYINDEX); /* and stash input pipe in Reg["stdin"] */ - - input_setup(LUA_MAXINPUT, get_prompt(L, 1)); - lua_input_string(" \n", 2); /* queue CR to issue first prompt */ - print_version(L); -//*DEBUG*/luaL_dbgbreak(); - - /* and last of all, kick off application initialisation */ - if (init[0] == '@') - dofile(L, init+1); - else - dostring(L, init, LUA_INIT); -//*DEBUG*/luaL_dbgbreak(); - return 0; -} - -/* -** The system initialisation CB nodemcu_init() calls lua_main() to startup -** the Lua environment by calling lua_open() which initiates the core Lua VM. -** The initialisation of the libraries, etc. is carried out by pmain in a -** separate Lua task, which also kicks off the user application through the -** LUA_INIT_STRING hook. -*/ -void lua_main (void) { - lua_State *L = lua_open(); /* create state */ - if (L == NULL) { - l_message("cannot create state: not enough memory"); - return; - } - lua_pushlightfunction(L, &pmain); /* Call 'pmain' as a high priority task */ - luaL_posttask(L, LUA_TASK_HIGH); -} - -/* -** The Lua interpreter is event-driven and task-oriented in NodeMCU rather than -** based on a readline poll loop as in the standard implementation. Input lines -** can come from one of two sources: the application can "push" lines for the -** interpreter to compile and execute, or they can come from the UART. To -** minimise application blocking, the lines are queued in a pipe when received, -** with the Lua interpreter task attached to the pipe as its reader task. This -** CB processes one line of input per task execution. -** -** Even though lines can be emitted from independent sources (the UART and the -** node API), and they could in theory get interleaved, the strategy here is -** "let the programmer beware": interactive input will normally only occur in -** development and injected input occur in telnet type applications. If there -** is a need for interlocks, then the application should handle this. -*/ -//static int n = 0; -void lua_input_string (const char *line, int len) { - lua_State *L = globalL; - lua_getfield(L, LUA_REGISTRYINDEX, "stdin"); - lua_rawgeti(L, -1, 1); /* get the pipe_write from stdin[1] */ - lua_insert(L, -2); /* stick above the pipe */ - lua_pushlstring(L, line, len); - -//const char*b = lua_tostring(L, -1); -//dbg_printf("Pushing (%u): %s", len, b); - lua_call(L, 2, 0); /* stdin:write(line) */ -} diff --git a/app/lua/lua.c b/app/lua/lua.c new file mode 120000 index 0000000000..dc86a772fb --- /dev/null +++ b/app/lua/lua.c @@ -0,0 +1 @@ +../lua53/lua.c \ No newline at end of file diff --git a/app/lua/lua.h b/app/lua/lua.h index e595d2c473..350ad6ff3d 100644 --- a/app/lua/lua.h +++ b/app/lua/lua.h @@ -39,7 +39,8 @@ #define lua_upvalueindex(i) (LUA_GLOBALSINDEX-(i)) -/* thread status; 0 is OK */ +/* thread status */ +#define LUA_OK 0 #define LUA_YIELD 1 #define LUA_ERRRUN 2 #define LUA_ERRSYNTAX 3 @@ -176,7 +177,7 @@ LUA_API const char *(lua_pushfstring) (lua_State *L, const char *fmt, ...); LUA_API void (lua_pushcclosure) (lua_State *L, lua_CFunction fn, int n); LUA_API void (lua_pushboolean) (lua_State *L, int b); LUA_API void (lua_pushlightuserdata) (lua_State *L, void *p); -LUA_API void (lua_pushlightfunction) (lua_State *L, void *p); +LUA_API void (lua_pushlightfunction) (lua_State *L, lua_CFunction f); LUA_API int (lua_pushthread) (lua_State *L); @@ -268,7 +269,7 @@ LUA_API void lua_setallocf (lua_State *L, lua_Alloc f, void *ud); #define lua_register(L,n,f) (lua_pushcfunction(L, (f)), lua_setglobal(L, (n))) -#define lua_pushcfunction(L,f) lua_pushcclosure(L, (f), 0) +#define lua_pushcfunction(L,f) lua_pushlightfunction(L, (f)) #define lua_strlen(L,i) lua_objlen(L, (i)) @@ -403,7 +404,7 @@ LUA_API void (lua_createrotable) (lua_State *L, ROTable *t, const ROTable_entry /**DEBUG**/extern void dbg_printf(const char *fmt, ...) __attribute__ ((format (printf, 1, 2))); -void lua_main (void); +int lua_main (void); void lua_input_string (const char *line, int len); #define luaN_freearray(L,b,l) luaM_freearray(L,b,l,sizeof(*b)); @@ -420,7 +421,7 @@ LUA_API void lua_setegcmode(lua_State *L, int mode, int limit); #define dbg_printf printf #endif -extern void luaL_dbgbreak(void); +extern void lua_debugbreak(void); /****************************************************************************** * Copyright (C) 1994-2008 Lua.org, PUC-Rio. All rights reserved. diff --git a/app/lua/luaconf.h b/app/lua/luaconf.h index d233569fa3..f21e0642d4 100644 --- a/app/lua/luaconf.h +++ b/app/lua/luaconf.h @@ -273,16 +273,6 @@ #endif -/* -@@ LUA_PROMPT is the default prompt used by stand-alone Lua. -@@ LUA_PROMPT2 is the default continuation prompt used by stand-alone Lua. -** CHANGE them if you want different prompts. (You can also change the -** prompts dynamically, assigning to globals _PROMPT/_PROMPT2.) -*/ -#define LUA_PROMPT "> " -#define LUA_PROMPT2 ">> " - - /* @@ LUA_PROGNAME is the default name for the stand-alone Lua program. ** CHANGE it if your stand-alone interpreter has a different name and @@ -340,18 +330,22 @@ extern int readline4lua(const char *prompt, char *buffer, int length); ** They are only used in libraries and the stand-alone program. (The #if ** avoids including 'stdio.h' everywhere.) */ -#if !defined(LUA_USE_STDIO) -#define lua_writestring(s, l) puts(s) -#define luai_writeline() puts("\n") -#endif // defined(LUA_USE_STDIO) +#ifdef LUA_USE_ESP +#define lua_writestring(s,l) output_redirect((s),(l)) +#else +#define lua_writestring(s,l) fwrite((s), sizeof(char), (l), stdout) +#endif +#define luai_writeline() lua_writestring("\n",1) /* @@ lua_writestringerror defines how to print error messages. ** (A format string with one argument is enough for Lua...) */ -#if !defined(LUA_USE_STDIO) +#ifdef LUA_USE_ESP #define lua_writestringerror(s,p) dbg_printf((s), (p)) -#endif // defined(LUA_USE_STDIO) +#else +#define lua_writestringerror(s,p) fprintf(stderr, (s), (p)) +#endif /* }================================================================== */ @@ -905,6 +899,6 @@ union luai_Cast { double l_d; long l_l; }; #error "Pipes not supported NodeMCU firmware" #endif -#define LUA_DEBUG_HOOK luaL_dbgbreak +#define LUA_DEBUG_HOOK lua_debugbreak #endif diff --git a/app/lua53/lnodemcu.c b/app/lua53/lnodemcu.c index 806101bd2c..fb17e9dc0a 100644 --- a/app/lua53/lnodemcu.c +++ b/app/lua53/lnodemcu.c @@ -140,7 +140,8 @@ void luaN_setabsolute(lu_int32 addr) { static lu_int32 platform_flash_get_partition (lu_int32 part_id, lu_int32 *addr) { lua_assert(part_id == NODEMCU_LFS0_PARTITION); if (!LFSregion) { - aligned_malloc(LFSregion, LFS_SIZE); + if(aligned_malloc(LFSregion, LFS_SIZE)) + return 0; memset(LFSregion, ~0, LFS_SIZE); lockFlashWrite(); } diff --git a/app/lua53/lstate.c b/app/lua53/lstate.c index cc48c2721d..e310164f4e 100644 --- a/app/lua53/lstate.c +++ b/app/lua53/lstate.c @@ -307,10 +307,6 @@ void luaE_freethread (lua_State *L, lua_State *L1) { luaM_free(L, l); } -LUA_API lua_State *lua_getstate (void) { - return L0; -} - LUA_API KeyCache *(lua_getcache) (int lineno) { return &G(L0)->cache[lineno][0]; } diff --git a/app/lua53/lua.c b/app/lua53/lua.c index b0db2fd3e9..4878bad1e5 100644 --- a/app/lua53/lua.c +++ b/app/lua53/lua.c @@ -1,14 +1,8 @@ /* -** Lua stand-alone interpreter +** NodeMCU Lua 5.1 and 5.3 main initiator and comand interpreter ** See Copyright Notice in lua.h */ -#define lua_c - -#define LUA_CORE -#include "lprefix.h" - - #include #include #include @@ -16,37 +10,45 @@ #include "driver/input.h" #define lua_c +#define LUA_CORE #include "lua.h" #include "lauxlib.h" #include "lualib.h" + +#if LUA_VERSION_NUM == 501 +#define LUA_VERSION_51 +#include "llimits.h" +#include "os_type.h" +#else /* LUA_VERSION_NUM == 503 */ +#define LUA_VERSION_53 +#include "lprefix.h" #include "lgc.h" #include "lnodemcu.h" +#endif #include "platform.h" - #if !defined(LUA_PROMPT) #define LUA_PROMPT "> " #define LUA_PROMPT2 ">> " #endif - #ifndef LUA_INIT_STRING #define LUA_INIT_STRING "@init.lua" #endif /* -** The NodeMCU version of lua.c is structurally different for standard lua.c as -** a result of architectural drivers arising from its context and being initiated -** within the startup sequence of an IoT SoC embedded runtime. +** The NodeMCU version of lua.c is structurally different for standard lua.c +** as a result of architectural drivers arising from its context and being +** initiated within the startup sequence of an IoT SoC embedded runtime. ** ** 1) Processing is based on a single threaded event loop model (somewhat akin -** to Node.js), so access to most system services is asyncronous and uses a -** callback mechanism. The Lua interactive mode processes input lines from -** a stdin pipe within this framework. Input must be handled on a line by -** line basis and indeed other Lua tasks might interleave any multiline -** processing here. The doREPL approach doesn't work. +** to Node.js), so access to most system services is asyncronous and uses +** a callback mechanism. The Lua interactive mode processes input lines +** that are provided by the firmware on a line by line basis and indeed +** other Lua tasks might interleave any multiline processing, so the +** standard doREPL approach won't work. ** ** 2) Most OS services and enviroment processing are supported so much of the ** standard functionality is irrelevant and is stripped out for simplicity. @@ -54,14 +56,14 @@ ** 3) stderr and stdout redirection aren't offered as an OS service, so this ** is handled in the baselib print function and errors are sent to print. */ +lua_State *globalL = NULL; static int pmain (lua_State *L); -static int dojob (lua_State *L); void lua_input_string (const char *line, int len); /* -** Prints (calling the Lua 'print' function) any values on the stack +** Prints (calling the Lua 'print' function) to print n values on the stack */ static void l_print (lua_State *L, int n) { if (n > 0) { /* any result to be printed? */ @@ -76,19 +78,28 @@ static void l_print (lua_State *L, int n) { /* -** Message handler used to run all chunks +** 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 */ - else - msg = lua_pushfstring(L, "(error object is a %s value)", + 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 */ } - luaL_traceback(L, L, msg, 1); /* append a standard traceback */ +#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 */ } @@ -99,22 +110,19 @@ static int msghandler (lua_State *L) { */ static int docall (lua_State *L, int narg, int nres) { int status; - int base = lua_gettop(L) - narg; /* function index */ - lua_pushcfunction(L, msghandler); /* push message handler */ - lua_insert(L, base); /* put it under function and args */ + 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); - lua_remove(L, base); /* remove message handler from the stack */ + 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; } static void print_version (lua_State *L) { - lua_pushliteral (L, "\n" NODE_VERSION " build " BUILD_DATE - " powered by " LUA_RELEASE " on SDK "); - lua_pushstring (L, SDK_VERSION); - lua_concat (L, 2); - l_print(L, 1); + lua_writestringerror( "\n" NODE_VERSION " build " BUILD_DATE + " powered by " LUA_RELEASE " on SDK %s\n", SDK_VERSION); } @@ -130,16 +138,18 @@ static const char *get_prompt (lua_State *L, int firstline) { return p; } -/* mark in error messages for incomplete statements */ -#define EOFMARK "" -#define MARKLEN (sizeof(EOFMARK)/sizeof(char) - 1) - /* ** Check whether 'status' signals a syntax error and the error ** message at the top of the stack ends with the above mark for ** incomplete statements. */ +#ifdef LUA_VERSION_51 +#define EOFMARK LUA_QL("") +#else +#define EOFMARK "" +#endif +#define MARKLEN (sizeof(EOFMARK)/sizeof(char) - 1) static int incomplete (lua_State *L, int status) { if (status == LUA_ERRSYNTAX) { size_t lmsg; @@ -152,66 +162,35 @@ static int incomplete (lua_State *L, int status) { return 0; } +static void l_create_stdin (lua_State *L); /* -** dojob replaces the standard doREPL loop. If is configured as a CB reader -** from the stdin pipe, and follows the calling conventions for pipe reader -** CBs. It has one argument, the stdin pipe that it is reading. -** ** Note that the Lua stack can't be used to stash part-line components as ** 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. */ -static int dojob (lua_State *L) { - static int MLref = LUA_NOREF; - size_t l; +static void dojob (lua_State *L) { + static int MLref = LUA_NOREF; /* Lua Reg entry for cached multi-line */ int status; const char *prompt; - - lua_settop(L, 1); /* pipe obj at S[1] */ - lua_getfield(L, 1, "read"); /* pobj:read at S[2] */ - lua_pushvalue(L, 1); /* dup pobj to S[3] */ - lua_pushliteral(L, "\n+"); /* S[4] = "\n+" */ - lua_call(L, 2, 1); /* S[2] = pobj:read("\n+") */ - const char* b = lua_tolstring(L, 2, &l); /* b = NULL if S[2] is nil */ - /* - * If the pipe is empty, or the line not CR terminated, return false to - * suppress automatic reposting - */ - lua_pushboolean(L, false); - if ((lua_isnil(L, 2) || l == 0)) - return 1; /* return false if pipe empty */ - if (b[l-1] != '\n') { - /* likewise if then unread and ditto */ - lua_getfield(L, 1, "unread"); - lua_insert(L, 1); /* insert pipe.unread above the pipe */ - lua_call(L, 2, 0); /* pobj:unread(line) */ - return 1; /* return false */ - } - lua_pop(L, 1); /* dump false value at ToS */ - /* - * Now we can process a proper CR terminated line - */ - lua_pushlstring(L, b, --l); /* remove end CR */ - lua_remove(L, 2); - b = lua_tostring(L, 2); + size_t l; + const char *b = lua_tostring(L, -1); /* ToS contains next input line */ if (MLref != LUA_NOREF) { /* processing multiline */ lua_rawgeti(L, LUA_REGISTRYINDEX, MLref); /* insert prev lines(s) */ lua_pushliteral(L, "\n"); /* insert CR */ - lua_pushvalue(L, 2); /* dup new line */ + lua_pushvalue(L, -3); /* dup new line */ lua_concat(L, 3); /* concat all 3 */ - lua_remove(L, 2); /* and shift down to S[2] */ - } else if (b[0] == '=') { - /* If firstline and of the format = */ + lua_remove(L,-2); /* and shift down to ToS */ + } else if (b[0] == '=') { /* If firstline and of the format = */ lua_pushfstring(L, "return %s", b+1); - lua_remove(L, 2); - } + lua_remove(L, -2); + } /* * ToS is at S[2] which contains the putative chunk to be compiled */ - status = luaL_loadbuffer(L, lua_tostring(L, 2), lua_strlen(L, 2), "=stdin"); - + b = lua_tolstring(L, -1, &l); + status = luaL_loadbuffer(L, b, l, "=stdin"); if (incomplete(L, status)) { /* Store line back in the Reg mlref sot */ if (MLref == LUA_NOREF) @@ -220,8 +199,8 @@ static int dojob (lua_State *L) { lua_rawseti(L, LUA_REGISTRYINDEX, MLref); } else { /* compile finished OK or with hard error */ - lua_remove(L, 2); /* remove line because now redundant */ - if (MLref!= LUA_NOREF) { /* also remove multiline if it exists */ + lua_remove(L, -2); /* remove line because now redundant */ + if (MLref != LUA_NOREF) { /* also remove multiline if it exists */ luaL_unref(L, LUA_REGISTRYINDEX, MLref); MLref = LUA_NOREF; } @@ -230,7 +209,7 @@ static int dojob (lua_State *L) { status = docall(L, 0, 0); /* print any returned results or error message */ if (status && !lua_isnil(L, -1)) - l_print(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); @@ -240,33 +219,27 @@ static int dojob (lua_State *L) { prompt = get_prompt(L, MLref!= LUA_NOREF ? 0 : 1); input_setprompt(prompt); - lua_writestring(prompt,strlen(prompt)); /* Note prompt NOT REDIRECTED */ + lua_writestring(prompt,strlen(prompt)); lua_pushnil(L); - return 1; /* return nil will retask if pipe not empty */ } /* -** Main body of stand-alone interpreter (called as a posted task). +** Main body of standalone interpreter. */ -extern void system_restart(void); static int pmain (lua_State *L) { - const char *init = (char *) lua_touserdata(L, 1); + const char *init = LUA_INIT_STRING; int status; lua_gc(L, LUA_GCSTOP, 0); /* stop GC during initialization */ - luaL_openlibs(L); /* the nodemcu open will throw to signal an LFS reload */ + luaL_openlibs(L); /* Nodemcu open will throw to signal an LFS reload */ +#ifdef LUA_VERSION_51 + lua_setegcmode( L, EGC_ALWAYS, 4096 ); +#else lua_gc( L, LUA_GCSETMEMLIMIT, 4096 ); +#endif lua_gc(L, LUA_GCRESTART, 0); /* restart GC and set EGC mode */ lua_settop(L, 0); - lua_pushliteral(L, "stdin"); - lua_getglobal(L, "pipe"); - lua_getfield(L, -1, "create"); - lua_remove(L, -2); - lua_pushcfunction(L, dojob); - lua_pushinteger(L, LUA_TASK_LOW); - lua_call(L, 2, 1); /* ToS = pipe.create(dojob, low_priority) */ - lua_rawset(L, LUA_REGISTRYINDEX); /* and stash input pipe in Reg["stdin"] */ - + l_create_stdin(L); input_setup(LUA_MAXINPUT, get_prompt(L, 1)); lua_input_string(" \n", 2); /* queue CR to issue first prompt */ print_version(L); @@ -275,10 +248,10 @@ static int pmain (lua_State *L) { * LUA_INIT_STRING is a file reference and the file system is uninitialised * then attempting the open will trigger a file system format. */ - if (init[0] == '@') - status = luaL_loadfile(L, init+1); - else - status = luaL_loadbuffer(L, init, strlen(init), "=INIT"); + platform_rcr_read(PLATFORM_RCR_INITSTR, (void**) &init); + status = (init[0] == '@') ? + luaL_loadfile(L, init+1) : + luaL_loadbuffer(L, init, strlen(init), "=INIT"); if (status == LUA_OK) status = docall(L, 0, 0); if (status != LUA_OK) @@ -286,46 +259,52 @@ static int pmain (lua_State *L) { return 0; } -void lua_main (void); -static int cpmain (lua_State *L) { - const char *RCRinit = NULL; - uint32_t n = platform_rcr_read(PLATFORM_RCR_INITSTR, cast(void**, &RCRinit)); - const char *init = RCRinit ? RCRinit : LUA_INIT_STRING; - int status; - UNUSED(n); - lua_pushcfunction(L, pmain); - lua_pushlightuserdata(L, cast(void *,init)); - status = lua_pcall(L, 1, 1, 0); - if (status != LUA_OK && (lua_isboolean(L,-1) && lua_toboolean(L,-1))) { - /* - * An LFS image has been loaded so close and restart the RTS - */ - luaL_posttask(NULL, LUA_TASK_HIGH+1); /* NULL/high+1 restarts lua_main() */ - return 0; - } - return lua_error(L); /* rethrow the error for post task to report */ -} /* ** The system initialisation CB nodemcu_init() calls lua_main() to startup the ** Lua environment by calling luaL_newstate() which initiates the core Lua VM. -** The initialisation of the libraries, etc. is carried out by pmain in a -** separate Lua task, which also kicks off the user application through the -** LUA_INIT_STRING hook. +** The initialisation of the libraries, etc. can potentially throw errors and +** so is wrapped in a protected call which also kicks off the user application +** through the LUA_INIT_STRING hook. */ -void lua_main (void) { - lua_State *L = lua_getstate(); - if (L) - lua_close(L); - L = luaL_newstate(); /* create state */ +int lua_main (void) { + lua_State *L = luaL_newstate(); if (L == NULL) { lua_writestringerror( "cannot create state: %s", "not enough memory"); - return; + return 0; + } + globalL = L; + lua_pushcfunction(L, pmain); + if (docall(L, 0, 0) != LUA_OK) { + if (strstr(lua_tostring(L, -1),"!LFSrestart!")) { + lua_close(L); + return 1; /* non-zero return to flag LFS reload */ + } + l_print(L, 1); } - lua_pushcfunction(L, cpmain); /* Call 'pmain' wrapper as a high priority task */ - luaL_posttask(L, LUA_TASK_HIGH); + return 0; +} + + +lua_State *lua_getstate(void) { + return globalL; } +/* +** The Lua interpreter is event-driven and task-oriented in NodeMCU rather than +** based on a readline poll loop as in the standard implementation. Input lines +** can come from one of two sources: the application can "push" lines for the +** interpreter to compile and execute, or they can come from the UART. To +** minimise application blocking, the lines are queued in a pipe when received, +** with the Lua interpreter task attached to the pipe as its reader task. This +** CB processes one line of input per task execution. +** +** Even though lines can be emitted from independent sources (the UART and the +** node API), and they could in theory get interleaved, the strategy here is +** "let the programmer beware": interactive input will normally only occur in +** development and injected input occur in telnet type applications. If there +** is a need for interlocks, then the application should handle this. +*/ void lua_input_string (const char *line, int len) { lua_State *L = lua_getstate(); @@ -335,3 +314,54 @@ void lua_input_string (const char *line, int len) { lua_pushlstring(L, line, len); lua_call(L, 2, 0); /* stdin:write(line) */ } + +/* +** CB reader for the stdin pipe, and follows the calling conventions for a +** pipe readers; it has one argument, the stdin pipe that it is reading. +*/ +static int l_read_stdin (lua_State *L) { + size_t l; + lua_settop(L, 1); /* pipe obj at S[1] */ + lua_getfield(L, 1, "read"); /* pobj:read at S[2] */ + lua_pushvalue(L, 1); /* dup pobj to S[3] */ + lua_pushliteral(L, "\n+"); /* S[4] = "\n+" */ + lua_call(L, 2, 1); /* S[2] = pobj:read("\n+") */ + const char* b = lua_tolstring(L, 2, &l); /* b = NULL if S[2] is nil */ + /* + * If the pipe is empty, or the line not CR terminated, return false to + * suppress automatic reposting + */ + lua_pushboolean(L, false); + if ((lua_isnil(L, 2) || l == 0)) + return 1; /* return false if pipe empty */ + if (b[l-1] != '\n') { + /* likewise if not CR terminated, then unread and ditto */ + lua_getfield(L, 1, "unread"); + lua_insert(L, 1); /* insert pipe.unread above the pipe */ + lua_call(L, 2, 0); /* pobj:unread(line) */ + return 1; /* return false */ + } + lua_pop(L, 1); /* dump false value at ToS */ + /* + * Now we can process a proper CR terminated line + */ + lua_pushlstring(L, b, --l); /* remove end CR */ + lua_remove(L, 2); + dojob(L); + return 0; +} + + +/* +** Create and initialise the stdin pipe +*/ +static void l_create_stdin (lua_State *L) { + lua_pushliteral(L, "stdin"); + lua_getglobal(L, "pipe"); + lua_getfield(L, -1, "create"); + lua_remove(L, -2); + lua_pushcfunction(L, l_read_stdin); + lua_pushinteger(L, LUA_TASK_LOW); + lua_call(L, 2, 1); /* ToS = pipe.create(dojob, low_priority) */ + lua_rawset(L, LUA_REGISTRYINDEX); /* and stash input pipe in Reg["stdin"] */ +} diff --git a/app/modules/adc.c b/app/modules/adc.c index 5c33317bcf..9f380acc20 100644 --- a/app/modules/adc.c +++ b/app/modules/adc.c @@ -28,33 +28,31 @@ static int adc_readvdd33( lua_State* L ) static int adc_init107( lua_State *L ) { uint8_t byte107 = luaL_checkinteger (L, 1); + uint32_t init_data[SPI_FLASH_SEC_SIZE/sizeof(uint32_t)]; + partition_item_t pd_pt = {0,0,0}; + uint32_t init_sector; - uint32 init_sector = flash_rom_get_sec_num () - 4; + luaL_argcheck(L, cast(uint8_t, byte107+1) < 2, 1, "Invalid mode"); + system_partition_get_item(SYSTEM_PARTITION_PHY_DATA, &pd_pt); + init_sector = platform_flash_get_sector_of_address(pd_pt.addr); - // Note 32bit alignment so we can safely cast to uint32 for the flash api - char init_data[SPI_FLASH_SEC_SIZE] __attribute__((aligned(4))); - - if (SPI_FLASH_RESULT_OK != flash_read ( - init_sector * SPI_FLASH_SEC_SIZE, - (uint32 *)init_data, sizeof(init_data))) - return luaL_error(L, "flash read error"); + if (pd_pt.size == 0 || + platform_s_flash_read(init_data, pd_pt.addr, sizeof(init_data))==0) + return luaL_error(L, "flash read error"); // If it's already the correct value, we don't need to force it - if (init_data[107] == byte107) - { + if (cast(uint8_t *, init_data)[107] == byte107) { lua_pushboolean (L, false); return 1; } - // Nope, it differs, we need to rewrite it - init_data[107] = byte107; - if (SPI_FLASH_RESULT_OK != flash_erase (init_sector)) + cast(uint8_t *, init_data)[107] = byte107; + /* Only do erase if toggling 0x00 to 0xFF */ + if(byte107 && platform_flash_erase_sector(init_sector) != PLATFORM_OK) return luaL_error(L, "flash erase error"); - if (SPI_FLASH_RESULT_OK != flash_write ( - init_sector * SPI_FLASH_SEC_SIZE, - (uint32 *)init_data, sizeof(init_data))) - return luaL_error(L, "flash write error"); + if(platform_flash_write(init_data, pd_pt.addr, sizeof(init_data))==0) + return luaL_error(L, "flash write error"); lua_pushboolean (L, true); return 1; diff --git a/app/modules/uart.c b/app/modules/uart.c index 632348b0d7..6f2c80abfc 100644 --- a/app/modules/uart.c +++ b/app/modules/uart.c @@ -28,17 +28,13 @@ static int l_uart_on( lua_State* L ) bool run_input = true; luaL_argcheck(L, method && !strcmp(method, "data"), 1, "method not supported"); - if (lua_type( L, stack ) == LUA_TNUMBER) - { + if (lua_type( L, stack ) == LUA_TNUMBER) { data_len = luaL_checkinteger( L, stack ); luaL_argcheck(L, data_len >= 0 && data_len < LUA_MAXINPUT, stack, "wrong arg range"); stack++; - } - else if (lua_isstring(L, stack)) - { + } else if (lua_isstring(L, stack)) { const char *end = luaL_checklstring( L, stack, &el ); - data_len = 0; - end_char = (int16_t) end[0]; + end_char = end[0]; stack++; if(el!=1) { return luaL_error( L, "wrong arg range" ); @@ -52,12 +48,15 @@ static int l_uart_on( lua_State* L ) lua_pushvalue(L, stack); luaL_unref(L, LUA_REGISTRYINDEX, uart_receive_rf); uart_receive_rf = luaL_ref(L, LUA_REGISTRYINDEX); - } else { luaL_unref(L, LUA_REGISTRYINDEX, uart_receive_rf); uart_receive_rf = LUA_NOREF; } - input_setup_receive(uart_on_data_cb, data_len, end_char, run_input); + + if (uart_receive_rf == LUA_NOREF) { + input_setup_receive(NULL, 0, 0, 1); + } else + input_setup_receive(uart_on_data_cb, data_len, end_char, run_input); return 0; } diff --git a/app/user/user_main.c b/app/user/user_main.c index 5b517de6c2..c5639b9cba 100644 --- a/app/user/user_main.c +++ b/app/user/user_main.c @@ -29,7 +29,7 @@ #ifdef LUA_USE_MODULES_RTCTIME #include "rtc/rtctime.h" #endif -extern void lua_main (void); +extern int lua_main (void); /* Contents of esp_init_data_default.bin */ extern const uint32_t init_data[], init_data_end[]; @@ -278,7 +278,8 @@ void nodemcu_init(void) { NODE_DBG("Task task_lua starting.\n"); // Call the Lua bootstrap startup directly. This uses the task interface // internally to carry out the main lua libraries initialisation. - lua_main(); + if(lua_main()) + lua_main(); // If it returns true then LFS restart is needed } #ifdef LUA_USE_MODULES_WIFI diff --git a/docs/modules/uart.md b/docs/modules/uart.md index 3c00b05865..617cfef65e 100644 --- a/docs/modules/uart.md +++ b/docs/modules/uart.md @@ -45,7 +45,7 @@ Currently only the "data" event is supported. - if n<255, the callback is called when n chars are received - if one char "c", the callback will be called when "c" is encountered, or max n=255 received - `function` callback function, event "data" has a callback like this: `function(data) end` -- `run_input` 0 or 1. If 0, input from UART will not go into Lua interpreter, can accept binary data. If 1, input from UART will go into Lua interpreter, and run. +- `run_input` 0 or 1. If 0, input from UART will not go into Lua interpreter, and this can accept binary data. If 1, input from UART is treated as a text stream with the `DEL`, `BS`, `CR` and `LF` characters processed as normal. Completed lines will be passed to the Lua interpreter for execution. _Note that the interpreter only processes complete lines._ To unregister the callback, provide only the "data" parameter.