############
This is a collection of Lua modules I've used in various nodemcu/ESP8266
-projects. It's largely an overlay-style approach to code residency; many
-things are intended to be ``dofile()``'d or ``loadfile()``'d and kept around
-only while being used.
+projects. It's largely an overlay-style approach to code residency: most
+things are fetched through the ``OVL`` table created by ``init.lua`` rather
+than ``require``.
The files here are available under the GNU Affero General Public License,
version 3 or later. See ``COPYING`` for details.
* ``util/diag.lua`` -- a simple set of diagnostic calls intended for general
calling from the command line. A quick overview of the device. Use as
- ``dofile("diag.lc")``.
+ ``OVL.diag()``.
* ``host/pushinit.sh`` -- a host-side utility to push a minimum set of files
up to the device, either via `luatool
Networking Framework
--------------------
-* ``net/nwfnet.lua`` -- an event dispatch module; intended to be resident at
- all times.
+* ``net/nwfnet.lua`` -- an event dispatch module; load with require so that
+ there is a singleton instance.
* ``nwfnet-diag.lua`` -- generic event reporting using the above; intended
- as diagnostics from console. Use as ``dofile("nwfnet-diag.lc")(true)`` to
- enable or ``...(false)`` to disable and unload.
+ as diagnostics from console. Use as ``OVL["nwfnet-diag"]()(true)`` to
+ enable or ``...(false)`` to disable.
* ``net/nwfnet-go.lua`` -- bring up the network and dispatch events via
- ``nwfnet`` above. Use via ``dofile``.
+ ``nwfnet`` above. Use via ``OVL``.
* ``net/netnet-sntp.lua`` -- utilities for invoking SNTP time
synchronization once or repeatedly (using ``tq``, below). Reads server
Enqueue events with ``:queue(time,function,args...)``; ``:queue`` returns
a handle suitable for use with ``:dequeue()`` to unregister a pending
future event. All ESP-specific behavior is overridable by replacing
- ``:now`` and ``:arm``. Use as ``tq = dofile("tq.lc")(timer)``.
+ ``:now`` and ``:arm``. Use as ``tq = OVL.tq()(timer)``.
* ``tq/tq-diag.lua`` -- knows how to traverse a ``tq`` for diagnostic
- utility. Use as ``dofile("tq-diag.lc")(tq,print,print)``, e.g.
+ utility. Use as ``OVL["tq-diag"]()(tq,print,print)``, e.g.
--- DEPEND: file?, gpio, node, rtctime?, tmr ; nwfnet, nwfnet-diag, nwfnet-go, telnetd
+-- DEPEND: gpio, node, rtctime?, tmr ; nwfnet, nwfnet-diag, nwfnet-go, telnetd
+
+-- An "overlay" table: load files or flash components in a way
+-- that, unlike require, doesn't cause them to "stick" in RAM.
+--
+-- Based on lua_examples/lfs/_init.lua
+local G=getfenv()
+local flashindex = node.flashindex
+local ovl_t = {
+ __index = function(_, name)
+ local f = loadfile(name..".lua")
+ if f then return f end
+ local f = loadfile(name..".lc")
+ if f then return f end
+ if flashindex then
+ local fn_ut, ba, ma, size, modules = flashindex(name)
+ if not ba then return fn_ut end
+ end
+ return nil
+ end,
+ __newindex = function(_, name, value)
+ error("Overlay is a synthetic view! " .. name, 2)
+ end,
+ }
+G.OVL = setmetatable(ovl_t,ovl_t)
+
+-- Install LFS as a package loader, as suggested by lua_examples/lfs/_init.lua
+if flashindex then
+ table.insert(package.loaders,function(module)
+ local fn, ba = flashindex(module)
+ return ba and "Module not in LFS" or fn
+ end)
+end
+
+-- Save some bytes, as suggested by lua_examples/lfs/_init.lua
+G.module = nil
+package.seeall = nil
+
if rtctime then rtctime.set(0) end -- set time to 0 until someone corrects us
-- See if there's any early startup to do.
-if file and file.exists("init-early.lua") then dofile("init-early.lua") end
+local ie = OVL["init-early"]
+if ie then ie() end
local function goab()
- dofile("nwfnet-diag.lc")(true)
- dofile("diag.lc")
- dofile("nwfnet-go.lc")
+ OVL["nwfnet-diag"]()(true)
+ OVL["diag"]()
+ OVL["nwfnet-go"]()
tcpserv = net.createServer(net.TCP, 180)
tcpserv:listen(23,function(k)
- local telnetd = dofile "telnetd.lc"
+ local telnetd = OVL["telnetd"]()
telnetd.on["conn"] = function(s)
tmr.unregister(6)
s(string.format("NODE-%06X RECOVERY (auto reboot cancelled)",node.chipid()))
telnetd.server(k)
end)
end
-local function gof(fn)
- local f, e = loadfile(fn)
- if f == nil then print("Error:",fn,e); goab() else node.task.post(f) end
+local function goi2()
+ local i2 = OVL.init2
+ if not i2 then goab() else node.task.post(i2) end
end
-local function goi2() gof("init2.lc") end
local function waitFLASH()
local function stop_()
gpio.mode(3,gpio.INPUT); gpio.trig(3); tmr.unregister(6)
local concat = table.concat
local insert = table.insert
-local fifo = (require "fifo")()
+local fifo = OVL.fifo()
return function(sock)
local ssend = function(s) sock:send(s) end
wifi.eventmon.register(wifi.eventmon.STA_GOT_IP, function(t)
(require "nwfnet"):runnet("wstagoip",t)
if mdns then mdns.register(wifi.sta.gethostname()) end
- dofile("nwfnet-sntp.lc").dosntp(nil)
+ OVL["nwfnet-sntp"]().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)
, ["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
+, ["flashreload"] = function(ll,s) local fn = string.match(ll,"^%s*([^%s]+)%s*$");
+ local r,err = pcall(node.flashreload,fn); if not r then s("ERR: "..err) end
+ end
, ["sha256"] = function(ll,s) -- compute the hash of a file in flash
local fn = string.match(ll,"^%s*([^%s]+)%s*$")
s(crypto.toBase64(crypto.fhash('sha256',fn)))
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
+ local rt = OVL["telnetd-"..c]
+ if type(rt) == 'function'
then self.tryin(r,rt(),function(c2) tx(c.." "..c2.."?") end, function() tx(c.." ??") end,tx)
else tx(c.."?")
end
function(_) tx("?") k(true) end,tx)
end
function self.server(sock_)
- local fsend = (dofile("fifosock.lc"))((require "fifo")(), sock_)
+ local fsend = OVL.fifosock()(sock_)
local function teardown(rawsock)
rawsock:on("sent", nil)
rawsock:on("receive", nil)
--- /dev/null
+OVL={}
+OVL.fifo = function() return dofile("./fifo/fifo.lua") end
+package.loaded["fifosock"] = dofile("./net/fifosock.lua")
+
+-- vprint = print
+vprint = function() end
+outs = {}
+
+fakesock = {
+ cb = nil,
+ on = function(this, _, cb) vprint("CBSET") this.cb = cb end,
+ send = function(this, s) vprint("SEND", verbose and s) table.insert(outs, s) end,
+}
+function sent() vprint("CB") fakesock.cb() end
+
+fsend = require "fifosock" (fakesock)
+function fcheck(x)
+ vprint ("CHECK", verbose and x)
+ assert (#outs > 0)
+ assert (x == outs[1])
+ table.remove(outs, 1)
+end
+function fsendc(x) fsend(x) fcheck(x) end
+function fchecke() vprint("CHECKE") assert (#outs == 0) end
+
+fsendc("abracadabra none")
+sent() ; fchecke()
+
+fsendc("abracadabra three")
+fsend("short")
+fsend("string")
+fsend("build")
+sent() ; fcheck("shortstringbuild")
+sent() ; fchecke()
+
+-- Hit default FSMALLLIM while building up
+fsendc("abracadabra lots small")
+for i = 1, 34 do fsend("a") end
+sent() ; fcheck(string.rep("a", 32))
+sent() ; fcheck("aa")
+sent() ; fchecke()
+
+-- Hit string length while building up
+fsendc("abracadabra overlong")
+for i = 1, 10 do fsend(string.rep("a",32)) end
+sent() ; fcheck(string.rep("a", 256))
+sent() ; fcheck(string.rep("a", 64))
+sent() ; fchecke()
+
+-- Hit neither before sending a big string
+fsendc("abracadabra mid long")
+for i = 1, 6 do fsend(string.rep("a",32)) end
+fsend(string.rep("b", 256))
+for i = 1, 6 do fsend(string.rep("c",32)) end
+sent() ; fcheck(string.rep("a", 192))
+sent() ; fcheck(string.rep("b", 256))
+sent() ; fcheck(string.rep("c", 192))
+sent() ; fchecke()
+
+-- send a huge string
+fsend(string.rep("a",256) .. string.rep("b", 256) .. string.rep("c", 260))
+fcheck(string.rep("a",256))
+sent() ; fcheck(string.rep("b",256))
+sent() ; fcheck(string.rep("c",260))
+sent() ; fchecke()
+
+print("All tests OK")
--- /dev/null
+morse = dofile("morse/morse.lua")
+
+function tm(str)
+ local m = morse(str)
+ while m(print) do end
+end
-- 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)
+-- tq:queue(1,function() OVL["tq-diag"]()(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
-- SOFT DEPENDS: file, rtcfifo, node, wifi
+local k,v
if node then
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())
if file then
print('FS:', file.fsinfo()); for k,v in pairs(file.list()) do print("",k,v) end
end
+if node.flashindex then
+ local ut, fa, ma, sz, t = node.flashindex()
+ if ut then
+ print('LFS:', ut, fa, ma, sz)
+ for k,v in ipairs(t) do print("", v) end
+ else
+ print('LFS:', fa, ma)
+ end
+end
print('PACKAGES:'); for k,v in pairs(package.loaded) do print("",k,v) end
print('GLOBAL:'); for k,v in pairs(_G) do print("",k,v) end
--- /dev/null
+-- from lua_examples/lfs/dummy_strings.lua
+local preload = "?.lc;?.lua", "/\n;\n?\n!\n-", "@init.lua", "_G", "_LOADED",
+"_LOADLIB", "__add", "__call", "__concat", "__div", "__eq", "__gc", "__index",
+"__le", "__len", "__lt", "__mod", "__mode", "__mul", "__newindex", "__pow",
+"__sub", "__tostring", "__unm", "collectgarbage", "cpath", "debug", "file",
+"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",
+"tmr.timer"
+
+local initload =
+ ".lc", ".lua", "loadfile",
+ "Module not in LFS",
+ "NODE-%06X RECOVERY (auto reboot cancelled)",
+ "Overlay is a synthetic view! "