### Index # NodeMCU Documentation NodeMCU is an open source [Lua](https://www.lua.org/) based firmware for the [ESP8266 WiFi SOC from Espressif](http://espressif.com/products/esp8266/) and uses an on-module flash-based [SPIFFS](https://github.com/pellepl/spiffs) file system. NodeMCU is implemented in C and is layered on the [Espressif NON-OS SDK](https://github.com/espressif/ESP8266_NONOS_SDK). The firmware was initially developed as is a companion project to the popular ESP8266-based [NodeMCU development modules](https://github.com/nodemcu/nodemcu-devkit-v1.0), but the project is now community-supported, and the firmware can now be run on _any_ ESP module. → [Getting Started](getting-started.md) ## Programming Model The NodeMCU programming model is similar to that of [Node.js](https://en.wikipedia.org/wiki/Node.js), only in Lua. It is asynchronous and event-driven. Many functions, therefore, have parameters for callback functions. To give you an idea what a NodeMCU program looks like study the short snippets below. For more extensive examples have a look at the [`/lua_examples`](https://github.com/nodemcu/nodemcu-firmware/tree/release/lua_examples) folder in the repository on GitHub. ```lua -- a simple HTTP server srv = net.createServer(net.TCP) srv:listen(80, function(conn) conn:on("receive", function(sck, payload) print(payload) sck:send("HTTP/1.0 200 OK\r\nContent-Type: text/html\r\n\r\n

Hello, NodeMCU.

") end) conn:on("sent", function(sck) sck:close() end) end) ``` ```lua -- connect to WiFi access point (DO NOT save config to flash) wifi.setmode(wifi.STATION) station_cfg={} station_cfg.ssid = "SSID" station_cfg.pwd = "password" station_cfg.save = false wifi.sta.config(station_cfg) ``` ```lua -- register event callbacks for WiFi events wifi.eventmon.register(wifi.eventmon.STA_CONNECTED, function(T) print("\n\tSTA - CONNECTED".."\n\tSSID: "..T.SSID.."\n\tBSSID: ".. T.BSSID.."\n\tChannel: "..T.channel) end) ``` ```lua -- manipulate hardware like with Arduino pin = 1 gpio.mode(pin, gpio.OUTPUT) gpio.write(pin, gpio.HIGH) print(gpio.read(pin)) ``` → [Getting Started](getting-started.md) ## Lua Flash Store (LFS) In September 2018 support for a [Lua Flash Store (LFS)](lfs.md) was introduced. LFS allows Lua code and its associated constant data to be executed directly out of flash-memory; just as the firmware itself is executed. This now enables NodeMCU developers to create Lua applications with up to 256Kb Lua code and read-only constants executing out of flash. All of the RAM is available for read-write data! ## Releases This project uses two main branches, `release` and `dev`. `dev` is actively worked on and it's also where PRs should be created against. `release` thus can be considered "stable" even though there are no automated regression tests. The goal is to merge back to `release` roughly every 2 months. Depending on the current "heat" (issues, PRs) we accept changes to `dev` for 5-6 weeks and then hold back for 2-3 weeks before the next snap is completed. A new tag is created every time `dev` is merged back to `release` branch. They are listed in the [releases section on GitHub](https://github.com/nodemcu/nodemcu-firmware/releases). Tag names follow the `-release_yyyymmdd` pattern. ## Up-To-Date Documentation At the moment the only up-to-date documentation maintained by the current NodeMCU team is in English. It is part of the source code repository (`/docs` subfolder) and kept in sync with the code. --- ### Getting Started # Getting Started aka NodeMCU Quick Start The basic process to get started with NodeMCU consists of the following three steps. 1. [Build the firmware](build.md) with the modules you need 1. [Flash the firmware](flash.md) to the chip 1. [Upload code](upload.md) to the device. You will typically do steps 1 and 2 only once, but then repeat step 3 as you develop your application. If your application outgrows the limited on-chip RAM then you can use the [Lua Flash Store](lfs.md) (LFS) to move your Lua code into flash memory, freeing a lot more RAM for variable data. This is why it is a good idea to enable LFS for step 1 if you are developing a larger application. As documented below there is a different approach to uploading Lua code. !!! caution For each of the tasks you have a number of choices with regards to tooling and depending on the OS you are on. The colored boxes represent an opinionated path to start your journey - the quickest way to success so to speak. Feel free to follow the links above to get more detailed information. ### Task and OS selector
Task \ OS Windows
macOS
Linux
Build firmware cloud builder cloud builder cloud builder
Docker Docker Docker
native
Flash firmware NodeMCU PyFlasher NodeMCU PyFlasher
esptool.py esptool.py esptool.py
Upload code ESPlorer (Java) ESPlorer (Java) ESPlorer (Java)
NodeMCU-Tool (Node.js) NodeMCU-Tool (Node.js) NodeMCU-Tool (Node.js)
LFS tasks below
Build LFS
enabled firmware
cloud builder cloud builder cloud builder
Docker Docker Docker
native
Build luac.cross not needed if you use Terry's webservice or Docker to later compile LFS image not needed if you use Terry's webservice or Docker to later compile LFS image not needed if you use Terry's webservice or Docker to later compile LFS image
native native native
download from release
Compile Lua into
LFS image
webservice webservice webservice
Docker Docker Docker
native native native
Upload LFS image generic generic generic
**How to read this** Use case: you're just starting with NodeMCU and your OS of choice is Windows (and you are not using LFS), then the blue boxes in the 'Windows' column are your guideline. You: - build the firmware on the cloud builder - download and run the NodeMCU PyFlasher to transfer the firmware to the device - download and run ESPlorer, which requires Java, to transfer Lua files from your system to the device **Missing tools?** Our intention is to introduce you to programming in Lua on the ESP8266 as quickly as possible, so we have kept the number of tools mentioned here to a minimum; [frightanic.com: Tools and IDEs](https://frightanic.com/iot/tools-ides-nodemcu/) discusses other tools and options. !!! caution The below chapters are not meant to be followed one-by-one. Pick a task from the matrix above and it will take you to the relevant chapter. ## Cloud Builder The cloud builder at [https://nodemcu-build.com](https://nodemcu-build.com) allows to pick NodeMCU branch, modules and a few other configuration options (e.g. SSL yes/no). After the build is completed you will receive an email with two links to download your custom firmware: - one for NodeMCU with floating support - one for NodeMCU *without* floating support i.e. an integer-only binary We recommend using the floating point build, even though the integer variant uses less RAM for storing variables, as there is little runtime difference between the two variants. Furthermore, the floating point variant handles non-integer values properly and this greatly simplifies numeric calculations. For everything else the cloud builder GUI is self-explanatory. Hence, no need for further explanations here. ### For LFS 1. Expand the "LFS options" panel 1. Select an LFS size, 64KB is likely going to be large enough 1. Select other options and build [↑ back to matrix](#task-and-os-selector) _Note that this service is not maintained by the NodeMCU team. It's run by a NodeMCU team member as an individual, though._ ## NodeMCU PyFlasher [Self-contained NodeMCU flasher](https://github.com/marcelstoer/nodemcu-pyflasher) with GUI based on Python, esptool.py (see below) and wxPython. A runnable .exe is available for Windows and a .dmg for macOS. **No installation required on Windows and macOS!** Instructions how to run it on other platforms are available on the project site. 1. [Install drivers for USB-to-serial](https://docs.thingpulse.com/how-tos/install-drivers/). Which driver you need depends on the ESP8266 module or USB-to-serial converter you use. 1. Connect USB cable to device and computer. 1. [Download](https://github.com/marcelstoer/nodemcu-pyflasher) then start PyFlasher 1. Select serial port, browse for firmware binary and set the flash options. [↑ back to matrix](#task-and-os-selector) _Note that this tool is not an official NodeMCU offering. It's maintained by a NodeMCU team member as an individual, though._ ## esptool.py [esptool.py](https://github.com/espressif/esptool) was started as a ESP8266 community effort but has since been adopted by Espressif. It's their officially recommended way to flash firmware to ESPxxx chips. 1. [Install drivers for USB-to-serial](https://docs.thingpulse.com/how-tos/install-drivers/). Which driver you need depends on the ESP8266 module or USB-to-serial converter you use. 1. Install [either Python 2.7 or Python >=3.4](https://www.python.org/downloads/) on your system if it's not available yet. 1. Connect USB cable to device and computer. 1. `$ pip install esptool` (also installs pySerial) 1. `$ esptool.py --port --baud write_flash -fm 0x00000 .bin` [`flash-mode`](https://github.com/espressif/esptool/#flash-modes) is `qio` for most ESP8266 ESP-01/07 (512 kByte modules) and `dio` for most ESP32 and ESP8266 ESP-12 (>=4 MByte modules). ESP8285 requires `dout`. The [default baud rate](https://github.com/espressif/esptool#baud-rate) is 115200. Most hardware configurations should work with 230400 dependent on OS, driver, and module. NodeMCU and WeMos modules are usually ok with 921600. More details available on esptool.py GitHub repo. [↑ back to matrix](#task-and-os-selector) ## ESPlorer TBD [https://github.com/4refr0nt/ESPlorer](https://github.com/4refr0nt/ESPlorer) [↑ back to matrix](#task-and-os-selector) ## NodeMCU-Tool Arguably [NodeMCU-Tool](https://github.com/andidittrich/NodeMCU-Tool), which requires Node.js, is the better code upload & execution tool than ESPlorer. Also, in contrast to the former it is very well maintained. However, we also understand that Windows users in general prefer GUI over command line. The [list of features](https://github.com/andidittrich/NodeMCU-Tool#tool-summary) is quite long but essentially NodeMCU-Tool offers: - upload (Lua) files from your host system to the device - manage the device file system (delete, up-/download, etc.) - run files on NodeMCU and display the output over UART/serial Quick start: 1. [Install Node.js and NPM](https://nodejs.org/en/download/) if not available yet 1. Install NodeMCU-Tool globally `$ npm install nodemcu-tool -g` 1. Verify installation by runnin `$ nodemcu-tool --version` 1. Upload a Lua file `$ nodemcu-tool upload --port=/dev/ttyUSB0 helloworld.lua` 1. Run it `$ nodemcu-tool run helloworld.lua` Note that you may need to use the `sudo` prefix to install the tool at step 2, and also possibly add the `–unsafe-perm` flag after the install command. [↑ back to matrix](#task-and-os-selector) ## Docker The [Docker NodeMCU build image](https://github.com/marcelstoer/docker-nodemcu-build) is the easiest method to build NodeMCU related components locally on your preferred platform. Offering: - build NodeMCU firmware based on locally cloned sources and configuration - cross-compile Lua files into LFS image locally Detailed instructions available in the image's README. As for available config options [check the documentation](build.md#build-options) and study the comments in `app/include/user_config.h`. ### For LFS 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) [↑ back to matrix](#task-and-os-selector) _Note that this Docker image is not an official NodeMCU offering. It's maintained by a NodeMCU team member as an individual, though._ ## Build `luac.cross` 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 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 [release ZIP file](https://github.com/nodemcu/nodemcu-firmware/archive/release.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. - Choose a preferred toolchain to build your `luac.cross` executable. You have a number of options here: - 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. - You can also use MS Visual Studio (free community version is available). Just open the supplied MS solution file (msvc\hosttools.sln) and build it to get the Lua 5.1 luac.cross.exe file. Currently there is no sln file available for the Lua 5.3 version. - 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 it out of the nodemcu-firmware hierarchy, since this folder hierarchy is no longer required and can be removed. ### Linux - 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 [release ZIP file](https://github.com/nodemcu/nodemcu-firmware/archive/release.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. ### macOS As for [Linux](#linux) [↑ back to matrix](#task-and-os-selector) ## Compile Lua into LFS image ### Select Lua files to be run from LFS The easiest approach is to maintain all the Lua files for your project in a single directory on your host. (These files will be compiled by `luac.cross` to build the LFS image in next step.) For example to run the Telnet and FTP servers from LFS, put the following files in your project directory: * [lua_examples/lfs/_init.lua](../lua_examples/lfs/_init.lua). LFS helper routines and functions. * [lua_examples/lfs/dummy_strings.lua](../lua_examples/lfs/dummy_strings.lua). Moving common strings into LFS. * [lua_examples/telnet/telnet_fifosock.lua](../lua_examples/telnet/telnet_fifosock.lua). A simple **telnet** server (example 1). * [lua_examples/telnet/telnet_pipe.lua](../lua_examples/telnet/telnet_pipe.lua). A simple **telnet** server (example 2). * [lua_modules/ftp/ftpserver.lua](../lua_modules/ftp/ftpserver.lua). A simple **FTP** server. You should always include the first two modules, but the remaining files would normally be replaced by your own project files. Also remember that these are examples and that you are entirely free to modify or to replace them for your own application needs. !!! Note You will need to grab a luac.cross compiler that matches your configuration regarding float/integer, Lua 5.1/5.3 and possibly the release. ### Terry's LFS Lua Cross-Compile Web Service [https://blog.ellisons.org.uk/article/nodemcu/a-lua-cross-compile-web-service/](https://blog.ellisons.org.uk/article/nodemcu/a-lua-cross-compile-web-service/) Note: read up on [selecting Lua files](#select-lua-files-to-be-run-from-lfs) first Upload a ZIP file with all your Lua files ready for LFS. The webservice will cross-compile them into a `.img` ready to be uploaded to the device. It supports LFS images for both floating point and integer firmware variants. Further details available on the service site. _Note that this service is not maintained by the NodeMCU team. It's run by a NodeMCU team member as an individual, though._ ### Docker Note: read up on [selecting Lua files](#select-lua-files-to-be-run-from-lfs) first The same Docker image you used to build the NodeMCU firmware can be used to [compile Lua files into an LFS image](https://github.com/marcelstoer/docker-nodemcu-build#run-this-image-with-docker-to-create-an-lfs-image). 1. `$ cd ` 1. `$ docker run --rm -ti -v `pwd`:/opt/nodemcu-firmware -v {PathToLuaSourceFolder}:/opt/lua marcelstoer/nodemcu-build lfs-image` ### Native on OS Note: read up on [selecting Lua files](#select-lua-files-to-be-run-from-lfs) first For Windows if you built with WSL / Cygwin you will do this from within the respective command window, both of which use the `bash` shell. If you used Visual Studio just use the windows cmd window. 1. `$ cd ` 1. `$ luac.cross -o lfs.img -f *.lua` You will need to adjust the `img` and `lua` paths according to their location, and ensure that `luac.cross` is in your `$PATH` search list. For example if you are using WSL and your project files are in `D:\myproject` then the Lua path would be `/mnt/d/myproject/*.lua` (For cygwin replace `mnt` by `cygwin`). This will create the `lfs.img` file if there are no Lua compile errors (again specify an explicit directory path if needed). You might also want to add a simple one-line script file to your `~/bin` directory to wrap this command up. [↑ back to matrix](#task-and-os-selector) ## Upload LFS image The compiled LFS image file (e.g. `lfs.img`) is uploaded as a regular file to the device file system (SPIFFS). You do this just like with Lua files with e.g. [ESPlorer](#esplorer) or [NodeMCU-Tool](#nodemcu-tool). There is also a new example, [HTTP_OTA.lua](https://github.com/nodemcu/nodemcu-firmware/tree/dev/lua_examples/lfs/HTTP_OTA.lua), in `lua_examples` that can retrieve images from a standard web service. Once the LFS image file is on SPIFFS, you can execute the [node.flashreload()](modules/node.md#nodeflashreload) command and the loader will then load it into flash and immediately restart the ESP module with the new LFS loaded, if the image file is valid. However, the call will return with an error _if_ the file is found to be invalid, so your reflash code should include logic to handle such an error return. ### Edit your `init.lua` file `init.lua` is the file that is first executed by the NodeMCU firmware. Usually it setups the WiFi connection and executes the main Lua application. Assuming that you have included the `_init` file discussed above, then executing this will add a simple API for LFS module access: - Individual functions can be executed directly, e.g. `LFS.myfunc(a,b)` - LFS is now in the require path, so `require 'myModule'` works as expected. Do a protected call of this `_init` code: `pcall(node.LFS._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 the NodeMCU firmware with LFS enabled. See [the matrix](#task-and-os-selector) section "Build LFS enabled firmware" for how to do that. 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 -o lfs.img -f *lua` on the host machine. Make sure to include a file named `hello_world.lua` with the following one line content: `print("Hello ESP8266 world!")` 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 to integrate the LFS system. 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 exists. This is done with [node.LFS.reload("lfs.img")](modules/node.md#nodelfsreload), which will trigger a reset, followed by [node.LFS._init()](modules/node.md#nodelfsget) to better integrate LFS; logging into the esp8266 and running the following commands gives an overview of the command sequence. ``` > > node.LFS.reload("lfs.img") -- node.LFS.reload() triggers one or two resets here. -- Call the LFS hello_world. > node.LFS.hello_world() Hello ESP8266 world! -- DONE! -- now for some more insights and helpers -- List the modules in the LFS. > print(node.LFS.list) function: 3fff0728 > for k,v in pairs(node.LFS.list()) do print(k,v) end 1 dummy_strings 2 _init 3 hello_world -- integrate LFS with SPIFFS > node.LFS._init() -- We now can run and load files from SPIFFS or LFS using `dofile` and `loadfile`. > dofile("hello_world.lua") Hello ESP8266 world! -- `require()` also works the same way now. -- if there was a file called "hello_world.lua" in SPIFFS the that would be executed. But if there isn't a lookup in LFS is made. -- _init.lua also sets a global LFS as a copy of node.LFS. This is somewhat backwards compatibility and might get removed in the future. > print(LFS) table: 3fff06e0 > ``` 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 simply a matter of eg: `LFS.hello_world()`. [node.LFS.reload()](modules/node.md#nodelfsreload) 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-and-os-selector) --- ### Img/Favicon Readme favicon.ico was generated using https://realfavicongenerator.net. favicon_package_v0.16.zip in this folder contains icons and instructions for all sorts of browsers and platforms (incl. mobile variants). However, without modifying the MkDocs theme/template they’re of no use. --- ### Lua Modules/Bh1750 # BH1750 Module | Since | Origin / Contributor | Maintainer | Source | | :----- | :-------------------- | :---------- | :------ | | 2015-01-30 | [Martin Han](https://github.com/MarsTechHAN) | [Martin Han](https://github.com/MarsTechHAN) | [bh1750.lua](../../lua_modules/bh1750/bh1750.lua) | This Lua module provides access to [BH1750](https://www.mouser.com/ds/2/348/bh1750fvi-e-186247.pdf) I²C ambient light sensor. !!! note This module requires `i2c` C module built into firmware. ### Require ```lua bh1750 = require("bh1750") ``` ### Release ```lua bh1750 = nil package.loaded["bh1750"] = nil ``` ## bh1750.init() Initializes the module and sets up I²C with hardcoded device address. #### Syntax `bh1750.init(sda, scl)` #### Parameters - `sda` SDA pin number. - `scl` SCL pin number. #### Returns `nil` ## bh1750.read() Reads lux value from the sensor. #### Syntax `bh1750.read()` #### Parameters None #### Returns Lux value. ## bh1750.getlux() Function used to return last read lux value. #### Syntax `bh1750.getlux()` #### Parameters None #### Returns Last known lux value. #### Example ```lua SDA_PIN = 6 -- sda pin, GPIO12 SCL_PIN = 5 -- scl pin, GPIO14 bh1750 = require("bh1750") bh1750.init(SDA_PIN, SCL_PIN) bh1750.read() l = bh1750.getlux() print("lux: "..(l / 100).."."..(l % 100).." lx") -- release module bh1750 = nil package.loaded["bh1750"] = nil ``` --- ### Lua Modules/Bme280 # BME280 module | Since | Origin / Contributor | Maintainer | Source | | :----- | :-------------------- | :---------- | :------ | | 2020-10-04 | [vsky279](https://github.com/vsky279) | [vsky279](https://github.com/vsky279) | [bme280.lua](../../lua_modules/bme280/bme280.lua)| This module communicates with [BME280/BMP280 temperature/air presssure/humidity sensors](http://www.bosch-sensortec.com/bst/products/all_products/bme280) (Bosch Sensortec) through [I2C](../modules/i2c.md) interface. !!! note The module works only with the [bme280_math](../modules/bme280_math) module. !!! caution The BMP280 only supports temperature and air pressure measurements. It will give wrong readings for humidity but no warnings or errors. Sometimes sellers of breakout boards for these sensors confuse the two and sell one as the other. To easily check if you have a BMP280 or a BME280 look at the shape of the sensor: The BMP280 has rectangular shape, whereas the BME280 has a square one. ## bme280.setup() Creates bme280sensor object and initializes the module. Initialization is mandatory before reading values. Note that there has to be a delay between some tens to hundreds of milliseconds between calling `setup()` and reading measurements. Functions supported by bme280sensor object: - [setup()](#sobjsetup) - [read()](#sobjread) - [startreadout()](#sobjstartreadout) - [qfe2qnh](#sobjqfe2qnh) - [altitude](#sobjaltitude) - [dewpoint](#sobjdewpoint) #### Syntax `bme280.setup(id, [address, temp_oss, press_oss, humi_oss, power_mode, inactive_duration, IIR_filter])` #### Parameters - `id` - I2C bus number - (optional)`address` - BME280 sensor address. `1` for `BME280_I2C_ADDRESS1 = 0x76`, `2` for `BME280_I2C_ADDRESS2 = 0x77`. Default sensor address is `BME280_I2C_ADDRESS1`. - (optional) `temp_oss` - Controls oversampling of temperature data. Default oversampling is 16x. - (optional) `press_oss` - Controls oversampling of pressure data. Default oversampling is 16x. - (optional) `humi_oss` - Controls oversampling of humidity data. Default oversampling is 16x - (optional) `sensor_mode` - Controls the sensor mode of the device. Default sensor more is normal. - (optional) `inactive_duration` - Controls inactive duration in normal mode. Default inactive duration is 20ms. - (optional) `IIR_filter` - Controls the time constant of the IIR filter. Default filter coefficient is 16. - (optional) `cold_start` - If 0 then the BME280 chip is not initialised. Useful in a battery operated setup when the ESP deep sleeps and on wakeup needs to initialise the driver (the module) but not the chip itself. The chip was kept powered (sleeping too) and is holding the latest reading that should be fetched quickly before another reading starts (`bme280sensor:startreadout()`). By default the chip is initialised. |`temp_oss`, `press_oss`, `humi_oss`|Data oversampling| |-----|-----------------| |0|Skipped (output set to 0x80000)| |1|oversampling ×1| |2|oversampling ×2| |3|oversampling ×4| |4|oversampling ×8| |**5**|**oversampling ×16**| |`sensor_mode`|Sensor mode| |-----|-----------------| |0|Sleep mode| |1 and 2|Forced mode| |**3**|**Normal mode**| Using forced mode is recommended for applications which require low sampling rate or hostbased synchronization. The sensor enters into sleep mode after a forced readout. Please refer to BME280 Final Datasheet for more details. |`inactive_duration`|t standby (ms)| |-----|-----------------| |0|0.5| |1|62.5| |2|125| |3|250| |4|500| |5|1000| |6|10| |**7**|**20**| |`IIR_filter`|Filter coefficient | |-----|-----------------| |0|Filter off| |1|2| |2|4| |3|8| |**4**|**16**| #### Returns `sobj` - BME280 Sensor Object (`nil` if initialization has failed) ## BME280 Sensor Object Methods ### sobj:setup() Re-initializes the sensor. ### Parameters Parameters are the same as for the [bme280.setup](#bme280setup) function. ### Return Returned values are the same as for the [bme280.setup](#bme280setup) function. ### sobj:altitude() For given air pressure (called QFE in aviation - see [wiki QNH article](https://en.wikipedia.org/wiki/QNH)) and sea level air pressure returns the altitude in meters, i.e. altimeter function. #### Syntax `sobj:altitude(P, QNH)` #### Parameters - `P` measured pressure - `QNH` current sea level pressure #### Returns altitude in meters of measurement point ## sobj:dewpoint() For given temperature and relative humidity returns the dew point in celsius. #### Syntax `sobj:dewpoint(H, T)` #### Parameters - `H` relative humidity in percent (100 means 100%) - `T` temperate in celsius #### Returns dew point in celsisus ## sobj:qfe2qnh() For given altitude converts the air pressure to sea level air pressure ([QNH](https://en.wikipedia.org/wiki/QNH)). #### Syntax `sobj:qfe2qnh(P, altitude)` #### Parameters - `P` measured pressure - `altitude` altitude in meters of measurement point #### Returns sea level pressure ## sobj:read() Reads the sensor and returns the temperature, the air pressure, the air relative humidity and see level pressure when `altitude` is specified. #### Syntax `sobj:read([altitude])` #### Parameters - (optional) `altitude`- altitude in meters of measurement point. If provided also the air pressure converted to sea level air pressure is returned. #### Returns - `T` temperature in celsius - `P` air pressure in hectopascals - `H` relative humidity in percent - (optional) `QNH` air pressure in hectopascals (when `altitude` is specified) Returns `nil` if the readout is not successful. ## sobj:startreadout() Starts readout (turns the sensor into forced mode). After the readout the sensor turns to sleep mode. Callback function is called with readout results. #### Syntax `sobj:startreadout(delay, callback)` #### Parameters - `callback` if provided it will be invoked after given `delay`. Callback parameters are identical to `sobj:read` results. - `altitude` in meters of measurement point (QNH is returned when specified) - `delay` sets sensor to forced mode and calls the `callback` (if provided) after given number of milliseconds. For 0 the default delay is set to 113ms (sufficient time to perform reading for oversampling settings 16x). For different oversampling setting please refer to [BME280 Final Datasheet - Appendix B: Measurement time and current calculation](https://ae-bst.resource.bosch.com/media/_tech/media/datasheets/BST-BME280-DS002.pdf#page=51). #### Returns `nil` #### Example ```lua alt=320 -- altitude of the measurement place sda, scl = 3, 4 i2c.setup(0, sda, scl, i2c.SLOW) s = require('bme280').setup(0) tmr.create():alarm(500, tmr.ALARM_AUTO, function() local T, P, H, QNH = s:read(alt) local D = s:dewpoint(H, T) print(("T=%.2f, QFE=%.3f, QNH=%.3f, humidity=%.3f, dewpoint=%.2f"):format(T, P, QNH, H, D)) end) ``` Example with sensor in sleep mode between readouts (asynchronous readouts) ```lua alt=320 -- altitude of the measurement place sda, scl = 3, 4 i2c.setup(0, sda, scl, i2c.SLOW) s = require('bme280').setup(0, nil, nil, nil, nil, 0) -- initialize to sleep mode tmr.create():alarm(1000, tmr.ALARM_AUTO, function() s:startreadout(function(T, P, H, QNH) local D = s:dewpoint(H, T) print(("T=%.2f, QFE=%.3f, QNH=%.3f, humidity=%.3f, dewpoint=%.2f"):format(T, P, QNH, H, D)) end, alt) end) ``` Altimeter function - calculate altitude based on current sea level pressure (QNH) and measure pressure ```lua alt = 0 -- initial altitude sda, scl = 3, 4 i2c.setup(0, sda, scl, i2c.SLOW) s = require('bme280').setup(0) tmr.create():alarm(100, tmr.ALARM_AUTO, function() local _, P, _, lQNH = s:read(alt) if not QNH then QNH = lQNH end local altitude = s:altitude(P, QNH) print(("altitude=%.3f m"):format(altitude)) end) ``` --- ### Lua Modules/Cohelper # 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, ) ``` ### 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, )` #### Parameters - `func`: Lua function to be executed as a coroutine. - ``: 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} ) ``` --- ### Lua Modules/Ds18b20 # DS18B20 Module | Since | Origin / Contributor | Maintainer | Source | | :----- | :-------------------- | :---------- | :------ | | 2014-12-08 | [Huang Rui](https://github.com/vowstar) | [Huang Rui](https://github.com/vowstar) | [ds18b20.lua](../../lua_modules/ds18b20/ds18b20.lua) | This Lua module provides access to [DS18B20](https://datasheets.maximintegrated.com/en/ds/DS18B20.pdf) 1-Wire digital thermometer. For integer version of firmware use [ds18b20-integer.lua](../../lua_modules/ds18b20/ds18b20-integer.lua) module - measured temperatures are multiplied by 10000. The module requires `ow` C module built into firmware. ### Require ```lua ds18b20 = require("ds18b20") ``` ### Release ```lua ds18b20 = nil package.loaded["ds18b20"] = nil ``` ## enable_debug() Enables debug output of the module. #### Parameters None #### Returns `nil` ## ds18b20.read_temp() Scans the bus for DS18B20 sensors (optional), starts a readout (conversion) for all sensors and calls a callback function when all temperatures are available. Powered sensors are read at once first. Parasite-powered sensors are read one by one. The first parasite-powered sensor is read together with all powered sensors. #### Syntax `read_temp(callback, pin, unit, force_search, save_search)` #### Parameters - `callback` function that receives all results when all conversions finish. The callback function has one parameter - an array addressed by sensor addresses and a value of the temperature (string for integer version). - `pin` pin of the one-wire bus. If nil, GPIO0 (3) is used. - `unit` unit can be Celsius ("C" or ds18b20.C), Kelvin ("K" or `ds18b20.K`) or Fahrenheit ("F" or `ds18b20.F`). If not specified (`nil`) latest used unit is used. - `force_search` if not nil a bus search for devices is performed before readout. If nil the existing list of sensors in memory is used. If the bus has not been searched yet the search performed as well. - `save_search` if not nil found sensors are saved to the file `ds18b20_save.lc`. When `read_temp` is called, list of sensors in memory is empty and file `ds18b20_save.lc` is present then sensor addresses are loaded from file - useful when running from batteries & deepsleep - immediate readout is performed (no bus scan). #### Returns `nil` #### Example ```lua local t = require("ds18b20") local pin = 3 -- gpio0 = 3, gpio2 = 4 local function readout(temp) if t.sens then print("Total number of DS18B20 sensors: ".. #t.sens) for i, s in ipairs(t.sens) do print(string.format(" sensor #%d address: %s%s", i, ('%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X'):format(s:byte(1,8)), s:byte(9) == 1 and " (parasite)" or "")) end end for addr, temp in pairs(temp) do print(string.format("Sensor %s: %s °C", ('%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X'):format(addr:byte(1,8)), temp)) end -- Module can be released when it is no longer needed t = nil package.loaded["ds18b20"] = nil end t:read_temp(readout, pin, t.C) ``` ## ds18b20.sens A table with sensors present on the bus. It includes its address (8 bytes) and information whether the sensor is parasite-powered (9-th byte, 0 or 1). ## ds18b20.temp A table with readout values (also passed as a parameter to callback function). It is addressed by sensor addresses. #### Notes Other examples of using this module can be found in [ds18b20-example.lua](../../lua_modules/ds18b20/ds18b20-example.lua) and [ds18b20-web.lua](../../lua_modules/ds18b20/ds18b20-web.lua) files. --- ### Lua Modules/Ds3231 # DS3231 Module | Since | Origin / Contributor | Maintainer | Source | | :----- | :-------------------- | :---------- | :------ | | 2015-01-19 | [Tobie Booth](https://github.com/tobiebooth) | [Tobie Booth](https://github.com/tobiebooth) | [ds3231.lua](../../lua_modules/ds3231/ds3231.lua) | This Lua module provides access to [DS3231](https://datasheets.maximintegrated.com/en/ds/DS3231.pdf) I²C real-time clock. !!! note This module requires `i2c` C module built into firmware. ### Require ```lua ds3231 = require("ds3231") ``` ### Release ```lua ds3231 = nil package.loaded["ds3231"] = nil ``` ## ds3231.setTime() Sets the current date and time. If _disableOscillator_ is set to 1 the oscillator will **stop** on battery. #### Syntax `ds3231.setTime(second, minute, hour, day, date, month, year[, disableOscillator])` #### Parameters - `second`: 00-59 - `minute`: 00-59 - `hour`: 00-23 - `day`: 1-7 (Sunday = 1, Saturday = 7) - `date`: 01-31 - `month`: 01-12 - `year`: 00-99 - `disableOscillator`: (optional) 0-1, defaults to 0 if omitted #### Returns `nil` #### Example ```lua i2c.setup(3, 4, scl, i2c.SLOW) -- call i2c.setup() only once ds3231 = require("ds3231") -- Set date and time to Sunday, January 18th 2015 6:30PM ds3231.setTime(0, 30, 18, 1, 18, 1, 15); -- Don't forget to release it after use ds3231 = nil package.loaded["ds3231"] = nil ``` ## ds3231.getTime() Get the current date and time. #### Syntax `ds3231.getTime()` #### Parameters None #### Returns - `second`: integer. Second 00-59 - `minute`: integer. Minute 00-59 - `hour`: integer. Hour 00-23 - `day`: integer. Day 1-7 (Sunday = 1, Saturday = 7) - `date`: integer. Date 01-31 - `month`: integer. Month 01-12 - `year`: integer. Year 00-99 #### Example ```lua i2c.setup(3, 4, scl, i2c.SLOW) -- call i2c.setup() only once ds3231=require("ds3231") -- Get date and time second, minute, hour, day, date, month, year = ds3231.getTime(); -- Print date and time print(string.format("Time & Date: %s:%s:%s %s/%s/%s", hour, minute, second, date, month, year)) -- Don't forget to release it after use ds3231 = nil package.loaded["ds3231"] = nil ``` ## ds3231setAlarm() Set an alarm to be triggered on SQW pin. _alarm1_ has a precision of **seconds**; _alarm2_ has a precision of **minutes** (`second` parameter will be ignored). Alarms sets gpio.LOW over the SQW pin and let it unchanged until reloaded. When reloaded sets gpio.HIGH. Alarms trigger **only once**, after that, if you want them to trigger again, you need to call `reloadAlarms()` or `setAlarm(...)` again. Alarm type set the alarm match conditions: - `ds3231.EVERYSECOND` works only with _alarm1_ and triggers every second; - `ds3231.EVERYMINUTE` works only with _alarm2_ and triggers every minute (at 00 seconds); - `ds3231.SECOND` triggers when time match given `seconds` parameter; - `ds3231.MINUTE` triggers when time match given `seconds` and `minutes` parameters; - `ds3231.HOUR` triggers when time match given `seconds`, `minutes`, and `hours` parameters; - `ds3231.DAY` triggers when time match given `seconds`, `minutes`, and `hours` on week day `date/day` parameters; - `ds3231.DATE` triggers when time match given `seconds`, `minutes`, and `hours` on date (day of the month) `date/day` parameters; #### Syntax `ds3231.setAlarm(alarmId, alarmType, seconds, minutes, hours, date/day)` #### Parameters - `alarmId`: 1-2 - `alarmType`: 1-7 - `seconds`: 00-59 - `minutes`: 00-59 - `hours`: 00-23 - `date/day`: 01-31 or 1-7 (Sunday = 1, Saturday = 7) #### Returns `nil` #### Example ```lua i2c.setup(3, 4, scl, i2c.SLOW) -- call i2c.setup() only once ds3231=require("ds3231") -- Setting PIN1 to triggers on interrupt when alarm triggers gpio.mode(1,gpio.INT) gpio.trig(1,'down',function(level) print('Time is passing') -- If not reloaded it will be triggered only once ds3231.reloadAlarms() end) ds3231.setAlarm(2,ds3231.EVERYMINUTE) -- Don't forget to release it after use ds3231 = nil package.loaded["ds3231"] = nil ``` ## ds3231.reloadAlarms() Reload an already triggered alarm. Otherwise it will never be triggered again. There are two different alarms and they have to be reloaded both to let, even only one, to be triggered again. So there isn't a param to select which alarm to reload. #### Syntax `ds3231.reloadAlarms()` #### Parameters None #### Returns `nil` #### Example ```lua i2c.setup(3, 4, scl, i2c.SLOW) -- call i2c.setup() only once ds3231=require("ds3231") -- Setting PIN1 to triggers on interrupt when alarm triggers gpio.mode(1,gpio.INT) gpio.trig(1,'down',function(level) print('Time is passing') -- If not reloaded it will be triggered only once ds3231.reloadAlarms() end) ds3231.setAlarm(2,ds3231.EVERYMINUTE) -- Don't forget to release it after use ds3231 = nil package.loaded["ds3231"] = nil ``` ## ds3231.enableAlarm() Enable an already setted alarm with the previous matching conditions. It reloads alarms internally. #### Syntax `ds3231.enableAlarm(alarmId)` #### Parameters `alarmId`: 1-2 #### Returns `nil` #### Example ```lua i2c.setup(3, 4, scl, i2c.SLOW) -- call i2c.setup() only once ds3231=require("ds3231") -- Trigger on x:20:15 ds3231.setAlarm(1,ds3231.MINUTE,15,20) if badThing == 1 then ds3231.disableAlarm(1) end if goodThing == 1 then ds3231.enableAlarm(1) end -- Don't forget to release it after use ds3231 = nil package.loaded["ds3231"] = nil ``` ## ds3231.disableAlarm() Disable an already set alarm with the previous matching conditions. if _alarmId_ is not 1 or 2 it disables both alarms. **Warning**: `disableAlarm()` prevent alarms to trigger interrupt over SQW pin but alarm itself will triggers at the matching conditions as it could be seen on _status byte_. #### Syntax `ds3231.disableAlarm(alarmId)` #### Parameters `alarmId: 0-2` #### Returns `nil` #### Example ```lua i2c.setup(3, 4, scl, i2c.SLOW) -- call i2c.setup() only once ds3231=require("ds3231") -- Trigger on x:20:15 ds3231.setAlarm(1,ds3231.MINUTE,15,20) if badThing == 1 then ds3231.disableAlarm(1) end if goodThing == 1 then ds3231.enableAlarm(1) end -- Don't forget to release it after use ds3231 = nil package.loaded["ds3231"] = nil ``` ## ds3231.getBytes() Get bytes of control, for debug purpose, and status of DS3231. To see what they means check the [datasheet](http://datasheets.maximintegrated.com/en/ds/DS3231.pdf). #### Syntax `ds3231.getBytes()` #### Parameters None #### Returns - `control`: integer. Control 0-255 - `status`: integer. Status 0-143 (bit 6-5-4 unused) #### Example ```lua i2c.setup(3, 4, scl, i2c.SLOW) -- call i2c.setup() only once ds3231=require("ds3231") control,status = ds3231.getBytes() print('Control byte: '..control) print('Status byte: '..status) -- Don't forget to release it after use ds3231 = nil package.loaded["ds3231"] = nil ``` ## da3231.resetStopFlag() Stop flag on status byte means that the oscillator either is stopped or was stopped for some period and may be used to judge the validity of the timekeeping data. When set to 1 this flag keeps that values until changed to 0. Call `resetStopFlag()` if you need to check validity of time data after that. #### Syntax `ds3231.resetStopFlag()` #### Parameters None #### Returns `nil` #### Example ```lua i2c.setup(3, 4, scl, i2c.SLOW) -- call i2c.setup() only once ds3231=require("ds3231") control,status = ds3231.getBytes() if bit.band(bit.rshift(status, 7),1) == 1 then print('[WARNING] RTC has stopped') ds3231.resetStopFlag() end -- Don't forget to release it after use ds3231 = nil package.loaded["ds3231"] = nil ``` #### Notes Other examples of using this module can be found in [ds3231-example.lua](../../lua_modules/ds3231/ds3231-example.lua) and [ds3231-web.lua](../../lua_modules/ds3231/ds3231-web.lua) files. --- ### Lua Modules/Fifo # FIFO Module | Since | Origin / Contributor | Maintainer | Source | | :----- | :-------------------- | :---------- | :------ | | 2019-02-10 | [nwf](https://github.com/nwf) | [nwf](https://github.com/nwf) | [fifo.lua](../../lua_modules/fifo/fifo.lua) | This module provides flexible, generic FIFOs built around Lua tables and callback functions. It is specifically engineered to work well with the NodeMCU event-based and memory-constrained environment. ## Constructor ```lua fifo = (require "fifo").new() ``` ## fifo.dequeue() #### Syntax `fifo:dequeue(k)` Fetch an element from the fifo and pass it to the function `k`, together with a boolean indicating whether this is the last element in the fifo. If the fifo is empty, `k` will not be called and the fifo will enter "immediate dequeue" mode (see below). Assuming `k` is called, ordinarily, `k` will return `nil`, which will cause the element given to `k` to be removed from the fifo and the queue to advance. If, however, `k` returns a non-`nil` value, that value will replace the element at the head of the fifo. This may be useful for generators, for example, which stand in for several elements. When `k` returns `nil`, it may also return a boolean as its second result. If that is `false`, processing ends and `fifo:dequeue` returns. If that is `true`, the fifo will be advanced again (i.e. `fifo:dequeue(k)` will be *tail called*). Elements for which `k` returns `nil, true` are called "phantom", as they cause the fifo to act as though they were not there. Phantom elements are useful for callback-like behavior as the fifo advances: when `k` sees a phantom element, it knows that all prior entries in the fifo have been seen, but the phantom does not necessarily know how to generate the next element of the fifo. #### Returns `true` if the queue contained at least one non-phantom entry, `false` otherwise. ## fifo.queue() #### Syntax `fifo:queue(a,k)` Enqueue the element `a` onto the fifo. If `k` is not `nil` and the fifo is in "immediate dequeue" mode (whence it starts), immediately pass the first element of the fifo (usually, but not necessarily, `a`) to `k`, as if `fifo:dequeue(k)` had been called, and exit "immediate dequeue" mode. ## FIFO Elements The elements stored in the FIFO are simply the integer indices of the fifo table itself, with `1` being the head of the fifo. The depth of the queue for a given `fifo` is just its table size, i.e. `#fifo`. Direct access to the elements is strongly discouraged. The number of elements in the fifo is also unlikely to be of interest; especially, decisions about the fifo's emptiness should instead be rewritten to use the existing interface, if possible, or may peek a bit at the immediate dequeueing state (see below). See the discussion of corking, below, too. ## Immediate Dequeueing The "immediate dequeue" behavior may seem counterintuitive, but it is very useful for the case that `fifo:dequeue`'s `k` arranges for subsequent invocations of `fifo:dequeue`, say by scheduling the next invocation of a timer or by sending on a socket with an `on("sent")` callback wired to `fifo:dequeue`. Because the fifo enters "immediate dequeue" mode only when `dequeue` has been called and the fifo was empty at the time of the call, rather than when the fifo *becomes* empty, `fifo:queue` will sometimes not invoke its `k` even if the queued element `a` ends up at the front of the fifo. This, too, is quite useful: it ensures that `k` will not be called in contexts where it would overlap any ongoing processing of the most-recently dequeued, fifo-emptying element. The immediate deququeing status of the fifo is visible as the `_go` member, which may be read (even if said reads are politely discouraged, but on occasion it is handy to know) but should never be written. ## Corking The fifo has no special support for corking (that is, queueing several elements which are guaranteed to not be dequeued until some later point, called "uncorking"). As one often wants to cork only when the fifo is transitioning out of immediate deququeing mode, the existing machinery is generally good enough to provide an easy emulation thereof. While it is typical to pass the same `k` to both `:queue` and `:dequeue`, there is nothing necessitating this convention. And so one may, as in the `fifosock` module, use the `:queue` `k` to record the transition out of immediate dequeueing mode for later, when one wishes to uncork: ```lua local corked = false fifo:queue(e1, function(e) corked = true ; return e end) -- e1 is now in the fifo, and corked is true if the fifo has exited -- immediate dequeue mode. e1 will be returned back to the fifo and -- so will not be deququed by the function argument. -- We can now queue more elements to the fifo. These will certainly -- queue behind e1. fifo:queue(e2) -- If we should have initiated draining the fifo above, we can do so now, -- instead, having built up a backlog as desired. if corked then fifo:dequeue(k) end ``` --- ### Lua Modules/Fifosock # fifosock Module | Since | Origin / Contributor | Maintainer | Source | | :----- | :-------------------- | :---------- | :------ | | 2019-02-10 | [TerryE](https://github.com/TerryE) | [nwf](https://github.com/nwf) | [fifosock.lua](../../lua_modules/fifo/fifosock.lua) | This module provides a moderately convenient, efficient wrapper around the `net.socket` `send` method. It ensures in-order transmission while striving to minimize memory footprint and packet count by coalescing queued strings. It also serves as a detailed, worked example of the `fifo` module. ## Use ```lua ssend = (require "fifosock").wrap(sock) ssend("hello, ") ssend("world\n") -- when finished ssend = nil sock:on("sent", nil) ``` Once the `sock`et has been wrapped, one should use only the resulting `ssend` function in lieu of `sock:send`, and one should not change the `sock:on("sent")` callback for the duration of the connection. Use of this module creates a circular reference through the Lua registry: the socket points at the fifosock wrapper, which points back at the socket. As such, it is vitally important to break this cycle when the socket has outlived its use. **The usual garbage collection will not be able to reclaim abandoned wrapped sockets**. The user of `fifosock` must, when disposing of the socket, unwire the wrapper, by calling `sock:on("sent", nil)` and should drop all references to `ssend`; a convenient place to do this is in the `sock:on("disconnect")` callback. ## Advanced Use In addition to passing strings representing part of the stream to be sent, it is possible to pass the resulting `ssend` function *functions*. These functions will be given no parameters, but should return two values: - A string to be sent on the socket, or `nil` if no output is desired - A replacement function, or `nil` if the function is to be dequeued. Functions may, of course, offer themselves as their own replacement to stay at the front of the queue. This facility is useful for providing a replacement for the `sock:on("sent")` callback channel. In the fragment below, "All sent" will be `print`ed only when the entirety of "hello, world\n" has been successfully sent on the `sock`et. ```lua ssend("hello, ") ssend("world\n") ssend(function() print("All sent") end) -- implicitly returns nil, nil ``` This facility is also useful for *generators* of the stream, roughly akin to `sendfile`-like primitives in larger systems. Here, for example, we can stream SPIFFS data across the network without ever holding a large amount in RAM. ```lua local function sendfile(fn) local offset = 0 local function send() local f = file.open(fn, "r") if f and f:seek("set", offset) then r = f:read() f:close() if r then offset = offset + #r return r, send end end -- implicitly returns nil, nil and falls out of the stream end return send, function() return offset end end local fn = "test" ssend(("Sending file '%s'...\n"):format(fn)) dosf, getsent = sendfile(fn) ssend(dosf) ssend(("Sent %d bytes from '%s'\n"):format(getsent(), fn)) ``` --- ### Lua Modules/Ftpserver # FTPServer Module | Since | Origin / Contributor | Maintainer | Source | | :----- | :-------------------- | :---------- | :------ | | 2018-07-02 | [Terry Ellison](https://github.com/TerryE) | [Terry Ellison](https://github.com/TerryE) | [ftpserver.lua](../../lua_modules/ftp/ftpserver.lua) | This Lua module implementation provides a basic FTP server for the ESP8266. It has been tested against a number of Table, Windows and Linux FTP clients and browsers. It provides a limited subset of FTP commands that enable such clients to transfer files to and from the ESP's file system. Only one server can be started at any one time, but this server can support multiple connected sessions (some FTP clients use multiple sessions and so require this feature). !!! warning This module is too big to load by standard `require` function or compile on ESP8266 using `node.compile()`. The only option to load and use it is to use [LFS](../lfs.md). ### Limitations - FTP over SSH or TLS is not currently supported so transfer is unencrypted. - The client session , must, authenticate against a single user/password. - Only the SPIFFS filesystem is currently supported, so changing directories is treated as a NO-OP. - This implementation has been optimized for running in LFS. - Only PASV mode is supported as the `net` module does not allow static allocation of outbound sockets. ### Notes The coding style adopted here is more similar to best practice for normal (PC) module implementations, as using LFS permits a bias towards clarity of coding over brevity. It includes extra logic to handle some of the edge case issues more robustly. It also uses a standard forward reference coding pattern to allow the code to be laid out in main routine, subroutine order. Most FTP clients are capable of higher transfer rates than the ESP SPIFFS write throughput, so the server uses TCP flow control to limit upload rates to the ESP. The following FTP commands are supported: - with no parameter: CDUP, NOOP, PASV, PWD, QUIT, SYST - with one parameter: CWD, DELE, MODE, PASS, PORT, RNFR, RNTO, SIZE, TYPE, USER - xfer commands: LIST, NLST, RETR, STOR This implementation is by [Terry Ellison](https://github.com/TerryE), but I wish to acknowledge the inspiration and hard work by [Neronix](https://github.com/NeiroNx) that made this possible. ## createServer() Create the FTP server on the standard ports 20 and 21. The global variable `FTP` is set to the server object. #### Syntax `FTP:createServer(user, pass[, dbgFlag])` #### Parameters - `user`: Username for access to the server - `pass`: Password for access to the server - `dbgFlag`: optional flag. If set true then internal debug output is printed #### Returns `nil` #### Example ```Lua require("ftpserver"):createServer('user', 'password') ``` ## open() Wrapper to createServer() which also connects to the WiFi channel. #### Syntax `FTP:open(user, pass, ssid, wifipwd, dbgFlag)` #### Parameters - `user`: Username for access to the server - `pass`: Password for access to the server - `ssid`: SSID for WiFi service - `wifipwd`: password for WiFi service - `dbgFlag`: optional flag. If set true then internal debug output is printed #### Returns `nil` #### Example ```Lua require("ftpserver"):open('user', 'password', 'myWifi', 'wifiPassword') ``` ## close() Close down server including any sockets and return all resources to Lua. Note that this include removing the FTP global variable and package references. #### Syntax `FTP:close()` #### Parameters None #### Returns `nil` #### Example ```Lua FTP:close() ``` --- ### Lua Modules/Gossip # ESPGossip | Since | Origin / Contributor | Maintainer | Source | | :----- | :-------------------- | :---------- | :------ | | 2020-01-20 | [alexandruantochi](https://github.com/alexandruantochi) | [alexandruantochi](https://github.com/alexandruantochi) | [gossip.lua](../../lua_modules/gossip/gossip.lua) | This module is based on the gossip protocol and it can be used to disseminate information through the network to other nodes. The time it takes for the information to reach all nodes is logN. For every round number n, 2^n nodes will receive the information. ### Require ```lua gossip = require('gossip') ``` ### Release ```lua gossip.inboundSocket:close() gossip = nil ``` ## Usage ```lua config = { seedList = { '192.168.0.1', '192.168.0.15' }, debug = true, debugOutput = print } gossip = require ("gossip") gossip.setConfig(config) gossip.start() ``` ## Strategy Each controller will randomly pick an IP from it's seed list. It will send a `SYN` request to that IP and set receiving node's `state` to an intermediary state between `Up` and `Suspect`. The node that receives the `SYN` request will compute a diff on the received networkState vs own networkState. It will then send that diff as an `ACK` request. If there is no data to send, it will only send an `ACK`. When the `ACK` is received, the sender's state will revert to `Up` and the receiving node will update it's own networkState using the diff (based on the `ACK` reply). Gossip will establish if the information received from another node has fresher data by first comparing the `revision`, then the `heartbeat` and lastly the `state`. States that are closer to `DOWN` have priority as an offline node does not update it's heartbeat. Any other parameter can be sent along with the mandatory `revision`, `heartbeat` and `state` thus allowing the user to spread information around the network. Every time a node receives 'fresh' data, the `gossip.updateCallback` will be called with that data as the first parameter. Currently there is no implemented deletion for nodes that are down except for the fact that their status is signaled as `REMOVE`. ## Example use-case There are multiple modules on the network that measure temperature. We want to know the maximum and minimum temperature at a given time and have every node display it. The brute force solution would be to query each node from a single point and save the `min` and `max` values, then go back to each node and present them with the computed `min` and `max`. This requires n*2 rounds, where n is the number of nodes. It also opens the algorithm to a single point of failure (the node that is in charge of gathering the data). Using gossip, one can have the node send it's latest value through `SYN` or `pushGossip()` and use the `callbackUpdate` function to compare the values from other nodes to it's own. Based on that, the node will display the values it knows about by gossiping with others. The data will be transmitted in ~log(n) rounds, where n is the number of nodes. ## Terms `revision` : generation of the node; if a node restarts, the revision will be increased by one. The revision data is stored as a file to provide persistency `heartBeat` : the node uptime in seconds (`tmr.time()`). This is used to help the other nodes figure out if the information about that particular node is newer. `networkState` : the list with the state of the network composed of the `ip` as a key and `revision`, `heartBeat` and `state` as values packed in a table. `state` : all nodes start with a state set to `UP` and when a node sends a `SYN` request, it will mark the destination node in an intermediary state until it receives an `ACK` or a `SYN` from it. If a node receives any message, it will mark that senders IP as `UP` as this provides proof that the node is online. ## setConfig() #### Syntax ```lua gossip.setConfig(config) ``` Sets the configuration for gossip. The available options are: `seedList` : the list of seeds gossip will start with; this will be updated as new nodes are discovered. Note that it's enough for all nodes to start with the same IP in the seedList, as once they have one seed in common, the data will propagate. If the seedList is empty a broadcast is sent, so this can be used for automatic discovery of nodes. `roundInterval`: interval in milliseconds at which gossip will pick a random node from the seed list and send a `SYN` request `comPort` : port for the listening UDP socket `debug` : flag that will provide debugging messages `debugOutput` : if debug is set to `true`, then this method will be used as a callback with the debug message as the first parameter ```lua config = { seedList = {'192.168.0.54','192.168.0.55'}, roundInterval = 10000, comPort = 5000, debug = true, debugOutput = function(message) print('Gossip says: '..message); end } ``` If any of them is not provided, the values will default: `seedList` : nil `roundInterval`: 10000 (10 seconds) `comPort` : 5000 `debug` : false `debugOutput` : print ## start() #### Syntax ```lua gossip.start() ``` Starts gossip, sets the `started` flag to true and initiates the `revision`. The revision (generation) main purpose is like a persistent heartbeat, as the heartbeat (measured by uptime in seconds) will obviously revert to 0. ## callbackFunction #### Syntax ```lua gossip.callbackFunction = function(data) processData(data) end -- stop the callback gossip.callbackFunction = nil ``` If declared, this function will get called every time there is a `SYN` with new data. ## pushGossip() #### Syntax ```lua gossip.pushGossip(data, [ip]) -- remove data gossip.pushGossip(nil, [ip]) ``` Send a `SYN` request outside of the normal gossip round. The IP is optional and if none given, it will pick a random node. ``` !!! note . By calling `pushGossip(nil)` you effectively remove the `data` table from the node's network state and notify other nodes of this. ``` ## setRevManually() #### Syntax ```lua gossip.setRevFileValue(number) ``` The only scenario when rev should be set manually is when a new node is added to the network and has the same IP. Having a smaller revision than the previous node with the same IP would make gossip think the data it received is old, thus ignoring it. ``` !!! note The revision file value will only be read when gossip starts and it will be incremented by one. ``` ## getNetworkState() #### Syntax ```lua networkState = gossip.getNetworkState() print(networkState) ``` The network state can be directly accessed as a Lua table : `gossip.networkState` or it can be received as a JSON with this method. #### Returns JSON formatted string regarding the network state. Example: ```JSON { "192.168.0.53": { "state": 3, "revision": 25, "heartbeat": 2500, "extra" : "this is some extra info from node 53" }, "192.168.0.75": { "state": 0, "revision": 4, "heartbeat": 6500 } } ``` --- ### Lua Modules/Hdc1000 # HDC1000 Module | Since | Origin / Contributor | Maintainer | Source | | :----- | :-------------------- | :---------- | :------ | | 2015-03-07 | [Francesco Truzzi](https://github.com/ftruzzi) | [Francesco Truzzi](https://github.com/ftruzzi) | [hdc1000.lua](../../lua_modules/hdc1000/HDC1000.lua) | This Lua module provides access to [HDC1000](https://www.ti.com/lit/ds/symlink/hdc1000.pdf) I²C digital humidity and temperature sensor. It should also work with HDC1008 sensor bout this haven't been tested. !!! note This module requires `i2c` C module built into firmware. ### Require ```lua HDC1000 = require("HDC1000") ``` ### Release ```lua HDC1000 = nil package.loaded["HDC1000"] = nil ``` ## HDC1000.setup() Function to setup the HDC1000 sensor. #### Syntax `HDC1000.setup(drdyn)` #### Parameters - `drdyn`: DRDYn pin number. If set to `false`, this feature will not be used and after each read request a 20ms delay will be added. #### Returns `nil` #### Example ```lua local sda, scl = 3, 4 -- Pins 3 and 4 will be used local drdyn = 6 -- Pin 6 will be used to connect with DRDYn pin i2c.setup(0, sda, scl, i2c.SLOW) -- call i2c.setup() only once HDC1000.setup(drdyn) ``` ## HDC1000.config() Function to configure various options of HDC1000 sensor. #### Syntax `HDC1000.config(address, resolution, heater)` #### Parameters - `address`: I²C sensor address. Default value is `0x40`. - `resolution`: Temperature and humidity sensor resolution. Can be set to 14 bits for both temperature and humidity (`0x00`), 11 bits for temperature (`0x40`), 11 bits for humidity (`0x01`) or 8 bits for humidity (`0x20`). Default value is `0x00`. - `heater`: Heater setting. `0x20` to enable and `0x00` to disable. Default value is `0x20`. #### Returns `nil` ## HDC1000.getTemp() Reads the temperature from HDC1000 sensor. #### Syntax `HDC1000.getTemp()` #### Parameters None #### Returns Temperature in Celsius degrees. ## HDC1000.getHumi() Reads the humidity value from HDC1000 sensor. #### Syntax `HDC1000.getHumi()` #### Parameters None #### Returns Humidity in percents. ## HDC1000.batteryDead() Function that checks if voltage of sensor power supply is bellow or above 2.8V. #### Syntax `HDC1000.batteryDead()` #### Parameters None #### Returns `true` if battery voltage is bellow 2.8V, `false` otherwise. #### Example ```lua HDC1000 = require("HDC1000") sda = 1 scl = 2 drdyn = false i2c.setup(0, sda, scl, i2c.SLOW) -- call i2c.setup() only once HDC1000.setup(drdyn) HDC1000.config() -- default values are used if called with no arguments. prototype is config(address, resolution, heater) print(string.format("Temperature: %.2f °C\nHumidity: %.2f %%", HDC1000.getTemp(), HDC1000.getHumi())) HDC1000 = nil package.loaded["HDC1000"] = nil ``` --- ### Lua Modules/Httpserver # HTTP Server Module | Since | Origin / Contributor | Maintainer | Source | | :----- | :-------------------- | :---------- | :------ | | 2015-01-19 | [Vladimir Dronnikov](https://github.com/dvv) | [Vladimir Dronnikov](https://github.com/dvv) | [http.lua](../../lua_modules/http/httpserver.lua) | This Lua module provides a simple callback implementation of a [HTTP 1.1](https://www.w3.org/Protocols/rfc2616/rfc2616.html) server. ### Require ```lua httpserver = require("httpserver") ``` ### Release ```lua httpserver = nil package.loaded["httpserver"] = nil ``` ## httpserver.createServer() Function to start HTTP server. #### Syntax `httpserver.createServer(port, handler(req, res))` #### Parameters - `port`: Port number for HTTP server. Most HTTP servers listen at port 80. - `handler`: callback function for when HTTP request was made. #### Returns `net.server` sub module. #### Notes Callback function has 2 arguments: `req` (request) and `res` (response). The first object holds values: - `conn`: `net.socket` sub module. **DO NOT** call `:on` or `:send` on this object. - `method`: Request method that was used (e.g.`POST` or `GET`) - `url`: Requested URL - `onheader`: assign a function to this value which will be called as soon as HTTP headers like `content-type` are available. This handler function has 3 parameters: - `self`: `req` object - `name`: Header name. Will allways be lowercase. - `value`: Header value - `ondata`: assign a function to this value which will be called as soon as body data is available. This handler function has 2 parameters: - `self`: `req` object - `chunk`: Request data. If all data is received there will be one last call with data = nil The second object holds functions: - `send(self, data, [response_code])`: Function to send data to client. - `self`: `res` object - `data`: data to send (may be nil) - `response_code`: the HTTP response code like `200`(default) or `404` (for example) *NOTE* if there are several calls with response_code given only the first one will be used. Any further codes given will be ignored. - `send_header(self, header_name, header_data)`: Function to send HTTP headers to client. This function will not be available after data has been sent. (It will be nil.) - `self`: `res` object - `header_name`: the HTTP header name - `header_data`: the HTTP header data - `finish([data[, response_code]])`: Function to finalize connection, optionally sending data and return code. - `data`: optional data to send on connection finalizing - `response_code`: the HTTP response code like `200`(default) or `404` (for example) *NOTE* if there are several calls with response_code given only the first one will be used. Any further codes given will be ignored. Full example can be found in [http-example.lua](../../lua_modules/http/http-example.lua) --- ### Lua Modules/Imap # IMAP Module | Since | Origin / Contributor | Maintainer | Source | | :----- | :-------------------- | :---------- | :------ | | 2015-03-12 | [AllAboutEE](https://github.com/AllAboutEE) | [AllAboutEE](https://github.com/AllAboutEE) | [imap.lua](../../lua_modules/email/imap.lua) | This Lua module provides a simple implementation of an [IMAP 4rev1](http://www.faqs.org/rfcs/rfc2060.html) protocol that can be used to read e-mails. ### Require ```lua imap = require("imap.lua") ``` ### Release ```lua imap = nil package.loaded["imap"] = nil ``` ## imap.response_processed() Function used to check if IMAP command was processed. #### Syntax `imap.response_processed()` #### Parameters None #### Returns The response process status of the last IMAP command sent. If return value is `true` it means the command was processed. ## imap.config() Initiates the IMAP settings. #### Syntax `imap.config(username, password, tag, [debug])` #### Parameters - `username`: IMAP username. For most e-mail providers e-mail address is used as username. - `password`: IMAP password. - `tag`: IMAP tag. With current implementation any tag like "t1" should work. - `debug`: (boolean) if set to true entire conversation between the ESP8266 and IMAP server will be shown. Default setting is false. #### Returns `nil` ## imap.login() Logs into a new email session. #### Syntax `imap.login(socket)` #### Parameters - `socket`: IMAP TCP socket object created using `net.createConnection` #### Returns `nil` ## imap.get_most_recent_num() Function to check the most recent email number. Should only be called after `examine` function. #### Syntax `imap.get_most_recent_num()` #### Parameters None #### Returns The most recent email number. ## imap.examine() IMAP examines the given mailbox/folder. Sends the IMAP EXAMINE command. #### Syntax `imap.examine(socket, mailbox)` #### Parameters - `socket`: IMAP TCP socket object created using `net.createConnection` - `mailbox`: E-mail folder name to examine like example `"INBOX"` #### Returns `nil` ## imap.get_header() Function that gets the last fetched header field. #### Syntax `imap.get_header()` #### Parameters None #### Returns The last fetched header field. ## imap.fetch_header() Fetches an e-mails header field e.g. SUBJECT, FROM, DATE. #### Syntax `imap.fetch_header(socket, msg_number, field)` #### Parameters - `socket`: IMAP TCP socket object created using `net.createConnection` - `msg_number`: The email number to read e.g. 1 will read fetch the latest/newest email - `field`: A header field such as SUBJECT, FROM, or DATE #### Returns `nil` ## imap.get_body() Function to get the last email read's body. #### Syntax `imap.get_body()` #### Parameters None #### Returns The last email read's body. ## imap.fetch_body_plain_text() Sends the IMAP command to fetch a plain text version of the email's body. #### Syntax `imap.fetch_body_plain_text(socket, msg_number)` #### Parameters - `socket`: IMAP TCP socket object created using `net.createConnection` - `msg_number`: The email number to obtain e.g. 1 will obtain the latest email. #### Returns `nil` ## imap.logout() Sends the IMAP command to logout of the email session. #### Syntax `imap.logout(socket)` #### Parameters - `socket`: IMAP TCP socket object created using `net.createConnection` #### Returns `nil` #### Example Example use of `imap` module can be found in [read_email_imap.lua](../../lua_examples/email/read_email_imap.lua) file. --- ### Lua Modules/Liquidcrystal # LiquidCrystal Module | Since | Origin / Contributor | Maintainer | Source | | :----- | :-------------------- | :---------- | :------ | | 2019-12-01 | [Matsievskiy Sergey](https://github.com/seregaxvm) | [Matsievskiy Sergey](https://github.com/seregaxvm) | [liquidcrystal.lua](../../lua_modules/liquidcrystal/liquidcrystal.lua) [i2c4bit.lua](../../lua_modules/liquidcrystal/lc-i2c4bit.lua) [gpio4bit.lua](../../lua_modules/liquidcrystal/lc-gpio4bit.lua) [gpio8bit.lua](../../lua_modules/liquidcrystal/lc-gpio8bit.lua) | This Lua module provides access to [Hitachi HD44780](https://www.sparkfun.com/datasheets/LCD/HD44780.pdf) based LCDs. It supports 4 bit and 8 bit GPIO interface, 4 bit [PCF8574](https://www.nxp.com/docs/en/data-sheet/PCF8574_PCF8574A.pdf) based I²C interface. !!! note This module requires `bit` C module built into firmware. Depending on the interface, `gpio` or `i2c` module is also required. ## Program example In this example LED screen is connected using I²C GPIO expander. Program defines five custom characters and prints text. ```lua backend_meta = require "lc-i2c4bit" lc_meta = require "liquidcrystal" -- create display object lc = lc_meta(backend_meta{sda=1, scl=2}, false, true, 20) backend_meta = nil lc_meta = nil -- define custom characters lc:customChar(0, {0,14,31,31,4,4,5,2}) lc:customChar(1, {4,6,5,5,4,12,28,8}) lc:customChar(2, {14,31,17,17,17,17,17,31}) lc:customChar(3, {14,31,17,17,17,17,31,31}) lc:customChar(4, {14,31,17,17,31,31,31,31}) lc:customChar(5, {14,31,31,31,31,31,31,31}) lc:clear() -- clear display lc:blink(true) -- enable cursor blinking lc:home() -- reset cursor position lc:write("hello", " ", "world") -- write string lc:cursorMove(1, 2) -- move cursor to second line lc:write("umbrella", 0, 32, "note", 1) -- mix text strings and characters lc:cursorMove(1, 3) lc:write("Battery level ", 2, 3, 4, 5) lc:home() lc:blink(false) for i=1,20 do print(lc:read()) end -- read back first line lc:home() for _, d in ipairs(lc:readCustom(0)) do print(d) end -- read back umbrella char for _, d in ipairs(lc:readCustom(1)) do print(d) end -- read back note char ``` ### Require ```lua i2c4bit_meta = require("lc-i2c4bit") gpio4bit_meta = require("lc-gpio4bit") gpio8bit_meta = require("lc-gpio8bit") lc_meta = require("liquidcrystal") ``` ### Release ```lua package.loaded["lc-i2c4bit"] = nil package.loaded["lc-gpio4bit"] = nil package.loaded["lc-gpio8bit"] = nil package.loaded["liquidcrystal"] = nil ``` ## Initialization Liquidcrystal module is initialized using closure, which takes backend object as an argument. ### I²C backend Loading I²C backend module returns initialization closure. It configures I²C backend and returns backend object. #### Syntax `function({[sda=sda_pin] [, scl=scl_pin] [, busid=id] [, busad=address] [, speed = spd] [, rs = rs_pos] [, rw = rw_pos] [, en = en_pos] [, bl = bl_pos] [, d4 = d4_pos] [, d5 = d5_pos] [, d6 = d6_pos] [, d7 = d7_pos]})` !!! note In most cases only `sda` and `scl` parameters are required #### Parameters - `sda`: I²C data pin. If set to `nil`, I²C bus initialization step via [`i2c.setup`](https://nodemcu.readthedocs.io/en/release/modules/i2c/#i2csetup) will be skipped - `scl`: I²C clock pin. If set to `nil`, I²C bus initialization step via [`i2c.setup`](https://nodemcu.readthedocs.io/en/release/modules/i2c/#i2csetup) will be skipped - `busid`: I²C bus ID. Defaults to `0` - `busad`: chip I²C address. Defaults to `0x27` (default PCF8574 address) - `speed`: I²C speed. Defaults to `i2c.SLOW` - `rs`: bit position assigned to `RS` pin in I²C word. Defaults to 0 - `rw`: bit position assigned to `RW` pin in I²C word. Defaults to 1 - `en`: bit position assigned to `EN` pin in I²C word. Defaults to 2 - `bl`: bit position assigned to backlight pin in I²C word. Defaults to 3 - `d4`: bit position assigned to `D4` pin in I²C word. Defaults to 4 - `d5`: bit position assigned to `D5` pin in I²C word. Defaults to 5 - `d6`: bit position assigned to `D6` pin in I²C word. Defaults to 6 - `d7`: bit position assigned to `D7` pin in I²C word. Defaults to 7 #### Returns - backend object #### Example ```lua backend_meta = require "lc-i2c4bit" backend = backend_meta{sda=1, scl=2 ,speed=i2c.FAST} ``` ### GPIO 4 bit backend Loading GPIO 4 bit backend module returns initialization closure. It configures GPIO 4 bit backend and returns backend object. #### Syntax `function({[, rs = rs_pos] [, rw = rw_pos] [, en = en_pos] [, bl = bl_pos] [, d4 = d4_pos] [, d5 = d5_pos] [, d6 = d6_pos] [, d7 = d7_pos]})` #### Parameters - `rs`: GPIO pin connected to `RS` pin. Defaults to 0 - `rw`: GPIO pin connected to `RW` pin. If set to `nil` then `busy`, `position` and `readChar` functions will not be available. Note that `RW` pin must be pulled to the ground if not connected to GPIO - `en`: GPIO pin connected to `EN` pin. Defaults to 1 - `bl`: GPIO pin controlling backlight. It is assumed, that high level turns backlight on, low level turns backlight off. If set to `nil` then backlight function will not be available - `d4`: GPIO pin connected to `D4` pin. Defaults to 2 - `d5`: GPIO pin connected to `D5` pin. Defaults to 3 - `d6`: GPIO pin connected to `D6` pin. Defaults to 4 - `d7`: GPIO pin connected to `D7` pin. Defaults to 5 #### Returns - backend object #### Example ```lua backend_meta = require "lc-gpio4bit" backend = backend_meta{rs=0, rw=1, en=4, d4=5, d5=6, d6=7, d7=8} ``` ### GPIO 8 bit backend Loading GPIO 8 bit backend module returns initialization closure. It configures GPIO 8 bit backend and returns backend object. #### Syntax `function({[, rs = rs_pos] [, rw = rw_pos] [, en = en_pos] [, bl = bl_pos] [, d0 = d0_pos] [, d1 = d1_pos] [, d2 = d2_pos] [, d3 = d3_pos] [, d4 = d4_pos] [, d5 = d5_pos] [, d6 = d6_pos] [, d7 = d7_pos]})` #### Parameters - `rs`: GPIO pin connected to `RS` pin. Defaults to 0 - `rw`: GPIO pin connected to `RW` pin. If set to `nil` then `busy`, `position` and `readChar` functions will not be available. Note that `RW` pin must be pulled to the ground if not connected to GPIO - `en`: GPIO pin connected to `EN` pin. Defaults to 1 - `bl`: GPIO pin controlling backlight. It is assumed, that high level turns backlight on, low level turns backlight off. If set to `nil` then backlight function will not be available - `d0`: GPIO pin connected to `D0` pin. Defaults to 2 - `d1`: GPIO pin connected to `D1` pin. Defaults to 3 - `d2`: GPIO pin connected to `D2` pin. Defaults to 4 - `d3`: GPIO pin connected to `D3` pin. Defaults to 5 - `d4`: GPIO pin connected to `D4` pin. Defaults to 6 - `d5`: GPIO pin connected to `D5` pin. Defaults to 7 - `d6`: GPIO pin connected to `D6` pin. Defaults to 8 - `d7`: GPIO pin connected to `D7` pin. Defaults to 9 #### Returns - backend object #### Example ```lua backend_meta = require "lc-gpio8bit" backend = backend_meta{rs=15, rw=2, en=5, d0=23, d1=13, d2=33, d3=32, d4=18, d5=19, d6=21, d7=22} ``` ### Liquidcrystal initialization Loading Liquidcrystal module returns initialization closure. It requires backend object and returns LCD object. #### Syntax `function(backend, onelinemode, eightdotsmode, column_width)` #### Parameters - `backend`: backend object - `onelinemode`: `true` to use one line mode, `false` to use two line mode - `eightdotsmode`: `true` to use 5x8 dot font, `false` to use 5x10 dot font - `column_width`: number of characters in column. Used for offset calculations in function `cursorMove`. If set to `nil`, functionality of `cursorMove` will be limited. For most displays column width is `20` characters #### Returns screen object #### Example ```lua lc_meta = require "liquidcrystal" lc = lc_meta(backend, true, true, 20) ``` ## liquidcrystal.autoscroll Autoscroll text when printing. When turned off, cursor moves and text stays still, when turned on, vice versa. #### Syntax `liquidcrystal.autoscroll(self, on)` #### Parameters - `self`: `liquidcrystal` instance - `on`: `true` to turn on, `false` to turn off #### Returns - sent data #### Example ```lua liquidcrystal:autoscroll(true) ``` ## liquidcrystal.backlight Control LCDs backlight. When using GPIO backend without `bl` argument specification function does nothing. #### Syntax `liquidcrystal.backlight(self, on)` #### Parameters - `self`: `liquidcrystal` instance - `on`: `true` to turn on, `false` to turn off #### Returns - backlight status #### Example ```lua liquidcrystal:backlight(true) ``` ## liquidcrystal.blink Control cursors blink mode. #### Syntax `liquidcrystal.blink(self, on)` #### Parameters - `self`: `liquidcrystal` instance - `on`: `true` to turn on, `false` to turn off #### Returns - sent data #### Example ```lua liquidcrystal:blink(true) ``` ## liquidcrystal.busy Get busy status of the LCD. When using GPIO backend without `rw` argument specification function does nothing. !!! note At least some HD44780s and/or interfaces have been observed to count polling the busy flag as grounds for incrementing their position in memory. This is mysterious, but software should restore the position after observing that the busy flag is clear. #### Syntax `liquidcrystal.busy(self)` #### Parameters - `self`: `liquidcrystal` instance #### Returns - `true` if device is busy, `false` if device is ready to receive commands #### Example ```lua while liquidcrystal:busy() do end ``` ## liquidcrystal.clear Clear LCD screen. #### Syntax `liquidcrystal.clear(self)` #### Parameters - `self`: `liquidcrystal` instance #### Returns - sent data #### Example ```lua liquidcrystal:clear() ``` ## liquidcrystal.cursorLeft Move cursor one character to the left. #### Syntax `liquidcrystal.cursorLeft(self)` #### Parameters - `self`: `liquidcrystal` instance #### Returns - sent data #### Example ```lua liquidcrystal:cursorLeft() ``` ## liquidcrystal.cursorMove Move cursor to position. If `row` not specified, move cursor to address `col`. !!! note Note that column and row indexes start with 1. However, when omitting `row` parameter, cursor addresses start with 0. #### Syntax `liquidcrystal.cursorMove(self, col, row)` #### Parameters - `self`: `liquidcrystal` instance - `col`: new cursor position column. If `row` not specified, new cursor position address - `row`: new cursor position row or `nil` #### Returns - sent data #### Example ```lua liquidcrystal:cursorMove(5, 1) liquidcrystal:cursorMove(10, 4) liquidcrystal:cursorMove(21) ``` ## liquidcrystal.cursor Control cursors highlight mode. #### Syntax `liquidcrystal.cursor(self, on)` #### Parameters - `self`: `liquidcrystal` instance - `on`: `true` to turn on, `false` to turn off #### Returns - sent data #### Example ```lua liquidcrystal:cursor(true) ``` ## liquidcrystal.cursorRight Move cursor one character to the right. #### Syntax `liquidcrystal.cursorRight(self)` #### Parameters - `self`: `liquidcrystal` instance #### Returns - sent data #### Example ```lua liquidcrystal:cursorRight() ``` ## liquidcrystal.customChar Define new custom char. Up to 8 custom characters with indexes 0 to 7 may be defined in eight dot mode. They are accessed via `write` function by index. In ten dot mode only 4 custom characters may be used. They are numbered from 0 to 7 with half of them being aliases to each other (0 to 1, 2 to 3 etc). !!! note Upon redefinition of a custom character all its instances will be updated automatically. This function resets cursor position to home if `liquidcrystal.position` function is not available. There are web services ([1](https://omerk.github.io/lcdchargen/), [2](https://www.quinapalus.com/hd44780udg.html)) and [desktop applications](https://pypi.org/project/lcdchargen/) that help create custom characters. #### Syntax `liquidcrystal.customChar(self, index, bytes)` #### Parameters - `self`: `liquidcrystal` instance - `index`: custom char index in range from 0 to 7 - `bytes`: array of 8 bytes in eight bit mode or 11 bytes in ten bit mode (eleventh line is a cursor line that can also be used) that defines new char bitmap line by line #### Returns `nil` #### Example ```lua liquidcrystal:customChar(5, {14,31,31,31,31,31,31,31}) liquidcrystal:write(5) ``` ## liquidcrystal.display Turn display on and off. Does not affect display backlight. Does not clear the display. #### Syntax `liquidcrystal.display(self, on)` #### Parameters - `self`: `liquidcrystal` instance - `on`: `true` to turn on, `false` to turn off #### Returns - sent data #### Example ```lua liquidcrystal:display(true) ``` ## liquidcrystal.home Reset cursor and screen position. #### Syntax `liquidcrystal.home(self)` #### Parameters - `self`: `liquidcrystal` instance #### Returns - sent data #### Example ```lua liquidcrystal:home() ``` ## liquidcrystal.leftToRight Print text left to right (default). #### Syntax `liquidcrystal.leftToRight(self)` #### Parameters - `self`: `liquidcrystal` instance #### Returns - sent data #### Example ```lua liquidcrystal:leftToRight() ``` ## liquidcrystal.position Get current position of the cursor. Position is 0 indexed. When using GPIO backend without `rw` argument specification function does nothing. !!! note At least some HD44780s and/or interfaces have been observed to count reading the position as grounds for incrementing their position in memory. This is mysterious, but software likely intends to restore the position anyway. #### Syntax `liquidcrystal.position(self)` #### Parameters - `self`: `liquidcrystal` instance #### Returns - 0 indexed position of the cursor #### Example ```lua local pos = liquidcrystal:position() -- save position -- some code liquidcrystal:cursorMove(pos) -- restore position ``` ## liquidcrystal.read Return current character numerical representation. When using GPIO backend without `rw` argument specification function does nothing. #### Syntax `liquidcrystal.read(self)` #### Parameters - `self`: `liquidcrystal` instance #### Returns - numerical representation of the current character #### Example ```lua liquidcrystal:home() -- goto home local ch = liquidcrystal:read() -- read char liquidcrystal:cursorMove(1, 2) -- move to the second line for i=ch,ch+5 do lc:write(i) end -- print 6 chars starting with ch ``` ## liquidcrystal.readCustom Return custom char byte array. When using GPIO backend without `rw` argument specification function returns zeros. #### Syntax `liquidcrystal.readCustom(self, index)` #### Parameters - `self`: `liquidcrystal` instance - `index`: custom char index in range from 0 to 7 #### Returns - table of size 8 in eight dot mode or 11 in ten dot mode. Each 8 bit number represents a character dot line #### Example ```lua lc:customChar(0, {0,14,31,31,4,4,5,2}) -- define custom character for _, d in ipairs(lc:readCustom(0)) do print(d) end -- read it back ``` ## liquidcrystal.rightToLeft Print text right to left. #### Syntax `liquidcrystal.rightToLeft(self)` #### Parameters - `self`: `liquidcrystal` instance #### Returns - sent data #### Example ```lua liquidcrystal:rightToLeft() ``` ## liquidcrystal.scrollLeft Move text to the left. #### Syntax `liquidcrystal.scrollLeft(self)` #### Parameters - `self`: `liquidcrystal` instance #### Returns - sent data #### Example ```lua liquidcrystal:scrollLeft() ``` ## liquidcrystal.scrollRight Move text to the right. #### Syntax `liquidcrystal.scrollRight(self)` #### Parameters - `self`: `liquidcrystal` instance #### Returns - sent data #### Example ```lua liquidcrystal:scrollRight() ``` ## liquidcrystal.write Print text. #### Syntax `liquidcrystal.write(self, ...)` #### Parameters - `self`: `liquidcrystal` instance - `...`: strings or char codes. For the list of available characters refer to [HD44780 datasheet](https://www.sparkfun.com/datasheets/LCD/HD44780.pdf#page=17) #### Returns `nil` #### Example ```lua liquidcrystal:write("hello world") liquidcrystal:write("hello yourself", "!!!", 243, 244) ``` --- ### Lua Modules/Lm92 # LM92 Module | Since | Origin / Contributor | Maintainer | Source | | :----- | :-------------------- | :---------- | :------ | | 2015-05-17 | [Levente Tamas](https://github.com/elgarbe) | [Levente Tamas](https://github.com/elgarbe) | [lm92.lua](../../lua_modules/lm92/lm92.lua) | This Lua module provides access to [LM92](http://www.ti.com/lit/ds/symlink/lm92.pdf) I²C ±0.33C 12bit+sign temperature sensor. !!! note This module requires `i2c` C module built into firmware. ### Require ```lua lm92 = require("lm92") ``` ### Release ```lua lm92 = nil package.loaded["lm92"] = nil ``` ## lm92.setup() Function used to setup the address for lm92. #### Syntax `lm92.setup(address)` #### Parameters - `address`: I²C address used by LM92. Depends on the connection of `A0` and `A1` pins. Can be either `0x48`, `0x49`, `0x4a` or `0x4b` according to page 9 of [LM92 datasheet](http://www.ti.com/lit/ds/symlink/lm92.pdf) #### Returns `nil` #### Example ```lua lm92 = require("lm92") sda = 3 -- GPIO 0 scl = 4 -- GPIO 2 addr = 0x48 i2c.setup(0, sda, scl, i2c.SLOW) -- call i2c.setup() only once lm92.setup(addr) ``` ## lm92.getTemperature() Returns the temperature register's content. #### Syntax `lm92.getTemperature()` #### Parameters None #### Returns Temperature in degree Celsius. ## lm92.shutdown() Makes the chip enter the low power shutdown mode. #### Syntax `lm92.shutdown()` #### Parameters None #### Returns `nil` ## lm92.wakeup() Makes the chip exit the low power shutdown mode. #### Syntax `lm92.wakeup()` #### Parameters None #### Returns `nil` ## lm92.setThyst() Set hysteresis Temperature. #### Syntax `lm92.setThyst(htemp)` #### Parameters - `htemp`: Hysteresis temperature from 130 to -55 in ºC #### Returns `nil` ## lm92.setTcrit() Set Critical Temperature. #### Syntax `lm92.setTcrit(ctemp)` #### Parameters `ctemp`: Critical temperature from 130 to -55 in ºC #### Returns `nil` ## lm92.setTlow() Set Low Window Temperature. #### Syntax `lm92.setTlow(lwtemp)` ####Parameters - `lwtemp`: Low window temperature from 130 to -55 in ºC #### Returns `nil` ## lm92.setThigh() Set High Window Temperature. #### Syntax `lm92.setThigh(hwtemp)` #### Parameters - `hwtemp`: High window temperature from 130 to -55 in ºC #### Returns `nil` ## lm92.getThyst() Get hysteresis Temperature. #### Syntax `lm92.getThyst()` #### Parameters None #### Returns Hysteresis Temperature in degree Celsius. ## lm92.getTcrit() Get Critical Temperature. #### Syntax `lm92.getTcrit()` #### Parameters None #### Returns Critical Temperature in degree Celsius. ## lm92.getTlow() Get Low Window Temperature. #### Syntax `lm92.getTlow()` #### Parameters None #### Returns Low Window Temperature in degree Celsius. ## lm92.getThigh() Get High Window Temperature. #### Syntax `lm92.getThigh()` #### Parameters None #### Returns High Window Temperature in degree Celsius. #### Example ```lua --node.compile("lm92.lua") lm92 = require("lm92") sda = 3 -- GPIO 0 scl = 4 -- GPIO 2 addr = 0x48 i2c.setup(0, sda, scl, i2c.SLOW) -- call i2c.setup() only once lm92.setup(addr) t = lm92.getTemperature() print("Got temperature: "..t.." C") --Seting comparison temperatures lm92.setThyst(3) lm92.setTcrit(40.75) lm92.setTlow(28.5) lm92.setThigh(31.625) t = lm92.getThyst() print("Got hyster: "..t.." C") t = lm92.getTcrit() print("Got Crit: "..t.." C") t = lm92.getTlow() print("Got Low: "..t.." C") t = lm92.getThigh() print("Got High: "..t.." C") ``` #### TODO: - add full support of the features, including interrupt and critical alert support --- ### Lua Modules/Mcp23008 # MCP23008 Module | Since | Origin / Contributor | Maintainer | Source | | :----- | :-------------------- | :---------- | :------ | | 2015-03-02 | [AllAboutEE](https://github.com/AllAboutEE) | [AllAboutEE](https://github.com/AllAboutEE) | [mcp23008.lua](../../lua_modules/mcp23008/mcp23008.lua) | This Lua module provides access to [MCP23008](http://ww1.microchip.com/downloads/en/DeviceDoc/21919e.pdf) I²C I/O Expander. !!! note This module requires `i2c` C module built into firmware. ### Require ```lua mcp32008 = require("mcp32008") ``` ### Release ```lua mcp32008 = nil package.loaded["mcp32008"] = nil ``` ## mcp32008.begin() Sets the MCP23008 device address's last three bits. !!! note The address is defined as binary `0100[A2][A1][A0]` where `A2`, `A1`, and `A0` are defined by the connection of the pins, e.g. if the pins are connected all to GND then the parameter address will need to be `0x0`. #### Syntax `mcp23008.begin(address, pinSDA, pinSCL, speed)` #### Parameters - `address`: The 3 least significant bits (LSB) of the address - `pinSDA`: The pin to use for SDA - `pinSCL`: The pin to use for SCL - `speed`: The speed of the I2C signal #### Returns `nil` ## mcp23008.writeGPIO() Writes a byte of data to the GPIO register. #### Syntax `mcp23008.writeGPIO(dataByte)` #### Parameters - `dataByte`: The byte of data to write #### Returns `nil` ## mcp23008.readGPIO() Reads a byte of data from the GPIO register #### Syntax `mcp23008.readGPIO()` #### Parameters None #### Returns One byte of data ## mcp23008.writeIODIR() Writes one byte of data to the IODIR register. #### Syntax `mcp23008.writeIODIR(dataByte)` #### Parameters - `dataByte`: The byte of data to write #### Returns `nil` ## mcp23008.readIODIR() Reads a byte from the IODIR register #### Syntax `mcp23008.readIODIR()` #### Parameters None #### Returns The byte of data in IODIR ## mcp23008.writeGPPU() Writes a byte of data to the GPPU (Pull-UP resistors register) #### Syntax `mcp23008.writeIODIR(dataByte)` #### Parameters - `dataByte`: the value to write to the GPPU register. Each bit in this byte is assigned to an individual GPIO pin #### Returns `nil` ## mcp23008.readGPPU() Reads the GPPU (Pull-UP resistors register) byte #### Syntax `mcp23008.readGPPU()` #### Parameters None #### Returns The GPPU byte i.e. state of all internal pull-up resistors #### Notes Other examples of using this module can be found in [mcp23008_buttons.lua](../../lua_examples/mcp23008/mcp23008_buttons.lua) and [mcp23008_leds.lua](../../lua_examples/mcp23008/mcp23008_leds.lua) files. --- ### Lua Modules/Mcp23017 # Lua MCP23017 Module for NodeMCU / ESP8266 | Since | Origin / Contributor | Maintainer | Source | | :----- | :-------------------- | :---------- | :------ | | 2020-04-10 | [Marcel P.](https://github.com/plomi-net) | [Marcel P.](https://github.com/plomi-net) | [mcp23017.lua](../../lua_modules/mcp23017/mcp23017.lua) | This Lua module provides access to the MCP23017 I²C I/O Expander. The [MCP23017](http://ww1.microchip.com/downloads/en/devicedoc/20001952c.pdf) is a port expander and provides 16 channels for inputs and outputs. Up to 8 devices (128 channels) are possible by the configurable address (A0 - A2). Due to the 16 channels, 2 bytes are required for switching outputs or reading input signals. These are A and B. A single pin can be set or a whole byte. The numbering of the individual pins starts at 0 and ends with 7. The numbers are for each register GPIO A and GPIO B. !!! important The module requires `i2c` and `bit` C module built into firmware. ### Require ```lua mcp = require "mcp23017" ``` ## Example Script The example script can be found [here](../../lua_examples/mcp23017/mcp23017_example.lua) ## setup() Configures the address of the module and tests the connection to the i2c bus. The i2c id is required for an existing i2c interface, alternatively the sda and scl pins can be specified. Then this function will establish the connection. Automatically resets the device state (see `mcp23017:reset()`) #### Syntax `mcp23017:setup(address, i2c_id)` #### Parameter - `address` address for MCP23017, default: 0x20 (should be between 0x20 and 0x27) - `i2c_id` id for the i2c bus connection (remember to call i2c.setup before) #### Return `true` if device found, otherwise `false`. #### possible Errors - `MCP23017 device on address not found` - `MCP23017 address is out of range` #### Example ```lua local mcp23017 = require "mcp23017" local address = 0x20 local cSCL = 1 local cSDA = 2 local i2c_instance = 0 -- setup i2c bus and create instance for mcp23017 (assigned to mcp) i2c.setup(i2c_instance, cSDA, cSCL, i2c.SLOW) local mcp = mcp23017(address, i2c_instance) ``` ## setMode() Set the mode of a single channel. This can be OUTPUT or INPUT. #### Syntax `mcp23017:setMode(register, pin, mode)` #### Parameter - `register` the side of channels (GPA or GPB) - `pin` the number to be set for the channel (0-15) - `mode` the mode for the channel. This can be `mcp23017.INPUT` or `mcp23017.OUTPUT` #### Return `true`, in case of error `nil`. #### Example ```lua -- set pin 7 and 8 to output (GPA7 and GPB0) and GPB1 to input mcp:setMode(mcp.GPA, 7, mcp.OUTPUT) mcp:setMode(mcp.GPB, 0, mcp.OUTPUT) mcp:setMode(mcp.GPB, 1, mcp.INPUT) ``` ## setPin() Set the state of a single channel. This can be HIGH or LOW. #### Syntax `mcp23017:setMode(register, pin, state)` #### Parameter - `register` the side of channels (GPA or GPB) - `pin` the number to be set for the channel (0-15) - `state` the state for the channel. This can be `mcp23017.HIGH` or `mcp23017.LOW` #### Return `true`, in case of error `nil`. #### Example ```lua -- set pin 7 to high (GPA7) mcp:setPin(mcp.GPA, 7, mcp.HIGH) -- set pin 8 to low (GPB0) mcp:setPin(mcp.GPB, 0, mcp.LOW) ``` ## getPinState() get the state for a single channel. This can be HIGH or LOW. #### Syntax `mcp23017:getPinState(register, pin)` #### Parameter - `register` the side of channels (GPA or GPB) - `pin` the number for which a state is to be queried (0-15) #### Return `true` for HIGH, `false` for LOW, in case of error `nil`. #### Example ```lua -- get the state for pin 9 (GPB1) print(mcp:getPinState(mcp.GPB, 1)) ``` ## reset() By calling this function, a safe state is established. All channels are set to input. This function can be used for a panic program. #### Syntax `mcp23017:reset()` #### Parameter None #### Return None #### Example ```lua -- reset the mcp23017 to startup defaults mcp:reset() ``` ## setInternalPullUp() Enable or disable the internal pullup resistors. #### Syntax `mcp23017:setInternalPullUp(register, byte)` #### Parameter - `register` the side of channels (GPA or GPB) - `byte` byte to set the pullup resistors #### Return None #### Example ```lua -- enable all pullup resistors for GPA print(mcp:setInternalPullUp(mcp.GPA, 0xFF)) -- disable all pullup resistors for GPA print(mcp:setInternalPullUp(mcp.GPA, 0x00)) ``` ## writeIODIR() Setup the mode of the channels with a whole byte. #### Syntax `mcp23017:writeIODIR(register, byte)` #### Parameter - `register` the side of channels (GPA or GPB) - `byte` byte to set the mode for all channels for this register #### Return None #### Example ```lua -- set all GPA to input print(mcp:writeIODIR(mcp.GPA, 0xFF)) -- set all GPA to output print(mcp:writeIODIR(mcp.GPA, 0x00)) ``` ## writeGPIO() Setup the output state of the channels with a whole byte. #### Syntax `mcp23017:writeGPIO(register, byte)` #### Parameter - `register` the side of channels (GPA or GPB) - `byte` byte to set the state for all channels for this register #### Return None #### Example ```lua -- set all GPA to HIGH print(mcp:writeGPIO(mcp.GPA, 0xFF)) -- set all GPA to LOW print(mcp:writeGPIO(mcp.GPA, 0x00)) ``` ## readGPIO() Read the input states of the channels with a whole byte. #### Syntax `mcp23017:readGPIO(register)` #### Parameter - `register` the side of channels (GPA or GPB) #### Return byte with states #### Example ```lua -- get states for GPA print(mcp:readGPIO(mcp.GPA)) ``` --- ### Lua Modules/README # NodeMCU Lua modules directory Reviewing, hosting and thus potentially maintaining an ever growing list of NodeMCU Lua modules (the ones here) does not scale well for the project team. Instead, we give the community a chance - and the responsibility - to maintain a directory of Lua modules found in the wild through the [GitHub wiki](https://github.com/nodemcu/nodemcu-firmware/wiki/Lua-modules-directory). In the (hopefully not too distant) future, we will request that Lua modules to be hosted _in this repository_ come with a test program in whatever framework [we end up adopting](https://github.com/nodemcu/nodemcu-firmware/issues/2145). **A module being listed on the wiki does NOT mean the NodeMCU project team endorses it in any way.** → [https://github.com/nodemcu/nodemcu-firmware/wiki/Lua-modules-directory](https://github.com/nodemcu/nodemcu-firmware/wiki/Lua-modules-directory) --- ### Lua Modules/Redis # Redis Module | Since | Origin / Contributor | Maintainer | Source | | :----- | :-------------------- | :---------- | :------ | | 2015-02-06 | [Vladimir Dronnikov](https://github.com/dvv) | [Vladimir Dronnikov](https://github.com/dvv) | [redis.lua](../../lua_modules/redis/redis.lua) | This Lua module provides a simple implementation of a [Redis](https://redis.io/) client. ### Require ```lua redis = dofile("redis.lua") ``` ### Release ```lua redis = nil ``` ## redis.connect() Function used to connect to Redis server. #### Syntax `redis.connect(host, [port])` #### Parameters - `host` Redis host name or address - `port` Redis database port. Default value is 6379. #### Returns Object with rest of the functions. !!! 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. #### Syntax `redis:subscribe(channel, handler)` #### Parameters - `channel` Channel name - `handler` Handler function that will be called on new message in subscribed channel #### Returns `nil` ## redis:publish() Publish a message to a Redis channel. #### Syntax `redis:publish(channel, message)` #### Parameters - `channel` Channel name - `message` Message to publish #### Returns `nil` ## redis:unsubscribe() Unsubscribes from a channel. #### Syntax `redis:unsubscribe(channel)` #### Parameters - `channel` Channel name to unsubscribe from #### Returns `nil` ## redis:close() Function to close connection to Redis server. #### Syntax `redis:close()` #### Parameters None #### Returns `nil` ## Example ```lua local redis = dofile("redis.lua").connect(host, port) redis:publish("chan1", "foo") redis:subscribe("chan1", function(channel, msg) print(channel, msg) end) ``` --- ### Lua Modules/Telnet # Telnet Module | Since | Origin / Contributor | Maintainer | Source | | :----- | :-------------------- | :---------- | :------ | | 2018-05-24 | [Terry Ellison](https://github.com/TerryE) | [Terry Ellison](https://github.com/TerryE) | [telnet.lua](../../lua_modules/telnet/telnet.lua) | The current version of this module exploits the stdin / stdout pipe functionality and task integration that is now build into the NodeNMCU Lua core. There are two nice advantages of this core implementation: - Errors are now written to stdout in a separate task execution. - The pipes pretty much eliminate UART and telnet overrun. Both have the same interface if required into the variable `telnet` ## telnet:open() Open a telnet server based on the provided parameters. #### Syntax `telnet:open(ssid, pwd, port)` #### Parameters `ssid` and `password`. Strings. SSID and Password for the Wifi network. If these are `nil` then the wifi is assumed to be configured or auto-configured. `port`. Integer TCP listening port for the Telnet service. The default is 2323 #### Returns Nothing returned (this is evaluated as `nil` in a scalar context). ## telnet:close() Close a telnet server and release all resources. Also set the variable `telnet` to nil to fully reference and GC the resources. #### Syntax `telnet:close()` #### Parameters None #### Returns Nothing returned (this is evaluated as `nil` in a scalar context). --- ### Lua Modules/Yeelink # Yeelink Module | Since | Origin / Contributor | Maintainer | Source | | :----- | :-------------------- | :---------- | :------ | | 2015-04-14 | [Martin Han](https://github.com/MarsTechHAN) | [Martin Han](https://github.com/MarsTechHAN) | [yeelink_lib.lua](../../lua_modules/yeelink/yeelink_lib.lua) | This Lua module provides a simple implementation of an [Yeelink](http://www.yeelink.net/) client. ### Require ```lua yeelink = require("yeelink_lib") ``` ### Release ```lua yeelink = nil package.loaded["yeelink_lib"] = nil ``` ## yeelink.init() Initializes Yeelink client. #### Syntax `yeelink.init(device, sensor, apikey)` #### Parameters - `device`: device number - `sensor`: sensor number - `apikey`: Yeelink API key string #### Returns IP address of `api.yeelink.net`, if not obtained then `false` ## yeelink.getDNS() Function to check DNS resolution of `api.yeelink.net` status. #### Syntax `yeelink.getDNS()` #### Parameters None #### Returns IP address of `api.yeelink.net` or `nil` when name resolution failed. ## yeelink.update() Send data to Yeelink Sever. #### Syntax `yeelink.update(datapoint)` #### Parameters - `datapoint`: Data to send to Yeelink API #### Returns `nil` #### Notes Example of using this module can be found in [Example_for_Yeelink_Lib.lua](../../lua_modules/yeelink/Example_for_Yeelink_Lib.lua) file. --- ### Modules/Adc # ADC Module | Since | Origin / Contributor | Maintainer | Source | | :----- | :-------------------- | :---------- | :------ | | 2014-12-24 | [Zeroday](https://github.com/funshine) | [jmattsson](https://github.com/jmattsson) | [adc.c](../../app/modules/adc.c)| The ADC module provides access to the in-built ADC. On the ESP8266 there is only a single-channel, which is multiplexed with the battery voltage. Depending on the setting in the "esp init data" (byte 107) one can either use the ADC to read an external voltage, or to read the system voltage (vdd33), but not both. Which mode to use the ADC in can be configured via the `adc.force_init_mode()` function. Note that after switching from one to the other a system restart (e.g. power cycle, reset button, [`node.restart()`](node.md#noderestart)) is required before the change takes effect. ## adc.force_init_mode() Checks and if necessary reconfigures the ADC mode setting in the ESP init data block. ####Syntax `adc.force_init_mode(mode_value)` ####Parameters `mode_value` One of `adc.INIT_ADC` or `adc.INIT_VDD33`. ####Returns True if the function had to change the mode, false if the mode was already configured. On a true return the ESP needs to be restarted for the change to take effect. ####Example ```lua -- in you init.lua: if adc.force_init_mode(adc.INIT_VDD33) then node.restart() return -- don't bother continuing, the restart is scheduled end print("System voltage (mV):", adc.readvdd33(0)) ``` ####See also [`node.restart()`](node.md#noderestart) ## adc.read() Samples the ADC. ####Syntax `adc.read(channel)` ####Parameters `channel` always 0 on the ESP8266 ####Returns the sampled value (number) If the ESP8266 has been configured to use the ADC for reading the system voltage, this function will always return 65535. This is a hardware and/or SDK limitation. ####Example ```lua val = adc.read(0) ``` ## adc.readvdd33() Reads the system voltage. ####Syntax `adc.readvdd33()` ####Parameters none ####Returns system voltage in millivolts (number) If the ESP8266 has been configured to use the ADC for sampling the external pin, this function will always return 65535. This is a hardware and/or SDK limitation. --- ### Modules/Ads1115 # ADS1115 Module | Since | Origin / Contributor | Maintainer | Source | | :----- | :-------------------- | :---------- | :------ | | 2017-04-24 | [fetchbot](https://github.com/fetchbot) | [fetchbot](https://github.com/fetchbot) | [ads1115.c](../../app/modules/ads1115.c)| This module provides access to the ADS1115 (16-Bit) and ADS1015 (12-Bit) analog-to-digital converters. Other chips from the same family (ADS1113, ADS1114, ADS1013 and ADS1014) are likely to work. Missing hardware features will be silently ignored. This module supports multiple devices connected to I²C bus. The devices of different types can be mixed. The addressing of ADS family allows for maximum of 4 devices connected to the same I²C bus. !!! caution The **ABSOLUTE MAXIMUM RATINGS** for all analog inputs are `–0.3V to VDD+0.3V` referred to GND. ## ads1115.ads1115() Registers ADS1115 (ADS1113, ADS1114) device. #### Syntax `ads1115.ADS1115(I2C_ID, I2C_ADDR)` #### Parameters - `I2C_ID` - always 0 - `ADDRESS` - I²C address of a device * `ads1115.ADDR_GND` * `ads1115.ADDR_VDD` * `ads1115.ADDR_SDA` * `ads1115.ADDR_SCL` #### Returns Registered `device` object #### Example ```lua local id, sda, scl = 0, 6, 5 i2c.setup(id, sda, scl, i2c.SLOW) ads1115.reset() adc1 = ads1115.ads1115(id, ads1115.ADDR_GND) ``` ## ads1115.ads1015() Registers ADS1015 (ADS1013, ADS1014) device. #### Syntax `ads1115.ads1015(I2C_ID, I2C_ADDR)` #### Parameters - `I2C_ID` - always 0 - `ADDRESS` - I²C address of a device * `ads1115.ADDR_GND` * `ads1115.ADDR_VDD` * `ads1115.ADDR_SDA` * `ads1115.ADDR_SCL` #### Returns Registered `device` object #### Example ```lua local id, sda, scl = 0, 6, 5 i2c.setup(id, sda, scl, i2c.SLOW) ads1115.reset() adc1 = ads1115.ads1015(id, ads1115.ADDR_VDD) adc2 = ads1115.ads1115(id, ads1115.ADDR_SDA) ``` ## ads1115.reset() Reset all devices connected to I²C interface. ### Syntax ads1115.reset() #### Parameters none #### Returns `nil` #### Example ```lua local id, alert_pin, sda, scl = 0, 7, 6, 5 i2c.setup(id, sda, scl, i2c.SLOW) ads1115.reset() ``` # ADS Device ## ads1115.device:read() Gets the result stored in the register of a previously issued conversion, e.g. in continuous mode or with a conversion ready interrupt. #### Syntax `volt, volt_dec, raw, sign = device:read()` #### Parameters none #### Returns - `volt` voltage in mV (see note below) - `volt_dec` voltage decimal in uV (see note below) - `adc` raw adc register value - `sign` sign of the result (see note below) !!! note If using float firmware then `volt` is a floating point number, `volt_dec` and `sign` are nil. On an integer firmware, the final value has to be concatenated from `volt`, `volt_dec` and `sign`. On integer firmware `volt` and `volt_dec` are always positive, sign can be `-1`, `0`, `1`. #### Example ```lua local id, alert_pin, sda, scl = 0, 7, 6, 5 i2c.setup(id, sda, scl, i2c.SLOW) ads1115.reset() adc1 = ads1115.ads1115(id, ads1115.ADDR_GND) -- continuous mode adc1:setting(ads1115.GAIN_6_144V, ads1115.DR_128SPS, ads1115.SINGLE_0, ads1115.CONTINUOUS) -- read adc result with read() volt, volt_dec, adc, sign = ads1:read() print(volt, volt_dec, adc, sign) -- comparator adc1:setting(ads1115.GAIN_6_144V, ads1115.DR_128SPS, ads1115.SINGLE_0, ads1115.CONTINUOUS, ads1115.COMP_1CONV, 1000, 2000) local function comparator(level, when) -- read adc result with read() when threshold reached gpio.trig(alert_pin) volt, volt_dec, adc, sign = adc1:read() print(volt, volt_dec, adc, sign) end gpio.mode(alert_pin, gpio.INT) gpio.trig(alert_pin, "both", comparator) -- read adc result with read() volt, volt_dec, adc, sign = ads1115:read() print(volt, volt_dec, adc, sing) -- format value in int build if sign then -- int build print(string.format("%s%d.%03d mV", sign >= 0 and "+" or "-", volt, volt_dec)) else -- float build -- just use V as it is end ``` ## ads1115.device:setting() Configuration settings for the ADC. #### Syntax `device:setting(GAIN, SAMPLES, CHANNEL, MODE[, CONVERSION_RDY][, COMPARATOR, THRESHOLD_LOW, THRESHOLD_HI[,COMP_MODE]])` #### Parameters - `GAIN` Programmable gain amplifier * `ads1115.GAIN_6_144V` 2/3x Gain * `ads1115.GAIN_4_096V` 1x Gain * `ads1115.GAIN_2_048V` 2x Gain * `ads1115.GAIN_1_024V` 4x Gain * `ads1115.GAIN_0_512V` 8x Gain * `ads1115.GAIN_0_256V` 16x Gain - `SAMPLES` Data rate in samples per second * `ads1115.DR_8SPS` ADS1115 only * `ads1115.DR_16SPS` ADS1115 only * `ads1115.DR_32SPS` ADS1115 only * `ads1115.DR_64SPS` ADS1115 only * `ads1115.DR_128SPS` * `ads1115.DR_250SPS` * `ads1115.DR_475SPS` ADS1115 only * `ads1115.DR_490SPS` ADS1015 only * `ads1115.DR_860SPS` ADS1115 only * `ads1115.DR_920SPS` ADS1015 only * `ads1115.DR_1600SPS` ADS1015 only * `ads1115.DR_2400SPS` ADS1015 only * `ads1115.DR_3300SPS` ADS1015 only - `CHANNEL` Input multiplexer for single-ended or differential measurement * `ads1115.SINGLE_0` channel 0 to GND * `ads1115.SINGLE_1` channel 1 to GND * `ads1115.SINGLE_2` channel 2 to GND * `ads1115.SINGLE_3` channel 3 to GND * `ads1115.DIFF_0_1` channel 0 to 1 * `ads1115.DIFF_0_3` channel 0 to 3 * `ads1115.DIFF_1_3` channel 1 to 3 * `ads1115.DIFF_2_3` channel 2 to 3 - `MODE` Device operating mode * `ads1115.SINGLE_SHOT` single-shot mode * `ads1115.CONTINUOUS` continuous mode - `CONVERSION_RDY` Number of conversions after conversion ready asserts (optional) * `ads1115.CONV_RDY_1` * `ads1115.CONV_RDY_2` * `ads1115.CONV_RDY_4` - `COMPARATOR` Number of conversions after comparator asserts (optional) * `ads1115.COMP_1CONV` * `ads1115.COMP_2CONV` * `ads1115.COMP_4CONV` - `THRESHOLD_LOW` * `0` - `+ GAIN_MAX` in mV for single-ended inputs * `- GAIN_MAX` - `+ GAIN_MAX` in mV for differential inputs - `THRESHOLD_HI` * `0` - `+ GAIN_MAX` in mV for single-ended inputs * `- GAIN_MAX` - `+ GAIN_MAX` in mV for differential inputs - `COMP_MODE` Comparator mode * `ads1115.CMODE_TRAD` traditional comparator mode (with hysteresis) * `ads1115.CMODE_WINDOW` window comparator mode note: Comparator and conversion ready are always configured to non-latching, active low. #### Returns `nil` #### Example ```lua local id, sda, scl = 0, 6, 5 i2c.setup(id, sda, scl, i2c.SLOW) ads1115.reset() adc1 = ads1115.ads1015(id, ads1115.ADDR_GND) adc1:setting(ads1115.GAIN_6_144V, ads1115.DR_3300SPS, ads1115.SINGLE_0, ads1115.SINGLE_SHOT) ``` ## ads1115.device:startread() Starts the ADC reading for single-shot mode and after the conversion is done it will invoke an optional callback function in which the ADC conversion result can be obtained. #### Syntax `device:startread([CALLBACK])` #### Parameters - `CALLBACK` callback function which will be invoked after the adc conversion is done * `function(volt, volt_dec, adc, sign) end` #### Returns - `nil` #### Example ```lua local id, alert_pin, sda, scl = 0, 7, 6, 5 i2c.setup(id, sda, scl, i2c.SLOW) ads1115.reset() adc1 = ads1115.ads1115(id, ads1115.ADDR_VDD) -- single shot adc1:setting(ads1115.GAIN_6_144V, ads1115.DR_128SPS, ads1115.SINGLE_0, ads1115.SINGLE_SHOT) -- start adc conversion and get result in callback after conversion is ready adc1:startread(function(volt, volt_dec, adc, sign) print(volt, volt_dec, adc, sign) end) -- conversion ready adc1:setting(ads1115.GAIN_6_144V, ads1115.DR_128SPS, ads1115.SINGLE_0, ads1115.SINGLE_SHOT, ads1115.CONV_RDY_1) local function conversion_ready(level, when) gpio.trig(alert_pin) volt, volt_dec, adc, sign = adc1:read() print(volt, volt_dec, adc, sign) end gpio.mode(alert_pin, gpio.INT) gpio.trig(alert_pin, "down", conversion_ready) -- start conversion and get result with read() after conversion ready pin asserts adc1:startread() ``` --- ### Modules/Adxl345 # ADXL345 Module | Since | Origin / Contributor | Maintainer | Source | | :----- | :-------------------- | :---------- | :------ | | 2016-04-08 | [Jason Schmidlapp](https://github.com/jschmidlapp) | [Jason Schmidlapp](https://github.com/jschmidlapp) | [adxl345.c](../../app/modules/adxl345.c)| This module provides access to the [ADXL345](https://www.sparkfun.com/products/9836) triple axis accelerometer. ## adxl345.read() Samples the sensor and returns X,Y and Z data from the accelerometer. #### Syntax `adxl345.read()` #### Returns X,Y,Z data (integers) #### Example ```lua local sda, scl = 1, 2 i2c.setup(0, sda, scl, i2c.SLOW) -- call i2c.setup() only once adxl345.setup() local x,y,z = adxl345.read() print(string.format("X = %d, Y = %d, Z = %d", x, y, z)) ``` ## adxl345.setup() Initializes the module. #### Syntax `adxl345.setup()` #### Parameters None #### Returns `nil` --- ### Modules/Am2320 # AM2320 Module | Since | Origin / Contributor | Maintainer | Source | | :----- | :-------------------- | :---------- | :------ | | 2016-02-14 | [Henk Vergonet](https://github.com/hvegh) | [Henk Vergonet](https://github.com/hvegh) | [am2320.c](../../app/modules/am2320.c)| This module provides access to the [AM2320](https://akizukidenshi.com/download/ds/aosong/AM2320.pdf) humidity and temperature sensor, using the i2c interface. !!! caution This code is licensed under GPL by its author. Any binaries that include this module becomes subject to the GPL, requiring anyone who ships them to also ship source. ## am2320.read() Samples the sensor and returns the relative humidity in % and temperature in celsius, as an integer multiplied with 10. #### Syntax `am2320.read()` #### Returns - `relative humidity` percentage multiplied with 10 (integer) - `temperature` in celcius multiplied with 10 (integer) #### Example ```lua sda, scl = 1, 2 i2c.setup(0, sda, scl, i2c.SLOW) -- call i2c.setup() only once am2320.setup() rh, t = am2320.read() print(string.format("RH: %s%%", rh / 10)) print(string.format("Temperature: %s degrees C", t / 10)) ``` ## am2320.setup() Initializes the module. Returns model, version, serial but is seams these where all zero on my model. #### Syntax `model, version, serial = am2320.setup()` #### Parameters None #### Returns - `model` 16 bits number of model - `version` 8 bits version number - `serial` 32 bits serial number Note: I have only observed values of 0 for all of these, maybe other sensors return more sensible readings. --- ### Modules/Apa102 # APA102 Module | Since | Origin / Contributor | Maintainer | Source | | :----- | :-------------------- | :---------- | :------ | | 2016-01-26 | [Robert Foss](https://github.com/robertfoss)| [Robert Foss](https://github.com/robertfoss)| [apa102.c](../../app/modules/apa102.c)| This module provides Lua access to [APA102 RGB LEDs](https://youtu.be/UYvC-hukz-0) which are similar in function to the common [WS2812](ws2812) addressable LEDs. > DotStar LEDs are 5050-sized LEDs with an embedded micro controller inside the LED. You can set the color/brightness of each LED to 24-bit color (8 bits each red green and blue). Each LED acts like a shift register, reading incoming color data on the input pins, and then shifting the previous color data out on the output pin. By sending a long string of data, you can control an infinite number of LEDs, just tack on more or cut off unwanted LEDs at the end. source: [Adafruit](https://www.adafruit.com/products/2343) !!! caution This module has an _optional_ dependency to the [pixbuf module](pixbuf.md) i.e. it can work without. However, if you compile the firmware without pixbuf the respective features will be missing from this module. ## apa102.write() Send ABGR data in 8 bits to a APA102 chain. #### Syntax `apa102.write(data_pin, clock_pin, string)` #### Parameters - `data_pin` any GPIO pin 0, 1, 2, ... - `clock_pin` any GPIO pin 0, 1, 2, ... - `data` payload to be sent to one or more APA102 LEDs. It may be a [pixbuf](pixbuf) with four channels or a string, composed from a ABGR quadruplet per element: - `A1` the first pixel's Intensity channel (0-31) - `B1` the first pixel's Blue channel (0-255)
- `G1` the first pixel's Green channel (0-255) - `R1` the first pixel's Red channel (0-255) ... You can connect a lot of APA102 ... - `A2`, `B2`, `G2`, `R2` are the next APA102s Intensity, Blue, Green and Red channel parameters #### Returns `nil` #### Example 1 ```lua a = 31 b = 0 g = 0 r = 255 leds_abgr = string.char(a, b, g, r, a, b, g, r) apa102.write(2, 3, leds_abgr) -- turn two APA102s to red, connected to data_pin 2 and clock_pin 3 ``` #### Example 2 ```lua -- set the first 30 leds to red apa102.write(2, 3, string.char(31, 255, 0, 0):rep(30)) ``` --- ### Modules/Bit # bit Module | Since | Origin / Contributor | Maintainer | Source | | :----- | :-------------------- | :---------- | :------ | | 2014-12-24 | [https://github.com/LuaDist/bitlib](https://github.com/LuaDist/bitlib), [Zeroday](https://github.com/funshine) | [Zeroday](https://github.com/funshine) | [bit.c](../../app/modules/bit.c)| Bit manipulation support, on 32bit integers. ## bit.arshift() Arithmetic right shift a number equivalent to `value >> shift` in C. #### Syntax `bit.arshift(value, shift)` #### Parameters - `value` the value to shift - `shift` positions to shift #### Returns the number shifted right (arithmetically) #### Example ```lua bit.arshift(3, 1) -- returns 1 -- Using a 4 bits representation: 0011 >> 1 == 0001 ``` ## bit.band() Bitwise AND, equivalent to `val1 & val2 & ... & valn` in C. #### Syntax `bit.band(val1, val2 [, ... valn])` #### Parameters - `val1` first AND argument - `val2` second AND argument - `...valn` ...nth AND argument #### Returns the bitwise AND of all the arguments (number) ### Example ```lua bit.band(3, 2) -- returns 2 -- Using a 4 bits representation: 0011 & 0010 == 0010 ``` ## bit.bit() Generate a number with a 1 bit (used for mask generation). Equivalent to `1 << position` in C. #### Syntax `bit.bit(position)` #### Parameters `position` position of the bit that will be set to 1 #### Returns a number with only one 1 bit at position (the rest are set to 0) ### Example ```lua bit.bit(4) -- returns 16 ``` ## bit.bnot() Bitwise negation, equivalent to `~value in C.` #### Syntax `bit.bnot(value)` #### Parameters `value` the number to negate #### Returns the bitwise negated value of the number ## bit.bor() Bitwise OR, equivalent to `val1 | val2 | ... | valn` in C. #### Syntax `bit.bor(val1, val2 [, ... valn])` #### Parameters - `val1` first OR argument. - `val2` second OR argument. - `...valn` ...nth OR argument #### Returns the bitwise OR of all the arguments (number) ### Example ```lua bit.bor(3, 2) -- returns 3 -- Using a 4 bits representation: 0011 | 0010 == 0011 ``` ## bit.bxor() Bitwise XOR, equivalent to `val1 ^ val2 ^ ... ^ valn` in C. #### Syntax `bit.bxor(val1, val2 [, ... valn])` #### Parameters - `val1` first XOR argument - `val2` second XOR argument - `...valn` ...nth XOR argument #### Returns the bitwise XOR of all the arguments (number) ### Example ```lua bit.bxor(3, 2) -- returns 1 -- Using a 4 bits representation: 0011 ^ 0010 == 0001 ``` ## bit.clear() Clear bits in a number. #### Syntax `bit.clear(value, pos1 [, ... posn])` #### Parameters - `value` the base number - `pos1` position of the first bit to clear - `...posn` position of thet nth bit to clear #### Returns the number with the bit(s) cleared in the given position(s) ### Example ```lua bit.clear(3, 0) -- returns 2 ``` ## bit.isclear() Test if a given bit is cleared. #### Syntax `bit.isclear(value, position)` #### Parameters - `value` the value to test - `position` bit position to test #### Returns true if the bit at the given position is 0, false otherwise ### Example ```lua bit.isclear(2, 0) -- returns true ``` ## bit.isset() Test if a given bit is set. #### Syntax `bit.isset(value, position)` #### Parameters - `value` the value to test - `position` bit position to test #### Returns true if the bit at the given position is 1, false otherwise ### Example ```lua bit.isset(2, 0) -- returns false ``` ## bit.lshift() Left-shift a number, equivalent to `value << shift` in C. #### Syntax `bit.lshift(value, shift)` #### Parameters - `value` the value to shift - `shift` positions to shift #### Returns the number shifted left ### Example ```lua bit.lshift(2, 2) -- returns 8 -- Using a 4 bits representation: 0010 << 2 == 1000 ``` ## bit.rshift() Logical right shift a number, equivalent to `( unsigned )value >> shift` in C. #### Syntax `bit.rshift(value, shift)` #### Parameters - `value` the value to shift. - `shift` positions to shift. #### Returns the number shifted right (logically) ### Example ```lua bit.rshift(2, 1) -- returns 1 -- Using a 4 bits representation: 0010 >> 1 == 0001 ``` ## bit.set() Set bits in a number. #### Syntax `bit.set(value, pos1 [, ... posn ])` #### Parameters - `value` the base number. - `pos1` position of the first bit to set. - `...posn` position of the nth bit to set. #### Returns the number with the bit(s) set in the given position(s) ### Example ```lua bit.set(2, 0) -- returns 3 ``` --- ### Modules/Bloom # Bloom Module | Since | Origin / Contributor | Maintainer | Source | | :----- | :-------------------- | :---------- | :------ | | 2017-11-13 | [Philip Gladstone](https://github.com/pjsg) | [Philip Gladstone](https://github.com/pjsg) | [bloom.c](../../app/modules/bloom.c)| This module implements a [Bloom filter](https://en.wikipedia.org/wiki/Bloom_filter). This is a probabilistic data structure that is used to test for set membership. There are two operations -- `add` and `check` that allow arbitrary strings to be added to the set or tested for set membership. Since this is a probabilistic data structure, the answer returned can be incorrect. However, if the string *is* a member of the set, then the `check` operation will always return `true`. ## bloom.create() Create a filter object. #### Syntax `bloom.create(elements, errorrate)` #### Parameters - `elements` The largest number of elements to be added to the filter. - `errorrate` The error rate (the false positive rate). This is represented as `n` where the false positive rate is `1 / n`. This is the maximum rate of `check` returning true when the string is *not* in the set. #### Returns A `filter` object. #### Example ``` filter = bloom.create(10000, 100) -- this will use around 11kB of memory ``` ## filter:add() Adds a string to the set and returns an indication of whether the string was already present. #### Syntax `filter:add(string)` #### Parameters - `string` The string to be added to the filter set. #### Returns `true` if the string was already present in the filter. `false` otherwise. #### Example ``` if filter:add("apple") then print ("Seen an apple before!") else print ("Noted that the first apple has been seen") end ``` ## filter:check() Checks to see if a string is present in the filter set. #### Syntax `present = filter:check(string)` #### Parameters - `string` The string to be checked for membership in the set. #### Returns `true` if the string was already present in the filter. `false` otherwise. #### Example ``` if filter:check("apple") then print ("Seen an apple before!") end ``` ## filter:reset() Empties the filter. #### Syntax `filter:reset()` #### Returns Nothing #### Example ``` filter:reset() ``` ## filter:info() Get some status information on the filter. #### Syntax `bits, fns, occupancy, fprate = filter:info()` #### Returns - `bits` The number of bits in the filter. - `fns` The number of hash functions in use. - `occupancy` The number of bits set in the filter. - `fprate` The approximate chance that the next `check` will return `true` when it should return `false`. This is represented as the inverse of the probability -- i.e. as the n in 1-in-n chance. This value is limited to 1,000,000. #### Example ``` bits, fns, occupancy, fprate = filter:info() ``` --- ### Modules/Bme280 # BME280 module | Since | Origin / Contributor | Maintainer | Source | | :----- | :-------------------- | :---------- | :------ | | 2016-02-21 | [vsky279](https://github.com/vsky279) | [vsky279](https://github.com/vsky279) | [bme280.c](../../app/modules/bme280.c)| This module provides a simple interface to [BME280/BMP280 temperature/air presssure/humidity sensors](http://www.bosch-sensortec.com/bst/products/all_products/bme280) (Bosch Sensortec). !!! caution Note that you must call [`setup()`](#bme280setup) before you can start reading values! Furthermore, there has to be a variable delay between some tens to hundreds of milliseconds between `setup()` and reading measurements. Instead of using a fixed delay you might also poll the sensor until data is delivered e.g. `humi()` not returning `nil` anymore. ## bme280.altitude() For given air pressure and sea level air pressure returns the altitude in meters as an integer multiplied with 100, i.e. altimeter function. #### Syntax `bme280.altitude(P, QNH)` #### Parameters - `P` measured pressure - `QNH` current sea level pressure #### Returns altitude in meters of measurement point ## bme280.baro() Reads the sensor and returns the air pressure in hectopascals as an integer multiplied with 1000 or `nil` when readout is not successful. Current temperature is needed to calculate the air pressure so temperature reading is performed prior reading pressure data. Second returned variable is therefore current air temperature. #### Syntax `bme280.baro()` #### Parameters none #### Returns - `P` air pressure in hectopascals multiplied by 1000 - `T` temperature in celsius as an integer multiplied with 100 ## bme280.dewpoint() For given temperature and relative humidity returns the dew point in celsius as an integer multiplied with 100. #### Syntax `bme280.dewpoint(H, T)` #### Parameters - `H` relative humidity in percent multiplied by 1000. - `T` temperate in celsius multiplied by 100. #### Returns dew point in celsisus ## bme280.humi() Reads the sensor and returns the air relative humidity in percents as an integer multiplied with 100 or `nil` when readout is not successful. Current temperature is needed to calculate the relative humidity so temperature reading is performed prior reading pressure data. Second returned variable is therefore current temperature. #### Syntax `bme280.humi()` #### Parameters none #### Returns - `H` last relative humidity reading in % times 1000 - `T` temperature in celsius as an integer multiplied with 100 ## bme280.qfe2qnh() For given altitude converts the air pressure to sea level air pressure. #### Syntax `bme280.qfe2qnh(P, altitude)` #### Parameters - `P` measured pressure - `altitude` altitude in meters of measurement point #### Returns sea level pressure ## bme280.read() Reads the sensor and returns the temperature, the air pressure, the air relative humidity and #### Syntax `bme280.read([altitude])` #### Parameters - (optional) `altitude`- altitude in meters of measurement point. If provided also the air pressure converted to sea level air pressure is returned. #### Returns - `T` temperature in celsius as an integer multiplied with 100 - `P` air pressure in hectopascals multiplied by 1000 - `H` relative humidity in percent multiplied by 1000 - `QNH` air pressure in hectopascals multiplied by 1000 converted to sea level Any of these variables is `nil` if the readout of given measure was not successful. ## bme280.startreadout() Starts readout (turns the sensor into forced mode). After the readout the sensor turns to sleep mode. #### Syntax `bme280.startreadout(delay, callback)` #### Parameters - `delay` sets sensor to forced mode and calls the `callback` (if provided) after given number of milliseconds. For 0 the default delay is set to 113ms (sufficient time to perform reading for oversampling settings 16x). For different oversampling setting please refer to [BME280 Final Datasheet - Appendix B: Measurement time and current calculation](https://ae-bst.resource.bosch.com/media/_tech/media/datasheets/BST-BME280-DS002.pdf#page=51). - `callback` if provided it will be invoked after given `delay`. The sensor reading should be finalized by then so. #### Returns `nil` ## bme280.setup() Initializes module. Initialization is mandatory before read values. #### Syntax `bme280.setup([temp_oss, press_oss, humi_oss, power_mode, inactive_duration, IIR_filter])` #### Parameters - (optional) `temp_oss` - Controls oversampling of temperature data. Default oversampling is 16x. - (optional) `press_oss` - Controls oversampling of pressure data. Default oversampling is 16x. - (optional) `humi_oss` - Controls oversampling of humidity data. Default oversampling is 16x - (optional) `sensor_mode` - Controls the sensor mode of the device. Default sensor more is normal. - (optional) `inactive_duration` - Controls inactive duration in normal mode. Default inactive duration is 20ms. - (optional) `IIR_filter` - Controls the time constant of the IIR filter. Default filter coefficient is 16. - (optional) `cold_start` - If 0 then the BME280 chip is not initialised. Useful in a battery operated setup when the ESP deep sleeps and on wakeup needs to initialise the driver (the module) but not the chip itself. The chip was kept powered (sleeping too) and is holding the latest reading that should be fetched quickly before another reading starts (`bme280.startreadout()`). By default the chip is initialised. |`temp_oss`, `press_oss`, `humi_oss`|Data oversampling| |-----|-----------------| |0|Skipped (output set to 0x80000)| |1|oversampling ×1| |2|oversampling ×2| |3|oversampling ×4| |4|oversampling ×8| |**5**|**oversampling ×16**| |`sensor_mode`|Sensor mode| |-----|-----------------| |0|Sleep mode| |1 and 2|Forced mode| |**3**|**Normal mode**| Using forced mode is recommended for applications which require low sampling rate or hostbased synchronization. The sensor enters into sleep mode after a forced readout. Please refer to BME280 Final Datasheet for more details. |`inactive_duration`|t standby (ms)| |-----|-----------------| |0|0.5| |1|62.5| |2|125| |3|250| |4|500| |5|1000| |6|10| |**7**|**20**| |`IIR_filter`|Filter coefficient | |-----|-----------------| |0|Filter off| |1|2| |2|4| |3|8| |**4**|**16**| #### Returns `nil` if initialization has failed (no sensor connected?), `2` if sensor is BME280, `1` if sensor is BMP280 #### Example ```lua alt=320 -- altitude of the measurement place sda, scl = 3, 4 i2c.setup(0, sda, scl, i2c.SLOW) -- call i2c.setup() only once bme280.setup() P, T = bme280.baro() print(string.format("QFE=%d.%03d", P/1000, P%1000)) -- convert measure air pressure to sea level pressure QNH = bme280.qfe2qnh(P, alt) print(string.format("QNH=%d.%03d", QNH/1000, QNH%1000)) H, T = bme280.humi() local Tsgn = (T < 0 and -1 or 1); T = Tsgn*T print(string.format("T=%s%d.%02d", Tsgn<0 and "-" or "", T/100, T%100)) print(string.format("humidity=%d.%03d%%", H/1000, H%1000)) D = bme280.dewpoint(H, T) local Dsgn = (D < 0 and -1 or 1); D = Dsgn*D print(string.format("dew_point=%s%d.%02d", Dsgn<0 and "-" or "", D/100, D%100)) -- altimeter function - calculate altitude based on current sea level pressure (QNH) and measure pressure P = bme280.baro() curAlt = bme280.altitude(P, QNH) local curAltsgn = (curAlt < 0 and -1 or 1); curAlt = curAltsgn*curAlt print(string.format("altitude=%s%d.%02d", curAltsgn<0 and "-" or "", curAlt/100, curAlt%100)) ``` Or simpler and more efficient ```lua alt=320 -- altitude of the measurement place sda, scl = 3, 4 i2c.setup(0, sda, scl, i2c.SLOW) -- call i2c.setup() only once bme280.setup() T, P, H, QNH = bme280.read(alt) local Tsgn = (T < 0 and -1 or 1); T = Tsgn*T print(string.format("T=%s%d.%02d", Tsgn<0 and "-" or "", T/100, T%100)) print(string.format("QFE=%d.%03d", P/1000, P%1000)) print(string.format("QNH=%d.%03d", QNH/1000, QNH%1000)) print(string.format("humidity=%d.%03d%%", H/1000, H%1000)) D = bme280.dewpoint(H, T) local Dsgn = (D < 0 and -1 or 1); D = Dsgn*D print(string.format("dew_point=%s%d.%02d", Dsgn<0 and "-" or "", D/100, D%100)) -- altimeter function - calculate altitude based on current sea level pressure (QNH) and measure pressure P = bme280.baro() curAlt = bme280.altitude(P, QNH) local curAltsgn = (curAlt < 0 and -1 or 1); curAlt = curAltsgn*curAlt print(string.format("altitude=%s%d.%02d", curAltsgn<0 and "-" or "", curAlt/100, curAlt%100)) ``` Use `bme280.setup(1, 3, 0, 3, 0, 4)` for "game mode" - Oversampling settings pressure ×4, temperature ×1, humidity ×0, sensor mode: normal mode, inactive duration = 0.5 ms, IIR filter settings filter coefficient 16. Example of readout in forced mode (asynchronous) ```lua sda, scl = 3, 4 i2c.setup(0, sda, scl, i2c.SLOW) -- call i2c.setup() only once bme280.setup(nil, nil, nil, 0) -- initialize to sleep mode bme280.startreadout(0, function () T, P = bme280.read() local Tsgn = (T < 0 and -1 or 1); T = Tsgn*T print(string.format("T=%s%d.%02d", Tsgn<0 and "-" or "", T/100, T%100)) end) ``` ## bme280.temp() Reads the sensor and returns the temperature in celsius as an integer multiplied with 100. #### Syntax `bme280.temp()` #### Parameters none #### Returns - `T` temperature in celsius as an integer multiplied with 100 or `nil` when readout is not successful - `t_fine` temperature measure used in pressure and humidity compensation formulas (generally no need to use this value) --- ### Modules/Bme280 Math # BME280_math module | Since | Origin / Contributor | Maintainer | Source | | :----- | :-------------------- | :---------- | :------ | | 2016-02-21 | [vsky279](https://github.com/vsky279) | [vsky279](https://github.com/vsky279) | [bme280_math.c](../../app/modules/bme280_math.c)| This module provides calculation routines for [BME280/BMP280 temperature/air presssure/humidity sensors](http://www.bosch-sensortec.com/bst/products/all_products/bme280) (Bosch Sensortec). Communication with the sensor is ensured by Lua code through I2C or SPI interface. Read registers are passed to the module to calculate measured values. See [bme280](../lua-modules/bme280.md) Lua module for examples. ## bme280_math.altitude() For given air pressure (called QFE in aviation - see [wiki QNH article](https://en.wikipedia.org/wiki/QNH)) and sea level air pressure returns the altitude in meters, i.e. altimeter function. #### Syntax `bme280_math.altitude([self], P, QNH)` #### Parameters - (optional) `self` userdata or table structure so that the function can be directly called as object method, parameter is ignored in the calculation - `P` measured pressure - `QNH` current sea level pressure #### Returns altitude in meters of measurement point ## bme280_math.dewpoint() For given temperature and relative humidity returns the dew point in celsius. #### Syntax `bme280_math.dewpoint([self], H, T)` #### Parameters - (optional) `self` userdata or table structure so that the function can be directly called as object method, parameter is ignored in the calculation - `H` relative humidity in percent (100 means 100%) - `T` temperate in celsius #### Returns dew point in celsisus ## bme280_math.qfe2qnh() For given altitude converts the air pressure to sea level air pressure ([QNH](https://en.wikipedia.org/wiki/QNH)). #### Syntax `bme280_math.qfe2qnh([self], P, altitude)` #### Parameters - (optional) `self` userdata or table structure so that the function can be directly called as object method, parameter is ignored in the calculation - `P` measured pressure - `altitude` altitude in meters of measurement point #### Returns sea level pressure ## bme280_math.read() Reads the sensor and returns the temperature, the air pressure, the air relative humidity and see level air pressure when `altitude` is specified. #### Syntax `bme280_math.read(bme280sensor, registers, [altitude])` #### Parameters - `bme280sensor` - BME280 sensor user data returned by `bme280_math.setup()` - `registers` - string of 8 bytes (chars) registers read from `BME280_REGISTER_PRESS` - (optional) `altitude`- altitude in meters of measurement point. If provided also the air pressure converted to sea level air pressure is returned. #### Returns - `T` temperature in celsius - `P` air pressure in hectopascals - `H` relative humidity in percent - (optional) `QNH` air pressure in hectopascals Returns `nil` if the conversion is not successful. ## bme280_math.setup() Initializes module. Initialization is mandatory before read values. #### Syntax `bme280_math.setup(registers, [temp_oss, press_oss, humi_oss, power_mode, inactive_duration, IIR_filter])` #### Parameters - registers - String of configuration registers read from the BME280 sensor. It consists of 6 bytes (chars) of `BME280_REGISTER_DIG_T`, 18 bytes (chars) `BME280_REGISTER_DIG_P` and optional (not present for BMP280 sensor) 8 bytes (chars) of `BME280_REGISTER_DIG_H1` (1 byte) and `BME280_REGISTER_DIG_H2` (7 bytes) - (optional) `temp_oss` - Controls oversampling of temperature data. Default oversampling is 16x. - (optional) `press_oss` - Controls oversampling of pressure data. Default oversampling is 16x. - (optional) `humi_oss` - Controls oversampling of humidity data. Default oversampling is 16x - (optional) `sensor_mode` - Controls the sensor mode of the device. Default sensor more is normal. - (optional) `inactive_duration` - Controls inactive duration in normal mode. Default inactive duration is 20ms. - (optional) `IIR_filter` - Controls the time constant of the IIR filter. Default filter coefficient is 16. |`temp_oss`, `press_oss`, `humi_oss`|Data oversampling| |-----|-----------------| |0|Skipped (output set to 0x80000)| |1|oversampling ×1| |2|oversampling ×2| |3|oversampling ×4| |4|oversampling ×8| |**5**|**oversampling ×16**| |`sensor_mode`|Sensor mode| |-----|-----------------| |0|Sleep mode| |1 and 2|Forced mode| |**3**|**Normal mode**| Using forced mode is recommended for applications which require low sampling rate or hostbased synchronization. The sensor enters into sleep mode after a forced readout. Please refer to BME280 Final Datasheet for more details. |`inactive_duration`|t standby (ms)| |-----|-----------------| |0|0.5| |1|62.5| |2|125| |3|250| |4|500| |5|1000| |6|10| |**7**|**20**| |`IIR_filter`|Filter coefficient | |-----|-----------------| |0|Filter off| |1|2| |2|4| |3|8| |**4**|**16**| #### Returns - `bme280sensor` user data (`nil` if initialization has failed) - `config` 3 (2 for BME280) field table with configuration parameters to be written to registers `BME280_REGISTER_CONFIG`, `BME280_REGISTER_CONTROL_HUM`, `BME280_REGISTER_CONTROL` consecutively #### Example See [bme280](../lua-modules/bme280.md) Lua module documentation. ## BME280 (selected) registers | name | address | |-------|----------| | BME280_REGISTER_CONTROL | 0xF4 | | BME280_REGISTER_CONTROL_HUM | 0xF2 | | BME280_REGISTER_CONFIG| 0xF5 | | BME280_REGISTER_CHIPID | 0xD0 | | BME280_REGISTER_DIG_T | 0x88 (0x88-0x8D (6)) | | BME280_REGISTER_DIG_P | 0x8E (0x8E-0x9F (18)) | | BME280_REGISTER_DIG_H1 | 0xA1 | | BME280_REGISTER_DIG_H2 | 0xE1 (0xE1-0xE7 (7)) | | BME280_REGISTER_PRESS | 0xF7 (0xF7-0xF9 (8)) | --- ### Modules/Bme680 # BME680 module | Since | Origin / Contributor | Maintainer | Source | | :----- | :-------------------- | :---------- | :------ | | 2017-10-28 | [vsky279](https://github.com/vsky279) | [vsky279](https://github.com/vsky279) | [bme680.c](../../app/modules/bme680.c)| This module provides a simple interface to [BME680](https://www.bosch-sensortec.com/bst/products/all_products/bme680) temperature/air presssure/humidity sensors/air quality sensor (Bosch Sensortec). Compared to the BME280 module the sensor does not support automatic mode which means that it can be setup to perform regular measurements. Every measurement has to be triggered manually. In order to measure the air quality the sensor needs to be heated first. In the example provided by the manufacturer the sensor is heated to 300 degrees centigrade for a period of 200 ms and then the measurement is taken. These values are taken as default values in this implementation. I have not tested the impact of different temperatures and heating times on the measurement. This module is able to measure the gas resistance (see Bosch's datasheet). The gas resistance is not the IAQ (Indoor Air Quality) Index. But apparently it can be used as some proxy. The value still should somehow reflect the air quality. It seems that the higher value the air quality is better. The algorithm for IAQ calculation from the gas restistances (probably measured at different temperatures) is not publicly available. Bosch says that at this point of time the calculations for the Indoor Air Quality index are offered only as a pre-compiled library (see discussion here: [BoschSensortec/BME680_driver#6](https://github.com/BoschSensortec/BME680_driver/issues/6)). It is available as the [BSEC Library](https://www.bosch-sensortec.com/bst/products/all_products/bsec). The algorithm is implemented in the library `bsec/algo/bin/ESP8266/libalgobsec.a`. Unfortunately I did not even manage to run the Bosch BSEC example on ESP8266 using this library. ## bme680.altitude() For given air pressure and sea level air pressure returns the altitude in meters as an integer multiplied with 100, i.e. altimeter function. #### Syntax `bme680.altitude(P, QNH)` #### Parameters - `P` measured pressure - `QNH` current sea level pressure #### Returns altitude in meters of measurement point ## bme680.dewpoint() For given temperature and relative humidity returns the dew point in Celsius as an integer multiplied with 100. #### Syntax `bme680.dewpoint(H, T)` #### Parameters - `H` relative humidity in percent multiplied by 1000. - `T` temperate in Celsius multiplied by 100. #### Returns dew point in Celsius ## bme680.qfe2qnh() For given altitude converts the air pressure to sea level air pressure. #### Syntax `bme680.qfe2qnh(P, altitude)` #### Parameters - `P` measured pressure - `altitude` altitude in meters of measurement point #### Returns sea level pressure ## bme680.read() Reads the sensor and returns the temperature, the air pressure, the air relative humidity and #### Syntax `bme680.read([altitude])` #### Parameters - (optional) `altitude`- altitude in meters of measurement point. If provided also the air pressure converted to sea level air pressure is returned. #### Returns - `T` temperature in Celsius as an integer multiplied with 100 - `P` air pressure in hectopascals multiplied by 100 - `H` relative humidity in percent multiplied by 1000 - `G` gas resistance - `QNH` air pressure in hectopascals multiplied by 100 converted to sea level Any of these variables is `nil` if the readout of given measure was not successful. The measured values can be read only once. Following attempts to read values will return nil. A new `startreadout()` needs to be called first before next `read()`. ## bme680.startreadout() Starts readout (turns the sensor into forced mode). After the readout the sensor turns to sleep mode. #### Syntax `bme680.startreadout(delay, callback)` #### Parameters - `delay` sets sensor to forced mode and calls the `callback` (if provided) after given number of milliseconds. For 0 the default delay is calculated by the [formula provided by Bosch](https://github.com/BoschSensortec/BME680_driver/blob/2a51b9c0c1899f28e561e6701caa22cb23201cfc/bme680.c#L586). Apparently for certain combinations of oversamplings setup the the delay returned by the formula is not sufficient and the readout is not ready (make sure you are not reading the previous measurement). For default parameters (2x, 16x, 1x) the calculated delay is 121 ms while in reality 150 ms are needed to get the result. - `callback` if provided it will be invoked after given `delay`. The sensor reading should be finalized by then so. #### Returns `nil` ## bme680.setup() Initializes module. Initialization is mandatory before read values. #### Syntax `bme680.setup([temp_oss, press_oss, humi_oss, heater_temp, heater_duration, IIR_filter, cold_start])` #### Parameters - (optional) `temp_oss` - Controls oversampling of temperature data. Default oversampling is 2x. - (optional) `press_oss` - Controls oversampling of pressure data. Default oversampling is 16x. - (optional) `humi_oss` - Controls oversampling of humidity data. Default oversampling is 1x - (optional) `heater_temp` - - (optional) `heater_duration` - - (optional) `IIR_filter` - Controls the time constant of the IIR filter. Default filter coefficient is 31. - (optional) `cold_start` - If 0 then the bme680 chip is not initialised. Useful in a battery operated setup when the ESP deep sleeps and on wakeup needs to initialise the driver (the module) but not the chip itself. The chip was kept powered (sleeping too) and is holding the latest reading that should be fetched quickly before another reading starts (`bme680.startreadout()`). By default the chip is initialised. |`temp_oss`, `press_oss`, `humi_oss`|Data oversampling| |-----|-----------------| |0|Skipped (output set to 0x80000)| |1|oversampling ×1| |2|oversampling ×2| |3|oversampling ×4| |4|oversampling ×8| |5|oversampling ×16| |`IIR_filter`|Filter coefficient | |-----|-----------------| |0|Filter off| |1|1| |2|3| |3|7| |4|15| |5|31| |6|63| |7|127| #### Returns `nil` if initialization has failed (no sensor connected?) #### Example ```lua alt=320 -- altitude of the measurement place sda, scl = 3, 4 i2c.setup(0, sda, scl, i2c.SLOW) -- call i2c.setup() only once bme680.setup() -- delay calculated by formula provided by Bosch: 121 ms, minimum working (empirical): 150 ms bme680.startreadout(150, function () T, P, H, G, QNH = bme680.read(alt) if T then local Tsgn = (T < 0 and -1 or 1); T = Tsgn*T print(string.format("T=%s%d.%02d", Tsgn<0 and "-" or "", T/100, T%100)) print(string.format("QFE=%d.%03d", P/100, P%100)) print(string.format("QNH=%d.%03d", QNH/100, QNH%100)) print(string.format("humidity=%d.%03d%%", H/1000, H%1000)) print(string.format("gas resistance=%d", G)) D = bme680.dewpoint(H, T) local Dsgn = (D < 0 and -1 or 1); D = Dsgn*D print(string.format("dew_point=%s%d.%02d", Dsgn<0 and "-" or "", D/100, D%100)) end end) ``` --- ### Modules/Bmp085 # BMP085 Module | Since | Origin / Contributor | Maintainer | Source | | :----- | :-------------------- | :---------- | :------ | | 2015-08-03 | [Konrad Beckmann](https://github.com/kbeckmann) | [Konrad Beckmann](https://github.com/kbeckmann) | [bmp085.c](../../app/modules/bmp085.c)| This module provides access to the [BMP085](https://www.sparkfun.com/tutorials/253) temperature and pressure sensor. The module also works with BMP180. ## bmp085.setup() Initializes the module. #### Syntax `bmp085.setup()` #### Parameters None #### Returns `nil` ## bmp085.temperature() Samples the sensor and returns the temperature in celsius as an integer multiplied with 10. #### Syntax `bmp085.temperature()` #### Returns temperature multiplied with 10 (integer) #### Example ```lua local sda, scl = 1, 2 i2c.setup(0, sda, scl, i2c.SLOW) bmp085.setup() local t = bmp085.temperature() print(string.format("Temperature: %s.%s degrees C", t / 10, t % 10)) ``` ## bmp085.pressure() Samples the sensor and returns the pressure in pascal as an integer. The optional `oversampling_setting` parameter determines for how long time the sensor samples data. The default is `3` which is the longest sampling setting. Possible values are 0, 1, 2, 3. See the data sheet for more information. #### Syntax `bmp085.pressure(oversampling_setting)` #### Parameters `oversampling_setting` integer that can be 0, 1, 2 or 3 #### Returns pressure in pascals (integer) #### Example ```lua local sda, scl = 1, 2 i2c.setup(0, sda, scl, i2c.SLOW) -- call i2c.setup() only once bmp085.setup() local p = bmp085.pressure() print(string.format("Pressure: %s.%s mbar", p / 100, p % 100)) ``` ## bmp085.pressure_raw() Samples the sensor and returns the raw pressure in internal units. Might be useful if you need higher precision. #### Syntax `bmp085.pressure_raw(oversampling_setting)` #### Parameters `oversampling_setting` integer that can be 0, 1, 2 or 3 #### Returns raw pressure sampling value (integer) --- ### Modules/Cjson # CJSON Module This module has been replaced by [sjson](sjson.md). It provides a superset of functionality. All references to `cjson` can be replaced by `sjson`. --- ### Modules/Coap # CoAP Module | Since | Origin / Contributor | Maintainer | Source | | :----- | :-------------------- | :---------- | :------ | | 2015-02-04 | Toby Jaffey , [Zeroday](https://github.com/funshine) | [Zeroday](https://github.com/funshine) | [coap.c](../../app/modules/coap.c) | The CoAP module provides a simple implementation according to [CoAP](http://tools.ietf.org/html/rfc7252) protocol. The basic endpoint server part is based on [microcoap](https://github.com/1248/microcoap), and many other code reference [libcoap](https://github.com/obgm/libcoap). This module implements both the client and the server side. GET/PUT/POST/DELETE is partially supported by the client. Server can register Lua functions and variables. No observe or discover supported yet. !!! caution This module is only in the very early stages and not complete yet. ## Constants Constants for various functions. `coap.CON`, `coap.NON` represent the request types. `coap.TEXT_PLAIN`, `coap.LINKFORMAT`, `coap.XML`, `coap.OCTET_STREAM`, `coap.EXI`, `coap.JSON` represent content types. ## coap.Client() Creates a CoAP client. #### Syntax `coap.Client()` #### Parameters none #### Returns CoAP client #### Example ```lua cc = coap.Client() -- assume there is a coap server at ip 192.168.100 cc:get(coap.CON, "coap://192.168.18.100:5683/.well-known/core") -- GET is not complete, the result/payload only print out in console. cc:post(coap.NON, "coap://192.168.18.100:5683/", "Hello") ``` ## coap.Server() Creates a CoAP server. #### Syntax `coap.Server()` #### Parameters none #### Returns CoAP server #### Example ```lua -- use copper addon for firefox cs=coap.Server() cs:listen(5683) myvar=1 cs:var("myvar") -- get coap://192.168.18.103:5683/v1/v/myvar will return the value of myvar: 1 all='[1,2,3]' cs:var("all", coap.JSON) -- sets content type to json -- function should tack one string, return one string. function myfun(payload) print("myfun called") respond = "hello" return respond end cs:func("myfun") -- post coap://192.168.18.103:5683/v1/f/myfun will call myfun ``` # CoAP Client ## coap.client:get() Issues a GET request to the server. #### Syntax `coap.client:get(type, uri[, payload])` #### Parameters - `type` `coap.CON`, `coap.NON`, defaults to CON. If the type is CON and request fails, the library retries four more times before giving up. - `uri` the URI such as "coap://192.168.18.103:5683/v1/v/myvar", only IP addresses are supported i.e. no hostname resoltion. - `payload` optional, the payload will be put in the payload section of the request. #### Returns `nil` ## coap.client:put() Issues a PUT request to the server. #### Syntax `coap.client:put(type, uri[, payload])` #### Parameters - `type` `coap.CON`, `coap.NON`, defaults to CON. If the type is CON and request fails, the library retries four more times before giving up. - `uri` the URI such as "coap://192.168.18.103:5683/v1/v/myvar", only IP addresses are supported i.e. no hostname resoltion. - `payload` optional, the payload will be put in the payload section of the request. #### Returns `nil` ## coap.client:post() Issues a POST request to the server. #### Syntax `coap.client:post(type, uri[, payload])` #### Parameters - `type` coap.CON, coap.NON, defaults to CON. when type is CON, and request failed, the request will retry another 4 times before giving up. - `uri` the uri such as coap://192.168.18.103:5683/v1/v/myvar, only IP is supported. - `payload` optional, the payload will be put in the payload section of the request. #### Returns `nil` ## coap.client:delete() Issues a DELETE request to the server. #### Syntax `coap.client:delete(type, uri[, payload])` #### Parameters - `type` `coap.CON`, `coap.NON`, defaults to CON. If the type is CON and request fails, the library retries four more times before giving up. - `uri` the URI such as "coap://192.168.18.103:5683/v1/v/myvar", only IP addresses are supported i.e. no hostname resoltion. - `payload` optional, the payload will be put in the payload section of the request. #### Returns `nil` # CoAP Server ## coap.server:listen() Starts the CoAP server on the given port. #### Syntax `coap.server:listen(port[, ip])` #### Parameters - `port` server port (number) - `ip` optional IP address #### Returns `nil` ## coap.server:close() Closes the CoAP server. #### Syntax `coap.server:close()` #### Parameters none #### Returns `nil` ## coap.server:var() Registers a Lua variable as an endpoint in the server. the variable value then can be retrieved by a client via GET method, represented as an [URI](http://tools.ietf.org/html/rfc7252#section-6) to the client. The endpoint path for variable is '/v1/v/'. #### Syntax `coap.server:var(name[, content_type])` #### Parameters - `name` the Lua variable's name - `content_type` optional, defaults to `coap.TEXT_PLAIN`, see [Content Negotiation](http://tools.ietf.org/html/rfc7252#section-5.5.4) #### Returns `nil` #### Example ```lua -- use copper addon for firefox cs=coap.Server() cs:listen(5683) myvar=1 cs:var("myvar") -- get coap://192.168.18.103:5683/v1/v/myvar will return the value of myvar: 1 -- cs:var(myvar), WRONG, this api accept the name string of the varialbe. but not the variable itself. all='[1,2,3]' cs:var("all", coap.JSON) -- sets content type to json ``` ## coap.server:func() Registers a Lua function as an endpoint in the server. The function then can be called by a client via POST method. represented as an [URI](http://tools.ietf.org/html/rfc7252#section-6) to the client. The endpoint path for function is '/v1/f/'. When the client issues a POST request to this URI, the payload will be passed to the function as parameter. The function's return value will be the payload in the message to the client. The function registered SHOULD accept ONLY ONE string type parameter, and return ONE string value or return nothing. #### Syntax `coap.server:func(name[, content_type])` #### Parameters - `name` the Lua function's name - `content_type` optional, defaults to `coap.TEXT_PLAIN`, see [Content Negotiation](http://tools.ietf.org/html/rfc7252#section-5.5.4) #### Returns `nil` #### Example ```lua -- use copper addon for firefox cs=coap.Server() cs:listen(5683) -- function should take only one string, return one string. function myfun(payload) print("myfun called") respond = "hello" return respond end cs:func("myfun") -- post coap://192.168.18.103:5683/v1/f/myfun will call myfun -- cs:func(myfun), WRONG, this api accept the name string of the function. but not the function itself. ``` --- ### Modules/Color Utils # color utils Module | Since | Origin / Contributor | Maintainer | Source | | :----- | :-------------------- | :---------- | :------ | | 2017-12-30 | [Konrad Huebner](https://github.com/skycoders) | [Konrad Huebner](https://github.com/skycoders) | [color_utils.c](../../app/modules/color_utils.c)| This module provides basic color transformations useful for color LEDs. ## color_utils.hsv2grb() Convert HSV color to GRB color. #### Syntax `color_utils.hsv2grb(hue, saturation, value)` #### Parameters - `hue` is the hue value, between 0 and 360 - `saturation` is the saturation value, between 0 and 255 - `value` is the value value, between 0 and 255 #### Returns `green`, `red`, `blue` as values between 0 and 255 ## color\_utils.hsv2grbw() Convert HSV color to GRB color and explicitly return a white value. This can be useful for RGB+W LED strips. The white value is simply calculated as min(g, r, b) and then removed from the colors. This does NOT take into account if the white chip used later creates an appropriate color. #### Syntax `color_utils.hsv2grbw(hue, saturation, value)` #### Parameters - `hue` is the hue value, between 0 and 360 - `saturation` is the saturation value, between 0 and 255 - `value` is the value value, between 0 and 255 #### Returns `green`, `red`, `blue`, `white` as values between 0 and 255 ## color\_utils.grb2hsv() Convert GRB color to HSV color. #### Syntax `color_utils.grb2hsv(green, red, blue)` #### Parameters - `green` is the green value, between 0 and 255 - `red` is the red value, between 0 and 255 - `blue` is the blue value, between 0 and 255 #### Returns `hue`, `saturation`, `value` as values between 0 and 360, respective 0 and 255 ## color\_utils.colorWheel() The color wheel function makes use of the HSV color space and calculates colors based on the color circle. The colors are created with full saturation and value. This function is a convenience function of the hsv2grb function and can be used to create rainbow colors. #### Syntax `color_utils.colorWheel(angle)` #### Parameters - `angle` is the angle on the color circle, between 0 and 359 #### Returns `green`, `red`, `blue` as values between 0 and 255 --- ### Modules/Cron # Cron Module | Since | Origin / Contributor | Maintainer | Source | | :----- | :-------------------- | :---------- | :------ | | 2016-12-18 | [PhoeniX](https://github.com/djphoenix) | [PhoeniX](https://github.com/djphoenix) | [cron.c](../../app/modules/cron.c)| [Cron](https://en.wikipedia.org/wiki/Cron)-like scheduler module. !!! important This module needs RTC time to operate correctly. Do not forget to include the [`rtctime`](rtctime.md) module **and** initialize it properly. !!! important The cron expression has to be in GMT/UTC! ## cron.schedule() Creates a new schedule entry. #### Syntax `cron.schedule(mask, callback)` #### Parameters - `mask` - [crontab](https://en.wikipedia.org/wiki/Cron#Overview)-like string mask for schedule - `callback` - callback `function(entry)` that is executed at the scheduled time #### Returns `cron.entry` sub module #### Example ```lua cron.schedule("* * * * *", function(e) print("Every minute") end) cron.schedule("*/5 * * * *", function(e) print("Every 5 minutes") end) cron.schedule("0 */2 * * *", function(e) print("Every 2 hours") end) ``` ## cron.reset() Removes all scheduled entries. #### Syntax `cron.reset()` #### Parameters none #### Returns nil # cron.entry Module ## cron.entry:handler() Sets a new handler for entry. #### Syntax `handler(callback)` #### Parameters - `callback` - callback `function(entry)` that is executed at the scheduled time #### Returns nil #### Example ```lua ent = cron.schedule("* * * * *", function(e) print("Every minute") end) ent:handler(function(e) print("New handler: Every minute") end) ``` ## cron.entry:schedule() Sets a new schedule mask. #### Syntax `schedule(mask)` #### Parameters - `mask` - [crontab](https://en.wikipedia.org/wiki/Cron#Overview)-like string mask for schedule #### Returns none #### Example ```lua ent = cron.schedule("* * * * *", function(e) print("Tick") end) -- Every 5 minutes is really better! ent:schedule("*/5 * * * *") ``` ## cron.entry:unschedule() Disables schedule. Disabled schedules may be enabled again by calling [`:schedule(mask)`](cron.md#cronentryschedule). #### Syntax `unschedule()` #### Parameters none #### Returns nil #### Example ```lua ent = cron.schedule("* * * * *", function(e) print("Tick") end) -- We don't need this anymore ent:unschedule() ``` --- ### Modules/Crypto # crypto Module | Since | Origin / Contributor | Maintainer | Source | | :----- | :-------------------- | :---------- | :------ | | 2015-06-02 | [DiUS](https://github.com/DiUS), [Johny Mattsson](https://github.com/jmattsson) | [Johny Mattsson](https://github.com/jmattsson) | [crypto.c](../../app/modules/crypto.c)| The crypto modules provides various functions for working with cryptographic algorithms. The following encryption/decryption algorithms/modes are supported: - `"AES-ECB"` for 128-bit AES in ECB mode (NOT recommended) - `"AES-CBC"` for 128-bit AES in CBC mode The following hash algorithms are supported: - MD5 - SHA1 - SHA256, SHA384, SHA512 (unless disabled in `app/include/user_config.h`) ## crypto.encrypt() Encrypts Lua strings. #### Syntax `crypto.encrypt(algo, key, plain [, iv])` #### Parameters - `algo` the name of a supported encryption algorithm to use - `key` the encryption key as a string; for AES encryption this *MUST* be 16 bytes long - `plain` the string to encrypt; it will be automatically zero-padded to a 16-byte boundary if necessary - `iv` the initilization vector, if using AES-CBC; defaults to all-zero if not given #### Returns The encrypted data as a binary string. For AES this is always a multiple of 16 bytes in length. #### Example ```lua print(encoder.toHex(crypto.encrypt("AES-ECB", "1234567890abcdef", "Hi, I'm secret!"))) ``` #### See also - [`crypto.decrypt()`](#cryptodecrypt) ## crypto.decrypt() Decrypts previously encrypted data. #### Syntax `crypto.decrypt(algo, key, cipher [, iv])` #### Parameters - `algo` the name of a supported encryption algorithm to use - `key` the encryption key as a string; for AES encryption this *MUST* be 16 bytes long - `cipher` the cipher text to decrypt (as obtained from `crypto.encrypt()`) - `iv` the initialization vector, if using AES-CBC; defaults to all-zero if not given #### Returns The decrypted string. Note that the decrypted string may contain extra zero-bytes of padding at the end. One way of stripping such padding is to use `:match("(.-)%z*$")` on the decrypted string. Additional care needs to be taken if working on binary data, in which case the real length likely needs to be encoded with the data, and at which point `:sub(1, n)` can be used to strip the padding. #### Example ```lua key = "1234567890abcdef" cipher = crypto.encrypt("AES-ECB", key, "Hi, I'm secret!") print(encoder.toHex(cipher)) print(crypto.decrypt("AES-ECB", key, cipher)) ``` #### See also - [`crypto.encrypt()`](#cryptoencrypt) ## crypto.fhash() Compute a cryptographic hash of a a file. #### Syntax `hash = crypto.fhash(algo, filename)` #### Parameters - `algo` the hash algorithm to use, case insensitive string - `filename` the path to the file to hash #### Returns A binary string containing the message digest. To obtain the textual version (ASCII hex characters), please use [`encoder.toHex()`](encoder.md#encodertohex ). #### Example ```lua print(encoder.toHex(crypto.fhash("sha1","myfile.lua"))) ``` ## crypto.hash() Compute a cryptographic hash of a Lua string. #### Syntax `hash = crypto.hash(algo, str)` #### Parameters `algo` the hash algorithm to use, case insensitive string `str` string to hash contents of #### Returns A binary string containing the message digest. To obtain the textual version (ASCII hex characters), please use [`encoder.toHex()`](encoder.md#encodertohex). #### Example ```lua print(encoder.toHex(crypto.hash("sha1","abc"))) ``` ## crypto.new_hash() Create a digest/hash object that can have any number of strings added to it. Object has `update` and `finalize` functions. #### Syntax `hashobj = crypto.new_hash(algo)` #### Parameters `algo` the hash algorithm to use, case insensitive string #### Returns Userdata object with `update` and `finalize` functions available. #### Example ```lua hashobj = crypto.new_hash("SHA1") hashobj:update("FirstString") hashobj:update("SecondString") digest = hashobj:finalize() print(encoder.toHex(digest)) ``` ## crypto.hmac() Compute a [HMAC](https://en.wikipedia.org/wiki/Hash-based_message_authentication_code) (Hashed Message Authentication Code) signature for a Lua string. #### Syntax `signature = crypto.hmac(algo, str, key)` #### Parameters - `algo` hash algorithm to use, case insensitive string - `str` data to calculate the hash for - `key` key to use for signing, may be a binary string #### Returns A binary string containing the HMAC signature. Use [`encoder.toHex()`](encoder.md#encodertohex) to obtain the textual version. #### Example ```lua print(encoder.toHex(crypto.hmac("sha1","abc","mysecret"))) ``` ## crypto.new_hmac() Create a hmac object that can have any number of strings added to it. Object has `update` and `finalize` functions. #### Syntax `hmacobj = crypto.new_hmac(algo, key)` #### Parameters - `algo` the hash algorithm to use, case insensitive string - `key` the key to use (may be a binary string) #### Returns Userdata object with `update` and `finalize` functions available. #### Example ```lua hmacobj = crypto.new_hmac("SHA1", "s3kr3t") hmacobj:update("FirstString") hmacobj:update("SecondString") digest = hmacobj:finalize() print(encoder.toHex(digest)) ``` ## crypto.mask() Applies an XOR mask to a Lua string. Note that this is not a proper cryptographic mechanism, but some protocols may use it nevertheless. #### Syntax `crypto.mask(message, mask)` #### Parameters - `message` message to mask - `mask` the mask to apply, repeated if shorter than the message #### Returns The masked message, as a binary string. Use [`encoder.toHex()`](encoder.md#encodertohex) to get a textual representation of it. #### Example ```lua print(encoder.toHex(crypto.mask("some message to obscure","X0Y7"))) ``` --- ### Modules/Dcc # DCC module | Since | Origin / Contributor | Maintainer | Source | | :----- | :-------------------- | :---------- | :------ | | 2019-12-28 | [vsky279](https://github.com/vsky279) | [vsky279](https://github.com/vsky279) | [dcc.c](../../app/modules/dcc.c)| The dcc module implements decoder of the [National Model Railroad Association](https://www.nmra.org/) (NMRA) Digital Command Control (DCC) decoder - see [DCC wiki](https://dccwiki.com/Introduction_to_DCC) for details. The hardware needed to decode the DCC signal can be built based on different DCC decoders implementation for Arduino, for inspiration see [https://mrrwa.org/dcc-decoder-interface/](https://mrrwa.org/dcc-decoder-interface/). Basically the signal from the DCC bus is connected via an optocoupler to any GPIO pin. The DCC bus can be also used to power the ESP. The module is based on the project NmraDcc [https://github.com/mrrwa/NmraDcc](https://github.com/mrrwa/NmraDcc) by Alex Shepherd (@kiwi64ajs). The module is based on latest commit from Oct 2020, commit [7e3b3e346991d74926e6c4f6fb46e27156b08578](https://github.com/mrrwa/NmraDcc/tree/7e3b3e346991d74926e6c4f6fb46e27156b08578). ## dcc.setup() Initializes the dcc module and links callback functions. #### Syntax `dcc.setup(Pin, [AckPin, ] DCC_command, ManufacturerId, VersionId, Flags, OpsModeAddressBaseCV [, CV_table] [, CV_callback])` #### Parameters - `Pin` the GPIO pin number connected to the DCC detector (must be interrupt capable pin). - `AckPin` the (optional) GPIO pin number connected to the ACK mechanism. Will be set HIGH to signal an ACK. - `DCC_command(cmd, params)` calllback function that is called when a DCC command is decoded. `cmd` parameters is one of the following values. `params` contains a collection of parameters specific to given command. - `dcc.DCC_RESET` no additional parameters, `params` is `nil`. - `dcc.DCC_IDLE` no additional parameters, `params` is `nil`. - `dcc.DCC_SPEED` parameters collection members are `Addr`, `AddrType`, `Speed`, `Dir`, `SpeedSteps`. - `dcc.DCC_SPEED_RAW` parameters collection members are `Addr`, `AddrType`, `Raw`. - `dcc.DCC_FUNC` parameters collection members are `Addr`, `AddrType`, `FuncGrp`, `FuncState`. - `dcc.DCC_TURNOUT` parameters collection members are `BoardAddr`, `OutputPair`, `Direction`, `OutputPower` or `Addr`, `Direction`, `OutputPower`. - `dcc.DCC_ACCESSORY` parameters collection has one member `BoardAddr` or `Addr` or `State`. - `dcc.DCC_RAW` parameters collection member are `Size`, `PreambleBits`, `Data1` to `Data6`. - `dcc.DCC_SERVICEMODE` parameters collection has one member `InServiceMode`. - `ManufacturerId` Manufacturer ID returned in CV 8. Commonly `dcc.MAN_ID_DIY`. - `VersionId` Version ID returned in CV 7. - `Flags` one of or combination (OR operator) of - `dcc.FLAGS_MY_ADDRESS_ONLY`Only process packets with My Address. - `dcc.FLAGS_DCC_ACCESSORY_DECODER` Decoder is an accessory decode. - `dcc.FLAGS_OUTPUT_ADDRESS_MODE` This flag applies to accessory decoders only. Accessory decoders normally have 4 paired outputs and a single address refers to all 4 outputs. Setting this flag causes each address to refer to a single output. - `dcc.FLAGS_AUTO_FACTORY_DEFAULT` Call DCC command callback with `dcc.CV_RESET` command if CV 7 & 8 == 255. - `OpsModeAddressBaseCV` Ops Mode base address. Set it to 0? - `CV_table` The CV values will be directly accessed from this table. metamethods will be invoked if needed. Any errors thrown will cause the CV to be considered invalid. Using this option will prevent `CV_VALID`, `CV_READ`, `CV_WRITE` and `CV_ACK_COMPLETE` from happening. - `CV_callback(operation, param)` callback function that is called when any manipulation with CV ([Configuarion Variable](https://dccwiki.com/Configuration_Variable)) is requested. - `dcc.CV_VALID` to determine if a given CV is valid and (possibly) writable. This callback must determine if a CV is readable or writable and return the appropriate value(0/1/true/false). The `param` collection has members `CV` and `Writable`. - `dcc.CV_READ` to read a CV. This callback must return the value of the CV. The `param` collection has one member `CV` determing the CV number to be read. - `dcc.CV_WRITE` to write a value to a CV. This callback must write the Value to the CV and return the value of the CV. The `param` collection has members `CV` and `Value`. Ideally, the final value should be returned -- this may differ from the requested value. - `dcc.CV_RESET` Called when CVs must be reset to their factory defaults. - `dcc.CV_ACK_COMPLETE` Called when an ACK pulse has finished being sent. Only invoked if `AckPin` is specified. #### Returns `nil` #### Example `bit` module is used in the example though it is not needed for the dcc module functionality. ``` /* Detailed source-code truncated for AI context efficiency. */ ``` ## dcc.close() Stops the dcc module. #### Syntax `dcc.close()` #### Parameters `nil` #### Returns `nil` --- ### Modules/Dht # DHT Module | Since | Origin / Contributor | Maintainer | Source | | :----- | :-------------------- | :---------- | :------ | | 2015-06-17 | [RobTillaart](https://github.com/RobTillaart/Arduino/tree/master/libraries/DHTlib) | [Vowstar](https://github.com/vowstar) | [dhtlib](../../app/dht/)| ## Constants Constants for various functions. `dht.OK`, `dht.ERROR_CHECKSUM`, `dht.ERROR_TIMEOUT` represent the potential values for the DHT read status ## dht.read() Reads all kinds of DHT sensors, including DHT11, 21, 22, 33, 44 humidity temperature combo sensor. Returns correct readout except for DHT12 and negative temperatures by DHT11. Use [`dht.read12()`](#dhtread12) and [`dht.read11()`](#dhtread11) instead. It is to use model specific read function anyway. #### Syntax `dht.read(pin)` #### Parameters `pin` pin number of DHT sensor (can't be 0), type is number #### Returns - `status` as defined in Constants - `temp` temperature (see note below) - `humi` humidity (see note below) - `temp_dec` temperature decimal - `humi_dec` humidity decimal !!! note If using float firmware then `temp` and `humi` are floating point numbers. On an integer firmware, the final values have to be concatenated from `temp` and `temp_dec` / `humi` and `hum_dec`. #### Example ```lua pin = 5 status, temp, humi, temp_dec, humi_dec = dht.read(pin) if status == dht.OK then -- Integer firmware using this example print(string.format("DHT Temperature:%d.%03d;Humidity:%d.%03d\r\n", math.floor(temp), temp_dec, math.floor(humi), humi_dec )) -- Float firmware using this example print("DHT Temperature:"..temp..";".."Humidity:"..humi) elseif status == dht.ERROR_CHECKSUM then print( "DHT Checksum error." ) elseif status == dht.ERROR_TIMEOUT then print( "DHT timed out." ) end ``` ## dht.read11() Read DHT11 humidity temperature combo sensor. #### Syntax `dht.read11(pin)` #### Parameters `pin` pin number of DHT11 sensor (can't be 0), type is number #### Returns - `status` as defined in Constants - `temp` temperature (see note below) - `humi` humidity (see note below) - `temp_dec` temperature decimal - `humi_dec` humidity decimal !!! note If using float firmware then `temp` and `humi` are floating point numbers. On an integer firmware, the final values have to be concatenated from `temp` and `temp_dec` / `humi` and `hum_dec`. #### See also [dht.read()](#dhtread) ## dht.read12() Read DHT12 humidity temperature combo sensor. #### Syntax `dht.read12(pin)` #### Parameters `pin` pin number of DHT12 sensor (can't be 0), type is number #### Returns - `status` as defined in Constants - `temp` temperature (see note below) - `humi` humidity (see note below) - `temp_dec` temperature decimal - `humi_dec` humidity decimal !!! note If using float firmware then `temp` and `humi` are floating point numbers. On an integer firmware, the final values have to be concatenated from `temp` and `temp_dec` / `humi` and `hum_dec`. #### See also [dht.read()](#dhtread) ## dht.readxx() Read all kinds of DHT sensors, except DHT11 and DHT12. Differs from `dht.read()` only by waiting only sufficient 1 ms for sensor wake-up while `dht.read()` waits universal 18 ms. ####Syntax `dht.readxx(pin)` #### Parameters `pin` pin number of DHT sensor (can't be 0), type is number #### Returns - `status` as defined in Constants - `temp` temperature (see note below) - `humi` humidity (see note below) - `temp_dec` temperature decimal - `humi_dec` humidity decimal !!! note If using float firmware then `temp` and `humi` are floating point numbers. On an integer firmware, the final values have to be concatenated from `temp` and `temp_dec` / `humi` and `hum_dec`. #### See also [dht.read()](#dhtread) --- ### Modules/Encoder # encoder Module | Since | Origin / Contributor | Maintainer | Source | | :----- | :-------------------- | :---------- | :------ | | 2016-02-26 | [Terry Ellison](https://github.com/TerryE) | [Terry Ellison](https://github.com/TerryE) | [encoder.c](../../app/modules/encoder.c)| The encoder modules provides various functions for encoding and decoding byte data. ## encoder.toBase64() Provides a Base64 representation of a (binary) Lua string. #### Syntax `b64 = encoder.toBase64(binary)` #### Parameters `binary` input string to Base64 encode #### Return A Base64 encoded string. #### Example ```lua print(encoder.toBase64(crypto.hash("sha1","abc"))) ``` ## encoder.fromBase64() Decodes a Base64 representation of a (binary) Lua string back into the original string. An error is thrown if the string is not a valid base64 encoding. #### Syntax `binary_string = encoder.fromBase64(b64)` #### Parameters `b64` Base64 encoded input string #### Return The decoded Lua (binary) string. #### Example ```lua print(encoder.fromBase64(encoder.toBase64("hello world"))) ``` ## encoder.toHex() Provides an ASCII hex representation of a (binary) Lua string. Each byte in the input string is represented as two hex characters in the output. #### Syntax `hexstr = encoder.toHex(binary)` #### Parameters `binary` input string to get hex representation for #### Returns An ASCII hex string. #### Example ```lua print(encoder.toHex(crypto.hash("sha1","abc"))) ``` ## encoder.fromHex() Returns the Lua binary string decode of a ASCII hex string. Each byte in the output string is represented as two hex characters in the input. An error is thrown if the string is not a valid base64 encoding. #### Syntax `binary = encoder.fromHex(hexstr)` #### Parameters `hexstr` An ASCII hex string. #### Returns Decoded string of hex representation. #### Example ```lua print(encoder.fromHex("6a6a6a")) ``` --- ### Modules/Enduser Setup # enduser setup Module aka Captive Portal aka WiFi Manager | 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)| 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 "NodeMCU_XXXXXX" will start. This prefix can be overridden in `user_config.h` by defining `ENDUSER_SETUP_AP_SSID` or by supplying the whole SSID to the `enduser_setup.start` method. Connect to that SSID and captive portal detection on the client should automatically open the configuration dialog. If not, 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. 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 ``` Then the `eus_params.lua` file will contain the following: ```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 ``` ### How to use the eus_params.lua file Simply include the file by using the `dofile` function: ```lua p = dofile('eus_params.lua') -- now use the parameters in the Lua table print("Wifi device_name: " .. p.device_name) ``` ### HTTP endpoints: |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}]`| |/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`. | Module functions are described below. ## enduser_setup.manual() 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. #### Syntax `enduser_setup.manual([on_off])` #### Parameters - `on_off` a boolean value indicating whether to use manual mode; if not given, the function only returns the current setting. #### Returns The current setting, true if manual mode is enabled, false if it is not. #### Example ```lua wifi.setmode(wifi.STATIONAP) wifi.ap.config({ssid="MyPersonalSSID", auth=wifi.OPEN}) enduser_setup.manual(true) enduser_setup.start( function() print("Connected to WiFi as:" .. wifi.sta.getip()) end, function(err, str) print("enduser_setup: Err #" .. err .. ": " .. str) end ) ``` ## enduser_setup.start() Starts the captive portal. *Note: Calling start() while EUS is already running is an error, and will result in stop() to be invoked to shut down EUS.* #### Syntax `enduser_setup.start([AP_SSID,] [onConnected()], [onError(err_num, string)], [onDebug(string)])` #### Parameters - `AP_SSID` the (optional) SSID to use for the AP. This defaults to `NodeMCU_`. - `onConnected()` callback will be fired when an IP-address has been obtained, just before the enduser_setup module will terminate itself - `onError()` callback will be fired if an error is encountered. `err_num` is a number describing the error, and `string` contains a description of the error. - `onDebug()` callback is disabled by default (controlled by `#define ENDUSER_SETUP_DEBUG_ENABLE` in `enduser_setup.c`). It is intended to be used to find internal issues in the module. `string` contains a description of what is going on. #### Returns `nil` #### Example ```lua enduser_setup.start( function() print("Connected to WiFi as:" .. wifi.sta.getip()) end, function(err, str) print("enduser_setup: Err #" .. err .. ": " .. str) end, print -- Lua print function can serve as the debug callback ) ``` ## enduser_setup.stop() Stops the captive portal. #### Syntax `enduser_setup.stop()` #### Parameters none #### Returns `nil` --- ### Modules/File # file Module | Since | Origin / Contributor | Maintainer | Source | | :----- | :-------------------- | :---------- | :------ | | 2014-12-22 | [Zeroday](https://github.com/funshine) | [Zeroday](https://github.com/funshine) | [file.c](../../app/modules/file.c)| The file module provides access to the file system and its individual files. The file system is a flat file system, with no notion of subdirectories/folders. Besides the SPIFFS file system on internal flash, this module can also access FAT partitions on an external SD card if [FatFS is enabled](../sdcard.md). ```lua -- open file in flash: if file.open("init.lua") then print(file.read()) file.close() end -- or with full pathspec file.open("/FLASH/init.lua") -- open file on SD card if file.open("/SD0/somefile.txt") then print(file.read()) file.close() end ``` ## file.chdir() Change current directory (and drive). This will be used when no drive/directory is prepended to filenames. Current directory defaults to the root of internal SPIFFS (`/FLASH`) after system start. !!! note Function is only available when [FatFS support](../sdcard.md#enabling-fatfs) is compiled into the firmware. #### Syntax `file.chdir(dir)` #### Parameters `dir` directory name - `/FLASH`, `/SD0`, `/SD1`, etc. #### Returns `true` on success, `false` otherwise ## file.exists() Determines whether the specified file exists. #### Syntax `file.exists(filename)` #### Parameters - `filename` file to check #### Returns true if the file exists (even if 0 bytes in size), and false if it does not exist #### Example ```lua files = file.list() if files["device.config"] then print("Config file exists") end if file.exists("device.config") then print("Config file exists") end ``` #### See also [`file.list()`](#filelist) ## file.format() Format the file system. Completely erases any existing file system and writes a new one. Depending on the size of the flash chip in the ESP, this may take several seconds. !!! note Function is not supported for SD cards. #### Syntax `file.format()` #### Parameters none #### Returns `nil` #### See also [`file.remove()`](#fileremove) ## file.fscfg () Returns the flash address and physical size of the file system area, in bytes. !!! note Function is not supported for SD cards. #### Syntax `file.fscfg()` #### Parameters none #### Returns - `flash address` (number) - `size` (number) #### Example ```lua print(string.format("0x%x", file.fscfg())) ``` ## file.fsinfo() Return size information for the file system. The unit is Byte for SPIFFS and kByte for FatFS. #### Syntax `file.fsinfo()` #### Parameters none #### Returns - `remaining` (number) - `used` (number) - `total` (number) #### Example ```lua -- get file system info remaining, used, total=file.fsinfo() 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() Lists all files in the file system. #### Syntax `file.list([pattern])` #### Parameters - `pattern` only files matching the Lua pattern will be returned #### Returns a Lua table which contains all {file name: file size} pairs, if no pattern given. If a pattern is given, only those file names matching the pattern (interpreted as a traditional [Lua pattern](https://www.lua.org/pil/20.2.html), not, say, a UNIX shell glob) will be included in the resulting table. `file.list` will throw any errors encountered during pattern matching. #### Example ```lua l = file.list(); for k,v in pairs(l) do print("name:"..k..", size:"..v) end ``` ## file.mount() Mounts a FatFs volume on SD card. !!! note Function is only available when [FatFS support](../sdcard.md#enabling-fatfs) is compiled into the firmware and it is not supported for internal flash. #### Syntax `file.mount(ldrv[, pin])` #### Parameters - `ldrv` name of the logical drive, `/SD0`, `/SD1`, etc. - `pin` 1~12, IO index for SS/CS, defaults to 8 if omitted. #### Returns Volume object #### Example ```lua vol = file.mount("/SD0") vol:umount() ``` ## file.on() Registers callback functions. Trigger events are: - `rtc` deliver current date & time to the file system. Function is expected to return a table containing the fields `year`, `mon`, `day`, `hour`, `min`, `sec` of current date and time. Not supported for internal flash. #### Syntax `file.on(event[, function()])` #### Parameters - `event` string - `function()` callback function. Unregisters the callback if `function()` is omitted or `nil`. #### Returns `nil` #### Example ```lua sntp.sync(server_ip, function() print("sntp time sync ok") file.on("rtc", function() return rtctime.epoch2cal(rtctime.get()) end) end) ``` #### See also [`rtctime.epoch2cal()`](rtctime.md#rtctimeepoch2cal) ## file.open() Opens a file for access, potentially creating it (for write modes). When done with the file, it must be closed using `file.close()`. #### Syntax `file.open(filename, mode)` #### Parameters - `filename` file to be opened - `mode`: - "r": read mode (the default) - "w": write mode - "a": append mode - "r+": update mode, all previous data is preserved - "w+": update mode, all previous data is erased - "a+": append update mode, previous data is preserved, writing is only allowed at the end of file #### Returns file object if file opened ok. `nil` if file not opened, or not exists (read modes). #### Example (basic model) ```lua -- open 'init.lua', print the first line. if file.open("init.lua", "r") then print(file.readline()) file.close() end ``` #### Example (object model) ```lua -- open 'init.lua', print the first line. fd = file.open("init.lua", "r") if fd then print(fd:readline()) fd:close(); fd = nil end ``` #### See also - [`file.close()`](#fileclose-fileobjclose) - [`file.readline()`](#filereadline-fileobjreadline) ## file.remove() Remove a file from the file system. The file must not be currently open. ###Syntax `file.remove(filename)` #### Parameters `filename` file to remove #### Returns `nil` #### Example ```lua -- remove "foo.lua" from file system. file.remove("foo.lua") ``` #### See also [`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() Renames a file. If a file is currently open, it will be closed first. #### Syntax `file.rename(oldname, newname)` #### Parameters - `oldname` old file name - `newname` new file name #### Returns `true` on success, `false` on error. #### Example ```lua -- rename file 'temp.lua' to 'init.lua'. file.rename("temp.lua","init.lua") ``` ## file.stat() Get attribtues of a file or directory in a table. Elements of the table are: - `size` file size in bytes - `name` file name - `time` table with time stamp information. Default is 1970-01-01 00:00:00 in case time stamps are not supported (on SPIFFS). - `year` - `mon` - `day` - `hour` - `min` - `sec` - `is_dir` flag `true` if item is a directory, otherwise `false` - `is_rdonly` flag `true` if item is read-only, otherwise `false` - `is_hidden` flag `true` if item is hidden, otherwise `false` - `is_sys` flag `true` if item is system, otherwise `false` - `is_arch` flag `true` if item is archive, otherwise `false` #### Syntax `file.stat(filename)` #### Parameters `filename` file name #### Returns table containing file attributes #### Example ```lua s = file.stat("/SD0/myfile") print("name: " .. s.name) print("size: " .. s.size) t = s.time print(string.format("%02d:%02d:%02d", t.hour, t.min, t.sec)) print(string.format("%04d-%02d-%02d", t.year, t.mon, t.day)) if s.is_dir then print("is directory") else print("is file") end if s.is_rdonly then print("is read-only") else print("is writable") end if s.is_hidden then print("is hidden") else print("is not hidden") end if s.is_sys then print("is system") else print("is not system") end if s.is_arch then print("is archive") else print("is not archive") end s = nil t = nil ``` # File access functions The `file` module provides several functions to access the content of a file after it has been opened with [`file.open()`](#fileopen). They can be used as part of a basic model or an object model: ## Basic model In the basic model there is max one file opened at a time. The file access functions operate on this file per default. If another file is opened, the previous default file needs to be closed beforehand. ```lua -- open 'init.lua', print the first line. if file.open("init.lua", "r") then print(file.readline()) file.close() end ``` ## Object model Files are represented by file objects which are created by `file.open()`. File access functions are available as methods of this object, and multiple file objects can coexist. ```lua src = file.open("init.lua", "r") if src then dest = file.open("copy.lua", "w") if dest then local line repeat line = src:read() if line then dest:write(line) end until line == nil dest:close(); dest = nil end src:close(); dest = nil end ``` !!! Attention It is recommended to use only one single model within the application. Concurrent use of both models can yield unpredictable behavior: Closing the default file from basic model will also close the corresponding file object. Closing a file from object model will also close the default file if they are the same file. !!! Note The maximum number of open files on SPIFFS is determined at compile time by `SPIFFS_MAX_OPEN_FILES` in `user_config.h`. ## file.close(), file.obj:close() Closes the open file, if any. #### Syntax `file.close()` `fd:close()` #### Parameters none #### Returns `nil` #### See also [`file.open()`](#fileopen) ## file.flush(), file.obj:flush() Flushes any pending writes to the file system, ensuring no data is lost on a restart. Closing the open file using [`file.close()` / `fd:close()`](#fileclose-fileobjclose) performs an implicit flush as well. #### Syntax `file.flush()` `fd:flush()` #### Parameters none #### Returns `nil` #### Example (basic model) ```lua -- open 'init.lua' in 'a+' mode if file.open("init.lua", "a+") then -- write 'foo bar' to the end of the file file.write('foo bar') file.flush() -- write 'baz' too file.write('baz') file.close() end ``` #### See also [`file.close()` / `file.obj:close()`](#fileclose-fileobjclose) ## file.read(), file.obj:read() Read content from the open file. !!! note The function temporarily allocates 2 * (number of requested bytes) on the heap for buffering and processing the read data. Default chunk size (`FILE_READ_CHUNK`) is 1024 bytes and is regarded to be safe. Pushing this by 4x or more can cause heap overflows depending on the application. Consider this when selecting a value for parameter `n_or_char`. #### Syntax `file.read([n_or_char])` `fd:read([n_or_char])` #### Parameters - `n_or_char`: - if nothing passed in, then read up to `FILE_READ_CHUNK` bytes or the entire file (whichever is smaller). - if passed a number `n`, then read up to `n` bytes or the entire file (whichever is smaller). - if passed a string containing the single character `char`, then read until `char` appears next in the file, `FILE_READ_CHUNK` bytes have been read, or EOF is reached. #### Returns File content as a string, or nil when EOF #### Example (basic model) ```lua -- print the first line of 'init.lua' if file.open("init.lua", "r") then print(file.read('\n')) file.close() end ``` #### Example (object model) ```lua -- print the first 5 bytes of 'init.lua' fd = file.open("init.lua", "r") if fd then print(fd:read(5)) fd:close(); fd = nil end ``` #### See also - [`file.open()`](#fileopen) - [`file.readline()` / `file.obj:readline()`](#filereadline-fileobjreadline) ## file.readline(), file.obj:readline() Read the next line from the open file. Lines are defined as zero or more bytes ending with a EOL ('\n') byte. If the next line is longer than 1024, this function only returns the first 1024 bytes. #### Syntax `file.readline()` `fd:readline()` #### Parameters none #### Returns File content in string, line by line, including EOL('\n'). Return `nil` when EOF. #### Example (basic model) ```lua -- print the first line of 'init.lua' if file.open("init.lua", "r") then print(file.readline()) file.close() end ``` #### See also - [`file.open()`](#fileopen) - [`file.close()` / `file.obj:close()`](#fileclose-fileobjclose) - [`file.read()` / `file.obj:read()`](#fileread-fileobjread) ## file.seek(), file.obj:seek() Sets and gets the file position, measured from the beginning of the file, to the position given by offset plus a base specified by the string whence. #### Syntax `file.seek([whence [, offset]])` `fd:seek([whence [, offset]])` #### Parameters - `whence` - "set": base is position 0 (beginning of the file) - "cur": base is current position (default value) - "end": base is end of file - `offset` default 0 If no parameters are given, the function simply returns the current file offset. #### Returns the resulting file position, or `nil` on error #### Example (basic model) ```lua if file.open("init.lua", "r") then -- skip the first 5 bytes of the file file.seek("set", 5) print(file.readline()) file.close() end ``` #### See also [`file.open()`](#fileopen) ## file.write(), file.obj:write() Write a string to the open file. #### Syntax `file.write(string)` `fd:write(string)` #### Parameters `string` content to be write to file #### Returns `true` if the write is ok, `nil` on error #### Example (basic model) ```lua -- open 'init.lua' in 'a+' mode if file.open("init.lua", "a+") then -- write 'foo bar' to the end of the file file.write('foo bar') file.close() end ``` #### Example (object model) ```lua -- open 'init.lua' in 'a+' mode fd = file.open("init.lua", "a+") if fd then -- write 'foo bar' to the end of the file fd:write('foo bar') fd:close() end ``` #### See also - [`file.open()`](#fileopen) - [`file.writeline()` / `file.obj:writeline()`](#filewriteline-fileobjwriteline) ## file.writeline(), file.obj:writeline() Write a string to the open file and append '\n' at the end. #### Syntax `file.writeline(string)` `fd:writeline(string)` #### Parameters `string` content to be write to file #### Returns `true` if write ok, `nil` on error #### Example (basic model) ```lua -- open 'init.lua' in 'a+' mode if file.open("init.lua", "a+") then -- write 'foo bar' to the end of the file file.writeline('foo bar') file.close() end ``` #### See also - [`file.open()`](#fileopen) - [`file.readline()` / `file.obj:readline()`](#filereadline-fileobjreadline) ---