The HC-SR501 PIR sensor and Arduino Uno R3 are the default pick for indoor motion detection, requiring just three wires (VCC, GND, OUT) and a 5V power supply capable of delivering at least 200mA to handle the sensor's initialization surge. If you are building an automated lighting rig, a security alarm, or a smart home occupancy node, this combination offers the best balance of range (up to 7 meters), ease of wiring, and code simplicity. Below is the exact hardware decision path, the pinout, production-ready C++ code with non-blocking debounce, and the specific debugging steps to eliminate the infamous "false trigger" ghost readings.
Decision Path: Choosing the Right Motion Sensor
Not all motion sensors use the same physics. Before wiring anything, confirm that a Passive Infrared (PIR) sensor is actually what your project needs. PIR sensors detect changes in infrared radiation (body heat) moving across their Fresnel lens zones. They cannot see through walls, and they struggle if the ambient room temperature is very close to human body temperature (around 90°F / 32°C).
| Sensor Model | Technology | Detection Range | See Through Walls? | Best Use Case |
|---|---|---|---|---|
| HC-SR501 | PIR (Infrared) | 3m - 7m | No | Standard indoor room occupancy, security alarms |
| AM312 | PIR (Infrared) | 1m - 3m | No | Battery-powered wearables, tight enclosures |
| RCWL-0516 | Microwave Radar | 5m - 9m | Yes (non-metal) | Hidden behind drywall, outdoor lighting, through plastic |
Parts List and Hardware Specifications
This build targets the Arduino Uno R3 (ATmega328P). The code and pin mappings below are specifically written for this board variant's 5V logic and digital pin architecture.
- Microcontroller: Arduino Uno R3 (or compatible clone with ATmega328P) - ~$22.00
- Sensor: HC-SR501 PIR Motion Sensor Module (featuring the BISS0001 controller IC) - ~$2.50
- Power Supply: 5V/2A USB wall adapter (Do not use a standard 9V alkaline battery snap; the HC-SR501's onboard regulator will overheat and cause voltage sag, leading to false triggers).
- Wiring: 3x Male-to-Female jumper wires (Dupont style, 20cm).
- Optional: 10kΩ pull-down resistor (only needed if your specific HC-SR501 clone lacks the onboard output pulldown, which causes floating pin states during boot).
Pin Mapping and Wiring Procedure
The HC-SR501 has three pins. Looking at the sensor with the Fresnel lens facing away from you and the pins pointing down, the order from left to right is GND, OUT, and VCC. However, always verify the silkscreen on your specific board's PCB, as some cheap clones reverse the VCC and GND pins.
| HC-SR501 Pin | Arduino Uno R3 Pin | Wire Color (Standard) | Notes |
|---|---|---|---|
| VCC (Right) | 5V | Red | Requires clean 4.5V to 5V. Do not exceed 5V. |
| OUT (Middle) | Digital Pin 2 | Yellow/Orange | Outputs HIGH (VCC level) when motion is detected. |
| GND (Left) | GND | Black | Must share common ground with the Arduino. |
Wiring Steps:
- De-energize: Unplug the Arduino Uno from your PC or wall adapter before making connections.
- Connect Ground: Plug the black jumper from the sensor's GND pin to any GND pin on the Arduino's POWER header.
- Connect Power: Plug the red jumper from the sensor's VCC pin to the Arduino's 5V pin. Warning: Plugging this into the Vin or 9V pin will instantly fry the BISS0001 chip on the sensor.
- Connect Signal: Plug the yellow jumper from the sensor's OUT pin to Digital Pin 2 on the Arduino.
- Verify Potentiometers: Using a small Phillips screwdriver, turn the Cx potentiometer (sensitivity) fully counter-clockwise to start with a narrow detection zone. Turn the Tx potentiometer (time delay) fully counter-clockwise to set the output HIGH time to the minimum (~0.3 seconds) for faster testing.
Complete Arduino C++ Code with Debounce
Many beginner tutorials use the delay() function to handle the PIR's timing. This blocks the main loop, preventing you from reading other sensors or handling network traffic. The code below uses a non-blocking millis() approach to track state changes and includes a software lockout to handle the sensor's hardware initialization period.
// Target Board: Arduino Uno R3 (ATmega328P)
// Sensor: HC-SR501 PIR Motion Sensor
#define PIR_PIN 2
#define LED_PIN 13 // Built-in Uno LED for visual confirmation
#define BOOT_LOCKOUT_MS 60000 // HC-SR501 needs ~60s to calibrate baseline IR
unsigned long lastTriggerTime = 0;
unsigned long bootTime = 0;
bool motionState = false;
void setup() {
Serial.begin(115200);
pinMode(PIR_PIN, INPUT);
pinMode(LED_PIN, OUTPUT);
bootTime = millis();
Serial.println(F("System Booting..."));
Serial.println(F("HC-SR501 calibrating baseline. Please clear the room for 60 seconds."));
digitalWrite(LED_PIN, HIGH); // Flash LED to indicate boot lockout
delay(1000);
digitalWrite(LED_PIN, LOW);
}
void loop() {
unsigned long currentMillis = millis();
// Handle the 60-second hardware initialization lockout
if (currentMillis - bootTime < BOOT_LOCKOUT_MS) {
return; // Skip reading the pin while the sensor calibrates
}
// Read the PIR sensor state
int sensorValue = digitalRead(PIR_PIN);
// State change detection with simple debounce
if (sensorValue == HIGH && !motionState) {
motionState = true;
lastTriggerTime = currentMillis;
digitalWrite(LED_PIN, HIGH);
Serial.print(F("MOTION DETECTED at: "));
Serial.println(currentMillis / 1000);
}
else if (sensorValue == LOW && motionState) {
motionState = false;
digitalWrite(LED_PIN, LOW);
unsigned long duration = (currentMillis - lastTriggerTime) / 1000;
Serial.print(F("Motion ended. Duration: "));
Serial.print(duration);
Serial.println(F(" seconds."));
}
}
BOOT_LOCKOUT_MS variable in the code above prevents your logic from reacting to these false boot-up triggers.
Debugging: The First Three Things to Check When It Fails
PIR sensors are notorious for acting up in specific environmental and electrical conditions. If your build is failing, follow this exact decision path.
1. Software Error: 'PIR_PIN' was not declared in this scope
If the Arduino IDE throws the exact error string 'PIR_PIN' was not declared in this scope during compilation, it means the preprocessor cannot find your pin definition.
- Cause A: You missed the
#define PIR_PIN 2line at the top of the sketch, or you placed it after thesetup()function. - Cause B: You typed
#define PIR_PIN = 2. The C++ preprocessor does not use equals signs for macro definitions. Remove the=. - Fix: Ensure
#define PIR_PIN 2is on line 4, before any functions.
2. Hardware Error: Continuous False Triggers (Output stuck HIGH)
If the Serial Monitor prints "MOTION DETECTED" continuously even when the room is empty, check these three physical factors in order:
- Power Supply Ripple: The HC-SR501 is highly sensitive to voltage noise. If you are powering the Arduino via a cheap, unregulated 5V USB hub or a dying 9V battery, the voltage ripple will trick the BISS0001 chip into seeing IR noise. Fix: Plug the Arduino into a high-quality 5V/2A smartphone charger.
- The Trigger Mode Jumper: Look at the bottom left corner of the HC-SR501 PCB. There is a small jumper cap connecting three pins. If it is set to L (Non-repeatable), the output goes LOW immediately after the time delay, even if you are still standing there. If it is set to H (Repeatable), it stays HIGH as long as motion is present. Fix: Move the jumper to the H position for standard occupancy tracking.
- Thermal Drafts: PIR sensors detect changes in heat. An HVAC vent blowing warm air across a cold wall, or sunlight shifting across the floor, will trigger it. Fix: Aim the sensor away from windows and AC registers, or use electrical tape to mask off the bottom zones of the Fresnel lens.
3. Hardware Error: Never Triggers (Output stuck LOW)
- Cause: You are testing within the first 60 seconds of power-on, or the Cx potentiometer is turned so far counter-clockwise that the sensitivity is effectively zero.
- Fix: Wait 90 seconds after plugging in the board. Turn the Cx potentiometer clockwise by two full turns to increase the detection range to ~5 meters.
How to Extend or Simplify the Build
Depending on your end goal, you can strip this project down to its barest components or scale it up into a networked IoT node.
Simplify: The Direct-Wire LED (No Code Required)
If you just need a motion-activated light for a closet and don't care about logging data, ditch the Arduino entirely. Connect the HC-SR501 VCC to a 5V USB breakout board, GND to ground, and connect the OUT pin directly to the base of a 2N2222 NPN transistor (with a 1kΩ base resistor) to switch a 12V LED strip. The sensor's Tx potentiometer will handle the "lights on" duration natively.
Extend: Migrating to ESP32 for MQTT Smart Home Integration
To push motion events to Home Assistant via WiFi, you will likely upgrade from the Uno to an ESP32 DevKit V1. However, you must address a critical voltage mismatch.
The Fix: You have two options when migrating to ESP32:
- Power the HC-SR501 at 3.3V: The HC-SR501's official datasheet states it requires 4.5V to 20V. However, in practice, many hobbyist modules will operate marginally at 3.3V if you bypass the onboard 7133 voltage regulator and feed 3.3V directly to the VCC pin. This makes the OUT pin output a safe 3.3V. Range will drop by about 30%.
- Use a Voltage Divider: Keep the sensor at 5V for maximum range, but route the OUT pin through a simple voltage divider (a 2.2kΩ resistor in series with the signal, and a 3.3kΩ resistor to ground) before it hits the ESP32 GPIO. This safely steps the 5V logic HIGH down to ~3.0V, which the ESP32 will reliably read as a logic HIGH without risking silicon damage.
By pairing the HC-SR501 with the Arduino Uno R3 and utilizing non-blocking debounce code, you eliminate the most common pitfalls of hobbyist motion detection. Secure your power supply, respect the 60-second boot lockout, and your sensor will provide years of reliable occupancy data.






