Introduction: Low-cost, Variable Intensity, Programmable AC LED Christmas Lights With MSGEQ7

I started this project in December 2014, after searching the internet for AC dimmable LED Christmas lights. Every design that I found were for 12V LED strings, so I decided to build my own. I decided to implement the following features, to meet my needs and make this project interesting for others.

- The hardware should be minimal, to reduce cost.

- Parts should be easily available.

- It should use AC powered LED strings, rather than 12V because of their lower cost and the possibility of using the LED strings outside, weathering the elements.

- The number of independent LED strings should be high (up to 10) for nice effects, and scalable down to any number.

- The LEDs should be dimmable, with a large possibility of different intensities (more than 100 levels)

- The software should be flexible, and implement nice effects (fade-in, fade-out, sequences, random sparkles..) and each string could be controlled independently.

- There should be a state-machine implemented in software to go through the different effects.

- Also, a possibility of having the LED lights change intensity with music (a.k.a. color-organ), using an inexpensive MSGEQ7 chip.

These goals were achieved, using a standard Arduino Uno running a sketch timed with the AC power line, and a handful of Triacs, MOC3021, resistors, capacitors, diodes and an AC transformer. With a suitable enclosure, it should cost about $50 of parts to build a 5 channel unit, and about $30 more for a 10 channel unit. The LED strings could be bought for about $7 a string before Christmas, and half that price between December 26th and January 1st. The project is expendable; you may add as many strings of LEDs on each channel. My current implementation has over 2000 LEDs (sixty strings of 35 LEDs), and has been working flawlessly for 2 years.

The next steps explain how to build your own. This is not a beginner's project, and before you consider building it, you should have experience with the Arduino environment, building hardware circuits on perforated boards and most of all, a good understanding of the risks involved with anything that operates on the AC power line. If you are up for the challenge, then read-on.

Step 1: Parts List

The list of parts that you will be requiring to build this project is:

- Arduino UNO board

- 120V to 9V AC transformer (not DC output, but AC)

- 1N4004 diodes (qty 3)

- 1000uF, 25V electrolytic capacitor (you may also use values between 470 to 2200uF)

- 2.7K resistor, ¼ W

- 1K resistor, ¼ W

- Some wire, solder, prototyping board, etc.

- Box to hold the Arduino and AC sockets.

For each independent channel driving one or several LED strings, you will also need, per channel:

- MOC3021 integrated circuit

- 2N6071 Triac, or equivalent

- 330 ohm resistor, ¼ W

- 360 ohm resistor, ¼ W

- 39 ohm resistor, 1 W

- 0.01uF capacitor, 200V (Very important, must be 200V or higher)

For the optional color-organ using the MSGEQ7, you will also need

- MSGEQ7 integrated circuit

- 22K resistor, ¼ W (qty 2)

- 200K resistor, ¼ W

- 0.1uF capacitor, 50V (qty 3)

- 33pF capacitor, 50V

Step 2: Finding the Polarity of Your LED Strings

Most Christmas lights sold in stores are now using LED, although this project may also work with incandescent strings of lights, it is preferable to use LEDs because they last longer and take less current, which is good for something that will be powered for up to 16 hours per day. However, LED strings will normally not work with dimmers, because they exhibit non-linear characteristics. LED strings are polarized, meaning they only turn-on on either the positive or negative cycle of the AC main. We will use this to our advantage in the software, but before we can use the strings, we have to find out their polarity.

If you decide to use incandescent lights, skip the following three paragraphs.

For this project, I have used strings of 35 LED lights, available at a local store (Ocean State Job Lot) for a very reasonable price. They were reduced by 50% after Christmas and I managed to buy the bulk of what I needed then. The strings have all their LEDs in one single series, with a 2.2K resistor and fuses also in series. The plug is not polarized. To find the polarity, I have built a quick test jig, which puts a 1N4004 diode in series with the string. When the string is plugged in the jig, and the LEDs turn-on, I identify the positive side of the string with a piece of tape on the wire connected to the cathode of the diode. To complete the test, plug the string in reverse (negative side on the cathode). The string should not light up.

For strings with more than 35 lights, chances are they are built using 2 or more series circuits in parallel. The odds are 50% (in a 50-75 LED string) that the series circuits inside the string do not share a common polarity (and 16% with strings of 100 LEDs), meaning that the test above will only light a part of the string. This is a trial and error method, if you cannot find strings with a single series circuit, you can buy longer strings and test them, and only use those which have common polarity.

Once you have identified the polarity of your LED strings, it is time to build a driver circuit to power them, and more important, to isolate your Arduino from the main AC line.

Step 3: The AC Driver, Part 1

This next step requires building a circuit which is connected to the main AC line. If you do not know the risks involved, please do not do it. Also, I cannot be held responsible for any malfunctions, electrocution, risk of fire..etc. using the following circuits.

There are several circuit built to drive AC loads out there, using a Triac and a MOC3021, you just have to decide which one. For my part, I have used the one suggested in the Motorola MOC datasheet, omitting the 0.05uF capacitor and the 470 ohm resistor, and the NAND gate. See modified diagram:

Be sure to use a non-zero crossing MOC3021, or the circuit will not work. Decide how many channels you want to build, and clone that circuit several times on a piece of perforated board. On the photos, I have built 5 channels on a 2” x 5” board, which fits nicely in an electrical wiring box for 3 regular receptacles (2 sockets per receptacle). Connect to the main AC line as indicated on the diagram, the HOT wire (black one) to the bus identified as such, as well as the NEUTRAL wire (white wire).

On the schematic, the LOAD box represent our LED string, the wire connected directly to the Triac pin goes on the live/hot (black wiring, smaller prong) of the socket, and identify this as the positive prong. The other prong (white, larger prong) is connected to the neutral side of the AC line. See photos in the next step:

Step 4: The AC Driver, Part 2

Also, only one socket is shown, in the picture above, and the AC main line input is not shown.

BE SURE TO CUT THE TAB CONNECTING THE TWO SCREWS ON EACH SIDE OF THE SOCKET, to ensure that they are both independent. Again, if you do not feel sure about this, get the help of a technician or electrician.

Each pin #1 of the MOC3021 are each connected to Arduino digital pins 4 to 13 (depending on how many drivers you built), through 330 ohms resistors. All cathodes of the MOC (pin 2) goes to the GND terminal of the Arduino. On the photos above, we only built a 5 channel board, which is connected on pins D13 to D9 of the Arduino. If you build a 10 channel version, build a second perforated board and connect the second board on pins D8 to D3.

Step 5: The Zero-crossing Circuit and Power Supply

The other part of the circuit that interfaces with the AC power line is the step down transformer, which provides power to the Arduino. It also provides a very important signal, the AC zero-crossing, on which the whole software algorithm and timing is based on, as you will find later. The transformer provides isolation as well, preventing any harmful AC line voltage to be on the Arduino pins. The transformer shown in the photo gives an AC output, it will not work if it has a DC output. The secondary side of the transformer (which provides about 9V AC) is connected to a classic 2 diode rectifier and capacitor circuit to generate about 13V DC, which is connected to the Vin pin of the Arduino. The secondary is also connected to a 2.7K : 1K divider, and this reduced voltage is available on analog pin A0. A 1N4004 diode is in parallel with the 1K divider to limit the negative voltage on the A0 pin. The main loop of the Arduino program polls this pin and sets flags to identify positive and negative cycles of the AC line, in order to fire the MOCs and Triacs at precise moments to control the intensity, and perform calculations for the special effects.

Step 6: Optional MSGEQ7 Circuit

(Optional audio-equalizer or color-organ circuit)

While building the Christmas lights, I thought it would be cool to control the intensity of the LEDs with music. This became an easy task with the MSGEQ7 chip, which contains 7 equalizers and interfaces with the Arduino by using only a few pins (Data OUT, pin 3 of the MSGEQ7 on analog pin A1 of the Arduino, STROBE, pin 4 and RESET, pin 7 on pins D3 and D2 of the Arduino, +5V and GND on the +5V and GND of the Arduino). There are also a few other components around the MSGEQ7 chip to connect the audio input and to get a clock signal. I followed the suggested wiring diagram from the manufacturer.

The software initialize the MSGEQ7 and specific variables, and the chip is read periodically in the Loop() section of the program. All 7 audio intensities are stored in the MSGEQ7spectrumValue array.

That's it for the hardware section, now let's move on to the software.

Step 7: Arduino Software, Part 1

The XMAS_light_2015_22patterns_with_MSGEQ7.ino file is available on GitHub, just look for it under my repository (

There are some variables to be initialized by the user, mainly the number of channels, or LED strings used, which in the example supplied, is 10. This is the N_drivers variable and the size of the channel array. Also the wait_for_50uSec() delay should be adjusted as well. Next is the number of patterns, in the How_many_patterns variable (set to 23).

Here is a brief overview of the program's main loop. Analog pin A0 is sampled using the A/D converter, until the beginning of the positive cycle of the AC line is discovered. The boolean variable positive_cycle is set true, for a period equal to one half of 1/60Hz, or about 8 mSec,. Then, it waits until the AC line voltage reaches approximately 60V, which is enough voltage for the LED string to just dim lightly. The drive_window boolean variable is then set, and will remain true for about 6mSec.

While the drive_window variable is true, the program scans the gate_trigger array (pre-programmed in the negative cycle, more on this later). When the trigger limit is reached, the software generates a 1mSec pulse on the proper Arduino pin connected to the MOC, actually turning on the LED string. The position in time of the pulse regulates the intensity of the string, if fired early, the string in turned-on during the whole positive crest, and it will be bright. If fired late, the string will be dim.

The 10 channels (in the code) are checked one at a time, every 50uSec. This is done 120 times, in the 6 mSec period. The result is 120 level of user-programmable intensities for each channel, all done in one half of a positive cycle, all by software and without using interrupts or PWM pins!

During the positive cycle, the MSGEQ7 is reset and made ready to be polled in the second half of the program corresponding to the negative cycle of the AC line. In this portion, no LED is turned-on, and the program sets the triggers and actions dictated by the pattern state-machine, which is a case-switch function with 23 actions (this is user-programmable).

Step 8: Arduino Software, Part 2

During the negative cycle, the MSGEQ7 registers are read, spurious noise is removed and values are normalized from 0 to 100, corresponding to sound level intensities at pre-programmed frequencies. Those intensities are stored in the MSGEQ7spectrumValue array.

Beside the gate_trigger array described above, there are many other arrays used to control the behavior of each LED string. Each string can be controlled to stay at a programmed intensity, or brighten up towards a maximum value, or dim down towards a minimum value, or go back and from two intensity levels. The following arrays holds the key parameters: ramp_down[], ramp_control[]

top_load[] and bottom_load[]. The speed at which these transitions happen is set in the cycle_rate[] array. And finally, cycle_begin[] and cycle_end[] dictates when the changes happens. The software does all this math during the negative cycle, as well as keeping track of the events in the pattern state-machine.

The different patterns programmed in the subroutines at the end of the program are: dim_up_and_down(), hold_it(), sparkle(), chenillard(), all_on(), turn_on() and the routines for the color-organ, MSGEQ7(). A few explanations: sparkle() uses random numbers to turn on or off any strings, at a specific rate and for a period of time. Chenillard turns on lights in a sequential fashion, on several patterns, either 1 through 10, 10 through 1, 1 to 10 then 10 to 1. All_on() controls all the lights at once, rather than specific lights in turn_on(). The MSGEQ7 routine extracts the intensity out of its dedicated array, and program the intensity of up to 7 different LED strings.

The program has a number of comments to explain what is happening, as well as commented statements used for debugging the code. With 10 strings of LEDs, it should work as-is with no modifications. If you want to experiment, look at the pattern sorter routine and change some of the values.

Step 9: Trying It and Troubleshooting

Now it's time to try it. Make the necessary changes in the hardware and software if you want less than 10 independent strings of LEDs. Upload the program in the Arduino, connect the strings of LEDs, positive prong (remember the one we identified with tape earlier) on the positive side of the socket (the one connected to the Triac pin). Verify one last time your electrical connections, especially everything connected to the AC line, then turn it on. The LED strings should turn on, as well as the Arduino power light. Should it not be the case, immediately remove the power, and check for the cause. If the Arduino light is not on, then check the power supply and the transformer. Measure voltages in reference to the GND pin, you should have about 13V on Vin and 5V on the 5V pin of the Arduino. If the Arduino light is lit, but the LED strings are not on or very dim, then it would be possible that your transformer is not in phase with the rest of the circuit: in this case, just reverse the wires on the primary side of the transformer. If nothing works at this point, have a friend (a technically oriented one) review your wiring. A quick test to check if the driver circuit works is to upload the Blink.ino program on the Arduino, and check if the LED connected on pin 13 blinks, then the LED string connected on pin 13 should blink as well. If this works, but the main program does not, then there must be a difference between your setup and mine. Read the instructions again and look at the program carefully. You may want to play with the threshold variable (currently at 200) by 25% either way if the LED strings are too dim or too bright.

Step 10: Conclusion

Well, that's it. I had fun doing this project and happy to share it with everyone. I welcome comments about code improvement or your own implementation.