To build a reliable PIR motion sensor and buzzer Arduino alarm, wire the HC-SR501 sensor's VCC to 5V, OUT to digital pin 2, and GND to GND. Connect a 5V active piezo buzzer's positive lead to digital pin 8 and its negative lead to GND. The code below targets the Arduino Nano v3 (ATmega328P, USB-C variant), utilizing non-blocking state-change detection to prevent the buzzer from droning continuously while motion is present.
Estimated Build Time: 20 minutes
Target Board: Arduino Nano v3 (ATmega328P) or Arduino Uno R3
HC-SR501 Sensor Specifications & Calibration
Before wiring, you must understand the hardware you are working with. The HC-SR501 relies on the BISS0001 analog signal processing IC to interpret pyroelectric infrared signals. Out of the box, the potentiometers are often set to random factory defaults. You will need a small flathead screwdriver to calibrate them.
| Parameter | Specification / Value | Notes & Bench Observations |
|---|---|---|
| Operating Voltage | 4.5V to 20V DC | Use 5V from the Nano's 5V pin. Do not use the 3.3V pin (insufficient current/voltage). |
| Quiescent Current | < 50 µA | Excellent for battery-powered nodes when paired with a sleep library. |
| Output Logic Level | HIGH: ~3.3V / LOW: 0V | When powered at 5V, the OUT pin outputs ~3.3V. Safe for 5V Arduinos, but requires a logic level shifter for 3.3V ESP32 boards. |
| Delay Time (Adjustable) | 0.3s to ~200s | Adjusted via the 'Time Delay' potentiometer. Fully counter-clockwise is ~0.3s. |
| Trigger Mode (Jumper) | H (Repeat) / L (No Repeat) | H (Repeatable): Output stays HIGH as long as motion is detected. L (Single): Output goes HIGH, then LOW after delay, ignoring new motion during the delay. |
| Sensing Angle & Distance | < 110° cone / 3m to 7m | Determined by the Fresnel lens. Range drops significantly if the lens is removed or obscured. |
Parts List & Wiring Procedure
Component selection matters. The most common mistake beginners make is buying a passive buzzer. A passive buzzer requires an AC square wave (PWM) to generate sound. An active buzzer has a built-in oscillator and only requires a DC HIGH signal to emit a continuous tone. This build uses an active buzzer to keep the code clean and CPU cycles free.
Required Components (2026 Pricing Estimates)
- Microcontroller: Arduino Nano v3 (ATmega328P, USB-C) — $6.50
- Sensor: HC-SR501 PIR Motion Sensor Module (with BISS0001 IC) — $2.00
- Alert: 5V Active Piezo Buzzer (continuous tone, ~30mA draw) — $1.50
- Hardware: Half-size breadboard, male-to-male jumper wires (22 AWG stranded) — $4.00
Pin Mapping Table
| Arduino Nano Pin | Direction | Component Pin | Wire Color (Recommended) |
|---|---|---|---|
| 5V | Power Out | HC-SR501 VCC (Left) | Red |
| GND | Ground | HC-SR501 GND (Right) | Black |
| D2 | Input | HC-SR501 OUT (Middle) | Yellow |
| D8 | Output | Active Buzzer + (Red wire) | Orange |
| GND | Ground | Active Buzzer - (Black wire) | Black |
Step-by-Step Wiring
- De-energize the board: Ensure the Arduino Nano is unplugged from your PC or USB power bank before inserting wires into the breadboard.
- Mount the Nano: Straddle the Nano across the center trench of the breadboard so pins on both sides are accessible.
- Wire the PIR Power: Connect the Nano 5V pin to the left pin of the HC-SR501, and Nano GND to the right pin. (Looking at the module from the front with the dome facing you, the pins are usually VCC, OUT, GND from left to right. Verify with the silkscreen on your specific board).
- Wire the Signal: Connect the middle OUT pin of the PIR to Nano Digital Pin 2.
- Wire the Buzzer: Connect the buzzer's positive (red) wire to Nano Digital Pin 8, and the negative (black) wire to a common GND rail shared with the PIR.
- Verify connections: Tug gently on jumper wires to ensure solid breadboard contact. Loose ground wires are the #1 cause of false PIR triggers.
Complete Arduino C++ Code with Error Handling
The following code uses millis() for non-blocking timing. This is critical if you plan to add WiFi (ESP8266/ESP32) or sensor polling later, as delay() halts the CPU. It also includes a startup calibration period and a serial error trap for floating pins.
// Target Board: Arduino Nano v3 (ATmega328P) or Uno R3
// PIR Motion Sensor and Buzzer Arduino Alarm
#define PIR_PIN 2
#define BUZZER_PIN 8
#define CALIBRATION_TIME 15000 // 15 seconds for PIR to stabilize
#define ALARM_DURATION 2000 // Buzzer sounds for 2 seconds per trigger
#define COOLDOWN_TIME 3000 // Ignore new motion for 3 seconds after alarm
unsigned long calibrationStart;
unsigned long alarmStart;
unsigned long cooldownStart;
bool isCalibrated = false;
bool alarmActive = false;
bool cooldownActive = false;
int lastPirState = LOW;
int stuckHighCounter = 0;
void setup() {
Serial.begin(115200);
pinMode(PIR_PIN, INPUT);
pinMode(BUZZER_PIN, OUTPUT);
digitalWrite(BUZZER_PIN, LOW); // Ensure buzzer is off at boot
Serial.println("System Boot: Calibrating PIR sensor...");
Serial.println("Keep area clear of motion for 15 seconds.");
calibrationStart = millis();
}
void loop() {
unsigned long currentMillis = millis();
int currentPirState = digitalRead(PIR_PIN);
// 1. Handle Calibration Phase
if (!isCalibrated) {
if (currentMillis - calibrationStart >= CALIBRATION_TIME) {
isCalibrated = true;
Serial.println("Calibration complete. System Armed.");
}
return; // Skip rest of loop during calibration
}
// 2. Hardware Error Detection (Floating Pin / Stuck HIGH)
if (currentPirState == HIGH) {
stuckHighCounter++;
if (stuckHighCounter > 10000) { // If stuck HIGH for ~10k loops without dropping
Serial.println("SERIAL_ERR: PIR_OUT stuck HIGH. Check GND connection or shield cable.");
stuckHighCounter = 0; // Reset to prevent serial buffer flooding
}
} else {
stuckHighCounter = 0;
}
// 3. Handle Cooldown Phase
if (cooldownActive) {
if (currentMillis - cooldownStart >= COOLDOWN_TIME) {
cooldownActive = false;
}
return; // Ignore motion during cooldown
}
// 4. Handle Active Alarm Phase
if (alarmActive) {
if (currentMillis - alarmStart >= ALARM_DURATION) {
digitalWrite(BUZZER_PIN, LOW); // Turn off buzzer
alarmActive = false;
cooldownActive = true;
cooldownStart = currentMillis;
Serial.println("Alarm ended. Entering cooldown.");
}
return; // Keep buzzer on, ignore new state changes
}
// 5. Detect NEW Motion (State Change)
if (currentPirState == HIGH && lastPirState == LOW) {
Serial.println("MOTION DETECTED: Triggering Alarm!");
digitalWrite(BUZZER_PIN, HIGH);
alarmActive = true;
alarmStart = currentMillis;
}
lastPirState = currentPirState;
}
Debugging: First Three Checks & Common Failures
Embedded hardware rarely works perfectly on the first power-up. If your PIR motion sensor and buzzer Arduino build fails, check these three specific failure modes first.
1. The "Stuck HIGH" Serial Error
Symptom: The serial monitor repeatedly prints SERIAL_ERR: PIR_OUT stuck HIGH. Check GND connection or shield cable. immediately after calibration, even in an empty room.
Ranked Causes:
- Missing or loose GND wire: Without a common ground, the Arduino's internal pull-up resistors or floating breadboard contacts will read the PIR's OUT pin as HIGH. Fix: Verify the black wire connects Nano GND to PIR GND.
- RF Interference: The HC-SR501 is notoriously susceptible to 2.4GHz RF noise from nearby WiFi routers. Fix: Move the router at least 2 feet away, or solder a 0.1µF ceramic capacitor across the PIR's VCC and GND pins to filter noise.
- Defective BISS0001 IC: Cheap clones sometimes ship with dead logic chips. Fix: Measure the OUT pin with a multimeter. If it reads a steady 3.3V regardless of motion, replace the module.
2. Buzzer Emits a Faint "Click" Instead of a Tone
Symptom: The code triggers, the serial monitor logs motion, but the buzzer only makes a quiet ticking sound.
Cause: You are using a passive buzzer. Passive buzzers require a PWM square wave to vibrate the diaphragm. Supplying a steady DC HIGH (via digitalWrite(HIGH)) only moves the diaphragm once, resulting in a single click.
Fix: Swap the component for a 5V active buzzer. If you must use the passive buzzer, replace digitalWrite(BUZZER_PIN, HIGH); with tone(BUZZER_PIN, 2000); and digitalWrite(BUZZER_PIN, LOW); with noTone(BUZZER_PIN); in the code above. See the official Arduino tone() reference for frequency limits.
3. Random False Triggers at Night
Symptom: The alarm sounds at 3 AM when no one is in the room.
Ranked Causes:
- HVAC Vents & Thermal Drift: PIR sensors detect changes in infrared heat signatures. A furnace kicking on and blowing warm air across the sensor's field of view mimics a human walking by. Fix: Reorient the sensor away from vents and windows.
- Pets: Cats and dogs emit strong IR signatures. Fix: Mount the sensor higher (7+ feet) and angle it slightly upward, or apply electrical tape to the bottom segments of the Fresnel lens to create a "pet alley" blind spot.
- Fluorescent/CFL Ballasts: Aging ballasts emit IR spikes when igniting. Fix: Replace room lighting with LEDs.
Extending and Simplifying the Build
Once the baseline alarm is functional, you can adapt the circuit for specific deployment scenarios.
How to Simplify for Battery Operation
The HC-SR501 draws roughly 50µA at rest, but the Arduino Nano draws ~19mA continuously. To run this on a 9V battery for months, you must put the microcontroller to sleep.
- Install the
LowPowerlibrary via the Arduino Library Manager. - Remove the serial debugging code (Serial consumes significant power).
- Wire the PIR OUT pin to Digital Pin 3 (an interrupt-capable pin on the Nano).
- Use
LowPower.powerDown(SLEEP_FOREVER, ADC_OFF, BOD_OFF);in the loop, and attach an interrupt to wake the board only when the PIR goes HIGH.
How to Extend for a Real Security Siren
A 5V piezo buzzer is loud enough for a desk drawer, but useless for a whole-house alarm. Real security sirens operate at 12V and draw 200mA to 500mA—far exceeding the Nano's 40mA absolute maximum GPIO limit.
To drive a 12V siren, add an IRLZ44N Logic-Level N-Channel MOSFET.
- Connect the Nano D8 pin to the MOSFET Gate via a 220Ω resistor.
- Connect the MOSFET Source to GND.
- Connect the 12V Siren's negative terminal to the MOSFET Drain, and the siren's positive terminal to a 12V power supply.
- Place a 1N4007 flyback diode in reverse parallel across the siren terminals to protect the MOSFET from inductive voltage spikes when the siren turns off.






