Introduction: Wireless Christmas Light Timer With Raspberry Pi and Python

About: Writer for Science Buddies (www.sciencebuddies.org) and lecturer at Cornell University's Sibley School of Mechanical and Aerospace Engineering.
Update 1/14/2014: Thanks to everyone who voted for this project in the Hardware Hacking Contest! Looking forward to my new maker pen.

Update 12/31/2013: looking for a more advanced version of this project? Check out my new multi-channel voice-controlled version.


We all know you could just go BUY an electrical outlet timer instead of building your own, but where's the fun in that? This project will show you how to hack a $10 wireless outlet adapter so you can control it with a Raspberry Pi and a simple Python script, with the motivation of controlling Christmas lights - but it will work for any device you can plug into an electrical outlet.

I should acknowledge right away that this is by no means the first (nor the last) project that will discuss home automation or controlling electrical outlets with a Raspberry Pi. My goal is for this to be a very beginner-friendly project - the code will run locally on your Raspberry Pi (no internet control), the circuit will only control one outlet, and you won't be touching any high-voltage wires. If you're ready to tackle something more advanced, check out these Instructables on web-enabled multiple outlet control from hacking a multi-channel remote or building your own power strip. This project will still require use of a multimeter and a soldering iron, so it does require some experience with electronics, or someone who can help.

I also hope to provide some helpful pointers for Raspberry Pi newbies (this is my first RPi project, and I'm more familiar with Arduino, so I hit a few bumps getting started), but please note that this is NOT a "Getting Started with Raspberry Pi" tutorial - I'd recommend their official Quick Start Guide and this wiki resource for that, and I will assume you already have a Raspberry Pi up and running with Raspbian.

Now, on to a materials list. Disclaimer: you may notice that I'm partial to Amazon and Sparkfun, but of course you can shop around and try other vendors like Adafruit, Radio Shack, Ebay, etc. Costs are approximate as of December 2013.

Materials
  • Raspberry Pi Model B ($40)
    • Monitor or TV and appropriate cable. I have an old Acer Model AL1717 B VGA/DVI compatible monitor ($50 at a recycling center) and an Amazon Basics HDMI to DVI adapter ($8) cable.
    • 5V micro-USB power supply. I have this one ($6).
    • USB keyboard and mouse. I have a Logitech K400 wireless keyboard/mouse combo ($35).
    • SD card, at least 4GB, with an operating system installed. I have an 8GB card ($9) and I followed directions to install the raw image of Raspbian, available here.
    • Ethernet cable or USB wi-fi adapter. I have an ethernet cable plugged straight into my router. Note that the Raspberry Pi only has two USB ports, so you will need a USB hub if you use a mouse/keyboard combo that requires two ports, and also want wi-fi.
  • Circuit components
    • Two single-pole, double-throw (SPDT) relays, available from SparkFun ($2 each).
    • Two N-channel MOSFETs, available from SparkFun ($1 each)
    • Male-female jumper cables. 10-pack available from SparkFun ($4). These cables have a female end which allows you to connect to the Raspberry Pi's (male) GPIO (General Purpose Input/Output) pins, and a male end that can be pushed into a solderless breadboard.
    • Male-male jumper cables or 22 AWG solid-core hookup wire for making breadboard connections. I prefer using hookup wire because you can cut the wires to a shorter length and your circuit is less messy, but either will work. You will need about 15 connections for the project, so a single 10-pack of wires is not enough. SparkFun has a variety of jumper wires available, like premium wire ($4 for 10-pack), or standard wire ($5 for 30-pack). They also have a variety of colors of solid-core hookup wire ($3 per spool).
    • Solderless breadboard. I have this one from SparkFun ($5) but there are tons of breadboard options out there. You could also use perf board if you know what you're doing and want to solder a permanent circuit.
  • Hardware
    • Wireless outlet adapter with remote control. I used a "Utilitech Outdoor Wireless Outlet", part number 0357410, that I bought in-person at a Lowe's ($10). Unfortunately I am having a hard time finding a link to that exact product anywhere online. You should be able to follow my procedure using any single-channel remote with separate ON and OFF buttons (you could adapt the instructions to work for a remote with a single toggle on/off button, or multiple channels, but I won't provide those directions). For example, this one from Home Depot or this one from Amazon.
    • Christmas lights of your choice (price varies). Remember to pay attention to the labels about how many strands you can plug into a single outlet.
  • Tools
Total Cost

The total cost of this project will vary quite a bit depending on what you already have available. Here are rough totals for the sub-sections above:

Raspberry Pi: $150
Circuit: $23
Hardware: $20
Tools: $77

So, if you already have a Raspberry Pi setup, an electronics workbench with a soldering iron, spare breadboards and jumper wires etc., and some Christmas lights, this project will be pretty cheap (probably under $20). If you're literally buying everything brand-new, it's nearly $300. But of course, all of those tools will be good investments for other projects in the future - hopefully you wouldn't buy them just for this!

Now, on to the next step to get started cracking open and hacking your wireless remote.

Step 1: Open the Remote Case and Remove the Circuit Board

First you'll need to open up your wireless outlet and crack open the remote. Here are the steps I followed - you may have to improvise if you couldn't find the exact same model remote. Your goal is just to get the circuit board out intact. It's OK if you have to break some of the plastic case to do this.

1. Remove the battery cover and battery.
2. Pry off the little keychain attachment.
3. Remove screw holding on the back cover.
4. Pop off the back cover.
5. Remove the circuit board.

Step 2: Reverse-engineer the Circuit: Part 1

This is probably the most difficult (and possibly intimidating) part of the project - figuring out how the circuit works. Don't feel overwhelmed though - you DON'T need to understand what every single component in the circuit does. Your goal is just to figure out what the on and off buttons do, so you can use the Raspberry Pi to "trick" the remote into thinking those buttons are being pressed.

So, what's going on when we look at the circuit board? The first two images, showing side-by-side pictures of both sides of the board, give you a rough idea of the things we care about. There's a battery, which has positive and negative terminals, connected to the board. There are two pushbutton switches, labeled SW1 and SW2 on the circuit board, corresponding to the ON and OFF buttons on the exterior plastic case, respectively. Each one of these switches has four pins connected to the circuit board, labeled in the second image above. So, we want to figure out what these buttons do - a good way to start is to figure out what the voltages at these pins are when the buttons are pushed.

This is where your multimeter will come in handy. If you need help using a multimeter, check out this tutorial from SparkFun or this one from Science Buddies (full disclosure: I work for Science Buddies and I wrote that tutorial). You'll want to attach your multimeter's black probe to the negative battery terminal (this is where I find those alligator clips very handy). Then, use the red probe to measure the voltage at EACH pin when:
  • Nothing is pressed
  • The ON button is pressed (SW1)
  • The OFF button is pressed (SW2)
The last three pictures above show the results of my measurements. It looks like, when nothing is pressed, all of the pins sit at either 12V or 0V. However, when either button is pressed, some of the 0V pins go up to 4.5V (and some of the 12V pins also go down to 4.5V). What does that tell us? It means that the remote is probably using digital logic, where a 5V signal is considered "high" (or TRUE) and a 0V signal is considered "low" (or FALSE). If you aren't familiar with digital logic, check out this SparkFun resource on logic levels or the Wikipedia article.

So, now we know how the voltages of the button pins change when they are pushed. The next step is to figure out which of those pins are connected to inputs on the chip - the big black rectangle that acts like the "brains" of the circuit.

Step 3: Reverse-engineer the Circuit: Part 2

Next you need to analyze the chip's connection to the circuit. The chip in my remote is in a dual in-line package (DIP), meaning it has two parallel rows of pins. The first image above highlights all of these pins on the bottom of the board using yellow circles.

Our goal is to figure out which of these pins are connected to the pins on the buttons we analyzed in the previous step. You can guess at this by looking at the lighter-green traces on the circuit board. The second image above highlights these in magenta and orange - it looks like we can trace two different pins (one from the ON button and one from the OFF button) to pins on the chip.

It probably isn't a good idea just to eyeball this, however. You need to use a multimeter in continuity-test mode to check and make sure that these pins are actually electrically connected. So, do that - set your multimeter to continuity test, and use the probe tips to simultaneously poke two different pins (bounded by the orange or magenta polygons above, respectively). If the pins are electrically connected, you should hear a loud BEEP from your multimeter.

Now, let's cross-reference this with our analysis of the pin voltages (on the buttons) in the previous step, to find out what happens to the pin voltages on the chip when the buttons are pressed. The last three images above show this. It looks like both pins sit at 0V by default. When the ON button is pressed, one of them goes up to 4.5V. When the OFF button is pressed, the other one goes up to 4.5V. This confirms our suspicion that the circuit probably has a chip operating at 5V logic levels. Now we can move on to building a circuit that will trick the remote into thinking the buttons are being pressed, by sending 5V signals to these pins.

Step 4: Solder Jumper Wires to the Remote

You'll need to solder three jumper wires to the remote: one to each of the pins identified in the previous step, and one to the pins connected to the battery's negative terminal (so you can make sure the remote has a common ground connection with the circuit you'll build).

Make sure you remove the battery before soldering (excessive heat can make batteries explode), and if you're cutting your own jumper wires, make sure they're long enough (a few inches) to reach a nearby breadboard.

Odds are if you've gotten this far you already know how to solder (or can get help from someone who does), but here's the SparkFun soldering tutorial just in case.

Step 5: Build the Circuit

In this step you'll assemble the circuit that will connect your newly-hacked remote to your Raspberry Pi. Follow the steps below (corresponding to the diagrams above) to build the circuit. To learn how the circuit actually works, go on to the next step.

1. Line up your Raspberry Pi, breadboard, and remote left to right as shown.
2. Populate your breadboard with the two MOSFETs and two relays. Pay attention to the orientation based on the pins of the relays (one side has three pins, the other side only has two), and the big "tabs" on the back of the MOSFETs (facing to the left in my diagrams).
3. Use jumper cables to make connections on the breadboard as shown in the second diagram.
4. Use jumper cables to make external connections to the remote and the Raspberry Pi, as shown in the third diagram. A couple notes:
  • I've only shown the pins on the remote that you need to connect to, rather than drawing the whole circuit. You already soldered jumper wires to these pins in Step 4, so you should be able to plop their other ends right into the breadboard.
  • This is where the Raspberry Pi's wildly non-intuitive pin numbering scheme can be quite confusing for a new user, especially if you're used to an Arduino. I won't go into details here - this page has a good explanation if you scroll down to "A word about GPIO pin numberings...". I've provided a simplified diagram that highlights the pins you need to use for this project, which will hopefully be less confusing than the diagram on their Wiki initially was for me. Point being, you need to connect to +5V (P1-02), GND (P1-06), GPIO 17 (P1-11) and GPIO 18 (P1-12), as shown.
  • The colors I used in my diagrams don't match photos of my physical circuit perfectly, because I only had red and black hookup wire. You can use whatever color scheme you want, as long as you get the connections right.

Step 6: How Does the Circuit Work?

Disclaimer: I'm not an electrical engineer, and there might be much, much better ways to go about doing this. I'll present what I did and how it works. If you have a better circuit design, or see something wrong with mine, please go ahead and leave suggestions in the comments!

Here's the general idea: we need to be able to toggle a 5V logic signal on and off to trick the remote into thinking the buttons are being pressed. The Raspberry Pi operates at a 3.3V logic level, but generally chips designed for 5V logic will still recognize 3.3V as "high". So, ideally you should just be able to wire the Raspberry Pi's GPIO pins straight to the remote, with no intermediate circuit. That's kind of what you see in this Instructable (although the pins aren't connected directly to the remove, there is a resistor in between).

So, I tried that method first, but could not get it to work at all (even for a range of different resistor values). Ultimately, after some tinkering around with a multimeter, it looked like my remote was drawing more current than the Raspberry Pi's GPIO pins could supply - which was causing the output voltage to drop to around two-point-something, too low to be recognized as a logical HIGH. Some Googling about GPIO pin current limitations lead to a bunch of forum discussions about how you're really supposed to use a buffer with the GPIO pins, they aren't designed to drive much of anything directly.

Thus, the buffer circuit using relays and MOSFETs. A relay is an electrically-controlled switch with an electromagnet inside. A "single pole double throw" (SPDT) relay can toggle between two different connections, depending on whether or not the electromagnet is energized. So in this case, we can hook a relay up to +5V and 0V and toggle back and forth depending on which voltage we want to send to the remote. We use the Raspberry Pi to control the electromagnet to turn the relay on or off - but the electromagnets still require more current than the Raspberry Pi's GPIO pins can supply. So, we use a MOSFET, which allows you to drive high-power loads using a low-power source (you can't magically draw power from nowhere - you have to connect to a bigger external power supply, in this case the Raspberry Pi's 5V source coming straight from USB, which can supply more current than the GPIO pins themselves). You might be familiar with MOSFETs if you've ever tried to control a motor or huge LED strip with an Arduino, which also has current limitations.

The three diagrams above show what happens depending on which GPIO pins are set to HIGH in Python (which we'll get to next). When both pins are sitting at LOW, both relays connect the buttons on the remote to 0V (ground), so nothing happens. When GPIO 17 is set to HIGH, the first MOSFET turns on, which allows current to flow through the electromagnet in the first relay, flipping the switch and connecting it to 5V instead of 0V. This sends a 5V signal to the ON button's pin on the remote, making the remote think the button has been pressed. The same concept applies to GPIO 18 and the OFF button.

Step 7: Python Code

Another disclaimer: I'm also not a computer scientist or programmer, much less a Python expert. If the code below looks awful, inefficient, or just makes you cringe, please feel free to post a link to better code, for the sake of people reading this in the future.

Update 12/13/2013: See mmoon's comment below for a link to some neater code.

Now that you have the circuit built, it's time to start using the Raspberry Pi! This is another part that was confusing for a first-time Raspberry Pi user - it wasn't immediately obvious how to actually control the GPIO pins, because apparently there's more than one way to do it. A Google search for "raspberry pi gpio python" reveals multiple different tutorials and Python packages you can download. You can also control the pins directly from a terminal without using Python at all. So, it took me a while to figure out that the default Raspbian distribution already comes with a Python module for controlling GPIO pins. Phew!

Remember that I'm assuming you already have a Raspberry Pi up and running this point, using Raspbian, which comes with both Python 2.x and 3.x (as of December 2013). I wrote the code in Python 3.2.3 using IDLE 3 (Integrated Development Library) - there's a shortcut for it on the default Raspbian desktop.

To run the code:

1. Open a terminal ("LXTerminal" icon on the desktop), type sudo idle3, and hit enter. This runs IDLE "as root", which is required for GPIO access with Python.
2. Download christmas_timer.py (below) and then open it in IDLE 3, or create a new Python file and copy and paste the code below.
3. Take a minute to look at the code and the comments. The only part you should need to edit is the section towards the beginning with all the "on" and "off" times (MonOn, MonOff, TueOn, etc).
4. You're almost ready to run the code! But first...(go to the next step)

# Raspberry Pi custom Christmas light timer

# import GPIO module
import RPi.GPIO as GPIO

# set up GPIO pins as outputs
# This convention is for the "P1" header pin convention
# where the pins start with P1 in the upper left
# and go to P26 in the lower right, with odds in the
# left column and evens in the right column.
# So, pins P1-11 and P1-12 correspond to GPIO17 and
# GPIO18 respectively.
GPIO.setmode(GPIO.BOARD)
GPIO.setup(11, GPIO.OUT)
GPIO.setup(12, GPIO.OUT)

# import date and time modules
import datetime
import time

# Enter the times you want the lights to turn on and off for
# each day of the week. Default is for lights to turn on at
# 5:30pm and off at 10:30pm on weekdays, on at 5:00pm and off
# at 11:30pm on weekends. Note that this is using a 24-hour clock.

MonOn  = datetime.time(hour=17,minute=30,second=0)
MonOff = datetime.time(hour=22,minute=30,second=0)
TueOn  = datetime.time(hour=17,minute=30,second=0)
TueOff = datetime.time(hour=22,minute=30,second=0)
WedOn  = datetime.time(hour=17,minute=30,second=0)
WedOff = datetime.time(hour=22,minute=30,second=0)
ThuOn  = datetime.time(hour=17,minute=30,second=0)
ThuOff = datetime.time(hour=22,minute=30,second=0)
FriOn  = datetime.time(hour=17,minute=30,second=0)
FriOff = datetime.time(hour=22,minute=30,second=0)
SatOn  = datetime.time(hour=17,minute=0,second=0)
SatOff = datetime.time(hour=23,minute=30,second=0)
SunOn  = datetime.time(hour=17,minute=0,second=0)
SunOff = datetime.time(hour=23,minute=30,second=0)

# Store these times in an array for easy access later.
OnTime = [MonOn, TueOn, WedOn, ThuOn, FriOn, SatOn, SunOn]
OffTime = [MonOff, TueOff, WedOff, ThuOff, FriOff, SatOff, SunOff]

# Set a "wait time" in seconds. This ensures that the program pauses
# briefly after it turns the lights on or off. Otherwise, since the
# loop will execute more than once a second, it will try to keep
# turning the lights on when they are already on (or off when they are
# already off.

waitTime = 3

# Start the loop that will run until you stop the program or turn
# off your Raspberry Pi.

while True:

    # get the current time in hours, minutes and seconds
    currTime = datetime.datetime.now()
    # get the current day of the week (0=Monday, 1=Tuesday, 2=Wednesday...)
    currDay = datetime.datetime.now().weekday()

    #Check to see if it's time to turn the lights on
    if (currTime.hour - OnTime[currDay].hour == 0 and
        currTime.minute - OnTime[currDay].minute == 0 and
        currTime.second - OnTime[currDay].second == 0):

        # set the GPIO pin to HIGH, equivalent of
        # pressing the ON button on the remote
        GPIO.output(11, GPIO.HIGH)

        # wait for a very short period of time then set
        # the value to LOW, the equivalent of releasing the
        # ON button
        time.sleep(.5)
        GPIO.output(11, GPIO.LOW)

        # wait for a few seconds so the loop doesn't come
        # back through and press the "on" button again
        # while the lights ae already on
        time.sleep(waitTime)

    #check to see if it's time to turn the lights off
    elif (currTime.hour - OffTime[currDay].hour == 0 and
        currTime.minute - OffTime[currDay].minute == 0 and
        currTime.second - OffTime[currDay].second == 0):

        # set the GPIO pin to HIGH, equivalent of
        # pressing the OFF button on the remote
        GPIO.output(12, GPIO.HIGH)

        # wait for a very short period of time then set
        # the value to LOW, the equivalent of releasing the
        # OFF button
        time.sleep(.5)
        GPIO.output(12, GPIO.LOW)

        # wait for a few seconds so the loop doesn't come
        # back through and press the "off" button again
        # while the lights ae already off
        time.sleep(waitTime)

Step 8: Hook Up Your Christmas Lights!

Don't forget the most important part of the project! If you haven't done so already, string up your Christmas lights and make sure they're plugged into the wireless outlet adapter you bought (I used the remote manually to make sure the lights were on for these pictures).

Step 9: Test Your Code

You're ready for a test run! Select Run->Run Module or press F5 in IDLE to run the code.

However, if you're doing this project in the middle of the day, you probably don't want to wait until evening to see if the lights turn on, and then a few more hours to see if they turn off. For testing purposes it's a lot faster to change a few lines of code temporarily. For example, if it's 3:30pm on a Tuesday, you could change

TueOn  = datetime.time(hour=17,minute=30,second=0)
TueOff = datetime.time(hour=22,minute=30,second=0)


(on at 5:30pm, off at 10:30pm)

to

TueOn  = datetime.time(hour=15,minute=32,second=0)
TueOff = datetime.time(hour=15,minute=33,second=0)


(on at 3:32pm, off at 3:33pm)

That will give you enough time to run outside and see if the lights turn on and off. You could also just temporarily set the wireless outlet up right next to your desk (with a lamp or a strand of lights) so you don't have to run back and forth to test.

If it works - congrats! Sit back and bask on the glory of your fully-customizable automatic Christmas light timer. Maybe you can work on expanding your code to allow web control, or multiple outlets for one of those crazy Wizards of Winter Christmas light shows. If it didn't work on the first try, don't worry - head over to the next step for some debugging tips.

Step 10: Debugging

Circuit not working? Here are a bunch of things you can try.
  • Make sure your wireless outlet and remote work to begin with. Disconnect the remote from your circuit entirely, and try just pressing the ON and OFF buttons. Sometimes these things don't have a great range, so try moving closer to the outlet.
  • Use a multimeter to check all the voltages you should be getting on the breadboard:
    • Are you actually getting +5V from the +5V pin?
    • Use the continuity test on your multimeter to check for short circuits.
    • Use the command line in Python to manually set the GPIO pins to HIGH one at a time (you can copy and paste the appropriate lines of code from my file to the command line). Do you get 3.3V from each pin when you do this? Remember to set the pins to LOW again afterwards.
    • Does the output of the relay actually toggle between 0V and 5V when the relay switches positions?
  • Make sure your relays are working by manually connecting the electromagnet (the end of the relay that has two pins directly across from each other, see the circuit diagrams in Step 6) to +5V and GND. You should hear an audible "click" when you do this.
  • Double and triple-check all your jumper wires. It only takes one missed connection, or something accidentally shorting +5V to GND, to stop the whole thing from working.
  • Make sure it isn't something obvious, like an electrical outlet in your house that is also controlled by a light switch on the wall.

Step 11: References

Hardware Hacking

Second Prize in the
Hardware Hacking

Make It Glow Contest

Participated in the
Make It Glow Contest

Raspberry Pi Contest

Participated in the
Raspberry Pi Contest