LED Christmas Lights Project Completed

I have nearly 1100 feet of RGB LED strip lighting outlining my roof for Christmas this year. I’ve written about pieces of the project incrementally along the way; this post collects all of that information and any new updates all in one place.

Topics covered:

  • Physical attachment to house
  • LED controller boxes
  • Problem Statement: How to control 9 of these?
  • Arduino 9-way IR emitter implementation
  • Programming the light show
  • Some over-the-top study of circuit behavior
  • Future directions

This is going to be a long post … settle in!

Physical Attachment to House

The LED strips look like this:


All together it took 8 rolls (four are shown in this picture) to outline my roof and a ninth to do the observation tower.

My house has a metal roof with a metal fascia going all the way around the house. Magnets will stick to the fascia metal, so that’s how I attached the LEDs to the house. It took 500 magnets from http://www.magnetshop.com/ and 500 zip-tie mount points that had to be stuck onto each magnet by hand:



The magnets attach to the fascia and the zip ties anchor the LED strips. We prototyped this method back in September and left some LED strips up for a month or so to test it out. The only problem encountered was that some of the zip-tie mounts separated from their magnets; glue fixed those.

This attachment method also has the advantage of being fairly easy to take down at the end of the season and to redeploy again next year.

Controller Box Detail

Each LED strip comes with a controller box:


As you can see, the box has an IR receiver (the “tail” on the box) with a corresponding remote control.  The RED, GREEN, BLUE, etc buttons each command varying voltage levels on the R/G/B outputs which are hooked up to the corresponding LED colors on the strand. The buttons labeled “DIY” can be programmed for custom color blends. The other buttons (JUMP/FADE/FLASH/etc) select built-in effects; for example JUMP3 cycles the strand through a rotation of three different colors.

This works great for a simple installation but for my application there is a big problem…

Problem Statement

I have nine of these boxes, because there’s a maximum strand length (based on voltage and power requirements) a single box can drive. So, for example, to set my entire house to be RED I’d have to walk around the outside of my house, aim at each individual strand’s control box, press the RED button, and repeat that process 9 times – once for each individual strand.

That’s just impractical. Nor can I just set each strand to a built-in effect (e.g., the JUMP3 cycle), because there’s no easy way to synchronize them all. Even if I could synchronize them at the start somehow, they’d probably drift apart anyway as it’s unlikely their internal timing mechanisms are constructed to very tight electronic tolerances. You might think that the JUMP3 program is switching colors every second, but one control box might switch them every 1.0001 seconds and another might switch them every 0.9999 seconds. Even a difference that small would result in the strands being visibly out of sync after just two hours.

There are other controllers you can buy that have built-in solutions for this sort of synchronization, but being a nerd I decided to address the problem by building something myself. Basically I needed a network-addressed and computer-controlled version of the handheld remote that could also drive nine IR emitters in parallel (so as to command all the boxes at once).

9-way IR Emitter Implementation

The first step for any such implementation is to capture the IR codes from the remote control in the same way any universal-remote would.

I used an Arduino Uno for this, with a standard TSOP38238 IR decoder chip (I bought mine from Adafruit) and Ken Shiriff’s excellent IR Remote library.

Here’s what the TSOP38238 looks like:


This decoder is trivial to connect to an Arduino – connect the power input to +5V, connect the ground to Arduino ground, and the data output pin goes to any Arduino input pin (I used pin 11) so the library can read it.

Strictly speaking different IR decoders are tuned for different specific IR protocol frequencies. For example the TSOP38238 is tuned for 38KHz signals and there’s a related part TSOP38236 that operates at 36KHz.

[ In fact both of these parts are deprecated and have newer versions (different part numbers) you should probably use instead; I only picked the TSOP38238 because “that’s what they had on the web site I was surfing at the time (Adafruit).” ]

I didn’t know in advance what type of code my remotes would transmit so I just had to pick something and start somewhere and see if it worked. Fortunately the decoders only cost about $2 each so you could buy a few different ones and have them in your lab kit.  At one point it was true that many (maybe even “most”) manufacturers started using the NEC protocol at 38KHz, which makes it a popular choice and probably explains why Adafruit sells that particular decoder. You might wish to peruse this IR decoder selection guide or do some further reading if you run into problems with a particular remote you are trying to decode.

I got lucky at the start because the remotes I had do in fact transmit at 38KHz using the NEC protocol and my decoder chip received them just fine.

I used some example code from the library which simply receives IR data from the decoder and prints it out on the Arduino serial output which you can monitor from Arduino IDE. Here’s the code:

#include <IRremote.h>
IRrecv irrecv(11);  // my rcvr is on pin 11
decode_results results;

void setup() {
  irrecv.enableIRIn(); // Start the receiver

void loop() {  
  if (irrecv.decode(&results)) {
    Serial.print(" ");
    Serial.print(" ");
    Serial.print("  == 0x");
    Serial.println(results.value, HEX);
    irrecv.resume(); // Continue receiving

I pushed every button on the remote and noted the corresponding values for each. As already mentioned the remote sends using the  NEC protocol with 32-bit values. Here are all the codes I got:

    BRIGHTER = 16726725
    DIMMER = 16759365
    PLAYSTOP = 16745085
    POWER = 16712445
    RED = 16718565                              
    R1 = 16722645
    R2 = 16714485
    R3 = 16726215
    R4 = 16718055

    GREEN = 16751205
    G1 = 16755285
    G2 = 16747125
    G3 = 16758855
    G4 = 16750695
    BLUE = 16753245
    B1 = 16749165
    B2 = 16757325
    B3 = 16742535
    B4 = 16734375
    WHITE = 16720605
    W1 = 16716525
    W2 = 16724685
    W3 = 16775175
    W4 = 16767015

    RED_UP = 16722135
    RED_DOWN = 16713975

    GREEN_UP = 16754775
    GREEN_DOWN = 16746615

    BLUE_UP = 16738455
    BLUE_DOWN = 16730295

    QUICK = 16771095
    SLOW = 16762935

    DIY1 = 16724175
    DIY2 = 16756815
    DIY3 = 16740495
    DIY4 = 16716015
    DIY5 = 16748655
    DIY6 = 16732335

    AUTO = 16773135
    FLASH = 16764975

    JUMP3 = 16720095
    JUMP7 = 16752735
    FADE3 = 16736415
    FADE7 = 16769055

The RED/GREEN/BLUE/WHITE correspond to the color buttons. Names such as R1, R2, etc correspond to each of the four buttons under the RED, GREEN, BLUE, and WHITE buttons on the remote.

If you look up the details of the NEC infrared protocol system, you will find that the 32-bit codes are broken down into four eight bit fields:

  • 8 bits of Address
  • 8 bits of Inverted Address
  • 8 bits of Command
  • 8 bits of Inverted Command

The “address” field is one way to make sure that hitting the fast forward button on your DVD remote control doesn’t also accidentally turn off your TV; each manufacturer was assigned their own address value to use.

The “command” field is meant to be used for the actual command, e.g., RED, BLUE, etc., in this case.

Looking at the RED code of 16718565 for example, in hex:

16718565 = 0x00FF1AE5

We can see that the “address” used by the box is 00 (inverted address FF) and the RED command itself is 1A (hex) or 26 decimal and the inverted version of that is E5 (hex) or 229 decimal. The sum of the command and the inverted command (or the sum of the address and the inverted address) should always be 255.

None of this is necessary to know but explains why the code values look so unusual in decimal vs being something more obvious like “1”, “2”, “3”, etc.

It also tells us that there are only 256 possible codes that can be encoded for any given device using this protocol. Fortunately, humans start getting annoyed with remotes that have around 75 buttons or more so this isn’t really much of a limitation.

Armed with the codes, the next step was to figure out how to send these codes under computer control, and eventually to all 9 boxes at once.

Again using an Arduino it’s pretty trivial to control one IR emitter LED with the IR Remote library and a simple circuit. Here are two example approaches:

The IRRemote example uses the Arduino output directly and can only drive the IR emitter at less than about 50% of its theoretical power because the maximum current output of an Arduino pin is 40mA. To work around this limitation the Adafruit example uses an external transistor as an amplifier and so can drive the IR emitter at a higher power setting.

I needed to drive 9 IR emitters. There are many ways I could have done that but I decided on an approach based on some things I already had sitting around in the project bin (old wall-wart power supplies from old otherwise-dead equipment) and also based on wanting to learn about MOSFETs for other, future projects.

So while I was surfing at Adafruit I also bought some logic level N-channel power MOSFETs: International Rectifier part number IRLB8721. These can handle far more voltage and power than necessary for this purpose, but they are inexpensive and have the important property that they turn on at a “logic level” of Vgs = 4.5V at the Gate G. An Arduino output pin supplies 5V so this will work well for controlling the MOSFET.

The basic idea is simple: the MOSFET will take the low-power output signal from pin 3 of the Arduino and use that to modulate the higher power from the external wall-wart supplied to a string of 9 IR emitters all in series.  The circuit looks like this:


You may wish to click the picture to open it at full resolution.

Ignoring all the resistors for a moment, the operation of this circuit is pretty simple. When Arduino pin 3 is low (0V) the MOSFET is off and no current can flow from the Drain (D) to the Source (S). Therefore the IR emitters are off as they have no connection to circuit ground. When Arduino pin 3 is high (5V) the MOSFET turns on and current can flow from D to S, connecting the ground side of the series of IR emitters and allowing current to flow through them and turn them on. Thus, the IRRemote library modulates the output on Arduino pin 3 and in turn that pin modulates the power to the nine IR emitters and they all end up transmitting the same remote control code at the same time.

Each of the nine IR emitters is actually out on my roof somewhere connected by several hundred feet of wiring back to the central point where this circuit and the Arduino sit. Because the circuit connects all the IR emitters in series, if any of these wires breaks (or if an IR emitter burns out somehow) the whole show comes to a halt; no codes will be transmitted by any of the IR emitters unless all of them are properly connected and functioning. In my case I consider this property of the series arrangement to be an advantage. If something goes wrong electrically I will know right away because nothing will work – the entire roof will stay whatever the last commanded color was. In this case I consider that better than having eight of the nine strands work (continue to change according to the programming) with one of the strands stuck, as would happen in other parallel-wiring solutions. In practice there haven’t been any failures of any emitters or wires yet so it really hasn’t made a difference one way or the other.

If you read some of my older postings on this circuit, you’ll see that there’s been some evolution of thought as the project went along. Originally we expected to need 11 or 12 strands, so I was originally trying to figure out how to power 16 (four extra on general principles) emitters. When it turned out we only needed 9 things got a little simpler and this circuit is the final result.

I’ve played around with different values for R1 and the bottom line is it really makes no difference. We taped the IR emitters directly onto the corresponding receivers (the “tail” on the control boxes). As long as the emitter is receiving at least 20mA (the minimum) and no more than 100mA (the maximum recommended) it will work just fine. I picked an R1 value of 33Ω because that value will keep the current flow between those two limits whether I have 8 or 9 IR emitters hooked up; sometimes for debugging it has been handy to take one of the emitters out of the circuit and this value allows for that.

There is some empirical measurement necessary to figure these values out because the voltage drop (“Vf”) across an individual IR emitter depends somewhat on the current flow, and the current flow in this circuit depends on all the voltage drops – in other words there’s a circular dependency so the easiest way to figure it out is to measure it rather than figure it out. So what I did to measure a few key parameters was to take a close-enough guess for the values and then measure the circuit with the MOSFET gate connected to the +5V output of the Arduino instead of pin 3. This allowed me to use a voltmeter to measure key parameters like the total voltage drop across all nine emitters.

Similarly the voltage provided by my wall wart, Vx, varied depending on load because it is an unregulated power supply. Again I just iteratively measured the reality of its output with the MOSFET gate statically fed 5V to turn the circuit on. My detailed write-up on figuring all that out is in this earlier note and won’t be repeated here. The thing I did learn from that exercise is that in the next project it would probably be smarter to use a regulated power source!

Resistor Rg protects the Arduino output pin from excessive current draw when the MOSFET transitions from off to on. When the MOSFET is off and Arduino pin first rises to 5V, the Gate input acts somewhat like a capacitor between G and S. This is just inherent in how MOSFETs work. This means DC current will flow for a brief instant between G and S until the MOSFET accumulates enough charge and blocks further DC current flow “somewhat like a capacitor between G and S“. This is all part of the transition of the MOSFET from the OFF state (very high resistance between D and S) to the ON state (very low resistance D to S). To keep that brief current spike below the Arduino max pin output of 40mA, I choose Rg at 330Ω which leads to this calculation for the current draw when G and S are momentarily (effectively) shorted:

Ig = 5V / 330Ω = 15mA

That 15mA (which is within the 40mA limit) will only be drawn for a brief fraction of a second as the MOSFET charges. During the instant of time the 15mA is flowing the resistor will dissipate:

W = I^2 * R = (0.015 * 0.015) * 330 = 75mW

of power; I’m using a 1/4W resistor which is more than enough to tolerate this.

Resistor Rgs is there for the “Good Housekeeping Seal of Approval”. If the Arduino is powered off and pin3 is in an unknown state we don’t want the MOSFET randomly turning on and off, so Rgs  functions as a pull-down resistor making sure that the MOSFET gate stays at zero volts unless the Arduino is actively supplying voltage on pin 3. In some designs there are other considerations for this resistor but in this simple case “it just doesn’t really matter that much” is the answer to all of those complicated questions. I picked 18000Ω somewhat arbitrarily, trying to get a relatively low value for the pull-down effect while also being aware that the two resistors Rg and Rgs form a classic voltage divider; the voltage at the MOSFET gate will be:

Vg = 5 * (Rgs / (Rg + Rgs))

This means that as Rgs gets smaller, so does the gate voltage that will appear at G. With the chosen values of 330Ω for Rg and 18000Ω for Rgs the gate voltage works out to about 4.9V when the Arduino is supplying 5V. The MOSFET will turn on at anything above 4.5V so this works out just fine.

I originally prototyped all this on a breadboard and then eventually built this soldered-together implementation:


This actually shows an earlier version of the board built back when I expected to only have eight IR emitters (and to build two of these boards). Instead I have nine emitters and I daisy-chain them on a separate terminal block rather than the screw terminals shown in the picture (and I also changed the resistor R1 from 56Ω which you sort of can’t see in this picture to the 33Ω described earlier).

Well, we can geek on about electronics forever; indeed I’ll come back later in this posting and talk about some other things I measured. In the meantime, let’s move on to programming.

Programming the Light Show

From a previous project I had some code implementing a simple JSON POST interface on an Arduino (with ethernet shield) allowing remote access to digital I/O. That code is up on github.

So I plugged an Ethernet shield onto my Arduino, hooked the whole thing up to my network, and built a server starting with that code. I modified it to accept POST requests of JSON to control sending IR codes using the IRRemote library.

The JSON it accepts looks like this:

{ "codes": [ ircode-dictionaries ], "repeat": n }

An ircode-dictionary looks like:

{ "code": 16718565, "bits": 32, "protocol": "NEC" }

POSTing this to my Arduino server will make all the LED strings RED for example.

There is an optional “delay” element specifying a delay, in microseconds, to wait after sending the code. Although the NEC protocol allows for fairly tightly-repeated IR codes, the control boxes themselves don’t always respond reliably if you send codes too quickly after each other. Empirically I  found that a delay of about 175 milliseconds between codes works reasonably well.

The server can accept multiple ircode-dictionaries in one POST; the main limitation is that there isn’t much RAM (a whopping 2K!) on an Arduino so the maximum POST size my code can accept isn’t much. At the moment I have it defined as 450 characters. Still, that’s enough to allow you to do some interesting things. Only the first ircode-dictionary has to have the protocol and the bits specified; those values become the default for subsequent ircode-dictionaries in the same POST. Similarly for delay, if it’s been specified.

Given all that, we can make the LEDs rotate through RED GREEN and BLUE like this:

{ "codes" : [
    { "code": 16718565, 
      "protocol": "NEC", "bits": 32 },
    { "code": 16751205 },
    { "code": 16753245 } ],
  "repeat": 10 }

I have to be careful with the “repeat” because the Arduino server is non-responsive to network requests during the execution of all this. I usually try to limit the “repeat” value to something that will cycle in 5-10 seconds at most. Longer cycle programming is implemented by looping over multiple POST operations rather than using “repeat” to make the server do it directly.

I wrote a python script using the Requests library to send POST requests to my Arduino.  The Requests library makes it dead-simple to do stuff like this; here’s the key portions of my python code for driving my Arduino (beware of / apologies for wordpress messing up indentation and line breaks):

def sendcodes(server, code_dicts, repn=0):
    """POST code dictionaries to the server."""
    d = {"codes": code_dicts}
    if repn > 0:
        d["repeat"] = repn
    s = json.dumps(d, separators=(',', ':'))
    if server == "debug":
        print("Would send: [{}]\n".format(s))

I squeezed out extra spaces using the “separators” argument in the JSON conversion, just because of space limitations on the Arduino and thus not wanting to waste any of the limitation on unnecessary white space. For the same reason I only send the “repeat” element if the repeat is more than just one.

I then built up a whole bunch of library routines to do various effects. So far I’ve written:

  • redgreen() alternates the roof between RED and GREEN at five second intervals
  • rotate() picks eight colors at random (different each time it is called) and an interval at random (also different each time it is called) and rotates the lights through those colors. The shortest interval is 10 seconds and the longest is four minutes.
  • heartbeat() pulses the lights on and off in what I can only describe as a “thump thump” pattern meant to be reminiscent of a heartbeat. Obviously the default color for this one is RED but the display can heartbeat() in any color.
  • rapid() cycles the lights as fast as possible through a set of colors. Used sparingly; this is pretty distracting (but amusing).
  • seizure() is really a special case of rapid and just flashes between two colors, or one color and off, rapidly.

Given all that the main loop driving the display that everyone is seeing these days looks more or less like this:

def run_normal_program():
    while True:
        seizure(c=C.PINK4, c2=C.BLUE)
        seizure(c=C.PINK4, c2=C.GREEN)
        seizure(c=C.RED, c2=C.GREEN)
        ... etc ...

Most of the special effects routines run for about a minute or so. They are eye-catching but it’s just overkill if they run for very long. The rotate() routine is where the display ends up spending the majority of its time. Going through the eight colors takes 80 seconds when the random color duration chosen is 10 seconds (the shortest duration it will pick) but takes 32 minutes when the color duration is four minutes. So on average the house is in this mode for 15 minutes at a time and therefore it spends the majority of its time in this somewhat-sedate display mode punctuated by occasional blinking craziness. Whether this whole concept is cool, impressive, or hopelessly tacky is in the eye of the beholder! I have to admit that I do get a kick out of receiving text messages:  hey did I just see your house from Mopac? (and many other locations around town where the house display is quite visible).

This picture was taken from 4.8 miles away:


One problem I ran into relates to the POWER command. There is no command to turn the strands ON and OFF; there is just the POWER command which toggles the on/off state. This is potentially a big problem because if one strand ever misses a code for any reason at all then it will be out of sync. They could also get out of sync if some of them lose power while the others do not (as happened recently when a contractor working on something else temporarily unplugged one section of the display). Because of the toggle functionality I really have no way to know for sure whether sending a POWER command is turning it on or off.

Just to be sure there was no hidden explicit ON or OFF IR command I wrote a program allowing me to send “every possible” command to the boxes. Under the normal NEC IR protocol, which these devices are using for every other code on the remote control, there are only 256 possible codes that can be sent. So I tried them all and did not find any additional POWER ON or POWER OFF codes.

This is a pretty common problem in the multimedia control system world and the way it is usually solved involves additional sensors on the target device – to either sense its power draw in one way or another or to sense some other attribute that reveals whether the device is currently on or off. I certainly could have done that here with a photosensor, or even a direct voltage sense on the output of the control box, but that would require bringing another set of 9 wires back to my Arduino controller.

Instead, I never turn the boxes off; I never send the POWER command. I programmed one of the DIY codes (more on this in a moment) to be zero RED, zero GREEN, and zero BLUE — in other words, to be OFF. Instead of turning the LED strands off with the POWER command, I accomplish virtually the same thing by setting them to display the “color” DIY number 6 which I’ve programmed to be “off”.

If there is a power glitch the units always start up in the ON state. So that’s consistent with my control methodology; the units are simply never turned off and if they reboot because of a power failure they start up in the ON state by default.

The controller boxes remember the last setting of certain parameters for each color button that was on the remote. For example if you send the RED command and then send a few DIMMER commands, you will get a lower-output RED color on the LED strands. If you switch to GREEN, the GREEN will be full intensity (assuming you also haven’t dimmed it before), and when you switch back to RED the RED will be dimmed as it was before.

In other words, each color setting – each of RED, GREEN, BLUE, but also all the R1..R4, G1..G4, etc colors – each one of those separately remembers its own brightness setting.

The DIY codes work in a similar fashion, except instead of responding to the DIMMER and BRIGHTER commands they respond to the RED_UP, RED_DOWN, GREEN_UP, GREEN_DOWN, and BLUE_UP, BLUE_DOWN commands. There are six supported DIY code buttons, each one remembers its own separate value of how much RED, GREEN, and BLUE to power. I’m not entirely sure exactly how many levels of each of R/G/B the box can produce, but empirically I’ve determined it is no more than 32 levels (for each color separately). Thus to “program” the DIY color number 6 to be off all I did was select it (send DIY6), then send 32 RED_DOWN commands, 32 GREEN_DOWN commands, and 32 BLUE_DOWN commands. After doing that, any time I send DIY6 the strands all turn “off” (provide zero power to RED, GREEN, and BLUE).

For my purposes I want all of the ordinary colors to be programmed at maximum intensity, and as already described I need to set up DIY6 to be a surrogate for “off”. It’s time consuming to send all the codes to set this up, so I don’t want to do it more often than necessary. I have a separate command I wrote that will send all these configuration codes when needed. After it has been done once it shouldn’t be necessary to do it again unless we have to replace a controller box. Truth be told during debugging of this whole installation we blew a few boxes up (rain will punish all your waterproofing mistakes) and so it is handy to have the “reprogram everything” command available when needed.

Once I had a python program running that was driving the LEDs with IR codes I was able to integrate some other fun things. For example I have a Big Red Button and connected it up so that it turns the display on and off with a simple press. I also have a web server visible only within my house network and I have a page on that server that can be used to control the lights. This means I can turn them on or off from my phone from anywhere in the house now which comes in handy sometimes, and I’m adding other features to that control page so I can put the lights into a specific pattern if I want to.

One thing I might do in the future is set up a web cam (so they can see the lights) and actually let my friends control some of the light effects over the internet!

Over the Top Circuit Analysis

I had access to an oscilloscope and decided it would be fun/interesting to look at some of the details of how the circuit itself was behaving. These pictures were obtained with an earlier version of the circuit that had R1 at 56Ω and eight IR emitters instead of R1 at 33Ω and nine emitters. As these measurements were all about the MOSFET gate behavior I don’t think those changes have any effect but I list them anyway in the name of Science and full disclosure.

First I just captured a trace of what the Arduino output looks like on pin3 when it is being modulated:


In this picture there is nothing attached to the Arduino output other than the oscilloscope probe. According to the scope the signal peaks (rings) at 6.36V and dips to -1.40V on the down transition. You always have to be careful with stuff like this because the oscilloscope probes could also be contributing to some of the effects, but my GUESS is that this is pretty close to the actual truth of what is going on.

Here’s what it looks like on a broader scale, in effect showing you the NEC IR encoding at work:


If you go look at the NEC protocol details you will appreciate this picture better. We’re seeing the initial burst (the very closely-spaced vertical traces at the start) and then we can see the zero and one bit values encoded by the different-duration gaps between subsequent (smaller) bursts. In fact you can clearly see the 8 zero bits (short gaps) being transmitted for the address (zero) and then the 8 one bits (long gaps) being transmitted for the inverted address (255), and if you cared to you could decode what the actual command (and inverted command) sent was in this trace.

Here’s what it looks like on a much smaller scale showing the details of the ringing on a downward transition:


At this scale we can start to see the chunkiness of the limitations of the scope sampling bandwidth.

Here is the corresponding waveform when this output is driving the MOSFET gate as shown in this circuit.


It’s maxing at 5.56V now and I didn’t have the scope set to explicitly read out the min but visual inspection shows the negative ringing is less than -1.0V (each vertical grid is 1V).

In this last picture the yellow trace is on the Arduino side of the resistor Rg and the blue trace is on the MOSFET gate input. We can see the difference between the Arduino output and the response of the gate which behaves somewhat like an RC circuit in this case:


I did have the scope set up to show minimum voltage in this picture so you can see the ringing on the yellow dives down to -0.64V in this case.

I was really curious to know what was going on with that “pause” in the rise of the MOSFET voltage … the plateaus that occur in the middle of the blue trace rise and fall. I did some googling and discovered the “Miller Plateau” and it’s cool to see it in reality here.

Everything of course is working exactly as one would (or should) expect here; I just thought it was interesting to see it in detail. That trace with the yellow (pin3 “as commanded”) and blue (MOSFET gate “as responding”) traces shows how the signal is getting slightly distorted by my circuit; however, the distortion clearly doesn’t matter both because it is relatively minor and (more importantly) because the way the IR protocol works the exact shape of this waveform doesn’t matter so long as the receiver recognizes the modulation of the highs and lows of the 38KHz signal, which it does (we know this because … the whole thing works!)

Future Directions

There’s lots of ways I could go with this. Expect more craziness next year but for this year I’m happy with what we’ve got working so far.

One idea is to recapture the idea of each strand being individually controllable and allow for some effects like a color that “rolls around the house” at strand granularity. It’s not entirely clear to me if this effect will be as good as that sounds, because of how the strands divide up around the roof perimeter and whether it would really have the visual effect desired. But it might be worth trying. A simpler version of this idea would be to break out the tower control from the rest of the house and allow for some multi-color effects that way. These sorts of ideas will, of course, require a completely different approach for powering the IR emitters; the simple circuit I’m using now won’t allow me to do any of those things.

People of course always ask for “sync the lights to some music”. The problem with that is the control boxes don’t respond very quickly to the IR commands. They really don’t respond quickly enough to enable any convincing music synchronization.

That observation leads to the next idea: building my own LED control boxes and getting rid of the whole idea of IR control. I’ve taken one of the LED control boxes apart (of course I have!) and it appears to be a fairly straightforward dimming control circuit that I’m guessing uses PWM and quite clearly uses MOSFETS to control the 110V that are sent down each of the RED, GREEN, and BLUE supply lines (there are two MOSFETS per line and I’m wondering if somehow they used the same 60V MOSFETS I have in pairs to control a 110V signal; I’m not really sure how this works yet and have more kitbashing and research to do on that). I’ll try not to electrocute myself while doing this research. If I can build my own LED controller then I can put each of the strands directly onto my network without the intermediary IR control connection and that will allow much faster and cooler effects.

Or, of course, I could just probably buy some different controllers that might already have network connections in them. But where’s the fun in that?

Well, that’s it — this is the longest post ever and if you are still reading it at this point let me say: Thank You and Merry Christmas!

*** Updates ***

12/08/2016: I added a second bt.tn button that puts the lights through one cycle of “crazy” mode: about ten minutes of blinking and color effects. This button was very popular at a recent party.



12/10/2016: Someone in the neighborhood left me this awesome comment. Thank you!


12/26/2016: I modified the circuit to give me independent control of the tower and the roof perimeter. I simply split the IR voltage control (Arduino pin 3) into two different AND gates (74HCT08). The other input to each AND gate is an “enable” line (two different enables). This now allows me to individually enable two different copies of the IR signal, so I can control just the tower, just the roof perimeter, or both at the same time.

The TTL specification for a “HIGH” voltage output level is 2.7V minimum. That’s too low to trigger the MOSFET “logic-level” gate input which requires 4.5V minimum. It turns out the 74HCT08 (quad-AND) chip I’m using will output at least 3.8V minimum (per the data sheet) which is better than 2.7V but still not enough. Therefore, to interface the TTL outputs to the MOSFET gate I added a TC427 MOSFET dual-driver chip (one 8-pin DIP contains two independent MOSFET drivers). These drivers accept TTL-spec inputs and convert them to “full supply voltage” outputs (sort of; see the data sheet for details as obviously it’s a bit more complex/subtle than that). These are also good chips to have in your toolkit because they do a better job of driving current quickly into the MOSFET gate to force a faster transition by charging the gate faster. That’s also another benefit of using these driver chips although it turns out to be not especially important in this particular application.

The whole thing looks like this schematically:

The 330Ω Rg resistor really should be taken out now, as the driver chip works perfectly well (best, arguably) if directly connected to the MOSFET gate input; I left it in place however because this was a last-minute retrofit/add-on and it was just easier to leave that part be for now.

I programmed one effect – the tower and roof exchanging red/green between themselves (tower red, perimeter green, then tower green, perimeter red). Expect more fun with this next year!

12/29/2016: Unfortunately a mechanical connection failure has disabled the tower, and I can’t safely get to it to fix it myself. So no more tower lights 🙁 … it’s all coming down after New Year’s anyway.

2 Replies to “LED Christmas Lights Project Completed”

  1. I came to your site through looking up usb wall outlets, but I stayed for the christmas lights. Marvelous job and the whole town definitely appreciates it from afar. I appreciate the time taken to detail all the technical work done, great read!

Comments are closed.