Xmas-box: Arduino/ioBridge Internet Controlled Christmas Lights and Music Show




Introduction: Xmas-box: Arduino/ioBridge Internet Controlled Christmas Lights and Music Show

My xmas-box project consists of an internet controlled Christmas lights and music show. A Christmas song can be requested on-line which is then put in a queue and played in the order it was requested. The music is transmitted on an FM station within a 300 ft radius from my house. 

The xmas-box has 8 Channels  (power outlets) where different light modes can be played: vu meter style, ascending, descending, split, merge, sequence and random. During each song one of these modes is used randomly every 10 seconds (to make the show less monotonous).

I started my research right after Halloween and I came across a couple different options, but I settled with the following combination of hardware : arduino + adafruit wave shield + ioBridge + wifi bridge + solid state relays (SSRs).

The xmas-box is enclosed in a small plastic tool box. I have place it on my deck under a roof ( it is not completely weather proof). The tool box has "3 levels." The bottom is where all the SSRs and AC wiring are located. The middle (the inside tray) contains the wall warts for the arduino (9v), ioBridge (5v) and Wifi Bridge with power. The top level contains the Arduino board, the ioBridge module and the FM transmitter.

This is the first time I lit my house so I was just able to put 3,300 mini lights, 3 spotlights,1 LED Rope, 4 LED (40 led each) branch trees and 1 reindeer. I hope the lights last so I can keep adding each year.

Step 1: BoM - Bill of Materials

Here is the list of materials I used for the xmas-box:

Step 2: Relay Setup

You will be dealing with AC power. If you don't feel comfortable or you are not sure how to handle AC, please consult a licensed electrician before starting to wire the relays.

A great advantage of using SSRs instead of mechanical relays is that I did not have to add any diodes, transistors, or resistors. Also since there there is no mechanical dependency the switching will be smoother. I would advise to use a breadboard with 1k resistors and different color LEDs during your prototyping. Then when you are ready you can use the SSRs and real Christmas lights.

The connections are pretty straight forward. I daisy chained the ground coming from the Arduino to each of the 8 SSRs. Then 8 digital outputs from the Arduino were connected to each positive of the 8 SSRs.

I gutted the PowerSquid Power Multiplier and removed the 10 outgoing outlets (5 per PowerSquid) as well as the AC plugs. The 5 outgoing outlets came nicely attached together so I cut one from each that used to power the arduino, ioBridge and Wifi bridge.

I connected all the Neutrals (white) together, all the Grounds (green) and then I took the Live (black) and daisy chained to each of the 8 SSRs. Then I individually wired each SSR Live wires to one of the 8 outlets.

I decided to have two power plugs to make the wiring a bit nicer and less bulky. So basically the SSRs on the left are connected to one wall outlet and the SSRs on the right to the other.

Step 3: Arduino Sketch

You can download the Arduino Sketch from the bottom of this page (xmas_box.pde).
 You will need the following Arduino Libraries:

- AF_Wave and Wave
- String (formerly TextString)

Here is a run down for the pin connections on the Arduino with WAVE shield on top.

D0(RX) -> ioBridge Serial Board TX
D1(TX )-> ioBridge Serial Board RX

D2-D5 are used by the WAVE shield (they could be changed)
D2 -> LCS
D3 -> CLK
D4 -> DI
D5 -> LAT

xmas-box first 3 channels
D6 -> Channel 1
D7 -> Channel 2
D8 -> Channel 3

FM transmitter ON/OFF
D9 -> 10k resistor -> 2N2222 Base -- Collector and Emitter to FM transmitter switch

WAVE shield
D10 -> CCS

SD card WAVE shield communication (cannot be changed)

Gnd[0] -> Relay daisy chain.
5v pin -> FM transmitter positive
Gnd[1] -> FM transmitter ground
Gnd[2] -> ioBridge Smart Serial Board

Vu Meter
A0 -> R7 1.5K on the WAVE shield to measure output from amplifier. See image.

Analog pins 1-5 are used as digital out for xmas-box channels
A1 = D15 -> Channel 4
A2 = D16 -> Channel 5
A3 = D17 -> Channel 6
A4 = D18 -> Channel 7
A5 = D19 -> Channel 8

Wave shield speaker (mono) to FM transmitter input

Step 4: IoBridge Configuration

The ioBridge configuration is a breeze! All you have to do is Serial.print the current track number being played to the ioBridge Serial Board. The serial board will hold the track number and have it available to be retrieved by the Data Feed API in JSON format (LastSerialOutput).
I also created a series of widgets with predefined xmas-box commands for a one-click input.

Here is the syntax to communicate with the xmas-box through serial communication.  The semi-colon indicates the end of the command.

Next track
Put on queue
Put on queuewith specific light mode
Change light mode
Turn all lights on/off
Turn individual channel on/off

Step 5: FM Transmitter

I was hoping to find a Belkin TuneCast II since I have found a lot of information online (including this instructable) but I had to settle with the PPA Digital FM Transmitter and it worked out pretty well! The only modifications I had to do were to desolder the stereo mini plug and connect 2 wires coming from the WAVE shield speaker output and desolder the mini barrel plug to connect power directly from the Arduino.

I'm powering the FM transmitter with the 5v coming out from the Arduino. This would probably not be a good idea if I was powering the Arduino with a 9v battery but since I'm using a wall wart I'm not too concerned.

I am bypassing the ON/OFF switch by connecting a 2N2222 transistor connected to a 10k resistor coming from a digital output from the Arduino. To turn on I just send a fast HIGH/delay/LOW and that's all I need to start the FM transmitter.

I also soldered a wire as an antenna to the ground of the battery power pin.

Step 6: Web Interface

I decided to leave the job of song queuing to a web application. I am using Oracle Application Express (APEX) for http://xmas-box.com since this is my favorite rapid web application development tool. But this can also be managed by any database oriented web solution (ie  Linux/Apache/MySQL/PHP -  LAMP).

I have 2 main tables:
SONGS - It stores the name and artist of the song
QUEUE - it stores the song requests

The web app uses jQuery to retrieve the ioBridge JSON Data Feeed API to determine the current song being played. Using Javascript I retrieve LastSerialOutput node that holds the number of the current track.

Then another jQuery calls my web application REST service and sends the track number. The REST service returns the song name,artists and name of the person that requested the song.

If the song was not on the queue it means that its just looping through the play list. So in this case the requester will be Santa Claus itself! And it will show Santa's current location by retrieving it from http://www.noradsanta.org/ feed.

The page might have a live view and live sound, but I'm still working on that...

Step 7: Alternatives

During my research I stumbled upon a few mentionable resources. My requirements became more tight since I wanted a computer-free solution, a stand-alone application and that is why I went with the WAVE shield . Another possible way is to get a cheap mp3 player and build an op amp low filter to detect beats on the music, but then again the WAVE shield is actually pretty inexpensive so it worked out better.

Beat detection
One of the easiest ways to do an Arduino based light/sound show is by using a combination of Proccessing + Minim+ Firmata. All you need is to have serial communication from your pc to your arduino.  These instructables rely on Minim's beat detection and could be adapted for Christmas lights:
LED Dance Room
Music Synchronized LED Pumpkin

Relay board fabrication

You can follow the Arduino Christmas Light Controller Instructable if you prefer to build your own relay board. Note that this will be using mechanical relays instead of SSRs.

Internet communication
Another alternative for the web control part is to have a secondary Arduino with a Ethernet shield or a wi-fi shield (it will be absolutely necessary to have a second Arduino board). But as a side note I really like using the ioBridge module. Their servers manage all incoming/outgoing connections and the Data Feeds and APIs offers great flexibility.

Commercial/Hobby products
diylightanimation.com (Linx)
animatedlighting.com (they got a Christmas In A Box 4 channel for $500!!)

Homemade Holidays Contest

Participated in the
Homemade Holidays Contest

Be the First to Share


    • Remote Control Contest

      Remote Control Contest
    • Colors of the Rainbow Contest

      Colors of the Rainbow Contest
    • Make It Modular: Student Design Challenge

      Make It Modular: Student Design Challenge



    4 years ago

    There is an age old story that we all have heard as kids- where in the Santa lived in the North Pole and during Christmas, he rides on his sled and flies in the houses of the “well-behaved” kids to fill them up with presents.

    But do you think that Santa exists? Or was it all a farce? Where is Santa?


    9 years ago on Introduction

    I'm looking to buy those SSRs for this project. What load current should I buy?
    They come in 10A, 25A ... some in 3-25mA. range.


    9 years ago on Introduction

    Hi, I'm a newbie and want to follow this project. I plan to start with a simplified version by just using SSRs & Arduino Uno board. 
    - wire up the SSRs like in here: https://www.instructables.com/files/deriv/FZ6/9Y65/GGPFB9Y3/FZ69Y65GGPFB9Y3.LARGE.jpg
    - connect the SSR wiring with the Arduino Uno board (using D2 - D9)
    - connect the Arduino board with a PC and load the code to test the relay.

    Would that be enough to allow me to (learn to) program the code to sequence of relays on/off? (in other words, program the light to the music).



    9 years ago on Step 2

    By the way, this phenomenon is usually defined as current leakage but I'm surprised it's that much. Most of what I've read says to put a varistor in between the AC contacts to provide enough resistance to keep the lights off at open state.


    Reply 9 years ago on Step 2

    Here's a SSR that has only .1 mA of off-state leakage.


    That should be good enough to keep the lights from lighting up since most LED light strings require about .2 mA to get them to light up at all.


    9 years ago on Introduction

    Has anyone ported the xmas_box code using the new wave_hc library?


    9 years ago on Step 2

    Question about the SSRs. I'm using they Crydom D2410 SSR and have it wired as you have shown above. It is showing about a .3 mA current leakage at the off state. That's enough to light the string of LED lights, although they are dim. Did you experience this problem?


    9 years ago on Introduction

    can i just use a direct ethernet connection instead of a wireless bridge??


    11 years ago on Step 4

    How do you serial.print the current track being played to the iobridge serial board? I have never used any of this hardware before, so I'm not sure where to begin.

    Can you provide more detail on how to set up the iobridge web interface, or provide a link that explains how to create widgets? Where are the commands provided above entered? Is this code stored in the iobridge web interface?

    Thanks in advance for any additional info.


    11 years ago on Step 3

    When I try to upload the sketch to my Arduino, I get multiple error messages:

    xmas_box.cpp: In function 'void playComplete(char*)':
    xmas_box:167: error: 'class String' has no member named 'append'
    xmas_box:196: error: 'class String' has no member named 'append'
    xmas_box:198: error: 'class String' has no member named 'append'
    xmas_box:199: error: 'class String' has no member named 'append'
    xmas_box:200: error: cannot convert 'String' to 'char*' in assignment

    I'm using arduino-0021 which supposedly has string class incorporated in it. Do I need to modify the code? If so, please tell me how.


    Reply 11 years ago on Introduction

    I'm using arduino 0017, so maybe that is the issue. For the error you get it is related to the String library. I wonder if there is a new lib?
    Let me see if I found something.


    Reply 11 years ago on Introduction

    Hi Noel,

    I replaced all the "append" references with "concat" in your code, and that seemed to eliminate the errors: "no member named 'append'".

    However, I am still getting the following error:

    xmas_box.cpp: In function 'void playComplete(char*)':
    xmas_box:201: error: cannot convert 'String' to 'char*' in assignment

    The line that the compiler stops at is:

    track = tmpTrack;

    I'm sorry to be posting so many questions, but I really want to get this thing working for my lights this year, and I am clueless as to what this error has to do with the above line of code. Do you have any ideas?

    I feel like I might have bitten off more than I can chew with this project, because my understanding of computer code is almost non existent, which is forcing me to try to fix the code by trial and error. I can't figure out why I would be the only one getting errors. From the other posts, I assume other people have more recently built the xmas box and used the Arduino 0021 compiler, but maybe not. I'm not opposed to using 0017, but even with that compiler I get errors because I can't download the same string library you used.

    If you wouldn't mind replying just to let me know you are reading my posts I would appreciate it, even if you don't know how to help me yet. I will try picking this project up again at a later date if necessary when I have some programming under my belt. I've already invested in all the materials and partially assembled the instructable though, so I'm determined to complete it eventually, even if I have to wait 'til next year to get to use it.

    Thanks in advance.


    Reply 11 years ago on Introduction

    @phorensyc I think you are almost there...Obviously there is an issue with using Strings or Chars. I havent fired up my code this year... I will upgrade to arduino 0021 and compile it...I will use the x-mas box in my christmas tree this year.
    Keep up at it...Try the Arduino forums as well...I will let you know if i run in the same issue and how i solve it.



    Reply 11 years ago on Introduction

    Hi Noel, I fixed the final error. I posted my solution on your instructable. One other question though:

    The instructable says to use pin connection D10 for LCS. It looks like from the picture that D10 should be connected to CCS. Is this correct?


    Reply 11 years ago on Introduction

    I was able to solve the final error. I did a lot of searching, and don't fully understand all the changes that have taken place with the string libraries since you published this instructable, but the issue seemed to be that the variable track was declared as a char, and tmpTrack is a string, and the new string library can only convert a string to a char with the function toCharArray()

    The link for the string library you have in your instructable only takes you to a page that tells you that the TextString library is now obsolete. String libraries are now included natively with the code compiler.

    The instructions for the use of toCharArray() on the Arduino site are limited and some say a bit misleading, so I'll just share what I changed to make the program compile and load with the new string library.

    Okay, I seem to have fixed it, or at least changed it to where the code compiles and loads on the Arduino. I replaced the following line of code:


    with this:

    char tracklngth[tmpTrack.length() + 1];
    tmpTrack.toCharArray(tracklngth, tmpTrack.length() + 1);
    track = tracklngth;

    Sorry if this is an abomination to all seasoned programmers out there, but it's the best I could come up with. I'm sure it could have been done in one or two lines, but I'm in a hurry to get the xmas_box working.


    11 years ago on Introduction

    Is it possible to fade any of the lights? Maybe with an AC dimmer or something. Very cool setup, will try this christmas.


    Reply 11 years ago on Introduction


    DIMMERS 120V 60Hz (8-channel Possibly more)

    by Gromain59
    Translated By Mike Deuschle

    Material part:
    - Triac driven by a digital output via an optocoupler
    - AC opto-coupler for detecting the zero crossing of phase

    Software part:
    - A hardware interrupt input 2 at the zero crossing of phase
    - A software interrupt that occurs between 100us and 1400us.
         => Interrupt interval is variable to obtain a light curve by linear orders, because of the shape of the sinusoidal signal.
    we have:
    1. Detection of the transition to zero on input 2
    2. execution of detection_zero (): processing channel with a setpoint of 0% and 100%
    3. deactivating hardware interrupt, enabling the software interrupt on the basis of delay [0]
    4. interrupt after delay [c2] ?s (c2 = 0)
    5. execution of controle_canaux ()
         => Index increment c2
         and if c2 is greater than 49, then this is the last cycle
               => Turn OFF of all channels
               => Activate the hardware interrupt
               => Activation of output channels with 98% to record (either a 469?s delay) or if
               => Interrupt reconfiguration of time with another delay, delay [c2]

    To change the setpoint of a channel, you must send via the serial monitor a frame of the form:
               " D/0/45/F"
         => Space
         => "D" to indicate the start of the frame
         => "/" As separator
         => The affected channel (0 to 7 here)
         => "/" As separator
         => The desired level (from 0% to 100%)
         => "/" As separator
         => "F" to indicate the end of the frame

         Once the frame received, the function sscanf is responsible for retrieving data.
         It converts the received record levels (0 to 50 levels)

    http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1230333861/30 for first test
    http://www.hoelscher-hi.de/hendrik/english/dimmer.htm for electronics
    http://www.abcelectronique.com/bigonoff/domocan.php?par=3efd4 for electronics and soft (PIC)
    arduino forum


    #include <TimerOne.h> // for the interruption time http://www.arduino.cc/playground/Code/Timer1
    #include <stdio.h> // for the treatment of the frame containing the change orders

                         // timeout value for the reception of the frame
    int tps_max_lecture = 200; // reading code, counter max between all the characters of a code
    int tps_max_carte = 1000; // max meter between reception of a character

    long curve[] = {
    1469 , // 98 % 1 225,3V retard / zéro = 1469 ms
    287 , // 96 % 2 222,7V retard / zéro = 1867 ms
    234 , // 94 % 3 220,6V retard / zéro = 2154 ms
    201 , // 92 % 4 218,2V retard / zéro = 2388 ms
    180 , // 90 % 5 215,4V retard / zéro = 2589 ms
    164 , // 88 % 6 213,3V retard / zéro = 2769 ms
    152 , // 86 % 7 210,8V retard / zéro = 2933 ms
    143 , // 84 % 8 208V retard / zéro = 3085 ms
    135 , // 82 % 9 205,7V retard / zéro = 3228 ms
    129 , // 80 % 10 202,8V retard / zéro = 3363 ms
    124 , // 78 % 11 200,5V retard / zéro = 3492 ms
    120 , // 76 % 12 197,6V retard / zéro = 3616 ms
    116 , // 74 % 13 195,2V retard / zéro = 3736 ms
    112 , // 72 % 14 192,4V retard / zéro = 3852 ms
    110 , // 70 % 15 189,6V retard / zéro = 3964 ms
    108 , // 68 % 16 186,8V retard / zéro = 4074 ms
    106 , // 66 % 17 184V retard / zéro = 4182 ms
    105 , // 64 % 18 180,9V retard / zéro = 4288 ms
    103 , // 62 % 19 178,1V retard / zéro = 4393 ms
    102 , // 60 % 20 175,1V retard / zéro = 4496 ms
    101 , // 58 % 21 172,1V retard / zéro = 4598 ms
    101 , // 56 % 22 168,9V retard / zéro = 4699 ms
    100 , // 54 % 23 166,2V retard / zéro = 4800 ms
    100 , // 52 % 24 162,6V retard / zéro = 4900 ms
    100 , // 50 % 25 159,3V retard / zéro = 5000 ms
    101 , // 48 % 26 155,8V retard / zéro = 5100 ms
    100 , // 46 % 27 152,6V retard / zéro = 5201 ms
    101 , // 44 % 28 149,1V retard / zéro = 5301 ms
    102 , // 42 % 29 145,3V retard / zéro = 5402 ms
    103 , // 40 % 30 141,8V retard / zéro = 5504 ms
    105 , // 38 % 31 138V retard / zéro = 5607 ms
    106 , // 36 % 32 133,8V retard / zéro = 5712 ms
    108 , // 34 % 33 130V retard / zéro = 5818 ms
    110 , // 32 % 34 126V retard / zéro = 5926 ms
    112 , // 30 % 35 121,7V retard / zéro = 6036 ms
    116 , // 28 % 36 117,1V retard / zéro = 6148 ms
    120 , // 26 % 37 112,6V retard / zéro = 6264 ms
    124 , // 24 % 38 107,7V retard / zéro = 6384 ms
    129 , // 22 % 39 102,4V retard / zéro = 6508 ms
    135 , // 20 % 40 97,2V retard / zéro = 6637 ms
    143 , // 18 % 41 92V retard / zéro = 6772 ms
    152 , // 16 % 42 85,7V retard / zéro = 6915 ms
    164 , // 14 % 43 79,4V retard / zéro = 7067 ms
    180 , // 12 % 44 72,8V retard / zéro = 7231 ms
    201 , // 10 % 45 64,8V retard / zéro = 7411 ms
    234 , // 8 % 46 56,4V retard / zéro = 7612 ms
    286 , // 6 % 47 46V retard / zéro = 7846 ms
    399 , // 4 % 48 32,4V retard / zéro = 8132 ms
    500 , //
    1469 // 2 % 49 0V retard / zéro = 8531 ms

    int set[] = { // set channel level (0 = 100%, 50 = 0%)
    0, // Output 0
    0, // output 1
    0, // output 2
    0, // output 3
    0, // output 4
    0, // output 5
    0, // output 6
    0, // output 7

    int output [] = { // assign a pin for each channel.
    4, // Output 0
    3, // output 1
    5, // output 2
    0, // output 3
    0, // output 4
    0, // output 5
    0, // output 6
    0, // output 7

    volatile int c1 = 0; // index c1 for reading data from each channel (No pin, luggage)
    volatile int c2 = 0; // c2 index number passing through the loop control phase delay (49 passages)

    // Definition of macros to drive the output
    #define lightON(index) (digitalWrite(output[index], HIGH))
    #define lightOFF(index) (digitalWrite(output[index], LOW))

    void setup () {// Start of setup

    // Initialize the serial
    Serial.begin (9600);

    // Initialize the channel outputs (triacs)
         for (c1 = 0; c1 <= 7; c1++) {// we traverse the 8 channels to configure
                     pinMode(output[c1], OUTPUT); // we associate each channel has a pin, which sets the output digital
                     lightOFF(output[c1]); // and we switch off the output

         Serial.println( "Gromain 8-CHANNEL DIMMER v0.2");
         Serial.println( "FRAME EXPECTED: <space> 'D' / 'Output Port' / 'Value of DIM' / 'F'");

    // Initialize the interruption time Timer1
         Timer1.initialize(); // Initialize TimerOne library for the freq we need

    // Attach the interrupt 0 to pin 2 for the detection of zero crossing (Zero Cross Detection)
         attachInterrupt(0, detection_zero, FALLING); // Attach an Interrupt to Pin 2 (Interrupt 0) for Zero Cross Detection

    } // End of setup

    void detection_zero() {// function associated with the interrupt 0

         detachInterrupt(0); // disables the interrupt on zero crossing

         c2 = 0;

         for (c1 = 0; c1 <= 7; c1++) {// we scan the 8 outputs to check their orders
                 if (set[c1] >= 49 ) {// if set 0%
                     lightOFF(c1); // then we switch off

               if (set[c1]<= 0){// if set 100%
                     lightON(c1); // then we light


         Timer1.attachInterrupt(controle_canaux, curve[c2]); // we attach the interruption time

    } // End of detection_zero

    void controle_canaux() {// here we verified whether the triac must be initiated


         attachInterrupt(0, detection_zero, FALLING); // we attach an interrupt on pin 2 (interrupt 0)
         Timer1.detachInterrupt(); // we detach the interruption time

         if (c2 >= 41) {// If last cycle then (best at 41 for 60Hz)

         for (c1 = 0; c1 <= 7; c1++) {// we scan the 8 outputs
               lightOFF(c1); // and we put out the channel for the next cycle

    else { // else

         Timer1.attachInterrupt(controle_canaux, curve[c2]); // we attach a break time

         for (c1 = 0; c1 <= 7; c1++) { // we scan the 8 outputs to check their orders
                 if (set[c1] == c2) // if is set equal to the processed (no change in the loop)
               {lightON(c1);} // then we light the channel

    } // End function controle_canaux

    void loop() {// Main Loop

         int n = 0;

         if (Serial.available ()> 0) {
               n = lecture();


    int lecture() {// read a frame type: "D / aaa / bbb / F
                                 // Or "D" starting character frame
                                 // Or "yyyy" No output which is set to modify
                                 // Or "bbbb" new set of output (between 0 and 100%)

         char buf[15] = " ";
         int timeout = 0;
         int i = 0;
         int n1 = 0;
         int n2 = 0;
         char c1, c2;

         while (Serial.available() > 0) {
               buf[i] = Serial.read ();


         if (timeout>tps_max_lecture)
               return -1;
         if (timeout> tps_max_lecture)
               return -2;
         sscanf(buf, "%c/%d/%d/%c", &c1, &n1, &n2, &c2); // decoding frame

         if (c1 == 'D' && c2 == 'F') {// Check if the plot starts out by D and ending in F

               int nouv_cons = n2; // we store the new value for the work then

               nouv_cons = constrain(nouv_cons, 0, 100); // on the new terminal value between 0 and 100%

               Serial.print("Output ");
               Serial.print(" , new value of: ");
               Serial.print(" % index, delay: ");

               set[n1] = (50 - (nouv_cons / 2)); // it converts the value 0-100% in no phase delay

               Serial.println (set[n1]);
         else // if character from the beginning or end of frame not recognized
         {Serial.println("Code Unknown");}

         return i;


    AC_control2.pngAC dimmer.png

    Reply 11 years ago on Introduction

    Sorry this is to control and dim 8 channels...Sorry for the length of the post. https://www.instructables.com/id/Christmas-Lights-to-Music-Using-Arduino/ here on how to build one using triac and an octupocoupler (basically an SSR).