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

Merge pull request #2886 from nodemcu/dev

Next master drop
parents 68c425c0 a08e74d9
...@@ -37,9 +37,6 @@ endif ...@@ -37,9 +37,6 @@ endif
# Required for each makefile to inherit from the parent # Required for each makefile to inherit from the parent
# #
INCLUDES := $(INCLUDES) -I $(PDIR)include
INCLUDES += -I ./
INCLUDES += -I ../libc
PDIR := ../$(PDIR) PDIR := ../$(PDIR)
sinclude $(PDIR)Makefile sinclude $(PDIR)Makefile
...@@ -12,21 +12,20 @@ ...@@ -12,21 +12,20 @@
#define UZLIB_INFLATE_H #define UZLIB_INFLATE_H
#include <setjmp.h> #include <setjmp.h>
#include <stdint.h>
#include <stdlib.h>
#define uz_malloc malloc
#define uz_free free
#if defined(__XTENSA__) #if defined(__XTENSA__)
#include "c_stdint.h"
#include "mem.h" #include "mem.h"
#define UZLIB_THROW(v) longjmp(unwindAddr, (v)) #define UZLIB_THROW(v) longjmp(unwindAddr, (v))
#define UZLIB_SETJMP setjmp #define UZLIB_SETJMP setjmp
#define uz_malloc os_malloc
#define uz_free os_free
#else /* Host */ #else /* Host */
#include <stdint.h>
#include <stdlib.h>
extern int dbg_break(void); extern int dbg_break(void);
#if defined(_MSC_VER) || defined(__MINGW32__) //msvc requires old name for longjmp #if defined(_MSC_VER) || defined(__MINGW32__) //msvc requires old name for longjmp
#define UZLIB_THROW(v) {dbg_break();longjmp(unwindAddr, (v));} #define UZLIB_THROW(v) {dbg_break();longjmp(unwindAddr, (v));}
...@@ -36,9 +35,6 @@ extern int dbg_break(void); ...@@ -36,9 +35,6 @@ extern int dbg_break(void);
#define UZLIB_SETJMP(n) _setjmp(n) #define UZLIB_SETJMP(n) _setjmp(n)
#endif #endif
#define uz_malloc malloc
#define uz_free free
#endif /* defined(__XTENSA__) */ #endif /* defined(__XTENSA__) */
extern jmp_buf unwindAddr; extern jmp_buf unwindAddr;
......
...@@ -39,11 +39,7 @@ ...@@ -39,11 +39,7 @@
*/ */
#include <string.h> #include <string.h>
#ifdef __XTENSA__
#include "c_stdio.h"
#else
#include <stdio.h> #include <stdio.h>
#endif
#include "uzlib.h" #include "uzlib.h"
......
...@@ -38,12 +38,6 @@ STD_CFLAGS=-std=gnu11 -Wimplicit ...@@ -38,12 +38,6 @@ STD_CFLAGS=-std=gnu11 -Wimplicit
# Required for each makefile to inherit from the parent # Required for each makefile to inherit from the parent
# #
INCLUDES := $(INCLUDES) -I $(PDIR)include
INCLUDES += -I ./
INCLUDES += -I ./include
INCLUDES += -I ../include
INCLUDES += -I ../libc
INCLUDES += -I ../../include
PDIR := ../$(PDIR) PDIR := ../$(PDIR)
sinclude $(PDIR)Makefile sinclude $(PDIR)Makefile
...@@ -27,12 +27,11 @@ ...@@ -27,12 +27,11 @@
#include "espconn.h" #include "espconn.h"
#include "mem.h" #include "mem.h"
#include "limits.h" #include "limits.h"
#include "stdlib.h" #include <stdint.h>
#include <stddef.h>
#include "c_types.h" #include <string.h>
#include "c_string.h" #include <stdlib.h>
#include "c_stdlib.h" #include <stdio.h>
#include "c_stdio.h"
#include "websocketclient.h" #include "websocketclient.h"
...@@ -83,7 +82,7 @@ static char *cryptoSha1(char *data, unsigned int len) { ...@@ -83,7 +82,7 @@ static char *cryptoSha1(char *data, unsigned int len) {
SHA1Init(&ctx); SHA1Init(&ctx);
SHA1Update(&ctx, data, len); SHA1Update(&ctx, data, len);
uint8_t *digest = (uint8_t *) c_zalloc(20); uint8_t *digest = (uint8_t *) calloc(1,20);
SHA1Final(digest, &ctx); SHA1Final(digest, &ctx);
return (char *) digest; // Requires free return (char *) digest; // Requires free
} }
...@@ -93,7 +92,7 @@ static const char *bytes64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwx ...@@ -93,7 +92,7 @@ static const char *bytes64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwx
static char *base64Encode(char *data, unsigned int len) { static char *base64Encode(char *data, unsigned int len) {
int blen = (len + 2) / 3 * 4; int blen = (len + 2) / 3 * 4;
char *out = (char *) c_zalloc(blen + 1); char *out = (char *) calloc(1,blen + 1);
out[blen] = '\0'; out[blen] = '\0';
int j = 0, i; int j = 0, i;
for (i = 0; i < len; i += 3) { for (i = 0; i < len; i += 3) {
...@@ -197,7 +196,7 @@ static void ws_sendFrame(struct espconn *conn, int opCode, const char *data, uns ...@@ -197,7 +196,7 @@ static void ws_sendFrame(struct espconn *conn, int opCode, const char *data, uns
return; return;
} }
char *b = c_zalloc(10 + len); // 10 bytes = worst case scenario for framming char *b = calloc(1,10 + len); // 10 bytes = worst case scenario for framming
if (b == NULL) { if (b == NULL) {
NODE_DBG("Out of memory when receiving message, disconnecting...\n"); NODE_DBG("Out of memory when receiving message, disconnecting...\n");
...@@ -301,7 +300,7 @@ static void ws_receiveCallback(void *arg, char *buf, unsigned short len) { ...@@ -301,7 +300,7 @@ static void ws_receiveCallback(void *arg, char *buf, unsigned short len) {
if (ws->frameBuffer != NULL) { // Append previous frameBuffer with new content if (ws->frameBuffer != NULL) { // Append previous frameBuffer with new content
NODE_DBG("Appending new frameBuffer to old one \n"); NODE_DBG("Appending new frameBuffer to old one \n");
ws->frameBuffer = c_realloc(ws->frameBuffer, ws->frameBufferLen + len); ws->frameBuffer = realloc(ws->frameBuffer, ws->frameBufferLen + len);
if (ws->frameBuffer == NULL) { if (ws->frameBuffer == NULL) {
NODE_DBG("Failed to allocate new framebuffer, disconnecting...\n"); NODE_DBG("Failed to allocate new framebuffer, disconnecting...\n");
...@@ -358,7 +357,7 @@ static void ws_receiveCallback(void *arg, char *buf, unsigned short len) { ...@@ -358,7 +357,7 @@ static void ws_receiveCallback(void *arg, char *buf, unsigned short len) {
NODE_DBG("INCOMPLETE Frame \n"); NODE_DBG("INCOMPLETE Frame \n");
if (ws->frameBuffer == NULL) { if (ws->frameBuffer == NULL) {
NODE_DBG("Allocing new frameBuffer \n"); NODE_DBG("Allocing new frameBuffer \n");
ws->frameBuffer = c_zalloc(len); ws->frameBuffer = calloc(1,len);
if (ws->frameBuffer == NULL) { if (ws->frameBuffer == NULL) {
NODE_DBG("Failed to allocate framebuffer, disconnecting... \n"); NODE_DBG("Failed to allocate framebuffer, disconnecting... \n");
...@@ -379,7 +378,7 @@ static void ws_receiveCallback(void *arg, char *buf, unsigned short len) { ...@@ -379,7 +378,7 @@ static void ws_receiveCallback(void *arg, char *buf, unsigned short len) {
NODE_DBG("PARTIAL frame! Should concat payload and later restore opcode\n"); NODE_DBG("PARTIAL frame! Should concat payload and later restore opcode\n");
if(ws->payloadBuffer == NULL) { if(ws->payloadBuffer == NULL) {
NODE_DBG("Allocing new payloadBuffer \n"); NODE_DBG("Allocing new payloadBuffer \n");
ws->payloadBuffer = c_zalloc(payloadLength); ws->payloadBuffer = calloc(1,payloadLength);
if (ws->payloadBuffer == NULL) { if (ws->payloadBuffer == NULL) {
NODE_DBG("Failed to allocate payloadBuffer, disconnecting...\n"); NODE_DBG("Failed to allocate payloadBuffer, disconnecting...\n");
...@@ -395,7 +394,7 @@ static void ws_receiveCallback(void *arg, char *buf, unsigned short len) { ...@@ -395,7 +394,7 @@ static void ws_receiveCallback(void *arg, char *buf, unsigned short len) {
ws->payloadOriginalOpCode = opCode; ws->payloadOriginalOpCode = opCode;
} else { } else {
NODE_DBG("Appending new payloadBuffer to old one \n"); NODE_DBG("Appending new payloadBuffer to old one \n");
ws->payloadBuffer = c_realloc(ws->payloadBuffer, ws->payloadBufferLen + payloadLength); ws->payloadBuffer = realloc(ws->payloadBuffer, ws->payloadBufferLen + payloadLength);
if (ws->payloadBuffer == NULL) { if (ws->payloadBuffer == NULL) {
NODE_DBG("Failed to allocate new framebuffer, disconnecting...\n"); NODE_DBG("Failed to allocate new framebuffer, disconnecting...\n");
...@@ -425,7 +424,7 @@ static void ws_receiveCallback(void *arg, char *buf, unsigned short len) { ...@@ -425,7 +424,7 @@ static void ws_receiveCallback(void *arg, char *buf, unsigned short len) {
return; return;
} }
// concat buffer with payload // concat buffer with payload
payload = c_zalloc(ws->payloadBufferLen + payloadLength); payload = calloc(1,ws->payloadBufferLen + payloadLength);
if (payload == NULL) { if (payload == NULL) {
NODE_DBG("Failed to allocate new framebuffer, disconnecting...\n"); NODE_DBG("Failed to allocate new framebuffer, disconnecting...\n");
...@@ -457,7 +456,7 @@ static void ws_receiveCallback(void *arg, char *buf, unsigned short len) { ...@@ -457,7 +456,7 @@ static void ws_receiveCallback(void *arg, char *buf, unsigned short len) {
extensionDataOffset += 2; extensionDataOffset += 2;
} }
payload = c_zalloc(payloadLength - extensionDataOffset + 1); payload = calloc(1,payloadLength - extensionDataOffset + 1);
if (payload == NULL) { if (payload == NULL) {
NODE_DBG("Failed to allocate payload, disconnecting...\n"); NODE_DBG("Failed to allocate payload, disconnecting...\n");
...@@ -511,7 +510,7 @@ static void ws_receiveCallback(void *arg, char *buf, unsigned short len) { ...@@ -511,7 +510,7 @@ static void ws_receiveCallback(void *arg, char *buf, unsigned short len) {
if (ws->frameBuffer != NULL) { if (ws->frameBuffer != NULL) {
NODE_DBG("Reallocing frameBuffer to remove consumed frame\n"); NODE_DBG("Reallocing frameBuffer to remove consumed frame\n");
ws->frameBuffer = c_realloc(ws->frameBuffer, ws->frameBufferLen + len); ws->frameBuffer = realloc(ws->frameBuffer, ws->frameBufferLen + len);
if (ws->frameBuffer == NULL) { if (ws->frameBuffer == NULL) {
NODE_DBG("Failed to allocate new frame buffer, disconnecting...\n"); NODE_DBG("Failed to allocate new frame buffer, disconnecting...\n");
...@@ -738,12 +737,12 @@ void ws_connect(ws_info *ws, const char *url) { ...@@ -738,12 +737,12 @@ void ws_connect(ws_info *ws, const char *url) {
} }
// Extract protocol - either ws or wss // Extract protocol - either ws or wss
bool isSecure = c_strncasecmp(url, PROTOCOL_SECURE, strlen(PROTOCOL_SECURE)) == 0; bool isSecure = strncasecmp(url, PROTOCOL_SECURE, strlen(PROTOCOL_SECURE)) == 0;
if (isSecure) { if (isSecure) {
url += strlen(PROTOCOL_SECURE); url += strlen(PROTOCOL_SECURE);
} else { } else {
if (c_strncasecmp(url, PROTOCOL_INSECURE, strlen(PROTOCOL_INSECURE)) != 0) { if (strncasecmp(url, PROTOCOL_INSECURE, strlen(PROTOCOL_INSECURE)) != 0) {
NODE_DBG("Failed to extract protocol from: %s\n", url); NODE_DBG("Failed to extract protocol from: %s\n", url);
if (ws->onFailure) ws->onFailure(ws, -1); if (ws->onFailure) ws->onFailure(ws, -1);
return; return;
...@@ -752,7 +751,7 @@ void ws_connect(ws_info *ws, const char *url) { ...@@ -752,7 +751,7 @@ void ws_connect(ws_info *ws, const char *url) {
} }
// Extract path - it should start with '/' // Extract path - it should start with '/'
char *path = c_strchr(url, '/'); char *path = strchr(url, '/');
// Extract hostname, possibly including port // Extract hostname, possibly including port
char hostname[256]; char hostname[256];
...@@ -800,9 +799,9 @@ void ws_connect(ws_info *ws, const char *url) { ...@@ -800,9 +799,9 @@ void ws_connect(ws_info *ws, const char *url) {
// Prepare internal ws_info // Prepare internal ws_info
ws->connectionState = 1; ws->connectionState = 1;
ws->isSecure = isSecure; ws->isSecure = isSecure;
ws->hostname = c_strdup(hostname); ws->hostname = strdup(hostname);
ws->port = port; ws->port = port;
ws->path = c_strdup(path); ws->path = strdup(path);
ws->expectedSecKey = NULL; ws->expectedSecKey = NULL;
ws->knownFailureCode = 0; ws->knownFailureCode = 0;
ws->frameBuffer = NULL; ws->frameBuffer = NULL;
...@@ -813,10 +812,10 @@ void ws_connect(ws_info *ws, const char *url) { ...@@ -813,10 +812,10 @@ void ws_connect(ws_info *ws, const char *url) {
ws->unhealthyPoints = 0; ws->unhealthyPoints = 0;
// Prepare espconn // Prepare espconn
struct espconn *conn = (struct espconn *) c_zalloc(sizeof(struct espconn)); struct espconn *conn = (struct espconn *) calloc(1,sizeof(struct espconn));
conn->type = ESPCONN_TCP; conn->type = ESPCONN_TCP;
conn->state = ESPCONN_NONE; conn->state = ESPCONN_NONE;
conn->proto.tcp = (esp_tcp *) c_zalloc(sizeof(esp_tcp)); conn->proto.tcp = (esp_tcp *) calloc(1,sizeof(esp_tcp));
conn->proto.tcp->local_port = espconn_port(); conn->proto.tcp->local_port = espconn_port();
conn->proto.tcp->remote_port = ws->port; conn->proto.tcp->remote_port = ws->port;
......
...@@ -93,12 +93,14 @@ make EXTRA_CCFLAGS="-DLUA_NUMBER_INTEGRAL .... ...@@ -93,12 +93,14 @@ make EXTRA_CCFLAGS="-DLUA_NUMBER_INTEGRAL ....
``` ```
### Tag Your Build ### Tag Your Build
Identify your firmware builds by editing `app/include/user_version.h` Identify your firmware builds by setting the environment variable `USER_PROLOG`.
You may also edit `app/include/user_version.h`. The variable `USER_PROLOG` will be included in `NODE_VERSION_LONG`.
```c ```c
#define NODE_VERSION "NodeMCU " ESP_SDK_VERSION_STRING "." NODE_VERSION_XSTR(NODE_VERSION_INTERNAL) #define NODE_VERSION "NodeMCU " ESP_SDK_VERSION_STRING "." NODE_VERSION_XSTR(NODE_VERSION_INTERNAL) " " NODE_VERSION_LONG
#ifndef BUILD_DATE #ifndef BUILD_DATE
#define BUILD_DATE "YYYYMMDD" #define BUILD_DATE "unspecified"
#endif #endif
``` ```
......
...@@ -50,19 +50,20 @@ mode is enabled by specifying the `-f`option. ...@@ -50,19 +50,20 @@ mode is enabled by specifying the `-f`option.
- **Compact relocatable**. This is selected by the `-f` option. Here the compiler compresses the compiled binary so that image is small for downloading over Wifi/WAN (e.g. a full 64Kb LFS image is compressed down to a 22Kb file.) The LVM processes such image in two passes with the integrity of the image validated on the first, and the LFS itself gets updated on the second. The LVM also checks that the image will fit in the allocated LFS region before loading, but you can also use the `-m` option to throw a compile error if the image is too large, for example `-m 0x10000` will raise an error if the image will not load into a 64Kb regions. - **Compact relocatable**. This is selected by the `-f` option. Here the compiler compresses the compiled binary so that image is small for downloading over Wifi/WAN (e.g. a full 64Kb LFS image is compressed down to a 22Kb file.) The LVM processes such image in two passes with the integrity of the image validated on the first, and the LFS itself gets updated on the second. The LVM also checks that the image will fit in the allocated LFS region before loading, but you can also use the `-m` option to throw a compile error if the image is too large, for example `-m 0x10000` will raise an error if the image will not load into a 64Kb regions.
- **Absolute**. This is selected by the `-a <baseAddr>` option. Here the compiler fixes all addresses relative to the base address specified. This allows an LFS absolute image to be loaded directly into the ESP flash using a tool such as `esptool.py`. - **Absolute**. This is selected by the `-a <baseAddr>` option. Here the compiler fixes all addresses relative to the base address specified. This allows an LFS absolute image to be loaded directly into the ESP flash using a tool such as `esptool.py`. _Note that the new NodeMCU loader uses the `-f` compact relocatable form and does relocation based on the Partition Table, so this option is deprecated and will be removed in future releases.
These two modes target two separate use cases: the compact relocatable format These two modes target two separate use cases: the compact relocatable format
facilitates simple OTA updates to an LFS based Lua application; the absolute format facilitates simple OTA updates to an LFS based Lua application; the absolute format
facilitates factory installation of LFS based applications. facilitates factory installation of LFS based applications.
Also note that the `app/lua/luac_cross` make and Makefile can be executed to build Also note that the `app/lua/luac_cross` make and Makefile can be executed to build
just the `luac.cross` image. You must first ensure that the following options in just the `luac.cross` image. You must first ensure that the following option in
`app/include/user_config.h` are matched to your target configuration: `app/include/user_config.h` is matched to your target configuration:
```c ```c
//#define LUA_NUMBER_INTEGRAL // uncomment if you want an integer build //#define LUA_NUMBER_INTEGRAL // uncomment if you want an integer build
//#define LUA_FLASH_STORE 0x10000 // uncomment if you LFS support
``` ```
Note that the use of LFS and the LFS region size is now configured through the partition table.
Developers have successfully built this on Linux (including docker builds), MacOS, Win10/WSL and WinX/Cygwin. Developers have successfully built this on Linux (including docker builds), MacOS, Win10/WSL and WinX/Cygwin.
...@@ -226,7 +226,7 @@ Detailed instructions available in the image's README. As for available config o ...@@ -226,7 +226,7 @@ Detailed instructions available in the image's README. As for available config o
### For LFS ### For LFS
1. In `app/include/user_config.h` uncomment `#define LUA_FLASH_STORE 0x10000` and adjust the size if necessary. 1. In `app/include/user_config.h` edit the line `#define LUA_FLASH_STORE 0x0` and adjust the size to that needed. Note that this must be a multiple of 4Kb.
2. Build as you would otherwise build with this image (i.e. see its README) 2. Build as you would otherwise build with this image (i.e. see its README)
[↑ back to matrix](#task-os-selector) [↑ back to matrix](#task-os-selector)
...@@ -238,24 +238,25 @@ _Note that this Docker image is not an official NodeMCU offering. It's maintaine ...@@ -238,24 +238,25 @@ _Note that this Docker image is not an official NodeMCU offering. It's maintaine
A local copy of `luac.cross` is only needed if you want to compile the Lua files into an LFS image yourself and you are _not_ using Docker. A local copy of `luac.cross` is only needed if you want to compile the Lua files into an LFS image yourself and you are _not_ using Docker.
### Windows ### Windows
Windows 10 users can install and use the Windows Subsystem for Linux (WSL). Alternatively all Windows users can [install Cygwin](https://www.cygwin.com/install.html) (only Cygwin core + **gcc-core** + **gnu make**). Either way, you will need a copy of the `luac.cross` compiler: Windows users can compile a local copy of the `luac.cross` executable for use on a development PC. To this you need:
- To download the current NodeMCU sources (this [dev ZIP file](https://github.com/nodemcu/nodemcu-firmware/archive/dev.zip) or [master ZIP file](https://github.com/nodemcu/nodemcu-firmware/archive/master.zip)) and unpack into a local folder, say `C:\nodemcu-firmware`; choose the master / dev versions to match the firmware version that you want to use. If you want an Integer buld then edit the `app/includes/user_config.h` file to select this.
- You can either download this from Terry's fileserver. The [ELF variant](http://files.ellisons.org.uk/esp8266/luac.cross) is used for all recent Linux and WSL flavours, or the [cygwin binary](http://files.ellisons.org.uk/esp8266/luac.cross.cygwin)) for the Cygwin environment. - Choose a preferred toolchain to build your `luac.cross` executable. You have a number of options here:
- Or you can compile it yourself by downloading the current NodeMCU sources (this [ZIPfile](https://github.com/nodemcu/nodemcu-firmware/archive/master.zip)); edit the `app/includes/user_config.h` file and then `cd` to the `app/lua/luac_cross` and run make to build the compiler in the NodeMCU firmware root directory. Note that the `luac.cross` make only needs the host toolchain which is installed by default. - If you are a Windows 10 user with the Windows Subsystem for Linux (WSL) already installed, then this is a Linux environment so you can follow the [Linux build instructions](#Linux) below.
- A less resource intensive option which works on all Windows OS variants is to use Cygwin or MinGW, which are varaint ports of the [GNU Compiler Collection](https://gcc.gnu.org/) to Windows and which can both compile to native Windows executables. In the case of Cygwin, [install Cygwin](https://www.cygwin.com/install.html) (selecting the Cygwin core + **gcc-core** + **gnu make** in the install menu). In the case of MinGW you again only need a very basic C build environment so [install the MINGW](http://mingw.org/wiki/InstallationHOWTOforMinGW); you only need the core GCC and mingw32-make. Both both these create a **Cmd** prompt which paths in the relevant GCC toolchain. Switch to the `app/lua/luac_cross` and run make to build the compiler in the NodeMCU firmware root directory. You do this by rning `make` in Cygwin and `mingw32-make -f mingw32-Makefile.mak` in MinGW.
### macOS - If you can C development experience on the PC and a version of the MS Visual Studio on your PC then you can also simply build the image using the supplied MS project file.
- Once you have a built `luac.cross` executable, then you can use this to compile Lua code into an LFS image. You might wish to move this out of the nodemcu-firmware hierarchy, since this folder hierarchy is no longer required and can be trashed.
TBD
1. `$ cd app/lua/luac_cross`
2. `$ make`
### Linux ### Linux
TBD - Ensure that you have a "build essential" GCC toolchain installed.
- Download the current NodeMCU sources (this [dev ZIP file](https://github.com/nodemcu/nodemcu-firmware/archive/dev.zip) or [master ZIP file](https://github.com/nodemcu/nodemcu-firmware/archive/master.zip)) and unpack into a local folder; choose the master / dev versions to match the firmware version that you want to use. If you want an Integer buld then edit the `app/includes/user_config.h` file to select this.
- Change directory to the `app/lua/luac_cross` sub-folder
- Run `make` to build the executable.
- Once you have a built `luac.cross` executable, then you can use this to compile Lua code into an LFS image. You might wish to move this out of the nodemcu-firmware hierarchy, since this folder hierarchy is no longer required and can be trashed.
1. `$ cd app/lua/luac_cross` ### macOS
2. `$ make`
As for [Linux](#linux)
[↑ back to matrix](#task-os-selector) [↑ back to matrix](#task-os-selector)
...@@ -325,4 +326,45 @@ Once the LFS image file is on SPIFFS, you can execute the [node.flashreload()](. ...@@ -325,4 +326,45 @@ Once the LFS image file is on SPIFFS, you can execute the [node.flashreload()](.
Do a protected call of this `_init` code: `pcall(node.flashindex("_init"))` and check the error status. See [Programming Techniques and Approachs](lfs.md#programming-techniques-and-approachs) in the LFS whitepaper for a more detailed description. Do a protected call of this `_init` code: `pcall(node.flashindex("_init"))` and check the error status. See [Programming Techniques and Approachs](lfs.md#programming-techniques-and-approachs) in the LFS whitepaper for a more detailed description.
### Minimal LFS example
Below is a brief overview of building and running the simplest LFS-based system possible.
To use LFS, start with a version of NodeMCU with `LUA_FLASH_STORE` set in `app/include/user_config.h`, and load it on the ESP8266 in the usual way (whatever that is for your set up).
Then build an LFS file system. This can be done in several ways, as discussed above; one of the easiest is to use `luac.cross -f -o lfs.img *lua` on the host machine. The file [lua_examples/lfs/_init.lua](https://github.com/nodemcu/nodemcu-firmware/tree/dev/lua_examples/lfs/_init.lua) should definitely be included in the image, since it's the easiest way of registering the LFS modules. The `lfs.img` file can then be downloaded to the ESP8266 just like any other file.
The next step is to tell the ESP8266 that the LFS file system exists. This is done with eg. [node.flashreload("lfs.img")](../modules/node/#nodeflashreload), which will trigger a reset, followed by [node.flashindex("_init")()](../modules/node/#nodeflashindex) to register the modules; logging into the esp8266 and running the following commands gives an overview of the command sequence, given a main.lua file consisting of the line `print("LFS main() module")`
```
>
> node.flashreload("lfs.img")
-- flashreload() triggers a reset here.
> print(LFS)
nil
> node.flashindex("_init")()
-- LFS is now initialised.
> print(LFS)
table: 3fff06e0
-- List the modules in the LFS.
> print(LFS._list)
table: 3fff0728
> for k,v in pairs(LFS._list) do print(k,v) end
1 dummy_strings
2 _init
3 main
-- Call the LFS main() module.
> LFS.main()
LFS main() module
>
```
Note that no error correction has been used, since the commands are intended to be entered at a terminal, and errors will become obvious.
Then you should set up the ESP8266 boot process to check for the existence of an LFS image and run whichever module is required. Once the LFS module table has been registered by running [lua_examples/lfs/_init.lua](https://github.com/nodemcu/nodemcu-firmware/tree/dev/lua_examples/lfs/_init.lua) , running an LFS module is simple a matter of eg: `LFS.main()`.
[node.flashreload()](../modules/node/#nodeflashreload) need only be rerun if the LFS image is updated; after it has loaded the LFS image into flash memory the original file (in SPIFFS) is no longer used, and can be deleted.
Once LFS is known to work, then modules such as [lua_examples/lfs/dummy_strings.lua](https://github.com/nodemcu/nodemcu-firmware/tree/dev/lua_examples/lfs/dummy_strings.lua) can usefully be added, together of course with effective error checking.
[↑ back to matrix](#task-os-selector) [↑ back to matrix](#task-os-selector)
# cohelper Module
| Since | Origin / Contributor | Maintainer | Source |
| :----- | :-------------------- | :---------- | :------ |
| 2019-07-24 | [TerryE](https://github.com/TerryE) | [TerryE](https://github.com/TerryE) | [cohelper.lua](../../lua_modules/cohelper/cohelper.lua) |
This module provides a simple wrapper around long running functions to allow
these to execute within the SDK and its advised limit of 15 mSec per individual
task execution. It does this by exploiting the standard Lua coroutine
functionality as described in the [Lua RM §2.11](https://www.lua.org/manual/5.1/manual.html#2.11) and [PiL Chapter 9](https://www.lua.org/pil/9.html).
The NodeMCU Lua VM fully supports the standard coroutine functionality. Any
interactive or callback tasks are executed in the default thread, and the coroutine
itself runs in a second separate Lua stack. The coroutine can call any library
functions, but any subsequent callbacks will, of course, execute in the default
stack.
Interaction between the coroutine and the parent is through yield and resume
statements, and since the order of SDK tasks is indeterminate, the application
must take care to handle any ordering issues. This particular example uses
the `node.task.post()` API with the `taskYield()`function to resume itself,
so the running code can call `taskYield()` at regular points in the processing
to spilt the work into separate SDK tasks.
A similar approach could be based on timer or on a socket or pipe CB. If you
want to develop such a variant then start by reviewing the source and understanding
what it does.
### Require
```lua
local cohelper = require("cohelper")
-- or linked directly with the `exec()` method
require("cohelper").exec(func, <params>)
```
### Release
Not required. All resources are released on completion of the `exec()` method.
## `cohelper.exec()`
Execute a function which is wrapped by a coroutine handler.
#### Syntax
`require("cohelper").exec(func, <params>)`
#### Parameters
- `func`: Lua function to be executed as a coroutine.
- `<params>`: list of 0 or more parameters used to initialise func. the number and types must be matched to the funct declaration
#### Returns
Return result of first yield.
#### Notes
1. The coroutine function `func()` has 1+_n_ arguments The first is the supplied task yield function. Calling this yield function within `func()` will temporarily break execution and cause an SDK reschedule which migh allow other executinng tasks to be executed before is resumed. The remaining arguments are passed to the `func()` on first call.
2. The current implementation passes a single integer parameter across `resume()` / `yield()` interface. This acts to count the number of yields that occur. Depending on your appplication requirements, you might wish to amend this.
### Full Example
Here is a function which recursively walks the globals environment, the ROM table
and the Registry. Without coroutining, this walk terminate with a PANIC following
a watchdog timout. I don't want to sprinkle the code with `tmr.wdclr(`) that could
in turn cause the network stack to fail. Here is how to do it using coroutining:
```Lua
require "cohelper".exec(
function(taskYield, list)
local s, n, nCBs = {}, 0, 0
local function list_entry (name, v) -- upval: taskYield, nCBs
print(name, v)
n = n + 1
if n % 20 == 0 then nCBs = taskYield(nCBs) end
if type(v):sub(-5) ~= 'table' or s[v] or name == 'Reg.stdout' then return end
s[v]=true
for k,tv in pairs(v) do
list_entry(name..'.'..k, tv)
end
s[v] = nil
end
for k,v in pairs(list) do
list_entry(k, v)
end
print ('Total lines, print batches = ', n, nCBs)
end,
{_G = _G, Reg = debug.getregistry(), ROM = ROM}
)
```
...@@ -22,21 +22,25 @@ Function used to connect to Redis server. ...@@ -22,21 +22,25 @@ Function used to connect to Redis server.
`redis.connect(host, [port])` `redis.connect(host, [port])`
#### Parameters #### Parameters
- `host`: Redis host name or address - `host` Redis host name or address
- `port`: Redis database port. Default value is 6379. - `port` Redis database port. Default value is 6379.
#### Returns #### Returns
Object with rest of the functions. Object with rest of the functions.
## subscribe() !!! important
You need to start calling this `connect()` function to obtain a Redis object. All other functions are invoked on this object. Note the difference between `redis.connect()` (single dot) and `redis:subscribe()` (colon).
## redis:subscribe()
Subscribe to a Redis channel. Subscribe to a Redis channel.
#### Syntax #### Syntax
`redis:subscribe(channel, handler)` `redis:subscribe(channel, handler)`
#### Parameters #### Parameters
- `channel`: Channel name - `channel` Channel name
- `handler`: Handler function that will be called on new message in subscribed channel - `handler` Handler function that will be called on new message in subscribed channel
#### Returns #### Returns
`nil` `nil`
...@@ -48,8 +52,8 @@ Publish a message to a Redis channel. ...@@ -48,8 +52,8 @@ Publish a message to a Redis channel.
`redis:publish(channel, message)` `redis:publish(channel, message)`
#### Parameters #### Parameters
- `channel`: Channel name - `channel` Channel name
- `message`: Message to publish - `message` Message to publish
#### Returns #### Returns
`nil` `nil`
...@@ -61,12 +65,12 @@ Unsubscribes from a channel. ...@@ -61,12 +65,12 @@ Unsubscribes from a channel.
`redis:unsubscribe(channel)` `redis:unsubscribe(channel)`
#### Parameters #### Parameters
- `channel`: Channel name to unsubscribe from - `channel` Channel name to unsubscribe from
#### Returns #### Returns
`nil` `nil`
#### redis:close() ## redis:close()
Function to close connection to Redis server. Function to close connection to Redis server.
#### Syntax #### Syntax
...@@ -78,9 +82,11 @@ None ...@@ -78,9 +82,11 @@ None
#### Returns #### Returns
`nil` `nil`
#### Example ## Example
```lua ```lua
local redis = dofile("redis.lua").connect(host, port) local redis = dofile("redis.lua").connect(host, port)
redis:publish("chan1", foo") redis:publish("chan1", "foo")
redis:subscribe("chan1", function(channel, msg) print(channel, msg) end) redis:subscribe("chan1", function(channel, msg)
print(channel, msg)
end)
``` ```
...@@ -125,7 +125,7 @@ adc1:setting(ads1115.GAIN_6_144V, ads1115.DR_128SPS, ads1115.SINGLE_0, ads1115.C ...@@ -125,7 +125,7 @@ adc1:setting(ads1115.GAIN_6_144V, ads1115.DR_128SPS, ads1115.SINGLE_0, ads1115.C
local function comparator(level, when) local function comparator(level, when)
-- read adc result with read() when threshold reached -- read adc result with read() when threshold reached
gpio.trig(alert_pin) gpio.trig(alert_pin)
volt, volt_dec, adc, sign = ads1:read() volt, volt_dec, adc, sign = adc1:read()
print(volt, volt_dec, adc, sign) print(volt, volt_dec, adc, sign)
end end
gpio.mode(alert_pin, gpio.INT) gpio.mode(alert_pin, gpio.INT)
......
...@@ -124,8 +124,8 @@ Userdata object with `update` and `finalize` functions available. ...@@ -124,8 +124,8 @@ Userdata object with `update` and `finalize` functions available.
#### Example #### Example
```lua ```lua
hashobj = crypto.new_hash("SHA1") hashobj = crypto.new_hash("SHA1")
hashobj:update("FirstString")) hashobj:update("FirstString")
hashobj:update("SecondString")) hashobj:update("SecondString")
digest = hashobj:finalize() digest = hashobj:finalize()
print(crypto.toHex(digest)) print(crypto.toHex(digest))
``` ```
...@@ -167,8 +167,8 @@ Userdata object with `update` and `finalize` functions available. ...@@ -167,8 +167,8 @@ Userdata object with `update` and `finalize` functions available.
#### Example #### Example
```lua ```lua
hmacobj = crypto.new_hmac("SHA1", "s3kr3t") hmacobj = crypto.new_hmac("SHA1", "s3kr3t")
hmacobj:update("FirstString")) hmacobj:update("FirstString")
hmacobj:update("SecondString")) hmacobj:update("SecondString")
digest = hmacobj:finalize() digest = hmacobj:finalize()
print(crypto.toHex(digest)) print(crypto.toHex(digest))
``` ```
......
# DS18B20 Module
| Since | Origin / Contributor | Maintainer | Source |
| :----- | :-------------------- | :---------- | :------ |
| 2017-06-11 | [fetchbot](https://github.com/fetchbot) | [fetchbot](https://github.com/fetchbot) | [ds18b20.c](../../app/modules/ds18b20.c)|
This module provides access to the DS18B20 1-Wire digital thermometer.
## Deprecation Notice
Note that NodeMCU offers both a C module (this one) and [a Lua module for this
sensor](https://github.com/nodemcu/nodemcu-firmware/tree/dev/lua_modules/ds18b20).
The C implementation is deprecated and will be removed soon; please transition
to Lua code.
## ds18b20.read()
Issues a temperature conversion of all connected sensors on the onewire bus and returns the measurment results after a conversion delay in a callback function.
The returned measurements can be filtered through the ROM addresses passed as a table or by the family type.
The callback function gets invoked for every specified sensor.
#### Syntax
`ds18b20.read(CALLBACK, ROM[, FAMILY_ADDRESS])`
#### Parameters
- `CALLBACK` callback function executed for each sensor
* e.g. `function(INDEX, ROM, RES, TEMP, TEMP_DEC, PAR) print(INDEX, ROM, RES, TEMP, TEMP_DEC, PAR) end`
- `ROM` table which contains the addresses for the specified sensors, or left empty to perform a onewire bus search for all sensors
* e.g. `{"28:FF:FF:FF:FF:FF:FF:FF","28:FF:FF:FF:FF:FF:FF:FF"}`, `{}`
- `FAMILY_ADDRESS` optional to limit the search for devices to a specific family type
* e.g `0x28`
#### Returns
`nil`
#### Callback function parameters
- `INDEX` index of the sensor on the bus
- `ROM` sensors 64-bit lasered rom code
* `28:FF:FF:FF:FF:FF:FF:FF` LSB, 8-bit family code, 48-bit serial number, MSB 8-bit crc
- `RES` temperature resolution
- `TEMP` temperature
- `TEMP_DEC` temperature decimals for integer firmware
- `PAR` sensor parasitic flag
!!! note
If using float firmware then `temp` is a floating point number. On an integer firmware, the final value has to be concatenated from `temp` and `temp_dec`.
#### Example
```lua
local ow_pin = 3
ds18b20.setup(ow_pin)
-- read all sensors and print all measurement results
ds18b20.read(
function(ind,rom,res,temp,tdec,par)
print(ind,string.format("%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X",string.match(rom,"(%d+):(%d+):(%d+):(%d+):(%d+):(%d+):(%d+):(%d+)")),res,temp,tdec,par)
end,{});
-- read only sensors with family type 0x28 and print all measurement results
ds18b20.read(
function(ind,rom,res,temp,tdec,par)
print(ind,string.format("%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X",string.match(rom,"(%d+):(%d+):(%d+):(%d+):(%d+):(%d+):(%d+):(%d+)")),res,temp,tdec,par)
end,{},0x28);
-- save device roms in a variable
local addr = {}
ds18b20.read(
function(ind,rom,res,temp,tdec,par)
addr[ind] = {string.format("%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X",string.match(rom,"(%d+):(%d+):(%d+):(%d+):(%d+):(%d+):(%d+):(%d+)"))}
end,{});
-- read only sensors listed in the variable addr
ds18b20.read(
function(ind,rom,res,temp,tdec,par)
print(ind,string.format("%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X",string.match(rom,"(%d+):(%d+):(%d+):(%d+):(%d+):(%d+):(%d+):(%d+)")),res,temp,tdec,par)
end,addr);
-- print only parasitic sensors
ds18b20.read(
function(ind,rom,res,temp,tdec,par)
if (par == 1) then
print(ind,string.format("%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X",string.match(rom,"(%d+):(%d+):(%d+):(%d+):(%d+):(%d+):(%d+):(%d+)")),res,temp,tdec,par)
end
end,{});
-- print if temperature is greater or less than a defined value
ds18b20.read(
function(ind,rom,res,temp,tdec,par)
if (t > 25) then
print(ind,string.format("%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X",string.match(rom,"(%d+):(%d+):(%d+):(%d+):(%d+):(%d+):(%d+):(%d+)")),res,temp,tdec,par)
end
if (t < 20) then
print(ind,string.format("%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X",string.match(rom,"(%d+):(%d+):(%d+):(%d+):(%d+):(%d+):(%d+):(%d+)")),res,temp,tdec,par)
end
end,{});
```
## ds18b20.setting()
Configuration of the temperature resolution settings.
#### Syntax
`ds18b20.setting(ROM, RES)`
#### Parameters
- `ROM` table which contains the addresses for the specified sensors, or empty for all sensors
* e.g. `{"28:FF:FF:FF:FF:FF:FF:FF","28:FF:FF:FF:FF:FF:FF:FF"}`, `{}`
- `RES` temperature bit resolution
* `9` - `12`
#### Returns
`nil`
#### Example
```lua
local ow_pin = 3
ds18b20.setup(ow_pin)
ds18b20.setting({"28:FF:FF:FF:FF:FF:FF:FF","28:FF:FF:FF:FF:FF:FF:FF"}, 9)
```
## ds18b20.setup()
Initializes the onewire bus on the selected pin.
#### Syntax
`ds18b20.setup(OW_BUS_PIN)`
#### Parameters
- `OW_BUS_PIN`
* `1` - `12`
#### Returns
`nil`
#### Example
```lua
local ow_pin = 3
ds18b20.setup(ow_pin)
```
# enduser setup Module # enduser setup Module aka Captive Portal aka WiFi Manager
| Since | Origin / Contributor | Maintainer | Source | | Since | Origin / Contributor | Maintainer | Source |
| :----- | :-------------------- | :---------- | :------ | | :----- | :-------------------- | :---------- | :------ |
| 2015-09-02 | [Robert Foss](https://github.com/robertfoss) | [Robert Foss](https://github.com/robertfoss) | [enduser_setup.c](../../app/modules/enduser_setup.c)| | 2015-09-02 | [Robert Foss](https://github.com/robertfoss) | [Robert Foss](https://github.com/robertfoss) | [enduser_setup.c](../../app/modules/enduser_setup.c)|
This module provides a simple way of configuring ESP8266 chips without using a serial interface or pre-programming WiFi credentials onto the chip. This module provides a simple way of configuring ESP8266 chips without using a
serial interface or pre-programming WiFi credentials onto the chip.
After running [`enduser_setup.start()`](#enduser_setupstart), a wireless
network named "SetupGadget_XXXXXX" will starting. This prefix can be overridden
in `user_config.h` by defining `ENDUSER_SETUP_AP_SSID`. Connect to that SSID
and then navigate to the root of any website or to 192.168.4.1.
`http://example.com/` will work, but do not use `.local` domains because it
will fail on iOS. A web page similar to the one depicted below will load,
allowing the end user to provide their Wi-Fi credentials.
![enduser setup config dialog](../img/enduser-setup-captive-portal.png "enduser setup config dialog")
After an IP address has been successfully obtained, then this module will stop
as if [`enduser_setup.stop()`](#enduser_setupstop) had been called. There is a
10-second delay before teardown to allow connected clients to obtain a last
status message while the SoftAP is still active.
Alternative HTML can be served by placing a file called `enduser_setup.html` on
the filesystem. Everything needed by the web page must be included in this one
file. This file will be kept in RAM, so keep it as small as possible. The file
can be gzip'd ahead of time to reduce the size (i.e., using `gzip -n` or
`zopfli`), and when served, the End User Setup module will add the appropriate
`Content-Encoding` header to the response.
*Note: If gzipped, the file can also be named `enduser_setup.html.gz` for
semantic purposes. GZIP encoding is determined by the file's contents, not the
filename.*
### Additional configuration parameters
You can also add some additional inputs in the `enduser_setup.html` (as long as
you keep those needed for the WiFi setup). The additional data will be written
in a `eus_params.lua` file in the root filesystem of the ESP8266, which you can
then load in your own code. In this case, the data will be saved as a set of
variables with the name being the input name, and the value being a string
representing what you put in the form.
For instance, if your HTML contains two additional inputs:
```html
<input name=timeout_delay type=text placeholder="Delay in seconds" />
<input name=device_name type=text placeholder="Unique device name" />
```
Then the `eus_params.lua` file will contain the following:
![enduser setup config dialog](../img/enduser-setup.jpg "enduser setup config dialog") ```lua
-- those wifi_* are the base parameters that are saved anyway
local p = {}
p.wifi_ssid="ssid"
p.wifi_password="password"
-- your own parameters:
p.timeout_delay="xxx"
p.device_name="yyy"
return p
```
After running [`enduser_setup.start()`](#enduser_setupstart), a wireless network named "SetupGadget_XXXXXX" will start (this prefix can be overridden in `user_config.h` by defining ### How to use the eus_params.lua file
`ENDUSER_SETUP_AP_SSID`). Connect to that SSID and then navigate to the root
of any website (e.g., `http://example.com/` will work, but do not use `.local` domains because it will fail on iOS). A web page similar to the picture above will load, allowing the
end user to provide their Wi-Fi information.
After an IP address has been successfully obtained, then this module will stop as if [`enduser_setup.stop()`](#enduser_setupstop) had been called. There is a 10-second delay before Simply include the file by using the `dofile` function:
teardown to allow connected clients to obtain a last status message while the SoftAP is still active. ```lua
p = dofile('eus_params.lua')
-- now use the parameters in the Lua table
print("Wifi device_name: " .. p.device_name)
```
Alternative HTML can be served by placing a file called `enduser_setup.html` on the filesystem. Everything needed by the web page must be included in this one file. This file will be kept ### HTTP endpoints:
in RAM, so keep it as small as possible. The file can be gzip'd ahead of time to reduce the size (i.e., using `gzip -n` or `zopfli`), and when served, the End User Setup module will add
the appropriate `Content-Encoding` header to the response.
*Note: If gzipped, the file can also be named `enduser_setup.html.gz` for semantic purposes. Gzip encoding is determined by the file's contents, not the filename.* |Path|Method|Description|
|----|------|-----------|
|/|GET|Returns HTML for the web page. Will return the contents of `enduser_setup.html` if it exists on the filesystem, otherwise will return a page embedded into the firmware image.|
|/aplist|GET|Forces the ESP8266 to perform a site survey across all channels, reporting access points that it can find. Return payload is a JSON array: `[{"ssid":"foobar","rssi":-36,"chan":3}]`|
|/generate_204|GET|Returns a HTTP 204 status (expected by certain Android clients during Wi-Fi connectivity checks)|
|/status|GET|Returns plaintext status description, used by the web page|
|/status.json|GET|Returns a JSON payload containing the ESP8266's chip id in hexadecimal format and the status code: 0=Idle, 1=Connecting, 2=Wrong Password, 3=Network not Found, 4=Failed, 5=Success|
|/setwifi|POST|HTML form post for setting the WiFi credentials. Expects HTTP content type `application/x-www-form-urlencoded`. Supports sending and storing additinal configuration parameters (as input fields). Returns the same payload as `/status.json` instead of redirecting to `/`. See also: `/update`.|
|/update|GET|Data submission target. Example: `http://example.com/update?wifi_ssid=foobar&wifi_password=CorrectHorseBatteryStaple`. Will redirect to `/` when complete. Note that will NOT update the `eus_params.lua` file i.e. it does NOT support sending arbitrary parameters. See also: `/setwifi`. |
The following HTTP endpoints exist: Module functions are described below.
|Endpoint|Description|
|--------|-----------|
|/|Returns HTML for the web page. Will return the contents of `enduser_setup.html` if it exists on the filesystem, otherwise will return a page embedded into the firmware image.|
|/aplist|Forces the ESP8266 to perform a site survey across all channels, reporting access points that it can find. Return payload is a JSON array: `[{"ssid":"foobar","rssi":-36,"chan":3}]`|
|/generate_204|Returns a HTTP 204 status (expected by certain Android clients during Wi-Fi connectivity checks)|
|/status|Returns plaintext status description, used by the web page|
|/status.json|Returns a JSON payload containing the ESP8266's chip id in hexadecimal format and the status code: 0=Idle, 1=Connecting, 2=Wrong Password, 3=Network not Found, 4=Failed, 5=Success|
|/setwifi|Endpoint intended for services to use for setting the wifi credentials. Identical to `/update` except returns the same payload as `/status.json` instead of redirecting to `/`.|
|/update|Form submission target. Example: `http://example.com/update?wifi_ssid=foobar&wifi_password=CorrectHorseBatteryStaple`. Must be a GET request. Will redirect to `/` when complete. |
## enduser_setup.manual() ## enduser_setup.manual()
Controls whether manual AP configuration is used. Controls whether manual AP configuration is used.
By default the `enduser_setup` module automatically configures an open access point when starting, and stops it when the device has been successfully joined to a WiFi network. If manual mode has been enabled, neither of this is done. The device must be manually configured for `wifi.SOFTAP` mode prior to calling `enduser_setup.start()`. Additionally, the portal is not stopped after the device has successfully joined to a WiFi network. By default the `enduser_setup` module automatically configures an open access
point when starting, and stops it when the device has been successfully joined
to a WiFi network. If manual mode has been enabled, neither of this is done.
The device must be manually configured for `wifi.SOFTAP` mode prior to calling
`enduser_setup.start()`. Additionally, the portal is not stopped after the
device has successfully joined to a WiFi network.
#### Syntax #### Syntax
`enduser_setup.manual([on_off])` `enduser_setup.manual([on_off])`
#### Parameters #### Parameters
- `on_off` a boolean value indicating whether to use manual mode; if not given, the function only returns the current setting. - `on_off` a boolean value indicating whether to use manual mode; if not
given, the function only returns the current setting.
#### Returns #### Returns
The current setting, true if manual mode is enabled, false if it is not. The current setting, true if manual mode is enabled, false if it is not.
...@@ -57,12 +115,12 @@ wifi.ap.config({ssid="MyPersonalSSID", auth=wifi.OPEN}) ...@@ -57,12 +115,12 @@ wifi.ap.config({ssid="MyPersonalSSID", auth=wifi.OPEN})
enduser_setup.manual(true) enduser_setup.manual(true)
enduser_setup.start( enduser_setup.start(
function() function()
print("Connected to wifi as:" .. wifi.sta.getip()) print("Connected to WiFi as:" .. wifi.sta.getip())
end, end,
function(err, str) function(err, str)
print("enduser_setup: Err #" .. err .. ": " .. str) print("enduser_setup: Err #" .. err .. ": " .. str)
end end
); )
``` ```
## enduser_setup.start() ## enduser_setup.start()
...@@ -86,13 +144,13 @@ Starts the captive portal. ...@@ -86,13 +144,13 @@ Starts the captive portal.
```lua ```lua
enduser_setup.start( enduser_setup.start(
function() function()
print("Connected to wifi as:" .. wifi.sta.getip()) print("Connected to WiFi as:" .. wifi.sta.getip())
end, end,
function(err, str) function(err, str)
print("enduser_setup: Err #" .. err .. ": " .. str) print("enduser_setup: Err #" .. err .. ": " .. str)
end, end,
print -- Lua print function can serve as the debug callback print -- Lua print function can serve as the debug callback
); )
``` ```
## enduser_setup.stop() ## enduser_setup.stop()
......
...@@ -139,6 +139,27 @@ remaining, used, total=file.fsinfo() ...@@ -139,6 +139,27 @@ remaining, used, total=file.fsinfo()
print("\nFile system info:\nTotal : "..total.." (k)Bytes\nUsed : "..used.." (k)Bytes\nRemain: "..remaining.." (k)Bytes\n") print("\nFile system info:\nTotal : "..total.." (k)Bytes\nUsed : "..used.." (k)Bytes\nRemain: "..remaining.." (k)Bytes\n")
``` ```
## file.getcontents()
Open and read the contents of a file.
#### Syntax
`file.getcontents(filename)`
#### Parameters
- `filename` file to be opened and read
#### Returns
file contents if the file exists. `nil` if the file does not exist.
#### Example (basic model)
```lua
print(file.getcontents('welcome.txt'))
```
#### See also
- [`file.putcontents()`](#fileputcontents)
## file.list() ## file.list()
Lists all files in the file system. Lists all files in the file system.
...@@ -287,6 +308,30 @@ file.remove("foo.lua") ...@@ -287,6 +308,30 @@ file.remove("foo.lua")
#### See also #### See also
[`file.open()`](#fileopen) [`file.open()`](#fileopen)
## file.putcontents()
Open and write the contents of a file.
#### Syntax
`file.putcontents(filename, contents)`
#### Parameters
- `filename` file to be created
- `contents` to be written to the file
#### Returns
`true` if the write is ok, `nil` on error
#### Example (basic model)
```lua
file.putcontents('welcome.txt', [[
Hello to new user
-----------------
]])
```
#### See also
- [`file.getcontents()`](#filegetcontents)
## file.rename() ## file.rename()
Renames a file. If a file is currently open, it will be closed first. Renames a file. If a file is currently open, it will be closed first.
......
...@@ -20,7 +20,10 @@ to make it easy to access. If there are multiple headers of the same name, then ...@@ -20,7 +20,10 @@ to make it easy to access. If there are multiple headers of the same name, then
**SSL/TLS support** **SSL/TLS support**
Take note of constraints documented in the [net module](net.md). !!! attention
Secure (`https`) connections come with quite a few limitations. Please see
the warnings in the [tls module](tls.md)'s documentation.
## http.delete() ## http.delete()
......
...@@ -2,16 +2,56 @@ ...@@ -2,16 +2,56 @@
| Since | Origin / Contributor | Maintainer | Source | | Since | Origin / Contributor | Maintainer | Source |
| :----- | :-------------------- | :---------- | :------ | | :----- | :-------------------- | :---------- | :------ |
| 2014-12-22 | [Zeroday](https://github.com/funshine) | [Zeroday](https://github.com/funshine) | [i2c.c](../../app/modules/i2c.c)| | 2014-12-22 | [Zeroday](https://github.com/funshine) | [Zeroday](https://github.com/funshine) | [i2c.c](../../app/modules/i2c.c)|
| 2018-08-30 | [Natalia Sorokina](https://github.com/sonaux) | | [i2c_master.c](../../app/driver/i2c_master.c)|
I²C (I2C, IIC) is a serial 2-wire bus for communicating with various devices. Also known as SMBus or TWI, though SMBus have some additions to the I2C protocol.
ESP8266 chip does not have hardware I²C, so module uses software I²C driver.
It can be set up on any GPIO pins including GPIO16 (see below).
This module supports:
- Master mode
- Multiple buses (up to 10) with different speeds on each bus
- Standard(Slow, 100kHz), Fast(400kHz) and FastPlus(1MHz) modes or an arbitrary clock speed
- Clock stretching (slow slave device can tell the master to wait)
- Sharing SDA line over multiple I²C buses to save available pins
- GPIO16 pin can be used as SCL pin, but selected bus will be limited to not more than FAST speed.
HIGH-speed mode (3.5MHz clock) and 10-bit addressing scheme is not supported.
You have to call `i2c.setup` on a given I²C bus at least once before communicating to any device connected to that bus, otherwise you will get an error.
I²C bus designed to work in open-drain mode, so it needs pull-up resistors 1k - 10k on SDA and SCL lines. Though many peripheral modules have pull-up resistors onboard and will work without additional external resistors.
Hint for using many identical devices with same address:
Many devices allow to choose between 2 I²C addresses via pin or soldered 0 Ohm resistor.
If address change is not an option or you need to use more than 2 similar devices, you can use different I²C buses.
Initialize them once by calling `i2c.setup` with different bus numbers and pins, then refer to each device by bus id and device address.
SCL pins should be different, SDA can be shared on one pin.
Note that historically many NodeMCU drivers and modules assumed that only a single I²C bus with id 0 is available, so it is always safer to start with id 0 as first bus in your code.
If your device driver functions do not have I²C bus id as an input parameter and/or not built with Lua OOP principles then most probably device will be accessible through bus id 0 only and must be connected to its pins.
To enable new driver comment line `#define I2C_MASTER_OLD_VERSION` in `user_config.h`
To enable support for GPIO16 (D0) uncomment line `#define I2C_MASTER_GPIO16_ENABLED` in `user_config.h`
GPIO16 does not support open-drain mode and works in push-pull mode. That may lead to communication errors when slave device tries to stretch SCL clock but unable to hold SCL line low. If that happens, try setting lower I²C speed.
!!! caution
If your module reboots when trying to use GPIO16 pin, then it is wired to RESET pin to support deep sleep mode and you cannot use GPIO16 for I²C bus or other purposes.
## i2c.address() ## i2c.address()
Setup I²C address and read/write mode for the next transfer. Setup I²C address and read/write mode for the next transfer.
On I²C bus every device is addressed by 7-bit number. Address for the particular device can be found in its datasheet.
#### Syntax #### Syntax
`i2c.address(id, device_addr, direction)` `i2c.address(id, device_addr, direction)`
#### Parameters #### Parameters
- `id` always 0 - `id` bus number
- `device_addr` 7-bit device address, remember that [in I²C `device_addr` represents the upper 7 bits](http://www.nxp.com/documents/user_manual/UM10204.pdf#page=13) followed by a single `direction` bit - `device_addr` 7-bit device address. Remember that [in I²C `device_addr` represents the upper 7 bits](http://www.nxp.com/documents/user_manual/UM10204.pdf#page=13) followed by a single `direction` bit. Sometimes device address is advertised as 8-bit value, then you should divide it by 2 to get 7-bit value.
- `direction` `i2c.TRANSMITTER` for writing mode , `i2c. RECEIVER` for reading mode - `direction` `i2c.TRANSMITTER` for writing mode , `i2c. RECEIVER` for reading mode
#### Returns #### Returns
...@@ -27,7 +67,7 @@ Read data for variable number of bytes. ...@@ -27,7 +67,7 @@ Read data for variable number of bytes.
`i2c.read(id, len)` `i2c.read(id, len)`
#### Parameters #### Parameters
- `id` always 0 - `id` bus number
- `len` number of data bytes - `len` number of data bytes
#### Returns #### Returns
...@@ -39,11 +79,11 @@ id = 0 ...@@ -39,11 +79,11 @@ id = 0
sda = 1 sda = 1
scl = 2 scl = 2
-- initialize i2c, set pin1 as sda, set pin2 as scl -- initialize i2c, set pin 1 as sda, set pin 2 as scl
i2c.setup(id, sda, scl, i2c.SLOW) i2c.setup(id, sda, scl, i2c.FAST)
-- user defined function: read from reg_addr content of dev_addr -- user defined function: read 1 byte of data from device
function read_reg(dev_addr, reg_addr) function read_reg(id, dev_addr, reg_addr)
i2c.start(id) i2c.start(id)
i2c.address(id, dev_addr, i2c.TRANSMITTER) i2c.address(id, dev_addr, i2c.TRANSMITTER)
i2c.write(id, reg_addr) i2c.write(id, reg_addr)
...@@ -56,29 +96,51 @@ function read_reg(dev_addr, reg_addr) ...@@ -56,29 +96,51 @@ function read_reg(dev_addr, reg_addr)
end end
-- get content of register 0xAA of device 0x77 -- get content of register 0xAA of device 0x77
reg = read_reg(0x77, 0xAA) reg = read_reg(id, 0x77, 0xAA)
print(string.byte(reg)) print(string.byte(reg))
``` ```
####See also #### See also
[i2c.write()](#i2cwrite) [i2c.write()](#i2cwrite)
## i2c.setup() ## i2c.setup()
Initialize the I²C module. Initialize the I²C bus with the selected bus number, pins and speed.
#### Syntax #### Syntax
`i2c.setup(id, pinSDA, pinSCL, speed)` `i2c.setup(id, pinSDA, pinSCL, speed)`
####Parameters #### Parameters
- `id` always 0 - `id` 0~9, bus number
- `pinSDA` 1~12, IO index - `pinSDA` 1~12, IO index
- `pinSCL` 1~12, IO index - `pinSCL` 0~12, IO index
- `speed` only `i2c.SLOW` supported - `speed` `i2c.SLOW` (100kHz), `i2c.FAST` (400kHz), `i2c.FASTPLUS` (1MHz) or any clock frequency in range of 25000-1000000 Hz.
FASTPLUS mode results in 600kHz I2C clock speed at default 80MHz CPU frequency. To get 1MHz I2C clock speed change CPU frequency to 160MHz with function `node.setcpufreq(node.CPU160MHZ)`.
#### Returns #### Returns
`speed` the selected speed `speed` the selected speed, `0` if bus initialization error.
####See also #### Example
```lua
i2c0 = {
id = 0,
sda = 1,
scl = 0,
speed = i2c.FAST
}
i2c1 = {
id = 1,
sda = 1,
scl = 2,
speed = i2c.FASTPLUS
}
-- initialize i2c bus 0
i2c0.speed = i2c.setup(i2c0.id, i2c0.sda, i2c0.scl, i2c0.speed)
-- initialize i2c bus 1 with shared SDA on pin 1
node.setcpufreq(node.CPU160MHZ) -- to support FASTPLUS speed
i2c1.speed = i2c.setup(i2c1.id, i2c1.sda, i2c1.scl, i2c1.speed)
print("i2c bus 0 speed: ", i2c0.speed, "i2c bus 1 speed: ", i2c1.speed)
```
#### See also
[i2c.read()](#i2cread) [i2c.read()](#i2cread)
## i2c.start() ## i2c.start()
...@@ -88,12 +150,12 @@ Send an I²C start condition. ...@@ -88,12 +150,12 @@ Send an I²C start condition.
`i2c.start(id)` `i2c.start(id)`
#### Parameters #### Parameters
`id` always 0 `id` bus number
#### Returns #### Returns
`nil` `nil`
####See also #### See also
[i2c.read()](#i2cread) [i2c.read()](#i2cread)
## i2c.stop() ## i2c.stop()
...@@ -102,23 +164,23 @@ Send an I²C stop condition. ...@@ -102,23 +164,23 @@ Send an I²C stop condition.
#### Syntax #### Syntax
`i2c.stop(id)` `i2c.stop(id)`
####Parameters #### Parameters
`id` always 0 `id` bus number
#### Returns #### Returns
`nil` `nil`
####See also #### See also
[i2c.read()](#i2cread) [i2c.read()](#i2cread)
## i2c.write() ## i2c.write()
Write data to I²C bus. Data items can be multiple numbers, strings or Lua tables. Write data to I²C bus. Data items can be multiple numbers, strings or Lua tables.
####Syntax #### Syntax
`i2c.write(id, data1[, data2[, ..., datan]])` `i2c.write(id, data1[, data2[, ..., datan]])`
####Parameters #### Parameters
- `id` always 0 - `id` bus number
- `data` data can be numbers, string or Lua table. - `data` data can be numbers, string or Lua table.
#### Returns #### Returns
...@@ -126,7 +188,30 @@ Write data to I²C bus. Data items can be multiple numbers, strings or Lua table ...@@ -126,7 +188,30 @@ Write data to I²C bus. Data items can be multiple numbers, strings or Lua table
#### Example #### Example
```lua ```lua
i2c.write(0, "hello", "world") id = 0
sda = 1
scl = 2
-- initialize i2c, set pin 1 as sda, set pin 2 as scl
i2c.setup(id, sda, scl, i2c.FAST)
-- user defined function: write some data to device
-- with address dev_addr starting from reg_addr
function write_reg(id, dev_addr, reg_addr, data)
i2c.start(id)
i2c.address(id, dev_addr, i2c.TRANSMITTER)
i2c.write(id, reg_addr)
c = i2c.write(id, data)
i2c.stop(id)
return c
end
-- set register with address 0x45 of device 0x77 with value 1
count = write_reg(id, 0x77, 0x45, 1)
print(count, " bytes written")
-- write text into i2c EEPROM starting with memory address 0
count = write_reg(id, 0x50, 0, "Sample")
print(count, " bytes written")
``` ```
#### See also #### See also
......
...@@ -123,26 +123,27 @@ none ...@@ -123,26 +123,27 @@ none
Connects to the broker specified by the given host, port, and secure options. Connects to the broker specified by the given host, port, and secure options.
#### Syntax #### Syntax
`mqtt:connect(host[, port[, secure[, autoreconnect]]][, function(client)[, function(client, reason)]])` `mqtt:connect(host[, port[, secure]][, function(client)[, function(client, reason)]])`
#### Parameters #### Parameters
- `host` host, domain or IP (string) - `host` host, domain or IP (string)
- `port` broker port (number), default 1883 - `port` broker port (number), default 1883
- `secure` 0/1 for `false`/`true`, default 0. Take note of constraints documented in the [net module](net.md). - `secure` boolean: if `true`, use TLS. Take note of constraints documented in the [net module](net.md).
- `autoreconnect` 0/1 for `false`/`true`, default 0. This option is *deprecated*.
- `function(client)` callback function for when the connection was established - `function(client)` callback function for when the connection was established
- `function(client, reason)` callback function for when the connection could not be established. No further callbacks should be called. - `function(client, reason)` callback function for when the connection could not be established. No further callbacks should be called.
!!! attention
Secure (`https`) connections come with quite a few limitations. Please see
the warnings in the [tls module](tls.md)'s documentation.
#### Returns #### Returns
`true` on success, `false` otherwise `true` on success, `false` otherwise
#### Notes #### Notes
Don't use `autoreconnect`. Let me repeat that, don't use `autoreconnect`. You should handle the errors explicitly and appropriately for An application should watch for connection failures and handle errors in the error callback,
your application. In particular, the default for `cleansession` above is `true`, so all subscriptions are destroyed when the connection in order to achieve a reliable connection to the server. For example:
is lost for any reason.
In order to acheive a consistent connection, handle errors in the error callback. For example:
``` ```
function handle_mqtt_error(client, reason) function handle_mqtt_error(client, reason)
...@@ -156,13 +157,12 @@ end ...@@ -156,13 +157,12 @@ end
In reality, the connected function should do something useful! In reality, the connected function should do something useful!
This is the description of how the `autoreconnect` functionality may (or may not) work. The two callbacks to `:connect()` alias with the "connect" and "offline"
callbacks available through `:on()`.
> When `autoreconnect` is set, then the connection will be re-established when it breaks. No error indication will be given (but all the Previously, we instructed an application to pass either the *integer* 0 or
> subscriptions may be lost if `cleansession` is true). However, if the *integer* 1 for `secure`. Now, this will trigger a deprecation warning; please
> very first connection fails, then no reconnect attempt is made, and the error is signalled through the callback (if any). The first connection use the *boolean* `false` or `true` instead.
> is considered a success if the client connects to a server and gets back a good response packet in response to its MQTT connection request.
> This implies (for example) that the username and password are correct.
#### Connection failure callback reason codes: #### Connection failure callback reason codes:
...@@ -213,7 +213,7 @@ Registers a callback function for an event. ...@@ -213,7 +213,7 @@ Registers a callback function for an event.
`mqtt:on(event, function(client[, topic[, message]]))` `mqtt:on(event, function(client[, topic[, message]]))`
#### Parameters #### Parameters
- `event` can be "connect", "message", "offline" or "overflow" - `event` can be "connect", "suback", "unsuback", "puback", "message", "overflow", or "offline"
- `function(client[, topic[, message]])` callback function. The first parameter is the client. If event is "message", the 2nd and 3rd param are received topic and message (strings). - `function(client[, topic[, message]])` callback function. The first parameter is the client. If event is "message", the 2nd and 3rd param are received topic and message (strings).
#### Returns #### Returns
...@@ -231,8 +231,13 @@ Publishes a message. ...@@ -231,8 +231,13 @@ Publishes a message.
- `message` the message to publish, (buffer or string) - `message` the message to publish, (buffer or string)
- `qos` QoS level - `qos` QoS level
- `retain` retain flag - `retain` retain flag
- `function(client)` optional callback fired when PUBACK received. NOTE: When calling publish() more than once, the last callback function defined will be called for ALL publish commands. - `function(client)` optional callback fired when PUBACK received.
#### Notes
When calling publish() more than once, the last callback function defined will
be called for ALL publish commands. This callback argument also aliases with
the "puback" callback for `:on()`.
#### Returns #### Returns
`true` on success, `false` otherwise `true` on success, `false` otherwise
...@@ -249,7 +254,13 @@ Subscribes to one or several topics. ...@@ -249,7 +254,13 @@ Subscribes to one or several topics.
- `topic` a [topic string](http://www.hivemq.com/blog/mqtt-essentials-part-5-mqtt-topics-best-practices) - `topic` a [topic string](http://www.hivemq.com/blog/mqtt-essentials-part-5-mqtt-topics-best-practices)
- `qos` QoS subscription level, default 0 - `qos` QoS subscription level, default 0
- `table` array of 'topic, qos' pairs to subscribe to - `table` array of 'topic, qos' pairs to subscribe to
- `function(client)` optional callback fired when subscription(s) succeeded. NOTE: When calling subscribe() more than once, the last callback function defined will be called for ALL subscribe commands. - `function(client)` optional callback fired when subscription(s) succeeded.
#### Notes
When calling subscribe() more than once, the last callback function defined
will be called for ALL subscribe commands. This callback argument also aliases
with the "suback" callback for `:on()`.
#### Returns #### Returns
`true` on success, `false` otherwise `true` on success, `false` otherwise
...@@ -278,7 +289,13 @@ Unsubscribes from one or several topics. ...@@ -278,7 +289,13 @@ Unsubscribes from one or several topics.
#### Parameters #### Parameters
- `topic` a [topic string](http://www.hivemq.com/blog/mqtt-essentials-part-5-mqtt-topics-best-practices) - `topic` a [topic string](http://www.hivemq.com/blog/mqtt-essentials-part-5-mqtt-topics-best-practices)
- `table` array of 'topic, anything' pairs to unsubscribe from - `table` array of 'topic, anything' pairs to unsubscribe from
- `function(client)` optional callback fired when unsubscription(s) succeeded. NOTE: When calling unsubscribe() more than once, the last callback function defined will be called for ALL unsubscribe commands. - `function(client)` optional callback fired when unsubscription(s) succeeded.
#### Notes
When calling subscribe() more than once, the last callback function defined
will be called for ALL subscribe commands. This callback argument also aliases
with the "unsuback" callback for `:on()`.
#### Returns #### Returns
`true` on success, `false` otherwise `true` on success, `false` otherwise
......
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