Unverified Commit a92da3c3 authored by Terry Ellison's avatar Terry Ellison Committed by GitHub
Browse files

Lua 5.1 / 5.3 alignment and document (#3193)

parent 1f386e93
...@@ -48,6 +48,7 @@ LUAI_FUNC int luaU_DumpAllProtos(lua_State *L, const Proto *m, lua_Writer w, ...@@ -48,6 +48,7 @@ LUAI_FUNC int luaU_DumpAllProtos(lua_State *L, const Proto *m, lua_Writer w,
void *data, int strip); void *data, int strip);
LUAI_FUNC int luaU_undumpLFS(lua_State *L, ZIO *Z, int isabs); LUAI_FUNC int luaU_undumpLFS(lua_State *L, ZIO *Z, int isabs);
LUAI_FUNC int luaU_stripdebug (lua_State *L, Proto *f, int level, int recv);
typedef struct FlashHeader LFSHeader; typedef struct FlashHeader LFSHeader;
/* /*
......
...@@ -19,7 +19,6 @@ ...@@ -19,7 +19,6 @@
#include "lauxlib.h" #include "lauxlib.h"
#include "lualib.h" #include "lualib.h"
#include "lnodemcu.h"
#define MAXUNICODE 0x10FFFF #define MAXUNICODE 0x10FFFF
...@@ -249,7 +248,7 @@ LROT_BEGIN(utf8_meta, NULL, LROT_MASK_INDEX) ...@@ -249,7 +248,7 @@ LROT_BEGIN(utf8_meta, NULL, LROT_MASK_INDEX)
LROT_END(utf8_meta, NULL, LROT_MASK_INDEX) LROT_END(utf8_meta, NULL, LROT_MASK_INDEX)
LROT_BEGIN(utf8, NULL, 0) LROT_BEGIN(utf8, LROT_TABLEREF(utf8_meta), 0)
LROT_FUNCENTRY( offset, byteoffset ) LROT_FUNCENTRY( offset, byteoffset )
LROT_FUNCENTRY( codepoint, codepoint ) LROT_FUNCENTRY( codepoint, codepoint )
LROT_FUNCENTRY( char, utfchar ) LROT_FUNCENTRY( char, utfchar )
......
// Module for interfacing with system // Module for interfacing with system
#include "module.h" #include "module.h"
#include "lauxlib.h" #include "lauxlib.h"
#include "lstate.h"
#include "lmem.h" #include "lmem.h"
#include "platform.h" #include "platform.h"
#if LUA_VERSION_NUM == 501
#include "lflash.h"
#endif
#include <stdint.h> #include <stdint.h>
#include <string.h> #include <string.h>
#include "user_interface.h" #include "user_interface.h"
...@@ -163,14 +158,27 @@ static void add_string_field( lua_State* L, const char *s, const char *name) { ...@@ -163,14 +158,27 @@ static void add_string_field( lua_State* L, const char *s, const char *name) {
lua_setfield(L, -2, name); lua_setfield(L, -2, name);
} }
static int node_info( lua_State* L ) static void get_lfs_config ( lua_State* L ){
{ int config[5];
const char* options[] = {"hw", "sw_version", "build_config", "legacy", NULL}; lua_getlfsconfig(L, config);
int option = luaL_checkoption (L, 1, options[3], options); lua_createtable(L, 0, 4);
add_int_field(L, config[0], "lfs_mapped");
add_int_field(L, config[1], "lfs_base");
add_int_field(L, config[2], "lfs_size");
add_int_field(L, config[3], "lfs_used");
}
static int node_info( lua_State* L ){
const char* options[] = {"lfs", "hw", "sw_version", "build_config", "legacy", NULL};
int option = luaL_checkoption (L, 1, options[4], options);
switch (option) { switch (option) {
case 0: { // hw case 0: { // lfs
lua_createtable (L, 0, 5); get_lfs_config(L);
return 1;
}
case 1: { // hw
lua_createtable(L, 0, 5);
add_int_field(L, system_get_chip_id(), "chip_id"); add_int_field(L, system_get_chip_id(), "chip_id");
add_int_field(L, spi_flash_get_id(), "flash_id"); add_int_field(L, spi_flash_get_id(), "flash_id");
add_int_field(L, flash_rom_get_size_byte() / 1024, "flash_size"); add_int_field(L, flash_rom_get_size_byte() / 1024, "flash_size");
...@@ -178,8 +186,8 @@ static int node_info( lua_State* L ) ...@@ -178,8 +186,8 @@ static int node_info( lua_State* L )
add_int_field(L, flash_rom_get_speed(), "flash_speed"); add_int_field(L, flash_rom_get_speed(), "flash_speed");
return 1; return 1;
} }
case 1: { // sw_version case 2: { // sw_version
lua_createtable (L, 0, 7); lua_createtable(L, 0, 7);
add_int_field(L, NODE_VERSION_MAJOR, "node_version_major"); add_int_field(L, NODE_VERSION_MAJOR, "node_version_major");
add_int_field(L, NODE_VERSION_MINOR, "node_version_minor"); add_int_field(L, NODE_VERSION_MINOR, "node_version_minor");
add_int_field(L, NODE_VERSION_REVISION, "node_version_revision"); add_int_field(L, NODE_VERSION_REVISION, "node_version_revision");
...@@ -189,8 +197,8 @@ static int node_info( lua_State* L ) ...@@ -189,8 +197,8 @@ static int node_info( lua_State* L )
add_string_field(L, BUILDINFO_RELEASE_DTS, "git_commit_dts"); add_string_field(L, BUILDINFO_RELEASE_DTS, "git_commit_dts");
return 1; return 1;
} }
case 2: { // build_config case 3: { // build_config
lua_createtable (L, 0, 4); lua_createtable(L, 0, 4);
lua_pushboolean(L, BUILDINFO_SSL); lua_pushboolean(L, BUILDINFO_SSL);
lua_setfield(L, -2, "ssl"); lua_setfield(L, -2, "ssl");
lua_pushnumber(L, BUILDINFO_LFS_SIZE); lua_pushnumber(L, BUILDINFO_LFS_SIZE);
...@@ -199,8 +207,7 @@ static int node_info( lua_State* L ) ...@@ -199,8 +207,7 @@ static int node_info( lua_State* L )
add_string_field(L, BUILDINFO_BUILD_TYPE, "number_type"); add_string_field(L, BUILDINFO_BUILD_TYPE, "number_type");
return 1; return 1;
} }
default: default: { // legacy
{
platform_print_deprecation_note("node.info() without parameter", "in the next version"); platform_print_deprecation_note("node.info() without parameter", "in the next version");
lua_pushinteger(L, NODE_VERSION_MAJOR); lua_pushinteger(L, NODE_VERSION_MAJOR);
lua_pushinteger(L, NODE_VERSION_MINOR); lua_pushinteger(L, NODE_VERSION_MINOR);
...@@ -333,8 +340,6 @@ static int writer(lua_State* L, const void* p, size_t size, void* u) ...@@ -333,8 +340,6 @@ static int writer(lua_State* L, const void* p, size_t size, void* u)
} }
#if LUA_VERSION_NUM == 501 #if LUA_VERSION_NUM == 501
#undef lua_dump
#define lua_dump lua_dumpEx
#define getproto(o) (clvalue(o)->l.p) #define getproto(o) (clvalue(o)->l.p)
#endif #endif
...@@ -461,8 +466,6 @@ static int node_restore (lua_State *L) ...@@ -461,8 +466,6 @@ static int node_restore (lua_State *L)
return 0; return 0;
} }
#if defined(LUA_OPTIMIZE_DEBUG) && LUA_VERSION_NUM == 501
/* node.stripdebug([level[, function]]).  /* node.stripdebug([level[, function]]). 
* level: 1 don't discard debug * level: 1 don't discard debug
* 2 discard Local and Upvalue debug info * 2 discard Local and Upvalue debug info
...@@ -472,54 +475,36 @@ static int node_restore (lua_State *L) ...@@ -472,54 +475,36 @@ static int node_restore (lua_State *L)
* If function is omitted, this is the default setting for future compiles * If function is omitted, this is the default setting for future compiles
* The function returns an estimated integer count of the bytes stripped. * The function returns an estimated integer count of the bytes stripped.
*/ */
LUA_API int luaG_stripdebug (lua_State *L, Proto *f, int level, int recv);
static int node_stripdebug (lua_State *L) { static int node_stripdebug (lua_State *L) {
int n = lua_gettop(L); int n = lua_gettop(L);
if (n == 0) { int strip = 0;
lua_pushlightuserdata(L, &luaG_stripdebug );
lua_gettable(L, LUA_REGISTRYINDEX);
if (lua_isnil(L, -1)) {
lua_pop(L, 1);
lua_pushinteger(L, LUA_OPTIMIZE_DEBUG);
}
return 1;
}
int level = luaL_checkint(L, 1); lua_settop(L, 2);
luaL_argcheck(L, level > 0 && level < 4, 1, "must in range 1-3"); if (!lua_isnil(L, 1)) {
strip = lua_tointeger(L, 1);
if (n == 1) { luaL_argcheck(L, strip > 0 && strip < 4, 1, "Invalid strip level");
/* Store the default level in the registry if no function parameter */ }
lua_pushlightuserdata(L, &luaG_stripdebug);
lua_pushinteger(L, level);
lua_settable(L, LUA_REGISTRYINDEX);
return 0;
}
if (level == 1) {
lua_pushinteger(L, 0);
return 1;
}
if (lua_isnumber(L, 2)) { if (lua_isnumber(L, 2)) {
int scope = luaL_checkint(L, 2); /* Use debug interface to replace stack level by corresponding function */
if (scope > 0) { int scope = luaL_checkinteger(L, 2);
/* if the function parameter is a +ve integer then climb to find function */ if (scope > 0) {
lua_Debug ar; lua_Debug ar;
lua_pop(L, 1); /* pop level as getinfo will replace it by the function */ lua_pop(L, 1);
if (lua_getstack(L, scope, &ar)) { if (lua_getstack(L, scope, &ar)) {
lua_getinfo(L, "f", &ar); lua_getinfo(L, "f", &ar); /* put function at [2] (ToS) */
} }
} }
} }
int isfunc = lua_isfunction(L, 2);
luaL_argcheck(L, n < 2 || isfunc, 2, "not a valid function");
luaL_argcheck(L, lua_isfunction(L, 2), 2, "must be a Lua Function"); /* return result of lua_stripdebug, adding 1 if this is get/set level) */
Proto *f = getproto(L->ci->func + 1); lua_pushinteger(L, lua_stripdebug(L, strip - 1) + (isfunc ? 0 : 1));
lua_pushinteger(L, luaG_stripdebug(L, f, level, 1));
return 1; return 1;
} }
#endif
#if LUA_VERSION_NUM == 501 #if LUA_VERSION_NUM == 501
...@@ -539,9 +524,10 @@ static int node_egc_setmode(lua_State* L) { ...@@ -539,9 +524,10 @@ static int node_egc_setmode(lua_State* L) {
} }
// totalallocated, estimatedused = node.egc.meminfo() // totalallocated, estimatedused = node.egc.meminfo()
static int node_egc_meminfo(lua_State *L) { static int node_egc_meminfo(lua_State *L) {
global_State *g = G(L); int totals[2];
lua_pushinteger(L, g->totalbytes); lua_getegcinfo(L, totals);
lua_pushinteger(L, g->estimate); lua_pushinteger(L, totals[0]);
lua_pushinteger(L, totals[1]);
return 2; return 2;
} }
#endif #endif
...@@ -651,6 +637,64 @@ static int node_writercr (lua_State *L) { ...@@ -651,6 +637,64 @@ static int node_writercr (lua_State *L) {
} }
#endif #endif
// Lua: n = node.LFS.reload(lfsimage)
static int node_lfsreload (lua_State *L) {
lua_settop(L, 1);
luaL_lfsreload(L);
return 1;
}
// Lua: n = node.flashindex(module)
// Lua: n = node.LFS.get(module)
static int node_lfsindex (lua_State *L) {
lua_settop(L, 1);
luaL_pushlfsmodule(L);
return 1;
}
// Lua: n = node.LFS.list([option])
// Note that option is ignored in this release
static int node_lfslist (lua_State *L) {
lua_settop(L, 1);
luaL_pushlfsmodules(L);
if (lua_istable(L, -1) && lua_getglobal(L, "table") == LUA_TTABLE) {
lua_getfield(L, -1, "sort");
lua_remove(L, -2); /* remove table table */
lua_pushvalue(L, -2); /* dup array of modules ref to ToS */
lua_call(L, 1, 0);
}
return 1;
}
//== node.LFS Table emulator ==============================================//
static int node_lfs_func (lua_State* L) { /*T[1] = LFS, T[2] = fieldname */
lua_remove(L, 1);
lua_settop(L, 1);
const char *name = lua_tostring(L, 1);
if (!name) {
lua_pushnil(L);
} else if (!strcmp(name, "config")) {
get_lfs_config(L);
} else if (!strcmp(name, "time")) {
luaL_pushlfsdts(L);
} else {
luaL_pushlfsmodule(L);
}
return 1;
}
LROT_BEGIN(node_lfs_meta, NULL, LROT_MASK_INDEX)
LROT_FUNCENTRY( __index, node_lfs_func)
LROT_END(node_lfs_meta, NULL, LROT_MASK_INDEX)
LROT_BEGIN(node_lfs, LROT_TABLEREF(node_lfs_meta), 0)
LROT_FUNCENTRY( list, node_lfslist)
LROT_FUNCENTRY( get, node_lfsindex)
LROT_FUNCENTRY( reload, node_lfsreload )
LROT_END(node_lfs, LROT_TABLEREF(node_lfs_meta), 0)
typedef enum pt_t { lfs_addr=0, lfs_size, spiffs_addr, spiffs_size, max_pt} pt_t; typedef enum pt_t { lfs_addr=0, lfs_size, spiffs_addr, spiffs_size, max_pt} pt_t;
LROT_BEGIN(pt_map, NULL, 0) LROT_BEGIN(pt_map, NULL, 0)
...@@ -826,8 +870,8 @@ LROT_BEGIN(node, NULL, 0) ...@@ -826,8 +870,8 @@ LROT_BEGIN(node, NULL, 0)
LROT_FUNCENTRY( heap, node_heap ) LROT_FUNCENTRY( heap, node_heap )
LROT_FUNCENTRY( info, node_info ) LROT_FUNCENTRY( info, node_info )
LROT_TABENTRY( task, node_task ) LROT_TABENTRY( task, node_task )
LROT_FUNCENTRY( flashreload, lua_lfsreload ) LROT_FUNCENTRY( flashindex, node_lfsindex )
LROT_FUNCENTRY( flashindex, lua_lfsindex ) LROT_TABENTRY( LFS, node_lfs )
LROT_FUNCENTRY( setonerror, node_setonerror ) LROT_FUNCENTRY( setonerror, node_setonerror )
LROT_FUNCENTRY( startupcommand, node_startupcommand ) LROT_FUNCENTRY( startupcommand, node_startupcommand )
LROT_FUNCENTRY( restart, node_restart ) LROT_FUNCENTRY( restart, node_restart )
...@@ -856,9 +900,7 @@ LROT_BEGIN(node, NULL, 0) ...@@ -856,9 +900,7 @@ LROT_BEGIN(node, NULL, 0)
LROT_FUNCENTRY( bootreason, node_bootreason ) LROT_FUNCENTRY( bootreason, node_bootreason )
LROT_FUNCENTRY( restore, node_restore ) LROT_FUNCENTRY( restore, node_restore )
LROT_FUNCENTRY( random, node_random ) LROT_FUNCENTRY( random, node_random )
#if LUA_VERSION_NUM == 501 && defined(LUA_OPTIMIZE_DEBUG)
LROT_FUNCENTRY( stripdebug, node_stripdebug ) LROT_FUNCENTRY( stripdebug, node_stripdebug )
#endif
#if LUA_VERSION_NUM == 501 #if LUA_VERSION_NUM == 501
LROT_TABENTRY( egc, node_egc ) LROT_TABENTRY( egc, node_egc )
#endif #endif
......
#define LUA_LIB #define LUA_LIB
#include "lauxlib.h" #include "lauxlib.h"
#include "lstring.h"
#ifndef LOCAL_LUA #ifndef LOCAL_LUA
#include "module.h" #include "module.h"
......
...@@ -5,7 +5,7 @@ ...@@ -5,7 +5,7 @@
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>
#include <stdlib.h> #include <stdlib.h>
#include "llimits.h" #include <stdint.h>
#include "gpio.h" #include "gpio.h"
#include "user_interface.h" #include "user_interface.h"
#include "driver/gpio16.h" #include "driver/gpio16.h"
...@@ -215,7 +215,7 @@ static void ICACHE_RAM_ATTR platform_gpio_intr_dispatcher (void *dummy){ ...@@ -215,7 +215,7 @@ static void ICACHE_RAM_ATTR platform_gpio_intr_dispatcher (void *dummy){
uint32_t j=0; uint32_t j=0;
uint32_t gpio_status = GPIO_REG_READ(GPIO_STATUS_ADDRESS); uint32_t gpio_status = GPIO_REG_READ(GPIO_STATUS_ADDRESS);
uint32_t now = system_get_time(); uint32_t now = system_get_time();
UNUSED(dummy); (void)(dummy);
#ifdef GPIO_INTERRUPT_HOOK_ENABLE #ifdef GPIO_INTERRUPT_HOOK_ENABLE
if (gpio_status & platform_gpio_hook->all_bits) { if (gpio_status & platform_gpio_hook->all_bits) {
...@@ -986,13 +986,13 @@ uint32_t platform_rcr_read (uint8_t rec_id, void **rec) { ...@@ -986,13 +986,13 @@ uint32_t platform_rcr_read (uint8_t rec_id, void **rec) {
} }
uint32_t platform_rcr_delete (uint8_t rec_id) { uint32_t platform_rcr_delete (uint8_t rec_id) {
void *rec = NULL; uint32_t *rec = NULL;
platform_rcr_read (rec_id, &rec); platform_rcr_read(rec_id, (void**)&rec);
if (rec) { if (rec) {
uint32_t *pHdr = cast(uint32_t *,rec)-1; uint32_t *pHdr = rec - 1; /* the header is the word proceeding the rec */
platform_rcr_t hdr = {.hdr = *pHdr}; platform_rcr_t hdr = {.hdr = *pHdr};
hdr.id = PLATFORM_RCR_DELETED; hdr.id = PLATFORM_RCR_DELETED;
platform_s_flash_write(&hdr, platform_flash_mapped2phys(cast(uint32_t, pHdr)), WORDSIZE); platform_s_flash_write(&hdr, platform_flash_mapped2phys((uint32_t) pHdr), WORDSIZE);
return 0; return 0;
} }
return ~0; return ~0;
......
...@@ -26,7 +26,6 @@ ...@@ -26,7 +26,6 @@
#include "user_interface.h" #include "user_interface.h"
#include "espconn.h" #include "espconn.h"
#include "mem.h" #include "mem.h"
#include "limits.h"
#include <stdint.h> #include <stdint.h>
#include <stddef.h> #include <stddef.h>
#include <string.h> #include <string.h>
......
...@@ -29,8 +29,8 @@ ...@@ -29,8 +29,8 @@
#include "user_interface.h" #include "user_interface.h"
#include "espconn.h" #include "espconn.h"
#include "mem.h" #include "mem.h"
#include "limits.h" #include <limits.h>
#include "stdlib.h" #include <stdlib.h>
#if defined(USES_SDK_BEFORE_V140) #if defined(USES_SDK_BEFORE_V140)
#define espconn_send espconn_sent #define espconn_send espconn_sent
......
...@@ -75,48 +75,15 @@ Formatting a file system on a large flash device (e.g. the 16MB parts) can take ...@@ -75,48 +75,15 @@ Formatting a file system on a large flash device (e.g. the 16MB parts) can take
## SDK Init Data ## SDK Init Data
!!! note NodeMCU versions are compiled against specific versions of the Espressif SDK. The SDK reserves space in flash that is used to store calibration and other data. Espressif refers to this area as "System Param" and it occupies four 4&nbsp;Kb sectors of flash. A fifth 4&nbsp;Kb sector is also reserved for RF calibration.
- With SDK version 2.x builds, these 5 sectors are located in the last pages at in the Flash memory.
- With SDK version 3.x builds, these 5 sectors are located in the otherwise unused pages at Flash offset 0x0B000-0x0FFFF, between the `bin/0x00000.bin` segment at 0x00000 and the `bin/0x10000.bin` to 0x10000.
Normally, NodeMCU will take care of writing the SDK init data when needed. Most users can ignore this section. If this data gets corrupted or you are upgrading major SDK versions, then the firmware may not boot correctly. Symptoms include messages like `rf_cal[0] !=0x05,is 0xFF`, or endless reboot loops and/or fast blinking module LEDs. If you are seeing one or several of the above symptoms, ensure that your chip is fully erased before flashing, for example by using `esptool.py`. The SDK version 3.x firmware builds detect if the RF calibration sector has been erased or corrupted, and will automatically initialise it with the correct content before restarting the processor. This works for all SDK supported flash sizes.
NodeMCU versions are compiled against specific versions of the Espressif SDK. The SDK reserves space in flash that is used to store calibration and other data. This data changes between SDK versions, and if it is invalid or not present, the firmware may not boot correctly. Symptoms include messages like `rf_cal[0] !=0x05,is 0xFF`, or endless reboot loops and/or fast blinking module LEDs.
!!! tip
If you are seeing one or several of the above symptoms, ensure that your chip is fully erased before flashing, for example:
`esptool.py --port <serial-port-of-ESP8266> erase_flash`
Also verify that you are using an up-to-date NodeMCU release, as some early releases of NodeMCU 1.5.4.1 did not write the SDK init data to a freshly erased chip.
Espressif refers to this area as "System Param" and it resides in the last four 4&nbsp;kB sectors of flash. Since SDK 1.5.4.1 a fifth sector is reserved for RF calibration (and its placement is controlled by NodeMCU) as described by this [patch notice](http://bbs.espressif.com/viewtopic.php?f=46&t=2407). At minimum, Espressif states that the 4th sector from the end needs to be flashed with "init data", and the 2nd sector from the end should be blank.
The default init data is provided as part of the SDK in the file `esp_init_data_default.bin`. NodeMCU will automatically flash this file to the right place on first boot if the sector appears to be empty.
If you need to customize init data then first download the [Espressif SDK 2.2.0](https://github.com/espressif/ESP8266_NONOS_SDK/archive/v2.2.0.zip) and extract `esp_init_data_default.bin`. Then flash that file just like you'd flash the firmware. The correct address for the init data depends on the capacity of the flash chip.
- `0x7c000` for 512 kB, modules like most ESP-01, -03, -07 etc.
- `0xfc000` for 1 MB, modules like ESP8285, PSF-A85, some ESP-01, -03 etc.
- `0x1fc000` for 2 MB
- `0x3fc000` for 4 MB, modules like ESP-12E, NodeMCU devkit 1.0, WeMos D1 mini
- `0x7fc000` for 8 MB
- `0xffc000` for 16 MB, modules like WeMos D1 mini pro
See "4.1 Non-FOTA Flash Map" and "6.3 RF Initialization Configuration" of the [ESP8266 Getting Started Guide](https://www.espressif.com/sites/default/files/documentation/2a-esp8266-sdk_getting_started_guide_en.pdf) for details on init data addresses and customization.
## Determine flash size ## Determine flash size
To determine the capacity of the flash chip *before* a firmware is installed you can run The easiest way to determine the flash capacity is to load the firmware and then `print(node.info'hw'.flash_size)` which reports the flash size in Kb. Alternatively, if you want to determine the capacity of the flash chip _before_ a firmware is installed then you can run the following command. This will return a 2 hex digit **Manufacturer** ID and a 4 digit **Device** ID and the detected flash size.
`esptool.py --port <serial-port> flash_id` `esptool.py --port <serial-port> flash_id`
The chip ID can then be looked up in [https://review.coreboot.org/cgit/flashrom.git/tree/flashchips.h](https://review.coreboot.org/cgit/flashrom.git/tree/flashchips.h).
It will return a manufacturer ID and a chip ID like so:
```
Connecting...
Manufacturer: e0
Device: 4016
```
The chip ID can then be looked up in [https://review.coreboot.org/cgit/flashrom.git/tree/flashchips.h](https://review.coreboot.org/cgit/flashrom.git/tree/flashchips.h). This leads to a manufacturer name and a chip model name/number e.g. `AMIC_A25LQ032`. That information can then be fed into your favorite search engine to find chip descriptions and data sheets.
By convention the last two or three digits in the module name denote the capacity in megabits. So, `A25LQ032` in the example above is a 32Mb(=4MB) module.
This diff is collapsed.
This diff is collapsed.
# node Module # node Module
| Since | Origin / Contributor | Maintainer | Source | | Since | Origin / Contributor | Maintainer | Source |
| :----- | :-------------------- | :---------- | :------ | | :----- | :-------------------- | :---------- | :------ |
| 2014-12-22 | [Zeroday](https://github.com/funshine) | [Zeroday](https://github.com/funshine) | [node.c](../../app/modules/node.c)| | 2014-12-22 | [Zeroday](https://github.com/funshine) | [TerryE](https://github.com/TerryE) | [node.c](../../app/modules/node.c)|
The node module provides access to system-level features such as sleep, restart and various info and IDs. The node module provides access to system-level features such as sleep, restart and various info and IDs.
...@@ -174,36 +174,13 @@ flash ID (number) ...@@ -174,36 +174,13 @@ flash ID (number)
## node.flashindex() ## node.flashindex()
Returns the function reference for a function in the [LFS (Lua Flash Store)](../lfs.md). Deprecated synonym for [`node.LFS.get()`](#nodelfsget) to return an LFS function reference.
#### Syntax Note that this returns `nil` if the function does not exist in LFS.
`node.flashindex(modulename)`
#### Parameters
`modulename` The name of the module to be loaded. If this is `nil` or invalid then an info list is returned
#### Returns
- In the case where the LFS in not loaded, `node.flashindex` evaluates to `nil`, followed by the flash mapped base addresss of the LFS, its flash offset, and the size of the LFS.
- If the LFS is loaded and the function is called with the name of a valid module in the LFS, then the function is returned in the same way the `load()` and the other Lua load functions do.
- Otherwise an extended info list is returned: the Unix time of the LFS build, the flash and mapped base addresses of the LFS and its current length, and an array of the valid module names in the LFS.
#### Example
The `node.flashindex()` is a low level API call that is normally wrapped using standard Lua code to present a simpler application API. See the module `_init.lua` in the `lua_examples/lfs` directory for an example of how to do this.
## node.flashreload() ## node.flashreload()
Reload the [LFS (Lua Flash Store)](../lfs.md) with the flash image provided. Flash images are generated on the host machine using the `luac.cross`commnad. Deprecated synonym for [`node.LFS.reload()`](#nodelfsreload) to reload [LFS (Lua Flash Store)](../lfs.md) with the named flash image provided.
#### Syntax
`node.flashreload(imageName)`
#### Parameters
`imageName` The name of a image file in the filesystem to be loaded into the LFS.
#### Returns
`Error message` LFS images are now gzip compressed. In the case of the `imagename` being a valid LFS image, this is expanded and loaded into flash. The ESP is then immediately rebooted, _so control is not returned to the calling Lua application_ in the case of a successful reload. This reload process internally makes two passes through the LFS image file; and on the first it validates the file and header formats and detects any errors. If any is detected then an error string is returned.
## node.flashsize() ## node.flashsize()
...@@ -278,7 +255,6 @@ system heap size left in bytes (number) ...@@ -278,7 +255,6 @@ system heap size left in bytes (number)
Returns information about hardware, software version and build configuration. Returns information about hardware, software version and build configuration.
#### Syntax #### Syntax
`node.info([group])` `node.info([group])`
...@@ -294,6 +270,13 @@ If a `group` is given the return value will be a table containing the following ...@@ -294,6 +270,13 @@ If a `group` is given the return value will be a table containing the following
- `flash_size` (number) - `flash_size` (number)
- `flash_mode` (number) 0 = QIO, 1 = QOUT, 2 = DIO, 15 = DOUT. - `flash_mode` (number) 0 = QIO, 1 = QOUT, 2 = DIO, 15 = DOUT.
- `flash_speed` (number) - `flash_speed` (number)
- for `group` = `"lfs"`
- `lfs_base` (number) Flash offset of selected LFS region
- `lfs_mapped` (number) Mapped memory address of selected LFS region
- `lfs_size` (number) size of selected LFS region
- `lfs_used` (number) actual size used by current LFS image
- for `group` = `"sw_version"` - for `group` = `"sw_version"`
- `git_branch` (string) - `git_branch` (string)
- `git_commit_id` (string) - `git_commit_id` (string)
...@@ -302,6 +285,7 @@ If a `group` is given the return value will be a table containing the following ...@@ -302,6 +285,7 @@ If a `group` is given the return value will be a table containing the following
- `node_version_major` (number) - `node_version_major` (number)
- `node_version_minor` (number) - `node_version_minor` (number)
- `node_version_revision` (number) - `node_version_revision` (number)
- for `group` = `"build_config"` - for `group` = `"build_config"`
- `ssl` (boolean) - `ssl` (boolean)
- `lfs_size` (number) as defined at build time - `lfs_size` (number) as defined at build time
...@@ -361,6 +345,65 @@ See the `telnet/telnet.lua` in `lua_examples` for a more comprehensive example. ...@@ -361,6 +345,65 @@ See the `telnet/telnet.lua` in `lua_examples` for a more comprehensive example.
#### See also #### See also
[`node.output()`](#nodeoutput) [`node.output()`](#nodeoutput)
## node.LFS
Sub-table containing the API for [Lua Flash Store](../lfs.md)(**LFS**) access. Programmers might prefer to map this to a global or local variable for convenience for example:
```lua
local LFS = node.LFS
```
This table contains the following methods and properties:
Property/Method | Description
-------|---------
`config` | A synonym for [`node.info('lfs')`](#nodeinfo). Returns the properties `lfs_base`, `lfs_mapped`, `lfs_size`, `lfs_used`.
`get()` | See [node.LFS.get()](#nodelfsget).
`list()` | See [node.LFS.list()](#nodelfslist).
`reload()` |See [node.LFS.reload()](#nodelfsreload).
`time` | Returns the Unix timestamp at time of image creation.
## node.LFS.get()
Returns the function reference for a function in LFS.
Note that unused `node.LFS` properties map onto the equialent `get()` call so for example: `node.LFS.mySub1` is a synonym for `node.LFS.get('mySub1')`.
#### Syntax
`node.LFS.get(modulename)`
#### Parameters
`modulename` The name of the module to be loaded.
#### Returns
- If the LFS is loaded and the `modulename` is a string that is the name of a valid module in the LFS, then the function is returned in the same way the `load()` and the other Lua load functions do
- Otherwise `nil` is returned.
## node.LFS.list()
List the modules in LFS.
#### Returns
- If no LFS image IS LOADED then `nil` is returned.
- Otherwise an sorted array of the name of modules in LFS is returned.
## node.LFS.reload()
Reload LFS with the flash image provided. Flash images can be generated on the host machine using the `luac.cross`command.
#### Syntax
`node.LFS.reload(imageName)`
#### Parameters
`imageName` The name of a image file in the filesystem to be loaded into the LFS.
#### Returns
- In the case when the `imagename` is a valid LFS image, this is expanded and loaded into flash, and the ESP is then immediately rebooted, _so control is not returned to the calling Lua application_ in the case of a successful reload.
- The reload process internally makes multiple passes through the LFS image file. The first pass validates the file and header formats and detects many errors. If any is detected then an error string is returned.
## node.output() ## node.output()
Redirects the Lua interpreter to a `stdout` pipe when a CB function is specified (See `pipe` module) and resets output to normal otherwise. Optionally also prints to the serial console. Redirects the Lua interpreter to a `stdout` pipe when a CB function is specified (See `pipe` module) and resets output to normal otherwise. Optionally also prints to the serial console.
......
This diff is collapsed.
# Programming in NodeMCU
The standard Lua runtime offers support for both Lua modules that can define multiple Lua functions and properties in an encapsulating table as described in the [Lua 5.3 Reference Manual](https://www.lua.org/manual/5.3/) \("**LRM**") and specifically in [LRM Section 6.3](https://www.lua.org/manual/5.3/manual.html#6.3). Lua also provides a C API to allow developers to implement modules in compiled C.
NodeMCU developers are also able to develop and incorporate their own C modules into their own firmware build using this standard API, although we encourage developers to download the standard [Lua Reference Manual](https://www.lua.org/manual/) and also buy of copy of Roberto Ierusalimschy's Programming in Lua edition 4 \("**PiL**"). The NodeMCU implementation extends standard Lua as documented in the [NodeMCU Reference Manual](nodemcu-lrm.md) \("**NRM**").
Those developers who wish to develop or to modify existing C modules should have access to the LRM, PiL and NRM and familiarise themselves with these references. These are the primary references; and this document does not repeat this content, but rather provide some NodeMCU-specific information to supplement it.
From a perspective of developing C modules, there is very little difference from that of developing modules in standard Lua. All of the standard Lua library modules (`bit`, `coroutine`, `debug`, `math`, `string`, `table`, `utf8`) use the C API for Lua and the NodeMCU versions have been updated to use NRM extensions. so their source code is available for browsing and using as style template (see the corresponding `lXXXlib.c` file in GitHub [NodeMCU lua53](../app/lua53) folder).
The main functional change is that NodeMCU supports a read-only subclass of the `Table` type, known as a **`ROTable`**, which can be statically declared within the module source using static `const` declarations. There are also limitations on the valid types for ROTable keys and value in order to ensure that these are consistent with static declaration; and hence ROTables are stored in code space (and therefore in flash memory on the IoT device). Hence unlike standard Lua tables, ROTables do not take up RAM resources.
Also unlike standard Lua, two global ROTables are used for the registration of C modules. Again, static declaration macros plus linker "magic" (use of make filters plus linker _section_ directives) result in the marshalling of these ROTables during the make process, and because this is all ROTable based, the integration of modules into the firmware builds and their access from executing Lua applications depends on code space rather than RAM-based data structures.
Note that dynamic loading of C modules is not supported within the ESP SDK, so any library registration must be compiled into the source used in the firmware build. Our approach is simple, flexible and avoids the RAM overheads of the standard Lua approach. The special ROTable **`ROM`** is core to this approach. The global environment table has an `__index` metamethod referencing this ROM table. Hence, any non-raw lookups against the global table will also resolve against ROM. All base Lua functions (such as `print`) and any C libraries (written to NodeMCU standards) have an entry in the ROM table and hence have global visibility. This approach does not prevent developers use of standard Lua mechanisms, but rather it offers a simple low RAM use alternative.
The `NODEMCU_MODULE` macro is used in each module to register it in an entry in the **`ROM`** ROTable. It also adds a entry in the second (hidden) **`ROMentry`** ROTable.
- All `ROM` entries will resolve globally
- The Lua runtime scans the `ROMentry` ROTable during its start up, and it will execute any non-NULL `CFunction` values in this table. This enables C modules to hook in any one-time start-up functions if they are needed.
Note that the standard `make` will include any modules found in the `app/modules` folder within a firmware build _if_ the corresponding `LUA_USE_MODULES_modname` macro has been defined. These defines are conventionally set in a common include file `user_modules.h`, and this practice is mandated for any user-submitted modules that are added to to the NodeMCU distribution. However, this does not prevent developers adding their own local modules to the `app/modules` folder and simply defining the corresponding `LUA_USE_MODULES_modname` inline.
This macro + linker approach renders the need for `luaL_reg` declarations and use of `luaL_openlib()` unnecessary, and these are not permitted in project-adopted `app/modules` files.
Hence a NodeMCU C library module typically has a standard layout that parallels that of the standard Lua library modules and uses the same C API to access the Lua runtime:
- A `#ìnclude` block to resolve access to external resources. All modules will include entries for `"module.h"`, and `"lauxlib.h"`. They should _not_ reference any other `lXXX.h` includes from the Lua source directory as these are private to the Lua runtime. These may be followed by C standard runtime includes, external application libraries and any SDK resource headers needed. Note that whilst we recommend using the C standard runtime API for `<stdlib.h>` etc., the SDK only implements a poorly documented subset of this API, so be aware that you might get linker errors and in which case you might need to recode some calls if you are using non-implemented functions.
- The only external interface to a C module should be via the Lua runtime and its `NODEMCU_MODULE` hooks. Therefore all functions and resources should be declared `static` and be private to the module. These can be ordered as the developer wishes, subject of course to the need for appropriate forward declarations to comply with C scoping rules.
- Module methods will typically employ a Lua standard `static int somefunc (lua_State *L) { ... }` template.
- ROTables are typically declared at the end of the module to minimise the need for forward references and use the `LROT` macros described in the NRM. Note that ROTables only support static declaration of string keys and the value types: C function, Lightweight userdata, Numeric, ROTable. ROTables can also have ROTable metatables.
- Whilst the ROTable search algorithm is a simply linear scan of the ROTable entries, the runtime also maintains a LRU cache of ROTable accesses, so typically over 95% of ROTable accesses bypass the linear scan and do a direct access to the appropriate entry.
- ROTables are also reasonable lightweight and well integrated into the Lua runtime, so the normal metamethod processing works well. This means that developers can use the `__index` method to implement other key and value typed entries through an index function.
- NodeMCU modules are intended to be compilable against both our Lua 5.1 and Lua 5.3 runtimes. The NRM discusses the implications and constraints here. However note that:
- We have back-ported many new Lua 5.3 features into the NodeMCU Lua 5.1 API, so in general you can use the 5.3 API to code your modules. Again the NRM notes the exceptions where you will either need variant code or to decide to limit yourself to the the 5.3 runtime. In this last case the simplest approach is to `#if LUA_VERSION_NUM != 503` to disable the 5.3 content so that 5.1 build can compile and link. Note that all modules currently in the `app/modules` folder will compile against and execute within both the Lua 5.1 and the 5.3 environments.
- Lua 5.3 uses a 32-bit representation for all numerics with separate subtypes for integer (stored as a 32 bit signed integer) and float (stored as 32bit single precision float). This achieves the same RAM storage density as Lua 5.1 integer builds without the loss of use of floating point when convenient. We have therefore decided that there is no benefit in having a separate Integer 5.3 build variant.
- We recommend that developers make use of the full set of `luaL_` API calls to minimise code verbosity. We have also added a couple of registry access optimisations that both simply and improve runtime performance when using the Lua registry for callback support.
- `luaL_reref()` replaces an existing registry reference in place (or creates a new one if needed). Less code and faster execution than a `luaL_unref()` plus `luaL_ref()` construct.
- `luaL_unref2()` does the unref and set the static int hook to `LUA_NOREF`.
Rather than include simple examples of module templates, we suggest that you review the modules in our GitHub repository, such as the [`utf8`](../app/lua53/lutf8lib.c) library. Note that whilst all of the existing modules in `app/modules` folder compile and work, we plan to do a clean up of the core modules to ensure that they conform to best practice.
...@@ -32,15 +32,19 @@ pages: ...@@ -32,15 +32,19 @@ pages:
- Uploading code: 'upload.md' - Uploading code: 'upload.md'
- Compiling code: 'compiling.md' - Compiling code: 'compiling.md'
- Support: 'support.md' - Support: 'support.md'
- Reference:
- NodeMCU Language Reference Manual: 'nodemcu-lrm.md'
- Programming in NodeMCU: 'nodemcu-pil.md'
- FAQs: - FAQs:
- Lua Developer FAQ: 'lua-developer-faq.md' - Lua Developer FAQ: 'lua-developer-faq.md'
- Extension Developer FAQ: 'extn-developer-faq.md' - Extension Developer FAQ: 'extn-developer-faq.md'
- Hardware FAQ: 'hardware-faq.md' - Hardware FAQ: 'hardware-faq.md'
- Whitepapers: - Whitepapers:
- Lua 5.3 Support: 'lua53.md'
- Lua Flash Store (LFS): 'lfs.md'
- Lua Compact Debug (LCD): 'lcd.md'
- Filesystem on SD card: 'sdcard.md' - Filesystem on SD card: 'sdcard.md'
- Internal filesystem: 'spiffs.md' - Internal filesystem: 'spiffs.md'
- Lua Compact Debug (LCD): 'lcd.md'
- Lua Flash Store (LFS): 'lfs.md'
- Lua Modules: - Lua Modules:
- 'Lua modules directory': 'lua-modules/README.md' - 'Lua modules directory': 'lua-modules/README.md'
- 'bh1750': 'lua-modules/bh1750.md' - 'bh1750': 'lua-modules/bh1750.md'
......
...@@ -193,6 +193,7 @@ ...@@ -193,6 +193,7 @@
<ClCompile Include="..\..\app\lua\llex.c" /> <ClCompile Include="..\..\app\lua\llex.c" />
<ClCompile Include="..\..\app\lua\lmathlib.c" /> <ClCompile Include="..\..\app\lua\lmathlib.c" />
<ClCompile Include="..\..\app\lua\lmem.c" /> <ClCompile Include="..\..\app\lua\lmem.c" />
<ClCompile Include="..\..\app\lua\lnodemcu.c" />
<ClCompile Include="..\..\app\lua\loadlib.c" /> <ClCompile Include="..\..\app\lua\loadlib.c" />
<ClCompile Include="..\..\app\lua\lobject.c" /> <ClCompile Include="..\..\app\lua\lobject.c" />
<ClCompile Include="..\..\app\lua\lopcodes.c" /> <ClCompile Include="..\..\app\lua\lopcodes.c" />
...@@ -250,4 +251,4 @@ ...@@ -250,4 +251,4 @@
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets"> <ImportGroup Label="ExtensionTargets">
</ImportGroup> </ImportGroup>
</Project> </Project>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> <Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup> <ItemGroup>
<Filter Include="Source Files"> <Filter Include="Source Files">
...@@ -132,6 +132,9 @@ ...@@ -132,6 +132,9 @@
<ClCompile Include="..\..\app\lua\lzio.c"> <ClCompile Include="..\..\app\lua\lzio.c">
<Filter>app\lua</Filter> <Filter>app\lua</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="..\..\app\lua\lnodemcu.c">
<Filter>app\lua</Filter>
</ClCompile>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ClInclude Include="..\..\app\uzlib\uzlib.h"> <ClInclude Include="..\..\app\uzlib\uzlib.h">
...@@ -228,4 +231,4 @@ ...@@ -228,4 +231,4 @@
<Filter>app\include</Filter> <Filter>app\include</Filter>
</ClInclude> </ClInclude>
</ItemGroup> </ItemGroup>
</Project> </Project>
\ No newline at end of file
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment