From 8bdcde9efb2960abcd08113a788a3262edc952f7 Mon Sep 17 00:00:00 2001 From: Nathaniel Wesley Filardo Date: Sun, 22 Jan 2017 20:00:14 -0500 Subject: [PATCH] Tweak ctfws display device example --- README.rst | 50 ++++++++++++++++--- ctfws-lcd.lua | 34 ++++++++++--- init2.lua | 130 ++++++-------------------------------------------- init3.lua | 120 ++++++++++++++++++++++++++++++++++++++++++++++ pushall.sh | 7 +-- 5 files changed, 209 insertions(+), 132 deletions(-) create mode 100644 init3.lua diff --git a/README.rst b/README.rst index e4a3755..d58be22 100644 --- a/README.rst +++ b/README.rst @@ -18,16 +18,25 @@ of parsing. Centrally-set topics: * ``ctfws/game/config`` the string ``none`` or a whitespace-separated text field: + * ``starttime`` -- NTP seconds indicating start state + * ``setupduration`` -- setup duration, in seconds + * ``rounds`` -- number of rounds + * ``roundduration`` -- seconds per round + * ``nflags`` -- number of flags per team + * any additional fields are to be ignored. * ``ctfws/game/flags`` -- whitespace-separated text field: + * ``red`` -- red team flag capture count (int) + * ``yel`` -- yellow team flag capture count (int) + * any additional fields are to be ignored. * ``ctfws/game/endtime`` -- a single number, denoting NTP seconds of a @@ -57,6 +66,7 @@ guest account for a hypothetical CtFwS app. Device-set topics: * ``ctfws/dev/$DEVICENAME/beat`` + * one of ``alive``, ``beat``, or ``dead`` (LWT; no further fields) * ``time`` (UNIX time, from local clock) * ``ap`` (MAC addr) @@ -93,20 +103,20 @@ To send MQTT messages, try variants of these. Note that in all cases, we set messages persistent so that devices that (re)connect mid-way into a game get the latest messages automatically. - * To start a game:: +* To start a game:: mosquitto_pub -h $MQTT_SERVER -u ctfwsmaster -P asdf -q 1 -t ctfws/game/flags -r -m '0 0' mosquitto_pub -h $MQTT_SERVER -u ctfwsmaster -P asdf -q 1 -t ctfws/game/config -r -m `date +%s`' 900 3 900 10' - * To post information:: +* To post information:: mosquitto_pub -h $MQTT_SERVER -u ctfwsmaster -P asdf -q 1 -t ctfws/game/flags -r -m '1 2' mosquitto_pub -h $MQTT_SERVER -u ctfwsmaster -P asdf -q 1 -t ctfws/game/message -r -m 'Red team captured a flag!' - * To end a game:: +* To end a game:: mosquitto_pub -h $MQTT_SERVER -u ctfwsmaster -P asdf -q 1 -t ctfws/game/endtime -r -m `date +%s` - + Jail Glyph Timers ################# @@ -136,6 +146,34 @@ AP they're associated with. The device should otherwise function more or less as a glorified stopwatch under centralized control. +NodeMCU modules used +==================== + +Please ensure that your build of NodeMCU supports the following modules: + +* ``bit`` (for LCD) +* ``cjson`` +* ``cron`` +* ``file`` +* ``i2c`` (for LCD) +* ``mqtt`` +* ``net`` +* ``node`` +* ``rtctime`` +* ``sntp`` +* ``tmr`` +* ``wifi`` + +Additionally, + +* ``mDNS`` may be a good idea, too, if you want to talk to your device over, + e.g. telnet, and want it to have a somewhat friendly name. + +* ``rtcmem`` may be useful if you wish to stash a little bit of state + frequently and don't want to write to flash. + +* ``uart`` is in most default builds but is not necessary, if you need space. + BOM === @@ -165,7 +203,7 @@ Setup time display:: 0 1 01234567890123456789 SETUP : MM:SS.s - NN⚑: R=0 Y=0 + NN⚑: R=0 Y=0 messagemessagemessag START IN : MM:SS.s @@ -174,7 +212,7 @@ Steady state display:: 0 1 01234567890123456789 ROUND r/R : MM:SS.s - NN⚑: R=NN Y=NN + NN⚑: R=NN Y=NN messagemessagemessag JAILBREAK : MM:SS.s diff --git a/ctfws-lcd.lua b/ctfws-lcd.lua index 1a40e3b..b23ce3c 100644 --- a/ctfws-lcd.lua +++ b/ctfws-lcd.lua @@ -68,18 +68,34 @@ local function drawSteadyBotLine(self,rix,maxt,rem) drawDS(lcd,3,13,maxt,self.dl_remain ,rem); self.dl_remain = rem end +local function attention(self) + if self.attnState then return end + + local tq = self.tq + local lcd = self.lcd + + local function doBlink() + if self.attnState <= 0 then self.attnState = nil ; return end + self.attnState = self.attnState - 1 + lcd:light(false) + tq:queue(250, function() lcd:light(true); tq:queue(500, doBlink) end) + end + + self.attnState = 2 + tq:queue(250, doBlink) +end + -- returns true if timers should keep going or false if we should wait for -- the next message or event local function drawTimes(self) local ctfws = self.ctfws local rix, maxt, ela = ctfws:times(rtctime.get) if rix == nil then - -- XXX beep to get attention drawNoGame(self.lcd, maxt) return false end if rix ~= self.dl_round then - if self.dl_round ~= nil then end -- XXX beep when not forcibly reset + if self.dl_round ~= nil then attention(self) end -- XXX beep when not forcibly reset self.dl_round = rix self.dl_elapsed = nil -- force redraws of times on round boundaries self.dl_remain = nil @@ -92,7 +108,9 @@ end local function drawFlags(self) local lcd = self.lcd local ctfws = self.ctfws - lcd:put(lcd:locate(1,0)," ") + if ctfws.flagsN then -- try not to blank a flagsmessage unless we have reason + lcd:put(lcd:locate(1,0)," ") + end if ctfws.startT then local str = string.format("%d\000: R=%d Y=%d", ctfws.flagsN, ctfws.flagsR, ctfws.flagsY) @@ -144,10 +162,12 @@ return function(ctfws, lcd, tq, t) self.tq = tq self.mtmr = t - self.reset = reset - self.drawTimes = drawTimes - self.drawFlags = drawFlags - self.drawMessage = drawMessage + self.attnState = nil + + self.reset = reset + self.drawTimes = drawTimes + self.drawFlags = drawFlags + self.drawMessage = drawMessage self.drawFlagsMessage = drawFlagsMessage -- load custom flag glyph diff --git a/init2.lua b/init2.lua index 0afe3f7..52e8d62 100644 --- a/init2.lua +++ b/init2.lua @@ -1,122 +1,20 @@ --- common module initialization -cron.schedule("*/5 * * * *", function(e) dofile("nwfnet-sntp.lc").dosntp(nil) end) -nwfnet = require "nwfnet" +-- It's early in boot, so we have plenty of RAM. Compile +-- the rest of the firmware from source if it's there. +local k,v +for k,v in pairs(file.list()) do + local ix, _ = k:find("^.*%.lua$") + if ix and k ~= "init.lua" then + print("early compile",k) + node.compile(k); file.remove(k) + end +end -tq = (dofile "tq.lc")(tmr.create()) -- Hardware initialization -i2c.setup(0,2,1,i2c.SLOW) -- init i2c as per silk screen (GPIO4, GPIO5) +i2c.setup(0,2,1,i2c.SLOW) -- init i2c on GPIO4 and GPIO5 lcd = dofile("lcd1602.lc")(0x27) --- Game logic modules -ctfws = dofile("ctfws.lc")() -ctfws:setFlags(0,0) - -msg_tmr = tmr.create() -ctfws_lcd = dofile("ctfws-lcd.lc")(ctfws, lcd, tq, msg_tmr) -ctfws_tmr = tmr.create() - --- Draw the default display -ctfws_lcd:drawTimes() -ctfws_lcd:drawFlagsMessage("BOOT...") - --- MQTT plumbing - -mqc, mqttUser = dofile("nwfmqtt.lc").mkclient("nwfmqtt.conf") -local mqttBootTopic = string.format("ctfws/dev/%s/beat",mqttUser) -mqc:lwt(mqttBootTopic,"dead",1,1) - --- This is not, properly speaking, OK, but it's so convenient -ctfws_lcd:drawMessage(string.format("I am: %s", mqttUser)) - -local myBSSID = "00:00:00:00:00:00" - -local mqtt_reconn_cronentry -local function mqtt_reconn() - mqtt_reconn_cronentry = cron.schedule("* * * * *", function(e) - mqc:close(); dofile("nwfmqtt.lc").connect(mqc,"nwfmqtt.conf") - end) - dofile("nwfmqtt.lc").connect(mqc,"nwfmqtt.conf") -end - -local mqtt_beat_cronentry -local function mqtt_beat() - mqtt_beat_cronentry = cron.schedule("*/5 * * * *", function(e) - mqc:publish(mqttBootTopic,string.format("beat %d %s",rtctime.get(),myBSSID),1,1) - end) -end - -local function ctfws_lcd_draw_all() - ctfws_lcd:reset() - ctfws_lcd:drawFlags() - ctfws_lcd:drawTimes() -end - -local function ctfws_start_tmr() - ctfws_tmr:alarm(100,tmr.ALARM_AUTO,function() - if not ctfws_lcd:drawTimes() then ctfws_tmr:unregister() end - end) -end - -nwfnet.onmqtt["init"] = function(c,t,m) - if t == "ctfws/game/config" then - ctfws_tmr:unregister() - if not m or m == "none" - then ctfws:deconfig() - else local st, sd, nr, rd, nf = m:match("^%s*(%d+)%s+(%d+)%s+(%d+)%s+(%d+)%s+(%d+).*$") - if st == nil - then ctfws:deconfig() - else -- the game's afoot! - ctfws:config(tonumber(st), tonumber(sd), tonumber(nr), - tonumber(rd), tonumber(nf)) - ctfws_start_tmr() - end - end - ctfws_lcd_draw_all() - elseif t == "ctfws/game/endtime" then - ctfws:setEndTime(tonumber(m)) - ctfws_lcd_draw_all() - ctfws_start_tmr() -- might have been unset; restart display if so - elseif t == "ctfws/game/flags" then - if not m then ctfws:setFlags(0,0); return end - local fr, fy = m:match("^%s*(%d+)%s+(%d+).*$") - if fr ~= nil then - ctfws:setFlags(tonumber(fr),tonumber(fy)) - ctfws_lcd:drawFlags() - end - elseif t:match("^ctfws/game/message") then - ctfws_lcd:drawMessage(m) - end -end -nwfnet.onnet["init"] = function(e,c) - if e == "mqttdscn" and c == mqc then - if mqtt_beat_cronentry then mqtt_beat_cronentry:unschedule() mqtt_beat_cronentry = nil end - if not mqtt_reconn_cronentry then mqtt_reconn() end - ctfws_lcd:drawFlagsMessage("MQTT Disconnected") - elseif e == "mqttconn" and c == mqc then - if mqtt_reconn_cronentry then mqtt_reconn_cronentry:unschedule() mqtt_reconn_cronentry = nil end - if not mqtt_beat_cronentry then mqtt_beat() end - mqc:publish(mqttBootTopic,"alive",1,1) - mqc:subscribe("ctfws/game/config",1) - mqc:subscribe("ctfws/game/endtime",1) - mqc:subscribe("ctfws/game/flags",1) - mqc:subscribe("ctfws/game/message",1) -- broadcast messages - mqc:subscribe("ctfws/game/message/jail",1) -- jail-specific messages - ctfws_lcd:drawFlagsMessage("MQTT CONNECTED") - elseif e == "wstagoip" then - if not mqtt_reconn_cronentry then mqtt_reconn() end - ctfws_lcd:drawFlagsMessage(string.format("DHCP %s",c.IP)) - elseif e == "wstaconn" then - myBSSID = c.BSSID - ctfws_lcd:drawFlagsMessage(string.format("WIFI %s",c.SSID)) - elseif e == "sntpsync" then - -- If we have a game configuration and just got SNTP sync, it might - -- be that we just lept far into the future, so go ahead and start - -- the game! - if ctfws.startT then ctfws_start_tmr() end - end -end +tq = (dofile "tq.lc")(tmr.create()) -ctfws_lcd:drawFlagsMessage("CONNECTING...") -dofile("nwfnet-diag.lc")(true) -dofile("nwfnet-go.lc") +-- give the LCD time to initialize properly +tq:queue(125, function() dofile("init3.lc") end) diff --git a/init3.lua b/init3.lua new file mode 100644 index 0000000..e1d5318 --- /dev/null +++ b/init3.lua @@ -0,0 +1,120 @@ +-- common module initialization +cron.schedule("*/5 * * * *", function(e) dofile("nwfnet-sntp.lc").dosntp(nil) end) +nwfnet = require "nwfnet" + +-- Game logic modules +ctfws = dofile("ctfws.lc")() +ctfws:setFlags(0,0) + +msg_tmr = tmr.create() +ctfws_lcd = dofile("ctfws-lcd.lc")(ctfws, lcd, tq, msg_tmr) +ctfws_tmr = tmr.create() + +-- Draw the default display +ctfws_lcd:drawTimes() +ctfws_lcd:drawFlagsMessage("BOOT...") + +-- MQTT plumbing +mqc, mqttUser = dofile("nwfmqtt.lc").mkclient("nwfmqtt.conf") +local mqttBootTopic = string.format("ctfws/dev/%s/beat",mqttUser) +mqc:lwt(mqttBootTopic,"dead",1,1) + +-- This is not, properly speaking, OK, but it's so convenient +ctfws_lcd:drawMessage(string.format("I am: %s", mqttUser)) + +local myBSSID = "00:00:00:00:00:00" + +local mqtt_reconn_cronentry +local function mqtt_reconn() + mqtt_reconn_cronentry = cron.schedule("* * * * *", function(e) + mqc:close(); dofile("nwfmqtt.lc").connect(mqc,"nwfmqtt.conf") + end) + dofile("nwfmqtt.lc").connect(mqc,"nwfmqtt.conf") +end + +local mqtt_beat_cronentry +local function mqtt_beat() + mqtt_beat_cronentry = cron.schedule("*/5 * * * *", function(e) + mqc:publish(mqttBootTopic,string.format("beat %d %s",rtctime.get(),myBSSID),1,1) + end) +end + +local function ctfws_lcd_draw_all() + ctfws_lcd:reset() + ctfws_lcd:drawFlags() + ctfws_lcd:drawTimes() +end + +local function ctfws_start_tmr() + ctfws_tmr:alarm(100,tmr.ALARM_AUTO,function() + if not ctfws_lcd:drawTimes() then ctfws_tmr:unregister() end + end) +end + +nwfnet.onmqtt["init"] = function(c,t,m) + if t == "ctfws/game/config" then + ctfws_tmr:unregister() + if not m or m == "none" + then ctfws:deconfig() + ctfws_lcd_draw_all() + else local st, sd, nr, rd, nf = m:match("^%s*(%d+)%s+(%d+)%s+(%d+)%s+(%d+)%s+(%d+).*$") + if st == nil + then ctfws:deconfig() + else -- the game's afoot! + ctfws:config(tonumber(st), tonumber(sd), tonumber(nr), + tonumber(rd), tonumber(nf)) + ctfws_start_tmr() + end + ctfws_lcd_draw_all() + end + elseif t == "ctfws/game/endtime" then + ctfws:setEndTime(tonumber(m)) + ctfws_lcd_draw_all() + ctfws_start_tmr() -- might have been unset; restart display if so + elseif t == "ctfws/game/flags" then + if not m then ctfws:setFlags(0,0); return end + local fr, fy = m:match("^%s*(%d+)%s+(%d+).*$") + if fr ~= nil then + ctfws:setFlags(tonumber(fr),tonumber(fy)) + ctfws_lcd:drawFlags() + end + elseif t:match("^ctfws/game/message") then + ctfws_lcd:drawMessage(m) + end +end + +-- network callbacks + +nwfnet.onnet["init"] = function(e,c) + if e == "mqttdscn" and c == mqc then + if mqtt_beat_cronentry then mqtt_beat_cronentry:unschedule() mqtt_beat_cronentry = nil end + if not mqtt_reconn_cronentry then mqtt_reconn() end + ctfws_lcd:drawFlagsMessage("MQTT Disconnected") + elseif e == "mqttconn" and c == mqc then + if mqtt_reconn_cronentry then mqtt_reconn_cronentry:unschedule() mqtt_reconn_cronentry = nil end + if not mqtt_beat_cronentry then mqtt_beat() end + mqc:publish(mqttBootTopic,"alive",1,1) + mqc:subscribe("ctfws/game/config",1) + mqc:subscribe("ctfws/game/endtime",1) + mqc:subscribe("ctfws/game/flags",1) + mqc:subscribe("ctfws/game/message",1) -- broadcast messages + mqc:subscribe("ctfws/game/message/jail",1) -- jail-specific messages + ctfws_lcd:drawFlagsMessage("MQTT CONNECTED") + elseif e == "wstagoip" then + if not mqtt_reconn_cronentry then mqtt_reconn() end + ctfws_lcd:drawFlagsMessage(string.format("DHCP %s",c.IP)) + elseif e == "wstaconn" then + myBSSID = c.BSSID + ctfws_lcd:drawFlagsMessage(string.format("WIFI %s",c.SSID)) + elseif e == "sntpsync" then + -- If we have a game configuration and just got SNTP sync, it might + -- be that we just lept far into the future, so go ahead and start + -- the game! + if ctfws.startT then ctfws_start_tmr() end + end +end + +-- hook us up to the network! +ctfws_lcd:drawFlagsMessage("CONNECTING...") +-- dofile("nwfnet-diag.lc")(true) +dofile("nwfnet-go.lc") diff --git a/pushall.sh b/pushall.sh index 81db084..aa2baa9 100755 --- a/pushall.sh +++ b/pushall.sh @@ -4,13 +4,14 @@ set -e -u . ./host/pushcommon.sh -#dopushcompile net/nwfmqtt.lua -#dopush examples/ctfws/conf/nwfnet.conf +dopushcompile net/nwfmqtt.lua +dopush examples/ctfws/conf/nwfnet.conf dopush examples/ctfws/conf/nwfnet.conf2 dopush examples/ctfws/conf/nwfmqtt.conf -#dopushcompile _external/dvv-nodemcu-thingies/lcd1602.lua +dopushcompile _external/lcd1602.lua dopushcompile examples/ctfws/ctfws.lua dopushcompile examples/ctfws/ctfws-lcd.lua +dopushcompile examples/ctfws/init3.lua dopushcompile examples/ctfws/init2.lua echo "SUCCESS" -- 2.50.1