commit 26d1dd5e8705fe7d9a736f9cc0d0db9882cbe1e1 Author: Wesley Kerfoot Date: Sun Oct 17 00:11:12 2021 -0400 initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..b72a232 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +credentials.lua diff --git a/.nodemcutool b/.nodemcutool new file mode 100644 index 0000000..361f6b5 --- /dev/null +++ b/.nodemcutool @@ -0,0 +1,7 @@ +{ + "baudrate": "115200", + "port": "/dev/ttyUSB0", + "minify": false, + "compile": false, + "keeppath": false +} \ No newline at end of file diff --git a/credentials.lua.example b/credentials.lua.example new file mode 100644 index 0000000..9b92e9d --- /dev/null +++ b/credentials.lua.example @@ -0,0 +1,2 @@ +SSID="something" +PASSWORD="yourpassword" diff --git a/fifo.lua b/fifo.lua new file mode 100644 index 0000000..d21b1da --- /dev/null +++ b/fifo.lua @@ -0,0 +1,45 @@ +-- A generic fifo module. See docs/lua-modules/fifo.md for use examples. + +local tr, ti = table.remove, table.insert + +-- Remove an element and pass it to k, together with a boolean indicating that +-- this is the last element in the queue; if that returns a value, leave that +-- pending at the top of the fifo. +-- +-- If k returns nil, the fifo will be advanced. Moreover, k may return a +-- second result, a boolean, indicating "phantasmic" nature of this element. +-- If this boolean is true, then the fifo will advance again, passing the next +-- value, if there is one, to k, or priming itself for immediate execution at +-- the next call to queue. +-- +-- If the queue is empty, do not invoke k but flag it to enable immediate +-- execution at the next call to queue. +-- +-- Returns 'true' if the queue contained at least one non-phantom entry, +-- 'false' otherwise. +local function dequeue(q,k) + if #q > 0 + then + local new, again = k(q[1], #q == 1) + if new == nil + then tr(q,1) + if again then return dequeue(q, k) end -- note tail call + else q[1] = new + end + return true + else q._go = true ; return false + end +end + +-- Queue a on queue q and dequeue with `k` if the fifo had previously emptied. +local function queue(q,a,k) + ti(q,a) + if k ~= nil and q._go then q._go = false; dequeue(q, k) end +end + +-- return a table containing just the FIFO constructor +return { + ['new'] = function() + return { ['_go'] = true ; ['queue'] = queue ; ['dequeue'] = dequeue } + end +} diff --git a/fifosock.lua b/fifosock.lua new file mode 100644 index 0000000..c453fb1 --- /dev/null +++ b/fifosock.lua @@ -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 } diff --git a/fifosocktest.lua b/fifosocktest.lua new file mode 100644 index 0000000..4ac9f21 --- /dev/null +++ b/fifosocktest.lua @@ -0,0 +1,145 @@ +-- +-- Set verbose to 0 for quiet output (either the first assertion failure or +-- "All tests OK"), to 1 to see the events ("SEND", "SENT", "CHECK") without +-- the actual bytes, or to 2 to see the events with the bytes. +-- +local verbose = 0 + +local vprint = (verbose > 0) and print or function() end + +-- +-- Mock up enough of the nodemcu tmr structure, but pretend that nothing +-- happens between ticks. This won't exercise the optimistic corking logic, +-- but that's probably fine. +-- luacheck: push ignore +tmr = {} +tmr.ALARM_SINGLE = 0 +function tmr.create() + local r = {} + function r:alarm(_i, _t, cb) vprint("TMR") cb() end + return r +end +-- luacheck: pop + +-- +-- Mock up enough of the nodemcu net.socket type; have it log all the sends +-- into this "outs" array so that we can later check against it. +-- +local outs = {} +local fakesock = { + cb = nil, + on = function(this, _, cb) this.cb = cb end, + send = function(this, s) vprint("SEND", (verbose > 1) and s) table.insert(outs, s) end -- luacheck: no unused +} +local function sent() vprint("SENT") fakesock.cb() end + +-- And wrap a fifosock around this fake socket +local fsend = (require "fifosock").wrap(fakesock) + +-- Verify that the next unconsumed output is as indicated +local function fcheck(x) + vprint ("CHECK", (verbose > 1) and x) + assert (#outs > 0) + assert (x == outs[1]) + table.remove(outs, 1) +end + +-- Enqueue an empty function to prevent coalescing within the fifosock +local function nocoal() fsend(function() return nil end) end + +-- Send and check, for when the string should be sent exactly as is +local function fsendc(x) fsend(x) fcheck(x) end + +-- Check that there are no more outputs +local function fchecke() vprint("CHECKE") assert (#outs == 0) end + +-- +-- And now for the tests, which start easy and grow in complexity +-- + +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, 32 do fsend("a") end -- luacheck: no unused +nocoal() +for i = 1, 4 do fsend("a") end -- luacheck: no unused +sent() ; fcheck(string.rep("a", 32)) +sent() ; fcheck(string.rep("a", 4)) +sent() ; fchecke() + +-- Hit string length while building up +fsendc("abracadabra overlong") +for i = 1, 10 do fsend(string.rep("a",32)) end -- luacheck: no unused +sent() ; fcheck(string.rep("a", 320)) +sent() ; fchecke() + +-- Hit neither before sending a big string +fsendc("abracadabra mid long") +for i = 1, 6 do fsend(string.rep("a",32)) end -- luacheck: no unused +fsend(string.rep("b", 256)) +nocoal() +for i = 1, 6 do fsend(string.rep("c",32)) end -- luacheck: no unused +sent() ; fcheck(string.rep("a", 192) .. string.rep("b", 256)) +sent() ; fcheck(string.rep("c", 192)) +sent() ; fchecke() + +-- send a huge string, verify that it coalesces +fsendc(string.rep("a",256) .. string.rep("b", 256) .. string.rep("c", 260)) +sent() ; fchecke() + +-- send a huge string, verify that it coalesces save for the short bit at the end +fsend(string.rep("a",256) .. string.rep("b", 256) .. string.rep("c", 256) .. string.rep("d",256)) +fsend("e") +fcheck(string.rep("a",256) .. string.rep("b", 256) .. string.rep("c", 256)) +sent() ; fcheck(string.rep("d",256) .. "e") +sent() ; fchecke() + +-- send enough that our 4x lookahead still leaves something in the queue +fsend(string.rep("a",512) .. string.rep("b", 512) .. string.rep("c", 512)) +fcheck(string.rep("a",512) .. string.rep("b", 512)) +sent() ; fcheck(string.rep("c",512)) +sent() ; fchecke() + +-- test a lazy generator +do + local ix = 0 + local function gen() vprint("GEN", ix); ix = ix + 1; return ("a" .. ix), ix < 3 and gen end + fsend(gen) + fsend("b") + fcheck("a1") + sent() ; fcheck("a2") + sent() ; fcheck("a3") + sent() ; fcheck("b") + sent() ; fchecke() +end +-- test a completion-like callback that does send text +do + local ix = 0 + local function gen() vprint("GEN"); ix = 1; return "efgh", nil end + fsend("abcd"); fsend(gen); fsend("ijkl") + assert (ix == 0) + fcheck("abcd"); assert (ix == 0) + sent() ; fcheck("efgh"); assert (ix == 1); ix = 0 + sent() ; fcheck("ijkl"); assert (ix == 0) + sent() ; fchecke() +end +-- and one that doesn't +do + local ix = 0 + local function gen() vprint("GEN"); ix = 1; return nil, nil end + fsend("abcd"); fsend(gen); fsend("ijkl") + assert (ix == 0) + fcheck("abcd"); assert (ix == 0) + sent() ; fcheck("ijkl"); assert (ix == 1); ix = 0 + sent() ; fchecke() ; assert (ix == 0) +end +print("All tests OK") diff --git a/flash.sh b/flash.sh new file mode 100755 index 0000000..8405b61 --- /dev/null +++ b/flash.sh @@ -0,0 +1,12 @@ +nodemcu-tool reset +esptool.py erase_flash +esptool.py --port /dev/ttyUSB0 write_flash -fm qio 0x00000 nodemcu-release-16-modules-2021-10-09-22-33-53-float.bin + + +nodemcu-tool upload *.lua + +while [[ $? != 0 ]]; do + nodemcu-tool upload *.lua +done + +echo 'dofile("init.lua")' | nodemcu-tool terminal diff --git a/httpserver.lua b/httpserver.lua new file mode 100644 index 0000000..edab602 --- /dev/null +++ b/httpserver.lua @@ -0,0 +1,217 @@ +------------------------------------------------------------------------------ +-- HTTP server module +-- +-- LICENCE: http://opensource.org/licenses/MIT +-- Vladimir Dronnikov +------------------------------------------------------------------------------ +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 diff --git a/init.lua b/init.lua new file mode 100644 index 0000000..50ccbfa --- /dev/null +++ b/init.lua @@ -0,0 +1,263 @@ +-- Never change these unless the board changes +echo_pin = 2 +trig_pin = 3 +light_pin = 5 + +light_on = false + +if adc.force_init_mode(adc.INIT_ADC) +then + node.restart() + return -- don't bother continuing, the restart is scheduled +end + +print("System voltage (mV):", adc.readvdd33(0)) + +gpio.mode(light_pin, gpio.OUTPUT) +gpio.mode(echo_pin, gpio.INT) -- interrupt mode +gpio.mode(trig_pin, gpio.OUTPUT) +gpio.write(light_pin, gpio.HIGH) + +function toggle_light() + if light_on then + gpio.write(light_pin, gpio.HIGH) + else + gpio.write(light_pin, gpio.LOW) + end + light_on = not light_on +end + +function turn_light_on() + gpio.write(light_pin, gpio.LOW) +end + +function turn_light_off() + gpio.write(light_pin, gpio.HIGH) +end + +function tablelen(T) + local count = 0 + for _ in pairs(T) do count = count + 1 end + return count +end + +samples = {} +distance = {start_v=0.0, end_v=0.0} + +sample_rate = 4 -- number of samples it requires before triggering, recommended <= 5, >=3 +max_stderr = 0.5 -- margin of error allowed before triggering the light, recommended <= 1 +distance_max = 30 -- threshold for how far an object must be to trigger it in cm +distance_min = 10 -- minimum distance (below this amount don't trigger it), recommended >= 5, <= 20 + +-- Get the mean value of a table +function mean(t) + -- http://lua-users.org/wiki/SimpleStats + local sum = 0 + local count = 0 + + for k,v in pairs(t) do + if type(v) == 'number' then + sum = sum + v + count = count + 1 + end + end + return (sum / count) +end + +function stdev(t) + -- http://lua-users.org/wiki/SimpleStats + local m + local vm + local sum = 0 + local count = 0 + local result + + m = mean(t) + + for k, v in pairs(t) do + if type(v) == 'number' then + vm = v - m + sum = sum + (vm * vm) + count = count + 1 + end + end + + result = math.sqrt(sum / (count-1)) + + return result +end + +function stderr(t) + -- based on the definition of standard error + return stdev(t) / math.sqrt(tablelen(t)) +end + +function measure(level, ts, evcount) + if level == 1 then + distance["start_v"] = ts + else + -- check if there's a low voltage but no starting high voltage + if distance["start_v"] > 0 then + distance["end_v"] = ts + + if distance["start_v"] >= distance["end_v"] then + distance = {start_v=0.0, end_v=0.0} -- bad reading + return + end + + -- See https://www.adafruit.com/product/165 + local temperature = ((adc.read(0)*100)-50)/1000 + + -- See http://hyperphysics.phy-astr.gsu.edu/hbase/Sound/souspe.html + local speed_of_sound = (331.4 + 0.6*temperature)/10000 + + -- See https://randomnerdtutorials.com/esp8266-nodemcu-hc-sr04-ultrasonic-arduino/ + local d = ((distance["end_v"] - distance["start_v"]) * speed_of_sound) / 2.0 + + -- Only care about values between 10 and 50 + if math.floor(d) > distance_min and d < distance_max then + table.insert(samples, d) + local s = stderr(samples) + if s > max_stderr and tablelen(samples) == (sample_rate - 1) then -- start over if it would cause stderr to be too great + samples = {} + distance = {start_v=0.0, end_v=0.0} + end + + end + distance = {start_v=0, end_v=0} + end + end + + local s_num = tablelen(samples) + + if s_num >= sample_rate then + local distance_mean = mean(samples) + local s = stderr(samples) + if s < max_stderr and distance_mean < distance_max and distance_mean > distance_min then + print("detected something") + print("stder = " .. s) + print("distance = " .. distance_mean) + if not light_on then + toggle_light() + end + end + samples = {} + end +end + +function trig() + gpio.write(trig_pin, gpio.HIGH) + tmr.delay(11) + gpio.write(trig_pin, gpio.LOW) +end + +-- load credentials, 'SSID' and 'PASSWORD' declared and initialize in there +dofile("credentials.lua") + +function startup() + + if file.open("init.lua") == nil then + print("init.lua deleted or renamed") + else + print("Running") + file.close("init.lua") + + print("Starting up") + sntp.sync(nil, + function(sec, usec, server, info) + print("sync'd") + tmr.create():alarm(61, tmr.ALARM_AUTO, trig) + gpio.trig(echo_pin, "both", measure) + end, + nil, 1) + + print("Timer created") + + require("httpserver").createServer(80, function(req, res) + print("+R", req.method, req.url, node.heap()) + + req.onheader = function(self, name, value) -- luacheck: ignore + print("+H", name, value) + end + -- setup handler of body, if any + req.ondata = function(self, chunk) -- luacheck: ignore + print("+B", chunk and #chunk, node.heap()) + print(req.url) + if not chunk then + -- reply + res:send(nil, 200) + res:send_header("Connection", "close") + if req.url == "/toggle" then + toggle_light() + elseif req.url == "/on" then + turn_light_on() + elseif req.url == "/off" then + turn_light_off() + end + + res:send("The light is " .. (light_on and "on\n" or "off\n")) + res:finish() + end + end + end) + + end +end + +-- Define WiFi station event callbacks +wifi_connect_event = function(T) + print("Connection to AP("..T.SSID..") established!") + print("Waiting for IP address...") + if disconnect_ct ~= nil then disconnect_ct = nil end +end + +wifi_got_ip_event = function(T) + -- Note: Having an IP address does not mean there is internet access! + -- Internet connectivity can be determined with net.dns.resolve(). + print("Wifi connection is ready! IP address is: "..T.IP) + print("Startup will resume momentarily, you have 3 seconds to abort.") + print("Waiting...") + tmr.create():alarm(3000, tmr.ALARM_SINGLE, startup) +end + +wifi_disconnect_event = function(T) + if T.reason == wifi.eventmon.reason.ASSOC_LEAVE then + --the station has disassociated from a previously connected AP + return + end + -- total_tries: how many times the station will attempt to connect to the AP. Should consider AP reboot duration. + local total_tries = 75 + print("\nWiFi connection to AP("..T.SSID..") has failed!") + + --There are many possible disconnect reasons, the following iterates through + --the list and returns the string corresponding to the disconnect reason. + for key,val in pairs(wifi.eventmon.reason) do + if val == T.reason then + print("Disconnect reason: "..val.."("..key..")") + break + end + end + + if disconnect_ct == nil then + disconnect_ct = 1 + else + disconnect_ct = disconnect_ct + 1 + end + if disconnect_ct < total_tries then + print("Retrying connection...(attempt "..(disconnect_ct+1).." of "..total_tries..")") + else + wifi.sta.disconnect() + print("Aborting connection to AP!") + disconnect_ct = nil + end +end + +-- Register WiFi Station event callbacks +wifi.eventmon.register(wifi.eventmon.STA_CONNECTED, wifi_connect_event) +wifi.eventmon.register(wifi.eventmon.STA_GOT_IP, wifi_got_ip_event) +wifi.eventmon.register(wifi.eventmon.STA_DISCONNECTED, wifi_disconnect_event) + +print("Connecting to WiFi access point...") +wifi.setmode(wifi.STATION) +wifi.sta.config({ssid=SSID, pwd=PASSWORD}) +-- wifi.sta.connect() not necessary because config() uses auto-connect=true by default diff --git a/nodemcu-release-16-modules-2021-10-09-22-33-53-float.bin b/nodemcu-release-16-modules-2021-10-09-22-33-53-float.bin new file mode 100644 index 0000000..9fd21e1 Binary files /dev/null and b/nodemcu-release-16-modules-2021-10-09-22-33-53-float.bin differ diff --git a/server.lua b/server.lua new file mode 100644 index 0000000..ca2d9d1 --- /dev/null +++ b/server.lua @@ -0,0 +1,56 @@ +temperature = "0" + +require("httpserver").createServer(80, function(req, res) + -- analyse method and url + print("+R", req.method, req.url, node.heap()) + -- setup handler of headers, if any + req.onheader = function(self, name, value) -- luacheck: ignore + print("+H", name, value) + -- E.g. look for "content-type" header, + -- setup body parser to particular format + -- if name == "content-type" then + -- if value == "application/json" then + -- req.ondata = function(self, chunk) ... end + -- elseif value == "application/x-www-form-urlencoded" then + -- req.ondata = function(self, chunk) ... end + -- end + -- end + end + -- setup handler of body, if any + req.ondata = function(self, chunk) -- luacheck: ignore + print("+B", chunk and #chunk, node.heap()) + --if not chunk then + -- reply + res:send(nil, 200) + -- res:send_header("Connection", "close") + -- res:send("Hello, world!\n") + --res:finish("yolo") + --end + + res:finish(temperature .. "\n") + end + -- or just do something not waiting till body (if any) comes + --res:finish("Hello, world!") + --res:finish("Salut, monde!") +end) + +print("Starting up") + +for k, v in pairs(softuart) do + print(tostring(k) .. ", " .. tostring(v)) +end + +if s then + print("Configuring callback") + s:on("data", "\n", + function(data) + stripped = string.gsub(data, '%s+', '') + print("Temperature = " .. stripped) + s:write("Sending back to cp: \n") + print("Wrote stuff back") + temperature = stripped + if data == "quit" then + s:on("data") -- unregister callback function + end + end, 0) +end