aboutsummaryrefslogtreecommitdiff
path: root/diller
diff options
context:
space:
mode:
Diffstat (limited to 'diller')
-rw-r--r--diller/README.md103
-rw-r--r--diller/inter.lua73
-rw-r--r--diller/main.lua134
-rw-r--r--diller/mq.lua61
4 files changed, 371 insertions, 0 deletions
diff --git a/diller/README.md b/diller/README.md
new file mode 100644
index 0000000..322ec45
--- /dev/null
+++ b/diller/README.md
@@ -0,0 +1,103 @@
+# TODOs / Notes
+
+Is registration required? Can't the firmware just send an update without a value on every boot?
+
+# MQTT
+
+## Device / Sensor hierarchy
+
+ /<device id>
+ /sensors
+ /<sensor id>
+ /value
+ /type (retained)
+ /name (retained)
+ /description (retained)
+ /type TODO: remove?
+ /firmware For updating the device's firmware
+
+## Device registration flow
+
+ sub: /registration/<random id>
+ pub: /registration {}
+
+# Diller serial API
+
+## Network settings
+
+Request:
+
+Update network settings. If no paramters are given, no changes are done.
+
+ network [ip=..] [gateway=..] [ssid=..]
+
+Response:
+
+The command will always return the current values. If the ssid was recently set, the ip, gateway and netmask fields may be missing.
+
+ ok ip=.. gateway=.. netmask=.. ssid=..
+
+## Register property
+
+Request:
+
+ register-property id=.. type=.. name=.. description=..
+
+Type examples:
+
+* `temperature`
+* `switch` - boolean switches
+* `humidity`
+* `rtc`
+
+Response:
+
+ ok
+
+## Publish property value
+
+Request:
+
+ set-property id=.. [value=..] [name=..] [description=..]
+
+Response:
+
+ ok
+
+The value might not be updated directly, but may be buffered on the device if it is not yet connected.
+
+## Publish raw MQTT message
+
+Request:
+
+ publish topic=.. [qos=..] [retain=..]
+ <next lines are payload. Double EOL completes payload>
+
+Response:
+
+ ok
+
+## Reset the device
+
+Request:
+
+ reset
+
+Response
+
+ ok
+
+# Example session
+
+ require('main').main();
+
+Get the current network configuration to update the Arduino's LCD display
+
+ > network
+ < ok ip=1.3.3.7 netmask=255.255.255.0 gateway=1.3.3.1 ssid=awesome
+
+Register the properties. This is done on every boot to keep the server in sync with the firmware's features. Old properties will not be removed.
+
+ > set-property id=temp-0 value=12.3 type=temperature name=Water
+ < ok
+
diff --git a/diller/inter.lua b/diller/inter.lua
new file mode 100644
index 0000000..725edbd
--- /dev/null
+++ b/diller/inter.lua
@@ -0,0 +1,73 @@
+local P = {}
+
+function P.parse(line)
+ -- print("inter_parse: line="..line)
+
+ line = string.sub(line, 1, string.find(line, "\r"))
+ line = line:gsub("%s+", "")
+
+ local args = {}
+ local i = string.find(line, " ")
+ if not i then
+-- print("inter_parse: invalid line: no command, line="..line)
+ return string.len(line) > 0 and line or nil, args
+ end
+ local cmd = string.sub(line, 1, i - 1)
+ -- print("inter_parse: cmd="..cmd)
+
+ local last = i + 1
+ while i do
+ local key
+ local value
+
+ i = string.find(line, " ", last)
+ arg = string.sub(line, last, i)
+ arg = arg:gsub("%s+", "")
+
+ if #arg > 0 then
+-- print("inter_parse: arg: "..arg)
+ j = string.find(arg, "=")
+ if not j then
+ if #arg > 0 then
+ key = arg
+ args[key] = true
+-- print("inter_parse: key="..key.."=true")
+ end
+ else
+ key = string.sub(arg, 1, j-1)
+ value = string.sub(arg, j+1)
+ args[key] = value
+-- print("inter_parse: key="..key..", value="..value)
+ end
+ end
+
+ if not i then
+ break
+ end
+ last = i + 1
+ end
+
+ return cmd, args
+end
+
+local cb
+function inter_on_line(line)
+-- print("line:"..line)
+
+ local cmd, args = P.parse(line)
+ if not cmd then
+ return
+ end
+
+ if cb then
+ cb(cmd, args)
+ end
+end
+
+function P.init(callback)
+ cb = callback
+
+ uart.on("data", "\r", inter_on_line, 0)
+end
+
+return P
diff --git a/diller/main.lua b/diller/main.lua
new file mode 100644
index 0000000..112284c
--- /dev/null
+++ b/diller/main.lua
@@ -0,0 +1,134 @@
+function panic(reason)
+ print("PANIC: "..reason)
+ -- This will trigger a restart, but not immediately
+ node.restart()
+end
+
+local P = {}
+
+local inter = require('inter')
+local mq = require('mq')
+
+local function on_cmd(cmd, args)
+ if not cmd then
+ return
+ end
+
+ print("on_cmd: '"..cmd.."', #args="..tostring(table.getn(args)))
+ for k, v in pairs(args) do
+ print(k.."="..tostring(v))
+ end
+
+ if cmd == "reset" then
+ print("ok")
+ panic("Reset requested")
+ elseif cmd == "wlan" then
+ local ssid = args.ssid password = args.password
+
+ if args.ssid then
+ wifi.sta.config(args.ssid, args.password, 1)
+ end
+ ssid, password, bssid_set, bssid = wifi.sta.getconfig()
+ print("ok ssid="..(ssid or ''))
+ elseif cmd == "network" then
+ local ip = args.ip nm = args.netmask gw = args.gateway
+
+ if args.ip then
+ wifi.sta.setip({ip=ip, netmask=nm, gateway=gw})
+ end
+
+ ip, nm, gw = wifi.sta.getip()
+ print("ok ip="..(ip or '').." netmask="..(nm or '').." gateway="..(gw or ''))
+ elseif cmd == "set-property" then
+ local id = args.id
+ if not id then
+ print("fail status=missing-id")
+ else
+ if args.value then
+ mq.publish(args.id.."/value", args.value)
+ end
+ if args.name then
+ mq.publish(args.id.."/name", args.name)
+ end
+ if args.description then
+ mq.publish(args.id.."/description", args.description)
+ end
+ end
+ elseif cmd == "publish" then
+-- print("Publishing, topic="..tostring(cmd.topic)..", payload="..tostring(cmd.payload))
+ ok, msg = mq.publish(cmd.topic, cmd.payload)
+ if ok then
+ print("ok status="..msg)
+ else
+ print("failed status="..msg)
+ end
+ else
+ print("failed status=unknown-command")
+ end
+end
+
+local function read_cfg(name)
+ local filename = "cfg-"..name
+ if not file.open(filename, "r") then
+ panic("Could not read configuration file: "..filename)
+ end
+ local value = file.readline()
+ file.close()
+ if value == nil or #value == 0 then
+ panic("Empty configuration file: "..filename)
+ end
+ return string.sub(value, 1, -2)
+end
+
+local function print_status()
+ print("System Status")
+ print("Uptime : "..tmr.time())
+ print("Heap left: "..node.heap())
+ ip, nm, gw = wifi.sta.getip()
+ print("IP : "..tostring(ip))
+ print("Netmask : "..tostring(nm))
+ print("Gateway : "..tostring(gw))
+end
+
+-- uart.setup(id, baud, databits, parity, stopbits, echo)
+-- uart.setup(0, 115200, 8, 0, 1, 0)
+-- uart.setup(0, 9600, 8, 0, 1, 0)
+
+function P.main()
+ local timers = {
+ status = 0,
+ inter = 1,
+ mqtt = 2
+ }
+
+ local wlan_ssid = read_cfg("wlan-ssid")
+ local wlan_password = read_cfg("wlan-password")
+ print("Connecting to SSID: "..wlan_ssid)
+ wifi.setmode(wifi.STATION)
+ wifi.sta.config(wlan_ssid, wlan_password)
+
+ local client_id = "esp8266-"..wifi.sta.getmac()
+ mq.init(timers.mqtt, client_id)
+
+ inter.init(on_cmd)
+
+ tmr.alarm(timers.status, 10 * 1000, 1, print_status)
+
+-- local majorVer, minorVer, devVer, chipId, flashId, flashSize, flashMode, flashSpeed, buildDate = node.info()
+-- payload = '{"version": "'..majorVer..'.'..minorVer..'.'..devVer..'", "chipId":'..chipId..', "flashId":'..flashId..', "flashSize":'..flashSize..', "flashMode":'..flashMode..', "flashSpeed":'..flashSpeed
+--
+-- if buildDate then
+-- payload = payload..', "buildDate": "'..buildDate..'"'
+-- end
+--
+-- if node.info_versions then
+-- major, minor, dev, buildDate, sdkVersion = node.info_versions()
+-- payload = payload..'", versions": {"major": '..major..', "minor": '..minor..', "dev": '..dev..', "buildDate": "'..buildDate..'", "sdk": "'..sdkVersion..'"}'
+-- end
+-- payload = payload.."}"
+-- P.publish("firmware", payload)
+
+ print("init done")
+end
+
+return P
diff --git a/diller/mq.lua b/diller/mq.lua
new file mode 100644
index 0000000..0b4a884
--- /dev/null
+++ b/diller/mq.lua
@@ -0,0 +1,61 @@
+local P = {}
+local m, topic, cid
+
+local function p(msg)
+ print("MQTT: "..msg)
+end
+
+local function mq_client_connected(con)
+ p("connected")
+end
+
+function mq_on_timer()
+ -- This crashes the module:
+ -- local status = wifi.sta.status()
+ -- p("checking status, status="..status)
+
+ local status = wifi.sta.getip()
+
+ if status and status ~= "0.0.0.0" then
+ if not m then
+ p("connecting")
+
+ -- client id, keepalive, username, password
+ m = mqtt.Client(cid, 120)
+ -- m:on("connect", mq_connected)
+ m:on("offline", function() m:close(); m = nil end)
+
+ -- host, port, secure, auto_reconnect, function(client), ssl=8883
+ m:connect("trygvis.io", 1883, 0, 0, mq_client_connected)
+ end
+ else
+ if m then
+ p("Lost wifi connection, disconnecting")
+ m:close()
+ m = nil
+ end
+ end
+end
+
+function P.init(timer_id, client_id)
+ cid = client_id
+
+ topic = "/esp8266/"..client_id
+
+ tmr.alarm(timer_id, 3 * 1000, 1, mq_on_timer)
+end
+
+function P.publish(path, payload)
+ if not m then
+ print("Not connected, dropping message to "..path)
+ return
+ end
+
+ path = topic.."/"..path
+ print("path="..path)
+ m:publish(path, payload, 0, 0)
+
+ return true, "yo?"
+end
+
+return P