### 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
**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)
---