#include <ESP32Servo.h>
// defining pins
// PIR sensor provides digital high when motion is detected
// button uses INPUT_PULLUP: high when idle and low when pressed
// servo uses PWM signal to control position
const int PIR_PIN = 13;
const int BUTTON_PIN = 12;
const int SERVO_PIN = 18;
// servo positions for gate states
// 0 = closed, 90 degrees = open
const int SERVO_CLOSED = 0;
const int SERVO_OPEN = 90;
// timings in ms
// OPEN_DURATION controls how long long gate stays open (10sec)
// added DEBOUNCE_DELAY to filter out any unwanted rapid button signals
//which is why its short, only 50ms
const unsigned long OPEN_DURATION = 10000; // 10 seconds
const unsigned long DEBOUNCE_DELAY = 50; // 50 ms
Servo gateServo;
// gate state, initially closed
// gateCloseTime stores when the gate should close in the future
bool gateIsOpen = false;
unsigned long gateCloseTime = 0;
// button debounce variables for pushbutton
// ensure only one clean press is detected
int lastButtonReading = HIGH;
int stableButtonState = HIGH;
unsigned long lastDebounceTime = 0;
// stores previous PIR state for edge detection
// to ensure motion only detected once per trigger
int lastPirState = LOW;
// gate open logic, opens gate by rotating servo to open position
//future closing time set using millis()
//outputs all this info to serial monitor
void openGate(const char* source) {
gateServo.write(SERVO_OPEN);
gateIsOpen = true;
gateCloseTime = millis() + OPEN_DURATION;
Serial.print("Gate OPENED by ");
Serial.print(source);
Serial.print(" at ");
Serial.print(millis());
Serial.println(" ms");
}
// extend gate open duration if triggered whilst open
// updates the GateCloseTime variaable
void extendGateTime(const char* source) {
gateCloseTime = millis() + OPEN_DURATION;
Serial.print("Gate already open. Time EXTENDED by ");
Serial.print(source);
Serial.print(" at ");
Serial.print(millis());
Serial.println(" ms");
}
//close gate by returning servo to close position
// triggered automatically at gateCloseTime
void closeGate() {
gateServo.write(SERVO_CLOSED);
gateIsOpen = false;
Serial.print("Gate CLOSED automatically at ");
Serial.print(millis());
Serial.println(" ms");
}
//runs once at startup to initialise
void setup() {
Serial.begin(115200);
delay(1000); // gives serial time to initialise
//serial monitor initialising outputs
Serial.println("System started");
Serial.println("Gate is initially CLOSED");
//INPUT_PULLUP for button avoids floating input
//keeps stable high when not pressed through esp32 3v3
pinMode(PIR_PIN, INPUT);
pinMode(BUTTON_PIN, INPUT_PULLUP);
// configuring servo with 50hz PWM signal
gateServo.setPeriodHertz(50);
gateServo.attach(SERVO_PIN, 500, 2400);
gateServo.write(SERVO_CLOSED);
}
//loop runs continuously and handles real time system behaviour
// uses millis() for non blocking timing so system remains responsive
void loop() {
unsigned long now = millis();
// detect PIR motion using edge detection: low to high
// prevents multiple triggers from continuous high signal
int pirState = digitalRead(PIR_PIN);
if (pirState == HIGH && lastPirState == LOW) {
if (!gateIsOpen) {
openGate("PIR motion sensor");
} else {
extendGateTime("PIR motion sensor");
}
}
lastPirState = pirState;
// pushbutton debounce logic to ensure button press is stable
// only registers next input if signal remains unchanged for a short time
int currentReading = digitalRead(BUTTON_PIN);
if (currentReading != lastButtonReading) {
lastDebounceTime = now;
}
if ((now - lastDebounceTime) > DEBOUNCE_DELAY) {
if (currentReading != stableButtonState) {
stableButtonState = currentReading;
// button pressed = low because INPUT_PULLUP is used
if (stableButtonState == LOW) {
if (!gateIsOpen) {
openGate("manual pushbutton");
} else {
extendGateTime("manual pushbutton");
}
}
}
}
lastButtonReading = currentReading;
// automatically close gate when current time exceeds stored close time
// nonblocking timing to allow continuous input checking
if (gateIsOpen && now >= gateCloseTime) {
closeGate();
}
}Loading
esp32-devkit-c-v4
esp32-devkit-c-v4