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.
end
wps.disable()
end)
--Full example
do
-- Register wifi station event callbacks
......@@ -84,10 +84,10 @@ Start WiFi WPS function. WPS must be enabled prior calling this function.
end)
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 < 3 then
if wps_retry_count < 3 then
wps.disable()
wps.enable()
wps_retry_count = wps_retry_count + 1
......@@ -101,7 +101,7 @@ Start WiFi WPS function. WPS must be enabled prior calling this function.
wps_cb = nil
end
end
wps_cb = function(status)
if status == wps.SUCCESS then
wps.disable()
......@@ -138,5 +138,5 @@ Start WiFi WPS function. WPS must be enabled prior calling this function.
wps.enable()
wps.start(wps_cb)
end
```
......@@ -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.
**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
protocol). If you want to keep access to Lua's console, you will have to
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
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()
......@@ -191,11 +191,11 @@ Returns the contents of the buffer (the pixel values) as a string. This can then
`buffer:dump()`
#### Returns
A string containing the pixel values.
A string containing the pixel values.
#### Example
```lua
local s = buffer:dump()
local s = buffer:dump()
```
## ws2812.buffer:replace()
......@@ -249,7 +249,7 @@ buffer:mix(192, buffer)
## 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
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
`buffer:power()`
......@@ -271,7 +271,7 @@ end
```
## 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
`buffer:fade(value [, direction])`
......@@ -289,15 +289,15 @@ buffer:fade(2)
buffer:fade(2, ws2812.FADE_IN)
```
## 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.
#### Syntax
`buffer:shift(value [, mode[, i[, j]]])`
#### Parameters
- `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.
- `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.
- `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.
......
# 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.
# 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
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.
### Syntax
### Syntax
```
spiffsimg -f <filename>
spiffsimg -f <filename>
[-o <offsetfile>]
[-c <size>]
[-c <size>]
[-S <flashsize>]
[-U <usedsize>]
[-d]
......@@ -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
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
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
system will survive lots of reflashing and at least 64k of firmware growth.
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
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.
The standard build process for the firmware builds the `spiffsimg` tool (found in the `tools/spiffsimg` subdirectory).
The top level Makfile also checks if
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.
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
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.
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.
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.
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
#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
filesystem is reformatted from Lua (using file.format()) then the new file system will obey the size limit.
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.
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
```
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.
......
As with [flashing](flash.md) there are several ways to upload code from your computer to the device.
!!! 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.
## Tools
......@@ -56,32 +56,32 @@ function startup()
end
end
-- Define WiFi station event callbacks
wifi_connect_event = function(T)
-- Define WiFi station event callbacks
wifi_connect_event = function(T)
print("Connection to AP("..T.SSID..") established!")
print("Waiting for IP address...")
if disconnect_ct ~= nil then disconnect_ct = nil end
if disconnect_ct ~= nil then disconnect_ct = nil 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!
-- 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("Startup will resume momentarily, you have 3 seconds to abort.")
print("Waiting...")
print("Waiting...")
tmr.create():alarm(3000, tmr.ALARM_SINGLE, startup)
end
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
return
return
end
-- total_tries: how many times the station will attempt to connect to the AP. Should consider AP reboot duration.
local total_tries = 75
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.
for key,val in pairs(wifi.eventmon.reason) do
if val == T.reason then
......@@ -90,17 +90,17 @@ wifi_disconnect_event = function(T)
end
end
if disconnect_ct == nil then
disconnect_ct = 1
if disconnect_ct == nil then
disconnect_ct = 1
else
disconnect_ct = disconnect_ct + 1
disconnect_ct = disconnect_ct + 1
end
if disconnect_ct < total_tries then
if disconnect_ct < total_tries then
print("Retrying connection...(attempt "..(disconnect_ct+1).." of "..total_tries..")")
else
wifi.sta.disconnect()
print("Aborting connection to AP!")
disconnect_ct = nil
disconnect_ct = nil
end
end
......@@ -118,7 +118,7 @@ wifi.sta.config({ssid=SSID, pwd=PASSWORD})
## 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.
......
......@@ -102,7 +102,7 @@ SECTIONS
*(.entry.text)
*(.init.literal)
*(.init)
/* SDK libraries that used in bootup process, interruption handling
* and other ways where flash cache (iROM) is unavailable: */
*libmain.a:*(.literal .literal.* .text .text.*)
......@@ -110,7 +110,7 @@ SECTIONS
*libphy.a:*(.literal .text)
*libpp.a:*(.literal .text)
*libgcc.a:*(.literal .text)
/* Following SDK libraries have .text sections, but not included in iRAM: */
/* *libat.a:*(.literal .text) - not used anywhere in NodeMCU */
/* *libcrypto.a:*(.literal .text) - tested that safe to keep in iROM */
......@@ -124,7 +124,7 @@ SECTIONS
*(.iram.text .iram0.text .iram0.text.*)
*(.stub .gnu.warning .gnu.linkonce.literal.* .gnu.linkonce.t.*.literal .gnu.linkonce.t.*)
*(.fini.literal)
*(.fini)
*(.gnu.version)
......@@ -241,7 +241,7 @@ SECTIONS
KEEP(*(.lua_rotable))
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*)
/* end libc functions */
......
--
--
-- 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
--
--
-- It uses the input from the sensor to drive a "rainbow" effect on the RGB LED
-- Includes a very "pseudoSin" function
--
function led(r,Sg,b)
pwm.setduty(8,r)
pwm.setduty(6,g)
pwm.setduty(7,b)
function led(r,Sg,b)
pwm.setduty(8,r)
pwm.setduty(6,g)
pwm.setduty(7,b)
end
-- 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)
-- 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).
-- 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).
function pseudoSin (idx)
idx = idx % 128
lookUp = 32 - idx % 64
......@@ -26,18 +26,18 @@ function pseudoSin (idx)
return 256+val
end
pwm.setup(6,500,512)
pwm.setup(7,500,512)
pwm.setup(6,500,512)
pwm.setup(7,500,512)
pwm.setup(8,500,512)
pwm.start(6)
pwm.start(7)
pwm.start(6)
pwm.start(7)
pwm.start(8)
tmr.alarm(1,20,1,function()
tmr.alarm(1,20,1,function()
idx = 3 * adc.read(0) / 2
r = pseudoSin(idx)
g = pseudoSin(idx + 43)
b = pseudoSin(idx + 85)
led(r,g,b)
idx = (idx + 1) % 128
idx = (idx + 1) % 128
end)
---
-- Working Example: https://www.youtube.com/watch?v=PDxTR_KJLhc
-- @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
-- 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).
require("imap")
local IMAP_USERNAME = "email@domain.com"
local IMAP_USERNAME = "email@domain.com"
local IMAP_PASSWORD = "password"
-- find out your unencrypted imap server and port
......@@ -37,7 +37,7 @@ local imap_socket = net.createConnection(net.TCP,0)
---
-- @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
function setup(sck)
-- Set the email user name and password, IMAP tag, and if debugging output is needed
......@@ -89,7 +89,7 @@ function do_next()
body = imap.get_body() -- store the BODY response in body
imap.logout(imap_socket) -- Logout of the email account
count = count + 1
else
else
-- display the email contents
-- create patterns to strip away IMAP protocl text from actual message
......@@ -107,7 +107,7 @@ function do_next()
body = string.gsub(body,pattern1,"")
body = string.gsub(body,pattern2,"")
print("Message: " .. body)
tmr.stop(0) -- Stop the timer alarm
imap_socket:close() -- close the IMAP socket
collectgarbage() -- clean up
......
......@@ -78,7 +78,7 @@ function do_next()
"To: \"".. mail_to .. "\"<".. mail_to..">\r\n"..
"Subject: ".. email_subject .. "\r\n\r\n" ..
email_body,"\r\n.\r\n","")
smtp_socket:send(message.."\r\n.\r\n")
elseif(count==8) then
count = count+1
......@@ -91,7 +91,7 @@ end
-- 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
-- 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.
function connected(sck)
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
-- that is unencrypted and unsigned. But the loader does validate that
-- the image file is a valid and complete LFS image before loading.
......@@ -9,9 +9,9 @@ local host, dir, image = ...
local doRequest, firstRec, subsRec, finalise
local n, total, size = 0, 0
doRequest = function(sk,hostIP)
if hostIP then
if hostIP then
local con = net.createConnection(net.TCP,0)
con:connect(80,hostIP)
-- Note that the current dev version can only accept uncompressed LFS images
......@@ -22,7 +22,7 @@ doRequest = function(sk,hostIP)
"Accept: application/octet-stream",
"Accept-Encoding: identity",
"Host: "..host,
"Connection: close",
"Connection: close",
"", "", }, "\r\n")
print(request)
sck:send(request)
......@@ -46,7 +46,7 @@ firstRec = function (sck,rec)
sck:close()
print("GET failed")
end
end
end
subsRec = function(sck,rec)
total, n = total + #rec, n + 1
......
......@@ -2,11 +2,11 @@
-- 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
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
resolve functions in the LFS, so you can execute the main function of module
......@@ -24,20 +24,20 @@
print(table.concat(LFS._list,'\n'))
gives you a single column listing of all modules in the LFS.
---------------------------------------------------------------------------------]]
---------------------------------------------------------------------------------]]
local index = node.flashindex
local lfs_t = {
__index = function(_, name)
local fn_ut, ba, ma, size, modules = index(name)
if not ba then
if not ba then
return fn_ut
elseif name == '_time' then
return fn_ut
elseif name == '_config' then
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}
elseif name == '_list' then
return modules
......@@ -49,7 +49,7 @@ local lfs_t = {
__newindex = function(_, name, value)
error("LFS is readonly. Invalid write to LFS." .. name, 2)
end,
}
local G=getfenv()
......@@ -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
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
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).
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)
package.loaders[3] = function(module) -- loader_flash
local fn, ba = index(module)
return ba and "Module not in LFS" or fn
return ba and "Module not in LFS" or fn
end
--[[-------------------------------------------------------------------------------
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
---------------------------------------------------------------------------------]]
G.module = nil -- disable Lua 5.0 style modules to save RAM
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
is an exercise left to the reader.-
---------------------------------------------------------------------------------]]
......
......@@ -2,18 +2,18 @@
-- File: LFS_dummy_strings.lua
--[[
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
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
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)
overhead.
overhead.
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
listing of the current RAM string table.
following code in your application during testing, then this will provide a
listing of the current RAM string table.
do
local a=debug.getstrings'RAM'
......@@ -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",
"loader", "loaders", "loadlib", "module", "net.tcpserver", "net.tcpsocket",
"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"
......@@ -11,7 +11,7 @@ do
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.
--
--[[ 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
$ESPTOOL --port $USB --baud 460800 write_flash -fm dio 0x00000 \
$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
#
$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 \
--
-- File: init.lua
--
-- 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
-- delay or a GPIO pin during development, so that you as developer can break into
-- 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
-- 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
-- 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.
......@@ -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
-- file named 'flash.img'.
--
if node.flashindex() == nil then
node.flashreload('flash.img')
if node.flashindex() == nil then
node.flashreload('flash.img')
end
tmr.alarm(0, 1000, tmr.ALARM_SINGLE,
tmr.alarm(0, 1000, tmr.ALARM_SINGLE,
function()
local fi=node.flashindex; return pcall(fi and fi'_init')
end)
......
## ESP8266 Lua OTA
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
module development and production updates by carrying out automatic synchronisation
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
module development and production updates by carrying out automatic synchronisation
with a named provisioning service at reboot.
### 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/).
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
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
implement a directory hierarchy as such, but instead simply treats the conventional
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/_init.lc`
- `luaOTA/_init.lc`
- `luaOTA/_doTick.lc`
- `luaOTA/_provision.lc`
......@@ -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
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
the provisioning service.
the provisioning service.
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.
......@@ -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
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
the process. This greatly simplifies the `luaOTA` client coding as this is a simple
responder, which actions simple commands such as:
- download a file,
the process. This greatly simplifies the `luaOTA` client coding as this is a simple
responder, which actions simple commands such as:
- download a file,
- download and compile file,
- upload a file
- rename (or delete) a file
with the ESP being rebooted on completion of the updates to the SPIFFS. Hence in
practice the ESP boots into one one two modes:
- _normal execution_ or
with the ESP being rebooted on completion of the updates to the SPIFFS. Hence in
practice the ESP boots into one one two modes:
- _normal execution_ or
- _OTA update_ followed by reboot and normal execution.
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.
### init.lua
This is typically includes a single line:
This is typically includes a single line:
```Lua
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
```Lua
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.
- `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.
- `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
service for IP configuration, otherwise you need to specify the ESP's IP, gateway,
- `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,
netmask and default nameserver.
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
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
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
the application entry method as defined in `config.json` using a `node.task.post()`
......@@ -132,99 +132,99 @@ ESP SPIFFS.
## Implementation Notes
- 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
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.
- 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
transparently) by an `__index` metamethod.
- 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
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.
- 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
to a minimum.
- This coding also makes a lot of use of tailcalls (See PiL 6.3) to keep the stack size
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
allows the developer to abort the process early in the boot sequence by issuing a
`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
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.
- 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
- 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
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
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
transferred by ~30% and this also means that developers have no excuse for not properly
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.
- 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.
- 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
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
- 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
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
WiFi.
## 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.
- 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
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.
- 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
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:
```Lua
tmr.alarm(0, 500, tmr.ALARM_AUTO, self:_doTick())
```
- 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
- 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
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.
- 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.
- 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
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.
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.
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.
- 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
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
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
stuck cascade due to upvalues being preserved in the function closure or entries in a
table.
tmr.stop(0)--SAFETRIM
tmr.stop(0)--SAFETRIM
-- function _doTick(self)
-- Upvals
local self = ...
local wifi,net = wifi,net
local wifi,net = wifi,net
local sta = wifi.sta
local config,log,startApp = self.config,self.log,self.startApp
local tick_count = 0
local function socket_close(socket) --upval: self, startApp
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)
return startApp("Unexpected socket close")
end
......@@ -24,12 +24,12 @@ tmr.stop(0)--SAFETRIM
print "No provisioning changes required"
self.socket = nil
self.post(function() --upval: socket
if socket then pcall(socket.close, socket) end
if socket then pcall(socket.close, socket) end
end)
return startApp("OK! No further updates needed")
end
-- 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.
collectgarbage()
tmr.alarm(0, 30000, tmr.ALARM_SINGLE, self.startApp)
......@@ -44,7 +44,7 @@ tmr.stop(0)--SAFETRIM
return self.socket_send(socket, self.config)
end
local conn
local conn
return function() -- the proper doTick() timer callback
tick_count = tick_count + 1
log("entering tick", tick_count, sta.getconfig(false), sta.getip())
......
--SAFETRIM
-- function _init(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.
--
--
-- ssid, spwd Credentials for the WiFi
-- server, port, secret Provisioning server:port and signature secret
-- leave If true then the Wifi is left connected
......@@ -45,5 +45,5 @@
package.loaded[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
--SAFETRIM
-- function _provision(self,socket,first_rec)
local self, socket, first_rec = ...
local crypto, file, json, node, table = crypto, file, sjson, node, table
local stripdebug, gc = node.stripdebug, collectgarbage
local buf = {}
gc(); gc()
gc(); gc()
local function getbuf() -- upval: buf, table
if #buf > 0 then return table.remove(buf, 1) end -- else return nil
......@@ -14,8 +14,8 @@ end
-- Process a provisioning request record
local function receiveRec(socket, rec) -- upval: self, buf, crypto
-- Note that for 2nd and subsequent responses, we assme that the service has
-- "authenticated" itself, so any protocol errors are fatal and lkely to
-- Note that for 2nd and subsequent responses, we assme that the service has
-- "authenticated" itself, so any protocol errors are fatal and lkely to
-- cause a repeating boot, throw any protocol errors are thrown.
local buf, config, file, log = buf, self.config, file, self.log
local cmdlen = (rec:find('\n',1, true) or 0) - 1
......@@ -70,7 +70,7 @@ local function receiveRec(socket, rec) -- upval: self, buf, crypto
if s then
resp.lcsize=#code
print("Updated ".. name)
else
else
msg = "file write failed"
end
end
......@@ -87,15 +87,15 @@ local function receiveRec(socket, rec) -- upval: self, buf, crypto
end
file.close()
end
if s then
print("Updated ".. name)
else
else
file.remove(name)
resp.s = "write failed"
end
buf = {}
elseif action == "ul" then
if file.open(cmd.name, "r") then
file.seek("set", cmd.offset)
......@@ -120,6 +120,6 @@ local function receiveRec(socket, rec) -- upval: self, buf, crypto
end
-- 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)
return receiveRec(socket, first_rec)
......@@ -11,22 +11,22 @@
-- upvals
local crypto, file, json, 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 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 = {}}
self.log = (DEBUG == true) and print or function() end
self.modname = ...
self.log = (DEBUG == true) and print or function() end
self.modname = ...
--------------------------------------------------------------------------------------
-- Utility Functions
setmetatable( self, {__index=function(self, func) --upval: loadfile
-- The only __index calls in in LuaOTA are dynamically loaded functions.
-- The convention is that functions starting with "_" are treated as
-- call-once / ephemeral; the rest are registered in self
-- The only __index calls in in LuaOTA are dynamically loaded functions.
-- The convention is that functions starting with "_" are treated as
-- call-once / ephemeral; the rest are registered in self
func = self.prefix .. func
local f,msg = loadfile( func..".lc")
if msg then f, msg = loadfile(func..".lua") end
......@@ -48,19 +48,19 @@ function self.startApp(arg) --upval: gc, self, tmr, wifi
local appMod = self.config.app or "luaOTA.default"
local appMethod = self.config.entry or "entry"
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)
end)
end
end
function self.socket_send(socket, rec, opt_buffer)
return socket:send(self.sign(rec) .. (opt_buffer or ''))
end
self.post(function() -- upval: self
-- This config check is to prevent a double execution if the
-- user invokes with "require 'luaOTA/check':_init( etc>)" form
if not rawget(self, "config") then self:_init() end
-- This config check is to prevent a double execution if the
-- user invokes with "require 'luaOTA/check':_init( etc>)" form
if not rawget(self, "config") then self:_init() end
end)
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)
package.loaded["luaOTA.default"]=nil
local gc=collectgarbage; gc(); gc()
if DEBUG then
for k,v in pairs(_G) do print(k,v) end
for k,v in pairs(debug.getregistry()) do print(k,v) end
if DEBUG then
for k,v in pairs(_G) do print(k,v) end
for k,v in pairs(debug.getregistry()) do print(k,v) end
end
gc(); gc()
gc(); gc()
print(msg, node.heap())
end}
......@@ -6,10 +6,10 @@
-- 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
system for ESPs used the SPI Flash FS (SPIFFS) on development and production modules.
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.
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
......@@ -40,9 +40,9 @@ local concat = table.concat
local load = loadstring or load
local format = string.format
-- use string % operators as a synomyn for string.format
getmetatable("").__mod =
getmetatable("").__mod =
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))
end
......@@ -56,18 +56,18 @@ local function main ()
local server = assert(socket.bind("*", ESPport))
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})
-- Main loop forever waiting for ESP clients then processing each request ------------
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
-- receive the opening request
-- receive the opening request
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)
local inventory, fingerprint = get_inventory(src_dir, config.id)
-- Process the ESP request
......@@ -77,7 +77,7 @@ local function main ()
else
local status, msg = pcall(provision, esp, config, inventory, fingerprint)
if not status then print (msg) end
end
end
pcall(esp.close, esp)
......@@ -88,8 +88,8 @@ end
-- Local Function Implementations ------------------------------------------------------
local function get_hmac_md5(key)
if key:len() > 64 then
key = md5.sum(key)
if key:len() > 64 then
key = md5.sum(key)
elseif key:len() < 64 then
key = key .. ('\0'):rep(64-key:len())
end
......@@ -105,8 +105,8 @@ get_inventory = function(dir, cpuid)
error("Cannot open directory, aborting %s" % arg[0], 0)
end
-- Load the CPU's (or the default) inventory
local invtype, inventory = "custom", read_file("%s/ESP-%s.json" % {dir, cpuid})
-- Load the CPU's (or the default) inventory
local invtype, inventory = "custom", read_file("%s/ESP-%s.json" % {dir, cpuid})
if not inventory then
invtype, inventory = "default", read_file(dir .. "/default.json")
end
......@@ -129,15 +129,15 @@ get_inventory = function(dir, cpuid)
assert(fa, "File %s is required but not in sources directory" % name)
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) }
assert (f[i].size == #(f[i].content or ''), "File %s unreadable" % name )
end
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")
hmac = get_hmac_md5(inventory.secret)
hmac = get_hmac_md5(inventory.secret)
return inventory, md5.sumhexa(concat(fp,":"))
end
......@@ -145,15 +145,15 @@ end
-- Encode a response buff, add a signature and any optional buffer
------------------------------------------------------------------
send_command = function(esp, resp, buffer)
if type(buffer) == "string" then
resp.data = #buffer
else
if type(buffer) == "string" then
resp.data = #buffer
else
buffer = ''
end
local rec = json.encode(resp)
rec = rec .. hmac(rec):sub(-6) .."\n"
-- print("requesting ", rec:sub(1,-2), #(buffer or ''))
esp:send(rec .. buffer)
esp:send(rec .. buffer)
end
......@@ -173,10 +173,10 @@ receive_and_parse = function(esp)
end
end
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
for _, f in ipairs(inventory.files) do
......@@ -190,7 +190,7 @@ provision = function(esp, config, inventory, fingerprint)
-- if the source is tagged with SAFETRIM then its safe to remove "--"
-- comments, leading and trailing whitespace. Not as good as LuaSrcDiet,
-- 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("[ \t]+\n","\n")
content = content:gsub("%-%-[^\n]*","")
......@@ -201,7 +201,7 @@ provision = function(esp, config, inventory, fingerprint)
action = "dl"
end
print ("Sending file ".. name)
for i = 1, size, 1024 do
if i+1023 < size then
cmd = {a = "pu", data = 1024}
......@@ -209,11 +209,11 @@ provision = function(esp, config, inventory, fingerprint)
else
cmd = {a = action, data = size - i + 1, name = name}
buf = content:sub(i)
end
end
send_command(esp, cmd, buf)
local resp = receive_and_parse(esp)
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)
end
end
......@@ -243,7 +243,7 @@ end
----------------------------------
save_file = function(fname, data)
local file = io.open(fname, "wb")
file:write(data)
file:write(data)
file:close()
end
......
--[[
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,
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
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,
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
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.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
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,
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
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,
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.
]]--
......@@ -35,7 +35,7 @@ local URI = "/twilio/Calls.json"
function build_post_request(host, uri, data_table)
local data = ""
for param,value in pairs(data_table) do
data = data .. param.."="..value.."&"
end
......@@ -49,7 +49,7 @@ function build_post_request(host, uri, data_table)
data
print(request)
return request
end
......@@ -68,16 +68,16 @@ local function make_call(from,to,body)
From = from,
To = to
}
socket = net.createConnection(net.TCP,0)
socket:on("receive",display)
socket:connect(80,HOST)
socket:on("connection",function(sck)
socket:on("connection",function(sck)
local post_request = build_post_request(HOST,URI,data)
sck:send(post_request)
end)
end)
end
function check_wifi()
......@@ -90,9 +90,9 @@ function check_wifi()
print("Connected to AP!")
print(ip)
-- 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
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