Monthly Archives: January 2012

Vacation cabin telephone thermostat

[Updated: I was mistaken as to how much work the two chips were doing]

I had a talking thermostat at my vacation cabin. You would call it up on the telephone, enter a code, and then you could turn the system on and off, change the temperature, etc. It was a bit pricey and the UI was needlessly complex, but it worked okay. Until a few weeks ago it stopped taking my calls, and when we arrived it was 36 degrees inside the house. The thermostat was somewhat functional but would neither answer the phone nor turn on the furnace.

I did a quick jury-rig of the furnace (shorted they yellow wire and the red together) so we had some heat besides the woodstove, and then later re-installed the original thermostat.

I then started looking for a replacement. Nobody seems to sell the model I have anymore, and there are some posts about poor quality control. There are other solutions out there, but they are overly complicated and seem to assume that you want to spend $350 on a thermostat. I spent $180 on the one that broke, so I’m not going there again.

I did a few searches and realized that there is a much easier approach that covers most scenarios. I don’t really need to be able to remotely set the exact temperature of the house; what I need to do is be able to switch from a setback temperature to an occupied temperature. You can do this by simply adding a second thermostat, and then build something to switch between the two thermostats.

Enter the QKits MXA065.

This is just the ticket – it hooks straight to the telephone and drives two relays. It also has local control for them. You can find this same board in a four-channel version if you would like to do more.

The chip on the left is the CM8870, which handles the telephone side of things, outputting DTMF codes. Next to it is the ATTiny2313, which handles the UI for the interface, PIN codes, ring number support, and local push buttons.

I don’t have an online schematic for this board, but this is a similar one, which I believe is from the same company.

The board is nicely made, and all it needs is 12V. That makes my job easy.

My ugly daughter board on the right has a 12-pin terminal strip. This will be used to connect the wires to the two thermostats and to the furnace. Furnaces use 24 VAC to run their electronics, so I built the traditional linear supply to get the 12V that I need for the board – a full-wave bridge, 7812, and a couple of filter caps. This was just wiring.

One thing that I don’t like about the board is that there is no provision for remote operation; if you want to switch off a channel or even check to see whether it’s on, you have to go to where the board is installed. I wanted to put a pushbutton and status LED next to the main thermostat, so it would be easy to see. I therefore attached to the board and send those to the 4-terminal strip in the upper right; that will connect the cable to the pushbutton and LED.

Here’s the back of the board. Ugly but functional.

Netduino and the Adafruit LPD8806 addressable RGB LED strip

(The library is on Github here. Look at program.cs – it has a few example animations that I cooked up. )

I picked up a Netduino and a couple of the Adafruit RGB LED strips – 1 meter of the non-addressable kind, and 1 meter of the addressable kind.

Ironically, the non-addressable one is harder to use, because you need some outboard transistors to source enough current to drive all the LEDs (which are connected in parallel), and you have to write code to set up the PWM to dim the red/green/blue channels separately (so you get the color you want).

The Adafruit site has a bit of code to set up the netduino to drive the LPD8806, which I used as a starting point:

using Microsoft.SPOT.Hardware;
        public static void LightStripSpi()
            var spi = new SPI(new SPI.Configuration(Cpu.Pin.GPIO_NONE,
                false, 0, 0, false, true, 10000, SPI.SPI_module.SPI1));
            var colors = new byte[3 * 32];
            var zeros = new byte[3 * ((32 + 63) / 64)];

            while (true)
                // all pixels off
                for (int i = 0; i < colors.Length; ++i) colors[i] = (byte)(0x80 | 0);
                // a progressive yellow/red blend
                for (byte i = 0; i < 32; ++i)
                    colors[i * 3 + 1] = 0x80 | 32;
                    colors[i * 3 + 0] = (byte)(0x80 | (32 - i));
                    Thread.Sleep(1000 / 32); // march at 32 pixels per second

Not really the prettiest code around, but it works and worked as a starting point. Before I get into the details of the library I built, a few details on the LPD8806 strips.

The Support Library

I wrote a library to make it easier to use the strip. It has a Raw mode for those who want speed (though it’s not very fast), and animation support for those who want to do more complex things.

Raw mode

If you are interested in the most speed possible with this library, you should use raw mode. Here’s a sample:

    RGBStrip strip = new RGBStrip(32);
    strip.ClearAllRaw();                // turn off all leds
    strip.SetLedColorsRaw(0, 35, 0, 0);     // first led to dim red
    strip.WriteToStripRaw();            // update the strip

In a real use, you would create a loop that sets the leds the way you want them and then updates the strip.

If we use this code and do some timings, we’ll find that the following approximate costs:

SetLedColorsRaw()          0.00031 seconds

WriteToStripRaw()          0.00063 seconds

Code that set all 32 leds and then wrote out the values took about 0.0136, which translates to about 73 updates/second. You would obviously need some code to figure out what to set the led values to, and that will slow things down. If you wanted to do 5 meters of strip (160 LEDs), that would take you down to 20 updates/second, which really isn’t that fast. It’s slow not because we’re trying to do a lot, but because the IL that the C# compiler generates is being interpreted on the netduino, and that (plus the other things the managed runtime has to do) add a lot of overhead.


After a few hours of working on the library, I got tired of spots before my eyes whenever I was running the animations; those LEDs are bright.

So, I added the DimFactor property to the RGBStrip class. This provides a brightness pre-scalaing for all of the color values; the colors are the same as before, only dimmer (this is only roughly true, but it’s true enough in most cases). The DimFactor setting maps to brightness as follows:

DimFactor Brightness
0 100%
1 50%
2 25%
3 13%
4 6%

The DimFactor defaults to 1 (50%)

I also hooked up the on-board pushbutton on the Netduino to cycle through the dim factors. That means you can do most of your testing at an eye-saving brightness, and then toggle it to full brightness when you want to test the final product.

To use DimMode, just call SetLedColorsRawWithDim() instead of SetLedColorsRaw(). I’ve kept both methods because the dimming adds one extra operation and slows things down slightly.

Animation Mode

Raw mode is fast for simple animations, but if you are doing complex ones, you’ll be writing a lot of code yourself. The library provides support for more sophisticated effects, where it will handle doing a smooth fade from one color value to another.

Here’s an example:

    RGBStrip strip = new RGBStrip(32);

    Animator animator = new Animator(strip);

    Animation item = new Animation(0, 127, 127, 0, 0, 30);
    Animation item2 = new Animation(0, 0, 0, 127, 30, 30);



Each animation is described by an Animation instance, which specifies the following:

  • The led to animate (0 through num-1)
  • The red, green, and blue values that are the endpoint of the animation.
  • The number of cycles to wait before starting the animation.
  • The number of cycles that the animation will take.

In this case, we add one animation to take the first pixel to full yellow, and then one that will start when that first one is done to take the pixel to full blue. The call to DoAnimation will run the animations until they are finished (which will take 60 cycles), and it will wait 2 milliseconds after each loop.

This is very convenient to use, but will be considerably slower than raw mode because of all the extra calculations required.

Handling the animation loop yourself

Using the start count to delay animations works well if you don’t mind setting up a whole group of animations at once, but it may be more convenient to just add animations along the way. If so, you can call DoAnimationCycle(), which does one cycle. If you take this approach, you will have to handled deciding when the cycle is complete yourself.

The LPD8806 protocol for Adafruit RGB LED Strips

I bought a meter of addressable RGB LED strip from Adafruit. It uses the LPD8806 driver chip to drive LEDs. I didn’t find a writeup of the protocol, so here’s a quick overview.

Each LPD8806 has 6 PWM outputs and can therefore drive two RGB LEDs. It has two lines for SPI input (data & clock), and two lines for SPI output (data & clock).

It is implemented in a very simple way:

  • When it receives a zero byte, it resets its byteCount to 0, and writes a zero byte on the output.
  • When it receives a byte with the high bit set (ie ORed with 0x80) and its byteCount < 6, it uses the lower 7 bits of the byte to set the output of one of the PWM outputs. It then increments byteCount to move to the next PWM output.
  • If byteCount == 6, it just sends the byte to the output.

When these chips are chained together, with the inputs of the second chip connected to the output of the first chip, they are quite easy to use. You just send a total of 3 bytes per LED, and bytes 1-3 go to the first one, 4-6 to the second, and so on.  The byte order is not GRB instead of RGB, which is presumably done so that the PCB layout is simpler.

The color value is 7 bits per color, for 128 * 128 * 128 = 2,097,152 colors.

That’s about it.

And just to repeat the note in the tutorial…

  • Adafruit buys the strip in 5 meter lengths, with connectors at both ends. When you buy a shorter length they cut it off the longer length, so if it has a connector on it, the connector may be the one on the end rather than the one on the start. If you try to hook up to a connector on the end, it won’t work.