Introduction: Log and Graph 24V Thermostat Events (Optocoupler + Raspberry Pi)

There are a lot of Instructables that show how to make a thermostat. But what if you already have a thermostat, and you want to log or monitor when it turns off and on? This Instructable shows how to use an optocoupler to obtain logic-level DC voltages from a common household programmable thermostat that connects to a 24 volt AC heating and cooling system. It also includes a temperature sensor, and code suitable for running on a Raspberry Pi to log and graph thermostat status, events, and temperatures.

WARNING: Working with electricity can be dangerous. Before continuing, be sure you have adequate understanding and skills to complete this project safely. While I've done my best to make it safe, I am not responsible if this project fries your thermostat, Raspberry Pi, HVAC system, your cat/dog, or burns down your house.

Step 1: Gather the Necessary Parts and Tools

You will need the following parts:

  • An HCPL3700 optocoupler
  • 2x 3.3 kOhm resistors, rated 1/2 watt or higher (the wattage rating is important)
  • A 10uF, minimum 10V capacitor (electrolytic is fine)
  • A 0.1 uF ceramic capacitor
  • An 8 kOhm resistor (or, more likely, a 4.7 kOhm and 3.3 kOhm resistor in series)
  • A Raspberry Pi
  • A length of two conductor, insulated wire, ideally with a quick disconnect of some kind
  • Basic electronic odds and ends, such as:
    • A breadboard
    • Jumper wires

If you also want to build the optional temperature sensor, you will also need:

  • An ADT7410 temperature sensor
  • An SOIC breakout board
  • Another 0.1 uF ceramic capacitor
  • 4x 10 kOhm resistors

If you want to design and manufacture your own circuit board, you will also need:

  • A two-terminal screw block
  • Optionally, female header pins to accept the SOIC breakout board
  • Male header pins to connect to a Pi, or female header pins to connect to an Arduino
  • Optionally, SMD versions of the resistors listed above to cut down on overall size

Required tools:

  • A multimeter

Other requirements:

  • A 24 VAC heating/cooling system

Software requirements:

  • Python
  • Perl (needed for HiPi, which is needed for hipi-i2c)
  • Plotly account (optional, for graphing data)
  • Eagle (free version, optional, for designing PCB)

Step 2: Figure Out How to Interface With Your Thermostat

Many household heating/cooling systems run at 24 volts AC. (Those that don't are outside the scope of this Instructable). Programmable thermostats usually either run off of batteries, or use some kind of "power-stealing" mechanism to power themselves. The actual switching is usually done by a relay inside the thermostat.

We want to monitor whether the heating/cooling system is running. In order to do this, we want to sense whether the relay is allowing power to flow or not. The first step is figuring out which wires to monitor. Since thermostat wiring conventions vary, this requires some experimentation.

Grab your multimeter, set it to measure AC voltage, and figure out which pair of wires reads 24 volts AC when your heating/cooling system is active. Make sure this same pair reads 0 volts AC when the system is inactive. Note that you might have multiple pairs that will work, depending on whether you have a fan that automatically runs, or other variables.

My thermostat uses five wires, attached to six contacts (one of the wires is jumpered to two contacts). This means that there are 10 possible two wire combinations to test, and we should test these combinations when the system is both on and off. It's probably helpful to write out the pairs of wires you're testing on a sheet of paper, then note the voltage (or lack thereof) as you go.

You can see in my case, the actual voltage is higher than the nominal 24 volts. When the heating is on, my multimeter reads nearly 29 volts across the pair of wires I've selected.

Step 3: Give Yourself Access to the Pair of Wires

Turn off power to your heating/cooling system, and use your multimeter to verify that the power is off. Remove the thermostat from its base, exposing the wiring. Add two additional wires connecting to the terminals of the pair of wires you chose in the previous step.

To make things tidy, it's nice to use wiring that terminates in some kind of quick-disconnect on the other end, so that the thermostat can be quickly and safely disconnected from the project when desired.

Step 4: Wire Up a Circuit to Measure the Voltage

This is perhaps the most complicated part of the process. Obviously we can't directly connect 24 volts AC to a Raspberry Pi - something needs to rectify and step down that voltage and do so safely.

We can use an optocoupler to accomplish this. An optocoupler electrically isolates two separate circuits. In our case, we want to isolate the 24 volt AC heating/cooling system from our Raspberry Pi.

I chose to use the HCPL3700 optocoupler because it includes a rectifier and can handle a wide range of voltages. Specifically, it takes either AC or DC as input, anywhere from 5V to 240V, and can run off of a supply ranging from 2V to 18V. The current requirements are small enough to run the device directly from the Raspberry Pi's 3.3V supply.

The included schematic shows how I wired up the HCPL3700 (you can ignore the bottom half of the schematic, which is the temperature sensor, for now). IMPORTANT: the two 3300 Ohm resistors connected to the AC input pins must be rated at least 1/2 watt. These two resistors set the trigger thresholds of the optocoupler, i.e., the input voltage at which it will turn on. For details about selecting these resistor values, see this application note.

The rectifier in the HCPL3700 rectifies the AC input, but does not smooth out the rectified sine wave. Thus, without any further input filtering, the logic output will rapidly oscillate, probably at the frequency of your line voltage (60 Hz in the U.S.). In order to avoid this, we place a capacitor across the DC pins of the rectifier. The application note has the details of how to calculate the value of this capacitor; a 10 uF, minimum 10V capacitor suffices.

Like many ICs, the HCPL3700 suggests placing a 0.1 uF capacitor across its supply voltage pins. Finally, the chip employs an open-collector output, which means it only drives its output low; in order to see logic-high outputs, we need a pull-up resistor. Calculating the appropriate value for this resistor is a bit of a challenge, since it depends on the characteristics of both the chip and the Pi's input pins, but I found that the standard 10k Ohm pull-up resistor could potentially not produce a high enough voltage to be read as a logic-high by the Pi. Thus, I went with an 8k Ohm resistor (actually a 3.3k Ohm and 4.7k Ohm in series). This calculation is based on worst-case scenarios, however; in practice, a 10k resistor might work fine.

That's it for the optocoupler - now when we connect our 24 (actually 29) volt AC source to the input pins of the chip (through the 1/2 watt resistors, of course!), the optocoupler will drive its output (pin 6) low. When we remove the source (or the source is off), the output will be pulled high. Note that this is inverted logic - a low level indicates the presence of the source voltage, and a high level indicates its absence.

I've included a schematic and a photo of my breadboard for reference. The optocoupler circuit is in the center of the breadboard.

Step 5: Wire Up a Circuit to Measure the Temperature (optional)

Of course, if you want to know if your heating/cooling system is actually responding to the thermostat events we're measuring, we need to measure temperature! There are lots of temperature sensor tutorials out there; many use the TMP36 or a similar device, which is an analog sensor. The TMP36 is incredibly easy to use, and usually requires no supporting components - however, because the Pi lacks an analog-to-digital converter, in this case you'll need an additional ADC chip, such as the MCP3008, to read values from it.

Instead of using the TMP36, I opted to use the ADT7410, which is significantly more accurate and produces digital output on an I2C interface, which can be read directly by the Pi. It also has other features, such as low-power modes and temperature thresholds that can trigger interrupts. The only downside is that it's only available as an SOIC-8 package, which can be tough to work with. I used an SOIC to DIP breakout board.

The circuit is simple - power, ground, four 10k Ohm pullup resistors, a 0.1 uF capacitor across the supply pins, and wires connecting the two I2C pins (SDA and SCL) to the Pi. The two address lines (A0 and A1) are connected to ground to give the device an I2C address of 0x48. The ADT7410 can operate at a range of voltages, and can be powered directly from the Pi's 3.3V supply.

The bottom half of the schematic shows the temperature sensor circuit.

Step 6: Connect to the Pi, and Write Some Code!

Connect your circuit(s) to the Pi - you'll need to use the Pi's 3.3V and GND for power supply to the circuit, one GPIO pin for the output of the optocoupler, and two I2C pins if you used the I2C temperature sensor.

Python code designed to work with these circuits can be found at my Github page. You'll need to install HiPi in order to get the hipi-i2c executable, which my code uses instead of the normal I2C kernel driver. Make sure to set appropriate configuration options in the conf file (most important are the Raspberry Pi board pin number and the interval between updates). The code itself must run as root (or otherwise with appropriate permissions to access the necessary devices), since it accesses hardware directly.

The code logs the thermostat status and temperature at intervals (default 5 minutes), writing the data to a file and uploading it to Plotly, a free online graphing service. Graphing can be disabled easily in the configuration file. In addition to interval logging, whenever a change in thermostat status is detected (e.g., the thermostat turns on/off its relay), an interrupt is generated, and a log entry is written (and new data appended to the graph).

This is the graph produced by my setup.

Step 7: Design a Manufacture a Printed Circuit Board (optional)

While the breadboard is fine for testing, if you want to make this a permanent project, you'll need to find a sturdier setup. I used Eagle to design a printed circuit board that mimics the breadboard circuit, then had it manufactured by OSH Park.

SparkFun has a great tutorial on how to use Eagle, and OSH Park produces reasonably priced, high quality printed circuit boards for as little as $10 including shipping. You also get three boards, so maybe find a friend who wants to do the same project! You'll have to do some searching to find the right components in the parts libraries, and might want to try manually routing traces rather than using the autorouter, but the end result looks pretty great.

Making a printed circuit board also makes it feasible to use surface-mount resistors, which cuts down on the size of the overall device considerably.

Lessons I learned from designing the board:

  • Manually routing can produce a nicer result
  • Including a ground plane can make routing easier
  • It would have been good to include mounting holes

Step 8: Populated Your Printed Circuit Board, and Test It Out

After you receive your printed circuit board, you'll need to populate it with the appropriate components. In addition to the list of components at the beginning, you'll probably want some of the following:

  • replacement SMD resistors, instead of through hole resistors
  • male or female headers
  • a screw block terminal

Flux makes soldering SMD component much easier. The finished product looks far nicer than the messy breadboard, takes up less space, and with a project case could be installed permanently.

Step 9: Enjoy Your Thermostat Logs and Graphs!

I have the Python script start at boot on my Pi, running in the background. You'll notice some spikes in temperature readings. Sometimes these are caused when resetting the ADT7410, but other times they happen for unknown reasons. I've added code to use a simple outlier detection routine (rolling median absolute deviation) to discard most of the spikes.

There's no reason you couldn't use an Arduino instead of a Raspberry Pi; in that case, I2C communication will be easier, but getting network connectivity will be harder. Also, if you were to power the circuit from the Arduino's 5 volt supply, the value of the HCPL3700's pull-up resistor might need to be tweaked.

I hope found this Instructable interesting! Comments, questions, and feedback are welcome.