Skip to content

Instantly share code, notes, and snippets.

@TerryE
Created October 27, 2019 10:55
Show Gist options
  • Save TerryE/721ffc10d2e397b5f25eec2a2c371d3a to your computer and use it in GitHub Desktop.
Save TerryE/721ffc10d2e397b5f25eec2a2c371d3a to your computer and use it in GitHub Desktop.
Rough cut coroutine based provisioning system
local post, yield = node.task.post, coroutine.yield
local max_namelen = 31
local request = '\z
%s %s%s.img HTTP/1.1\r\n\z
User-Agent: ESP8266 app (linux-gnu)\r\n\z
Accept: application/octet-stream\r\n\z
Accept-Encoding: identity\r\n\z
Host: %s\r\n\z
Connection: %s\r\n\r\n'
local host, dir, image, onDone
local cleanup, receiver
local function protected_receiver (soc, rec) -- Upval:post, next_rec, image
local status = tonumber(rec:match('HTTP/1.1%s+(%d+)') or '0')
assert(status == 200, "Image %s does not exist" % image)
local etag = rec:lower():match('\netag: *"([%d%-a-f]*)"\r')
local tailsize = max_namelen - #image - #'.img.'
local filename = image .. '.img.' .. (etag or ''):sub(-tailsize)
if etag and file.exists(filename) then
return "Image %s already downloaded" % image
end
soc:send(request % {'GET', dir, image, host,'Close'})
soc, rec = yield()
local i = rec:find('\r\n\r\n',1,true) or 1
local hdr = rec:sub(1,i+1):lower()
rec = rec:sub(i+4)
local size = tonumber(hdr:match('\ncontent%-length: *(%d+)\r')) or
error 'invalid size returned'
assert(etag == hdr:match('\netag: *"([%d%-a-f]*)"\r'), "Etag missmatch")
local f = file.open(filename, 'w+')
local j, nr = 0, 0
repeat
f:write(rec)
j, nr = j + #rec, nr + 1
if nr % 4 == 1 then -- hold and unhold socket to prevent overrun
soc:hold()
post(0, function() soc:unhold() end)
end
collectgarbage()
if j < size then soc, rec = yield() end
until j >= size
f:close()
if j ~= size then file.erase(filename) end
assert(j==size, "File size mismatch %s removed" % filename)
soc:on('receive', nil)
for n,l in pairs(file.list('^%s.img' % image)) do
if n ~= filename then file.remove(n) end
end
return "Image %s created as %s (%d bytes)" % {image, filename, size}
end
receiver = coroutine.wrap(
function(soc, rec) -- Upval: protected_receiver, post
local status, message = pcall(protected_receiver, soc, rec)
post(function() cleanup(status, message, soc) end)
end)
local function doRequest(sk,hostIP)
if not hostIP then return end
local con = net.createConnection(net.TCP,0)
con:connect(80,hostIP)
con:on("connection", function(sck) -- Upval: dir, iage, host
sck:send(request % {'HEAD', dir, image, host,'Keep-Alive'})
sck:on("receive", receiver)
end)
end
local cleanTBD = true
cleanup = function (status, message, soc) -- Upval: onDone, post
if not cleanTBD then return end
cleanTBD = false
if soc and soc.close then soc:__gc() end
receiver=nil
collectgarbage()
if onDone and type(onDone)=="function" then
post(function() onDone(status, message) end) -- Upval onDone, status, message
else
print ("%s: %s" % {status and "Success" or "Failed", message})
end
end
return function(h, d, i, f)
host, dir, image, onDone = h, d, i, f
net.dns.resolve(host, doRequest)
end
local ssid, pwd = 'passiveHouse','IN.TE.SPERANT.DOMINE'
local host, dir = '192.168.1.2','/test2/'
local images = 'api attrib big bitwise closure code constructs \z
coroutine db errors esp-files events files gc goto \z
literals locals main math nextvar pm sort stackbreak \z
strings tpack utf8 vararg verybig '
local check
local post,yield = node.task.post, coroutine.yield
local httpload = dofile'HTTPloadfile.lua'
check = coroutine.wrap(function(t)
if wifi.sta.status() ~= wifi.STA_GOTIP and not wifi.sta.getip() then
wifi.setmode(wifi.STATION, false)
wifi.sta.config { ssid = ssid, pwd = pwd, save = false }
while wifi.sta.status() == wifi.STA_GOTIP do
uart.write(0,".")
yield()
end
end
print("NodeMCU connected", node.heap(), wifi.sta.getip())
t:unregister()
for i in images:gmatch("([%w%-]+) ") do
print ("Starting download for %s" % i)
post(function() httpload(host, dir, i, check) end)
local status, message = yield()
if status then
print(message)
else
print("Load failed:", message)
end
end
end)
tmr.create():alarm(500, tmr.ALARM_AUTO,coroutine.wrap(check))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment