Hack a wireless home automation system to be USB controlled using two AVR microcontrollers!
Check out the video! The system is really more responsive, but the browser on my phone is slow.
If you like this, you may also like this:
Step 1: Skills ans tools
There are two ways of hacking an RF remote to be controlled by a computer or a microcontroller.
The lame way:
Soldering wires onto the button pads on the remote and hooking them up to an Arduino.
The cool way:
Most RF remotes have a separate module for transmitting data. This device usually has a VCC and a GND line and a DATA line. You can easily transmit your own wireless data by connecting a microcontroller to the DATA line.
However, in order to transmit something that the wireless receivers can understand, you first have to figure out how the wireless data is formatted and transmitted.
To do this hack you will need a logic analyzer and optionally an oscilloscope.
I use the Logic from Saleae. This is an awesome tool and I have done a few reverse engineering hacks using this device!
Check it out at http://www.saleae.com/logic/
It costs 149 USD but it's a good investment for any hacker!
You also need to be familiar and comfortable with microcontrollers and programming in C.
Step 2: Parts required
You will need:
- 1x wireless home automation kit
- 1x project box
- 1x USB type B connector
- 2x 3.6v zener diode
- 1x 8.2v zener diode
- 1x BC548 transistor
- 2x 22pF ceramic capacitor
- 2x 100nF ceramic capacitor
- 1x 4.7uF capacitor
- 1x 100uF capacitor
- 1x 470uF capacitor
- 1x 330uF capacitor
- 1x 12MHz crystal
- 2x LED with resistor (I used 1k ohm)
- 2x 68R resistor
- 1x 1k5 resistor
- 1x 2k2 resistor
- 1x 1m resistor
- 1x 270uH inductor
- 1x 1N4004 diode
- 1x ATmega8 microcontroller
- 1x protoboard. Solder eye type, not stripboard.
Step 3: Don't turn it on, take it apart!
I assumed that the remote had a separate RF module. Let's crack it open and check if this is the case.
Eeey! It sure does! The little green board inside the remote is the RF module.
The board is even clearly labeled and has 3 inputs:
The connections were kinda hard to get to with my oscilloscope and logic analyzer probes, so I just extended the connections using some solid core copper wire.
Now I can push the buttons while sniffing the DATA line.
Step 4: Figure out what's going on inside
The remote is powered by a 9V battery. My logic analyzer is only rated for 5 volts, so I want to check out what's going on with the DATA line before hooking it up to my logic analyzer.
If the signal on the DATA line is 9 volts, I have to do some tricks to get it down to 5V for the logic analyzer.
I connected an oscilloscope probe to the DATA line, and GND to the GND line on the remote. I set the trigger to two-ish volts and pushed a button. Out comes data! Sweet. This looks very hackable!
Turns out, the data line is only 3 volts. The distance between the horizontal dotted lines on the oscilloscope display is 2 volts.
The RF module looks like a pretty simple device, so I'll just assume that it can handle 5 volts as well as 3. The micro controller will be running at 5 volts.
Step 5: Reverse engineering: first glance
The oscilloscope is a great tool, but to see what's really going on with that data signal, it's a lot easier to use a logic analyzer.
The logic analyzer only reads 0 and 1, so I don't get all the analog noise that i get on the oscilloscope, and it is connected to the computer via USB, so it's a lot easier to read than the small oscilloscope display.
So I hook the DATA line up to channel 1 of my logic analyzer. I select 1 MHz capturing rate, that should be more than enough for this.
I start the logic analyzer and press the ON button for lamp 1 on the remote control.
The logic analyzer shows 4 distinct frames of data. At first it thought maybe this was going to be more complicated to reverse engineer than I had anticipated. But to my relief, all 4 frames was identical. The same was true for all the other buttons on the remote. The data is probably transmitted 4 times because the wireless link is inherently unreliable :p
So I zoom in on one of the frames and see that it consists of pulses of different length. At this point I have no idea what is 0 and what is 1.
Step 6: Reverse engineering: diving into the data
Ok, so at this point I just have a bunch of short and long pulses, and I have no idea what it means!
The remote has a small button under the battery lid. If this button is pressed, I have to re-associate all the receivers with the remote. If you neighbor's remote is interfering with your lights press this button to get a new random ID. I suspect that pushing this button creates some kind of randomized code specific to that remote.
If that is true, I can use it to identify at least some parts of the data.
I started the logic analyzer again and pushed ON for lamp one 5 times while pressing the reset button between every time i pressed lamp 1 ON.
To make it easier to see what was going on, I copy pasted the data frames into gimp and placed them under each other. In the logic analyzer they are represented side by side, which makes comparison pretty hard.
Luckily, the Saleae guys had thought of this. Ctrl+shift+m lets you copy a selecton of the screen to clipboard.
As I suspected, pushing the reset button changed a random number inside the remote that is transmitted with each data frame.
The first bit is always the same. This makes sense. It probably "wakes up" the receivers or tells it that "Hey, here comes data, be ready!"
The next 12 consecutive bits change every time i press the reset button. I marked the bits that changed in red and the constant bits in green.
Lets call the 12 random bits network address from now on.
It looks like the payload data for each frame is 8 bits.
Another great thing about doing this hack the cool way instead of just soldering wires onto the buttons, is that you can use the 12 bit random field as well. You can have 4 lights on one network ID, and 4 others on another network ID, and control them from the same remote! Actually, you can control (2^12)*4 = 16384 lamps with this hack!
Step 7: Reverse engineering: what is 0 and what is 1
So I know which bits to ignore, the start bit and network ID bits. But I still don't know how the remote represents 0 and 1.
The remote has buttons for 4 lamps. The most logical way to represent these in the data frame is with a 2 bit binary number.
I started the logic analyzer again and pressed the ON button for lamp 1, 2, 3 and 4. Then I copy-pasted it into Gimp to get an overview.
Ok, so four bits change when i press an ON button. Two of the bits seems to be counting in binary from 0 to 3. It is most likely that they are the lamp address bits.
For lamp 1 they are both long pulses. For lamp 2 there is one short and one long pulse. This means that the least significant bit is sent first. The opposite of they way you would normally write a binary number.
Because it looks like the bits marked in green seems to be counting fro 0 to 3, I will assume that this is the lamp address bits. I don't know what the bits marked in blue are yet. Probably some kind of checksum to ensure error free communication.
Also, I have learned from this that in all likelihood, the bits are transmitted like this.
- Long pulse: 0
- Short pulse: 1
Step 8: Reverse engineering: figure out the rest of the data
At this point I know how 0 and 1 is represented, and I think I know which bits represent the lamp address. I also think that the last two bits are some form of check sum.
To figure out the rest of the data frame, I had to capture data for all possible button presses.
I started the logic analyzer and pressed ON for all 4 lamps, then OFF, then ALL ON and ALL OFF, and finally DIM + and DIM -.
To make it a little easier to debug, I typed all the captured frames into OpenOffice. I left out the first 13 bits, since I already knew what they were. I also left out some lines for the DIM buttons so that the screenshot would fit in the Instructables default image size.
It looks like the payload data has two bits for lamp address, then 4 bits for command data.
The command bits were easy enough to figure out. In the second picture, I have split the data into 3 columns, lamp address, command bits and checksum.
As you can see command bit 2 is only on when I press the ALL ON or ALL OFF buttons. That means that this bit is a broadcast bits that makes all the receivers listen.
Bit 3 is only on when I press the ON button or DIM - button. Lets call this command bit ON/OFF.
Bit 4 is only on when I press the DIM +/- buttons. Let's call it DIM.
Bit 5 is always low. Mystery bit. I have no idea what it does. Maybe it is just there because the checksum algorithm needs an even number of bits?
In the last picture, you can see that I have reverse engineered the entire data frame.
I assumed that the system had 4 lamp addresses, since there is 4 buttons on the remote. But another possibility is that the first three bits are lamp address, and that address 111 is broadcast. If this is the case, then you could have 7 lamps + broadcast on one network ID.
Step 9: Reverse engineering: checksum head-scratching
At this point I know what everything inside the data frame is. However, I have no idea how the checksum is calculated.
I started reading about checksums on Wikipedia, and tried applying all sorts of algorithms to the data. Nothing really seemed to work. Then I noticed that the checksum for any given button press was identical regardless of the random network ID. The checksum is only calculated based on the payload data.
My guess is that it is hard-coded into the remote to make the chip design simpler.
So with this in mind, I tried some simpler stuff. I noticed that sequences of 01 and 01 would cancel each other out and produce a checksum of 00, and that sequences of 01 and 10 would produce a checksum of 11.
This hinted towards a simple XOR algorithm.
After some trial and error, I found a simple algorithm that always produced the correct checksum.
The first two bits are XOR'ed with the 2nd two bits. The result of this is XOR'ed with the last two bits.
Check the image to see how the hcecksum calculation is done:
Step 10: Reverse engineering: timing
So now we know what everything is, the only thing left to do is figure out the timing of the signals.
Each bit is made up of a period of low, and a period of high. The entire cycle is always 1.92-ish milliseconds. A long pulse is 1.3 ms and a short pulse is 0.62 ms.
Each frame, start bit excluded, is 38.4 ms. 38.4/20 = 1.92 ms. So 1.92 ms seems like a good starting point for creating the right timings.
Step 11: Reverse engineering: Re-create the result
I know how it works, now lets try to recreate the result.
I set up a timer on an ATmega8.
The ATmega is running at 12MHz.
A timer is set up with a 128 prescaler in CTC mode. In the CTC mode, the timer is reset and an interrupt is called when the counter reaches a given timer compare value.
The counter is updated every 128 clock cycle. This means that I have to use 120 and 58 as the counter values for the short and long pulses.
(1000/12000000)*128*121 = 1.29 mS
(1000/12000000)*128*59 = 0.62 mS
This is pretty close to the original timings.
I put together a simple function to fill a buffer with the delay times for a given RF frame. An interrupt routine then switches an IO pin on and off and sets the timer compare value to create the desired length pulse.
I hooked it up to the logic analyzer and copy-pasted the result into Gimp. Bingo! The resulting signal is identical to the one from the RF remote :D
Code is included in a later step.
Step 12: Hardware: RF-fail and charge pump
I was quite happy with the result from the timer interrupt function. I expected everything to work when i plugged in the RF module... but it didn't :/
But the remote used a 9v battery.. maybe the RF module needed 9v? I connected the VCC on the RF module to +9V and tried again. Success!
But I don't want to have a 9v battery inside my gadget
After some googling, I found out that charge pumps was the right solution.
The charge pump consists of an inductor, a transistor, a diode and a capacitor.
I am no guru on analog electronics, but I'll try to explain how (i think) it works.
When the transistor is activated, the inductor is shorted to ground. When the transistor is deactivated, there is a flyback effect in the inductor that releases a short burst of high voltage. This voltage goes through the diode and is trapped in the capacitor.
A PWM signal is supplied to the transistor to do this 23.000 ish times per second. Every time the transistor releases, the voltage in the capacitor increases a little.
To keep the voltage from rising to high, the voltage on the capacitor is fed back to the micro controller via a zener diode. The analog comparator checks if the voltage is higher than 1.2 volts + the zener voltage.
The main loop of the microcontroller continuously checks if the voltage is below the threshold level. If it is, the pwm signal is started. If the threshold level is reached, pwm is disabled.
Step 13: Hardware: circuit
The circuit is pretty simple. All the complicated stuff is inside the RF module.
The main part is an ATmega8 AVR microcontroller. A USB connector is connected via some resistors and zener diodes. USB signals are 3.3v, so we need zeners to reduce the voltage.
The DATA line of the RF module is connected to an IO pin. The charge pump transistor and status LED are also connected to IO pins.
The feedback signal from the charge pump circuit is connected to the analog comparator. It compares the voltage to an internal reference voltage. I think the reference voltage is 1.1 ish volts. Not that important.
The circuit has the regular support circuitry like filtering capacitors too, and a 12MHz crystal.
(I forgot resistors on the two LEDs in the schematic. You can add appropriate resistors yourself.)
Step 14: Hardware: prototype
Before i turn on the soldering iron, I want to check if everything works the way it's supposed to.
I've only used USB one time before, and never with 5V system power and zener diodes like this design uses. Also, the box I'm putting the circuit inside is quite small, so there won't be any room for an ISP header. I'll program the AVR on the breadboard then move it over to the soldered circuit.
At this point, everything works. Let's hope it still works when everything is soldered in place ;)
Step 15: Software
Before the chip is removed from the breadboard, it needs some software.
The software is written in C and based on an example project from Objective Developments V-USB lib. This is a great piece of software, and it is free and open source for personal/non-commercial use.
I'm not going to go into great detail on how the software works. The reverse engineering is the emphasis of this Instructable. Here is the short version:
You need two programs to make this work. A program on your computer and firmware for the microcontroller.
The actual RF transmissions is done by an interrupt routine. I use a timer interrupt because this is the easiest way to get precise timing. The timer interrupt reads from a global buffer where the delay times are stored. I don't store the on/off status of the RF transmitter since on and off always alternates. I start with an off pulse, then alternate on and off pulses.
The buffer contains 42 values. There are 21 bits to be transmitted, and each has a low period and a high period. This configuration is not very RAM efficient, but the ATmega8 has plenty. I'll trade RAM for code readability instead of having unused ram!
The buffer is populated by the send_rf_frame(network, payload) function. It fills in the right timings in the rf buffer array, starting with the start bit, followed by the 12bit network id and the 8 bits of payload+checksum. When the buffer is populated, the buffer position variable is reset to 0, so that the interrupt routine will start working from bit 0 in the buffer.
When data is sent to the microcontroller over USB, the function usbFunctionSetup() is called. This is a function that you create and where you put your incoming USB code.
Depending on the request type sent from the PC, you can do different things inside this function. I have two request types configured, set_network_id and send_command.
The set_network_id request just takes the 12bit network id sent from the computer and stores it in a global integer value.
The send_command request calls send_rf_frame() and passes the received command byte to it. After that, the interrupt routine takes over.
Inside the main() loop:
usbPoll(); has to be called every few milliseconds (10 or 50, not sure) for the USB to work properly.
After that is done, the analog comparator is checked. If the charge pump voltage is too low, the charge pump is started. If it is at the desired voltage, the charge pump is shut off.
Finally, a status LED is set to ON if the rf_busy flag is active.
On the computer side I also modified the example provided by Objective Development. I added some code to parse arguments from the command line. I also wrote a function to create the payload byte. It takes arguments such as lamp number, on/off, broadcast.
The computer software uses libusb to communicate with the microcontroller.
I also created a small php script to call the commandline computer program when buttons on a web page is pressed. Open the webpage on your Android/iPhone and control the lights! :D
Step 16: Hardware: prepare the box
I have a nice little box that I want my project to live inside.
A couple of things had to be done to make this work.
1) The smallest circuit board i had was too small, and the next step up was too big. It had to be cut down to size. Easy enough. I just use a carpet knife and cut along the line where I want to cut. Do this 3-5 times and just break off the part you want to remove. I also had to do some dremeling to make room for some plastic bits inside the case.
2) The USB port needs to be accessible from the outside, so dremel away! I printed a template with the USB plug's dimensions and taped it to the case. I then used a dremel "router bit" to dremel it out. This bit can be drilled in, then moved sideways to cut out larger holes.
3) I added some spacers to keep the circuit board in place.
Step 17: Hardware: build the circuit
Finally, I get to use my soldering iron :D
The RF module takes up almost half the space inside the little box, so I had to make the circuit very compact.
I put everything as close together as I could and used magnet wire to make connections.
This is the first time I used magnet wire. It was great to work with! Even better than Kynar wire. I got the idea from this Instructable: http://www.instructables.com/id/Wiring-Pen/
I managed to squeeze everything in using a little over half the board. The RF module fits perfectly in the remaining space!
Step 18: Hardware: Alternative RF module
If you don't want to sacrifice your remote control, you can also buy an RF module.
The oscillator on the RF module has the text 433.92 written on it, so I assume it is a 434 MHz transmitter.
Sparkfun has a 434 MHz wireless transmitter that only costs $3.95.
Step 19: Check if it works
Time to see if all my hard work has payed off.
Everything works :D
Also, it can switch the lights a lot faster than the original remote :)