Commit 45a7187a authored by Martijn van Buul's avatar Martijn van Buul Committed by Terry Ellison
Browse files

luaOTA updated to include object form timers (#2752)

parent f1b5dfc3
{
"files": [ "main.lc", "supporfile.lc", "othercontent.txt" ],
"secret": "supersekrit"
}
...@@ -31,7 +31,7 @@ call which invokes the `luaOTA` module by a `require "luaOTA.check"` statement. ...@@ -31,7 +31,7 @@ call which invokes the `luaOTA` module by a `require "luaOTA.check"` statement.
The `config.json` file which provides the minimum configuration parameters to connect to The `config.json` file which provides the minimum configuration parameters to connect to
the WiFi and provisioning server, however these can by overridden through the UART by the WiFi and provisioning server, however these can by overridden through the UART by
first doing a `tmr.stop(0)` and then a manual initialisation as described in the first doing a `abortOTA()` and then a manual initialisation as described in the
[init.lua](#initlua) section below. [init.lua](#initlua) section below.
`luaOTA` configures the wifi and connects to the required sid in STA mode using the `luaOTA` configures the wifi and connects to the required sid in STA mode using the
...@@ -90,13 +90,17 @@ require "LuaOTA.check" ...@@ -90,13 +90,17 @@ 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. ]]} abortOTA(); 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.
- `app`. The filename of the module which will be `required` after provisioning is
complete. Defaults to LuaOTA/default.
- `entry`. The method that will be called on the module indicated by `app`. Defaults
to `init`
- `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
...@@ -129,6 +133,12 @@ Note that even though this file is included in the `luaOTA` subdirectory within ...@@ -129,6 +133,12 @@ Note that even though this file is included in the `luaOTA` subdirectory within
examples, this is designed to run on the host and should not be included in the examples, this is designed to run on the host and should not be included in the
ESP SPIFFS. ESP SPIFFS.
The example server expects a repository directory, which is expected to contain
the to-be-provisioned files (.lua files, .lc files...). Additionally, it expects
a .json file for every ESP that is to be provisioned, containing the "secret"
as well as the relevant filenames. This file should be called 'ESP-xxxxxxxx.json',
with 'xxxxxxxx' replaced with the ChipID.
## 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`,
...@@ -156,11 +166,6 @@ called using the object form self:someFunc() to get the context as a parameter. ...@@ -156,11 +166,6 @@ called using the object form self:someFunc() to get the context as a parameter.
- 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
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 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
...@@ -205,7 +210,7 @@ function using an object constructor `self:self:somefunction()`, but where the f ...@@ -205,7 +210,7 @@ function using an object constructor `self:self:somefunction()`, but where the f
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()) self.timer:alarm( 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
......
tmr.stop(0)--SAFETRIM if (self.timer) then self.timer:stop() end--SAFETRIM
-- function _doTick(self) -- function _doTick(self)
-- Upvals -- Upvals
...@@ -32,7 +32,7 @@ tmr.stop(0)--SAFETRIM ...@@ -32,7 +32,7 @@ tmr.stop(0)--SAFETRIM
-- 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) self.timer:alarm(0, 30000, tmr.ALARM_SINGLE, self.startApp)
return self:_provision(socket,rec) return self:_provision(socket,rec)
end end
...@@ -67,7 +67,7 @@ tmr.stop(0)--SAFETRIM ...@@ -67,7 +67,7 @@ tmr.stop(0)--SAFETRIM
return self.startApp("OK: Timeout on waiting for wifi station setup") return self.startApp("OK: Timeout on waiting for wifi station setup")
elseif (tick_count == 26) then -- wait up to 2.5 secs for TCP response elseif (tick_count == 26) then -- wait up to 2.5 secs for TCP response
tmr.unregister(0) self.timer:unregister()
pcall(conn.close, conn) pcall(conn.close, conn)
self.socket=nil self.socket=nil
return startApp("OK: Timeout on waiting for provision service response") return startApp("OK: Timeout on waiting for provision service response")
......
...@@ -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()) self.timer:alarm( 500, tmr.ALARM_AUTO, self:_doTick())
-- end -- end
...@@ -17,7 +17,7 @@ local function receiveRec(socket, rec) -- upval: self, buf, crypto ...@@ -17,7 +17,7 @@ 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 config, file, log = 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
local cmd,hash = rec:sub(1,cmdlen-6), rec:sub(cmdlen-5,cmdlen) local cmd,hash = rec:sub(1,cmdlen-6), rec:sub(cmdlen-5,cmdlen)
if cmdlen < 16 or if cmdlen < 16 or
...@@ -89,9 +89,9 @@ local function receiveRec(socket, rec) -- upval: self, buf, crypto ...@@ -89,9 +89,9 @@ local function receiveRec(socket, rec) -- upval: self, buf, crypto
end end
if s then if s then
print("Updated ".. name) print("Updated ".. cmd.name)
else else
file.remove(name) file.remove(cmd.name)
resp.s = "write failed" resp.s = "write failed"
end end
buf = {} buf = {}
......
...@@ -9,8 +9,8 @@ ...@@ -9,8 +9,8 @@
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
-- upvals -- upvals
local crypto, file, json, net, node, table, tmr, wifi = local crypto, file, json, net, node, table, wifi =
crypto, file, sjson, net, node, table, tmr, wifi crypto, file, sjson, net, node, table, 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
...@@ -19,7 +19,11 @@ local self = {post = node.task.post, prefix = "luaOTA/", conf = {}} ...@@ -19,7 +19,11 @@ 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 = ...
self.timer = tmr.create()
_G["stopOTA"] = function()
self.timer:stop()
end
-------------------------------------------------------------------------------------- --------------------------------------------------------------------------------------
-- Utility Functions -- Utility Functions
...@@ -40,9 +44,9 @@ function self.sign(arg) --upval: crypto, json, self ...@@ -40,9 +44,9 @@ function self.sign(arg) --upval: crypto, json, self
return arg .. crypto.toHex(crypto.hmac("MD5", arg, self.secret):sub(-3)) .. '\n' return arg .. crypto.toHex(crypto.hmac("MD5", arg, self.secret):sub(-3)) .. '\n'
end end
function self.startApp(arg) --upval: gc, self, tmr, wifi function self.startApp(arg) --upval: gc, self, wifi
gc();gc() gc();gc()
tmr.unregister(0) self.timer.unregister()
self.socket = nil self.socket = nil
if not self.config.leave then wifi.setmode(wifi.NULLMODE,false) end if not self.config.leave then wifi.setmode(wifi.NULLMODE,false) end
local appMod = self.config.app or "luaOTA.default" local appMod = self.config.app or "luaOTA.default"
......
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