I recently built a replacement remote control system for an electric garage door after the old remote had given up for the babillionth time.
I’m sure the old one would have been possible to repair once again but it was 35 years old and not much fun.
Old receiver and transmitter
Pictures from left to right:
- The front of the old receiver with an indoor open/close button.
- Inside of the receiver. Nice and clean and very analog electronic fossil.
- The guts from the old transmitter. The case that this used to sit in was removed and reused for the new transmitter.
- Backside of the old receiver. The label reads “Chambron Radio Control Model C-2216”. There’s also a letter to Mr. Serviceman.
Who knows anything about this thing?
The motor to the door is controlled by shorting two cables and thereby closing a circuit. A short pulse is enough to start the motor. If the door is closed it will open. If it’s already open, the door will close. If the motor is moving when the pulse arrives it will change direction.
The new solution
I wanted a new solution that would be a bit more secure and hopefully have the same range as the old one had and I wanted to base it on an ATmega328P with an Arduino core and some really cheap 433Mhz ASK modulated RX/TX modules that can be found everywhere on eBay.
I have used these RF modules before and know they have a decent range and can penetrate doors, windows and even walls in some cases. They are also easy to interface with using the VirtualWire library for Arduino. The downside is that they only work in one direction.
Even if it wasn’t really necessary in this case I wanted to implement some kind of security or obfuscation of the commands sent over the air. This is something of a challenge with a system that only communicates in one direction since it’s not possible to use a challenge/response based protocol and the Arduino doesn’t have storage enough to store predefined passcode sequenced.
This was what I wanted:
- Use one time passcodes so that simple sniffing wouldn’t be enough to grant access forever
- Not have to store the passcode sequence in advance
- Generate passcodes that don’t seem to follow a clear pattern to complicate analysis of the commands
The protocol I ended up with works like this:
- The receiver and transmitter are paired to use a sequence of numbers starting at a large random number that is decided by the transmitter. The number is sent to the receiver during the pairing process which more or less is a one time operation.
- When the transmitter wants to send a open/close command to the door it increases the number by one and hashes the number using MD5
- The receiver knows which number it expects and when it receives a hash from the transmitter it hashes the expected number and compares the resulting hash with the received hash. If the hash is correct it sends a pulse to the door so that it will be opened or closed. The next expected passcode is simply the last passcode +1.
- If the hashes didn’t match it increases the expected number one by one and creates new hashes to compare with up to fifty times to allow for missed passcodes that might have been sent when the transmitter was out of range or similar. This gives us a sliding window of 50 valid passcodes. As soon as a matching passcode is found, the receiver is synced with the number received and a pulse is sent to the door.
- The current numbers are stored in the ATmega EEPROM in both transmitter and receiver.
I’m using ATmega328P (DIP-28) for both transmitter and receiver. I would probably have used an ATtiny for the transmitter to try and reduce size if it wasn’t for the MD5 library that adds a whopping 12KB to the binary sketch size making the total sizes around 18KB for both receiver and transmitter.
34.5cm or 69.1cm would also have been OK if there was space enough in the box but looping the antenna round and round in several loops close to each other would not do much good.
The board definition I put in boards.txt is:
328_8.name=ATmega328, 8MHz Internal OSC, BOD disabled
This configuration makes it possible to use a bootloader if desired but I upload the sketch to the chip using an ISP programmer so I don’t have a need for a bootloader in this case.
For other applications using 8MHz internal oscillator, I have always used the ATmegaBOOT bootloader because I’ve not had any luck compiling the Optiboot for this. If someone has a Makefile that is generating a working Optiboot image for 8MHz internal, please let me know in the comments.
I got rid of the old receiver case because it was made of thick metal and I was afraid that it could affect the reception in a negative way but it would probably have been OK due to the external antenna. There are plenty of boxes suitable for the transmitter but I settled with a minimal, cheap plastic box for electric installations. I should have picked a bigger one because this was a pretty tight fit.
The connector on the right side is the DC plug for the power supply.
To the left is an LED that flashes during pairing and is lit during reception of data. The button on the same side as the LED is the pairing button.
The antenna which is rolled up outside the box in this picture should normally be stretched out. It consists of 69.1 cm of wire soldered to the antenna connection on the RF module.
How well does this solution work?
50 metres (with the transmitter in a car, new batteries in the transmitter and the receive inside the closed garage door)
This is more than enough and just as good as the old system ever was.
Power consumption: Receiver
Approx. 30mA @ 5V
I haven’t done anything to reduce the power consumption on the receiver since it will be powered by a DC adapter.
Power consumption: Transmitter
The transmitter runs straight from three AA batteries that should (until depleted) keep the voltage over 3.0V at all times which is what the RF transmitter module needs to work properly. The maximum 4.5V is also perfectly fine for both RF module and ATmega.
Current during transmission (basically as long as the button is pressed) is ~25mA @ 4.5V
During powerdown which is the normal condition when no button is pressed, the current drops to 0.1uA
This is as low as it gets with an ATmega328P and to achieve this brownout detection has to be disabled.
Possible future improvements
- Add possibility to use more than one transmitter.
- Prevent brute force attacks by adding growing delays after incorrect codes
- Etch custom PCBs, possibly using SMT components for transmitter
- Add more decoupling capacitors, at least for the receiver.
- Shrink the transmitter by creating better and smaller case and PCB for the transmitter
- Fix bugs
- Well… let’s see how it works
Download both sketches and all libraries in one zip file: Code