Below you'll find all necessary information to flash a NodeMCU firmware binary to ESP8266 or ESP8285. Note that this is a reference documentation and not a tutorial with fancy screen shots. Turn to your favorite search engine for those. Make sure you follow a recent tutorial rather than one that is several months old!
Adafruit provides a really nice [firmware flashing tutorial](https://learn.adafruit.com/building-and-running-micropython-on-the-esp8266/flash-firmware). Below you'll find just the basics for the two popular tools esptool and NodeMCU Flasher.
!!! attention
Keep in mind that the ESP8266 needs to be [put into flash mode](#putting-device-into-flash-mode) before you can flash a new firmware!
!!! important
When switching between NodeMCU versions, see the notes about
[Upgrading Firmware](#upgrading-firmware).
## Tool overview
## esptool.py
> A cute Python utility to communicate with the ROM bootloader in Espressif ESP8266. It is intended to be a simple, platform independent, open source replacement for XTCOM.
### esptool.py
> A Python-based, open source, platform independent, utility to communicate with the ROM bootloader in Espressif ESP8266.
Supported platforms: OS X, Linux, Windows, anything that runs Python
...
...
@@ -22,33 +15,27 @@ Supported platforms: OS X, Linux, Windows, anything that runs Python
Run the following command to flash an *aggregated* binary as is produced for example by the [cloud build service](build.md#cloud-build-service) or the [Docker image](build.md#docker-image).
-`mode` is `qio` for 512 kByte modules and `dio` for 4 MByte modules (`qio` might work as well, YMMV).
-`size` is given in bits. Specify `4m` for 512 kByte and `32m` for 4 MByte.
- See [below](#determine-flash-size) if you don't know or are uncertain about the capacity of the flash chip on your device. It might help to double check as e.g. some ESP-01 modules come with 512kB while others are equipped with 1MB.
- esptool.py is under heavy development. It's advised you run the latest version (check with `esptool.py version`). Since this documentation may not have been able to keep up refer to the [esptool flash modes documentation](https://github.com/themadinventor/esptool#flash-modes) for current options and parameters.
- In some uncommon cases, the [SDK init data](#sdk-init-data) may be invalid and NodeMCU may fail to boot. The easiest solution is to fully erase the chip before flashing:
Note that this tool was created by the initial developers of the NodeMCU firmware. It hasn't seen updates since September 2015 and is not maintained by the current NodeMCU *firmware* team. Be careful to not accidentally flash the very old default firmware the tool is shipped with.
## Putting Device Into Flash Mode
To enable ESP8266 firmware flashing GPIO0 pin must be pulled low before the device is reset. Conversely, for a normal boot, GPIO0 must be pulled high or floating.
If you have a [NodeMCU dev kit](https://github.com/nodemcu/nodemcu-devkit-v1.0) then you don't need to do anything, as the USB connection can pull GPIO0 low by asserting DTR and reset your board by asserting RTS.
If you have an ESP-01 or other device without built-in USB, you will need to enable flashing yourself by pulling GPIO0 low or pressing a "flash" switch, while powering up or resetting the module.
If you have an ESP-01 or other device without built-in USB, you will need to enable flashing yourself by pulling GPIO0 low or pressing a "flash" switch.
## Which Files To Flash
...
...
@@ -59,59 +46,55 @@ Otherwise, if you built your own firmware from source code:
-`bin/0x00000.bin` to 0x00000
-`bin/0x10000.bin` to 0x10000
## Upgrading Firmware
There are three potential issues that arise from upgrading (or downgrading!) firmware from one NodeMCU version to another:
* Lua scripts written for one NodeMCU version (like 0.9.x) may not work error-free on a more recent firmware. For example, Espressif changed the `socket:send` operation to be asynchronous i.e. non-blocking. See [API documentation](modules/net.md#netsocketsend) for details.
* The NodeMCU flash file system may need to be reformatted, particularly if its address has changed because the new firmware is different in size from the old firmware. If it is not automatically formatted then it should be valid and have the same contents as before the flash operation. You can still run [`file.format()`](modules/file.md#fileformat) manually to re-format your flash file system. You will know if you need to do this if your flash files exist but seem empty, or if data cannot be written to new files. However, this should be an exceptional case.
Formatting a file system on a large flash device (e.g. the 16MB parts) can take some time. So, on the first boot, you shouldn't get worried if nothing appears to happen for a minute. There's a message printed to console to make you aware of this.
Also, in some special circumstances, you may need to flash `blank.bin` or `esp_init_data_default.bin` to various addresses on the flash (depending on flash size and type), see [below](#upgrading-from-sdk-09x-firmware).
* The Espressif SDK Init Data may change between each NodeMCU firmware version, and may need to be erased or reflashed. See [SDK Init Data](#sdk-init-data) for details. Fully erasing the module before upgrading firmware will avoid this issue.
## SDK Init Data
!!! note
Normally, NodeMCU will take care of writing the SDK init data when needed. Most users can ignore this section.
## Upgrading Firmware
NodeMCU versions are compiled against specific versions of the Espressif SDK. The SDK reserves space in flash that is used to store calibration and other data. This data changes between SDK versions, and if it is invalid or not present, the firmware may not boot correctly. Symptoms include messages like `rf_cal[0] !=0x05,is 0xFF`, or endless reboot loops and/or fast blinking module LEDs.
!!! important
!!! tip
It goes without saying that you shouldn't expect your NodeMCU 0.9.x Lua scripts to work error-free on a more recent firmware. Most notably Espressif changed the `socket:send` operation to be asynchronous i.e. non-blocking. See [API documentation](modules/net.md#netsocketsend) for details.
If you are seeing one or several of the above symptoms, ensure that your chip is fully erased before flashing, for example:
Espressif changes the init data block (`esp_init_data_default.bin`) for their devices along the way with the SDK. So things break when a NodeMCU firmware with a certain SDK is flashed to a module which contains init data from a different SDK. Hence, this section applies to upgrading NodeMCU firmware just as well as *downgrading* firmware.
A typical case that often fails is when a module is upgraded from a 0.9.x firmware to a recent version. It might look like the new firmware is broken, but the reason for the missing Lua prompt is related to the big jump in SDK versions.
Also verify that you are using an up-to-date NodeMCU release, as some early releases of NodeMCU 1.5.4.1 did not write the SDK init data to a freshly erased chip.
If there is no init data block found during SDK startup, the SDK will install one itself. If there is a previous (potentially too old) init block, the SDK *probably* doesn't do anything with it but there is no documentation from Espressif on this topic.
Espressif refers to this area as "System Param" and it resides in the last four 4 kB sectors of flash. Since SDK 1.5.4.1 a fifth sector is reserved for RF calibration (and its placement is controlled by NodeMCU) as described by this [patch notice](http://bbs.espressif.com/viewtopic.php?f=46&t=2407). At minimum, Espressif states that the 4th sector from the end needs to be flashed with "init data", and the 2nd sector from the end should be blank.
Hence, there are two strategies to update the SDK init data:
The default init data is provided as part of the SDK in the file `esp_init_data_default.bin`. NodeMCU will automatically flash this file to the right place on first boot if the sector appears to be empty.
- Erase flash completely. This will also erase the (Lua) files you uploaded to the device! The SDK will install the init data block during startup.
- Don't erase the flash but replace just the init data with a new file during the flashing procedure. For this you would download [SDK patch 1.5.4.1](http://bbs.espressif.com/download/file.php?id=1572) and extract `esp_init_data_default.bin` from there.
If you need to customize init data then first download the [Espressif SDK patch 1.5.4.1](http://bbs.espressif.com/download/file.php?id=1572) and extract `esp_init_data_default.bin`. Then flash that file just like you'd flash the firmware. The correct address for the init data depends on the capacity of the flash chip.
When flashing a new firmware (particularly with a much different size), the flash filesystem may be reformatted as the firmware starts. If it is not automatically reformatted, then it should be valid and have the same contents as before the flash operation. You can still run [`file.format()`](modules/file.md#fileformat) to re-format your flash filesystem. You will know if you need to do this if your flash files exist but seem empty, or if data cannot be written to new files. However, this should be an exceptional case.
-`0x7c000` for 512 kB, modules like most ESP-01, -03, -07 etc.
-`0xfc000` for 1 MB, modules like ESP8285, PSF-A85, some ESP-01, -03 etc.
-`0x1fc000` for 2 MB
-`0x3fc000` for 4 MB, modules like ESP-12E, NodeMCU devkit 1.0, WeMos D1 mini
**esptool.py**
See "4.1 Non-FOTA Flash Map" and "6.3 RF Initialization Configuration" of the [ESP8266 Getting Started Guide](https://espressif.com/en/support/explore/get-started/esp8266/getting-started-guide) for details on init data addresses and customization.
For [esptool.py](https://github.com/themadinventor/esptool) you specify the init data file as an additional file for the `write_flash` command.
To determine the capacity of the flash chip *before* a firmware is installed you can run
!!! note "Note:"
`esptool.py --port <serial-port> flash_id`
The address for `esp_init_data_default.bin` depends on the size of your module's flash.
- `0x7c000` for 512 kB, modules like ESP-01, -03, -07 etc.
- `0xfc000` for 1 MB, modules like ESP8285, PSF-A85
- `0x1fc000` for 2 MB
- `0x3fc000` for 4 MB, modules like ESP-12E, NodeMCU devkit 1.0, WeMos D1 mini
It will return a manufacturer ID and a chip ID like so:
**NodeMCU Flasher**
The [NodeMCU Flasher](https://github.com/nodemcu/nodemcu-flasher) will download init data using a special path:
```
Connecting...
Manufacturer: e0
Device: 4016
INTERNAL://DEFAULT
```
The chip ID can then be looked up in [https://code.coreboot.org/p/flashrom/source/tree/HEAD/trunk/flashchips.h](https://code.coreboot.org/p/flashrom/source/tree/HEAD/trunk/flashchips.h). This leads to a manufacturer name and a chip model name/number e.g. `AMIC_A25LQ032`. That information can then be fed into your favorite search engine to find chip descriptions and data sheets.
By convention the last two or three digits in the module name denote the capacity in megabits. So, `A25LQ032` in the example above is a 32Mb(=4MB) module.
Replace the provided (old) `esp_init_data_default.bin` with the one extracted above and use the flasher like you're used to.
After running [`enduser_setup.start()`](#enduser_setupstart), a wireless network named "SetupGadget_XXXXXX" will start. Connect to that SSID and then navigate to the root
of any website (e.g., `http://example.com/` will work, but do not use `.local` domains because it will fail on iOS). A web page similar to the picture above will load, allowing the
end user to provide their Wi-Fi information.
After an IP address has been successfully obtained, then this module will stop as if [`enduser_setup.stop()`](#enduser_setupstop) had been called. There is a 10-second delay before
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: Even if gzipped, the file still needs to be named `enduser_setup.html`.*
The following HTTP endpoints exist:
|Endpoint|Description|
|--------|-----------|
|/|Returns HTML for the web page. Will return the contents of `enduser_setup.html` if it exists on the filesystem, otherwise will return a page embedded into the firmware image.|
|/aplist|Forces the ESP8266 to perform a site survey across all channels, reporting access points that it can find. Return payload is a JSON array: `[{"ssid":"foobar","rssi":-36,"chan":3}]`|
|/generate_204|Returns a HTTP 204 status (expected by certain Android clients during Wi-Fi connectivity checks)|
|/status|Returns plaintext status description, used by the web page|
|/status.json|Returns a JSON payload containing the ESP8266's chip id in hexadecimal format and the status code: 0=Idle, 1=Connecting, 2=Wrong Password, 3=Network not Found, 4=Failed, 5=Success|
|/setwifi|Endpoint intended for services to use for setting the wifi credentials. Identical to `/update` except returns the same payload as `/status.json` instead of redirecting to `/`.|
|/update|Form submission target. Example: `http://example.com/update?wifi_ssid=foobar&wifi_password=CorrectHorseBatteryStaple`. Must be a GET request. Will redirect to `/` when complete. |
After running [`enduser_setup.start()`](#enduser_setupstart) a portal like the above can be accessed through a wireless network called SetupGadget_XXXXXX. The portal is used to submit the credentials for the WiFi of the enduser.
After an IP address has been successfully obtained this module will stop as if [`enduser_setup.stop()`](#enduser_setupstop) had been called.
Alternative HTML can be served by placing a file called `enduser_setup.html` in the filesystem. This file will be kept in RAM, so keep it as small as possible.
## enduser_setup.manual()
...
...
@@ -72,7 +53,7 @@ Starts the captive portal.
#### Parameters
-`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.
-`onDebug()` callback is disabled by default. It is intended to be used to find internal issues in the module. `string` contains a description of what is going on.
@@ -7,6 +7,8 @@ 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.
Only one file can be open at any given time.
Besides the SPIFFS file system on internal flash, this module can also access FAT partitions on an external SD card is [FatFS is enabled](../sdcard.md).
```lua
...
...
@@ -41,6 +43,30 @@ Current directory defaults to the root of internal SPIFFS (`/FLASH`) after syste
#### Returns
`true` on success, `false` otherwise
## file.close()
Closes the open file, if any.
#### Syntax
`file.close()`
#### Parameters
none
#### Returns
`nil`
#### Example
```lua
-- open 'init.lua', print the first line.
iffile.open("init.lua","r")then
print(file.readline())
file.close()
end
```
#### See also
[`file.open()`](#fileopen)
## file.exists()
Determines whether the specified file exists.
...
...
@@ -69,6 +95,34 @@ end
#### See also
[`file.list()`](#filelist)
## file.flush()
Flushes any pending writes to the file system, ensuring no data is lost on a restart. Closing the open file using [`file.close()`](#fileclose) performs an implicit flush as well.
#### Syntax
`file.flush()`
#### Parameters
none
#### Returns
`nil`
#### Example
```lua
-- open 'init.lua' in 'a+' mode
iffile.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()`](#fileclose)
## 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.
...
...
@@ -226,9 +280,9 @@ When done with the file, it must be closed using `file.close()`.
- "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).
`nil` if file not opened, or not exists (read modes).`true` if file opened ok.
#### Example (basic model)
#### Example
```lua
-- open 'init.lua', print the first line.
iffile.open("init.lua","r")then
...
...
@@ -236,245 +290,120 @@ if file.open("init.lua", "r") then
file.close()
end
```
#### Example (object model)
```lua
-- open 'init.lua', print the first line.
fd=file.open("init.lua","r")
iffdthen
print(fd:readline())
fd:close();fd=nil
end
```
#### See also
-[`file.close()`](#fileclose)
-[`file.readline()`](#filereadline)
## 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.rename()
## file.read()
Renames a file. If a file is currently open, it will be closed first.
Read content from the open file.
#### Syntax
`file.rename(oldname, newname)`
`file.read([n_or_str])`
#### Parameters
-`oldname` old file name
-`newname` new file name
-`n_or_str`:
- if nothing passed in, read up to `LUAL_BUFFERSIZE` bytes (default 1024) or the entire file (whichever is smaller)
- if passed a number n, then read the file until the lesser of `n` bytes, `LUAL_BUFFERSIZE` bytes, or EOF is reached. Specifying a number larger than the buffer size will read the buffer size.
- if passed a string `str`, then read until `str` appears next in the file, `LUAL_BUFFERSIZE` bytes have been read, or EOF is reached
#### Returns
`true` on success, `false` on error.
File content as a string, or nil when EOF
#### Example
```lua
-- rename file 'temp.lua' to 'init.lua'.
file.rename("temp.lua","init.lua")
```
# 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.
-- print the first line of 'init.lua'
iffile.open("init.lua","r")then
print(file.readline())
print(file.read('\n'))
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")
ifsrcthen
dest=file.open("copy.lua","w")
ifdestthen
localline
repeat
line=src:read()
iflinethen
dest:write(line)
end
untilline==nil
dest:close();dest=nil
end
src:close();dest=nil
-- print the first 5 bytes of 'init.lua'
iffile.open("init.lua","r")then
print(file.read(5))
file.close()
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 correspoding 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.open()`](#fileopen)
-[`file.readline()`](#filereadline)
## file.flush()
## file.obj:flush()
## file.readline()
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) performs an implicit flush as well.
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 `LUAL_BUFFERSIZE`, this function only returns the first `LUAL_BUFFERSIZE` bytes (this is 1024 bytes by default).
#### Syntax
`file.flush()`
`fd:flush()`
`file.readline()`
#### Parameters
none
#### Returns
`nil`
File content in string, line by line, including EOL('\n'). Return `nil` when EOF.
#### Example (basic model)
#### Example
```lua
-- open 'init.lua' in 'a+' mode
iffile.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')
-- print the first line of 'init.lua'
iffile.open("init.lua","r")then
print(file.readline())
file.close()
end
```
#### See also
[`file.close()` / `file.obj:close()`](#fileclose)
## file.read()
## file.obj:read()
Read content from the open file.
!!! note
-[`file.open()`](#fileopen)
-[`file.close()`](#fileclose)
-[`file.read()`](#filereade)
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`.
## file.remove()
#### Syntax
`file.read([n_or_char])`
Remove a file from the file system. The file must not be currently open.
`fd:read([n_or_char])`
###Syntax
`file.remove(filename)`
#### 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.
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.
Renames a file. If a file is currently open, it will be closed first.
#### Syntax
`file.readline()`
`fd:readline()`
`file.rename(oldname, newname)`
#### Parameters
none
-`oldname` old file name
-`newname` new file name
#### Returns
File content in string, line by line, including EOL('\n'). Return `nil` when EOF.
Serialize output based on a sequence of delay-times in µs. After each delay, the pin is toggled. After the last cycle and last delay the pin is not toggled.
Serialize output based on a sequence of delay-times in µs. After each delay, the pin is toggled. After the last repeat and last delay the pin is not toggled.
The function works in two modes:
* synchronous - for sub-50 µs resolution, restricted to max. overall duration,
* asynchrounous - synchronous operation with less granularity but virtually unrestricted duration.
Whether the asynchronous mode is chosen is defined by presence of the `callback` parameter. If present and is of function type the function goes asynchronous and the callback function is invoked when sequence finishes. If the parameter is numeric the function still goes asynchronous but no callback is invoked when done.
Whether the asynchronous mode is chosen is defined by presence of the `callback` parameter. If present and is of function type the function goes asynchronous the callback function is invoked when sequence finishes. If the parameter is numeric the function still goes asynchronous but no callback is invoked when done.
For the asynchronous version, the minimum delay time should not be shorter than 50 μs and maximum delay time is 0x7fffff μs (~8.3 seconds).
In this mode the function does not block the stack and returns immediately before the output sequence is finalized. HW timer `FRC1_SOURCE` mode is used to change the states. As there is only a single hardware timer, there
are restrictions on which modules can be used at the same time. An error will be raised if the timer is already in use.
For asynchronous version minimum delay time should not be shorter than 50 μs and maximum delay time is 0x7fffff μs (~8.3 seconds).
In this mode the function does not block the stack and returns immediately before the output sequence is finalized. HW timer inf `FRC1_SOURCE` mode is used to change the states.
Note that the synchronous variant (no or nil `callback` parameter) function blocks the stack and as such any use of it must adhere to the SDK guidelines (also explained [here](../extn-developer-faq/#extension-developer-faq)). Failure to do so may lead to WiFi issues or outright to crashes/reboots. In short it means that the sum of all delay times multiplied by the number of cycles should not exceed 15 ms.
Note that the synchronous variant (no or nil `callback` parameter) function blocks the stach and as such any use of it must adhere to the SDK guidelines (also explained [here](https://nodemcu.readthedocs.io/en/dev/en/extn-developer-faq/#extension-developer-faq)). Failure to do so may lead to WiFi issues or outright to crashes/reboots. Shortly it means that sum of all delay times multiplied by the number of repeats should not exceed 15 ms.
@@ -135,21 +135,15 @@ Listen on port from IP address.
#### Example
```lua
-- server listens on 80, if data received, print data to console and send "hello world" back to caller
-- 30s time out for a inactive client
-- 30s time out for a inactive client
sv=net.createServer(net.TCP,30)
functionreceiver(sck,data)
print(data)
sck:close()
end
ifsvthen
sv:listen(80,function(conn)
conn:on("receive",receiver)
conn:send("hello world")
-- server listens on 80, if data received, print data to console and send "hello world" back to caller
sv:listen(80,function(c)
c:on("receive",function(c,pl)
print(pl)
end)
end
c:send("hello world")
end)
```
#### See also
...
...
@@ -309,35 +303,32 @@ Multiple consecutive `send()` calls aren't guaranteed to work (and often don't)
#### Example
```lua
srv=net.createServer(net.TCP)
functionreceiver(sck,data)
localresponse={}
-- if you're sending back HTML over HTTP you'll want something like this instead
-- local response = {"HTTP/1.0 200 OK\r\nServer: NodeMCU on ESP8266\r\nContent-Type: text/html\r\n\r\n"}
response[#response+1]="lots of data"
response[#response+1]="even more data"
response[#response+1]="e.g. content read from a file"
-- sends and removes the first element from the 'response' table
localfunctionsend(localSocket)
if#response>0
thenlocalSocket:send(table.remove(response,1))
else
localSocket:close()
response=nil
srv:listen(80,function(conn)
conn:on("receive",function(sck,req)
localresponse={}
-- if you're sending back HTML over HTTP you'll want something like this instead
-- local response = {"HTTP/1.0 200 OK\r\nServer: NodeMCU on ESP8266\r\nContent-Type: text/html\r\n\r\n"}
response[#response+1]="lots of data"
response[#response+1]="even more data"
response[#response+1]="e.g. content read from a file"
-- sends and removes the first element from the 'response' table
localfunctionsend(sk)
if#response>0
thensk:send(table.remove(response,1))
else
sk:close()
response=nil
end
end
end
-- triggers the send() function again once the first chunk of data was sent
sck:on("sent",send)
send(sck)
end
srv:listen(80,function(conn)
conn:on("receive",receiver)
-- triggers the send() function again once the first chunk of data was sent
sck:on("sent",send)
send(sck)
end)
end)
```
If you do not or can not keep all the data you send back in memory at one time (remember that `response` is an aggregation) you may use explicit callbacks instead of building up a table like so:
For applications where it is necessary to take samples with high regularity, this function is useful. It provides an easy way to implement a "wake up on the next 5-minute boundary" scheme, without having to explicitly take into account how long the module has been active for etc before going back to sleep.
@@ -12,7 +12,7 @@ When compiled together with the [rtctime](rtctime.md) module it also offers seam
Attempts to obtain time synchronization.
For best results you may want to to call this periodically in order to compensate for internal clock drift. As stated in the [rtctime](rtctime.md) module documentation it's advisable to sync time after deep sleep and it's necessary to sync after module reset (add it to [`init.lua`](../upload.md#initlua) after WiFi initialization).
For best results you may want to to call this periodically in order to compensate for internal clock drift. As stated in the [rtctime](rtctime.md) module documentation it's advisable to sync time after deep sleep and it's necessary to sync after module reset (add it to [`init.lua`](upload.md#initlua) after WiFi initialization).
This module provides a simple interface to control Somfy blinds via an RF transmitter (433.42 MHz). It is based on [Nickduino Somfy Remote Arduino skecth](https://github.com/Nickduino/Somfy_Remote).
The hardware used is the standard 433 MHz RF transmitter. Unfortunately these chips are usually transmitting at he frequency of 433.92MHz so the crystal resonator should be replaced with the 433.42 MHz resonator though some reporting that it is working even with the original crystal.
To understand details of the Somfy protocol please refer to [Somfy RTS protocol](https://pushstack.wordpress.com/somfy-rts-protocol/) and also discussion [here](https://forum.arduino.cc/index.php?topic=208346.0).
The module is using hardware timer so it cannot be used at the same time with other NodeMCU modules using the hardware timer, i.e. `sigma delta`, `pcm`, `perf`, or `pwm` modules.
## somfy.sendcommand()
Builds an frame defined by Somfy protocol and sends it to the RF transmitter.
-`pin` GPIO pin the RF transmitter is connected to.
-`remote_address` address of the remote control. The device to be controlled is programmed with the addresses of the remote controls it should listen to.
-`command` command to be transmitted. Can be one of `somfy.SOMFY_UP`, `somfy.SOMFY_DOWN`, `somfy.SOMFY_PROG`, `somfy.SOMFY_STOP`
-`rolling_code` The rolling code is increased every time a button is pressed. The receiver only accepts command if the rolling code is above the last received code and is not to far ahead of the last received code. This window is in the order of a 100 big. The rolling code needs to be stored in the EEPROM (i.e. filesystem) to survive the ESP8266 reset.
-`repeat_count` how many times the command is repeated
-`call_back` a function to be called after the command is transmitted. Allows chaining commands to set the blinds to a defined position.
My original remote is [TELIS 4 MODULIS RTS](https://www.somfy.co.uk/products/1810765/telis-4-modulis-rts). This remote is working with the additional info - additional 56 bits that follow data (shortening the Inter-frame gap). It seems that the scrumbling alhorithm has not been revealed yet.
When I send the `somfy.DOWN` command, repeating the frame twice (which seems to be the standard for a short button press), i.e. `repeat_count` equal to 2, the blinds go only 1 step down. This corresponds to the movement of the wheel on the original remote. The down button on the original remote sends also `somfy.DOWN` command but the additional info is different and this makes the blinds go full down. Fortunately it seems that repeating the frame 16 times makes the blinds go fully down.
#### Returns
nil
#### Example
To start with controlling your Somfy blinds you need to:
- Choose an arbitrary remote address (different from your existing remote) - `123` in this example
- Choose a starting point for the rolling code. Any unsigned int works, 1 is a good start
- Long-press the program button of your existing remote control until your blind goes up and down slightly
- execute `somfy.sendcommand(4, 123, somfy.PROG, 1, 2)` - the blinds will react and your ESP8266 remote control is now registered
All transactions for sending and receiving are most-significant-bit first and least-significant last.
For technical details of the underlying hardware refer to [metalphreak's ESP8266 HSPI articles](http://d.av.id.au/blog/tag/hspi/).
!!! note
The ESP hardware provides two SPI busses, with IDs 0, and 1, which map to pins generally labelled SPI and HSPI. If you are using any kind of development board which provides flash, then bus ID 0 (SPI) is almost certainly used for communicating with the flash chip. You probably want to choose bus ID 1 (HSPI) for your communication, as you will have uncontended use of it.
## High Level Functions
The high level functions provide a send & receive API for half- and
full-duplex mode. Sent and received data items are restricted to 1 - 32 bit
@@ -8,7 +8,7 @@ U8glib is a graphics library developed at [olikraus/u8glib](https://github.com/o
I²C and SPI mode:
- sh1106_128x64
- ssd1306 - 128x32, 128x64, and 64x48 variants
- ssd1306 - 128x64 and 64x48 variants
- ssd1309_128x64
- ssd1327_96x96_gr
- uc1611 - dogm240 and dogxl240 variants
...
...
@@ -107,7 +107,6 @@ Initialize a display via I²C.
The init sequence would insert delays to match the display specs. These can destabilize the overall system if wifi service is blocked for too long. It is therefore advisable to disable such delays unless the specific use case can exclude wifi traffic while initializing the display driver.
-`u8g.sh1106_128x64_i2c()`
-`u8g.ssd1306_128x32_i2c()`
-`u8g.ssd1306_128x64_i2c()`
-`u8g.ssd1306_64x48_i2c()`
-`u8g.ssd1309_128x64_i2c()`
...
...
@@ -147,7 +146,6 @@ The init sequence would insert delays to match the display specs. These can dest
Initialize a virtual display that provides run-length encoded framebuffer contents to a Lua callback.
The callback function can be used to process the framebuffer line by line. It's called with either `nil` as parameter to indicate the start of a new frame or with a string containing a line of the framebuffer with run-length encoding. First byte in the string specifies how many pairs of (x, len) follow, while each pair defines the start (leftmost x-coordinate) and length of a sequence of lit pixels. All other pixels in the line are dark.