To interface a passive infrared sensor with a Raspberry Pi, you must bridge the sensor's typical 5V logic output to the Pi's strictly 3.3V GPIO tolerance. Feeding 5V directly into a Raspberry Pi GPIO pin will permanently destroy the System-on-Chip (SoC). The direct solution is to use a 2-resistor voltage divider on the sensor's OUT pin, or select a natively 3.3V sensor variant like the AM312. Below is the exact hardware spec sheet, wiring procedure, and Bookworm-compatible Python code to get your motion detection running reliably.
The PIR Decision Tree: Which Sensor Variant to Buy?
Not all PIR modules are created equal. The market is flooded with clones that behave differently under the hood. Use this decision matrix to select the right module for your specific build constraints.
| Criteria | HC-SR501 (Standard) | AM312 (Mini) | Panasonic EKMB (PaPiRs) |
|---|---|---|---|
| Native Logic Level | 5V (Requires voltage divider for Pi) | 3.3V (Safe for direct Pi GPIO) | 3.3V (Safe for direct Pi GPIO) |
| Detection Range | Up to 7 meters (Adjustable) | Up to 3 meters (Fixed) | Up to 5 meters (Fixed/Lens dependent) |
| On-board Adjustability | Yes (Delay & Sensitivity pots) | No | No |
| Typical Price (2026) | $2.50 - $4.00 | $1.50 - $2.50 | $12.00 - $18.00 |
Hardware Spec Sheet & Pin Mapping
This build assumes you are using the ubiquitous HC-SR501 to demonstrate proper 5V-to-3.3V logic level translation. If you opted for the AM312, you can skip the resistors and wire the OUT pin directly to GPIO 17.
Parts List
- Microcontroller: Raspberry Pi 4 Model B (4GB RAM variant) running Raspberry Pi OS (Bookworm).
- Sensor: HC-SR501 PIR Motion Sensor (Standard 3-pin variant with white Fresnel lens).
- Resistors: 1x 1kΩ and 1x 2kΩ (1/4W, 5% tolerance) for the voltage divider.
- Wiring: 22 AWG solid-core copper jumper wires.
- Decoupling (Optional but recommended): 0.1µF ceramic capacitor.
Pin Mapping Table
| HC-SR501 Pin | Intermediate Component | Raspberry Pi 4 Physical Pin | Pi BCM GPIO Number |
|---|---|---|---|
| VCC | Direct | Pin 2 (5V Power) | N/A |
| GND | Direct | Pin 6 (Ground) | N/A |
| OUT | 1kΩ Resistor (Series) | Pin 11 (GPIO 17) | GPIO 17 |
| N/A (Divider Tap) | 2kΩ Resistor (to GND) | Pin 9 (Ground) | N/A |
Wiring the HC-SR501 (With 3.3V Logic Protection)
The HC-SR501 outputs roughly 5V on its OUT pin when motion is detected. The Raspberry Pi 4 GPIO pins are strictly limited to 3.3V. We use a voltage divider to step this down safely. The math: V_out = 5V × (2000Ω / (1000Ω + 2000Ω)) = 3.33V, which is safely within the Pi's 3.3V logic HIGH threshold.
- Power the Sensor: Connect the HC-SR501 VCC pin to Physical Pin 2 (5V) on the Pi. The sensor requires 4.5V to 20V to operate its internal LDO regulator properly; do not power it from the Pi's 3.3V pin.
- Establish Common Ground: Connect the HC-SR501 GND pin to Physical Pin 6 (GND) on the Pi. Shared ground is mandatory for the signal reference.
- Build the Voltage Divider: On a breadboard, insert the 1kΩ and 2kΩ resistors in series. Connect the free end of the 1kΩ resistor to the HC-SR501 OUT pin.
- Wire the GPIO Signal: Connect the junction between the 1kΩ and 2kΩ resistors to Physical Pin 11 (GPIO 17) on the Pi.
- Ground the Divider: Connect the free end of the 2kΩ resistor to Physical Pin 9 (GND) on the Pi.
- Add Decoupling (Pro-Tip): Solder or plug a 0.1µF ceramic capacitor across the VCC and GND pins on the sensor side of the breadboard to filter out RF interference from the Pi's Wi-Fi antenna.
Python Motion Detection Code (Bookworm Compatible)
This code targets the Raspberry Pi 4 Model B running Raspberry Pi OS (Bookworm). It uses the official gpiozero library, which is pre-installed on modern Pi OS releases and utilizes the lgpio backend under the hood.
#!/usr/bin/env python3
"""
Passive Infrared Sensor Raspberry Pi Motion Detection
Target Board: Raspberry Pi 4 Model B (Bookworm OS)
Library: gpiozero (lgpio backend)
"""
from gpiozero import MotionSensor
from signal import pause
import sys
import logging
# Configure logging for production deployment
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
# BCM GPIO 17 corresponds to Physical Pin 11
# queue_len=1 and threshold=0.5 provides a slight software debounce
PIR_PIN = 17
def motion_detected():
logging.info("INTRUSION: Motion detected in monitored zone.")
# Add your payload here (e.g., trigger camera, send MQTT message)
def motion_stopped():
logging.info("CLEAR: Zone is now empty.")
def main():
try:
# Initialize the sensor with hardware debounce parameters
pir = MotionSensor(PIR_PIN, queue_len=3, threshold=0.6)
# Bind callbacks to state changes
pir.when_motion = motion_detected
pir.when_no_motion = motion_stopped
logging.info(f"Monitoring GPIO {PIR_PIN} for PIR state changes. Press Ctrl+C to exit.")
# Keep the script running efficiently without blocking CPU
pause()
except KeyboardInterrupt:
logging.info("Script terminated by user.")
sys.exit(0)
except Exception as e:
logging.critical(f"Fatal initialization error: {e}")
sys.exit(1)
if __name__ == '__main__':
main()
Debugging: The First Three Things to Check When It Fails
When your passive infrared sensor Raspberry Pi build fails, it is almost always a software backend issue or an RF interference problem. Check these three specific failure modes in order.
1. Error: gpiozero.exc.BadPinFactory: Unable to load any default pin factory!
The Cause: On Raspberry Pi OS Bookworm, the legacy RPi.GPIO library is deprecated and broken due to the shift from sysfs to the lgpio C library. gpiozero cannot find a valid backend to talk to the hardware.
The Fix: Install the modern lgpio Python bindings. Run this in your terminal:
sudo apt update
sudo apt install python3-rpi-lgpio
2. Error: RuntimeError: No access to /dev/mem. Try running as root!
The Cause: You are attempting to use legacy tutorials that rely on the RPi.GPIO library, which requires direct memory mapping that standard users no longer have permission to access on modern kernels.
The Fix: Abandon RPi.GPIO. Refactor your code to use gpiozero as shown in the script above. If you absolutely must use legacy code, run it with sudo (not recommended for security reasons).
3. Symptom: Sensor triggers continuously (False Positives) without motion
The Cause: The HC-SR501 is notoriously susceptible to Radio Frequency (RF) interference, specifically the 2.4GHz Wi-Fi and Bluetooth signals radiating from the Raspberry Pi's on-board antenna. The high-frequency noise couples into the PIR's high-gain op-amp, registering as thermal movement.
The Fix: 1. Ensure the 0.1µF decoupling capacitor is installed across VCC and GND. 2. Move the PIR sensor at least 3 inches away from the Pi's Wi-Fi antenna using longer jumper wires. 3. If using a metal project box, ensure the Pi and sensor share an equipotential bonding ground to prevent ground loops.
Scaling the Build: Extend or Simplify
Once your baseline circuit is stable, you will likely want to adapt it for a specific deployment environment. Here is how to pivot the architecture without rewriting your core logic.
How to Simplify (The AM312 Swap)
If the voltage divider and false-trigger debugging are causing friction, rip out the HC-SR501 and wire an AM312 Mini PIR.
The Wiring Change: AM312 VCC to Pi 3.3V (Pin 1), AM312 GND to Pi GND (Pin 6), AM312 OUT directly to Pi GPIO 17 (Pin 11).
The Code Change: None. The gpiozero script above remains 100% identical because the logic HIGH/LOW thresholds map perfectly to the 3.3V native output of the AM312.
How to Extend (Home Assistant MQTT Integration)
To turn this into a smart home occupancy node, extend the Python script by importing paho.mqtt.client. Inside the motion_detected() callback, publish a JSON payload to your broker:
import json
import paho.mqtt.client as mqtt
client = mqtt.Client(client_id="pi_pir_node_01")
client.connect("192.168.1.100", 1883, 60)
def motion_detected():
payload = json.dumps({"state": "ON", "sensor": "HC-SR501", "zone": "hallway"})
client.publish("homeassistant/binary_sensor/hallway/state", payload, qos=1, retain=True)
By publishing to the homeassistant/binary_sensor discovery topic, your Raspberry Pi will automatically register as a native motion entity in Home Assistant, requiring zero YAML configuration on the server side.
For deeper physics on how the Fresnel lens focuses infrared radiation onto the pyroelectric crystal, refer to Adafruit's comprehensive PIR guide. Always verify your local electrical codes if integrating these low-voltage sensor circuits into permanent, mains-powered home automation enclosures.






