good init.lua for nodemcu

dev
IoTcat 3 years ago
parent b1d4811c6b
commit 830b23418a
  1. 6
      src/cli/device.js
  2. BIN
      src/drivers/nodemcu/bin/full.bin
  3. BIN
      src/drivers/nodemcu/bin/nodemcu_float_master_20210214-0803.bin
  4. BIN
      src/drivers/nodemcu/bin/old4.bin
  5. 36
      src/drivers/nodemcu/lua/config.json
  6. 134
      src/drivers/nodemcu/lua/fifosock.lua
  7. BIN
      src/drivers/nodemcu/lua/httpserver.lc
  8. 217
      src/drivers/nodemcu/lua/httpserver.lua
  9. BIN
      src/drivers/nodemcu/lua/init.lc
  10. 979
      src/drivers/nodemcu/lua/init.lua
  11. 842
      src/drivers/nodemcu/lua/init_old.lua
  12. 314
      src/drivers/nodemcu/lua/init_old2.lua
  13. BIN
      src/drivers/nodemcu/lua/lfs.img

@ -10,12 +10,10 @@ module.exports = (yargs) => {
ban.info('Preparing NodeMCU...10%');
await upload(argv._[1], __dirname+'/../drivers/nodemcu/lua/init.lua');
ban.info('Preparing NodeMCU...30%');
//await upload(argv._[1], __dirname+'/../drivers/nodemcu/lua/lfs.img');
ban.info('Preparing NodeMCU...60%');
await upload(argv._[1], __dirname+'/../drivers/nodemcu/lua/config.json');
ban.info('Preparing NodeMCU...50%');
await upload(argv._[1], __dirname+'/../drivers/nodemcu/lua/FUNC.json');
ban.info('Preparing NodeMCU...70%');
await upload(argv._[1], __dirname+'/../drivers/nodemcu/lua/__stopped');
ban.info('Preparing NodeMCU...90%');
await reset(argv._[1]);
ban.info('Preparing NodeMCU...100%');
ban.succeed('NodeMCU on '+argv._[1]+' is ready!! You can pull it out now~');

Binary file not shown.

Binary file not shown.

@ -1,31 +1,15 @@
{
"firmware": {
"version": "0.0.1"
},
"nid": "good",
"wifi": {
"station": {
"ssid": "yimian-iot",
"pwd": "1234567890.",
"save": true
}
"ssid": "yimian-iot",
"pwd": "1234567890."
},
"udp": {
"server": {
"port": 5678
}
"msg": {
"port": 6789
},
"w": {
"id": "test2",
"director": {
"ip": "192.168.3.100",
"port": 5678
},
"heartbeat": {
"interval": 2000
},
"key": "1234567890abcdef",
"scanInterval": 300,
"retryInterval": 10000,
"maxRetryTimes": 5
"director": {
"HeartbeatInterval": 10000,
"ip": "192.168.3.100",
"port": 4444
}
}
}

@ -0,0 +1,134 @@
-- Wrap a two-staged fifo around a socket's send; see
-- docs/lua-modules/fifosock.lua for more documentation.
--
-- See fifosocktest.lua for some examples of use or tricky cases.
--
-- Our fifos can take functions; these can be useful for either lazy
-- generators or callbacks for parts of the stream having been sent.
local BIGTHRESH = 256 -- how big is a "big" string?
local SPLITSLOP = 16 -- any slop in the big question?
local FSMALLLIM = 32 -- maximum number of small strings held
local COALIMIT = 3
local concat = table.concat
local insert = table.insert
local gc = collectgarbage
local function wrap(sock)
-- the two fifos
local fsmall, lsmall, fbig = {}, 0, (require "fifo").new()
-- ssend last aggregation string and aggregate count
local ssla, sslan = nil, 0
local ssend = function(s,islast)
local ns = nil
-- Optimistically, try coalescing FIFO dequeues. But, don't try to
-- coalesce function outputs, since functions might be staging their
-- execution on the send event implied by being called.
if type(s) == "function" then
if sslan ~= 0 then
sock:send(ssla)
ssla, sslan = nil, 0; gc()
return s, false -- stay as is and wait for :on("sent")
end
s, ns = s()
elseif type(s) == "string" and sslan < COALIMIT then
if sslan == 0
then ssla, sslan = s, 1
else ssla, sslan = ssla .. s, sslan + 1
end
if islast then
-- this is shipping; if there's room, steal the small fifo, too
if sslan < COALIMIT then
sock:send(ssla .. concat(fsmall))
fsmall, lsmall = {}, 0
else
sock:send(ssla)
end
ssla, sslan = "", 0; gc()
return nil, false
else
return nil, true
end
end
-- Either that was a function or we've hit our coalescing limit or
-- we didn't ship above. Ship now, if there's something to ship.
if s ~= nil then
if sslan == 0 then sock:send(s) else sock:send(ssla .. s) end
ssla, sslan = nil, 0; gc()
return ns or nil, false
elseif sslan ~= 0 then
assert (ns == nil)
sock:send(ssla)
ssla, sslan = nil, 0; gc()
return nil, false
else
assert (ns == nil)
return nil, true
end
end
-- Move fsmall to fbig; might send if fbig empty
local function promote(f)
if #fsmall == 0 then return end
local str = concat(fsmall)
fsmall, lsmall = {}, 0
fbig:queue(str, f or ssend)
end
local function sendnext()
if not fbig:dequeue(ssend) then promote() end
end
sock:on("sent", sendnext)
return function(s)
-- don't sweat the petty things
if s == nil or s == "" then return end
-- Function? Go ahead and queue this thing in the right place.
if type(s) == "function" then promote(); fbig:queue(s, ssend); return; end
s = tostring(s)
-- cork sending until the end in case we're the head of line
local corked = false
local function corker(t) corked = true; return t end
-- small fifo would overfill? promote it
if lsmall + #s > BIGTHRESH or #fsmall >= FSMALLLIM then promote(corker) end
-- big string? chunk and queue big components immediately
-- behind any promotion that just took place
while #s > BIGTHRESH + SPLITSLOP do
local pfx
pfx, s = s:sub(1,BIGTHRESH), s:sub(BIGTHRESH+1)
fbig:queue(pfx, corker)
end
-- Big string? queue and maybe tx now
if #s > BIGTHRESH then fbig:queue(s, corker)
-- small and fifo in immediate dequeue mode
elseif fbig._go and lsmall == 0 then fbig:queue(s, corker)
-- small and queue already moving; let it linger in the small fifo
else insert(fsmall, s) ; lsmall = lsmall + #s
end
-- if it happened that we corked the transmission above...
-- if we queued a good amount of data, go ahead and start transmitting;
-- otherwise, wait a tick and hopefully we will queue more in the interim
-- before transmitting.
if corked then
if #fbig <= COALIMIT
then tmr.create():alarm(1, tmr.ALARM_SINGLE, sendnext)
else sendnext()
end
end
end
end
return { wrap = wrap }

@ -0,0 +1,217 @@
------------------------------------------------------------------------------
-- HTTP server module
--
-- LICENCE: http://opensource.org/licenses/MIT
-- Vladimir Dronnikov <dronnikov@gmail.com>
------------------------------------------------------------------------------
local collectgarbage, tonumber, tostring = collectgarbage, tonumber, tostring
local http
do
------------------------------------------------------------------------------
-- request methods
------------------------------------------------------------------------------
local make_req = function(conn, method, url)
return {
conn = conn,
method = method,
url = url,
}
end
------------------------------------------------------------------------------
-- response methods
------------------------------------------------------------------------------
local make_res = function(csend, cfini)
local send = function(self, data, status)
-- TODO: req.send should take care of response headers!
if self.send_header then
csend("HTTP/1.1 ")
csend(tostring(status or 200))
-- TODO: real HTTP status code/name table
csend(" OK\r\n")
-- we use chunked transfer encoding, to not deal with Content-Length:
-- response header
self:send_header("Transfer-Encoding", "chunked")
-- TODO: send standard response headers, such as Server:, Date:
end
if data then
-- NB: no headers allowed after response body started
if self.send_header then
self.send_header = nil
-- end response headers
csend("\r\n")
end
-- chunked transfer encoding
csend(("%X\r\n"):format(#data))
csend(data)
csend("\r\n")
end
end
local send_header = function(_, name, value)
-- NB: quite a naive implementation
csend(name)
csend(": ")
csend(value)
csend("\r\n")
end
-- finalize request, optionally sending data
local finish = function(self, data, status)
-- NB: res.send takes care of response headers
if data then
self:send(data, status)
end
-- finalize chunked transfer encoding
csend("0\r\n\r\n")
-- close connection
cfini()
end
--
local res = { }
res.send_header = send_header
res.send = send
res.finish = finish
return res
end
------------------------------------------------------------------------------
-- HTTP parser
------------------------------------------------------------------------------
local http_handler = function(handler)
return function(conn)
local csend = (require "fifosock").wrap(conn)
local req, res
local buf = ""
local method, url
local ondisconnect = function(connection)
connection:on("receive", nil)
connection:on("disconnection", nil)
connection:on("sent", nil)
collectgarbage("collect")
end
local cfini = function()
csend(function()
conn:on("sent", nil)
conn:close()
ondisconnect(conn)
end)
end
-- header parser
local cnt_len = 0
local onheader = function(_, k, v)
-- TODO: look for Content-Type: header
-- to help parse body
-- parse content length to know body length
if k == "content-length" then
cnt_len = tonumber(v)
end
if k == "expect" and v == "100-continue" then
csend("HTTP/1.1 100 Continue\r\n")
end
-- delegate to request object
if req and req.onheader then
req:onheader(k, v)
end
end
-- body data handler
local body_len = 0
local ondata = function(_, chunk)
-- feed request data to request handler
if not req or not req.ondata then return end
req:ondata(chunk)
-- NB: once length of seen chunks equals Content-Length:
-- ondata(conn) is called
body_len = body_len + #chunk
-- print("-B", #chunk, body_len, cnt_len, node.heap())
if body_len >= cnt_len then
req:ondata()
end
end
local onreceive = function(connection, chunk)
-- merge chunks in buffer
if buf then
buf = buf .. chunk
else
buf = chunk
end
-- consume buffer line by line
while #buf > 0 do
-- extract line
local e = buf:find("\r\n", 1, true)
if not e then break end
local line = buf:sub(1, e - 1)
buf = buf:sub(e + 2)
-- method, url?
if not method then
do
local _
-- NB: just version 1.1 assumed
_, _, method, url = line:find("^([A-Z]+) (.-) HTTP/1.1$")
end
if method then
-- make request and response objects
req = make_req(connection, method, url)
res = make_res(csend, cfini)
end
-- spawn request handler
handler(req, res)
-- header line?
elseif #line > 0 then
-- parse header
local _, _, k, v = line:find("^([%w-]+):%s*(.+)")
-- header seems ok?
if k then
k = k:lower()
onheader(connection, k, v)
end
-- headers end
else
-- NB: we explicitly reassign receive handler so that
-- next received chunks go directly to body handler
connection:on("receive", ondata)
-- NB: we feed the rest of the buffer as starting chunk of body
ondata(connection, buf)
-- buffer no longer needed
buf = nil
-- parser done
break
end
end
end
conn:on("receive", onreceive)
conn:on("disconnection", ondisconnect)
end
end
------------------------------------------------------------------------------
-- HTTP server
------------------------------------------------------------------------------
local srv
local createServer = function(port, handler)
-- NB: only one server at a time
if srv then srv:close() end
srv = net.createServer(net.TCP, 15)
-- listen
srv:listen(port, http_handler(handler))
return srv
end
------------------------------------------------------------------------------
-- HTTP server methods
------------------------------------------------------------------------------
http = {
createServer = createServer,
}
end
return http

Binary file not shown.

File diff suppressed because it is too large Load Diff

@ -0,0 +1,842 @@
tmr.delay(3000000);
print('total'..node.heap());
(function(__run)
--Packages Used: file, sjson, http, httpserver, mqtt, encoder, timer, node, wifi, gpio
print('close'..node.heap())
--Global Methods
---------------
-- File Object Operation
local f = {};
--f
f.read = function(f)--f:filename
local status, obj = pcall(sjson.decode, file.getcontents(f));
if status then
return obj;
else
return {};
end
end
f.write = function(f, obj)
local status, json = pcall(sjson.encode, obj);
if status then
return file.putcontents(f, json);
else
return false;
end
end
--split string
local stringSplit = function(str, reps)
local resultStrList = {}
string.gsub(str,'[^'..reps..']+', function (w)
table.insert(resultStrList,w);
end);
return resultStrList;
end
--Method Declaration
--------------------
_ = {
_ = {
restart = nil,
reset = nil
},
init = {
http = nil,
mqtt = nil,
onlineFunc = nil
},
config = {
v = nil,
path = 'config.json',
default = {
nid = 'default',
offlineOnly = true,
signalPin = 0,
flag = {
MaxRetryTimes = 2,
MaxResetTimes = 3
},
func = {
offline = {
MaxWaitTime = 60
},
online = {
MaxWaitTime = 60
}
},
fs = {
prefix = {
root = '',
system = '__system/',
data = '__data/',
swap = '__system/swap/'
},
filename = {
nsmap = 'ns.map',
flag = 'flag',
func = 'func.json',
error = 'error.json'
}
},
wifi = {
CheckInterval = 1000,
config = {
ssid = '',
pwd = '',
save = false
}
},
http = {
port = 6789,
api = {
discover = '/discover'
}
},
mqtt = {
host = 'mqtt.yimian.xyz',
port = 1883,
user = nil,
password = nil,
tls = false,
keepaliveTimer = 30,
topicPrefix = '/wiot/default/',
ConnectRetryInterval = 2000,
OfflineRetryInterval = 3000,
PostCheckInterval = 100,
PostTimeout = 10
}
}
},
flag = {
v = nil,
set = nil,
load = nil
},
func = {
id = nil,
offline = nil,
online = nil
},
table = {
merge = nil
},
timer = {
setTimeout = nil,
setInterval = nil
},
signal = {
init = nil,
set = nil,
destroy = nil,
v = nil
},
http = {
v = nil
},
mqtt = {
getTopic = nil,
v = nil
},
f = {
read = nil,
write = nil
},
ns = {
set = nil,
get = nil,
render = nil,
exist = nil,
check = nil,
verify = nil
},
package = {
pack = nil,
depack = nil,
set = nil,
get = nil
},
db = {
public = {
keys = nil,
get = nil,
set = nil,
del = nil,
clear = nil
},
toIndex = nil,
fromIndex = nil,
getFileName = nil
},
msg = {
reg = {
send = {},
post = {}
},
postWaitList = {},
public = {
post = nil,
send = nil,
onPost = nil,
onSend = nil
},
mpost = nil,
msend = nil,
dpost = nil,
dsend = nil
},
api = {
http = {
discover = nil,
msg = nil
},
mqtt = {
msg = nil,
heartbeat = nil,
ns = nil,
func = nil,
restart = nil
}
}
};
print('_.all'..node.heap())
print(_.config.default.nid);
local CONFIG_DEFAULT = {
nid = 'default',
offlineOnly = true,
signalPin = 0,
flag = {
MaxRetryTimes = 2,
MaxResetTimes = 3
},
func = {
offline = {
MaxWaitTime = 60
},
online = {
MaxWaitTime = 60
}
},
fs = {
prefix = {
root = '',
system = '__system/',
data = '__data/',
swap = '__system/swap/'
},
filename = {
nsmap = 'ns.map',
flag = 'flag',
func = 'func.json',
error = 'error.json'
}
},
wifi = {
CheckInterval = 1000,
config = {
ssid = '',
pwd = '',
save = false
}
},
http = {
port = 6789,
api = {
discover = '/discover'
}
},
mqtt = {
host = 'mqtt.yimian.xyz',
port = 1883,
user = nil,
password = nil,
tls = false,
keepaliveTimer = 30,
topicPrefix = '/wiot/default/',
ConnectRetryInterval = 2000,
OfflineRetryInterval = 3000,
PostCheckInterval = 100,
PostTimeout = 10
}
}
--Merge config
----------------------
--Methods preparation
-- merge two tables
tableMerge = function(a, b)
for k, v in pairs(b) do
if type(v) == 'table' and type(a[k] or false) == 'table' then
a[k] = tableMerge(a[k], v);
else
a[k] = v;
end
end
return a;
end
print('merge.p'..node.heap())
--Exec
_.config.v = _.config.default;
_.config.v = tableMerge(_.config.v, f.read(_.config.path));
print('merge.e'..node.heap())
--Release Resources
tableMerge = nil;
fRead = nil;
collectgarbage("collect");
print(_.config.v.wifi.config.ssid)
print(_.config.v.mqtt.host)
print('merge.r'..node.heap())
--Load SWAP
-----------
--Methods Preparation
Load_ToSwap = function(obj, s)
print('swap.p'..node.heap()..s)
for k, v in pairs(_) do
s = s..'_'..encoder.toBase64(k);
if type(v) == 'table' then
Load_ToSwap(v, s);
else
file.putcontents(_.config.v.fs.prefix.swap..s, v);
end
end
end
print('swap.p'..node.heap())
-- Exec
Load_ToSwap(_.config.v, encoder.toBase64('config')..'_'..encoder.toBase64('v'));
local SWAP_PREFIX = _.config.v.fs.prefix.swap;
print('swap.e'..node.heap())
--Release Resources
Load_ToSwap = nil;
_ = nil;
print('swap.r'..node.heap())
--Global APIs
_ = setmetaltable({}, {
__index = function(table, key)
local arr = stringSplit(key, '_');
local s = '';
for v in arr do
s = s..'_'..encoder.toBase64(v);
end
return file.getcontents(SWAP_PREFIX..s);
end,
__newindex = function(table, key, val)
local arr = stringSplit(key, '_');
local s = '';
for v in arr do
s = s..'_'..encoder.toBase64(v);
end
if type(val) == 'function' then
val = string.dump(val);
end
file.putcontents(SWAP_PREFIX..s, val);
end
});
print('swap.a'..node.heap())
print(_.config_v_nid);
--package loaded
----------------
local httpserver = dofile('httpserver.lua');
--Method Defination
-------------------
--_
_._.restart = function(err)
if err then
file.putcontents(_.config.v.fs.prefix.system.._.config.v.fs.filename.error, tostring(err));
end
node.restart();
end
_._.reset = function()
file.remove(_.config.v.fs.prefix.system.._.config.v.fs.filename.error);
file.remove(_.config.v.fs.prefix.system.._.config.v.fs.filename.func);
file.remove(_.config.v.fs.prefix.system.._.config.v.fs.filename.flag);
node.restart();
end
--flag
_.flag.load = function()
_.flag.v = tonumber(file.getcontents(_.config.v.fs.prefix.system.._.config.v.fs.filename.flag));
end
_.flag.set = function(val)
_.config.flag.v = val;
file.putcontents(_.config.v.fs.prefix.system.._.config.v.fs.filename.flag, tostring(val));
end
_.flag.ward = function(f)
f();
_.flag.set(-1);
end
--table
--timer
_.timer.setTimeout = function(f, time_ms)
return tmr.create():alarm(time_ms, tmr.ALARM_SINGLE, f);
end
_.timer.setInterval = function(f, time_ms)
return tmr.create():alarm(time_ms, tmr.ALARM_AUTO, function(timer)
f(function(delay_time_ms)
if delay_time_ms < 0 then
timer:unregister();
else
timer:stop();
_.timer.setTimeout(function()
timer:start();
end, delay_time_ms);
end
end)
end);
end
--signal
_.signal.init = function()
gpio.mode(_.config.v.signalPin, gpio.OUTPUT);
_.signal.v = tmr.create();
end
_.signal.set = function(interval_ms)
_.signal.v:alarm(interval_ms, tmr.ALARM_AUTO, function()
if gpio.read(_.config.v.signalPin) == gpio.HIGH then
gpio.write(_.config.v.signalPin, gpio.LOW);
else
gpio.write(_.config.v.signalPin, gpio.HIGH);
end
end);
end
_.signal.destroy = function()
gpio.write(_.config.v.signalPin, gpio.HIGH);
_.signal.v:unregister();
_.signal.v = nil;
end
--mqtt
_.mqtt.getTopic = function(s)
return _.config.v.mqtt.topicPrefix.._.config.v.nid..'/'..s;
end
_.mqtt.start = function()
_.mqtt.v:connect(
_.config.v.mqtt.host,
_.config.v.mqtt.port,
_.config.v.mqtt.tls,
function(client)
client:subscribe(_.mqtt.getTopic('msg/#'));
client:subscribe(_.mqtt.getTopic('ctl/#'));
client:publish(_.mqtt.getTopic('status'), 'online', 0, 0);
end,
function(client, reason)
_.timer.setTimeout(_.mqtt.start, _.config.v.mqtt.ConnectRetryInterval);
end
);
end
--ns
_.ns.set = function(obj)
return _.f.write(_.config.v.fs.prefix.system.._.config.v.fs.filename.nsmap, obj);
end
_.ns.get = function()
return _.f.read(_.config.v.fs.prefix.system.._.config.v.fs.filename.nsmap);
end
_.ns.render = function(nid)
for k, v in pairs(_.ns.get()) do
if k == nid and v then
return v;
end
end
return nid;
end
_.ns.exist = function(nid)
if _.ns.render(nid) == nid then
return false;
else
return true;
end
end
_.ns.check = function(nid, ip, cb)--cb:bool status
http.post('http://'..ip.._.config.v.http.api.discover, nil, '', function(code, data)
if code ~= 200 or data ~= nid then
cb(false);
else
cb(true);
end
end);
end
_.ns.verify = function()
local ns = _.ns.get();
for k, v in pairs(ns) do
_.ns.check(k, v, function(status)
if not status then
ns[k] = false;
_.ns.set(ns);
end
end)
end
end
--package
_.package.pack = function(o)
local status, json = pcall(sjson.encode, o);
if status then
return json;
else
return nil;
end
end
_.package.depack = function(s)
local status, obj = pcall(json.decode, s);
if status then
return obj;
else
return {};
end
end
_.package.set = function(to, body, mode)--to:nid, body:obj
local o = {
from = _.config.v.nid,
to = to,
mode = mode,
body = body
}
return _.package.pack(o);
end
_.package.get = function(pack)--string package
local status, obj = pcall(json.decode, pack);
if status and obj.from and obj.mode then
return obj.from, obj.body, obj.mode;
else
return nil, nil, nil;
end
end
--db
_.db.toIndex = function(key)
return encoder.toBase64(key);
end
_.db.fromIndex = function(index)
return encoder.fromBase64(index);
end
_.db.getFileName = function(key)
return _.config.v.fs.prefix.data.._.db.toIndex(key);
end
_.db.public.keys = function() --list all keys with size
local o = {};
for k, v in pairs(file.list(_.config.v.fs.prefix.data..'.')) do
o[_.db.fromIndex(k)] = v;
end
return o;
end
_.db.public.get = function(key)
return _.f.read();
end
_.db.public.set = function(key, obj)
return _.f.write(_.db.getFileName(key), obj);
end
_.db.public.del = function(key)
local fileName = _.db.getFileName(key);
file.remove(fileName);
return not file.exists(fileName);
end
_.db.public.clear = function()
local flag = true;
for k, v in pairs(_.db.public.keys()) do
flag = flag and _.db.public.del(k);
end
return flag;
end
--msg
_.msg.dpost = function(to, name, body, cb)--return (bool status, data)
if _.ns.exist(to) then
http.post(
'http://'.._.ns.render(to)..'/msg/'..encoder.toBase64(name),
'Content-Type: application/json\r\n',
_.package.set(to, body, 'post'),
function(code, res)
if code ~= 200 then
cb(false);
else
cb(true, _.package.get(res));
end
end
);
else
cb(false);
end
end
_.msg.dsend = function(to, name, body)
if _.ns.exist(to) then
http.post(
'http://'.._.ns.render(to)..'/msg/'..encoder.toBase64(name),
'Content-Type: application/json\r\n',
_.package.set(to, body, 'send'),
function(code, res) end
);
end
end
_.msg.mpost = function(to, name, body, cb)
_.msg.postWaitList[encoder.toBase64(name)..to] = false;
local PostBeginAt = tmr.time();
_.timer.setInterval(function(delay)
if _.msg.postWaitList[encoder.toBase64(name)..to] ~= false then
cb(true, _.msg.postWaitList[encoder.toBase64(name)..to]);
_.msg.postWaitList[encoder.toBase64(name)..to] = nil;
delay(-1);
else
if PostBeginAt < tmr.time() - _.config.v.mqtt.PostTimeout then
cb(false);
_.msg.postWaitList[encoder.toBase64(name)..to] = nil;
delay(-1);
end
end
end, _.config.v.mqtt.PostCheckInterval);
_.mqtt.v:publish(
_.config.v.mqtt.topicPrefix..to..'/'..encoder.toBase64(name),
_.package.set(to, body, 'post'),
1,
0
);
end
_.msg.msend = function(to, name, body)
_.mqtt.v:publish(
_.config.v.mqtt.topicPrefix..to..'/'..encoder.toBase64(name),
_.package.set(to, body, 'send'),
0,
0
);
end
_.msg.public.onSend = function(name, f)--f:return (body, from)
_.msg.reg.send[encoder.toBase64(name)] = f;
end
_.msg.public.onPost = function(name, f)--f:return body, from, reply(reply_body)
_.msg.reg.post[encoder.toBase64(name)] = f;
end
_.msg.public.send = function(to, name, body)
if _.ns.exist(to) then
_.msg.dsend(to, name, body);
else
_.msg.msend(to, name, body);
end
end
_.msg.public.post = function(to, name, body, cb)
if _.ns.exist(to) then
_.msg.dpost(to, name, body, cb);
else
_.msg.mpost(to, name, body, cb);
end
end
--api
_.api.http.discover = function(res)
res.finish(_.config.v.nid, 200);
end
_.api.http.msg = function(name, req, res)
local data = '';
req.ondata = function(req, chunk)
if chunk ~= nil then
data = data..chunk;
else
local from, body, mode = _.package.get(data);
if mode == 'send' then
res.finish();
for k, v in pairs(_.msg.reg.send) do
if k == name then
v(body, from);
return;
end
end
elseif mode == 'post' then
for k, v in pairs(_.msg.reg.post) do
if k == name then
v(body, from, function(reply_body)
res.finish(_.package.set(from, reply_body, 'reply'));
end);
return;
end
end
res.finish(nil, 404);
end
end
end
end
_.api.mqtt.msg = function(name, data)
local from, body, mode = _.package.get(data);
if mode == 'send' then
for k, v in pairs(_.msg.reg.send) do
if k == name then
v(body, from);
return;
end
end
elseif mode == 'post' then
for k, v in pairs(_.msg.reg.post) do
if k == name then
v(body, from, function(reply_body)
_.mqtt.v:publish(_.config.v.mqtt.topicPrefix..from..'/msg/'..name.._.config.v.nid, _.package.set(from, reply_body, 'reply'), 1, 0);
end);
return;
end
end
elseif mode == 'reply' then
for k, v in pairs(_.msg.postWaitList) do
if k == name then
_.msg.postWaitList[k] = body;
return;
end
end
end
end
_.api.mqtt.heartbeat = function()
local o = {
nid = _.config.v.nid,
uptime = tmr.time(),
heap = node.heap(),
funcID = _.func.id,
ip = wifi.sta.getip(),
ns = _.ns.get()
}
_.mqtt.v:publish(_.mqtt.getTopic('heartbeat'), _.package.pack(o), 0, 0);
end
_.api.mqtt.ns = function(data)
local obj = _.package.depack(data);
_.ns.set(obj);
end
_.api.mqtt.func = function(data)
local obj = _.package.depack(data);
_.f.write(_.config.v.fs.prefix.system.._.config.v.fs.filename.func, obj);
_._.restart();
end
_.api.mqtt.restart = function()
_._.restart();
end
--Environment Loading & Checking
----------------------
--Flag Checking
_.flag.load();
if _.flag.v == nil then
_.flag.set(0);
end
--User Func Checking
local func = _.f.read(_.config.v.fs.prefix.system.._.config.v.fs.filename.func);
if func and func.id and func.offline and func.online then
_.func.id = func.id;
_.func.offline = func.offline;
_.func.online = func.online;
end
func = nil;
--System Preparation
--------------------
--Signal Start
_.signal.init();
_.signal.set(1500);
--Decide System Mode
_.flag.set(_.flag.v + 1);
if _.flag.v >= _.config.v.flag.MaxRetryTimes and _.flag.v <= _.config.v.flag.MaxRetryTimes + _.config.v.flag.MaxResetTimes then
_._.reset();
elseif _.flag.v > _.config.v.flag.MaxRetryTimes + _.config.v.flag.MaxResetTimes then
--safe mode
--Signal set
_.signal.set(3000);
do return end;
end
--OFFLINE System Launch
-----------------------
--Signal set
_.signal.set(800);
--Run user offline func
_.flag.ward(function()
tmr.softwd(_.config.v.func.offline.MaxWaitTime); --start watchdog
if not __run(_.func.offline, _.timer, _.db.public, nil) then --enable DB and disable MSG
_._.restart('Offline Func Startup Fail..');
end
tmr.softwd(-1); --disable watchdog
end);
--Network Modules Init
----------------------
--If OFFLINEONLY is configed, do not start network
if _.config.v.offlineOnly == true then
return;
end
--Signal set
_.signal.set(200);
--Connect to wifi AP
wifi.setmode(wifi.STATION);
wifi.sta.sethostname('WIOT-'.._.config.v.nid);
wifi.sta.config(_.config.v.wifi.config);
_.timer.setInterval(function(delay)
if wifi.sta.getip() ~= nil then
delay(-1);
_.init.http();
_.init.mqtt();
end
end, _.config.v.wifi.CheckInterval);
--http module
_.init.http = function()
_.http.v = httpserver.createServer(_.config.v.http.port, function(req, res)
local path = string.split(req.url, '/');
if path[2] == 'discover' then
_.api.http.discover(res);
elseif path[2] == 'msg' and path[3] then
_.api.http.msg(path[3], req, res);
else
res.finish('', 500);
end
end);
end
--mqtt module
_.init.mqtt = function()
_.mqtt.v = mqtt.Client(_.config.v.nid, _.config.v.mqtt.keepaliveTimer, _.config.v.mqtt.user, _.config.v.mqtt.password);
_.mqtt.v:lwt(_.mqtt.getTopic('status'), 'offline', 0, 0);
_.mqtt.v:on('offline', function()
_.timer.setTimeout(_.mqtt.start, _.config.v.mqtt.OfflineRetryInterval);
end);
_.mqtt.v:on('message', function(client, topic, data)
local path = string.split(string.split(topic, _.config.v.mqtt.topicPrefix), '/');
if path[1] == 'ctl' then
if path[2] == 'heartbeat' then
_.api.mqtt.heartbeat();
elseif path[2] == 'ns' then
_.api.mqtt.ns(data);
elseif path[2] == 'func' then
_.api.mqtt.func(data);
elseif path[2] == 'restart' then
_.api.mqtt.restart();
end
elseif path[1] == 'msg' and path[2] then
_.api.mqtt.msg(path[2], data);
end
end);
end
--ONLINE System Launch
----------------------
--Run user offline func
_.init.onlineFunc = function()
_.flag.ward(function()
tmr.softwd(_.config.v.func.online.MaxWaitTime); --start watchdog
if not __run(_.func.online, _.timer, _.db.public, _.msg.public) then --enable DB and MSG
_._.restart('online Func Startup Fail..');
end
tmr.softwd(-1); --disable watchdog
_.signal.destroy(); --destroy signal
end);
end
end)(function(func, timer, db, msg)
return pcall(loadstring(func));
end);

@ -0,0 +1,314 @@
tmr.create():alarm(5000, tmr.ALARM_SINGLE, function()
print('total', node.heap());
(function(__run)
--Packages Used: file, sjson, http, httpserver, mqtt, encoder, timer, node, wifi, gpio
collectgarbage("collect")
print('close', node.heap())
--SYSTEM CONSTANT
local CONFIG_PATH = 'config.json';
local DEFAULT_CONFIG = {
nid = 'default',
offlineOnly = true,
signalPin = 0,
flag_MaxRetryTimes = 200,
flag_MaxResetTimes = 3,
func_offline_MaxWaitTime = 60,
func_online_MaxWaitTime = 60,
fs_prefix_root = '',
fs_prefix_system = '__system/',
fs_prefix_data = '__data/',
fs_prefix_swap = '__system/swap/',
fs_filename_nsmap = 'ns.map',
fs_filename_flag = 'flag',
fs_filename_func = 'func.json',
fs_filename_error = 'error',
wifi_CheckInterval = 1000,
wifi_config_ssid = '',
wifi_config_pwd = '',
wifi_config_save = false,
http_port = 6789,
http_api_discover = '/discover',
mqtt_host = 'mqtt.yimian.xyz',
mqtt_port = 1883,
mqtt_user = nil,
mqtt_password = nil,
mqtt_tls = false,
mqtt_keepaliverTimer = 30,
mqtt_topicPrefix = '/wiot/default/',
mqtt_ConnectRetryInterval = 2000,
mqtt_OfflineRetryInterval = 3000,
mqtt_PostCheckInterval = 100,
mqtt_PostTimeout = 10
}
collectgarbage("collect")
print('SYSTEM CONSTANT', node.heap())
--Init Global Methods
---------------------
-- File Object Operation
local fs = {
read = function(f)--f:filename
local status, obj = pcall(sjson.decode, file.getcontents(f));
if status then
return obj;
else
return {};
end
end,
write = function(f, obj)
local status, json = pcall(sjson.encode, obj);
if status then
return file.putcontents(f, json);
else
return false;
end
end
}
--split string
local stringSplit = function(str, reps)
local resultStrList = {}
string.gsub(str,'[^'..reps..']+', function (w)
table.insert(resultStrList,w);
end);
return resultStrList;
end
collectgarbage("collect")
print('Init Global Methods', node.heap())
--Config Loading
-----------------
--load user config
config = fs.read(CONFIG_PATH);
--print sth here, otherwise cause error. Amazing.
print('');
--merge user and default config
for k, v in pairs(DEFAULT_CONFIG) do
if config[k] == nil then
config[k] = v;
end
end
--release resource
DEFAULT_CONFIG = nil;
collectgarbage("collect")
print('Config Loading', node.heap())
--SWAP Medthods
----------------
local swap = {
set = function(index, val)
fs.write(config.fs_prefix_swap..index, {val});
end,
get = function(index)
return fs.read(config.fs_prefix_swap..index)[1];
end
}
collectgarbage("collect")
print('SWAP Medthods', node.heap())
--[[
--CONFIG to SWAP
----------------
for k, v in pairs(config) do
swap.set('C'..k, v);
end
config = setmetatable({
fs_prefix_swap = config.fs_prefix_swap
}, {
__index = function(table, key)
return swap.get('C'..key);
end
});
collectgarbage("collect")
print('CONFIG to SWAP', node.heap())
]]
--Variables in SWAP
----------------
var = setmetatable({}, {
__index = function(table, key)
return swap.get('V'..key);
end,
__newindex = function(table, key, val)
swap.set('V'..key, val);
end
});
collectgarbage("collect")
print('Variables in SWAP', node.heap())
--Methods in SWAP
----------------
f = setmetatable({}, {
__index = function(table, key)
return loadstring(encoder.fromBase64(swap.get('F'..key)));
end,
__newindex = function(table, key, val)
swap.set('F'..key, encoder.toBase64(string.dump(val)));
end
});
collectgarbage("collect")
print('Methods in SWAP', node.heap())
--_
f._restart = function(err)
if err then
file.putcontents(config.fs_prefix_system..config.fs_filename_error, tostring(err));
end
node.restart();
end
f._reset = function()
file.remove(_.config.v.fs.prefix.system.._.config.v.fs.filename.error);
file.remove(_.config.v.fs.prefix.system.._.config.v.fs.filename.func);
file.remove(_.config.v.fs.prefix.system.._.config.v.fs.filename.flag);
node.restart();
end
--flag
f.flag_load = function()
local flag = file.getcontents(config.fs_prefix_system..config.fs_filename_flag);
if flag then
var.flag_v = tonumber(flag);
end
end
f.flag_set = function(val)
var.flag_v = val;
file.putcontents(config.fs_prefix_system..config.fs_filename_flag, tostring(val));
end
f.flag_ward = function(func)
func();
f.flag_set(var, config, -1);
end
--signal
f.signal_init = function()
gpio.mode(config.signalPin, gpio.OUTPUT);
signal_v = tmr.create();
end
f.signal_set = function(interval_ms)
signal_v:alarm(interval_ms, tmr.ALARM_AUTO, function()
if gpio.read(config.signalPin) == gpio.HIGH then
gpio.write(config.signalPin, gpio.LOW);
else
gpio.write(config.signalPin, gpio.HIGH);
end
end);
end
f.signal_destroy = function()
gpio.write(config.signalPin, gpio.HIGH);
signal_v:unregister();
signal_v = nil;
end
--timer
f.timer_setTimeout = function(func, time_ms)
return tmr.create():alarm(time_ms, tmr.ALARM_SINGLE, func);
end
f.timer_setInterval = function(func, time_ms)
return tmr.create():alarm(time_ms, tmr.ALARM_AUTO, func);
end
--mqtt
f.mqtt_getTopic = function(s)
return _.config.v.mqtt.topicPrefix.._.config.v.nid..'/'..s;
end
f.mqtt_start = function()
mqtt_v:connect(
config.mqtt_host,
config.mqtt_port,
config.mqtt_tls,
function(client)
client:subscribe(f.mqtt_getTopic('msg/#'));
client:subscribe(f.mqtt_getTopic('ctl/#'));
client:publish(f.mqtt_getTopic('status'), 'online', 0, 0);
end,
function(client, reason)
f.timer_setTimeout(f.mqtt_start, config.mqtt_ConnectRetryInterval);
end
);
end
collectgarbage("collect")
print('Methods in SWAP', node.heap())
--Environment Loading & Checking
----------------------
--Flag Checking
f.flag_load();
if var.flag_v == nil then
f.flag_set(0);
end
--User Func Checking
local func = fs.read(config.fs_prefix_system..config.fs_filename_func);
if func and func.id and func.offline and func.online then
var.func_id = func.id;
var.func_offline = func.offline;
var.func_online = func.online;
end
func = nil;
collectgarbage("collect")
print('Environment Loading & Checking', node.heap())
--System Preparation
--------------------
--Signal Start
f.signal_init();
collectgarbage("collect")
print('System Preparation', node.heap())
f.signal_set(1500);
collectgarbage("collect")
print('System Preparation', node.heap())
--Decide System Mode
f.flag_set(var.flag_v + 1);
collectgarbage("collect")
print('System Preparation', node.heap())
if var.flag_v >= config.flag_MaxRetryTimes and var.flag_v <= config.flag_MaxRetryTimes + config.flag_MaxResetTimes then
f._reset();
elseif var.flag_v > config.flag_MaxRetryTimes + config.flag_MaxResetTimes then
--safe mode
--Signal set
f.signal_set(3000);
do return end;
end
collectgarbage("collect")
print('System Preparation', node.heap())
--OFFLINE System Launch
-----------------------
--Signal set
f.signal_set(800);
--Run user offline func
f.flag_ward(function()
tmr.softwd(config.func_offline_MaxWaitTime); --start watchdog
if not __run(var.func_offline) then --enable DB and disable MSG
f._restart('Offline Func Startup Fail..');
end
tmr.softwd(-1); --disable watchdog
end);
collectgarbage("collect")
print('OFFLINE System Launch', node.heap())
end)(function(func)
return pcall(loadstring(func));
end);
end);

Binary file not shown.
Loading…
Cancel
Save