Garage Door Hacking

i thought this was supposed to be the most advanced security system...

May 6, 2022 3 min read
Post Feature Image

Our garage doors are integrated into the MyQ online control platform, but frankly, they can't stay connected to Wi-Fi to save their lives. From a quick search online, it seems we aren't the only ones with this problem. We convinced Chamberlain to send us an Ethernet gateway, and it's fixed all the issues we've had, but there's still something about it that irks me.

it requires the cloud.

None of our other smart devices require the cloud, and I really prefer devices with local control, as you can't control what you don't see. With locally controlled devices, there's really not much that can go wrong.

I thought about it for a while, and eventually realized that if the Ethernet gateway was sending control commands and receiving door states from the openers, then I could too.

I found the secplus Python library on GitHub that claimed it could decode packet captures of Security+ and Security+ 2.0 openers, so I started to work on a RF transceiver to integrate with it.

Looking at the FCC docs for the visor remotes, they communicate over the 315Mhz band, and according to the secplus docs, they transmit two sets of 62 bit packets each with a 21 bit preamble and 1 bit to state which of the two packets it is.

Connecting my logic analyzer to a cheap MX-RM-5V RF receiver, I realized that they transmitted using basic Manchester encoding at 2Kbps.

I connected the receiver to an Arduino Micro and wrote a basic Manchester decoder that outputs the packets over USB to be decoded by the library.

void emitBit(byte state) {
  if(!gotFirstBit) {
    gotFirstBit = true;
  gotFirstBit = false;
  packet[packetPos] = state;

  if(packetPos == 62) {
    if(completedPacketsPos != 10) {
      //Copy completed packets to storage.
      memcpy(completedPackets[completedPacketsPos], packet, 62);
    memset(packet, 0, 62);
    packetPos = 0;
  } else {

void isr() {
  rxState = PINE & B01000000;
  long now = micros();
  unsigned long timeDiff = now - lastBitChange;
  lastBitChange = now;

  if(packetPos == 0 && gotFirstBit == false) {
  } else {
    if(timeDiff > 225 && timeDiff < 275) {
    } else if(timeDiff > 475 && timeDiff < 525) {
      gotFirstBit = true;
    } else if(timeDiff > 5000) {
      packetPos = 0;

I used an ISR to detect when the RX pin had a changed state, and depending on how long it took to change states, the ISR requests to add a bit to the received Manchester signal. This took a lot of trial and error, but it's pretty solid now.

The packets then get put into a buffer to be read out once there is no activity for 100ms.

The Python code is pretty basic, it simply allows you to decode incoming packets to get the fixed and rolling codes to retransmit later. It also can function as an emulated remote, sending Manchester data to the Arduino for transmission.

import secplus
import serial

fixed_code = 1234565432
rolling = 234123123
roll_i = 0

print('Searching for bridge...')
ports =
for port in ports :
    print('Found port '+ port.device)

ser = serial.Serial(port.device)
if ser.isOpen():

ser = serial.Serial(port.device, 115200, timeout=1)

code = ""

while True:
    while ser.in_waiting:  # Or: while ser.inWaiting():
    act = input("Action:")
    if act == "s":
        ser.write((code + "\n").encode())
    elif act == "g":
        code = secplus.encode_v2_manchester(rolling + (roll_i * 2), fixed_code);
        code = ''.join([str(x) for x in code])
        roll_i = roll_i + 1;

It's pretty basic right now, but it controls the garage doors. I don't see how the Ethernet gateway is getting status updates from the doors, but looking at the FCC docs, it seems that it operates on the 900Mhz band, and there aren't any decoders online.

I might have to write my own, stay tuned!

Don't miss out!Stay in the loop by signing up for my (infrequent) newsletter. No spam, unsubscribe anytime.