Cheap Home Automation Using Wireless Outlet Modules
by CalcProgrammer1 in Circuits > Remote Control
44996 Views, 91 Favorites, 0 Comments
Cheap Home Automation Using Wireless Outlet Modules
It's that time of year again, when the stores are full of Christmas decorations and accessories. Christmas may be months away yet, but never underestimate the power of hacking seasonal holiday accessories, one accessory in particular is incredibly useful year-round in home automation systems. This is, of course, wireless remote-controlled outlets. These are small boxes that plug in the wall with an outlet on them that act as switches. Included is a wireless remote with on/off buttons for one or more outlet modules. These often communicate on hacker-friendly radio frequencies with simple protocols that are easily reverse engineered. By pairing these wireless outlet modules with a simple transmitter circuit, you can attach AC lights and appliances to a computer-controlled home automation network.
I had previously purchased a 5-pack of wireless DBTech outlets from Amazon and reversed their protocol, but found that they only offered one signal, which was a toggle command. This is not ideal for home automation, where we want to explicitly send on or off to avoid issues caused by missed transmissions. Fortunately, I found a 3-pack of "Holiday Time" outlets at Walmart for $15, quite a good deal for what you can do with them, and these have both on and off buttons on the remote.
I had previously purchased a 5-pack of wireless DBTech outlets from Amazon and reversed their protocol, but found that they only offered one signal, which was a toggle command. This is not ideal for home automation, where we want to explicitly send on or off to avoid issues caused by missed transmissions. Fortunately, I found a 3-pack of "Holiday Time" outlets at Walmart for $15, quite a good deal for what you can do with them, and these have both on and off buttons on the remote.
What You Will Need to Continue...
If you purchased the exact same outlets I did, you don't need to reverse engineer them as I did the work for you. However, you likely didn't get the exact same ones as me, and there are tons of brands out there. They are all similar, but you will likely need your own reverse-engineering tools and skills to crack the protocol.
First, get yourself a set of radio transmitters and receivers from SparkFun. Get both the 315MHz and 434MHz versions of each, because these outlets can use either frequency (my 5-pack set uses 434, these use 315). You need the receiver to decode the protocol unless you want to disassemble the remote and probe around inside it. I'd still recommend the receivers to confirm your transmitter is transmitting. If you use the receiver, you don't even have to risk voiding your warranty by opening up the remote.
315MHz Transmitter: https://www.sparkfun.com/products/10535
315MHz Receiver: https://www.sparkfun.com/products/10533
434MHz Transmitter: https://www.sparkfun.com/products/10534
434MHz Receiver: https://www.sparkfun.com/products/10532
You'll also want a microcontroller of some sort, I used ATTiny2313 but any AVR, PIC, Arduino, or similar should do. You need to know how to use its microsecond and millisecond delay functions or write your own delay loops. You also want a microcontroller that has a serial port so you can talk to the computer.
For the reverse engineering you will also need either an oscilloscope or a logic analyzer. These can run at fairly high speeds so a DIY analyzer off an Arduino might not be fast enough for microsecond-scale pulses. My Rigol DS1052E 100MHz scope works wonders for this.
First, get yourself a set of radio transmitters and receivers from SparkFun. Get both the 315MHz and 434MHz versions of each, because these outlets can use either frequency (my 5-pack set uses 434, these use 315). You need the receiver to decode the protocol unless you want to disassemble the remote and probe around inside it. I'd still recommend the receivers to confirm your transmitter is transmitting. If you use the receiver, you don't even have to risk voiding your warranty by opening up the remote.
315MHz Transmitter: https://www.sparkfun.com/products/10535
315MHz Receiver: https://www.sparkfun.com/products/10533
434MHz Transmitter: https://www.sparkfun.com/products/10534
434MHz Receiver: https://www.sparkfun.com/products/10532
You'll also want a microcontroller of some sort, I used ATTiny2313 but any AVR, PIC, Arduino, or similar should do. You need to know how to use its microsecond and millisecond delay functions or write your own delay loops. You also want a microcontroller that has a serial port so you can talk to the computer.
For the reverse engineering you will also need either an oscilloscope or a logic analyzer. These can run at fairly high speeds so a DIY analyzer off an Arduino might not be fast enough for microsecond-scale pulses. My Rigol DS1052E 100MHz scope works wonders for this.
Look Buddy, I'm a Reverse Engineer!
Now it's time to start reversing the protocol. Power up your 315MHz and 434MHz receiver modules in a breadboard, insert antenna wires for each, and attach your scope/analyzer to one of the two receivers. You should see random noise. This is normal.
To see the signal the transmitter is sending, put your analyzer on auto mode or start capturing, then press and hold one of the buttons on the remote. Pause your analyzer before releasing the button to ensure you have the sample captured. These remotes send out a short message, repeated continuously as long as you are holding the button. They usually leave a 5-10ms gap between transmissions which makes it easy to locate the message in the captured waveform.
The convention used for these modules is typically short pulses and long pulses. I'm not sure which is supposed to represent 0 or 1, so I called them S (short) and L (long) to keep track of them. Use your analyzer's cursor functionality to measure both the high and low times as well as the total pulse period. This will be used when writing the code that creates these pulses.
Also, write down the sequences using S and L (or 1 and 0 if you want to assign them). Repeat this for each button on the remote to get all the codes. Typically there will be one code for ON and one code for OFF for each outlet module. Some outlets, as I said before, only have one code which toggles the output. Avoid these if you can.
Once you have all the button codes down, line them up and look down the columns to find columns that are all the same. You will likely find a set of bits at the beginning and at the end that are the same, with changes in the middle. Mark these unchanging bits at both the beginning and end as the preamble and end sequence respectively.
To see the signal the transmitter is sending, put your analyzer on auto mode or start capturing, then press and hold one of the buttons on the remote. Pause your analyzer before releasing the button to ensure you have the sample captured. These remotes send out a short message, repeated continuously as long as you are holding the button. They usually leave a 5-10ms gap between transmissions which makes it easy to locate the message in the captured waveform.
The convention used for these modules is typically short pulses and long pulses. I'm not sure which is supposed to represent 0 or 1, so I called them S (short) and L (long) to keep track of them. Use your analyzer's cursor functionality to measure both the high and low times as well as the total pulse period. This will be used when writing the code that creates these pulses.
Also, write down the sequences using S and L (or 1 and 0 if you want to assign them). Repeat this for each button on the remote to get all the codes. Typically there will be one code for ON and one code for OFF for each outlet module. Some outlets, as I said before, only have one code which toggles the output. Avoid these if you can.
Once you have all the button codes down, line them up and look down the columns to find columns that are all the same. You will likely find a set of bits at the beginning and at the end that are the same, with changes in the middle. Mark these unchanging bits at both the beginning and end as the preamble and end sequence respectively.
Re-implement the Protocol on Your Microcontroller
Once you've got the protocol extracted from the remote, you are now ready to re-implement it on your microcontroller. First, note which receiver you got the data from. Use that frequency transmitter in your circuit. Wire it up to an available GPIO pin on your microcontroller and remember which pin it is. Also make sure your microcontroller's programmer is connected and note the clock frequency of your microcontroller, which is used as the basis for accurate delays.
Start by writing functions for long and short pulses. Below is some pseudocode to get you started, individual implementations may vary.
void bit_long()
{
set_pin_high();
delay(HIGH_TIME_LONG);
set_pin_low();
delay(LOW_TIME_LONG);
}
void bit_short()
{
set_pin_high();
delay(HIGH_TIME_SHORT);
set_pin_low();
delay(LOW_TIME_SHORT);
}
You get the time constants by measuring the pulse widths with your analyzer or scope's cursor functions. Use the appropriate delay function (microsecond/millisecond) to best fit the measured width. On AVR these are _delay_us() and _delay_ms(), both from the <util/delay.h> library.
Then, start implementing your transmissions. Start by making a function for the preamble and a function for the end sequence, then make functions for each button's internal data. You can either write a function for each button, or if you can figure out what the protocol is doing you can write one function that builds the appropriate message from outlet number and on/off inputs. I chose the former, as the protocol did not seem to directly correlate outlet number and on/off state into its data, rather it was just one long pulse for the pressed button. For example:
void send_preamble()
{
bit_short();
bit_long();
bit_long();
bit_short();
bit_long();
bit_short();
}
Finally, use these functions in a for loop to send them approximately 10-20 times, with the appropriate delay in between transmissions (you can measure this delay with your scope or analyzer). For instance:
for(int i = 0; i < 10; i++)
{
send_preamble();
send_outlet_1_on();
send_end_sequence();
_delay_ms(10);
}
Then build a switch/case or cascading if/else statement with one of these blocks for each button, using outlet number and switch state as your if/else conditions.
Finally, import a serial library or write your own serial port interrupt routine. In that interrupt routine, set both the outlet number and the switch state from received data. Use volatile variables so that they can be changed from within an ISR, so that the main loop can pick up on the set variables and perform the desired transmission. Remember to initialize your serial port and set your baud rate before starting your main loop!
Start by writing functions for long and short pulses. Below is some pseudocode to get you started, individual implementations may vary.
void bit_long()
{
set_pin_high();
delay(HIGH_TIME_LONG);
set_pin_low();
delay(LOW_TIME_LONG);
}
void bit_short()
{
set_pin_high();
delay(HIGH_TIME_SHORT);
set_pin_low();
delay(LOW_TIME_SHORT);
}
You get the time constants by measuring the pulse widths with your analyzer or scope's cursor functions. Use the appropriate delay function (microsecond/millisecond) to best fit the measured width. On AVR these are _delay_us() and _delay_ms(), both from the <util/delay.h> library.
Then, start implementing your transmissions. Start by making a function for the preamble and a function for the end sequence, then make functions for each button's internal data. You can either write a function for each button, or if you can figure out what the protocol is doing you can write one function that builds the appropriate message from outlet number and on/off inputs. I chose the former, as the protocol did not seem to directly correlate outlet number and on/off state into its data, rather it was just one long pulse for the pressed button. For example:
void send_preamble()
{
bit_short();
bit_long();
bit_long();
bit_short();
bit_long();
bit_short();
}
Finally, use these functions in a for loop to send them approximately 10-20 times, with the appropriate delay in between transmissions (you can measure this delay with your scope or analyzer). For instance:
for(int i = 0; i < 10; i++)
{
send_preamble();
send_outlet_1_on();
send_end_sequence();
_delay_ms(10);
}
Then build a switch/case or cascading if/else statement with one of these blocks for each button, using outlet number and switch state as your if/else conditions.
Finally, import a serial library or write your own serial port interrupt routine. In that interrupt routine, set both the outlet number and the switch state from received data. Use volatile variables so that they can be changed from within an ISR, so that the main loop can pick up on the set variables and perform the desired transmission. Remember to initialize your serial port and set your baud rate before starting your main loop!
Testing 1...2...3...
Plug in a serial level shifter or FTDI chip, connect it to your computer, and fire up a terminal program that can send the data in the format you chose. I prefer RealTerm as it can send arbitrary bytes input as hex or decimal. Plug in your outlet module and plug something such as a lamp into it. Then send your ON packet for that outlet. With any luck, your lamp will click on and shine brightly, indicating your code is a success. Otherwise, it's time to stick the scope probe on the transmitter's data input and double check to make sure what you're outputting is the same as what you recorded earlier. You may have to adjust the timing, as what you output may not be exactly the same as what the transmitter receives. Make sure your transmissions match and that your timings are correct. Adjust your antenna wire if necessary, and try to eliminate sources of electrical or radio interference (programmer, receiver, scope, analyzer) by removing unnecessary components from your breadboard and powering down equipment. With a bit of diagnostics, you should be able to tweak your code into working state fairly easily.
If it works, test all combinations of on and off to make sure all your codes are correct. If everything is good, it's time to move on to the computer side for integration into a networked system or home automation setup.
If it works, test all combinations of on and off to make sure all your codes are correct. If everything is good, it's time to move on to the computer side for integration into a networked system or home automation setup.
[New!] Increase the Range!
I'm adding this new step a year after the initial project to remedy an issue I had with it. The functionality of wireless outlets is great, but I was having issues where outlets in rooms farther away from the server were not responding very often. This new step shows how you can increase the range of your transmitter for optimal performance.
The range is basically determined by two things - transmitter voltage and antenna. I haven't messed with computing the optimal antenna length but I do know that a transmitter capable of operating up to 12V DC is not going to be performing at its best on 5V from a USB port. In the original remotes, the transmitter uses a 12V mini-battery (which is 6 1.5V button cells stacked) and has great range. It easily switches outlets on the other side of the house reliably so I figured I needed to increase my voltage.
Problem. I'm using USB power provided by a USB to serial board (Prolific PL2303 based) and it provides only two voltage outputs - 5V directly from USB and 3.3V regulated. I'm running everything off of 5V but it's not enough. The answer? Use a voltage doubler circuit to boost the 5V up to 10V and power the transmitter off of 10V.
A voltage doubler is a simple diode and capacitor circuit that can double an input voltage using a PWM pin on a microcontroller. Basically you charge up the capacitor by driving the pin low, then "pump" the voltage by driving the pin high and adding the capacitor's charge voltage on top of your 5V microcontroller pin. That output goes through another diode to charge another capacitor to 10V. I had no issues running the transmitter as fast as I needed to off of the supply and managed to get around 8V under load. Not bad!
I learned of the technique via Dave Jones' EEVBlog in this video:
Definitely give that a full watch if you're interested in the theory behind it.
The range is basically determined by two things - transmitter voltage and antenna. I haven't messed with computing the optimal antenna length but I do know that a transmitter capable of operating up to 12V DC is not going to be performing at its best on 5V from a USB port. In the original remotes, the transmitter uses a 12V mini-battery (which is 6 1.5V button cells stacked) and has great range. It easily switches outlets on the other side of the house reliably so I figured I needed to increase my voltage.
Problem. I'm using USB power provided by a USB to serial board (Prolific PL2303 based) and it provides only two voltage outputs - 5V directly from USB and 3.3V regulated. I'm running everything off of 5V but it's not enough. The answer? Use a voltage doubler circuit to boost the 5V up to 10V and power the transmitter off of 10V.
A voltage doubler is a simple diode and capacitor circuit that can double an input voltage using a PWM pin on a microcontroller. Basically you charge up the capacitor by driving the pin low, then "pump" the voltage by driving the pin high and adding the capacitor's charge voltage on top of your 5V microcontroller pin. That output goes through another diode to charge another capacitor to 10V. I had no issues running the transmitter as fast as I needed to off of the supply and managed to get around 8V under load. Not bad!
I learned of the technique via Dave Jones' EEVBlog in this video:
Definitely give that a full watch if you're interested in the theory behind it.
Computer Coding - Control Your Lamps From the Internet!
For starters, write a simple command line utility that opens the serial port at the appropriate baud rate, forms a packet based on outlet number and switch status, and transmits it to the microcontroller. This gives you a command line utility to turn your lights on and off. You can use it in scripts or call it from other utilities. Here is mine:
#include <cstdlib>
#include "serial_port.h"
using namespace std;
int main(int argc, char *argv[])
{
if(argc != 3)
{
return 0;
}
serial_port port("/dev/ttyUSB0", 38400);
char pkt[] = {0x00, (char)atoi(argv[1]), 0xAA, (char)atoi(argv[2]), 0xFF};
port.serial_write(pkt, 5);
port.serial_close();
return 0;
}
That's all there is to it! We now have a command line utility, lampctl, to turn the lamps on and off from the command line. As a test, I remoted in on my Android phone and was able to turn the lamps on and off from my phone. How awesome is that!
I haven't looked into larger home automation setups yet, but I really want to. This Instructable is only one small part of what could be a much larger setup. If I find any more cool home automation stuff I will make more Instructables on it. It will be fun turning my living room lights on and off from halfway across the country this week!
Attached are the code files for this project. The OutletControl.c, fanbus.c, and fanbus.h files are the AVR files to compile with AVR-GCC or AVR Studio while the lampctl.cpp, serial_port.cpp, and serial_port.h files are the PC code for the lampctl command line utility. Note that my serial_port function doesn't set the baudrate properly, so set it using stty -F /dev/ttyX 38400 where ttyX is your serial port (USB0 for me).
#include <cstdlib>
#include "serial_port.h"
using namespace std;
int main(int argc, char *argv[])
{
if(argc != 3)
{
return 0;
}
serial_port port("/dev/ttyUSB0", 38400);
char pkt[] = {0x00, (char)atoi(argv[1]), 0xAA, (char)atoi(argv[2]), 0xFF};
port.serial_write(pkt, 5);
port.serial_close();
return 0;
}
That's all there is to it! We now have a command line utility, lampctl, to turn the lamps on and off from the command line. As a test, I remoted in on my Android phone and was able to turn the lamps on and off from my phone. How awesome is that!
I haven't looked into larger home automation setups yet, but I really want to. This Instructable is only one small part of what could be a much larger setup. If I find any more cool home automation stuff I will make more Instructables on it. It will be fun turning my living room lights on and off from halfway across the country this week!
Attached are the code files for this project. The OutletControl.c, fanbus.c, and fanbus.h files are the AVR files to compile with AVR-GCC or AVR Studio while the lampctl.cpp, serial_port.cpp, and serial_port.h files are the PC code for the lampctl command line utility. Note that my serial_port function doesn't set the baudrate properly, so set it using stty -F /dev/ttyX 38400 where ttyX is your serial port (USB0 for me).