Files
2022-03-17 00:35:16 -07:00

13 KiB

Exploring Christmas Lights

Table of Contents

Holidays

Before the Holidays of 2021 I bought Mr. Christmas Christmas String Lights. I checked out all the combinations it had to offer and they worked great and looked cool! But then I took a closer look at a single light and noticed only 2 wires going into it. How interesting! My naivety lead me to believe that each bulb should need at least 4 wires. That'd be one negative and three positive, one to control each of the red, green, and blue signals. Also there is a multicolor mode where each bulb is a random color, so that might require even more wires to be able to indivually address each bulb. The best I could come up with was that each "wire" was actually a bundle of even smaller wires. It seemed unlikely but it was the best I could do at the time.

Several weeks go by and I had forgotten all about how the lights work, and that's when I saw an article on Hacker News about controling LEDs with only powerlines. Hey! That's the thing that I don't know! I checked out the article and it turns out that each LED has it's own little tiny integrated circuit. Wow! And there are short pulses of the power dropping that it uses as the signal, genius! I was now satisfied that I knew how the lights work.

Exploring and Learning

Another several weeks go by and I get to thinking how can I repurpose the Christmas lights to use all year round. One thing lead to another and I end up wanting to customize the patterns and colors. I had a hunch that my lights are similar but not exactly the same as Tim's above. I'm also not an electrical engineer so I don't know much about circuits, but into the adventerous rabbit hole I go!

I thought the circuit board would be in the same case as the power brick, but it turns out there is a little case along the wire with a button and everything I needed was in there!

PCB
The PCB. The top is flipped vertically so the through-holes line up with the bottom

The L- and L+ are the terminals connected to the LED string. I tried to find out what's connected to those terminals to see what is sending the signals. I noticed the chip in the upper left (of the bottom side) labeled IC2. That seems to be the only thing connected to the L+ terminal. I thought that must be what sends the signals. After looking up "NCE55P05S", which is the code that's printed on it, I found its datasheet. It says it's a MOSFET, but I had no idea what that is. So again I looked it up and now I know it's a certain type of transistor that can be used as an electronic switch. Now I thought that means it's not the part that sends the signal, it's just a switch and something else must tell it to switch on and off. I learned that it has a source, a drain, and a gate. Depending on the voltage of the gate, the source will connect or disconnect from the drain. In this case all four upper pins are the drain, the botton left three pins are the source, and the bottom rightmost pin is the gate. So all I needed to do was figure out what the gate connects to in order to know where the signals come from.

I next noticed the STM32, also on the bottom. I knew just enough about those to know it was a micro controller, and ah ha, that seems like it could be the thing sending the signals! But there are so many pins coming out of it, which one could it be? And like I said I'm not an electrial engineer so trying to follow the traces on the board and also trying to make sense of the other components was a challenge! Somewhere along the way I learned about the resistive voltage divider. That allows a voltage to be stepped down a certain amount in between two resistors. I also noticed another transitor on the left side of the board labeled Q35. The voltage divider helped me figure out that the drain of Q35 is connected to the gate of the first MOSFET from before, IC2. So it's a two-stage switch! First, the Q35 needs to be switched, then that switches IC2. Then following the traces some more it seems that the gate of Q35 is connected to R13 which goes to a little hole just under the label of R8. Looking at the top of the board that hole goes down a bit and is covered by some orange goo (don't know what that is) then takes a right and goes down into a nother hole. Looking at the bottom side again, the hole under the label R4 connects to the STM32! Wow! Finally I found it, woohoo! So the leftmost pin on the bottom row of the STM32 is the signal generator.

Next thing was to figure out what the signal is that it sends. I did a little searching and I bought one of the cheapest logic anyalzers I could find, the HiLetgo USB Logic Analyzer, it was about $13. But Tim from the article was using an oscilliscope, so I also got a roughly $30 EspoTek Labrador. And I got some wire test hooks since there weren't any breadboard type pins to connect to. I wasn't sure if the HiLetgo would be good enough but, spoiler, it was way more than enough! From what I have learned, logic analyzers only detect discrete on/off values, usually only at common voltages such as 3.3V and 5V. That means it will only tell you if the signal is on or off, but it can't for example show you a full sine wave with the entire range of voltages. To see analog signals you would need to use an oscilliscope. I didn't end up needing the EspoTek but it was fun to play around with and use the oscilliscope!

The HiLetgo doesn't come with software but some reviews said that Sigrok PulseView worked for them. I searched in Fedora software repos and it was there, nice! I had some issues at first with the connection, but after also installing sigrok-firmware-fx2lafw it worked like a charm. And I will say it is actually quite a good piece of software too! In PulseView I used the fx2lafw generic driver. I tinkered with different sample rates and found 200kHz is a good minimum to get decent timing for this use case. Any lower and the timings start to get rounded too much. It is still usable lower than that but the timing gets rounded quite a bit. In order to get a more accurate timing for trying to reproduce the signal I needed to go to 500kHz or even 1Mhz. But even that is still a tiny fraction of the 24Mhz that it can go up to.

I tried different protocol decoders, but none of them showed any match to the signals. So I just added a basic Timing decoder which only shows the length of time of the signals. I found that easier to view when zoomed out a bit.

zoomed all the way out
Some signals, zoomed all the way out

After recording some samples while changing the lights with my phone, it looks like the signal is "on" most of the time and there are clusters of "off" pulses. The custers happened at the same time as when I switched the color or pattern. Let's zoom a bit more into a single cluster.

a single cluster
A single cluster

Zooming even further into a single pulse.

a single pulse
A single pulse

This pulse is 104 microseconds, and when I looked at more it does range a little bit how long the pulse is but it's in the range of 100-110µs. Then there are two different pause lengths between pulses.

two pauses
The two different pause lengths

The shorter pause is 4964µs and the long pause is 17000µs. Again, these range but are within about 25µs of the ones seen in the image.

I overthought a lot during this process but eventually I started labeling the short pauses as 0 and the long pauses as 1 to convert it to binary. That ended up helping a lot to understand it. With that method in place it seems like every command on my phone contributes to a 7 bit command being sent to the lights. Sometimes many 7 bits commands are sent by the phone, each spaced apart a little. I came up with a systematic way to figure out which bits did what. The lights only have a color and a pattern that can be changed. So I selected the first pattern and then recoreded the changes of cycleing through all the colors. What I found is that the last three bits control the color. and they represent blue, green, and red in that order. Wow neat! The only odd part is that there is also a "Multi-color" option for the colors, and that seems to have it's own bit to toggle that as the forth from the last. So that makes the sequence so far that I know something like this ...MBGR. This also means that when a color like Yellow is selected, it really just turns on green and red. And white is all three colors on, and off is well, all the colors off.

If I had to guess at this point, the first three bits would be for the pattern! So I switched the color to white and cycled the patterns. Yep! Only the first three bits changed. But actually only bits one and two changed, the zeroth bit is always 0 and never 1. That means there can only be 4 patterns, but there are five patterns listed as options. It turns out the fifth pattern is actually implented in software on the STM32. It switches between Steady white and Steady what ever the current color it. If the current color is already white, then it switches white and multi-color. So now the sequence is 0PPMBGR. I think I've cracked it! This also differs from what Tim found on his lights. His light had 6 addressable sections that can each have it's own settings. But my lights seem like they all have to be in the same state.

Summary

Here's a summary of the command format that I've found:

  • "On" by default
  • Short "off" pulses separate bits
  • Pulses are 105±5µs. 0 bit has 4965±5µs pause and 1 bit has 16998±6µs pause
  • Every command is 6 bits and a leading 0 (7 bits total)
  • After start bit
    • Two bits for pattern
    • One bit for multi-color mode
    • Three bits for color (BGR)
  • A full sequence is: 0 PP M BGR (spaces are for reading only, not for timing)
  • Patterns are
    • 00: Steady
    • 01: Fade
    • 10: Sparkle
    • 11: Firefly

Also some notes about the behaviour of some modes and commands that I observed:

  • Even when LEDs are off, the signal is still fully in "on" position, it's up to the LED to show light or not
  • The Power Off button sends all zeros: 0 00 0 000
  • The Power On button flashes white/off three times with about 330ms flashes, then waits about 475ms then sets the current color and pattern
  • The Fade command is resent about every 7 seconds. This seems to be becasue all the lights turn on at the same time, but there are slight differences in the fading time. So to sync the timing the command is sent again
  • Firefly seems to be the same as Fade but it repeats on its own without the sync commands
  • Flip is defined in software. It alternates between sending Steady white and Steady current color commands every 450±50ms. If current color is white, then it uses multi-color instead.
  • In multi-color mode, when a light turns off and back on, it always switches colors

Some example commands:

  • Steady white: 0 00 0 111
  • Steady multi-color 0 00 1 000
  • Fade red: 0 01 0 001
  • Fade cyan/LightBlue: 0 01 0 110
  • Sparkle multi-color: 0 10 1 000
  • Sparkle yellow: 0 10 0 011
  • Firefly red: 0 11 0 001
  • Firefly blue: 0 11 0 100

It was super fun to be able to figure out how the lights work. But to get back to my original goal, I wanted to be able to customize the patterns. I have less options than I thought I would since the patterns are hardcoded into the bulbs directly. But I can still customize switching between patterns and colors. At this time I don't have the skills or hardware to reproduce the MOSFET switching needed to drive the LEDs, but that is the next step in this project!

Code

Even though I can't test it yet, I have gone ahead and written a sample program to produce the same signals to control the lights. I wrote it for the Arduino platform and it's available as christmas-lights.c.ino. View these signals in a similar way I can see they it matches pretty well with the official signals, although the Arduino is not as precise and there is more variance. The current program just loops through all the possible colors and patterns to replicate a "demo" mode.