Skip to content

Commit

Permalink
Initial checkin
Browse files Browse the repository at this point in the history
  • Loading branch information
Nathaniel Wesley Filardo committed Aug 4, 2017
0 parents commit da0f530
Show file tree
Hide file tree
Showing 5 changed files with 434 additions and 0 deletions.
130 changes: 130 additions & 0 deletions README.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
Software
########

MQTT Topics
===========

See ``init3.lua`` and calls to ``logdata``, but in summary:

* Topics set by the device:

* ``.../boot`` -- heartbeat, boot announcement, and LWT.
* ``.../th`` -- temperature probe result
* ``.../zz`` -- temporary debug topic

* Topics set by the user/controller:

* ``.../fan`` -- ``on`` or ``1`` to force fan on, otherwise automatic.
* ``.../mode`` -- ``off``, ``cool``, ``heat``, or ``emht``
* ``.../target`` -- the target temperature, in half-degrees Celsius

.. note::

Temperatures are reported and consumed (in ``.../target``) in half
degrees Celsius. (Funky, ain't it?)

Control-side
============

I suggest a wrapper shell script, possibly named ``thermostat.sh`` or
something, along these lines, filling in the ``...`` appropriately::

#!/bin/bash
mosquitto_pub -h ... -u ... -P ... -t ".../$1" -m "$2" "${@:3}"

Then it's a matter of ``./thermostat.sh fan on``, ``./thermostat.sh mode
cool``, or ``./thermostat.sh target 50``.

Help! My broker's down! My network's down!
============================================

Don't panic! If your network is still online, you can telnet in to the
device. Failing that, the serial console is still viable (best grab the
pins with your own TTL adapter, as the nodemcu board has its own voltage
regulator and will attempt to power the thermostat's 3.3V rail from USB,
potentially fighting the other voltage regulator!). Failing that, just put
the original thermostat back, yeah?

In any case, you can simulate the receipt of a MQTT message from the Lua
interpreter prompt (or ``diag exec`` via ``telnetd``). Try one of these::

nwfnet.onmqtt["th"](mqc, mqttTargTopic, "60" )
nwfnet.onmqtt["th"](mqc, mqttModeTopic, "off")
nwfnet.onmqtt["th"](mqc, mqttFanTopic , "on" )

Hardware
########

Peripheral Setup
================

+------+----+-----------------------------------------------------------+
| GPIO | IX | |
+======+====+===========================================================+
| 16 | 0 | not used but somewhat special; "XPD" |
+------+----+-----------------------------------------------------------+
| 5 | 1 | 1-Wire |
+------+----+-----------------------------------------------------------+
| 4 | 2 | I2C SDA |
+------+----+-----------------------------------------------------------+
| 0 | 3 | I2C SCL / pull 0 for bootloader / bounce low to stop init |
+------+----+-----------------------------------------------------------+
| 2 | 4 | WS2812, by necessity of hardware |
+------+----+-----------------------------------------------------------+
| 14 | 5 | not used, but reserved for PCF IRQ |
+------+----+-----------------------------------------------------------+
| 12 | 6 | not used |
+------+----+-----------------------------------------------------------+
| 13 | 7 | not used |
+------+----+-----------------------------------------------------------+
| 15 | 8 | Pull low to select boot mode |
+------+----+-----------------------------------------------------------+

.. note::

* GPIO2 (ix 4) is also the onboard LED
* GPIOs 1,3 (ixes 9,10) are used for serial UART
* GPIOs 6-11 (incl. 9,10, ixes 11,12) are used in chatting with the flash chip

I2C Peripherals
---------------

We have a PCF8574A attached to us on the I2C bus at address 0x38. Its IO
lines are used as follows:

+----+-------------------+
| P0 | Relay 1: Fan |
+----+-------------------+
| P1 | Relay 2: AC |
+----+-------------------+
| P2 | Relay 3: Heat |
+----+-------------------+
| P3 | Relay 4: Em Heat |
+----+-------------------+
| P4 | |
+----+-------------------+
| P5 | |
+----+-------------------+
| P6 | |
+----+-------------------+
| P7 | |
+----+-------------------+

1W Peripherals
--------------

We have a DS1820 temperature probe attached to the 1Wire bus. This device
calls itself 1013878a02080098 in my case.

Internals
=========

RTC RAM Slots
-------------

* Slots 0 - 9 are used by the RTC itself
* Slots 10 - 20 are used by the RTC fifo for metadata
* Slots 21 - 31 are unused
* Slots 32 - 128 are used by the RTC fifo for its journal


14 changes: 14 additions & 0 deletions init2.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
-- It's early in boot, so we have plenty of RAM. Compile
-- the rest of the firmware from source if it's there.
dofile("compileall.lc")

-- telnetd overlay
tcpserv = net.createServer(net.TCP, 120)
tcpserv:listen(23,function(k)
local telnetd = dofile "telnetd.lc"
telnetd.on["conn"] = function(k) k(string.format("%s [NODE-%06X]",mqcu,node.chipid())) end
telnetd.server(k)
end)

print("Startup")
dofile("init3.lc")
63 changes: 63 additions & 0 deletions init3.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
-- local configuration
owpin = 1
local mqttHeartTopic = "lcn/therm/boot"
local mqttHeartTick = 600000
mqttTargTopic = "lcn/therm/target"
mqttModeTopic = "lcn/therm/mode"
mqttFanTopic = "lcn/therm/fan"
mqttPubRoot = "lcn/therm/"

-- modules
nwfnet = require "nwfnet"
mqc, mqcu = dofile("nwfmqtt.lc").mkclient("nwfmqtt.conf")

mqcCan = false

-- rtcfifo conditional init
if rtcfifo.ready() == 0 then rtcfifo.prepare() end

-- timers
tq = (dofile "tq.lc")(tmr.create())

-- setup peripherals
ow.setup(owpin)
i2c.setup(0,2,3,i2c.SLOW)

-- hook registry, MQTT connection management
local mqtt_beat_cancel
local mqtt_reconn_poller
local function mqtt_reconn()
mqtt_reconn_poller = tq:queue(30000,mqtt_reconn)
mqc:close(); dofile("nwfmqtt.lc").connect(mqc,"nwfmqtt.conf")
end

nwfnet.onnet["init"] = function(e,c)
if e == "mqttdscn" and c == mqc then
if mqtt_beat_cancel then mqtt_beat_cancel(); mqtt_beat_cancel = nil end
if not mqtt_reconn_poller then mqtt_reconn() end
mqcCan = false
elseif e == "mqttconn" and c == mqc then
if mqtt_reconn_poller then tq:dequeue(mqtt_reconn_poller); mqtt_reconn_poller = nil end
if not mqtt_beat_cancel then mqtt_beat_cancel = dofile("nwfmqtt.lc").heartbeat(mqc,mqttHeartTopic,tq,mqttHeartTick) end
mqc:publish(mqttHeartTopic,"alive",1,1)
mqc:subscribe(mqttTargTopic,1)
mqc:subscribe(mqttModeTopic,1)
mqc:subscribe(mqttFanTopic ,1)
mqcCan = true
elseif e == "wstagoip" then
if not mqtt_reconn_poller then mqtt_reconn() end
end
end

-- data logging
function logdata(v,e,n)
local t = rtctime.get()
if mqcCan then mqc:publish(mqttPubRoot..n,sjson.encode({ ['t']=t, ['v']=v, ['e']=e }),1,1) end
if v then rtcfifo.put(t,v,e,n) end
end

-- go online
dofile("nwfnet-go.lc")

-- do thermostat stuff
dofile("thermostat.lc")
53 changes: 53 additions & 0 deletions pushall.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
#!/bin/zsh

set -e -u

. ./core/host/pushcommon.sh

pushsrc() {
dopushcompile core/util/compileall.lua
dopushlua core/net/nwfmqtt.lua
dopushlua core/util/ow-ds18b20.lua
dopushlua core/util/i2cu.lua
dopushlua thermostat.lua
dopushlua init3.lua
dopushcompile init2.lua
}

if [ -n "${2:-}" ]; then
if [ -d $2 ]; then CONFDIR=$2
else echo "Not a directory: $2"; exit 1
fi
fi

pushconf() {
if [ -z "${CONFDIR:-}" ]; then
echo "Asked to push config without specifying?"
exit 1
fi
for f in ${CONFDIR}/*; do
dopushtext "$f"
done
}

case "${1:-}" in
all)
pushconf
pushsrc
./core/host/pushinit.sh
;;
both)
pushconf
pushsrc
;;
src)
pushsrc
;;
conf)
pushconf
;;
*)
echo "Please specify push mode: {conf,src,both,all}"
exit 1
;;
esac
Loading

0 comments on commit da0f530

Please sign in to comment.