Raspberry Pi Relay Timer
I've been doing a lot of relay-based projects lately; building several light timer projects. The first couple used Arduino-class devices, simple relays and provided the ability to set multiple on and off times throughout the day, just like those $10 power timers you can buy anywhere. These projects give me the ability to turn my Christmas lights at sundown and off when I go to bed, but not much more.
What I really wanted though, is a way to have the lights turn on and off randomly (during specific time periods) to more accurately simulate me being home when I'm not. Products may exist that do this, but I've not seen any, so I decided to make my own. This repository contains the code for this project.
Note: You can use this code for your own, personal projects, but if you make a commercial product based on this concept, can you share the love (10% for example)? :-)
- Enable a UTC mode that ignores local time zone.
- Figure out why sunrise and sunset aren't being calculated correctly.
To use this project, you'll need at a minimum the following hardware components:
- Raspberry Pi 3
- A compatible relay/relay board (see below)
- 5V, 2.5A Micro USB power source (basically, a smartphone charger) - I use the CanaKit 5V 2.5A Raspberry Pi 3 Power Supply/Adapter/Charger
- An enclosure for the Raspberry Pi and relay.
For the relay, I used the 1-channel relay board from yourduino.com. The boards they're selling now are different than the ones I had lying around. They have a really good relay tutorial on their web site; even though it's geared primarily at Arduino users, it's still a lot of good information. Amazon.com also has a good selection of relay boards you can use as well.
Configuring Your Raspberry Pi
Download the latest version of the Raspbian OS from the Raspberry Pi web site and follow the instructions for writing the OS image to a Micro SD card for the Pi. Insert the SD card in the Pi, connect Ethernet, keyboard, mouse, and a monitor to the Pi and finally power it up using a smartphone charger or some suitable power source.
The first thing you'll want to do is open the Raspberry Pi menu (in the upper-left corner of the screen), select Preferences, then Raspberry Pi Configuration as shown in the following figure:
Raspbian comes configured with its keyboard, timezone, and other locale settings configured for the United Kingdom (UK), so if you're in the US, or elsewhere that's not the UK, you'll want to switch over to the localisation tab and adjust the settings there as well.
When you're done configuring locale settings, you'll likely be prompted to reboot the Pi. Go ahead and do that before continuing.
When the Pi comes back up, open a terminal window and execute the following command:
sudo apt-get update
This updates the local catalog of application repositories. Next, execute the following command:
sudo apt-get upgrade
This command updates the Raspbian OS with all updates released after the latest image was published. The update process may take a long time, so pay attention, answer any prompts, and expect this process to take a few minutes or more (the last time I did this, it took about 15 minutes or more to complete).
Next, install some Python libraries used by the project; in the same terminal window, execute the following command:
sudo pip install pytz tzlocal numpy
Now, lets download and project code; still in the same terminal window (almost done now), execute the following command:
git clone https://github.com/johnwargo/raspberry-pi-relay-timer
This downloads the project's code from its Github repository and copies the files to the local (relative to your terminal window)
Switch to the new folder by executing the following command:
You'll find the following files in the folder:
controller.py- The project's main application file. You'll run this program to start the relay controller.
LICENSE- The MIT license for the application. You're free to use this code as you see fit, but, like I said, if you make a commercial product out of this, share the love (with me, of course).
readme.md- This file.
relay.py- A simple Python module that exposes the capabilities the application needs to control the relay. I broke this out into a separate module to make it easier for you to use my code in other projects.
relay-test.py- A Python application that I built to help me write and test the
relay.pymodule. You can run it to make sure your hardware works correctly.
solar-times.py- A simple Python application I wrote to help me write and test the code that connects to a web service to determine sunrise and sunset times for the current location. This code is also in the
start-controller.sh- A shell script you'll use to configure the Pi to start the controller application on start up.
Customizing the Controller Application
If you look in the project's
controller.py file, you'll find an area neat the top of the file that contains configuration settings I'm expecting you to update to make this application work for your configuration. It will look something like this:
# ============================================================================ # User (that's you) adjustable variables # ============================================================================ # adjust these variables based on your particular hardware configuration
I'll describe each of the settings components in the following sub-sections.
The controller application basically runs a continuous loop, waiting for input or waiting for the minute to change so it can check to see if it needs to turn the relay on or off. Input is provided through a simple push button attached to the Raspberry Pi. Connect one of the button's wires to one of the GPIO pins, and connect the other to one of the Pi's ground (GND) pins. The GPIO Zero library used by the application takes care of setting up the Pi to read the button's status. The application doesn't know which pin you connected the button to, so you'll have to tell it by populating the application's
BUTTON_PIN variable with the pin number for your implementation. In the example below, I have the button wired to the Pi's GPIO 19 pin.
# set this variable to the button pin used in your implementation BUTTON_PIN = 19
The controller application basically runs a continuous loop, waiting for input or waiting for the minute to change so it can check to see if it needs to turn the relay on or off. For this project, you'll connect a simple relay to the Raspberry Pi using three wires. Connect the 5V output to the relay's VCC input. Connect one of the Pi's ground (GND) connectors to the relay's GND connector, and finally, connect one of the Pi's GPIO pins to the relay's IN1 connector. The GPIO Zero library used by the application takes care of setting up the Pi to drive the relay as an output device. The application doesn't know which pin you connected the relay to, so you'll have to tell it by populating the application's
RELAY_PIN variable with the pin number for your implementation. In the example below, I have the relay wired to the Pi's GPIO 18 pin.
# set this variable to the GPIO pin the relay is connected to RELAY_PIN = 18
The controller application uses a public web service to determine the daily sunrise and sunset times for your Pi's current location. Unfortunately, the Pi doesn't really know where it's physically located (yes, I know, you could determine a location using Wi-Fi SSID or IP address, but that won't always be accurate). The API I used for this application (Sunrise Sunset), uses latitude and longitude values to determine the sunrise and sunset times. So, if you use one of the slot options (described later) that enables using solar times (sunrise and sunset), you'll have to provide latitude and longitude values for the Pi's current location by populating the
LOC_LONG variables shown below. In the example, I have configured the controller to get solar times for Charlotte, NC.
# Sunrise and sunset times vary depending on location, so... # If using sunrise or sunset as trigger options, populate the locLat # and locLong values with the location's latitude and longitude values # These values are for Charlotte, NC, to get Sunrise/Sunset values for # your location, replace these strings with the appropriate values for # your location. LOC_LAT = "35.227085" LOC_LONG = "-80.843124"
Note: The official name for our sun is Sol, so that's why I'm calling the controller's determining sunrise and sunset times as "getting Solar times". There's only one Sol in the universe, so even though you've seen it hundreds of times in the news and often in science fiction movies, there's only one Solar System. There is 'The Solar system' and there's no such thing as 'A Solar system.' There's only one Solar System, you're in it.
Default Solar Times
When you configure the controller to use sunrise and sunset to turn on/off the relay, every morning at 12:01 AM, the application connects to the web service and asks it for the current sunrise and sunset values for the current location. If the network is not available for some reason, or the service isn't working at the time, the application needs some fallback values to use instead. With that in mind, the controller defines default values for sunrise and sunset (in the two variables shown below) that are reset to actual sunrise and sunset values the first time a successful call is made to the Sunrise Sunset API. After that first time, if the controller can't retrieve data from the web service, it merely uses the values from the previous successful call to the remote API.
# default times for sunrise and sunset. If solar data is enabled, the # code will reach out every day at 12:01 and populate these values with # the correct values for the current day. If this fails for any reason, # the values will fall back to the previous day's values, or, finally, # these values time_sunrise = 700 time_sunset = 1900
Time values here and in the
slots variable described in the next section, are defined as integer vales in 24 hour format. So, 7:00 AM is represented as 700, and 5:00 PM is represented as 1700.
I wanted to make the controller as flexible as possible without going too far. Ultimately, I'd love to make the controller's on/off times dynamically configurable using an app server running on the Pi and a smartphone or tablet application. Perhaps someday I'll get around to that, but for now, you have to manually configure the time slots in the controller's code.
slots array is a simple two-dimensional array; each row represents a single time slot, and columns in the row control:
- On trigger
- On adjustment value
- Off trigger
- Off adjustment value
- A random flag
Take a look at an example, then I'll explain how it works:
# Slots array defines time windows and behavior for the relay # format: [ OnTrigger, OnValue, OffTrigger, OffValue, doRandom] slots = np.array( # ONLY modify the following array with your time settings [ (SETTIME, 700, SETTIME, 900, False), (SETTIME, 1700, SETTIME, 2300, True), (SUNRISE, 15, SUNSET, -10, True) ], # leave the rest of this alone dtype=[ ('onTrigger', np.dtype(int)), ('onValue', np.dtype(int)), ('offTrigger', np.dtype(int)), ('offValue', np.dtype(int)), ('doRandom', np.dtype(bool)) ] )
The On Trigger and Off Trigger values identifies the event that triggers the relay to go on or off:
# 'constants' that define the different time triggers used by the application # DO NOT MODIFY THESE, you'll mess up the app's logic SETTIME = -1 SUNRISE = -2 SUNSET = -3
Hopefully the purpose of each is clear, but just to make sure...
SETTIMEinstructs the controller to turn the relay on or off at a specific time. If
OnTriggeris set to
SETTIMEthen the value in
OnValuedefines the time used. Correspondingly, if
OffTriggeris set to
SETTIMEthen the value in
OffValuedefines the time used.
SUNRISEinstructs the controller to use the current day's sunrise time to trigger the relay. If one of the triggers is set to
SUNRISEthen the value in
OnValuedefines a time offset added to the sunrise time. For example, if
OnTriggeris set to
OnValueis 15, then the relay will turn on at 15 minutes after sunrise. if
OnTriggeris set to
OnValueis -10, then the relay will turn on at 10 minutes before sunrise.
SUNSETinstructs the controller to use the current day's sunset time to trigger the relay. If one of the triggers is set to
SUNSETthen the value in
OnValuedefines a time offset added to the sunset time. For example, if
OnTriggeris set to
OnValueis 30, then the relay will turn on at 30 minutes after sunset. if
OnTriggeris set to
OnValueis -20, then the relay will turn on at 20 minutes before sunset.
doRandom variable defines two options for the controller for each row:
False- The relay is turned on at on time and at off times calculated using
True- The relay is turned on and off at random times between on time and at off times calculated using
doRandom is enabled (
True), the relay simulates a random human flipping the switch on and off in order to simulate you being home when you're actually not.
Note: Boolean values in Python are case sensitive; the possible values for
False. If you use
falsethe controller won't work.
To configure the controller, define one or more array rows using the example shown above and the descriptions I just provided. When you execute the controller, at startup, it validates the slots and will tell you pretty quickly if things are OK. So, with that in mind, every time you make a change to these settings, make sure you check the controller's output window on startup to make sure everything's OK.
Instead of calculating whether the relay should be on or off with
doRandom enabled, at 12:01 AM every day, the controller application builds a separate array of on/off times called
daily_slots. This approach dramatically simplifies the overall code as all the controller has to do is query the
daily_slots list to determine what it needs to do. The application doesn't do anything to validate that there's no overlap across slots, so if you define two time slots that contradict each other (one slot tells the controller to turn the relay on at the same time the other slot tells the controller to turn it off) the controller will do everything you tell it to do, and turn the relay on then immediately turn it off again.
Note: You can see an example of the
daily_slotslist in the screen shot shown in the next section. In this example, there's two slots that have
doRandomenabled, so the controller generates a random series of on/off time slots within the defined time windows.
Starting the Controller
Open a terminal window, and change to the project folder using the following command:
Your terminal window prompt should change to reflect the switch to the new folder. Now, let start the server application. In the terminal window pointing to the
raspberry-pi-relay-timer folder (you changed to this folder with the last command you typed), execute the following command:
The controller process will start and begin managing the relay using the time slots you selected.
Starting The Controller Server Process Automatically
Right now, the server is only running because you started it manually. There are a few steps you must complete to configure the Raspberry Pi so it executes the the relay controller app on startup. You can read more about this here: Autostart Python App on Raspberry Pi in a Terminal Window.
If you don't already have a terminal window open, open one then navigate to the folder where you extracted the project files (if you followed these instructions, it should be at
home/pi/raspberry-pi-relay-timer. If you're not there, get there.
Make the project's bash script file (
start-controller.sh) executable by executing the following command:
chmod +x start-controller.sh
Open the pi user's session autostart file using the following command:
sudo nano ~/.config/lxsession/LXDE-pi/autostart
Add the following line to the end (bottom) of the file:
@lxterminal -e /home/pi/raspberry-pi-relay-timer/start-controller.sh
To save your changes, press ctrl-o then press the Enter key. Next, press ctrl-x to exit the application.
Reboot the Raspberry Pi; when it restarts, the controller server process should execute in its own terminal window.
By John M. Wargo - If you find this code useful, and feel like thanking me for providing it, please consider making a purchase from my Amazon Wish List. You can find information on many different topics on my personal blog. Learn about all of my publications at John Wargo Books.