A PIR sensor and a D1 mini make a motion-triggered light, an intrusion alert or a room-occupancy logger. Power the HC-SR501 from the 5V pin, connect OUT to D5, and the ESP8266 can react the instant someone walks in.
The sketch uses an interrupt, which brings out two ESP8266 rules: the handler must be in IRAM, and D0 can’t do interrupts at all. It also covers the most common D1 mini PIR complaint — false triggers caused by the board’s own Wi-Fi.
Wiring
| Wemos D1 mini | HC-SR501 PIR motion sensor | Wire | Notes |
|---|---|---|---|
| 5V | VCC | red | The HC-SR501 wants 4.5 V or more; the 5V pin gives USB 5 V. |
| D5 | OUT | yellow | OUT is a 3.3 V signal, safe on the ESP8266. |
| GND | GND | black |
4.5–20 V (5 V typical) part, 3.3 V board. Power it from 5 V (VIN/5V pin). Its output is a 3.3 V logic signal, so it connects directly to ESP32, Pico and Uno GPIOs.
Board quirks
- Interrupt handlers on the ESP8266 must be marked
IRAM_ATTR. Without it the board crashes (or refuses to attach the interrupt) the moment motion is seen. - D0 (GPIO16) has no interrupt on the ESP8266, which is why the PIR goes on D5. D1–D8 all support
attachInterrupt. - The HC-SR501 is known to false-trigger when an ESP8266 transmits close by — its amplifier picks up the 2.4 GHz bursts. Keep the PIR at least 10–15 cm from the D1 mini’s antenna end, or put it on a short cable.
- After power-up the sensor needs about a minute to settle; expect a few motion events in that time. The on-board time and sensitivity pots set how long OUT stays high and how far it sees.
- The built-in LED is on D4 and lights when the pin is LOW, so the sketch writes LOW for “motion”.
Code
// HC-SR501 PIR motion sensor on a Wemos D1 mini (ESP8266), read with an interrupt.
// VCC → 5V, OUT → D5 (GPIO14), GND → GND. The built-in LED (D4) lights while motion is seen.
const uint8_t PIR_PIN = D5; // GPIO14 - D0 can't do interrupts on the ESP8266
const uint8_t LED_PIN = LED_BUILTIN; // D4 / GPIO2, lit when LOW
volatile bool changed = false;
// Interrupt handlers must live in IRAM on the ESP8266, or the board crashes when one fires.
void IRAM_ATTR onPirChange() {
changed = true;
}
void setup() {
Serial.begin(115200);
Serial.println();
pinMode(PIR_PIN, INPUT);
pinMode(LED_PIN, OUTPUT);
digitalWrite(LED_PIN, HIGH); // LED off
attachInterrupt(digitalPinToInterrupt(PIR_PIN), onPirChange, CHANGE);
Serial.println("PIR on D5 - give it about 60 s to settle after power-up");
}
void loop() {
if (changed) {
changed = false;
bool motion = digitalRead(PIR_PIN);
digitalWrite(LED_PIN, motion ? LOW : HIGH);
Serial.printf("[%lus] %s\n", millis() / 1000, motion ? "Motion detected" : "Motion ended");
}
}// HC-SR501 PIR motion sensor on a Wemos D1 mini (ESP8266), read with an interrupt.
// VCC → 5V, OUT → D5 (GPIO14), GND → GND. The built-in LED (D4) lights while motion is seen.
const uint8_t PIR_PIN = D5; // GPIO14 - D0 can't do interrupts on the ESP8266
const uint8_t LED_PIN = LED_BUILTIN; // D4 / GPIO2, lit when LOW
volatile bool changed = false;
// Interrupt handlers must live in IRAM on the ESP8266, or the board crashes when one fires.
void IRAM_ATTR onPirChange() {
changed = true;
}
void setup() {
Serial.begin(115200);
Serial.println();
pinMode(PIR_PIN, INPUT);
pinMode(LED_PIN, OUTPUT);
digitalWrite(LED_PIN, HIGH); // LED off
attachInterrupt(digitalPinToInterrupt(PIR_PIN), onPirChange, CHANGE);
Serial.println("PIR on D5 - give it about 60 s to settle after power-up");
}
void loop() {
if (changed) {
changed = false;
bool motion = digitalRead(PIR_PIN);
digitalWrite(LED_PIN, motion ? LOW : HIGH);
Serial.printf("[%lus] %s\n", millis() / 1000, motion ? "Motion detected" : "Motion ended");
}
}Expected serial output
PIR on D5 - give it about 60 s to settle after power-up
[64s] Motion detected
[69s] Motion ended
[112s] Motion detectedPIR on D5 - give it about 60 s to settle after power-up [64s] Motion detected [69s] Motion ended [112s] Motion detected
Common errors
Board reboots when motion is detected
The interrupt handler isn’t in IRAM. Add IRAM_ATTR before the function. Full fix.
Motion every few seconds with nobody there
Wi-Fi interference or the settle time. Move the PIR away from the antenna and wait a minute after power-up; turn the sensitivity pot down.
OUT stays high for ages
The time-delay pot is turned up (up to several minutes), or the jumper is on repeat-trigger (H). Turn the pot anticlockwise.