Christmas Lights Controller
RaspberryPi based Christmas lights controller system using a Sainsmart 16-channel relay board.
I can't afford (nor would I pay if I could) the thousands of dollars it would take for a commercially-made Christmas lights control system. So, I built something super-simple instead.
- Run on a Raspberry Pi
- Use the cheap Sainsmart 16-channel relay board I found on Amazon
- Let me define the lighting sequences myself
- Get this done quickly and be ready for the current Christmas season (I started the original project in mid-October)
Things that are not goals:
- Syncing the show to music
- Having a slick GUI app to create the shows
- Control the relay / lights from a PC or Mac computer
Other projects that might better meet your goals:
In summary this is quick-n-dirty code and not really architected at all. Thar be dragons...
If you want to use this project for yourself, you will need the following:
- Raspberry Pi with a wireless adapter (I'm using the B+ since it has more than the 16 GPIO pins I needed for my board but other Pis would work)
- A relay board like listed above, 4, 8, or 16 channel boards will work
- A bunch of outlets, electrical boxes, wire, connectors, etc.
- A pack of female-to-female jumpers (you'll find a bunch of multipacks like this on Amazon)
- As many 1000 ohm resistors as you have relays
- Something waterproof to house the final product since it will sit outside for the holiday season. I used a large storage tub.
On your Raspberry Pi:
Set up your Raspberry Pi and make sure it is up-to-date with
sudo apt-get update && sudo apt-get dist-upgrade
Make sure you're running the latest version of Python with
sudo apt-get upgrade python3
Install the required Python modules:
sudo apt-get install python3-gpiozero python-gpiozero
Download this repo onto your Raspberry Pi
- Preferred method:
git clone email@example.com:skypanther/PiLit.git
- Or, download the zip, extract to a folder in your home directory
- Preferred method:
If you're using Python 2 or the older Node version of this project:
- Add the pi user to the gpio group:
sudo usermod -a -G gpio pi
- Run the following command to configure udev:
$ sudo cat >/etc/udev/rules.d/20-gpiomem.rules <<EOF SUBSYSTEM=="bcm2835-gpiomem", KERNEL=="gpiomem", GROUP="gpio", MODE="0660" EOF
(For more info on usermod and udev steps, see the rpio package docs)
- Add the pi user to the gpio group:
Once you have wired up your relay, update the
board_pinslist in the runshow.py file. (Update the
var pins=[...line in the index.js file for the older Node version of this project.) Make sure the pin numbers there correspond to the GPIO pins you used, in the order you used them.
There was a bunch of work that went into creating my hardware setup. I'm not going to cover how to wire up outlets, mount electrical boxes, etc. In general, you'll:
- Connect the ground wire of each outlet to a common ground for the entire setup
- Connect the hot side of each outlet to one lug of the relay
- Connect the hot side of the "mains" power (power in) to the other lug of each relay.
- You'll be connecting one jumper from each GPIO pin to the pins on the Saismart relay board. You'll want to wire in a 1k ohm resister in line with each too. You'll connect a ground jumper, without resister, too.
When you examine your relay board, you'll notice that the ones on the right are mounted 180° from the ones on the left. As you'll see, I attached the white/black wires to the top two lugs on each relay on the left, and the bottom two lugs on the right relays. You could do that opposite (bottom two on left, top two on right) but you do need to wire them in opposites like this.
I mounted four double-gang boxes, to hold 8 outlets, into a small sheet of plywood. I broke the tabs between the top & bottom outlet in each so that I could wire up the 16 receptacles separately. You'll see I have them numbered 1-16 in the above. I have one additional outlet into which I can plug the Raspberry Pi and Sainsmart board (yeah, I know I could feed them both power off the same wallpack).
I put the board into a plastic storage tub. I cut a flap in the side. All the outlets are wired up to short power cords that I plug into extension cords run across the yard to my garage. Those three cords, plus the 16 for the Christmas lights are fed through the flap in the side. This makes the setup pretty reasonably water/snow proof. Despite some howling winds and heavy snows, I had no water or snow get inside the box.
I found it helpful to put small tape flags on each of the jumper wires that ran from the Pi to the relay board. These I numbered with the outlet number.
- RPi pinout - http://pinout.xyz/ (don't connect a relay to the TXD pin, e.g. pin 8 on the Pi B+, as it will be flashed on/off very rapidly when the Pi boots, which could burn out your relay)
- I found it helpful to go through https://docs.google.com/document/d/1x97JIu5xVInZMutTNeaHlnQuyoLHjf3h-ugIo64pGfI/edit to set up and test my RPi. You could use it for off-season testing of your show, etc.
- Sainsmart relay board manual (community contributed) http://www.homebrewtalk.com/showthread.php?t=523263 which says about the fastest you can switch the relays on/off is roughly once per second. However, I've seen 10 ms (1/100th of a second) referenced elsewhere. I would stick to slower than 100ms so you don't wear out the relays too quickly.
The following steps could be done on any computer, not necessarily on your Raspberry Pi light show controller. If you do this on your desktop, you will need to have done steps 1 & 2 of the Software Setup above.
(I have not yet updated these steps to use Python, so you'll need a working Node setup. Or, see the note that follows.)
- Generate a show: run
node generateshow.jsand follow the prompts.
- Create the lighting sequences: run
node sequencer.jsand follow the prompts.
- Test a show: run
node testshow.jsand follow the prompt
- Test each relay in sequence (e.g. to make sure you have things wired correctly): run
To be honest, the generation scripts never worked out as I hoped. In practice, I've just manually edited the JSON files in a text editor (e.g. Sublime Text or Visual Studio Code). My plan is to write new generation scripts but I might not finish by Christmas this year.
Testing a show
There are a couple of scripts for testing your shows. The Node one makes pretty colored boxes while the Python version just outputs some grey ASCII box characters. But, either will work:
node testshow.jsthen choose the show to test
python3 testshow.py myshow.json
node testrelays.js script just loops through turning each of the relays on/off in sequence. It's useful for making sure you have the jumper pins connected correctly.
Running a show
However, you must run the show from the Raspberry Pi.
- On the Rasbperry Pi, make sure you've downloaded and installed this software package as described above
- If you created your sequence on a different computer, transfer the show file from the shows folder to your RPi's
- Then, in the project directory:
- The Python show runner is
runshow.py. Use it like this:
python3 runshow.py show.json HH:MMwhere HH:MM is a 24-hr time, such as 22:30 (10:30 pm).
- The older Node show runnier is
index.js, run it with
node index.js myshow.json 3.5which will run myshow.json and stop it after three-and-a-half hours
- The Python show runner is
Both the Python and Node runners also accept a couple of special "show names":
python3 runshow.py onand
python3 runshow.py offto turn on, or off, all the relays
node index.js onand
node index.js offto do the same
You can automate running this every night by using cron. On your Pi, run
crontab -e and choose your favorite editor (if necessary). Let's say you want to start your show every night at 5:30 pm. You would add this to the bottom of the file:
# start the light show at 5:30 pm every day 30 17 * * * cd /home/pi/PiLit && python3 runshow.py myshow.json 23:30 31 22 * * * cd /home/pi/PiLit && python3 runshow.py off
(The 30 and 17 represent 30 minutes past the hour of 17, aka 5pm. The asterisks mean every day of every week of every month. Then come the command(s) to run.)
Or for Node:
# start the light show at 5:30 pm every day 30 17 * * * cd /home/pi/PiLit && /usr/local/bin/node index.js myshow.json 5.5 31 22 * * * cd /home/pi/PiLit && /usr/local/bin/node index.js off
2017 Christmas Notes
The system worked fine again for me in 2017. I didn't make any meaningful changes to the hardware or software in 2017. I hand-edited the show JSON file from the previous year to create the new show.
2016 Christmas Notes
2016 was my first year using the
clc system (as I called it then). Overall, I'm very happy with the results. Well, except the Raspberry Pi would crash after a couple of hours. I've rewritten the
index.js file since then on the theory that I had a slow memory leak that eventually caused the Pi to crash. Also, my show generation scripts proved to be rather unusable. I ended up generating a 15-min show file then hand-editing it.