luaOTA updated to include object form timers (#2752)

This commit is contained in:
Martijn van Buul 2019-05-17 14:13:49 +02:00 committed by Terry Ellison
parent f1b5dfc34e
commit 45a7187a6c
6 changed files with 32 additions and 19 deletions

View File

@ -0,0 +1,4 @@
{
"files": [ "main.lc", "supporfile.lc", "othercontent.txt" ],
"secret": "supersekrit"
}

View File

@ -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 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.
`luaOTA` configures the wifi and connects to the required sid in STA mode using the
@ -90,13 +90,17 @@ require "LuaOTA.check"
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. ]]}
abortOTA(); require "luaOTA.check":_init {ssid ="SOMESID" --[[etc. ]]}
```
where the parameters to the `_init` method are:
- `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.
- `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
signing of the protocol messages.
- `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
examples, this is designed to run on the host and should not be included in the
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
- 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.
- 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
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
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
@ -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
`tmr` alarm call at the end of `_init.lua` as an example:
```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
`luaOTA/_doTick.lc` which stores `self` as a local and returns a function which takes

View File

@ -1,4 +1,4 @@
tmr.stop(0)--SAFETRIM
if (self.timer) then self.timer:stop() end--SAFETRIM
-- function _doTick(self)
-- Upvals
@ -32,7 +32,7 @@ tmr.stop(0)--SAFETRIM
-- 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)
self.timer:alarm(0, 30000, tmr.ALARM_SINGLE, self.startApp)
return self:_provision(socket,rec)
end
@ -67,7 +67,7 @@ tmr.stop(0)--SAFETRIM
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
tmr.unregister(0)
self.timer:unregister()
pcall(conn.close, conn)
self.socket=nil
return startApp("OK: Timeout on waiting for provision service response")

View File

@ -45,5 +45,5 @@
package.loaded[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

View File

@ -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
-- "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 config, file, log = self.config, file, self.log
local cmdlen = (rec:find('\n',1, true) or 0) - 1
local cmd,hash = rec:sub(1,cmdlen-6), rec:sub(cmdlen-5,cmdlen)
if cmdlen < 16 or
@ -89,9 +89,9 @@ local function receiveRec(socket, rec) -- upval: self, buf, crypto
end
if s then
print("Updated ".. name)
print("Updated ".. cmd.name)
else
file.remove(name)
file.remove(cmd.name)
resp.s = "write failed"
end
buf = {}

View File

@ -9,8 +9,8 @@
--------------------------------------------------------------------------------
-- upvals
local crypto, file, json, net, node, table, tmr, wifi =
crypto, file, sjson, net, node, table, tmr, wifi
local crypto, file, json, net, node, table, wifi =
crypto, file, sjson, net, node, table, wifi
local error, pcall = error, pcall
local loadfile, gc = loadfile, collectgarbage
local concat, unpack = table.concat, unpack or table.unpack
@ -19,7 +19,11 @@ local self = {post = node.task.post, prefix = "luaOTA/", conf = {}}
self.log = (DEBUG == true) and print or function() end
self.modname = ...
self.timer = tmr.create()
_G["stopOTA"] = function()
self.timer:stop()
end
--------------------------------------------------------------------------------------
-- Utility Functions
@ -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'
end
function self.startApp(arg) --upval: gc, self, tmr, wifi
function self.startApp(arg) --upval: gc, self, wifi
gc();gc()
tmr.unregister(0)
self.timer.unregister()
self.socket = nil
if not self.config.leave then wifi.setmode(wifi.NULLMODE,false) end
local appMod = self.config.app or "luaOTA.default"