Sep 1st, 2019
How To: Make A Raspberry Pi Powered Outlet Strip
Learn how to use a Raspberry Pi, Passel.js, and a little programming to make a smart outlet power strip. Wake yourself up in the morning with lights, or just start appliances at a certain time.
Getting started
For this project I went ahead and used my NPM package Passel.js to quickly get started. It doesn’t add much functionality to the example branch of this project, but it keeps a lot of doors open for easily extending the project down the road, or for anyone who would like to fork the project.
Also as a preface to this project, my goal for this was to turn a set of lights on near my bedside table every morning at 7-9. So all the code is geared towards that. It can be easily changed and organized to suit your needs though! I highly encourage changing it up and altering it to suit your needs. I will continue to change the master branch to suit my needs, but the “Blog Post” branch will be sticking around for anyone who wants a bare bones example to start from.
Hardware
Usually I have a list with links for each item, but this project was solely just using parts I had lying around the house. But I will do my best to give a list of all the items and link what I can:
- 9 outlet power strip
- Durable computer power supply cable
- Soldering Kit & Tools
- Raspberry Pi Zero W
- 8 relay board
- Jumper wires
Wiring
The wiring for this is pretty simple. Most power strips should have 3 power rails running to each outlet and share the power delivered in parallel. I wanted to keep this the same and just decided to wire 8 individual wires directly from the main lead, to the relay, and then back to each outlet.
In the image below, you can see that I’ve soldered the main lead together with 8 wires I cut out of a computer power supply cable. I then soldered another 8 leads into one side of each power outlet. As you may notice as well, the main power lead is coming directly into one of the outlets, which for us, is very nice! Our power strip has 9 outlets which leaves an always on power outlet for us to plug our Pi into once everything is setup.
Once everything is soldered and secured, all the wires can come together into the relay in whichever configuration you desire. I chose to go with a default position of an open circuit for each relay since I am intending for lights to automatically come on each morning and have no need of other devices to be powered. But you could split this up to have, for example, half of the outlets to be normally closed, and the other half to be normally open.
For ease of programming and scheduling later down the road, I would also suggest to make sure you run your cabling to the relays in order of where each outlet is. This way relay 1 is outlet 1, relay 2 is outlet 2, and so on and so forth.
After everything is hooked up you can go ahead and zip tie everything together, cut a slot in the side of your outlet strip, and put everything back together.
The last thing to do in this section is just the wiring between the Pi and the relay!
I highly suggest using pinout.xyz as an easy reference. I chose to wire the following BCM pins: 0, 5, 6, 13, 19, 26, 16, 20. These are given in the corresponding order of how they are plugged into the relay. This configuration leaves the bottom two pins (ground and 21) for use with a button, although, I did not end up using this in the final code.
Code
I’ll brush over the code slightly below, but if you would like to skip directly to the full code block, click here.
After defining the GPIO in the Passel component, I wrote a helper function setOutlets()
to simply loop over every pin defined and assign each one to pull high or low.
setOutlets(value) {
if (typeof value === "number") {
if (value === 1) value = 0;
else value = 1;
this.outlets.forEach(outlet => {
outlet.writeSync(value);
});
}
}
A quick note on this function: it switches the input value to the opposite of whichever is supplied as an input. The relay board we are using is backwards in my opinion. The circuit is closed when no voltage is being sent from the GPIO, and opens when there is power going out.
My scheduling is very simple as I only want it to be on for two hours in the morning and shut off for the rest of the day.
setInterval(() => {
const hours = new Date().getHours()
if (hours === 7 || hours === 8) {
this.setState({ isOn: true })
} else {
this.setState({ isOn: false })
}
}, 5 * 1000)
Every 5 seconds we check whether it is 7 or 8 in the morning, and if so, set the state to the corresponding value. This leaves us with an outlet that turns on from 7-9am. If you are using a similar schedule, ensure that you run sudo raspi-config
and configure your time zone!
With the code given above, every outlet turns on, but this could be easily extended through individualized components to have a different schedule for each and every outlet. Listed below is everything that makes this work:
Passel Component
const { Components } = require("passeljs")
class Lights extends Components.Base {
constructor(props) {
super(props)
this.state = {
isOn: false,
}
const clear = () => {
this.setOutlets(0)
process.nextTick(function() {
process.exit(0)
})
}
process.on("SIGINT", clear)
process.on("exit", clear)
process.on("uncaughtException", clear)
this.Gpio = require("onoff").Gpio
this.outlets = [
new this.Gpio(0, "out"),
new this.Gpio(5, "out"),
new this.Gpio(6, "out"),
new this.Gpio(13, "out"),
new this.Gpio(19, "out"),
new this.Gpio(26, "out"),
new this.Gpio(16, "out"),
new this.Gpio(20, "out"),
]
}
setOutlets(value) {
if (typeof value === "number") {
if (value === 1) value = 0
else value = 1
this.outlets.forEach(outlet => {
outlet.writeSync(value)
})
}
}
componentWillMount() {
this.setOutlets(0)
console.log("Component 'Lights' will mount")
this.stateChanged.on("isOn", isOn => {
this.setOutlets(isOn ? 1 : 0)
})
setInterval(() => {
const hours = new Date().getHours()
if (hours === 7 || hours === 8) {
this.setState({ isOn: true })
} else {
this.setState({ isOn: false })
}
}, 5 * 1000)
}
componentDidMount() {
console.log("Component 'Lights' did mount")
}
}
module.exports = Lights
The full source code can be found above at Github.
Software Install
Now that we’ve brushed over the code a bit, we can set everything up to stay running even after reboots or crashes!
Once your Pi is booted and you’ve SSHed into your Pi, you can install node with either of the following commands, depending on which Pi you are using.
Node for PiZW
curl -o node-v9.7.1-linux-armv6l.tar.gz https://nodejs.org/dist/v9.7.1/node-v9.7.1-linux-armv6l.tar.gz && tar -xzf node-v9.7.1-linux-armv6l.tar.gz && sudo cp -r node-v9.7.1-linux-armv6l/* /usr/local/
Node for Pi3B+
curl -sL https://deb.nodesource.com/setup_10.x | sudo -E bash - && sudo apt-get install -y nodejs
Once Node and NPM are installed, you can proceed to install everything. Note, you may need to install git.
Run the following command to clone the project, cd into the project, and install all node dependencies:
git clone https://github.com/ZachJMoore/smart-outlet.git && cd smart-outlet && npm install && sudo npm install -g forever forever-service
Forever Service
I chose to use Forever as my service manager as I am already familiar with it from previous projects.
Now is the time to make any changes to my code if you would like, otherwise, you can continue to installing our code.
At this point, all you need to do is run the “install” command found below, then use one of the service commands below and put the code into action! After this, you can unplug your Pi and move it wherever you would like - forever will start everything back up once it boots again!
install
sudo forever-service install smart-outlet --script index.js
delete
sudo service smart-outlet stop && sudo forever-service delete smart-outlet
Commands to interact with the service (do this before rebooting or shutting off)
- Start - “sudo service smart-outlet start”
- Stop - “sudo service smart-outlet stop”
- Status - “sudo service smart-outlet status”
- Restart - “sudo service smart-outlet restart”
What did you think?