1. Hardware Specs and Parts List
Before wiring, verify your module variant. Cheap clones sometimes ship with the delay potentiometer glued at the factory maximum (250 seconds). Ensure you have the standard dual-potentiometer version.
| Component | Exact Variant / Model | Key Specification | Est. Cost (2026) |
|---|---|---|---|
| Microcontroller | Arduino Uno R3 (ATmega328P) | 5V logic, 14 digital I/O | $22.00 |
| PIR Sensor | HC-SR501 (BISS0001 IC) | 4.5V-20V input, 3.3V TTL output | $2.50 |
| Indicator | 5mm Red LED + 220Ω Resistor | Forward voltage ~2.0V | $0.10 |
| Wiring | 24 AWG Solid Core Jumpers | Pre-cut for breadboard | $5.00 |
Note: The HC-SR501 outputs a 3.3V HIGH signal even when powered at 5V. This is perfectly safe for the 5V-tolerant digital input pins on the Uno R3, but requires a logic level shifter if you later migrate this exact circuit to a 3.3V Arduino Due or ESP32 without checking the module's onboard voltage divider.
2. Pin Mapping and Wiring Steps
The HC-SR501 has three pins: VCC, OUT, and GND. The physical order on the board is usually GND (left), OUT (middle), VCC (right) when looking at the component side with the dome lens facing up. Always verify with the silkscreen on your specific board.
Wiring Sequence
- De-energize: Ensure the Arduino is unplugged from USB or external power.
- Power Rails: Connect Arduino
5Vto the breadboard red rail, and ArduinoGNDto the blue rail. - Sensor Power: Wire HC-SR501
VCCto the red rail (5V) andGNDto the blue rail. - Signal Line: Wire HC-SR501
OUTto Arduino Digital Pin2. (Pin 2 is chosen because it supports hardware interrupts on the ATmega328P, though we will use polling in this specific code for broader compatibility). - Indicator: Connect the 220Ω resistor to Arduino Digital Pin
13, then to the LED anode. Connect the LED cathode toGND.
Pin Mapping Table
| HC-SR501 Pin | Arduino Uno R3 Pin | Wire Color (Std) |
|---|---|---|
| VCC | 5V | Red |
| OUT | Digital 2 | Yellow |
| GND | GND | Black |
3. Complete Arduino Code with Error Handling
This code targets the Arduino Uno R3 (ATmega328P). It avoids the blocking delay() function, using a millis()-based state machine. Crucially, it includes an error-handling routine that flags a serial error if the sensor gets "stuck" in a HIGH state—a common hardware fault with the HC-SR501.
/*
* Motion Detector Sensor Arduino - Non-Blocking with Timeout Error Handling
* Target Board: Arduino Uno R3 (ATmega328P)
* Sensor: HC-SR501 PIR Module
*/
// --- PIN DEFINITIONS ---
const int PIR_PIN = 2; // Digital pin connected to PIR OUT
const int LED_PIN = 13; // Onboard or external indicator LED
// --- TIMING CONSTANTS ---
const unsigned long DEBOUNCE_MS = 50; // Debounce window for PIR edge
const unsigned long TIMEOUT_MS = 30000; // 30-second max expected motion duration
const unsigned long PRINT_INTERVAL = 1000; // Serial print throttle
// --- STATE VARIABLES ---
int pirState = LOW; // Current state of the PIR
int lastPirState = LOW; // Previous state for edge detection
unsigned long lastMotionTime = 0; // Timestamp of last detected motion
unsigned long lastPrintTime = 0; // Timestamp for serial throttling
bool errorFlag = false; // Hardware fault flag
void setup() {
Serial.begin(115200);
while (!Serial) { ; } // Wait for serial port (Uno R3 native USB workaround)
pinMode(PIR_PIN, INPUT);
pinMode(LED_PIN, OUTPUT);
// Allow the PIR sensor to calibrate its internal baseline (takes 15-30s)
Serial.println("[SYS] Calibrating PIR sensor. Keep area clear for 30 seconds...");
for (int i = 0; i < 30; i++) {
delay(1000);
Serial.print(".");
}
Serial.println("\n[SYS] Calibration complete. Monitoring motion.");
}
void loop() {
unsigned long currentMillis = millis();
// Read the sensor
int currentRead = digitalRead(PIR_PIN);
// Basic edge detection
if (currentRead != lastPirState) {
lastMotionTime = currentMillis; // Reset timer on any state change
errorFlag = false; // Clear error flag on state transition
if (currentRead == HIGH) {
pirState = HIGH;
digitalWrite(LED_PIN, HIGH);
Serial.println("[MOTION] Detected: Output HIGH");
} else {
pirState = LOW;
digitalWrite(LED_PIN, LOW);
Serial.println("[MOTION] Cleared: Output LOW");
}
}
lastPirState = currentRead;
// --- ERROR HANDLING: STUCK HIGH DETECTION ---
// If the sensor stays HIGH longer than TIMEOUT_MS, it's likely a hardware fault
if (pirState == HIGH && (currentMillis - lastMotionTime > TIMEOUT_MS)) {
if (!errorFlag) {
Serial.println("[ERR] PIR Stuck HIGH: Output pin held > 30000ms. Check H/L jumper or VCC ripple.");
errorFlag = true;
digitalWrite(LED_PIN, LOW); // Turn off LED to indicate fault state
}
}
// Throttled heartbeat print to prove the loop isn't blocked
if (currentMillis - lastPrintTime >= PRINT_INTERVAL) {
lastPrintTime = currentMillis;
if (!errorFlag && pirState == LOW) {
// Serial.println("[SYS] Heartbeat OK"); // Uncomment for verbose debugging
}
}
}
4. Debugging: First Three Checks and Common Errors
When your motion detector sensor Arduino project fails to trigger, or triggers continuously, do not rewrite the code immediately. The HC-SR501 is an analog frontend wrapped in a digital output. 90% of issues are physical.
- VCC Rail Voltage: Measure the voltage at the sensor's VCC pin with a multimeter. It must be strictly between 4.5V and 5.5V. If your USB port is sagging to 4.2V, the BISS0001 chip will fail to latch, causing random flickering.
- The H/L Jumper Block: Look at the small plastic jumper on the bottom edge of the module. If it's set to 'H' (Repeatable Trigger), the output stays HIGH as long as motion is present. If set to 'L' (Non-Repeatable), it goes LOW after the delay time even if you are still standing in front of it. Set it to 'H' for standard Arduino logic.
- Potentiometer Positions: The Tx pot controls the output delay time. Factory default is often turned fully clockwise (250 seconds). Turn it fully counter-clockwise to drop the delay to ~3 seconds for bench testing.
Common Error Strings and Ranked Causes
Runtime Serial Error: [ERR] PIR Stuck HIGH: Output pin held > 30000ms.
- Cause 1 (Most Likely): The H/L jumper is missing or loose, causing the trigger mode to float.
- Cause 2: Severe 5V power supply ripple. The switching regulator on the HC-SR501 is injecting noise into the BISS0001 op-amp stage.
- Cause 3: ESD latch-up. The sensor was handled without grounding, permanently damaging the output stage of the IC.
IDE Compilation Error: error: 'PIR_PIN' was not declared in this scope
- Cause 1: You copied the
loop()function but missed theconst int PIR_PIN = 2;definition at the very top of the sketch. - Cause 2: You placed the pin definitions inside the
setup()block, making them local variables inaccessible to theloop().
5. FAQ: Motion Detector Sensor Arduino Questions
Why is my motion detector sensor Arduino project triggering randomly?
Random or "ghost" triggers are almost always caused by environmental thermal noise or power instability. The Fresnel lens focuses infrared heat signatures onto the pyroelectric sensor. If the sensor is pointed near an HVAC vent, a sunlit window, or a heat-generating appliance (like a refrigerator compressor), the rapid temperature delta will trip the threshold. Additionally, if you are powering the Arduino and sensor from a cheap, unregulated USB wall wart, high-frequency noise on the 5V rail will couple into the high-gain analog frontend of the PIR. Use a regulated bench supply or a high-quality USB power brick.
Can I power the HC-SR501 motion detector sensor with Arduino 3.3V?
No. The HC-SR501 has an onboard linear regulator (typically a 7133 or similar) designed to step down higher voltages to the 3.3V required by the BISS0001 chip. The dropout voltage of this regulator means you must supply at least 4.5V to the VCC pin for it to function. Supplying 3.3V directly to the VCC pin will result in a brownout condition where the output pin floats or oscillates wildly. If you strictly need a 3.3V system, you must bypass the onboard regulator by soldering a wire directly to the 3.3V pad on the BISS0001 chip, or switch to a modern digital PIR sensor like the Panasonic EKMB series which natively supports 3.3V logic.
How do I change the delay time on the PIR motion sensor?
The delay time is controlled by the potentiometer labeled Tx (usually the one closest to the edge of the board). Turning it fully counter-clockwise sets the delay to approximately 3 seconds. Turning it fully clockwise extends the delay to roughly 250 seconds (over 4 minutes). The adjustment is not perfectly linear. For precise timing in a production environment, do not rely on the physical potentiometer; instead, set the pot to its minimum (3 seconds) and handle the extended timing logic in your Arduino code using millis(), just as demonstrated in the timeout logic above.
To Simplify: Remove the external LED and resistor. Rely purely on the Arduino Serial Monitor and the onboard Pin 13 LED to reduce wiring complexity.
To Extend: Swap the Arduino Uno R3 for an ESP32-WROOM-32 DevKit. Keep the PIR wired to GPIO 4, and use the
PubSubClient library to publish a JSON payload ({"motion": true}) to an MQTT broker like Mosquitto, integrating the sensor directly into Home Assistant.
References: For more on digital pin behavior, see the Arduino digitalRead() documentation. For deeper hardware theory on PIR modules, review the Adafruit PIR Sensor Guide.






