An HC-SR501 Motion Sensor Switches a Light Through a Relay
An automatic light for a hallway or a storeroom: a PIR sensor sees a person, and the ESP32 switches a relay. Setting the sensitivity and hold time on the sensor, the relay module, NO/COM contacts, and safety when working with 230 V.
About the project
A passive infrared (PIR) sensor reacts to changes in heat radiation: a moving person "crosses" its sensitivity zones. It emits nothing itself, so it draws microamps and runs for years. Add a relay and you have the classic automatic light.
What you need
- ESP32 DevKit
- HC-SR501 PIR sensor (or the tiny AM312)
- 5 V relay module (with an optocoupler or a transistor on its input)
- A lamp, or for the first build an LED with a resistor in its place
Wiring
| Module | Pin | ESP32 |
|---|---|---|
| HC-SR501 | VCC | 5V |
| OUT | GPIO27 | |
| GND | GND | |
| Relay | VCC | 5V |
| IN | GPIO26 | |
| GND | GND |
The HC-SR501 output gives 3.3 V even though the sensor runs from 5 V, so it can go straight to the ESP32. The HC-SR501 needs a ≥ 4.5 V supply; from 3.3 V it is unreliable.
The load is switched by the relay contacts: COM (common) and NO (normally open) close when the relay is on. NC is the opposite: open while the relay is on.
⚠️ 230 V can kill. The relay contacts are isolated from the ESP32, but make mains connections only with the circuit switched off, inside a closed enclosure, with a fuse. Without experience, learn with a 12 V lamp or an LED instead.
Setting up the HC-SR501
The sensor board has two trimmers and a jumper:
- Sx (sensitivity) — range up to ~7 m.
- Tx (hold time) — how many seconds the output stays HIGH after the last motion, from ~3 s to several minutes. When the ESP32 is in control, set it to the minimum and keep the timer in code.
- H/L jumper: "H" means repeated motion extends the pulse (what you want for a light), "L" gives single pulses.
After power-up the sensor needs up to a minute to settle, and it may trigger falsely during that time.
Code
// A PIR sensor switches a lamp through a relay
const int PIR_PIN = 27;
const int RELAY_PIN = 26;
void setup() {
Serial.begin(115200);
pinMode(PIR_PIN, INPUT);
pinMode(RELAY_PIN, OUTPUT);
}
void loop() {
bool motion = digitalRead(PIR_PIN) == HIGH;
digitalWrite(RELAY_PIN, motion ? HIGH : LOW);
static bool last = false;
if (motion != last) { // log only the changes
last = motion;
Serial.println(motion ? "Motion! Light ON" : "No motion. Light OFF");
}
delay(50);
}
Improvements
- A timer in code. Instead of Tx on the sensor, keep the light on for 60 s after the last motion:
lastMotion = millis()and amillis() - lastMotion < 60000check. - Only in the dark. Add a photoresistor on ADC1 and switch the light only below a light threshold.
- Active-LOW relays. Many modules switch on with LOW on IN. Then invert the logic, and in
setup()calldigitalWrite(RELAY_PIN, HIGH)first and only thenpinMode, so the relay does not click at start-up.