These are the plans, bill of materials, and firmware necessary to build your own HVAC controller. Please be aware that I am a self-taught hobbyist in electronics and HVAC and I do not warrant my designs and implementation fit for any particular application. There are likely design flaws and software bugs lurking herein. You take full responsibility for hacking your air conditioner and playing with electricity.
Bill of materials: bom.csv
Controller firmware: etdey/hvac-controller
Although the controllers are designed to run autonomously without the need for control inputs from a central server, it is useful to transmit operational statistics to a time-series database for visualization and analysis. For my setup, I chose InfluxDB and Grafana for my database and analysis tool.
The central server has an XBee radio module attached via USB and it runs a Python program that receives these status packets from the HVAC controllers at 60 second intervals. This code is responsible for updating the InfluxDB time-series database.
typedef struct { uint8_t packetType; uint8_t versionMajor; uint8_t versionMinor; uint8_t sequenceNumber; unsigned long uptime; uint8_t systemState; uint8_t thermostatState; uint8_t currentState; uint8_t targetState; unsigned long timer_fanOn; unsigned long timer_fanOff; unsigned long timer_coolOn; unsigned long timer_coolOff; unsigned long timer_heatOn; unsigned long timer_heatOff; unsigned long accum_fanOn; unsigned long accum_fanOff; unsigned long accum_coolOn; unsigned long accum_coolOff; unsigned long accum_heatOn; unsigned long accum_heatOff; char nodeName[21]; } XBeeStatusPayload;
Data Packet Payload
packetType
is always 1. There is only one type of packet currently.
versionMajor
and versionMinor
contain the firmware version information.
sequenceNumber
increments by one for every transmitted packet and rolls over from 255 to 0.
uptime
is how many seconds the controller has been running since last reset. Internally, the controller tracks time as a 64-bit number where the first 32-bits count seconds and the other 32-bits count nanoseconds -- the fractional part of the next whole second.
systemState
is a bitmap of the current control lines that the HVAC unit is receiving.
thermostatState
is a bitmap of the current control lines that the thermostat is sending.
currentState
is an enum of the current FSM state.
targetState
is an enum of the desired FSM state once minimum time values and other constraints have been satisfied.
timer_*
values record the total seconds that the controller has been continuously asserting a control function. For example, if the fan has been on for 15 minutes, timer_fanOn=900
and timer_fanOff=0
.
accum_*
values record the total seconds during the interval since the last report that a control function was asserted. For example, if the fan was on for the first 40 seconds of the reporting period and then turned off, accum_fanOn=40
, accum_fanOff=20
, timer_fanOn=0
, and timer_fanOff=20
in the status report.
These are notable design and implementation lessons learned from this project.
double
may seem to be an easy way to track fractional accumulators such as elapsed time driven by a 100Hz interrupt, they will drive you crazy because the floating point representation will produce unacceptable artifacts due to error propagation.accum_fanOn + accum_fanOff == 61
for a one minute measurement interval.COOL
function was being asserted continuously. The K2 relay was occasionally sticking in a closed state after the coil voltage was removed. While I added R20, R21, and R22 as insurance against a stray voltage holding the relay closed, the ultimate problem was that I had a defective relay. In a future improvement, I would include the ability for the MCU to monitor the actual state of the output lines so that it could detect a mismatch between the desired and actual states.These are possible improvements to be made in the future or on the next versions of the controller board.
Previous: Building the controller
Copyright © 2015 by Eric Dey. All Rights Reserved.