Raspi Timer Lamp Control
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 groundconnection 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 schemecan 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 aMOSFET, 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.
I'd suggest using cron to schedule the fan's on/off tasks.
Scheduling tasks can be managed by the crontab command.
shell ~> crontab -e
Add each on/off task. They would look something like:
0 9 * * * /path/to/your/python/script.py on 0 21 * * * /path/to/your/python/script.py off
The actual python script would be straight forward. Import sys to evaluate the on/off argument, and the GPIO library to work with pin 5 (refer to GPIO reference to map correct pin number).
#!/usr/bin/env python import sys import RPi.GPIO as GPIO # Identify which pin controls transistor FAN_PIN = 4 # Set pin 4 as output GPIO.setmode(GPIO.BCM) GPIO.setup(FAN_PIN, GPIO.OUT) # Get what action to take action = sys.argv.pop() if action == "on" : print "Turning fan on" GPIO.output(FAN_PIN, GPIO.HIGH) elif action == "off" : print "Turning fan off" GPIO.output(FAN_PIN, GPIO.LOW) else : print "Don't know what to do"