Hacking Christmas Lights

Saturday, November 27, 2010 by darco
Posted in , ,

I went to Costco the other day with my wife to pick up some Christmas lights. While there, I found the most awesome christmas lights ever: the GE Color Effects G-35.

XMAS-Lights/IMG_1208

This string contains fifty "bulbs". Each bulb contains a red, green, and blue LED—allowing for any color to be produced. The biggest drawback is how you control them: there are 14 pre-set "programs" that you can select, and they are all pretty lame. However, with a little reverse engineering and an Arduino (or any other microcontroller) they can be made into the coolest Christmas lights on the block. If you buy several strings, you could easily make a large 2D LED array. The possibilities are endless. At $59.99USD for a string (A tad over $1 per node), these things are a steal for what you can do with them.

IMG_1325

My christmas lights, hacked to display a rainbow.

I wanted to share my efforts so that other people can give this a try. At the end of the post I have a link to the code I'm now using to drive my Christmas lights. Feel free to use it for your own hacking endeavors.

So far I've reverse engineered both the radio protocol and the protocol used on the LED data bus. Of the two, the LED data bus protocol is by far the most interesting. So that's where I'll start…

UPDATE: See follow-up post here.

LED Data Bus Protocol

ybox2-and-xmas-lights/IMG_1338There are three wires connecting all of the "bulbs" in the string: +5V, Data, and Ground, respectively. The wire isn't really marked for polarity, but the order I mention is from looking at the wire going into the first bulb from the controller box with the bulb facing up. I've labeled the wires in the following photo:

XMAS-Lights/IMG_1310

Each bulb has an address numbering from zero to fourty-nine, with bulb zero being the bulb closest to the control box.

The protocol on the data line is simple and self-clocked. Here are the low-level details:

  • Idle bus state: Low
  • Start Bit: High for 10µSeconds
  • 0 Bit: Low for 10µSeconds, High for 20µSeconds
  • 1 Bit: Low for 20µSeconds, High for 10µSeconds
  • Minimum quiet-time between frames: 30µSeconds

Each frame is 26 bits long and has the following format:

  • Start bit
  • 6-Bit Bulb Address, MSB first
  • 8-Bit Brightness, MSB first
  • 4-Bit Blue, MSB first
  • 4-Bit Green, MSB first
  • 4-Bit Red, MSB first

From this we can see that we have a color depth of 12 bits. Not terribly great, but this should still be plenty for our purposes. What is interesting is the Brightness field. This field acts a bit like a multiplier and enables smooth fade-ins and fade-outs.

Bulb address 63 can be thought of as the "broadcast" bulb, except that it only changes the brightness level—the color fields are ignored. Bulb addresses 50-62 are ignored under normal circumstances.

Each frame takes 820 µSeconds to transmit. Since there are 50 bulbs, that means that takes a minimum of 41 milliseconds to individually update every bulb. This gives us a maximum refresh rate of slightly more than 24Hz. Not bad.

Bulb Enumeration

It turns out that the data line is not a continuous wire of copper thru the whole string. Each bulb contains a microcontroller with two data lines: one is an input, and one is an output.

When the string first powers up, all bulbs are in the "enumerate" state. When in this state, the first command received is used to tell the bulb what its address is. Once the address is set, all subsequent commands are forwarded to the next bulb. This process continues until all bulbs are enumerated and have an address.

Normally, you would want to perform some sort of power-on enumeration which would give each lamp it's own address—but this isn't the only option. The enumeration step allows you to assign any address to any bulb you want—including giving several bulbs the same address. By giving several bulbs the same address, you can control several bulbs with a single command.

There may possibly be a way to force all bulbs back into the enumeration state without power cycling. If this turns out to be possible I'll update this page with the details.

NOTE: While I figured out all of the other details of the protocol independently, the bulb enumeration phase was figured out with the help of joyangel and jstjohnz from doityourselfchristmas.com.

Bus Snooping Observations

I was able to figure out how to use these lights by using a logic analyzer to snoop the data on the LED control bus. In addition to being able to eventually figure out the protocol, there are a few other observations that are probably worth noting.

First, while you can set the brightness level to any value from 0x00 to 0xFF, I've never seen the stock controller use a brightness level larger than 0xCC. While the bulbs will accept values larger than this (and it will indeed be a tad brighter), I don't recommend it unless you really know what you are doing. It was probably set to this for a reason.

'White' was not represented as 15 red, 15 green, and 15 blue as I might have expected. Instead, the stock controller uses 13 red, 13 green, and 13 blue. I'm not really sure why, but it might be a good idea to follow the same convention. Saturated colors were represented as expected, i.e.: cyan was 0 red, 15 green, and 15 blue.

Bugs in the Bulbs

It appears that any time you change the brightness level on a bulb by larger than a single increment or decrement that the bulb will randomly 'glitch' for a few milliseconds. These glitches are minor if you are only doing this infrequently, but if you are updating the brightness level quickly it can become a problem, especially if you are updating the brightness level for each bulb individually. You never see this problem with the stock controller because the only time it changes the brightness level more than a simple increment or decrement is when changing programs. Changing the color value (while not changing the brightness) doesn't cause glitches.

Notes About Voltage

Beware that the power supply for the string is nominally 5.6 volts, which is too high to directly power an AVR chip. This caused me some grief early on. Adding a 5 volt regulator fixed the issues I was having. If you don't have a 5-volt regulator handy, you could probably get away with throwing a diode in-line, with an added bonus of reverse-polarity protection. If you are using an Adruino, you can simply use the regulator on the Arduino board.

If you plan to control the string using a Parallax Propeller (or other 3.3-volt microcontroller), be aware of the voltage difference. I haven't tried driving the data line at 3.3 volts, but I wouldn't be surprised if it had some problems, especially with the last few lights in the string. A level converter may be necessary to reliably control the string with a 3.3 volt chip. UPDATE: People seem to have had no problems driving the data line with 3.3 volts from a propeller, so a level converter does not appear to be necessary.

Opening the Control Unit

The control unit is rather hacker-unfriendly. It has six TP3 security bolts that require a triangle (!) shaped bit to remove. I tried to drill out one of the screws, but that was a bad idea: the screw got hot and melted the plastic. Oops.

ybox2-and-xmas-lights/IMG_1348What I ended up doing was securing one of the many disposable hex wrenches with a vise and then using my Dremel to fashion myself a triangle-shaped bit. If you decide to go this route, here is some advice:

  • Don't do this if you don't have a vise. Trying to do this without properly securing your hex wrench is asking for trouble.
  • Don't do this without goggles. Sparks go everywhere. Lots of sparks. Don't be an idiot—wear goggles.

Luckily, I've done all of the hard work for you, so there is no need to open the control unit yourself. Simply cut the wires from the control unit, strip them, and use the LED bus protocol outlined above. Don't throw the control unit away though, you may want to harvest the RF Link receiver from the unit for some future unspecified project. If you are curious, this is what is inside of the control unit looks like:

IMG_1300 IMG_1301

While I have no idea what is under that epoxy blob, U1 is apparently the micro controller. The text on U1 reads:

MG87FE2051AS20
1010GMBCA
CON113.00

This is a 8051 micro-controller made by a Taiwanese company named Megawin. Grab the datasheet here.

Remote Control

The remote control was the first thing that I started to reverse engineer, partly due to the fact that I didn't immediately have the triangle-shaped bits required to disassemble the control unit.

XMAS-Lights/IMG_1282

The remote comes apart fairly easily: just four phillips screws and you are in.

XMAS-Lights/IMG_1284

The electronics are fairly simple. The first thing you'll notice when you remove the back cover is the radio transmitter module. This is a simple 433.92 MHz On-Off Keying transmitter, similar to the cheap RF Link transmitters that you can buy from SparkFun. It works in pretty much the exact same way.

Here is what the remote boards look like from the other side:

XMAS-Lights/IMG_1288

The format of the control codes is very similar to what you might find in a TV remote.

XMAS-Lights/on-off-button

After spending a little quality time with the logic analyzer, this is what I found:

  • Preamble: 15 pulses (0.2ms high, 0.2ms low), followed by 0.4ms high and 0.4ms low
  • 0 Bit: High for 0.1ms, Low for 0.1ms
  • 1 Bit: High for 0.2ms, Low for 0.2ms

Each radio frame is made up of the following parts:

  • Preamble
  • Two zero bits
  • 6-bit Device ID (Can be modified by adding R15-R20 on the large PCB)
  • 8-bit Command
  • One zero bit

Here are some examples:

  • on/off button: 00111111 10101010 0
  • change button: 00111111 01011010 0

I noticed that the value of the 'on/off' command appears to be somewhat dependent on the value of the device id:

  • R20: 00011111 01010101 0
  • R19: 00101111 01010101 0
  • R18: 00110111 10101010 0
  • R15: 00111011 01010101 0
  • R16: 00111101 10101010 0
  • R17: 00111110 01010101 0

Note that when R18 or R16 is set, the ON/OFF command seems inverted. The change button does not exhibit this behavior.

Software

At the moment I'm driving my string of Christmas lights using an Atmel ATTiny13A microcontroller. You can find the source code on Github.

Eventually I hope to also control these lights from a ybox2, so that I can control the Christmas lights easily from a web browser.

Anyway, feel free to use the source code for your own projects. If you happen to have the GE Color Effects Christmas Lights and end up hacking them like I did, please tell me about it in the comments section of this post.

Happy hacking!