Project Overview
This project creates a hands-free garage door opener that detects when your car approaches and automatically opens the door. Using a PIR sensor mounted near the driveway, the system detects your vehicle’s heat signature and triggers a relay that simulates pressing the garage door opener button.
Difficulty: Intermediate
Estimated time: 2-3 hours
Estimated cost: $25-35
How It Works
A PIR sensor placed at the driveway entrance detects the heat signature of your approaching car. When a vehicle is detected (and after a short delay to ensure it’s your car approaching, not a person walking), the system activates a relay that connects across the garage door opener’s wall button terminals, simulating a button press. The door opens automatically as you approach.
An optional reed switch on the garage door can detect whether the door is already open, preventing unnecessary triggering.
Materials Needed
- ESP32 or Arduino Uno (1)
- HC-SR501 PIR sensor (1) – or AM312 for lower power
- Relay module (5V, single channel)
- Reed switch (normally open) for door position detection (optional)
- Magnet (for reed switch)
- 12V to 5V converter (if using 12V garage door supply)
- Jumper wires
- Waterproof enclosure (for outdoor PIR sensor)
- Wire strippers and soldering equipment
Understanding Your Garage Door Opener
Most garage door openers have a wall button that simply shorts two wires together to trigger the door. You can connect a relay across these terminals to simulate a button press. The wires are low voltage (usually 12-24V DC), so it’s safe to interface with.
Important: Identify the correct wires before connecting. Use a multimeter to find the pair that shows continuity when the wall button is pressed.
Circuit Diagram
Connection Table
| Component | Pin | ESP32 Pin | PIR Sensor | VCC | 3.3V or VIN | PIR Sensor | GND | GND | PIR Sensor | OUT | GPIO 4 | Relay Module | VCC | 5V (if available) or VIN | Relay Module | GND | GND | Relay Module | IN | GPIO 5 | Reed Switch | One side | GPIO 12 (with 10k pull-up) | Reed Switch | Other side | GND |
|---|
Garage Door Opener Connection
Connect the relay COM and NO terminals across the garage door opener’s wall button terminals. When the relay activates, it shorts these wires, triggering the door.
Arduino Code
// Automatic Garage Door Opener with PIR
// Opens garage door when vehicle approaches
#include <WiFi.h>
#include <ESPmDNS.h>
#include <WebServer.h>
// Pin definitions
const int pirPin = 4;
const int relayPin = 5;
const int doorSensorPin = 12;
// Configuration
const char* ssid = "YourWiFiSSID";
const char* password = "YourWiFiPassword";
WebServer server(80);
// Timing
unsigned long lastTriggerTime = 0;
const unsigned long triggerDelay = 1000; // 1 second after detection
const unsigned long relayDuration = 500; // Hold relay for 0.5 seconds
const unsigned long cooldownPeriod = 30000; // 30 seconds between triggers
bool motionDetected = false;
bool doorOpen = false;
unsigned long motionStartTime = 0;
void setup() {
Serial.begin(115200);
pinMode(pirPin, INPUT);
pinMode(relayPin, OUTPUT);
pinMode(doorSensorPin, INPUT_PULLUP);
digitalWrite(relayPin, LOW);
// Connect to WiFi
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("WiFi connected");
Serial.print("IP address: ");
Serial.println(WiFi.localIP());
// Setup web server for manual control
server.on("/", handleRoot);
server.on("/open", handleOpen);
server.on("/close", handleClose);
server.on("/status", handleStatus);
server.begin();
// Read initial door state
doorOpen = digitalRead(doorSensorPin) == LOW;
Serial.println("Garage Door Opener Ready");
Serial.println("Waiting 60 seconds for PIR warm-up...");
delay(60000);
}
void handleRoot() {
String html = "<html><head><title>Garage Door Control</title>";
html += "<meta name='viewport' content='width=device-width'></head><body>";
html += "<h1>Garage Door Control</h1>";
html += "<p>Door status: ";
html += doorOpen ? "OPEN" : "CLOSED";
html += "</p>";
html += "<p><a href='/open'><button>Open Door</button></a></p>";
html += "<p><a href='/close'><button>Close Door</button></a></p>";
html += "</body></html>";
server.send(200, "text/html", html);
}
void handleOpen() {
if (!doorOpen) {
triggerDoor();
}
server.sendHeader("Location", "/");
server.send(303);
}
void handleClose() {
if (doorOpen) {
triggerDoor();
}
server.sendHeader("Location", "/");
server.send(303);
}
void handleStatus() {
String json = "{\"door\":" + String(doorOpen ? "true" : "false") + "}";
server.send(200, "application/json", json);
}
void triggerDoor() {
Serial.println("Triggering garage door");
digitalWrite(relayPin, HIGH);
delay(relayDuration);
digitalWrite(relayPin, LOW);
// Wait for door to start moving before updating state
delay(2000);
doorOpen = digitalRead(doorSensorPin) == LOW;
lastTriggerTime = millis();
}
void checkVehicleApproach() {
bool pirState = digitalRead(pirPin) == HIGH;
if (pirState && !motionDetected) {
// Motion detected - start timer
motionDetected = true;
motionStartTime = millis();
Serial.println("Motion detected - waiting for confirmation");
}
if (motionDetected && (millis() - motionStartTime > triggerDelay)) {
// Confirmed motion - trigger door
if (!doorOpen && (millis() - lastTriggerTime > cooldownPeriod)) {
Serial.println("Vehicle approaching - opening door");
triggerDoor();
} else {
Serial.println("Door already open or cooldown active");
}
motionDetected = false;
}
// Reset if motion stops before confirmation
if (motionDetected && !pirState && (millis() - motionStartTime < triggerDelay)) {
motionDetected = false;
Serial.println("Motion cleared - no trigger");
}
}
void loop() {
server.handleClient();
// Update door state from reed switch
bool newDoorState = digitalRead(doorSensorPin) == LOW;
if (newDoorState != doorOpen) {
doorOpen = newDoorState;
Serial.print("Door state changed: ");
Serial.println(doorOpen ? "OPEN" : "CLOSED");
}
checkVehicleApproach();
delay(100);
}
ESP32 Version with Deep Sleep (Battery-Powered)
For a battery-powered outdoor unit:
// Battery-Powered Version with Deep Sleep
#include <esp_sleep.h>
const int pirPin = 4;
const int relayPin = 5;
RTC_DATA_ATTR unsigned long lastTriggerTime = 0;
void setup() {
Serial.begin(115200);
pinMode(pirPin, INPUT);
pinMode(relayPin, OUTPUT);
digitalWrite(relayPin, LOW);
esp_sleep_enable_ext0_wakeup((gpio_num_t)pirPin, 1);
// Check if woken by PIR
if (esp_sleep_get_wakeup_cause() == ESP_SLEEP_WAKEUP_EXT0) {
if (millis() - lastTriggerTime > 30000) {
digitalWrite(relayPin, HIGH);
delay(500);
digitalWrite(relayPin, LOW);
lastTriggerTime = millis();
}
}
esp_deep_sleep_start();
}
void loop() {}
Installation Steps
- Test garage door wiring: Identify the wall button terminals on your garage door opener. Measure voltage and confirm which wires are shorted when the button is pressed.
- Connect relay: Wire the relay COM and NO terminals across the wall button terminals. Test with a multimeter to ensure it shorts correctly.
- Mount PIR sensor: Place sensor at driveway entrance, 1.5-2m high, angled to detect vehicle approach. Ensure it doesn't detect passing pedestrians.
- Position reed switch: Mount reed switch on door frame and magnet on door to detect open/closed state.
- Enclose electronics: Place ESP32 and relay in waterproof enclosure near the garage door opener.
- Power: Use 12V-5V converter if powering from garage door opener supply, or use separate power adapter.
- Test: Drive vehicle toward garage and verify door opens automatically.
Adjusting Sensitivity
- Adjust PIR sensitivity potentiometer to avoid detecting people walking by.
- Point sensor downward to focus on vehicle height.
- Add a shield to limit field of view to the driveway only.
- Adjust
triggerDelayto require longer detection before triggering.
Project Extensions
- Wi-Fi dashboard: Add web interface to monitor door status and manually control.
- Smart home integration: Use ESPHome to integrate with Home Assistant.
- Camera integration: Add ESP32-CAM to capture images when door opens.
- Multiple vehicles: Add RFID or license plate recognition for vehicle-specific control.
- Timer close: Automatically close door after set time if left open.
- Light control: Turn on garage lights when motion detected.
Troubleshooting
- Door not opening: Check relay wiring. Use multimeter to verify relay contacts close when triggered.
- False triggers: Adjust PIR sensitivity or reposition to avoid pedestrians and street traffic.
- Door opens too late: Reduce
triggerDelay. - Door opens when already open: Add door position sensor and check logic.
- Wi-Fi connection issues: Ensure ESP32 is within range of router.
Conclusion
This automatic garage door opener adds convenience and a modern touch to your home. With proper installation, it will reliably open your garage door as you arrive, eliminating the need to fumble for a remote control.
