From 9c3666344dca2c29ab35ea2acfda3e6fde7bab00 Mon Sep 17 00:00:00 2001 From: Nathaniel Wesley Filardo Date: Thu, 6 Oct 2016 23:27:43 -0400 Subject: [PATCH 1/1] Initial checkin of framework and lamp example --- cap1188/cap1188-init.lua | 27 ++++++++++++++++ cap1188/cap1188.lua | 40 +++++++++++++++++++++++ host/pushinit.sh | 32 +++++++++++++++++++ host/pushvia.expect | 68 ++++++++++++++++++++++++++++++++++++++++ init.lua | 38 ++++++++++++++++++++++ net/nwfmqtt.lua | 50 +++++++++++++++++++++++++++++ net/nwfnet-diag.lua | 16 ++++++++++ net/nwfnet-go.lua | 55 ++++++++++++++++++++++++++++++++ net/nwfnet-sntp.lua | 28 +++++++++++++++++ net/nwfnet.lua | 9 ++++++ telnetd/telnetd-diag.lua | 11 +++++++ telnetd/telnetd-file.lua | 41 ++++++++++++++++++++++++ telnetd/telnetd.lua | 35 +++++++++++++++++++++ tq/tq-diag.lua | 3 ++ tq/tq.lua | 42 +++++++++++++++++++++++++ util/diag.lua | 12 +++++++ util/fifosock.lua | 22 +++++++++++++ 17 files changed, 529 insertions(+) create mode 100644 cap1188/cap1188-init.lua create mode 100644 cap1188/cap1188.lua create mode 100755 host/pushinit.sh create mode 100755 host/pushvia.expect create mode 100644 init.lua create mode 100644 net/nwfmqtt.lua create mode 100644 net/nwfnet-diag.lua create mode 100644 net/nwfnet-go.lua create mode 100644 net/nwfnet-sntp.lua create mode 100644 net/nwfnet.lua create mode 100644 telnetd/telnetd-diag.lua create mode 100644 telnetd/telnetd-file.lua create mode 100644 telnetd/telnetd.lua create mode 100644 tq/tq-diag.lua create mode 100644 tq/tq.lua create mode 100644 util/diag.lua create mode 100644 util/fifosock.lua diff --git a/cap1188/cap1188-init.lua b/cap1188/cap1188-init.lua new file mode 100644 index 0000000..169c15b --- /dev/null +++ b/cap1188/cap1188-init.lua @@ -0,0 +1,27 @@ +return { + ["init"] = function(trigpin,resetpin,gofn) + gpio.mode(trigpin,gpio.INT,gpio.PULLUP) -- GPIO12 is cap sensor IRQ (active low) + gpio.mode(resetpin,gpio.OUTPUT,gpio.FLOAT) -- GPIO14 is cap sensor reset (active low) + + -- put cap through a reset cycle and then hook our IRQ handler + gpio.trig(trigpin) + gpio.write(resetpin,gpio.HIGH) + tq:queue(200,function() + gpio.write(resetpin,gpio.LOW) + tq:queue(300,function() + print('CAP', cap:info()) + gpio.trig(trigpin, "low", gofn) + cap:wr(0x20,0x28) -- config: add maximum duration autorecalibrate + cap:wr(0x22,0xF4) -- raise maximum duration to 11 seconds; repeat every 175ms + cap:wr(0x2A,0x00) -- do not block multiple touches + cap:wr(0x41,0x30) -- change standby sampling rate + cap:wr(0x72,0xFF) -- link all LEDs to touches + end) + end) + end, + + -- calibrate all input sensors + ["calibrate"] = function() + cap:wr(0x26,0xFF) + end +} diff --git a/cap1188/cap1188.lua b/cap1188/cap1188.lua new file mode 100644 index 0000000..5893c63 --- /dev/null +++ b/cap1188/cap1188.lua @@ -0,0 +1,40 @@ +-- DEPENDS: i2c +-- cap1188 based heavily on adafruit's documentation and code as well as the +-- device datasheet. Unsurprising, really, esp. that it's their breakout. +local self = {} +self.addr = 0x29 + +function self:rr(r) + i2c.start(0) + if not i2c.address(0, self.addr, i2c.TRANSMITTER) then i2c.stop(0) return nil end + i2c.write(0,r) + i2c.start(0) + i2c.address(0,0x29,i2c.RECEIVER) + local x = i2c.read(0,1) + i2c.stop(0) + return x:byte(1) +end + +function self:wr(r,v) + i2c.start(0) + if not i2c.address(0, self.addr, i2c.TRANSMITTER) then i2c.stop(0) return nil end + i2c.write(0,r) + i2c.write(0,v) + i2c.stop(0) + return true +end + +function self:mr(r,f) local n = f(self:rr(r)) ; self:wr(r,n); return n end + +function self:rt() + local t = self:rr(0x3) + self:mr(0, function(st) return bit.band(st,0xFE) end) + local t2 = self:rr(0x3) + return t, t2 +end + +function self:info() + return self:rr(0xFD), self:rr(0xFE), self:rr(0xFF) +end + +return self diff --git a/host/pushinit.sh b/host/pushinit.sh new file mode 100755 index 0000000..a48c051 --- /dev/null +++ b/host/pushinit.sh @@ -0,0 +1,32 @@ +#!/bin/bash +set -e -u + +if [ -z ${HOST:-} ]; then + # Uses LUATOOL to push init and dependencies to the device; bootstrap! + PUSHCMD="${LUATOOL} --delay 0.1 -p ${MCUPORT} -b ${MCUBAUD}" + dopush() { ${PUSHCMD} -f $1 -t ${2:-`basename $1`}; } + dopushcompile() { ${PUSHCMD} -f $1 -t ${2:-`basename $1`} -c; } +else + # Uses host/pushvia to push everything if HOST is set + PUSHCMD="./host/pushvia.expect ${HOST} ${PORT:-23}" + dopush() { ${PUSHCMD} ${2:-`basename $1`} $1; } + dopushcompile() { ${PUSHCMD} ${2:-`basename $1`} $1 compile; } +fi + +dopushcompile util/diag.lua +dopushcompile tq/tq.lua +dopushcompile tq/tq-diag.lua +dopushcompile net/nwfnet.lua +dopushcompile net/nwfnet-sntp.lua +dopushcompile net/nwfnet-go.lua +dopushcompile net/nwfnet-diag.lua +dopush net/conf/nwfnet.conf +dopush net/conf/nwfnet.cert +dopush net/conf/nwfnet.conf2 +dopushcompile util/fifosock.lua +dopushcompile telnetd/telnetd.lua +dopushcompile telnetd/telnetd-file.lua +dopushcompile telnetd/telnetd-diag.lua +dopush init.lua + +echo "SUCCESS" diff --git a/host/pushvia.expect b/host/pushvia.expect new file mode 100755 index 0000000..981d43b --- /dev/null +++ b/host/pushvia.expect @@ -0,0 +1,68 @@ +#!/usr/bin/env expect + +# expect wrapper around nc to push a file via telnetd-file.lua's interface; +# invoke with "host port remote_file_name local_file_name [compile]" + +package require Tcl 8 +package require base64 "2.4.2" + +proc docmd {sid cmd} { + set timeout 5 + + send -i ${sid} ${cmd} + expect { + -i ${sid} -ex "\n$ " {} + -i ${sid} -ex "ERR: " { + send_user "Command error\n" + exit 1 + } + -i ${sid} eof { + send_user "Command EOF\n" + exit 1 + } + timeout { + send_user "Command timeout\n" + exit 1 + } + } + +} + +spawn {*}"nc [lindex $argv 0] [lindex $argv 1]" +set remote_sid ${spawn_id} + +set rfn [lindex $argv 2] +set lfn [lindex $argv 3] +set lchan [open ${lfn} r+] +fconfigure ${lchan} -translation binary -encoding binary + +set timeout 2 +expect { + -i ${remote_sid} -ex "\n$ " {} + timeout { + send_user "Failed to find initial prompt" + exit 1 + } +} + +docmd ${remote_sid} "file remove ${rfn}\n" + +set pos 0 +while 1 { + set chunk [read ${lchan} 128] + set length [string length ${chunk}] + if {${length} <= 0} { break } + + set echunk [::base64::encode -maxlen 0 ${chunk}] + docmd ${remote_sid} "file pwrite ${pos} ${rfn} ${echunk}\n" + + set pos [expr {$pos + $length}] +} + +if { "compile" == [lindex $argv 4] } { + docmd ${remote_sid} "file compile ${rfn}\n" + docmd ${remote_sid} "file remove ${rfn}\n" +} + +send -i ${remote_sid} "quit\n" +expect { -i ${remote_sid} eof {} } diff --git a/init.lua b/init.lua new file mode 100644 index 0000000..0f34362 --- /dev/null +++ b/init.lua @@ -0,0 +1,38 @@ +-- DEPEND: gpio, node, rtctime?, tmr ; nwfnet, nwfnet-diag, nwfnet-go, telnetd +if rtctime then rtctime.set(0) end -- set time to 0 until someone corrects us +local function goab() + dofile("nwfnet-diag.lc")(true) + dofile("diag.lc") + dofile("nwfnet-go.lc") + tcpserv = net.createServer(net.TCP, 180) + tcpserv:listen(23,function(k) + local telnetd = dofile "telnetd.lc" + telnetd.on["conn"] = function(k) + tmr.unregister(6) + k:send(string.format("NODE-%06X RECOVERY (auto reboot cancelled)",node.chipid())) end + telnetd.server(k) + end) +end +local function go(fn) + local f, e = loadfile(fn) + if f == nil then print("Error:",fn,e); goab() else node.task.post(f) end +end +local function goi2() go("init2.lc") end +local function waitFLASH() + local function stop_() gpio.mode(3,gpio.INPUT); gpio.trig(3); tmr.unregister(6); stop = nil end + function stop() stop_(); goab(); end + print("Reset delay! Bounce GPIO3 low or type 'stop()' to stop autoboot...") + gpio.mode(3,gpio.INT,gpio.PULLUP) + tmr.alarm(6,8000,tmr.ALARM_SINGLE,function() print("Continuing boot..."); stop_(); goi2() end) + gpio.trig(3,"low",function(_) print("Aborting..."); stop() end) +end +local function bootPANIC() + print("Panic! Lingering for five minutes with telnet console up; 'tmr.unregister(6)' to persist...") + tmr.alarm(6,300000,tmr.ALARM_SINGLE,node.restart) + goab() +end +local bct = { [0] = waitFLASH, [1] = bootPANIC, [2] = bootPANIC, [3] = bootPANIC, [4] = waitFLASH, [5] = goi2, [6] = waitFLASH } +local mt = {__index = function() return goi2 end} +setmetatable(bct,mt) +local _, bc = node.bootreason() +bct[bc]() diff --git a/net/nwfmqtt.lua b/net/nwfmqtt.lua new file mode 100644 index 0000000..07ae8fb --- /dev/null +++ b/net/nwfmqtt.lua @@ -0,0 +1,50 @@ +-- DEPENDS: cjson, file, mqtt; nwfnet +local nwfnet = require "nwfnet" +local self = {} +function self.mkclient(cf) -- construct a client with config from json file cf + if file.open(cf) then + local conf = cjson.decode(file.read()) + local c, k, u, p + if type(conf) == "table" then + c = conf["clientid"]; k = conf["keepalive"]; u = conf["user"]; p = conf["pass"] + end + c = c or string.format("NODE-%06X",node.chipid()) + k = k or 1500 + file.close() + local m = mqtt.Client(c,k,u,p) + m:on("connect", function(c) nwfnet:runnet("mqttconn",c) end) + m:on("offline", function(c) nwfnet:runnet("mqttdscn",c) end) + m:on("message", function(c,t,m) nwfnet:runmqtt(c,t,m) end) + return m, u, c + end +end +function self.connect(m,cf) -- make a connection with parameters from json file cf + local broker, port, secure + if file.open(cf) then + local conf = cjson.decode(file.read()) + if type(conf) == "table" then + broker = conf["broker"]; port = conf["port"]; secure = conf["secure"] + end + file.close() + end + conf = nil + broker = broker or "iot.eclipse.org" + port = port or 1883 + secure = (secure == 1) or 0 + return m:connect(broker,port,secure,0) +end +function self.heartbeat(m,topic,tq,period) -- set up lw&t and periodically heartbeat using tq until cancelled + m:lwt(topic,"dead",1,1) + local handle + local function beat() mqc:publish(topic,"beat",1,1); handle = tq:queue(period, beat) end + handle = tq:queue(period,beat) + return function() tq:dequeue(handle) end +end +function self.suball(m,fn) -- subscribe to all lines in a file + if file.open(fn) then + local line + for line in function() return file.readline() end do m:subscribe(line:sub(1,-2),1) end + file.close() + end +end +return self diff --git a/net/nwfnet-diag.lua b/net/nwfnet-diag.lua new file mode 100644 index 0000000..08f03e6 --- /dev/null +++ b/net/nwfnet-diag.lua @@ -0,0 +1,16 @@ +-- DEPENDS: ; nwfnet +return function(ena) + local nn = require "nwfnet" + if ena then + nn.onmqtt["diag"] = function(...) print('mqttmsg',...) end + nn.onnet["diag"] = function(e,...) + if e == "wstagoip" then local t = ... ; print(e,t.IP,t.netmask,t.gateway) + elseif e == "wstaconn" then local t = ... ; print(e,t.SSID,t.BSSID,t.channel) + elseif e == "wstadscn" then local t = ... ; print(e,t.SSID,t.BSSID,t.reason) + else print(e,...) end + end + else + nn.onmqtt["diag"] = nil + nn.onnet["diag"] = nil + end +end diff --git a/net/nwfnet-go.lua b/net/nwfnet-go.lua new file mode 100644 index 0000000..962c142 --- /dev/null +++ b/net/nwfnet-go.lua @@ -0,0 +1,55 @@ +-- DEPENDS: cjson, file, mdns, net, rtctime, sntp, wifi; nwfnet, nwfnet-sntp +wifi.eventmon.register(wifi.eventmon.STA_GOT_IP, function(t) + (require "nwfnet"):runnet("wstagoip",t) + mdns.register(wifi.sta.gethostname()) + dofile("nwfnet-sntp.lc").dosntp(nil) +end) +wifi.eventmon.register(wifi.eventmon.STA_DHCP_TIMEOUT, function(_) (require "nwfnet"):runnet("wstadtmo") end) +wifi.eventmon.register(wifi.eventmon.STA_CONNECTED, function(t) (require "nwfnet"):runnet("wstaconn",t) end) +wifi.eventmon.register(wifi.eventmon.STA_DISCONNECTED, function(t) (require "nwfnet"):runnet("wstadscn",t) end) + +-- One-shot configuration options; useful to change many things at once +-- at the next boot; all of these options are persisted by the ESP +if file.open("nwfnet.conf","r") then + local conf = cjson.decode(file.read()) + if type(conf) == "table" then + local essid = conf["sta_essid"]; local pw = conf["sta_pw"] + if essid ~= nil and pw ~= nil then wifi.sta.config(essid,pw,0) end + + if conf["ap"] ~= nil then pcall(wifi.ap.config,conf["ap"]) end + + local modestr = conf["wifi_mode"] + if modestr == "station" then wifi.setmode(wifi.STATION) + elseif modestr == "softap" then wifi.setmode(wifi.SOFTAP) + elseif modestr == "stationap" then wifi.setmode(wifi.STATIONAP) + else wifi.setmode(wifi.STATION) + end + + print("Applied settings from nwfnet.conf; likely, you want to remove this file...") + else print("nwfnet.conf malformed") + end + file.close() +end +-- must come after we've got our event callbacks registered, yeah? +wifi.sta.connect() + +if file.open("nwfnet.cert","r") then + local cert = "" + local chunk = file.read() + while chunk ~= nil do cert = cert..chunk; chunk = file.read() end + ok, res = pcall(net.cert.verify,cert) + file.close() + if ok then + print("Loaded cert from nwfnet.cert; likely, you want to remove this file...") + else + print("Failed to load from nwfnet.cert", res) + end +end + +if file.open("nwfnet.conf2","r") then + local conf = cjson.decode(file.read()) + if type(conf) == "table" then + if conf["verify"] == 1 then print("Enabling certificate verification"); pcall(net.cert.verify,true) end + else print("nwfnet.conf2 malformed") + end +end diff --git a/net/nwfnet-sntp.lua b/net/nwfnet-sntp.lua new file mode 100644 index 0000000..8adcd03 --- /dev/null +++ b/net/nwfnet-sntp.lua @@ -0,0 +1,28 @@ +local function dosntp(server) + local nn = require "nwfnet" + if not server then + if file.open("nwfnet.conf2","r") then + local conf = cjson.decode(file.read()) + if type(conf) == "table" then + if conf["sntp"] then print("Setting SNTP server"); server = conf["sntp"] end + else print("nwfnet.conf2 malformed") + end end end + local x, y + if not server then x, y, server = wifi.ap.getip() end + if not server then x, y, server = wifi.sta.getip() end + if not server then nn:runnet("sntperr", "No sntp server?") return end + sntp.sync(server, + function(sec,usec,server) rtctime.set(sec,usec); nn:runnet("sntpsync",sec,usec,server) end, + function(err) nn:runnet("sntperr",err) end + ) +end + +local function loopsntp(tq,period,server) + local function f() dosntp(server); tq:queue(period,f) end + tq:queue(period,f) +end + +local self = {} +self.dosntp = dosntp +self.loopsntp = loopsntp +return self diff --git a/net/nwfnet.lua b/net/nwfnet.lua new file mode 100644 index 0000000..f338815 --- /dev/null +++ b/net/nwfnet.lua @@ -0,0 +1,9 @@ +-- Just callback registries +local nwfnet = {} + -- possible events: wstaconn, wstagoip, wstadscn, wstadtmo; sntpsync, sntperr ; mqttconn, mqttdscn +nwfnet.onnet = {} + -- specifically mqtt message events +nwfnet.onmqtt = {} +function nwfnet:runnet(e,...) for _,v in pairs(nwfnet.onnet) do v(e,...) end end +function nwfnet:runmqtt(...) for _,v in pairs(nwfnet.onmqtt) do v(...) end end +return nwfnet diff --git a/telnetd/telnetd-diag.lua b/telnetd/telnetd-diag.lua new file mode 100644 index 0000000..faceeb9 --- /dev/null +++ b/telnetd/telnetd-diag.lua @@ -0,0 +1,11 @@ +-- DEPENDS: node, rtcfifo? +return { + ["boot"] = function(_,s) s(string.format("raw=%d reason=%d",node.bootreason())) end, + ["info"] = function(_,s) s(string.format("major=%d minor=%d dev=%d chip=%d flash=%d fs=%d fm=%d fs=%d",node.info())) end, + ["heap"] = function(_,s) s(string.format("free=%d",node.heap())) end, + ["fifo"] = function(_,s) if rctfifo and rtcfifo.ready() ~= 0 then s(string.format("fifo=%d",rtcfifo.count())) else s("no rtcfifo") end end, + ["exec"] = function(l,s) + local f, err = loadstring(l) + if f then local ok, res = pcall(f); if ok == true then s("ok: "..tostring(res)) else s("pcall err: "..res) end + else s("err: "..err) end end +} diff --git a/telnetd/telnetd-file.lua b/telnetd/telnetd-file.lua new file mode 100644 index 0000000..9841e70 --- /dev/null +++ b/telnetd/telnetd-file.lua @@ -0,0 +1,41 @@ +-- DEPENDS: encoder, file, net +local function withfat(s,m,fn,off,k) + if not file.open(fn,m) then s("ERR: Cannot open file "..fn.." "..m); return end + if file.seek("set", tonumber(off)) == nil then s("ERR: Cannot seek file"); file.close(); return end + k() + file.close() +end +return { + ["info"] = function(ll,s) local rem, use, _ = file.fsinfo(); s(string.format("use=%d rem=%d",use,rem)) end +, ["list"] = function(ll,s) for k,v in pairs(file.list()) do s(string.format("%s %d\n",k,v)) end end +, ["remove"] = function(ll,s) local fn = string.match(ll,"^%s*([^%s]+)%s*$"); file.remove(fn) end +, ["compile"] = function(ll,s) local fn = string.match(ll,"^%s*([^%s]+)%s*$"); + local r,err = pcall(node.compile,fn); if not r then s("ERR: "..err) end + end +, ["pread"] = function(ll,s) -- read b64 data from off in fn + local len, off, fn = string.match(ll,"^%s*(%d+)%s+(%d+)%s+([^%s]+)%s*$") + if fn == nil then s("ERR: Need file"); return end + withfat(s,"r",fn,off,function() + local out = file.read(tonumber(len)) + if out == nil then s("ERR: Read err"); return end + s(encoder.toBase64(out)) + end) + end +, ["pwrite"] = function(ll,s) -- write b64 data at off in fn + local off, fn, edat = string.match(ll,"^%s*(%d+)%s+([^%s]+)%s*([^%s]+)%s*$") + if edat == nil then s("ERR: Malformed command"); return end + local ddat, err = encoder.fromBase64(edat) + if ddat == nil then s("ERR: "..err); return end + if tonumber(off) == 0 and not file.exists(fn) then file.open(fn,"w"); file.close() end + withfat(s,"r+",fn,off,function() if file.write(ddat) == nil then s("ERR: Write error") else s("OK") end end) + end +, ["cert"] = function(ll,s) -- load argument as certificate root + local fn = string.match(ll,"^%s*([^%s]+)%s*$") + withfat(s,"r",fn,0,function() + local cert = "" + local chunk = file.read() + while chunk ~= nil do cert = cert..chunk; chunk = file.read() end + net.cert.verify(cert) + end) + end +} diff --git a/telnetd/telnetd.lua b/telnetd/telnetd.lua new file mode 100644 index 0000000..ef6c5ff --- /dev/null +++ b/telnetd/telnetd.lua @@ -0,0 +1,35 @@ +-- DEPEND: net ; fifosock +local self = {} +self.commands = { ["echo"] = function(r,s) s(r) end } +function self.tryin(i,ft,nc,ns,...) -- input, function table, no-command, no-space, args + local ix = i:find("%f[%s]",1,false) + if (ix ~= nil) then + local c, r = i:sub(1,ix-1), i:sub(ix+1); local cf = ft[c] + if cf ~= nil then cf(r,...) else nc(c,r,...) end + else ns(i,...) + end +end +self.on = { ["conn"] = nil, ["disconn"] = nil } +local function tryon(e,...) local c = self.on[e]; if c ~= nil then c(...) end end +function self.rx(tx,input,k) + self.tryin(input, self.commands, + function(c,r) + if c == "quit" then k(false) else + local rt = loadfile(string.format("telnetd-%s.lc",c)) + if rt ~= nil + then self.tryin(r,rt(),function(c2) tx(c.." "..c2.."?") end, function() tx(c.." ??") end,tx) + else tx(c.."?") + end end end, + function(_) tx("?") end,tx) + k(true) +end +function self.server(sock_) + local sock = (dofile("fifosock.lc")).wrap(sock_) + local dosend = function(...) sock:send(...) end + local k = function(c) if c then sock:send("\n$ ") else sock:close() end end + sock:on("receive",function(s_,input) self.rx(dosend,input,k) end) + sock:on("disconnection",function(s_) tryon("disconn",sock); sock.fini(); sock=nil end) + tryon("conn",sock) + sock:send("\n$ ") +end +return self diff --git a/tq/tq-diag.lua b/tq/tq-diag.lua new file mode 100644 index 0000000..129cced --- /dev/null +++ b/tq/tq-diag.lua @@ -0,0 +1,3 @@ +-- call directly or wrap in tq immediately to get updated leader time value: +-- tq:queue(1,function() dofile("tq-diag.lc")(tq,print,print) end) +return function (self,kt,ke) local i,t; for i,t in ipairs(self._q) do kt(i,t["t"],#t) for k,v in ipairs(t) do ke(i,k,v) end end end diff --git a/tq/tq.lua b/tq/tq.lua new file mode 100644 index 0000000..9fda3d8 --- /dev/null +++ b/tq/tq.lua @@ -0,0 +1,42 @@ +-- DEPENDS: tmr [only by default] +local fire, doarm +function fire(self) + local cbs = table.remove(self._q,1) + if #self._q > 0 then doarm(self, self._q[1]["t"]) end + local v + for _,v in ipairs(cbs) do v() end +end +function doarm(self,when) self:arm(function() fire(self) end, when); self._tst = self:now() end +local function queue(self,when,what,...) + if #self._q > 0 then + local lapsed = (self:now() - self._tst)/1000 + self._q[1]["t"] = self._q[1]["t"] - lapsed + end + local ix = 0; local tleft = when + while (ix < #self._q) do + if (tleft < self._q[ix+1]["t"]) then break end + ix = ix + 1; tleft = tleft - self._q[ix]["t"] + end + local warg = {...}; local nwarg = select('#',...) + local wfn = function () return what(unpack(warg,1,nwarg)) end + if ix == 0 then doarm(self,when) + elseif tleft == 0 then table.insert(self._q[ix],wfn); return + end + table.insert(self._q,ix+1,{["t"] = tleft, [1] = wfn}) + if ix+1 < #self._q then + self._q[ix+2]["t"] = self._q[ix+2]["t"] - tleft + end + return wfn +end +local function dequeue(self,what) + local k,v,w + for _,v in pairs(self._q) do for k,w in ipairs(v) do + if w == what then table.remove(v,k) end + end end +end +local function defl_arm(self,fn,t) tmr.alarm(self.tmr, t, tmr.ALARM_SINGLE, fn) end +local function defl_now(_) return tmr.now() end +return function(tmrix) return { + _q = {}, _tst = 0, + ["arm"] = defl_arm, ["now"] = defl_now, ["tmr"] = tmrix, ["queue"] = queue, ["dequeue"] = dequeue +} end diff --git a/util/diag.lua b/util/diag.lua new file mode 100644 index 0000000..83648d1 --- /dev/null +++ b/util/diag.lua @@ -0,0 +1,12 @@ +-- DEPENDS: file, rtcfifo, node, wifi +print('INFO:',string.format("major=%d minor=%d dev=%d chip=%d flash=%d fs=%d fm=%d fs=%d",node.info())) +print('HEAP:', node.heap()) +print('WIFI:',wifi.getmode()) +print('MAC:',wifi.sta.getmac(), wifi.ap.getmac()) +print('HOST:',wifi.sta.gethostname()) +print('WSTA:',wifi.sta.getconfig()) +print('WAP:',wifi.ap.getconfig()) +print('IP:',wifi.sta.getip(), wifi.ap.getip()) +if rtcfifo.ready() ~= 0 then print('RTCF:',rtcfifo.count()) else print('RTCF:','NOT PREPARED') end +print('FS:', file.fsinfo()); for k,v in pairs(file.list()) do print("",k,v) end +print('GLOBAL:'); for k,v in pairs(_G) do print("",k,v) end diff --git a/util/fifosock.lua b/util/fifosock.lua new file mode 100644 index 0000000..0c0f882 --- /dev/null +++ b/util/fifosock.lua @@ -0,0 +1,22 @@ +-- STATELESS +local fs = {} +function fs.wrap(sock) + local sf = {} + local sfe = true + local function sfd() if #sf > 0 then sock:send(table.remove(sf,1)) else sfe = true end end + sock:on("sent", sfd) + local nsock = {} + nsock.send = function(k,s) + if s == nil or s == "" then return end + table.insert(sf,s) + if sfe then sfe = false; sfd() end + end + nsock.fini = function() sf=nil; sock=nil end + local sockit = getmetatable(sock)["__index"] + setmetatable(nsock,{ __index = function(_,k) + local fn = sockit[k] + return function(a,...) if a == nsock then fn(sock,...) else fn(a,...) end end + end }) + return nsock +end +return fs -- 2.50.1