← All projects
Automation Beginner

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.

Difficulty
Beginner
Time
30 min
Category
Automation
Updated

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 a millis() - lastMotion < 60000 check.
  • 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() call digitalWrite(RELAY_PIN, HIGH) first and only then pinMode, so the relay does not click at start-up.