All TILs

TIL: A garage door button is just two wires touching (Garage Door Project, Part 1/5)

Series: Opening my garage door with my phone — Part 1 of 5 Learned: 2–4 July 2026

I can open my garage door with a remote. I wanted to open it with an API call instead. I know how to write the software, but I had no idea what electronics sit between “HTTP request” and “door moves”. This post covers the first few days: figuring out the hardware, digging through an old microcontroller kit, and getting a relay to click on my desk.

1. The opener only needs a “dry contact”

The wall button on a garage door opener doesn’t send any power anywhere. It just shorts two low-voltage terminals together for a moment. That’s called a dry contact.

So the whole “smart” part boils down to: something that can briefly connect two wires when told to. That something is a relay.

  • Wire the relay’s COM (common) and NO (normally open) terminals in parallel with the existing wall button.
  • When the relay closes for ~0.5 s, the opener thinks someone pressed the button.
  • The existing remotes and wall button keep working. Nothing is replaced, only added.

Optional extra I’ve parked for later: a magnetic reed switch on the door frame to know whether the door is actually open or closed.

2. Inventory what you already have

I had an old kit from Hobby Components lying around. Relevant finds:

  • Arduino Uno (clone)
  • Keyes 5V relay module with three pins: S (signal), + (5V) and - (GND), plus three screw terminals: NC, COM, NO
  • Arduino Prototype Shield v5, a board that stacks on the Uno for soldering your own circuit onto

What I learned: the Uno has no networking at all. To expose an API you need something with Wi-Fi or Ethernet. The options were:

Option Notes
ESP8266 (ESP-01) module next to the Uno Runs at 3.3V, talks to the Uno over serial with AT commands. Fiddly.
Replace the Uno with an ESP32 / Wemos D1 Built-in Wi-Fi, can host the API itself. Logic is 3.3V, so a 5V relay may need a level shifter.
Uno + Raspberry Pi over USB Pi runs the API in a “real” language, Uno only flips the relay.

I chose the last one: an old Raspberry Pi as the brain and the Arduino as the muscle. (Spoiler: the Pi being old became its own adventure, see Part 2.)

3. Wiring the relay for a bench test

Before touching the garage at all, test on the desk with the Arduino plugged into the computer.

Relay pin Arduino pin
+ 5V
- GND
S Digital pin 7

Leave the COM/NO screw terminals empty during the bench test.

What I learned: many of these cheap relay modules are active LOW: writing LOW to the signal pin turns the relay on. So the sketch starts by writing HIGH to keep it off.

const int relayPin = 7;

void setup() {
  pinMode(relayPin, OUTPUT);
  digitalWrite(relayPin, HIGH);   // relay off (active LOW module)
  Serial.begin(9600);
}

void loop() {
  if (Serial.available() > 0) {
    char c = Serial.read();
    if (c == 'o' || c == 'O') {
      digitalWrite(relayPin, LOW);  // "press the button"
      delay(500);
      digitalWrite(relayPin, HIGH); // "release"
    }
  }
}

Open the Serial Monitor at 9600 baud, type o, press Enter → click, click. The little LED on the relay blinks with it.

Bonus check: put a multimeter in continuity mode across COM and NO. It should beep for exactly the half second the relay is closed.

4. The detour: Johnny-Five and Firmata

My first instinct as a JavaScript developer was to drive the board from Node.js with Johnny-Five. It timed out.

What I learned: Johnny-Five needs the StandardFirmata sketch uploaded to the board first (File → Examples → Firmata → StandardFirmata, the plain one, not the Plus version). Without it the computer and the board don’t speak the same protocol. In the end I didn’t need Johnny-Five at all, because a ten-line sketch listening on serial was simpler.

5. “programmer is not responding”

Uploading anything to the Uno failed with:

avrdude: stk500_recv(): programmer is not responding
avrdude: stk500_getsync() attempt 1 of 10: not in sync: resp=0x00

This means the computer is shouting down the cable and the board isn’t answering. The checklist that helped:

  • Pick the right port. On a Mac, ignore Bluetooth-Incoming-Port; look for /dev/cu.usbmodem… (or /dev/cu.wchusbserial… on clones).
  • Use a data cable, not a charge-only one. Lights turning on prove nothing.
  • Take the shield off and upload to the bare Uno first. A stacked shield can interfere with the serial pins.
  • Close anything else holding the port: the Serial Monitor, or a Node script still running in another terminal.
  • Clones may need the CH340 driver.

6. Soldering is optional (for now)

The Arduino pins are small and I was nervous about bridging two of them with solder. Alternatives that are perfectly fine for this phase:

  • Female-to-female / male-to-female jumper wires straight onto the header pins
  • A solderless breadboard (great for testing, less great long-term in a cold garage)
  • A screw terminal shield: strip the wire, tighten a screw, done

Takeaways

  • A garage door opener’s button input is a dry contact. A relay across COM/NO is all it takes.
  • An Arduino Uno can’t do networking on its own; decide early what provides the network.
  • Cheap relay modules are often active LOW.
  • Bench test first: Serial Monitor + listening for the click + a multimeter on COM/NO.
  • “programmer is not responding” is almost always port, cable, shield or a busy serial port.

Next up: getting a first-generation Raspberry Pi onto my network, which took far longer than the electronics.