A Passive Infrared (PIR) sensor detects changes in ambient infrared radiation, making it the standard choice for occupancy sensing and intruder alarms. When building a PIR detector Arduino project, the most common module is the HC-SR501. While it seems as simple as reading a digital HIGH/LOW pin, the underlying BISS0001 timing chip and pyroelectric element introduce hardware quirks—like initialization lockouts and RF-induced false triggers—that ruin poorly written code.

This guide provides the exact wiring, a production-ready code skeleton with calibration error handling, and the bench-tested debugging steps required to stop your sensor from ghost-triggering.

Parts List & Specification Sheet

The HC-SR501 operates natively at 5V logic and includes an onboard 3.3V LDO regulator (typically a 7133 series) for the BISS0001 IC. Do not power it directly from a 3.3V microcontroller pin without hardware modification.

Component Exact Variant / Model Operating Voltage Est. Cost (2026)
Microcontroller Arduino Uno R3 (ATmega328P) 5V Logic $24.00 - $28.00
PIR Sensor HC-SR501 (Standard 3-pin) 4.5V - 20V DC $2.00 - $3.50
Fresnel Lens Standard dome (included w/ HC-SR501) N/A Included
Jumper Wires 22 AWG Dupont Male-to-Female N/A $4.00 (pack)
Decoupling Capacitor 100nF (0.1µF) Ceramic 50V rated $0.10

Pin Mapping & Wiring Steps

The HC-SR501 outputs a 3.3V to 4V HIGH signal depending on the input voltage and the specific voltage drop of the onboard LDO. This is safely read by the 5V-tolerant digital pins on the Arduino Uno R3.

Callout Tip: The HC-SR501 requires a 30 to 60-second hardware initialization period upon first receiving power. During this time, the output pin may flutter or stay HIGH. Do not attempt to read motion data until this lockout period expires.

Wiring Table

HC-SR501 Pin Arduino Uno R3 Pin Notes
VCC (Left) 5V Do not use 3.3V; the BISS0001 will brownout.
OUT (Middle) D2 (INT0) Using a hardware interrupt pin ensures no missed triggers.
GND (Right) GND Ensure a solid common ground; loose grounds cause ghosting.

Numbered Wiring Steps

  1. De-energize the board: Disconnect the USB cable and any external power from the Arduino Uno R3.
  2. Connect Power: Route the 5V pin from the Arduino to the VCC pin on the HC-SR501.
  3. Establish Ground: Connect Arduino GND to the HC-SR501 GND. Bench tip: Solder a 100nF ceramic capacitor directly across the VCC and GND pins on the back of the PIR module to filter out high-frequency noise from the USB power supply.
  4. Route the Signal: Connect the middle OUT pin to Arduino Digital Pin 2.
  5. Verify Jumper Setting: Look at the bottom of the HC-SR501. Ensure the small plastic jumper cap is set to the H (Retrigger) position. This keeps the output HIGH as long as motion is continuously detected, which is required for most alarm logic.

Complete Arduino Code with Calibration Handling

This code targets the Arduino Uno R3. It utilizes a hardware interrupt for instantaneous trigger detection and includes a robust calibration loop in the setup() function to handle the sensor's mandatory initialization lockout. If the sensor fails to settle, it throws a specific serial error rather than hanging indefinitely.

// Target Board: Arduino Uno R3 (ATmega328P)
// Sensor: HC-SR501 PIR Motion Sensor

const int PIR_PIN = 2;        // Hardware interrupt pin (INT0)
const int LED_PIN = 13;       // Onboard LED for visual feedback
const int CALIBRATION_TIMEOUT = 60000; // 60 seconds max wait

volatile bool motionDetected = false;
unsigned long lastTriggerTime = 0;
const unsigned long DEBOUNCE_TIME = 500; // 500ms software debounce

void setup() {
  Serial.begin(115200);
  pinMode(PIR_PIN, INPUT);
  pinMode(LED_PIN, OUTPUT);
  
  Serial.println("System Boot: Waiting for PIR sensor to calibrate...");
  Serial.println("Do not move in front of the sensor during this phase.");
  
  // Hardware lockout handling with timeout error checking
  unsigned long startTime = millis();
  while (digitalRead(PIR_PIN) == HIGH) {
    if (millis() - startTime > CALIBRATION_TIMEOUT) {
      Serial.println("ERROR: PIR sensor stuck HIGH during calibration");
      Serial.println("Check power supply noise or BISS0001 IC failure.");
      // Halt execution or enter safe mode
      while(1) { 
        digitalWrite(LED_PIN, !digitalRead(LED_PIN)); 
        delay(100); // Fast blink indicates hardware fault
      }
    }
    delay(100);
  }
  
  Serial.println("Calibration complete. Sensor is now active.");
  
  // Attach hardware interrupt on RISING edge
  attachInterrupt(digitalPinToInterrupt(PIR_PIN), isrMotionTrigger, RISING);
}

void loop() {
  if (motionDetected) {
    // Software debounce check
    if (millis() - lastTriggerTime > DEBOUNCE_TIME) {
      Serial.print("Motion Detected at: ");
      Serial.println(millis());
      digitalWrite(LED_PIN, HIGH);
      lastTriggerTime = millis();
    }
    motionDetected = false; // Reset flag
  }
  
  // Turn off LED if no motion for 2 seconds
  if (digitalRead(LED_PIN) == HIGH && (millis() - lastTriggerTime > 2000)) {
    digitalWrite(LED_PIN, LOW);
  }
}

// Interrupt Service Routine (ISR) - Keep it as short as possible
void isrMotionTrigger() {
  motionDetected = true;
}

Debugging: First Three Things to Check When It Fails

When your PIR detector Arduino build misbehaves, it is almost never a code logic issue; it is a hardware environment issue. If you encounter the serial output "ERROR: PIR sensor stuck HIGH during calibration" or experience constant false triggers, check these three items in order.

1. Power Supply Ripple and USB Noise

The Symptom: The sensor triggers randomly every 10-30 seconds, or the output pin never drops LOW.

The Cause: The HC-SR501 is highly sensitive to voltage ripple. If you are powering the Arduino via a cheap, unregulated USB wall wart, high-frequency switching noise couples into the 5V rail and fools the BISS0001 chip's internal comparators.

The Fix: Power the Arduino via the barrel jack with a regulated 7V-9V DC power supply, allowing the Uno's onboard linear regulator to filter the 5V rail. Alternatively, solder a 100µF electrolytic capacitor and a 100nF ceramic capacitor in parallel across the VCC and GND pins on the PIR module itself.

2. The 30-Second Hardware Lockout

The Symptom: The sensor triggers immediately on boot and stays HIGH, ignoring actual motion.

The Cause: When power is first applied, the BISS0001 IC requires roughly 30 seconds to initialize its internal baseline and charge the timing capacitors. During this window, the output is unstable.

The Fix: The provided code handles this via the while loop in setup(). If you write your own code, you must implement a mandatory delay(30000) or a blocking loop before attaching your interrupts or reading the pin state. See the Adafruit PIR Sensor Guide for detailed timing diagrams.

3. RF Interference and Wi-Fi Crosstalk

The Symptom: The sensor works perfectly on the bench, but false-triggers when installed near a Wi-Fi router or when an ESP32 transmits data.

The Cause: The long unshielded jumper wires act as antennas, picking up 2.4GHz RF energy and inducing micro-voltages on the OUT pin.

The Fix: Keep the wires between the PIR and the Arduino under 12 inches. If longer runs are required, use shielded cable with the shield tied to ground at the Arduino end only, or add a 10kΩ pull-down resistor between the OUT pin and GND to stiffen the logic threshold.

Extending and Simplifying the Build

Depending on your end goal, the standard HC-SR501 and Uno R3 combination might be overkill or underpowered.

Simplify: Switch to the AM312 Mini PIR

If you are building a battery-powered wearable or a compact 3.3V IoT node, ditch the HC-SR501. The AM312 is a miniature PIR sensor that operates natively at 3.3V, draws less than 15µA of quiescent current, and has no potentiometers to adjust. It simplifies the BOM and eliminates the need for voltage dividers when pairing with 3.3V boards like the Arduino Nano 33 IoT or ESP32.

Extend: ESP32 Deep Sleep Integration

For a remote security node, move from the Uno R3 to an ESP32 DevKit V1. Wire the HC-SR501 OUT pin to GPIO 33 (an RTC-capable wake pin). Modify the code to use esp_sleep_enable_ext0_wakeup(). The ESP32 will drop into deep sleep (drawing ~10µA) and only wake to transmit an MQTT alarm payload when the PIR pulls the pin HIGH. Refer to the Arduino attachInterrupt documentation if you need to adapt the ISR logic for different core architectures.

Frequently Asked Questions

Why does my PIR detector Arduino project keep false triggering?

False triggers are usually caused by three environmental factors: rapid changes in ambient temperature (like an HVAC vent blowing across the Fresnel lens), power supply ripple on the 5V rail, or RF interference from nearby Wi-Fi routers. To fix this, relocate the sensor away from heat sources, add a 100nF decoupling capacitor across the sensor's VCC and GND pins, and ensure your power supply is adequately regulated.

Can I run an HC-SR501 PIR detector on Arduino at 3.3V?

No, not without hardware modification. The HC-SR501 requires a minimum of 4.5V to power its onboard LDO and BISS0001 chip. If you feed it 3.3V, the sensor will fail to initialize. If you must use a 3.3V microcontroller (like an Arduino Due or ESP32), power the HC-SR501 from a separate 5V rail, and use a logic level converter or a simple voltage divider (e.g., 2kΩ and 3.3kΩ resistors) on the OUT pin to step the 4V signal down to a safe 3.3V for your microcontroller's GPIO.

How do I change the HC-SR501 time delay and sensitivity?

The HC-SR501 features two trimpots (potentiometers) on the board. The orange potentiometer on the left adjusts the time delay (from ~0.3 seconds to ~200 seconds). Turning it fully counter-clockwise sets the minimum delay. The orange potentiometer on the right adjusts the detection sensitivity and range (from ~3 meters to ~7 meters). Use a small Phillips or flathead jeweler's screwdriver to make micro-adjustments, and allow 10 seconds between adjustments for the BISS0001 timing capacitors to settle.