As we rambled on about previously, we’re pretty into the idea of a wireless, networked temperature controller. An industrial temperature controller with, you know, modern features like monitoring over the local network and/or the web. So we built a prototype, and recently an even better prototype. See here for construction:
Here, we take it for a spin in a pretty popular application: the home thermostat. Once again, the 10,000 foot view looks like this:
In the link above, we detail what goes into the box. Just to review, what we have is the following:
- IO0 : Isolated relay output, open-drain output, PWM output, digital output
- IO1 -4 : Open-drain output, digital output, analog input, optional hardware pullup, optional hardware voltage divider to input
Internally, these IO map according to our MEGA map (full map here):
- IO0 : io9, pin 14
- IO1-4 : io21-24, pins 28-31
Here, we are going to use a waterproof 1Wire temperature sensor, the DS18B20. We’ll put it on IO4, and use a resistor pullup on the data line, as specified in the datasheet. The next board revision has available locations for though-hole resistor pullups, but for this guy we’ve just hand-soldered one into place.
Our furnace run signal is the closure of a contact. One side is a pretty typical 24VAC. Because we don’t want to mix signals, we use the contact of a mechanical relay installed on the board. This allows the controller to control more or less anything without worry for things like noise, overvoltage, etc.
- 4-digit 7-segment with dot point LED display via MAX7221 (previously discussed with code here)
- 12-bar bicolor display (red, green, red+green = yellow) display via 74HC595 shift registers) (previously discussed with code here)
- Rotary encoder with button (right, left increment with no stop, button knob press) (previously discussed with code here)
First, we need to tell our controller what to read and what to do with it. We do this using the standard command set at controller setup, as detailed previously in our UniMote write-up. Configuration is as simple as issuing our standard command set over serial or RF. If, for example if we wanted to set the first pin as an analog input, which we might do for an RTD with one of the built-in voltage dividers:
Set mode to analog input:
Set the read frequency:
Enable reporting (over RF):
Set report frequency (default is 0, meaning report will happen each time input is read):
For this controller, however, we are going to use a 1Wire DS18B20 temperature sensor, so our configuration for our input is as follows. Because 1Wire inputs are handled a bit differently (see here for details), they will automatically report each time they are read, using the tag ‘owrom’ instead of the ‘ioval’ used for general IO reporting. If IO reporting is enabled (as above for analog), it will also report using the ‘ioval’ message tag, as the 1Wire value is stored in the iovalues array on read, but we don’t need to double-report. This will probably change eventually, but for now it is what it is.
~modparams;iomode;24;4 ~modparams;ioenabled;24;1 ~modparams;ioreadfreq;24;15
Next we configure our output. We could enable reporting for this as well (to tell us when the furnace is on), but for now we’ll not.
And finally we configure and enable our control channel, setting the positive feedback to the output above.
We’ll configure the remainder of the channel parameters, such as deadband and delay time, from the interface.
The way the interface works is pretty simple and described by the menu tree below under Controls and Menu Tree. By default, the display shows the current temperature on the LED numerical display and the bar graph shows where the temperature is relative to the setpoint.
Bar location: temperature relative to setpoint
In the bar display, the center is the setpoint. The current temperature is displayed relative to the setpoint on the bar display. Each bar is a unit, 1F in Fahrenheit mode or 1C in Celsius mode. If the temperature is within one temperature unit of the setpoint, two bars in the center will be displayed. If the temperature is two below the setpoint, the bar will be two to the left of center, if it is three above, it will be three to the right of center … you get the picture. If it is beyond the numerical limit of the scale (6), it will show yellow at the end of the scale.
Bar color: controller action
If the controller is taking action, based on setpoint and deadband, the color will turn to red. If no action is being taken or pending (see below), the color will remain green.
Bar flashing or solid: controller action is active or pending
When the setpoint is outside of the currently measured value + deadband, the controller will attempt to take action. This will change the color of the bar display element. If the delay is > 0, however, the controller will wait until the delay time has expired before acting. During this time, the bar display will flash to indicate the action is pending.
Controls and Menu Tree
The menu is currently described by the tree below. Each movement vertically is controlled by a button push; horizontal is controlled by turning the rotary encoder knob.
Remote Data View/Display
As we detailed previously, the data is sent out on RF in json format, which our gateway RF Mote picks up and passes on to our gateway, a raspberry pi. The serial handler, which you can see here, handles a few different types of messages and puts them where they belong as inputs in our databases.
So magically, we open up our gateway and take a look and see our mote shows up on our motes table (this happens with no configuration required):
We add an interface from the UI for the mote, which has a NODEID of 15 (as you can see from the entry above). What this does is tell the updateio script that if there is a mote present in the motes table with a NODEID of 15, to insert it into our Inputs table.
And, as promised, our Mote temperature value shows up as an input:
We can edit it to give it a friendly name by expanding the entry and editing, which goes into a metadata table called ioinfo.
We don’t have a direct UI edit page for the ioinfo table yet, but it’s on the way. In the meantime, we edit it using phpliteadmin, and also add to the options field the specification “formula:1.8*x+32”. This keyword will tell our IO updater to parse the input using the formula above (in python’s interpreter). In this case, we take the value in Celsius reported by the temperature controller and translate it to Fahrenheit for the UI. Easy peasy. Now let’s take a look at our data accumulating in our Dataviewer (this is some time later, temperature spikes from handling the temperature sensor):
Set up alerts
Just for fun, let’s set up an alert so that when the temperature gets above or below a couple setpoints, we get an email alert. The current actions/notifications/indicators system is set up to handle arithmetic comparisons on Channel values. A Channel is the basic control structure for our CuPID Controller. It has an input, positive and negative feedback outputs, logging preferences, and an assortment of other control parameters. Although it’s only currently set up for simple on/off control (with deadband and delay), we’ve built in provisions for multiple feedback modes. It’s what we use for our reflow oven: set a TC input, set a GPIO output to a relay module, plug in a recipe, and it will control an action. If you don’t set any outputs (or disable them), it just acts as a data container, which in this case will allow us to set up alerts. In the future, it will allow remote setpoint and control parameter commands to be entered from the web UI (see Next Time below).
So let’s add one:
Then we specify that our Mote is the control input, and we’re done:
Next we create two alerts. One for Too Hot, and one for Too Cold. We could do other things like trigger on channel outputs active, which would take into account whatever control algorithm we’re using, or a boolean compare for if a channel is enabled (in case somebody accidentally disables it), or really do comparison on any value in the channels table (and eventually ANY table in the database).
For now, we configure the Actions, one will be less than, one greater than, both using the Control Value of Channel 1 (which we set above to be the reported Mote Temperature). There are a ton of other parameters you can set here, which are actually detailed in the Parameters expandable table item. Lots of flexibility.
To test our alert, we set the Too Cold Alert to have a temperature of 70, meaning that we should get an email if it drops below 70 for the specified OnDelay time. Sure enough, I get an email:
And after we change the temperature target back to a more reasonable 60F, we get an email letting us know our Alert has turned off. This email, called “Active Reset”, is optional.
Remote setpoint by Web GUI:
We already have all the tools necessary to enable control from our gateway. We can send commands over RF in the same format we used above, and with this we can change setpoint, control parameters, you name it. Next time, we’ll add in the control library infrastructure to send remote commands in an object-oriented fashion. We’ll need to do some thinking about data concurrency and race conditions, especially considering we don’t have an RTC on our thermostat.
Finally, we’ll add some shiny widgets to our UI to control all of it. That will be the easy part.
Alert on All the Things
As shown above, we have only partially fleshed out the Actions feature. We need to be able to alert on everything. Why not? We also want to be able to add more complex logic. Like … if one of five rooms is cold, turn on the heat. This will likely entail creating some auxiliary variables. We’ll have a think on that.
The above makes use of the open source libraries available on github, including sketches, python and all web libraries:
Explanation and installation here: