Skip to content

Instantly share code, notes, and snippets.

@bmatusiak
Last active April 5, 2025 23:16
Show Gist options
  • Save bmatusiak/6badef678d574abae9619de8d6eacb46 to your computer and use it in GitHub Desktop.
Save bmatusiak/6badef678d574abae9619de8d6eacb46 to your computer and use it in GitHub Desktop.
/*
Garage Door and Light Control with ESP32_Relay 4 - Conversation Summary:
This code implements a garage door and light control system using an ESP32_Relay 4 board.
It includes:
1. WiFi Connectivity: The ESP32 hosts a web server to control the garage door and light via a web interface.
2. Garage Door Control:
* Uses two relays to control the motor direction.
* Limit switches to stop the motor when the door is fully open or closed.
* A physical button that can be held for 3 seconds to control the door, or tapped for light control.
* Web interface control of the door.
* Dynamic button state updates on the web interface using JavaScript and JSON.
3. Light Control:
* A relay to control the light.
* A motion sensor to automatically turn the light on/off.
* Control of the light via the physical button (tap) and the web interface.
* Light state updates on the web interface.
4. Web Interface:
* A single HTML page with buttons for door and light control.
* JavaScript to dynamically update the button states based on the ESP32's status.
* JSON communication between the ESP32 and the web page for status updates.
5. Button control:
* Short tap of button toggles light
* Long hold of button changes garage door state.
6. Status updates:
* The web page updates every 5 seconds, to reflect current door and light states.
Pinout (ESP32_Relay 4):
- 5V: Do not use for programming
- TX: 3.3V level (Unused in this application)
- RX: 3.3V level (Unused in this application)
- GND: Ground
- GPIO0: 3.3V level (Connected to a push button for programming, Unused during normal operation)
- GPIO23: Status LED
- GPIO32: Relay #1 (Garage Door - Direction 1)
- GPIO33: Relay #2 (Garage Door - Direction 2) / Motion Sensor Input (Verify if this is correct Motion sensor input pin)
- GPIO25: Relay #3 (Light)
- GPIO26: Relay #4 (Unused in this application)
- GPIO27: Open Limit Switch Input
- GPIO15: Door Button Input
- Other GPIO Pins: Unused in this application
*/
#include <WiFi.h>
#include <WebServer.h>
#include <ArduinoJson.h>
// WiFi credentials
const char* ssid = "YOUR_WIFI_SSID";
const char* password = "YOUR_WIFI_PASSWORD";
// Garage Door Pins - Adjusted for your pinout
const int doorRelayPin1 = 32; // Relay #1
const int doorRelayPin2 = 33; // Relay #2
const int openLimitPin = 27; // Assuming this is still correct
const int closeLimitPin = 26; // Assuming this is still correct
const int doorButtonPin = 15; // Assuming this is still correct
// Light Pins - Adjusted for your pinout
const int lightRelayPin = 25; // Relay #3
const int motionSensorPin = 33; // Relay 2? is this correct?
// ESP32 Built-in LED - Adjusted for your pinout
const int builtInLedPin = 23; // Status LED
// Debouncing variables
unsigned long lastButtonPress = 0;
unsigned long debounceDelay = 50;
unsigned long buttonHoldStartTime = 0;
const unsigned long buttonHoldDuration = 3000; // 3 seconds
// Door State
enum DoorState {
CLOSED,
OPENING,
OPEN,
CLOSING,
STOPPED
};
DoorState currentState = CLOSED;
// Light State
bool lightOn = false;
WebServer server(80);
// setup(): Initializes the ESP32, connects to WiFi, and sets up the web server.
void setup() {
Serial.begin(115200);
pinMode(doorRelayPin1, OUTPUT);
pinMode(doorRelayPin2, OUTPUT);
pinMode(openLimitPin, INPUT_PULLUP);
pinMode(closeLimitPin, INPUT_PULLUP);
pinMode(doorButtonPin, INPUT_PULLUP);
pinMode(lightRelayPin, OUTPUT);
pinMode(motionSensorPin, INPUT); // Verify this is correct
pinMode(builtInLedPin, OUTPUT);
digitalWrite(doorRelayPin1, LOW);
digitalWrite(doorRelayPin2, LOW);
digitalWrite(lightRelayPin, LOW);
digitalWrite(builtInLedPin, LOW);
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(1000);
Serial.println("Connecting to WiFi...");
}
Serial.println("Connected to WiFi");
Serial.print("IP Address: ");
Serial.println(WiFi.localIP());
server.on("/", handleRoot);
server.on("/door", handleDoor);
server.on("/status", handleStatus);
server.on("/light", handleLight);
server.begin();
}
// loop(): Handles web server clients, checks door button, limit switches, and motion sensor.
void loop() {
server.handleClient();
checkDoorButton();
checkLimitSwitches();
checkMotionSensor();
}
// handleRoot(): Generates the HTML page with door and light control buttons and JavaScript for status updates.
void handleRoot() {
String html = "<!DOCTYPE html><html><head><title>Garage Door Control</title>";
html += "<script>";
html += "function updateStatus() {";
html += " fetch('/status')";
html += " .then(response => response.json())";
html += " .then(data => {";
html += " let doorButton = document.getElementById('doorButton');";
html += " let lightButton = document.getElementById('lightButton');";
html += " if (data.state === 'OPEN') {";
html += " doorButton.textContent = 'Close Door';";
html += " doorButton.disabled = false;";
html += " } else if (data.state === 'CLOSED' || data.state === 'STOPPED') {";
html += " doorButton.textContent = 'Open Door';";
html += " doorButton.disabled = false;";
html += " } else if (data.state === 'OPENING' || data.state === 'CLOSING') {";
html += " doorButton.textContent = data.state + '...';";
html += " doorButton.disabled = true;";
html += " }";
html += " lightButton.textContent = data.light ? 'Light Off' : 'Light On';";
html += " });";
html += "}";
html += "function triggerDoor() { fetch('/door'); }";
html += "function triggerLight() { fetch('/light'); }";
html += "setInterval(updateStatus, 5000);";
html += "</script></head><body>";
html += "<h1>Garage Door Control</h1>";
html += "<button id='doorButton' onclick=\"triggerDoor()\">Open Door</button><br><br>";
html += "<button id='lightButton' onclick=\"triggerLight()\">Light On</button>";
html += "<script>updateStatus();</script>";
html += "</body></html>";
server.send(200, "text/html", html);
}
// handleDoor(): Triggers the door's open/close/stop actions and sends an OK response.
void handleDoor() {
if (currentState == CLOSED || currentState == STOPPED) {
openDoor();
} else if (currentState == OPEN) {
closeDoor();
} else if (currentState == OPENING || currentState == CLOSING) {
stopDoor();
}
server.send(200, "text/plain", "OK");
}
// handleStatus(): Sends the current door and light status as a JSON response.
void handleStatus() {
StaticJsonDocument doc;
String stateString;
switch (currentState) {
case CLOSED: stateString = "CLOSED"; break;
case OPENING: stateString = "OPENING"; break;
case OPEN: stateString = "OPEN"; break;
case CLOSING: stateString = "CLOSING"; break;
case STOPPED: stateString = "STOPPED"; break;
default: stateString = "UNKNOWN"; break;
}
doc["state"] = stateString;
doc["light"] = lightOn;
String jsonString;
serializeJson(doc, jsonString);
server.send(200, "application/json", jsonString);
}
// handleLight(): Toggles the light state and sends an OK response.
void handleLight() {
toggleLight();
server.send(200, "text/plain", "OK");
}
// checkDoorButton(): Checks the physical door button for short press (light toggle) or long press (door control).
void checkDoorButton() {
if (digitalRead(doorButtonPin) == LOW) {
if (buttonHoldStartTime == 0) {
buttonHoldStartTime = millis();
} else if ((millis() - buttonHoldStartTime) >= buttonHoldDuration) {
if (currentState == CLOSED || currentState == STOPPED) {
openDoor();
} else if (currentState == OPEN) {
closeDoor();
} else if (currentState == OPENING || currentState == CLOSING) {
stopDoor();
}
buttonHoldStartTime = 0;
}
} else {
if (buttonHoldStartTime > 0 && (millis() - buttonHoldStartTime) < buttonHoldDuration) {
toggleLight();
}
buttonHoldStartTime = 0;
}
}
// toggleLight(): Toggles the light state and updates the light relay.
void toggleLight() {
lightOn = !lightOn;
digitalWrite(lightRelayPin, lightOn);
Serial.println(lightOn ? "Light On (tap/web)" : "Light Off (tap/web)");
}
// checkLimitSwitches(): Checks the limit switches and stops the door motor when a limit is reached.
void checkLimitSwitches() {
if (digitalRead(openLimitPin) == LOW && currentState == OPENING) {
stopDoor();
currentState = OPEN;
Serial.println("Door Open Limit Reached");
}
if (digitalRead(closeLimitPin) == LOW && currentState == CLOSING) {
stopDoor();
currentState = CLOSED;
Serial.println("Door Closed Limit Reached");
}
}
// openDoor(): Activates the relays to open the garage door.
void openDoor() {
digitalWrite(doorRelayPin1, HIGH);
digitalWrite(doorRelayPin2, LOW);
currentState = OPENING;
Serial.println("Opening Door");
digitalWrite(builtInLedPin, HIGH);
}
// closeDoor(): Activates the relays to close the garage door.
void closeDoor() {
digitalWrite(doorRelayPin1, LOW);
digitalWrite(doorRelayPin2, HIGH);
currentState = CLOSING;
Serial.println("Closing Door");
digitalWrite(builtInLedPin, HIGH);
}
// stopDoor(): Deactivates the relays to stop the garage door motor.
void stopDoor() {
digitalWrite(doorRelayPin1, LOW);
digitalWrite(doorRelayPin2, LOW);
currentState = STOPPED;
Serial.println("Door Stopped");
digitalWrite(builtInLedPin, LOW);
}
// checkMotionSensor(): Checks the motion sensor and turns the light on/off accordingly.
void checkMotionSensor() {
if (digitalRead(motionSensorPin) == HIGH) {
if (!lightOn) {
turnLightOn();
}
} else {
turnLightOff();
}
}
// turnLightOn(): Turns the light on and updates the light relay.
void turnLightOn() {
digitalWrite(lightRelayPin, HIGH);
lightOn = true;
Serial.println("Light On (motion)");
}
// turnLightOff(): Turns the light off and updates the light relay.
void turnLightOff() {
digitalWrite(lightRelayPin, LOW);
lightOn = false;
Serial.println("Light Off (no motion)");
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment