Commit d77666c0 authored by sergio's avatar sergio Committed by Terry Ellison
Browse files

trailing spaces cleanup (#2659)

parent d7583040
...@@ -70,7 +70,7 @@ Start WiFi WPS function. WPS must be enabled prior calling this function. ...@@ -70,7 +70,7 @@ Start WiFi WPS function. WPS must be enabled prior calling this function.
end end
wps.disable() wps.disable()
end) end)
--Full example --Full example
do do
-- Register wifi station event callbacks -- Register wifi station event callbacks
...@@ -84,10 +84,10 @@ Start WiFi WPS function. WPS must be enabled prior calling this function. ...@@ -84,10 +84,10 @@ Start WiFi WPS function. WPS must be enabled prior calling this function.
end) end)
wifi.setmode(wifi.STATION) wifi.setmode(wifi.STATION)
wps_retry_func = function() wps_retry_func = function()
if wps_retry_count == nil then wps_retry_count = 0 end if wps_retry_count == nil then wps_retry_count = 0 end
if wps_retry_count < 3 then if wps_retry_count < 3 then
wps.disable() wps.disable()
wps.enable() wps.enable()
wps_retry_count = wps_retry_count + 1 wps_retry_count = wps_retry_count + 1
...@@ -101,7 +101,7 @@ Start WiFi WPS function. WPS must be enabled prior calling this function. ...@@ -101,7 +101,7 @@ Start WiFi WPS function. WPS must be enabled prior calling this function.
wps_cb = nil wps_cb = nil
end end
end end
wps_cb = function(status) wps_cb = function(status)
if status == wps.SUCCESS then if status == wps.SUCCESS then
wps.disable() wps.disable()
...@@ -138,5 +138,5 @@ Start WiFi WPS function. WPS must be enabled prior calling this function. ...@@ -138,5 +138,5 @@ Start WiFi WPS function. WPS must be enabled prior calling this function.
wps.enable() wps.enable()
wps.start(wps_cb) wps.start(wps_cb)
end end
``` ```
...@@ -11,8 +11,8 @@ generate the bitstream. It can use UART0 routed to TXD0 as well to ...@@ -11,8 +11,8 @@ generate the bitstream. It can use UART0 routed to TXD0 as well to
handle two led strips at the same time. handle two led strips at the same time.
**WARNING**: In dual mode, you will loose access to the Lua's console **WARNING**: In dual mode, you will loose access to the Lua's console
through the serial port (it will be reconfigured to support WS2812-like through the serial port (it will be reconfigured to support WS2812-like
protocol). If you want to keep access to Lua's console, you will have to protocol). If you want to keep access to Lua's console, you will have to
use an other input channel like a TCP server (see [example](https://github.com/nodemcu/nodemcu-firmware/blob/master/lua_examples/telnet/telnet.lua)) use an other input channel like a TCP server (see [example](https://github.com/nodemcu/nodemcu-firmware/blob/master/lua_examples/telnet/telnet.lua))
## ws2812.init() ## ws2812.init()
...@@ -191,11 +191,11 @@ Returns the contents of the buffer (the pixel values) as a string. This can then ...@@ -191,11 +191,11 @@ Returns the contents of the buffer (the pixel values) as a string. This can then
`buffer:dump()` `buffer:dump()`
#### Returns #### Returns
A string containing the pixel values. A string containing the pixel values.
#### Example #### Example
```lua ```lua
local s = buffer:dump() local s = buffer:dump()
``` ```
## ws2812.buffer:replace() ## ws2812.buffer:replace()
...@@ -249,7 +249,7 @@ buffer:mix(192, buffer) ...@@ -249,7 +249,7 @@ buffer:mix(192, buffer)
## ws2812.buffer:power() ## ws2812.buffer:power()
Computes the total energy requirement for the buffer. This is merely the total sum of all the pixel values (which assumes that each color in each Computes the total energy requirement for the buffer. This is merely the total sum of all the pixel values (which assumes that each color in each
pixel consumes the same amount of power). A real WS2812 (or WS2811) has three constant current drivers of 20mA -- one for each of R, G and B. The pixel consumes the same amount of power). A real WS2812 (or WS2811) has three constant current drivers of 20mA -- one for each of R, G and B. The
pulse width modulation will cause the *average* current to scale linearly with pixel value. pulse width modulation will cause the *average* current to scale linearly with pixel value.
#### Syntax #### Syntax
`buffer:power()` `buffer:power()`
...@@ -271,7 +271,7 @@ end ...@@ -271,7 +271,7 @@ end
``` ```
## ws2812.buffer:fade() ## ws2812.buffer:fade()
Fade in or out. Defaults to out. Multiply or divide each byte of each led with/by the given value. Useful for a fading effect. Fade in or out. Defaults to out. Multiply or divide each byte of each led with/by the given value. Useful for a fading effect.
#### Syntax #### Syntax
`buffer:fade(value [, direction])` `buffer:fade(value [, direction])`
...@@ -289,15 +289,15 @@ buffer:fade(2) ...@@ -289,15 +289,15 @@ buffer:fade(2)
buffer:fade(2, ws2812.FADE_IN) buffer:fade(2, ws2812.FADE_IN)
``` ```
## ws2812.buffer:shift() ## ws2812.buffer:shift()
Shift the content of (a piece of) the buffer in positive or negative direction. This allows simple animation effects. A slice of the buffer can be specified by using the Shift the content of (a piece of) the buffer in positive or negative direction. This allows simple animation effects. A slice of the buffer can be specified by using the
standard start and end offset Lua notation. Negative values count backwards from the end of the buffer. standard start and end offset Lua notation. Negative values count backwards from the end of the buffer.
#### Syntax #### Syntax
`buffer:shift(value [, mode[, i[, j]]])` `buffer:shift(value [, mode[, i[, j]]])`
#### Parameters #### Parameters
- `value` number of pixels by which to rotate the buffer. Positive values rotate forwards, negative values backwards. - `value` number of pixels by which to rotate the buffer. Positive values rotate forwards, negative values backwards.
- `mode` is the shift mode to use. Can be one of `ws2812.SHIFT_LOGICAL` or `ws2812.SHIFT_CIRCULAR`. In case of SHIFT\_LOGICAL, the freed pixels are set to 0 (off). In case of SHIFT\_CIRCULAR, the buffer is treated like a ring buffer, inserting the pixels falling out on one end again on the other end. Defaults to SHIFT\_LOGICAL. - `mode` is the shift mode to use. Can be one of `ws2812.SHIFT_LOGICAL` or `ws2812.SHIFT_CIRCULAR`. In case of SHIFT\_LOGICAL, the freed pixels are set to 0 (off). In case of SHIFT\_CIRCULAR, the buffer is treated like a ring buffer, inserting the pixels falling out on one end again on the other end. Defaults to SHIFT\_LOGICAL.
- `i` is the first offset in the buffer to be affected. Negative values are permitted and count backwards from the end. Default is 1. - `i` is the first offset in the buffer to be affected. Negative values are permitted and count backwards from the end. Default is 1.
- `j` is the last offset in the buffer to be affected. Negative values are permitted and count backwards from the end. Default is -1. - `j` is the last offset in the buffer to be affected. Negative values are permitted and count backwards from the end. Default is -1.
......
# SPIFFS File System # SPIFFS File System
The NodeMCU project uses the [SPIFFS](https://github.com/pellepl/spiffs) The NodeMCU project uses the [SPIFFS](https://github.com/pellepl/spiffs)
filesystem to store files in the flash chip. The technical details about how this is configured can be found below, along with various build time options. filesystem to store files in the flash chip. The technical details about how this is configured can be found below, along with various build time options.
# spiffsimg - Manipulate SPI Flash File System disk images # spiffsimg - Manipulate SPI Flash File System disk images
...@@ -13,12 +13,12 @@ NodeMCU uses a SPIFFS filesystem that knows how big it is -- i.e. when you build ...@@ -13,12 +13,12 @@ NodeMCU uses a SPIFFS filesystem that knows how big it is -- i.e. when you build
image, it must fit into the flash chip, and it cannot be expanded once flashed. image, it must fit into the flash chip, and it cannot be expanded once flashed.
It is important to give the `spiffimg` tool the correct size. You can provide either the `-c` option or both the `-U` and `-S` options. It is important to give the `spiffimg` tool the correct size. You can provide either the `-c` option or both the `-U` and `-S` options.
### Syntax ### Syntax
``` ```
spiffsimg -f <filename> spiffsimg -f <filename>
[-o <offsetfile>] [-o <offsetfile>]
[-c <size>] [-c <size>]
[-S <flashsize>] [-S <flashsize>]
[-U <usedsize>] [-U <usedsize>]
[-d] [-d]
...@@ -76,26 +76,26 @@ The SPIFFS configuration is 4k sectors (the only size supported by the SDK) and ...@@ -76,26 +76,26 @@ The SPIFFS configuration is 4k sectors (the only size supported by the SDK) and
One of the goals is to make the filesystem more persistent across reflashing of the firmware. However, there are still cases One of the goals is to make the filesystem more persistent across reflashing of the firmware. However, there are still cases
where spiffs detects a filesystem and uses it when it isn't valid. If you are getting weirdness with the filesystem, then just reformat it. where spiffs detects a filesystem and uses it when it isn't valid. If you are getting weirdness with the filesystem, then just reformat it.
There are two significant sizes of flash -- the 512K and 4M (or bigger). There are two significant sizes of flash -- the 512K and 4M (or bigger).
The file system has to start on a 4k boundary, but since it ends on a much bigger boundary (a 16k boundary), it also starts on an 8k boundary. For the small flash chip, there is The file system has to start on a 4k boundary, but since it ends on a much bigger boundary (a 16k boundary), it also starts on an 8k boundary. For the small flash chip, there is
not much spare space, so a newly formatted file system will start as low as possible (to get as much space as possible). For the large flash, the not much spare space, so a newly formatted file system will start as low as possible (to get as much space as possible). For the large flash, the
file system will start on a 64k boundary. A newly formatted file system will start between 64k and 128k from the end of the firmware. This means that the file file system will start on a 64k boundary. A newly formatted file system will start between 64k and 128k from the end of the firmware. This means that the file
system will survive lots of reflashing and at least 64k of firmware growth. system will survive lots of reflashing and at least 64k of firmware growth.
The standard build process for the firmware builds the `spiffsimg` tool (found in the `tools/spiffsimg` subdirectory). The standard build process for the firmware builds the `spiffsimg` tool (found in the `tools/spiffsimg` subdirectory).
The top level Makfile also checks if The top level Makfile also checks if
there is any data in the `local/fs` directory tree, and it will then copy these files there is any data in the `local/fs` directory tree, and it will then copy these files
into the flash disk image. Two images will normally be created -- one for the 512k flash part and the other for the 4M flash part. If the data doesn't into the flash disk image. Two images will normally be created -- one for the 512k flash part and the other for the 4M flash part. If the data doesn't
fit into the 512k part after the firmware is included, then the file will not be generated. fit into the 512k part after the firmware is included, then the file will not be generated.
The disk image file is placed into the `bin` directory and it is named `0x<offset>-<size>.bin` where the offset is the location where it should be The disk image file is placed into the `bin` directory and it is named `0x<offset>-<size>.bin` where the offset is the location where it should be
flashed, and the size is the size of the flash part. It is quite valid (and quicker) to flash the 512k image into a 4M part. However, there will probably be flashed, and the size is the size of the flash part. It is quite valid (and quicker) to flash the 512k image into a 4M part. However, there will probably be
limited space in the file system for creating new files. limited space in the file system for creating new files.
The default configuration will try and build three different file systems for 512KB, 1MB and 4MB flash sizes. The 1MB size is suitable for the ESP8285. This can be overridden by specifying the FLASHSIZE parameter to the makefile. The default configuration will try and build three different file systems for 512KB, 1MB and 4MB flash sizes. The 1MB size is suitable for the ESP8285. This can be overridden by specifying the FLASHSIZE parameter to the makefile.
If the `local/fs` directory is empty, then no flash images will be created (and the ones from the last build will be removed). The `spiffsimg` tool can If the `local/fs` directory is empty, then no flash images will be created (and the ones from the last build will be removed). The `spiffsimg` tool can
then be used to build an image as required. then be used to build an image as required.
If no file system is found during platform boot, then a new file system will be formatted. This can take some time on the first boot. If no file system is found during platform boot, then a new file system will be formatted. This can take some time on the first boot.
...@@ -108,8 +108,8 @@ Just place the following define in `user_config.h` or some other file that is in ...@@ -108,8 +108,8 @@ Just place the following define in `user_config.h` or some other file that is in
#define SPIFFS_MAX_FILESYSTEM_SIZE 32768 #define SPIFFS_MAX_FILESYSTEM_SIZE 32768
``` ```
This filesystem size limit only affects the formatting of a file system -- if the firm finds an existing valid filesystem (of any size) it will use that. However, if the This filesystem size limit only affects the formatting of a file system -- if the firm finds an existing valid filesystem (of any size) it will use that. However, if the
filesystem is reformatted from Lua (using file.format()) then the new file system will obey the size limit. filesystem is reformatted from Lua (using file.format()) then the new file system will obey the size limit.
There is also an option to control the positioning of the SPIFFS file system: There is also an option to control the positioning of the SPIFFS file system:
...@@ -117,7 +117,7 @@ There is also an option to control the positioning of the SPIFFS file system: ...@@ -117,7 +117,7 @@ There is also an option to control the positioning of the SPIFFS file system:
#define SPIFFS_FIXED_LOCATION 0x100000 #define SPIFFS_FIXED_LOCATION 0x100000
``` ```
This specifies that the SPIFFS filesystem starts at 1Mb from the start of the flash. Unless otherwise specified, it will run to the end of the flash (excluding the 16k of space reserved by the SDK). This specifies that the SPIFFS filesystem starts at 1Mb from the start of the flash. Unless otherwise specified, it will run to the end of the flash (excluding the 16k of space reserved by the SDK).
There is an option that limits the size of the file system to run up to the next 1MB boundary (minus the 16k for the parameter space). This may be useful when dealing with OTA upgrades. There is an option that limits the size of the file system to run up to the next 1MB boundary (minus the 16k for the parameter space). This may be useful when dealing with OTA upgrades.
......
As with [flashing](flash.md) there are several ways to upload code from your computer to the device. As with [flashing](flash.md) there are several ways to upload code from your computer to the device.
!!! note !!! note
The NodeMCU serial interface uses 115'200bps at boot time. To change the speed after booting, issue `uart.setup(0,9600,8,0,1,1)`. If the device panics and resets at any time, errors will be written to the serial interface at 115'200 bps. The NodeMCU serial interface uses 115'200bps at boot time. To change the speed after booting, issue `uart.setup(0,9600,8,0,1,1)`. If the device panics and resets at any time, errors will be written to the serial interface at 115'200 bps.
## Tools ## Tools
...@@ -56,32 +56,32 @@ function startup() ...@@ -56,32 +56,32 @@ function startup()
end end
end end
-- Define WiFi station event callbacks -- Define WiFi station event callbacks
wifi_connect_event = function(T) wifi_connect_event = function(T)
print("Connection to AP("..T.SSID..") established!") print("Connection to AP("..T.SSID..") established!")
print("Waiting for IP address...") print("Waiting for IP address...")
if disconnect_ct ~= nil then disconnect_ct = nil end if disconnect_ct ~= nil then disconnect_ct = nil end
end end
wifi_got_ip_event = function(T) wifi_got_ip_event = function(T)
-- Note: Having an IP address does not mean there is internet access! -- Note: Having an IP address does not mean there is internet access!
-- Internet connectivity can be determined with net.dns.resolve(). -- Internet connectivity can be determined with net.dns.resolve().
print("Wifi connection is ready! IP address is: "..T.IP) print("Wifi connection is ready! IP address is: "..T.IP)
print("Startup will resume momentarily, you have 3 seconds to abort.") print("Startup will resume momentarily, you have 3 seconds to abort.")
print("Waiting...") print("Waiting...")
tmr.create():alarm(3000, tmr.ALARM_SINGLE, startup) tmr.create():alarm(3000, tmr.ALARM_SINGLE, startup)
end end
wifi_disconnect_event = function(T) wifi_disconnect_event = function(T)
if T.reason == wifi.eventmon.reason.ASSOC_LEAVE then if T.reason == wifi.eventmon.reason.ASSOC_LEAVE then
--the station has disassociated from a previously connected AP --the station has disassociated from a previously connected AP
return return
end end
-- total_tries: how many times the station will attempt to connect to the AP. Should consider AP reboot duration. -- total_tries: how many times the station will attempt to connect to the AP. Should consider AP reboot duration.
local total_tries = 75 local total_tries = 75
print("\nWiFi connection to AP("..T.SSID..") has failed!") print("\nWiFi connection to AP("..T.SSID..") has failed!")
--There are many possible disconnect reasons, the following iterates through --There are many possible disconnect reasons, the following iterates through
--the list and returns the string corresponding to the disconnect reason. --the list and returns the string corresponding to the disconnect reason.
for key,val in pairs(wifi.eventmon.reason) do for key,val in pairs(wifi.eventmon.reason) do
if val == T.reason then if val == T.reason then
...@@ -90,17 +90,17 @@ wifi_disconnect_event = function(T) ...@@ -90,17 +90,17 @@ wifi_disconnect_event = function(T)
end end
end end
if disconnect_ct == nil then if disconnect_ct == nil then
disconnect_ct = 1 disconnect_ct = 1
else else
disconnect_ct = disconnect_ct + 1 disconnect_ct = disconnect_ct + 1
end end
if disconnect_ct < total_tries then if disconnect_ct < total_tries then
print("Retrying connection...(attempt "..(disconnect_ct+1).." of "..total_tries..")") print("Retrying connection...(attempt "..(disconnect_ct+1).." of "..total_tries..")")
else else
wifi.sta.disconnect() wifi.sta.disconnect()
print("Aborting connection to AP!") print("Aborting connection to AP!")
disconnect_ct = nil disconnect_ct = nil
end end
end end
...@@ -118,7 +118,7 @@ wifi.sta.config({ssid=SSID, pwd=PASSWORD}) ...@@ -118,7 +118,7 @@ wifi.sta.config({ssid=SSID, pwd=PASSWORD})
## Compiling Lua on your PC for Uploading ## Compiling Lua on your PC for Uploading
If you install Lua on your development PC or Laptop, then you can use a standard `lua` environment to develop PC applications and also use the standard `luac` compiler to syntax check _any_ Lua source code. However because of architectural differences between the ESP8266 chipset with its SDK and a standard PC CPU, the system APIs are different and the binary output from the standard PC `luac` cannot be run on the ESP8266. If you install Lua on your development PC or Laptop, then you can use a standard `lua` environment to develop PC applications and also use the standard `luac` compiler to syntax check _any_ Lua source code. However because of architectural differences between the ESP8266 chipset with its SDK and a standard PC CPU, the system APIs are different and the binary output from the standard PC `luac` cannot be run on the ESP8266.
To address this issue, the standard NodeMCU make now generates a host executable `lua.cross` (or `lua.cross.int` for integer builds) as well as the firmware binary itself. Compiling source on one platform for use on another is known as _cross-compilation_ and this `luac.cross` compiler allows you to compile Lua source files on your PC for downloading onto ESP8266 in a binary format. To address this issue, the standard NodeMCU make now generates a host executable `lua.cross` (or `lua.cross.int` for integer builds) as well as the firmware binary itself. Compiling source on one platform for use on another is known as _cross-compilation_ and this `luac.cross` compiler allows you to compile Lua source files on your PC for downloading onto ESP8266 in a binary format.
......
...@@ -102,7 +102,7 @@ SECTIONS ...@@ -102,7 +102,7 @@ SECTIONS
*(.entry.text) *(.entry.text)
*(.init.literal) *(.init.literal)
*(.init) *(.init)
/* SDK libraries that used in bootup process, interruption handling /* SDK libraries that used in bootup process, interruption handling
* and other ways where flash cache (iROM) is unavailable: */ * and other ways where flash cache (iROM) is unavailable: */
*libmain.a:*(.literal .literal.* .text .text.*) *libmain.a:*(.literal .literal.* .text .text.*)
...@@ -110,7 +110,7 @@ SECTIONS ...@@ -110,7 +110,7 @@ SECTIONS
*libphy.a:*(.literal .text) *libphy.a:*(.literal .text)
*libpp.a:*(.literal .text) *libpp.a:*(.literal .text)
*libgcc.a:*(.literal .text) *libgcc.a:*(.literal .text)
/* Following SDK libraries have .text sections, but not included in iRAM: */ /* Following SDK libraries have .text sections, but not included in iRAM: */
/* *libat.a:*(.literal .text) - not used anywhere in NodeMCU */ /* *libat.a:*(.literal .text) - not used anywhere in NodeMCU */
/* *libcrypto.a:*(.literal .text) - tested that safe to keep in iROM */ /* *libcrypto.a:*(.literal .text) - tested that safe to keep in iROM */
...@@ -124,7 +124,7 @@ SECTIONS ...@@ -124,7 +124,7 @@ SECTIONS
*(.iram.text .iram0.text .iram0.text.*) *(.iram.text .iram0.text .iram0.text.*)
*(.stub .gnu.warning .gnu.linkonce.literal.* .gnu.linkonce.t.*.literal .gnu.linkonce.t.*) *(.stub .gnu.warning .gnu.linkonce.literal.* .gnu.linkonce.t.*.literal .gnu.linkonce.t.*)
*(.fini.literal) *(.fini.literal)
*(.fini) *(.fini)
*(.gnu.version) *(.gnu.version)
...@@ -241,7 +241,7 @@ SECTIONS ...@@ -241,7 +241,7 @@ SECTIONS
KEEP(*(.lua_rotable)) KEEP(*(.lua_rotable))
LONG(0) LONG(0) /* Null-terminate the array */ LONG(0) LONG(0) /* Null-terminate the array */
/* SDK doesn't use libc functions, and are therefore safe to put in flash */ /* SDK doesn't use libc functions, and are therefore safe to put in flash */
*/libc.a:*.o(.text* .literal*) */libc.a:*.o(.text* .literal*)
/* end libc functions */ /* end libc functions */
......
-- --
-- Light sensor on ADC(0), RGB LED connected to gpio12(6) Green, gpio13(7) Blue & gpio15(8) Red. -- Light sensor on ADC(0), RGB LED connected to gpio12(6) Green, gpio13(7) Blue & gpio15(8) Red.
-- This works out of the box on the typical ESP8266 evaluation boards with Battery Holder -- This works out of the box on the typical ESP8266 evaluation boards with Battery Holder
-- --
-- It uses the input from the sensor to drive a "rainbow" effect on the RGB LED -- It uses the input from the sensor to drive a "rainbow" effect on the RGB LED
-- Includes a very "pseudoSin" function -- Includes a very "pseudoSin" function
-- --
function led(r,Sg,b) function led(r,Sg,b)
pwm.setduty(8,r) pwm.setduty(8,r)
pwm.setduty(6,g) pwm.setduty(6,g)
pwm.setduty(7,b) pwm.setduty(7,b)
end end
-- this is perhaps the lightest weight sin function in existance -- this is perhaps the lightest weight sin function in existance
-- Given an integer from 0..128, 0..512 appximating 256 + 256 * sin(idx*Pi/256) -- Given an integer from 0..128, 0..512 appximating 256 + 256 * sin(idx*Pi/256)
-- This is first order square approximation of sin, it's accurate around 0 and any multiple of 128 (Pi/2), -- This is first order square approximation of sin, it's accurate around 0 and any multiple of 128 (Pi/2),
-- 92% accurate at 64 (Pi/4). -- 92% accurate at 64 (Pi/4).
function pseudoSin (idx) function pseudoSin (idx)
idx = idx % 128 idx = idx % 128
lookUp = 32 - idx % 64 lookUp = 32 - idx % 64
...@@ -26,18 +26,18 @@ function pseudoSin (idx) ...@@ -26,18 +26,18 @@ function pseudoSin (idx)
return 256+val return 256+val
end end
pwm.setup(6,500,512) pwm.setup(6,500,512)
pwm.setup(7,500,512) pwm.setup(7,500,512)
pwm.setup(8,500,512) pwm.setup(8,500,512)
pwm.start(6) pwm.start(6)
pwm.start(7) pwm.start(7)
pwm.start(8) pwm.start(8)
tmr.alarm(1,20,1,function() tmr.alarm(1,20,1,function()
idx = 3 * adc.read(0) / 2 idx = 3 * adc.read(0) / 2
r = pseudoSin(idx) r = pseudoSin(idx)
g = pseudoSin(idx + 43) g = pseudoSin(idx + 43)
b = pseudoSin(idx + 85) b = pseudoSin(idx + 85)
led(r,g,b) led(r,g,b)
idx = (idx + 1) % 128 idx = (idx + 1) % 128
end) end)
--- ---
-- Working Example: https://www.youtube.com/watch?v=PDxTR_KJLhc -- Working Example: https://www.youtube.com/watch?v=PDxTR_KJLhc
-- @author Miguel (AllAboutEE.com) -- @author Miguel (AllAboutEE.com)
-- @description This example will read the first email in your inbox using IMAP and -- @description This example will read the first email in your inbox using IMAP and
-- display it through serial. The email server must provided unecrypted access. The code -- display it through serial. The email server must provided unecrypted access. The code
-- was tested with an AOL and Time Warner cable email accounts (GMail and other services who do -- was tested with an AOL and Time Warner cable email accounts (GMail and other services who do
-- not support no SSL access will not work). -- not support no SSL access will not work).
require("imap") require("imap")
local IMAP_USERNAME = "email@domain.com" local IMAP_USERNAME = "email@domain.com"
local IMAP_PASSWORD = "password" local IMAP_PASSWORD = "password"
-- find out your unencrypted imap server and port -- find out your unencrypted imap server and port
...@@ -37,7 +37,7 @@ local imap_socket = net.createConnection(net.TCP,0) ...@@ -37,7 +37,7 @@ local imap_socket = net.createConnection(net.TCP,0)
--- ---
-- @name setup -- @name setup
-- @description A call back function used to begin reading email -- @description A call back function used to begin reading email
-- upon sucessfull connection to the IMAP server -- upon sucessfull connection to the IMAP server
function setup(sck) function setup(sck)
-- Set the email user name and password, IMAP tag, and if debugging output is needed -- Set the email user name and password, IMAP tag, and if debugging output is needed
...@@ -89,7 +89,7 @@ function do_next() ...@@ -89,7 +89,7 @@ function do_next()
body = imap.get_body() -- store the BODY response in body body = imap.get_body() -- store the BODY response in body
imap.logout(imap_socket) -- Logout of the email account imap.logout(imap_socket) -- Logout of the email account
count = count + 1 count = count + 1
else else
-- display the email contents -- display the email contents
-- create patterns to strip away IMAP protocl text from actual message -- create patterns to strip away IMAP protocl text from actual message
...@@ -107,7 +107,7 @@ function do_next() ...@@ -107,7 +107,7 @@ function do_next()
body = string.gsub(body,pattern1,"") body = string.gsub(body,pattern1,"")
body = string.gsub(body,pattern2,"") body = string.gsub(body,pattern2,"")
print("Message: " .. body) print("Message: " .. body)
tmr.stop(0) -- Stop the timer alarm tmr.stop(0) -- Stop the timer alarm
imap_socket:close() -- close the IMAP socket imap_socket:close() -- close the IMAP socket
collectgarbage() -- clean up collectgarbage() -- clean up
......
...@@ -78,7 +78,7 @@ function do_next() ...@@ -78,7 +78,7 @@ function do_next()
"To: \"".. mail_to .. "\"<".. mail_to..">\r\n".. "To: \"".. mail_to .. "\"<".. mail_to..">\r\n"..
"Subject: ".. email_subject .. "\r\n\r\n" .. "Subject: ".. email_subject .. "\r\n\r\n" ..
email_body,"\r\n.\r\n","") email_body,"\r\n.\r\n","")
smtp_socket:send(message.."\r\n.\r\n") smtp_socket:send(message.."\r\n.\r\n")
elseif(count==8) then elseif(count==8) then
count = count+1 count = count+1
...@@ -91,7 +91,7 @@ end ...@@ -91,7 +91,7 @@ end
-- The connectted() function is executed when the SMTP socket is connected to the SMTP server. -- The connectted() function is executed when the SMTP socket is connected to the SMTP server.
-- This function will create a timer to call the do_next function which will send the SMTP commands -- This function will create a timer to call the do_next function which will send the SMTP commands
-- in sequence, one by one, every 5000 seconds. -- in sequence, one by one, every 5000 seconds.
-- You can change the time to be smaller if that works for you, I used 5000ms just because. -- You can change the time to be smaller if that works for you, I used 5000ms just because.
function connected(sck) function connected(sck)
tmr.alarm(0,5000,1,do_next) tmr.alarm(0,5000,1,do_next)
......
-- --
-- If you have the LFS _init loaded then you invoke the provision by -- If you have the LFS _init loaded then you invoke the provision by
-- executing LFS.HTTP_OTA('your server','directory','image name'). Note -- executing LFS.HTTP_OTA('your server','directory','image name'). Note
-- that is unencrypted and unsigned. But the loader does validate that -- that is unencrypted and unsigned. But the loader does validate that
-- the image file is a valid and complete LFS image before loading. -- the image file is a valid and complete LFS image before loading.
...@@ -9,9 +9,9 @@ local host, dir, image = ... ...@@ -9,9 +9,9 @@ local host, dir, image = ...
local doRequest, firstRec, subsRec, finalise local doRequest, firstRec, subsRec, finalise
local n, total, size = 0, 0 local n, total, size = 0, 0
doRequest = function(sk,hostIP) doRequest = function(sk,hostIP)
if hostIP then if hostIP then
local con = net.createConnection(net.TCP,0) local con = net.createConnection(net.TCP,0)
con:connect(80,hostIP) con:connect(80,hostIP)
-- Note that the current dev version can only accept uncompressed LFS images -- Note that the current dev version can only accept uncompressed LFS images
...@@ -22,7 +22,7 @@ doRequest = function(sk,hostIP) ...@@ -22,7 +22,7 @@ doRequest = function(sk,hostIP)
"Accept: application/octet-stream", "Accept: application/octet-stream",
"Accept-Encoding: identity", "Accept-Encoding: identity",
"Host: "..host, "Host: "..host,
"Connection: close", "Connection: close",
"", "", }, "\r\n") "", "", }, "\r\n")
print(request) print(request)
sck:send(request) sck:send(request)
...@@ -46,7 +46,7 @@ firstRec = function (sck,rec) ...@@ -46,7 +46,7 @@ firstRec = function (sck,rec)
sck:close() sck:close()
print("GET failed") print("GET failed")
end end
end end
subsRec = function(sck,rec) subsRec = function(sck,rec)
total, n = total + #rec, n + 1 total, n = total + #rec, n + 1
......
...@@ -2,11 +2,11 @@ ...@@ -2,11 +2,11 @@
-- File: _init.lua -- File: _init.lua
--[[ --[[
This is a template for the LFS equivalent of the SPIFFS init.lua. This is a template for the LFS equivalent of the SPIFFS init.lua.
It is a good idea to such an _init.lua module to your LFS and do most of the LFS It is a good idea to such an _init.lua module to your LFS and do most of the LFS
module related initialisaion in this. This example uses standard Lua features to module related initialisaion in this. This example uses standard Lua features to
simplify the LFS API. simplify the LFS API.
The first section adds a 'LFS' table to _G and uses the __index metamethod to The first section adds a 'LFS' table to _G and uses the __index metamethod to
resolve functions in the LFS, so you can execute the main function of module resolve functions in the LFS, so you can execute the main function of module
...@@ -24,20 +24,20 @@ ...@@ -24,20 +24,20 @@
print(table.concat(LFS._list,'\n')) print(table.concat(LFS._list,'\n'))
gives you a single column listing of all modules in the LFS. gives you a single column listing of all modules in the LFS.
---------------------------------------------------------------------------------]] ---------------------------------------------------------------------------------]]
local index = node.flashindex local index = node.flashindex
local lfs_t = { local lfs_t = {
__index = function(_, name) __index = function(_, name)
local fn_ut, ba, ma, size, modules = index(name) local fn_ut, ba, ma, size, modules = index(name)
if not ba then if not ba then
return fn_ut return fn_ut
elseif name == '_time' then elseif name == '_time' then
return fn_ut return fn_ut
elseif name == '_config' then elseif name == '_config' then
local fs_ma, fs_size = file.fscfg() local fs_ma, fs_size = file.fscfg()
return {lfs_base = ba, lfs_mapped = ma, lfs_size = size, return {lfs_base = ba, lfs_mapped = ma, lfs_size = size,
fs_mapped = fs_ma, fs_size = fs_size} fs_mapped = fs_ma, fs_size = fs_size}
elseif name == '_list' then elseif name == '_list' then
return modules return modules
...@@ -49,7 +49,7 @@ local lfs_t = { ...@@ -49,7 +49,7 @@ local lfs_t = {
__newindex = function(_, name, value) __newindex = function(_, name, value)
error("LFS is readonly. Invalid write to LFS." .. name, 2) error("LFS is readonly. Invalid write to LFS." .. name, 2)
end, end,
} }
local G=getfenv() local G=getfenv()
...@@ -59,7 +59,7 @@ G.LFS = setmetatable(lfs_t,lfs_t) ...@@ -59,7 +59,7 @@ G.LFS = setmetatable(lfs_t,lfs_t)
The second section adds the LFS to the require searchlist, so that you can The second section adds the LFS to the require searchlist, so that you can
require a Lua module 'jean' in the LFS by simply doing require "jean". However require a Lua module 'jean' in the LFS by simply doing require "jean". However
note that this is at the search entry following the FS searcher, so if you also note that this is at the search entry following the FS searcher, so if you also
have jean.lc or jean.lua in SPIFFS, then this SPIFFS version will get loaded into have jean.lc or jean.lua in SPIFFS, then this SPIFFS version will get loaded into
RAM instead of using. (Useful, for development). RAM instead of using. (Useful, for development).
See docs/en/lfs.md and the 'loaders' array in app/lua/loadlib.c for more details. See docs/en/lfs.md and the 'loaders' array in app/lua/loadlib.c for more details.
...@@ -68,19 +68,19 @@ G.LFS = setmetatable(lfs_t,lfs_t) ...@@ -68,19 +68,19 @@ G.LFS = setmetatable(lfs_t,lfs_t)
package.loaders[3] = function(module) -- loader_flash package.loaders[3] = function(module) -- loader_flash
local fn, ba = index(module) local fn, ba = index(module)
return ba and "Module not in LFS" or fn return ba and "Module not in LFS" or fn
end end
--[[------------------------------------------------------------------------------- --[[-------------------------------------------------------------------------------
You can add any other initialisation here, for example a couple of the globals You can add any other initialisation here, for example a couple of the globals
are never used, so setting them to nil saves a couple of global entries are never used, so setting them to nil saves a couple of global entries
---------------------------------------------------------------------------------]] ---------------------------------------------------------------------------------]]
G.module = nil -- disable Lua 5.0 style modules to save RAM G.module = nil -- disable Lua 5.0 style modules to save RAM
package.seeall = nil package.seeall = nil
--[[------------------------------------------------------------------------------- --[[-------------------------------------------------------------------------------
These replaces the builtins loadfile & dofile with ones which preferentially These replaces the builtins loadfile & dofile with ones which preferentially
loads the corresponding module from LFS if present. Flipping the search order loads the corresponding module from LFS if present. Flipping the search order
is an exercise left to the reader.- is an exercise left to the reader.-
---------------------------------------------------------------------------------]] ---------------------------------------------------------------------------------]]
......
...@@ -2,18 +2,18 @@ ...@@ -2,18 +2,18 @@
-- File: LFS_dummy_strings.lua -- File: LFS_dummy_strings.lua
--[[ --[[
luac.cross -f generates a ROM string table which is part of the compiled LFS luac.cross -f generates a ROM string table which is part of the compiled LFS
image. This table includes all strings referenced in the loaded modules. image. This table includes all strings referenced in the loaded modules.
If you want to preload other string constants, then one way to achieve this is If you want to preload other string constants, then one way to achieve this is
to include a dummy module in the LFS that references the strings that you want to include a dummy module in the LFS that references the strings that you want
to load. You never need to call this module; it's inclusion in the LFS image is to load. You never need to call this module; it's inclusion in the LFS image is
enough to add the strings to the ROM table. Your application can use any strings enough to add the strings to the ROM table. Your application can use any strings
in the ROM table without incuring any RAM or Lua Garbage Collector (LGC) in the ROM table without incuring any RAM or Lua Garbage Collector (LGC)
overhead. overhead.
The local preload example is a useful starting point. However, if you call the The local preload example is a useful starting point. However, if you call the
following code in your application during testing, then this will provide a following code in your application during testing, then this will provide a
listing of the current RAM string table. listing of the current RAM string table.
do do
local a=debug.getstrings'RAM' local a=debug.getstrings'RAM'
...@@ -33,5 +33,5 @@ local preload = "?.lc;?.lua", "/\n;\n?\n!\n-", "@init.lua", "_G", "_LOADED", ...@@ -33,5 +33,5 @@ local preload = "?.lc;?.lua", "/\n;\n?\n!\n-", "@init.lua", "_G", "_LOADED",
"file.obj", "file.vol", "flash", "getstrings", "index", "ipairs", "list", "loaded", "file.obj", "file.vol", "flash", "getstrings", "index", "ipairs", "list", "loaded",
"loader", "loaders", "loadlib", "module", "net.tcpserver", "net.tcpsocket", "loader", "loaders", "loadlib", "module", "net.tcpserver", "net.tcpsocket",
"net.udpsocket", "newproxy", "package", "pairs", "path", "preload", "reload", "net.udpsocket", "newproxy", "package", "pairs", "path", "preload", "reload",
"require", "seeall", "wdclr", "not enough memory", "sjson.decoder","sjson.encoder", "require", "seeall", "wdclr", "not enough memory", "sjson.decoder","sjson.encoder",
"tmr.timer" "tmr.timer"
...@@ -11,7 +11,7 @@ do ...@@ -11,7 +11,7 @@ do
end end
end end
-- --
-- This will print out 3 hex constants: the absolute address used in the -- This will print out 3 hex constants: the absolute address used in the
-- 'luac.cross -a' options and the flash adresses of the LFS and SPIFFS. -- 'luac.cross -a' options and the flash adresses of the LFS and SPIFFS.
-- --
--[[ So you would need these commands to image your ESP module: --[[ So you would need these commands to image your ESP module:
...@@ -25,7 +25,7 @@ $ESPTOOL --port $USB erase_flash # Do this is you are having load funnies ...@@ -25,7 +25,7 @@ $ESPTOOL --port $USB erase_flash # Do this is you are having load funnies
$ESPTOOL --port $USB --baud 460800 write_flash -fm dio 0x00000 \ $ESPTOOL --port $USB --baud 460800 write_flash -fm dio 0x00000 \
$BIN/0x00000.bin 0x10000 $BIN/0x10000.bin $BIN/0x00000.bin 0x10000 $BIN/0x10000.bin
# #
# Now restart your module and use whatever your intective tool is to do the above # Now restart your module and use whatever your intective tool is to do the above
# cmds, so if this outputs 0x4027b000, -0x7b000, 0x100000 then you can do # cmds, so if this outputs 0x4027b000, -0x7b000, 0x100000 then you can do
# #
$NODEMCU/luac.cross -a 0x4027b000 -o $BIN/0x7b000-flash.img $SRC/*.lua $NODEMCU/luac.cross -a 0x4027b000 -o $BIN/0x7b000-flash.img $SRC/*.lua
...@@ -42,9 +42,9 @@ $ESPTOOL --port $USB --baud 460800 write_flash -fm dio 0x100000 \ ...@@ -42,9 +42,9 @@ $ESPTOOL --port $USB --baud 460800 write_flash -fm dio 0x100000 \
-- --
-- File: init.lua -- File: init.lua
-- --
-- With the previous example you still need an init.lua to bootstrap the _init -- With the previous example you still need an init.lua to bootstrap the _init
-- module in LFS. Here is an example. It's a good idea either to use a timer -- module in LFS. Here is an example. It's a good idea either to use a timer
-- delay or a GPIO pin during development, so that you as developer can break into -- delay or a GPIO pin during development, so that you as developer can break into
-- the boot sequence if there is a problem with the _init bootstrap that is causing -- the boot sequence if there is a problem with the _init bootstrap that is causing
-- a panic loop. Here is one example of how you might do this. You have a second -- a panic loop. Here is one example of how you might do this. You have a second
-- to inject tmr.stop(0) into UART0. Extend this delay if needed. -- to inject tmr.stop(0) into UART0. Extend this delay if needed.
...@@ -52,11 +52,11 @@ $ESPTOOL --port $USB --baud 460800 write_flash -fm dio 0x100000 \ ...@@ -52,11 +52,11 @@ $ESPTOOL --port $USB --baud 460800 write_flash -fm dio 0x100000 \
-- This example will also attempt to automatically load the LFS block from a SPIFFS -- This example will also attempt to automatically load the LFS block from a SPIFFS
-- file named 'flash.img'. -- file named 'flash.img'.
-- --
if node.flashindex() == nil then if node.flashindex() == nil then
node.flashreload('flash.img') node.flashreload('flash.img')
end end
tmr.alarm(0, 1000, tmr.ALARM_SINGLE, tmr.alarm(0, 1000, tmr.ALARM_SINGLE,
function() function()
local fi=node.flashindex; return pcall(fi and fi'_init') local fi=node.flashindex; return pcall(fi and fi'_init')
end) end)
......
## ESP8266 Lua OTA ## ESP8266 Lua OTA
Espressif use an optional update approach for their firmware know as OTA (over the air). Espressif use an optional update approach for their firmware know as OTA (over the air).
This module offers an equivalent facility for Lua applications developers, and enables This module offers an equivalent facility for Lua applications developers, and enables
module development and production updates by carrying out automatic synchronisation module development and production updates by carrying out automatic synchronisation
with a named provisioning service at reboot. with a named provisioning service at reboot.
### Overview ### Overview
This `luaOTA` provisioning service uses a different approach to This `luaOTA` provisioning service uses a different approach to
[enduser setup](https://nodemcu.readthedocs.io/en/dev/en/modules/enduser-setup/). [enduser setup](https://nodemcu.readthedocs.io/en/dev/en/modules/enduser-setup/).
The basic concept here is that the ESP modules are configured with a pre-imaged file The basic concept here is that the ESP modules are configured with a pre-imaged file
system that includes a number of files in the luaOTA namespace. (SPIFFS doesn't system that includes a number of files in the luaOTA namespace. (SPIFFS doesn't
implement a directory hierarchy as such, but instead simply treats the conventional implement a directory hierarchy as such, but instead simply treats the conventional
directory separator as a character in the filename. Nonetheless, the "luaOTA/" directory separator as a character in the filename. Nonetheless, the "luaOTA/"
prefix serves to separate the lc files in the luaOTA namespace.) prefix serves to separate the lc files in the luaOTA namespace.)
- `luaOTA/check.lc` This module should always be first executed at startup. - `luaOTA/check.lc` This module should always be first executed at startup.
- `luaOTA/_init.lc` - `luaOTA/_init.lc`
- `luaOTA/_doTick.lc` - `luaOTA/_doTick.lc`
- `luaOTA/_provision.lc` - `luaOTA/_provision.lc`
...@@ -24,7 +24,7 @@ A fifth file `luaOTA/config.json` contains a JSON parameterised local configurat ...@@ -24,7 +24,7 @@ A fifth file `luaOTA/config.json` contains a JSON parameterised local configurat
can be initially create by and subsequently updated by the provisioning process. Most can be initially create by and subsequently updated by the provisioning process. Most
importantly this configuration contains the TCP address of the provisioning service, and importantly this configuration contains the TCP address of the provisioning service, and
a shared secret that is used to sign any records exchanged between the ESP client and a shared secret that is used to sign any records exchanged between the ESP client and
the provisioning service. the provisioning service.
Under this approach, `init.lua` is still required but it is reduced to a one-line lua Under this approach, `init.lua` is still required but it is reduced to a one-line lua
call which invokes the `luaOTA` module by a `require "luaOTA.check"` statement. call which invokes the `luaOTA` module by a `require "luaOTA.check"` statement.
...@@ -57,16 +57,16 @@ as `luaOTA` will fall back to transferring control to the main Lua application. ...@@ -57,16 +57,16 @@ as `luaOTA` will fall back to transferring control to the main Lua application.
In the case of an active update, **the ESP is restarted** so resource cleanup on In the case of an active update, **the ESP is restarted** so resource cleanup on
completion is not an issue. The provisioning dialogue is signed, so the host completion is not an issue. The provisioning dialogue is signed, so the host
provisioning service and the protocol are trusted, with the provisioning service driving provisioning service and the protocol are trusted, with the provisioning service driving
the process. This greatly simplifies the `luaOTA` client coding as this is a simple the process. This greatly simplifies the `luaOTA` client coding as this is a simple
responder, which actions simple commands such as: responder, which actions simple commands such as:
- download a file, - download a file,
- download and compile file, - download and compile file,
- upload a file - upload a file
- rename (or delete) a file - rename (or delete) a file
with the ESP being rebooted on completion of the updates to the SPIFFS. Hence in with the ESP being rebooted on completion of the updates to the SPIFFS. Hence in
practice the ESP boots into one one two modes: practice the ESP boots into one one two modes:
- _normal execution_ or - _normal execution_ or
- _OTA update_ followed by reboot and normal execution. - _OTA update_ followed by reboot and normal execution.
Note that even though NodeMCU follows the Lua convention of using the `lua` and `lc` Note that even though NodeMCU follows the Lua convention of using the `lua` and `lc`
...@@ -83,25 +83,25 @@ will be done automatically on reboot. ...@@ -83,25 +83,25 @@ will be done automatically on reboot.
### init.lua ### init.lua
This is typically includes a single line: This is typically includes a single line:
```Lua ```Lua
require "LuaOTA.check" require "LuaOTA.check"
``` ```
however if the configuration is incomplete then this can be aborted as manual process however if the configuration is incomplete then this can be aborted as manual process
by entering the manual command through the UART by entering the manual command through the UART
```Lua ```Lua
tmr.stop(0); require "luaOTA.check":_init {ssid ="SOMESID" --[[etc. ]]} tmr.stop(0); require "luaOTA.check":_init {ssid ="SOMESID" --[[etc. ]]}
``` ```
where the parameters to the `_init` method are: where the parameters to the `_init` method are:
- `ssid` and `spwd`. The SSID of the Wifi service to connect to, together with its - `ssid` and `spwd`. The SSID of the Wifi service to connect to, together with its
password. password.
- `server` and `port`. The name or IP address and port of the provisioning server. - `server` and `port`. The name or IP address and port of the provisioning server.
- `secret`. A site-specific secret shared with the provisioning server for MD5-based - `secret`. A site-specific secret shared with the provisioning server for MD5-based
signing of the protocol messages. signing of the protocol messages.
- `leave`. If true the STA service is left connected otherwise the wifi is shutdown - `leave`. If true the STA service is left connected otherwise the wifi is shutdown
- `espip`,`gw`,`nm`,`ns`. These parameters are omitted if the ESP is using a DHCP - `espip`,`gw`,`nm`,`ns`. These parameters are omitted if the ESP is using a DHCP
service for IP configuration, otherwise you need to specify the ESP's IP, gateway, service for IP configuration, otherwise you need to specify the ESP's IP, gateway,
netmask and default nameserver. netmask and default nameserver.
If the global `DEBUG` is set, then LuaOTA will also dump out some diagnostic debug. If the global `DEBUG` is set, then LuaOTA will also dump out some diagnostic debug.
...@@ -111,7 +111,7 @@ If the global `DEBUG` is set, then LuaOTA will also dump out some diagnostic deb ...@@ -111,7 +111,7 @@ If the global `DEBUG` is set, then LuaOTA will also dump out some diagnostic deb
This only has one public method: `_init` which can be called with the above parameters. This only has one public method: `_init` which can be called with the above parameters.
However the require wrapper in the check module also posts a call to `self:_init()` as a However the require wrapper in the check module also posts a call to `self:_init()` as a
new task. This new task function includes a guard to prevent a double call in the case new task. This new task function includes a guard to prevent a double call in the case
where the binding require includes an explicit call to `_init()` where the binding require includes an explicit call to `_init()`
Any provisioning changes results in a reboot, so the only normal "callback" is to invoke Any provisioning changes results in a reboot, so the only normal "callback" is to invoke
the application entry method as defined in `config.json` using a `node.task.post()` the application entry method as defined in `config.json` using a `node.task.post()`
...@@ -132,99 +132,99 @@ ESP SPIFFS. ...@@ -132,99 +132,99 @@ ESP SPIFFS.
## Implementation Notes ## Implementation Notes
- The NodeMCu build must include the following modules: `wifi`, `net`, `file`, `tmr`, - The NodeMCu build must include the following modules: `wifi`, `net`, `file`, `tmr`,
`crypto` and`sjason`. `crypto` and`sjason`.
- This implementation follow ephemeral practices, that it is coded to ensure that all - This implementation follow ephemeral practices, that it is coded to ensure that all
resources used are collected by the Lua GC, and hence the available heap on resources used are collected by the Lua GC, and hence the available heap on
application start is the same as if luaOTA had not been called. application start is the same as if luaOTA had not been called.
- Methods in the `check` file are static and inherit self as an upvalue. - Methods in the `check` file are static and inherit self as an upvalue.
- In order to run comfortably within ESP resources, luaOTA executes its main - In order to run comfortably within ESP resources, luaOTA executes its main
functionality as a number of overlay methods. These are loaded dynamically (and largely functionality as a number of overlay methods. These are loaded dynamically (and largely
transparently) by an `__index` metamethod. transparently) by an `__index` metamethod.
- Methods starting with a "_" are call-once and return the function reference - Methods starting with a "_" are call-once and return the function reference
- All others are also entered in the self table so that successive calls will use - All others are also entered in the self table so that successive calls will use
the preloaded function. The convention is that any dynamic function is called in object the preloaded function. The convention is that any dynamic function is called in object
form so they are loaded and executed with self as the first parameter, and hence are form so they are loaded and executed with self as the first parameter, and hence are
called using the object form self:someFunc() to get the context as a parameter. called using the object form self:someFunc() to get the context as a parameter.
- Some common routines are also defined as closures within the dynamic methods - Some common routines are also defined as closures within the dynamic methods
- This coding also makes a lot of use of tailcalls (See PiL 6.3) to keep the stack size - This coding also makes a lot of use of tailcalls (See PiL 6.3) to keep the stack size
to a minimum. to a minimum.
- The update process uses a master timer in `tmr` slot 0. The index form is used here - The update process uses a master timer in `tmr` slot 0. The index form is used here
in preference to the object form because of the reduced memory footprint. This also in preference to the object form because of the reduced memory footprint. This also
allows the developer to abort the process early in the boot sequence by issuing a allows the developer to abort the process early in the boot sequence by issuing a
`tmr.stop(0)` through UART0. `tmr.stop(0)` through UART0.
- The command protocol is unencrypted and uses JSON encoding, but all exchanges are - The command protocol is unencrypted and uses JSON encoding, but all exchanges are
signed by a 6 char signature taken extracted from a MD5 based digest across the JSON signed by a 6 char signature taken extracted from a MD5 based digest across the JSON
string. Any command which fails the signature causes the update to be aborted. Commands string. Any command which fails the signature causes the update to be aborted. Commands
are therefore regarded as trusted, and this simplifies the client module on the ESP. are therefore regarded as trusted, and this simplifies the client module on the ESP.
- The process can support both source and compiled code provisioning, but the latter - The process can support both source and compiled code provisioning, but the latter
is recommended as this permits a compile-free runtime for production use, and hence is recommended as this permits a compile-free runtime for production use, and hence
minimises the memory use and fragmentation that occurs as a consequence of compilation. minimises the memory use and fragmentation that occurs as a consequence of compilation.
- In earlier versions of the provisioning service example, I included `luaSrcDiet` but - In earlier versions of the provisioning service example, I included `luaSrcDiet` but
this changes the line numbering which I found real pain for debugging, so I now just this changes the line numbering which I found real pain for debugging, so I now just
include a simple filter to remove "--" comments and leading and trailing whitespace if include a simple filter to remove "--" comments and leading and trailing whitespace if
the source includes a `--SAFETRIM` flag. This typically reduced the size of lua files the source includes a `--SAFETRIM` flag. This typically reduced the size of lua files
transferred by ~30% and this also means that developers have no excuse for not properly transferred by ~30% and this also means that developers have no excuse for not properly
commenting their code! commenting their code!
- The chip ID is included in the configuration identification response to permit the - The chip ID is included in the configuration identification response to permit the
provisioning service to support different variants for different ESP8266 chips. provisioning service to support different variants for different ESP8266 chips.
- The (optional update & reboot) operate model also has the side effect that the - The (optional update & reboot) operate model also has the side effect that the
`LuaOTA` client can reprovision itself. `LuaOTA` client can reprovision itself.
- Though the simplest approach is always to do a `luaOTA.check` immediately on reboot, - Though the simplest approach is always to do a `luaOTA.check` immediately on reboot,
there are other strategies that could be applied, for example to test a gpio pin or a there are other strategies that could be applied, for example to test a gpio pin or a
flag in RTC memory or even have the application call the require directly (assuming that flag in RTC memory or even have the application call the require directly (assuming that
there's enough free RAM for it to run and this way avoid the connection delay to the there's enough free RAM for it to run and this way avoid the connection delay to the
WiFi. WiFi.
## Discussion on RAM usage ## Discussion on RAM usage
`luaOTA` also itself serves as a worked example of how to write ESP-friendly `luaOTA` also itself serves as a worked example of how to write ESP-friendly
applications. applications.
- The functionality is divided into autoloaded processing chunks using a self - The functionality is divided into autoloaded processing chunks using a self
autoloader, so that `self:somefunction()` calls can load new code from flash in autoloader, so that `self:somefunction()` calls can load new code from flash in
a way that is simple and largely transparent to the application. The autoloader a way that is simple and largely transparent to the application. The autoloader
preferentially loads the `lc` compiled code variant if available. preferentially loads the `lc` compiled code variant if available.
- The local environment is maintained in a self array, to keep scoping explicit. Note - The local environment is maintained in a self array, to keep scoping explicit. Note
that since loaded code cannot inherit upvalues, then `self` must be passed to the that since loaded code cannot inherit upvalues, then `self` must be passed to the
function using an object constructor `self:self:somefunction()`, but where the function function using an object constructor `self:self:somefunction()`, but where the function
can have a self argument then the alternative is to use an upvalue binding. See the can have a self argument then the alternative is to use an upvalue binding. See the
`tmr` alarm call at the end of `_init.lua` as an example: `tmr` alarm call at the end of `_init.lua` as an example:
```Lua ```Lua
tmr.alarm(0, 500, tmr.ALARM_AUTO, self:_doTick()) tmr.alarm(0, 500, tmr.ALARM_AUTO, self:_doTick())
``` ```
- The `self:_doTick()` is evaluated before the alarm API call. This autoloads - The `self:_doTick()` is evaluated before the alarm API call. This autoloads
`luaOTA/_doTick.lc` which stores `self` as a local and returns a function which takes `luaOTA/_doTick.lc` which stores `self` as a local and returns a function which takes
no arguments; it is this last returned function that is used as the timer callback, no arguments; it is this last returned function that is used as the timer callback,
and when this is called it can still access self as an upvalue. and when this is called it can still access self as an upvalue.
- This code makes a lot of use of locals and upvalues as these are both fast and use - This code makes a lot of use of locals and upvalues as these are both fast and use
less memory footprint than globals or table entries. less memory footprint than globals or table entries.
- The lua GC will mark and sweep to reclaim any unreferenced resources: tables, - The lua GC will mark and sweep to reclaim any unreferenced resources: tables,
strings, functions, userdata. So if your code at the end of a processing phase leaves strings, functions, userdata. So if your code at the end of a processing phase leaves
no variables (directly or indirectly) in _G or the Lua registry, then all of the no variables (directly or indirectly) in _G or the Lua registry, then all of the
resources that were loaded to carry out your application will be recovered by the GC. resources that were loaded to carry out your application will be recovered by the GC.
In this case heap at the end of a "no provisioning" path is less than 1Kb smaller than In this case heap at the end of a "no provisioning" path is less than 1Kb smaller than
if luaOTA had not been called and this is an artifact of how the lua_registry system if luaOTA had not been called and this is an artifact of how the lua_registry system
adopts a lazy reuse of registry entries. adopts a lazy reuse of registry entries.
- If you find that an enumeration of `debug.getregistry()` includes function references - If you find that an enumeration of `debug.getregistry()` includes function references
or tables other than ROMtables, then you have not been tidying up by doing the or tables other than ROMtables, then you have not been tidying up by doing the
appropriate closes or unregister calls. Any such stuck resources can result in a appropriate closes or unregister calls. Any such stuck resources can result in a
stuck cascade due to upvalues being preserved in the function closure or entries in a stuck cascade due to upvalues being preserved in the function closure or entries in a
table. table.
tmr.stop(0)--SAFETRIM tmr.stop(0)--SAFETRIM
-- function _doTick(self) -- function _doTick(self)
-- Upvals -- Upvals
local self = ... local self = ...
local wifi,net = wifi,net local wifi,net = wifi,net
local sta = wifi.sta local sta = wifi.sta
local config,log,startApp = self.config,self.log,self.startApp local config,log,startApp = self.config,self.log,self.startApp
local tick_count = 0 local tick_count = 0
local function socket_close(socket) --upval: self, startApp local function socket_close(socket) --upval: self, startApp
if rawget(self,"socket") then if rawget(self,"socket") then
self.socket=nil -- remove circular reference in upval self.socket=nil -- remove circular reference in upval
pcall(socket.close,socket) pcall(socket.close,socket)
return startApp("Unexpected socket close") return startApp("Unexpected socket close")
end end
...@@ -24,12 +24,12 @@ tmr.stop(0)--SAFETRIM ...@@ -24,12 +24,12 @@ tmr.stop(0)--SAFETRIM
print "No provisioning changes required" print "No provisioning changes required"
self.socket = nil self.socket = nil
self.post(function() --upval: socket self.post(function() --upval: socket
if socket then pcall(socket.close, socket) end if socket then pcall(socket.close, socket) end
end) end)
return startApp("OK! No further updates needed") return startApp("OK! No further updates needed")
end end
-- Else a valid request has been received from the provision service free up -- Else a valid request has been received from the provision service free up
-- some resources that are no longer needed and set backstop timer for general -- some resources that are no longer needed and set backstop timer for general
-- timeout. This also dereferences the previous doTick cb so it can now be GCed. -- timeout. This also dereferences the previous doTick cb so it can now be GCed.
collectgarbage() collectgarbage()
tmr.alarm(0, 30000, tmr.ALARM_SINGLE, self.startApp) tmr.alarm(0, 30000, tmr.ALARM_SINGLE, self.startApp)
...@@ -44,7 +44,7 @@ tmr.stop(0)--SAFETRIM ...@@ -44,7 +44,7 @@ tmr.stop(0)--SAFETRIM
return self.socket_send(socket, self.config) return self.socket_send(socket, self.config)
end end
local conn local conn
return function() -- the proper doTick() timer callback return function() -- the proper doTick() timer callback
tick_count = tick_count + 1 tick_count = tick_count + 1
log("entering tick", tick_count, sta.getconfig(false), sta.getip()) log("entering tick", tick_count, sta.getconfig(false), sta.getip())
......
--SAFETRIM --SAFETRIM
-- function _init(self, args) -- function _init(self, args)
local self, args = ... local self, args = ...
-- The config is read from config.json but can be overridden by explicitly -- The config is read from config.json but can be overridden by explicitly
-- setting the following args. Setting to "nil" deletes the config arg. -- setting the following args. Setting to "nil" deletes the config arg.
-- --
-- ssid, spwd Credentials for the WiFi -- ssid, spwd Credentials for the WiFi
-- server, port, secret Provisioning server:port and signature secret -- server, port, secret Provisioning server:port and signature secret
-- leave If true then the Wifi is left connected -- leave If true then the Wifi is left connected
...@@ -45,5 +45,5 @@ ...@@ -45,5 +45,5 @@
package.loaded[self.modname] = nil package.loaded[self.modname] = nil
self.modname=nil self.modname=nil
tmr.alarm(0, 500, tmr.ALARM_AUTO, self:_doTick()) tmr.alarm(0, 500, tmr.ALARM_AUTO, self:_doTick())
-- end -- end
--SAFETRIM --SAFETRIM
-- function _provision(self,socket,first_rec) -- function _provision(self,socket,first_rec)
local self, socket, first_rec = ... local self, socket, first_rec = ...
local crypto, file, json, node, table = crypto, file, sjson, node, table local crypto, file, json, node, table = crypto, file, sjson, node, table
local stripdebug, gc = node.stripdebug, collectgarbage local stripdebug, gc = node.stripdebug, collectgarbage
local buf = {} local buf = {}
gc(); gc() gc(); gc()
local function getbuf() -- upval: buf, table local function getbuf() -- upval: buf, table
if #buf > 0 then return table.remove(buf, 1) end -- else return nil if #buf > 0 then return table.remove(buf, 1) end -- else return nil
...@@ -14,8 +14,8 @@ end ...@@ -14,8 +14,8 @@ end
-- Process a provisioning request record -- Process a provisioning request record
local function receiveRec(socket, rec) -- upval: self, buf, crypto local function receiveRec(socket, rec) -- upval: self, buf, crypto
-- Note that for 2nd and subsequent responses, we assme that the service has -- Note that for 2nd and subsequent responses, we assme that the service has
-- "authenticated" itself, so any protocol errors are fatal and lkely to -- "authenticated" itself, so any protocol errors are fatal and lkely to
-- cause a repeating boot, throw any protocol errors are thrown. -- cause a repeating boot, throw any protocol errors are thrown.
local buf, config, file, log = buf, self.config, file, self.log local buf, config, file, log = buf, self.config, file, self.log
local cmdlen = (rec:find('\n',1, true) or 0) - 1 local cmdlen = (rec:find('\n',1, true) or 0) - 1
...@@ -70,7 +70,7 @@ local function receiveRec(socket, rec) -- upval: self, buf, crypto ...@@ -70,7 +70,7 @@ local function receiveRec(socket, rec) -- upval: self, buf, crypto
if s then if s then
resp.lcsize=#code resp.lcsize=#code
print("Updated ".. name) print("Updated ".. name)
else else
msg = "file write failed" msg = "file write failed"
end end
end end
...@@ -87,15 +87,15 @@ local function receiveRec(socket, rec) -- upval: self, buf, crypto ...@@ -87,15 +87,15 @@ local function receiveRec(socket, rec) -- upval: self, buf, crypto
end end
file.close() file.close()
end end
if s then if s then
print("Updated ".. name) print("Updated ".. name)
else else
file.remove(name) file.remove(name)
resp.s = "write failed" resp.s = "write failed"
end end
buf = {} buf = {}
elseif action == "ul" then elseif action == "ul" then
if file.open(cmd.name, "r") then if file.open(cmd.name, "r") then
file.seek("set", cmd.offset) file.seek("set", cmd.offset)
...@@ -120,6 +120,6 @@ local function receiveRec(socket, rec) -- upval: self, buf, crypto ...@@ -120,6 +120,6 @@ local function receiveRec(socket, rec) -- upval: self, buf, crypto
end end
-- Replace the receive CB by the provisioning version and then tailcall this to -- Replace the receive CB by the provisioning version and then tailcall this to
-- process this first record. -- process this first record.
socket:on("receive", receiveRec) socket:on("receive", receiveRec)
return receiveRec(socket, first_rec) return receiveRec(socket, first_rec)
...@@ -11,22 +11,22 @@ ...@@ -11,22 +11,22 @@
-- upvals -- upvals
local crypto, file, json, net, node, table, tmr, wifi = local crypto, file, json, net, node, table, tmr, wifi =
crypto, file, sjson, net, node, table, tmr, wifi crypto, file, sjson, net, node, table, tmr, wifi
local error, pcall = error, pcall local error, pcall = error, pcall
local loadfile, gc = loadfile, collectgarbage local loadfile, gc = loadfile, collectgarbage
local concat, unpack = table.concat, unpack or table.unpack local concat, unpack = table.concat, unpack or table.unpack
local self = {post = node.task.post, prefix = "luaOTA/", conf = {}} local self = {post = node.task.post, prefix = "luaOTA/", conf = {}}
self.log = (DEBUG == true) and print or function() end self.log = (DEBUG == true) and print or function() end
self.modname = ... self.modname = ...
-------------------------------------------------------------------------------------- --------------------------------------------------------------------------------------
-- Utility Functions -- Utility Functions
setmetatable( self, {__index=function(self, func) --upval: loadfile setmetatable( self, {__index=function(self, func) --upval: loadfile
-- The only __index calls in in LuaOTA are dynamically loaded functions. -- The only __index calls in in LuaOTA are dynamically loaded functions.
-- The convention is that functions starting with "_" are treated as -- The convention is that functions starting with "_" are treated as
-- call-once / ephemeral; the rest are registered in self -- call-once / ephemeral; the rest are registered in self
func = self.prefix .. func func = self.prefix .. func
local f,msg = loadfile( func..".lc") local f,msg = loadfile( func..".lc")
if msg then f, msg = loadfile(func..".lua") end if msg then f, msg = loadfile(func..".lua") end
...@@ -48,19 +48,19 @@ function self.startApp(arg) --upval: gc, self, tmr, wifi ...@@ -48,19 +48,19 @@ function self.startApp(arg) --upval: gc, self, tmr, wifi
local appMod = self.config.app or "luaOTA.default" local appMod = self.config.app or "luaOTA.default"
local appMethod = self.config.entry or "entry" local appMethod = self.config.entry or "entry"
if not arg then arg = "General timeout on provisioning" end if not arg then arg = "General timeout on provisioning" end
self.post(function() --upval: appMod, appMethod, arg self.post(function() --upval: appMod, appMethod, arg
require(appMod)[appMethod](arg) require(appMod)[appMethod](arg)
end) end)
end end
function self.socket_send(socket, rec, opt_buffer) function self.socket_send(socket, rec, opt_buffer)
return socket:send(self.sign(rec) .. (opt_buffer or '')) return socket:send(self.sign(rec) .. (opt_buffer or ''))
end end
self.post(function() -- upval: self self.post(function() -- upval: self
-- This config check is to prevent a double execution if the -- This config check is to prevent a double execution if the
-- user invokes with "require 'luaOTA/check':_init( etc>)" form -- user invokes with "require 'luaOTA/check':_init( etc>)" form
if not rawget(self, "config") then self:_init() end if not rawget(self, "config") then self:_init() end
end) end)
return self return self
-- --
local function enum(t,log) for k,v in pairs(t)do log(k,v) end end local function enum(t,log) for k,v in pairs(t)do log(k,v) end end
return {entry = function(msg) return {entry = function(msg)
package.loaded["luaOTA.default"]=nil package.loaded["luaOTA.default"]=nil
local gc=collectgarbage; gc(); gc() local gc=collectgarbage; gc(); gc()
if DEBUG then if DEBUG then
for k,v in pairs(_G) do print(k,v) end for k,v in pairs(_G) do print(k,v) end
for k,v in pairs(debug.getregistry()) do print(k,v) end for k,v in pairs(debug.getregistry()) do print(k,v) end
end end
gc(); gc() gc(); gc()
print(msg, node.heap()) print(msg, node.heap())
end} end}
...@@ -6,10 +6,10 @@ ...@@ -6,10 +6,10 @@
-- See luaOTA.md for description -- See luaOTA.md for description
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
--[[ luaOTAserver.lua - an example provisioning server --[[ luaOTAserver.lua - an example provisioning server
This module implements an example server-side implementation of LuaOTA provisioning This module implements an example server-side implementation of LuaOTA provisioning
system for ESPs used the SPI Flash FS (SPIFFS) on development and production modules. system for ESPs used the SPI Flash FS (SPIFFS) on development and production modules.
This implementation is a simple TCP listener which can have one active provisioning This implementation is a simple TCP listener which can have one active provisioning
client executing the luaOTA module at a time. It will synchronise the client's FS client executing the luaOTA module at a time. It will synchronise the client's FS
...@@ -40,9 +40,9 @@ local concat = table.concat ...@@ -40,9 +40,9 @@ local concat = table.concat
local load = loadstring or load local load = loadstring or load
local format = string.format local format = string.format
-- use string % operators as a synomyn for string.format -- use string % operators as a synomyn for string.format
getmetatable("").__mod = getmetatable("").__mod =
function(a, b) function(a, b)
return not b and a or return not b and a or
(type(b) == "table" and format(a, unpack(b)) or format(a, b)) (type(b) == "table" and format(a, unpack(b)) or format(a, b))
end end
...@@ -56,18 +56,18 @@ local function main () ...@@ -56,18 +56,18 @@ local function main ()
local server = assert(socket.bind("*", ESPport)) local server = assert(socket.bind("*", ESPport))
local ip, port = server:getsockname() local ip, port = server:getsockname()
print("Lua OTA service listening on %s:%u\n After connecting, the ESP timeout is %u s" print("Lua OTA service listening on %s:%u\n After connecting, the ESP timeout is %u s"
% {ip, port, ESPtimeout}) % {ip, port, ESPtimeout})
-- Main loop forever waiting for ESP clients then processing each request ------------ -- Main loop forever waiting for ESP clients then processing each request ------------
while true do while true do
local esp = server:accept() -- wait for ESP connection local esp = server:accept() -- wait for ESP connection
esp:settimeout(ESPtimeout) -- set session timeout esp:settimeout(ESPtimeout) -- set session timeout
-- receive the opening request -- receive the opening request
local config = receive_and_parse(esp) local config = receive_and_parse(esp)
if config and config.a == "HI" then if config and config.a == "HI" then
print ("Processing provision check from ESP-"..config.id) print ("Processing provision check from ESP-"..config.id)
local inventory, fingerprint = get_inventory(src_dir, config.id) local inventory, fingerprint = get_inventory(src_dir, config.id)
-- Process the ESP request -- Process the ESP request
...@@ -77,7 +77,7 @@ local function main () ...@@ -77,7 +77,7 @@ local function main ()
else else
local status, msg = pcall(provision, esp, config, inventory, fingerprint) local status, msg = pcall(provision, esp, config, inventory, fingerprint)
if not status then print (msg) end if not status then print (msg) end
end end
end end
pcall(esp.close, esp) pcall(esp.close, esp)
...@@ -88,8 +88,8 @@ end ...@@ -88,8 +88,8 @@ end
-- Local Function Implementations ------------------------------------------------------ -- Local Function Implementations ------------------------------------------------------
local function get_hmac_md5(key) local function get_hmac_md5(key)
if key:len() > 64 then if key:len() > 64 then
key = md5.sum(key) key = md5.sum(key)
elseif key:len() < 64 then elseif key:len() < 64 then
key = key .. ('\0'):rep(64-key:len()) key = key .. ('\0'):rep(64-key:len())
end end
...@@ -105,8 +105,8 @@ get_inventory = function(dir, cpuid) ...@@ -105,8 +105,8 @@ get_inventory = function(dir, cpuid)
error("Cannot open directory, aborting %s" % arg[0], 0) error("Cannot open directory, aborting %s" % arg[0], 0)
end end
-- Load the CPU's (or the default) inventory -- Load the CPU's (or the default) inventory
local invtype, inventory = "custom", read_file("%s/ESP-%s.json" % {dir, cpuid}) local invtype, inventory = "custom", read_file("%s/ESP-%s.json" % {dir, cpuid})
if not inventory then if not inventory then
invtype, inventory = "default", read_file(dir .. "/default.json") invtype, inventory = "default", read_file(dir .. "/default.json")
end end
...@@ -129,15 +129,15 @@ get_inventory = function(dir, cpuid) ...@@ -129,15 +129,15 @@ get_inventory = function(dir, cpuid)
assert(fa, "File %s is required but not in sources directory" % name) assert(fa, "File %s is required but not in sources directory" % name)
fp[#fp+1] = name .. ":" .. fa.modification fp[#fp+1] = name .. ":" .. fa.modification
f[i] = {name = name, mtime = fa.modification, f[i] = {name = name, mtime = fa.modification,
size = fa.size, content = read_file(fullname) } size = fa.size, content = read_file(fullname) }
assert (f[i].size == #(f[i].content or ''), "File %s unreadable" % name ) assert (f[i].size == #(f[i].content or ''), "File %s unreadable" % name )
end end
assert(#f == #fp, "Aborting provisioning die to missing fies",0) assert(#f == #fp, "Aborting provisioning die to missing fies",0)
assert(type(inventory.secret) == "string", assert(type(inventory.secret) == "string",
"Aborting, config must contain a shared secret") "Aborting, config must contain a shared secret")
hmac = get_hmac_md5(inventory.secret) hmac = get_hmac_md5(inventory.secret)
return inventory, md5.sumhexa(concat(fp,":")) return inventory, md5.sumhexa(concat(fp,":"))
end end
...@@ -145,15 +145,15 @@ end ...@@ -145,15 +145,15 @@ end
-- Encode a response buff, add a signature and any optional buffer -- Encode a response buff, add a signature and any optional buffer
------------------------------------------------------------------ ------------------------------------------------------------------
send_command = function(esp, resp, buffer) send_command = function(esp, resp, buffer)
if type(buffer) == "string" then if type(buffer) == "string" then
resp.data = #buffer resp.data = #buffer
else else
buffer = '' buffer = ''
end end
local rec = json.encode(resp) local rec = json.encode(resp)
rec = rec .. hmac(rec):sub(-6) .."\n" rec = rec .. hmac(rec):sub(-6) .."\n"
-- print("requesting ", rec:sub(1,-2), #(buffer or '')) -- print("requesting ", rec:sub(1,-2), #(buffer or ''))
esp:send(rec .. buffer) esp:send(rec .. buffer)
end end
...@@ -173,10 +173,10 @@ receive_and_parse = function(esp) ...@@ -173,10 +173,10 @@ receive_and_parse = function(esp)
end end
end end
provision = function(esp, config, inventory, fingerprint) provision = function(esp, config, inventory, fingerprint)
if type(config.files) ~= "table" then config.files = {} end if type(config.files) ~= "table" then config.files = {} end
local cf = config.files local cf = config.files
for _, f in ipairs(inventory.files) do for _, f in ipairs(inventory.files) do
...@@ -190,7 +190,7 @@ provision = function(esp, config, inventory, fingerprint) ...@@ -190,7 +190,7 @@ provision = function(esp, config, inventory, fingerprint)
-- if the source is tagged with SAFETRIM then its safe to remove "--" -- if the source is tagged with SAFETRIM then its safe to remove "--"
-- comments, leading and trailing whitespace. Not as good as LuaSrcDiet, -- comments, leading and trailing whitespace. Not as good as LuaSrcDiet,
-- but this simple source compression algo preserves line numbering in -- but this simple source compression algo preserves line numbering in
-- the generated lc files, which helps debugging. -- the generated lc files, which helps debugging.
content = content:gsub("\n[ \t]+","\n") content = content:gsub("\n[ \t]+","\n")
content = content:gsub("[ \t]+\n","\n") content = content:gsub("[ \t]+\n","\n")
content = content:gsub("%-%-[^\n]*","") content = content:gsub("%-%-[^\n]*","")
...@@ -201,7 +201,7 @@ provision = function(esp, config, inventory, fingerprint) ...@@ -201,7 +201,7 @@ provision = function(esp, config, inventory, fingerprint)
action = "dl" action = "dl"
end end
print ("Sending file ".. name) print ("Sending file ".. name)
for i = 1, size, 1024 do for i = 1, size, 1024 do
if i+1023 < size then if i+1023 < size then
cmd = {a = "pu", data = 1024} cmd = {a = "pu", data = 1024}
...@@ -209,11 +209,11 @@ provision = function(esp, config, inventory, fingerprint) ...@@ -209,11 +209,11 @@ provision = function(esp, config, inventory, fingerprint)
else else
cmd = {a = action, data = size - i + 1, name = name} cmd = {a = action, data = size - i + 1, name = name}
buf = content:sub(i) buf = content:sub(i)
end end
send_command(esp, cmd, buf) send_command(esp, cmd, buf)
local resp = receive_and_parse(esp) local resp = receive_and_parse(esp)
assert(resp and resp.s == "OK", "Command to ESP failed") assert(resp and resp.s == "OK", "Command to ESP failed")
if resp.lcsize then if resp.lcsize then
print("Compiled file size %s bytes" % resp.lcsize) print("Compiled file size %s bytes" % resp.lcsize)
end end
end end
...@@ -243,7 +243,7 @@ end ...@@ -243,7 +243,7 @@ end
---------------------------------- ----------------------------------
save_file = function(fname, data) save_file = function(fname, data)
local file = io.open(fname, "wb") local file = io.open(fname, "wb")
file:write(data) file:write(data)
file:close() file:close()
end end
......
--[[ --[[
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
documentation files (the "Software"), to deal in the Software without restriction, documentation files (the "Software"), to deal in the Software without restriction,
including without limitation the rights to use, copy, modify, merge, publish, distribute, including without limitation the rights to use, copy, modify, merge, publish, distribute,
sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions: furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software. included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
]]-- ]]--
...@@ -35,7 +35,7 @@ local URI = "/twilio/Calls.json" ...@@ -35,7 +35,7 @@ local URI = "/twilio/Calls.json"
function build_post_request(host, uri, data_table) function build_post_request(host, uri, data_table)
local data = "" local data = ""
for param,value in pairs(data_table) do for param,value in pairs(data_table) do
data = data .. param.."="..value.."&" data = data .. param.."="..value.."&"
end end
...@@ -49,7 +49,7 @@ function build_post_request(host, uri, data_table) ...@@ -49,7 +49,7 @@ function build_post_request(host, uri, data_table)
data data
print(request) print(request)
return request return request
end end
...@@ -68,16 +68,16 @@ local function make_call(from,to,body) ...@@ -68,16 +68,16 @@ local function make_call(from,to,body)
From = from, From = from,
To = to To = to
} }
socket = net.createConnection(net.TCP,0) socket = net.createConnection(net.TCP,0)
socket:on("receive",display) socket:on("receive",display)
socket:connect(80,HOST) socket:connect(80,HOST)
socket:on("connection",function(sck) socket:on("connection",function(sck)
local post_request = build_post_request(HOST,URI,data) local post_request = build_post_request(HOST,URI,data)
sck:send(post_request) sck:send(post_request)
end) end)
end end
function check_wifi() function check_wifi()
...@@ -90,9 +90,9 @@ function check_wifi() ...@@ -90,9 +90,9 @@ function check_wifi()
print("Connected to AP!") print("Connected to AP!")
print(ip) print(ip)
-- make a call with a voice message "your house is on fire" -- make a call with a voice message "your house is on fire"
make_call("15558976687","1334856679","Your house is on fire!") make_call("15558976687","1334856679","Your house is on fire!")
end end
end end
tmr.alarm(0,2000,1,check_wifi) tmr.alarm(0,2000,1,check_wifi)
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment