How to Build a Raspberry Pi Motion Sensor with PIR Detection
The HC-SR501 PIR sensor costs under $2 and detects warm bodies up to 7 meters away, yet most Raspberry Pi tutorials stop at printing "motion detected" to a terminal. This guide covers the full path from wiring three pins on a breadboard to building automations that actually do something: trigger camera recordings, send Telegram alerts, log timestamps to CSV, and sync motion data to cloud storage where an AI agent can query it.
Why PIR Sensors Still Dominate Edge Detection
The Raspberry Pi Foundation's official Physical Computing curriculum dedicates an entire chapter to PIR sensors, and their "Parent Detector" project has been one of the most-visited tutorials on projects.raspberrypi.org since it launched. The reason is practical: passive infrared detection requires no camera feed, no ML model, and no cloud subscription. A PIR sensor watches for changes in infrared radiation from warm bodies moving through its field of view and outputs a clean digital signal on a GPIO pin.
The HC-SR501 is the standard module for Raspberry Pi projects. It operates on 5V to 20V DC, draws under 65 microamps of quiescent current, and outputs 3.3V logic that maps directly to GPIO without a level shifter. Two onboard potentiometers let you adjust the detection range (3 to 7 meters) and the hold time (3 seconds to 5 minutes). A jumper selects between single-trigger mode, where the output fires once and resets, and repeatable-trigger mode, where the output stays high as long as motion continues.
The detection cone spans roughly 120 degrees through the Fresnel lens on the front of the module. That wide angle is useful for hallways and room entries, but it also means you will get false triggers from pets, HVAC vents, and direct sunlight through windows. Understanding these constraints before you write code saves hours of debugging later.
How to Wire the HC-SR501 to a Raspberry Pi
The HC-SR501 has three pins: VCC, OUT, and GND. Wiring takes under two minutes with a breadboard and three jumper wires.
Connection steps:
- Connect VCC on the sensor to a 5V pin on the Pi (physical pin 2 or 4)
- Connect GND on the sensor to a ground pin on the Pi (physical pin 6, 9, 14, 20, 25, 30, 34, or 39)
- Connect OUT on the sensor to GPIO4 (physical pin 7) or any available GPIO pin
- Adjust the sensitivity potentiometer clockwise for maximum range (7m) or counter-clockwise to reduce it
- Adjust the time delay potentiometer to set how long the output stays high after detection
After connecting power, wait 30 to 60 seconds for the sensor to stabilize. During this initialization period the output may pulse randomly as the pyroelectric element calibrates to the ambient infrared baseline. Your code should account for this startup delay before treating readings as valid.
Choosing a GPIO pin: The gpiozero library's MotionSensor class accepts any GPIO pin number. GPIO4 is the conventional choice in Raspberry Pi Foundation tutorials, but GPIO17 is equally common in third-party guides. Pick whichever pin is physically convenient for your enclosure layout. Avoid GPIO pins that are already allocated to I2C (GPIO2, GPIO3) or SPI (GPIO7-GPIO11) if you plan to add other peripherals later.
Writing Python Detection Scripts
The fast path to a working motion detector is the gpiozero library, which ships pre-installed on Raspberry Pi OS. Here is the minimal detection loop:
from gpiozero import MotionSensor
from signal import pause
pir = MotionSensor(4)
pir.when_motion = lambda: print("Motion detected")
pir.when_no_motion = lambda: print("Clear")
pause()
This event-driven approach is better than polling in a while True loop because it lets the OS sleep the process between events instead of burning CPU cycles checking a pin every half second.
For more control, gpiozero's MotionSensor class accepts a queue_len parameter that smooths out twitchy sensors. If your HC-SR501 produces false triggers, increase queue_len from 1 to 5 or higher. The class samples the pin at sample_rate Hz (default 10) and only fires when_motion once the proportion of active readings in the queue exceeds threshold (default 0.5).
pir = MotionSensor(4, queue_len=5, sample_rate=10, threshold=0.5)
Logging motion events to CSV is the first practical automation beyond "print detected." Timestamps in a structured file give you data you can analyze later or push to a cloud workspace for long-term storage.
import csv
from datetime import datetime
from gpiozero import MotionSensor
from signal import pause
pir = MotionSensor(4)
def log_motion():
with open("motion_log.csv", "a", newline="") as f:
writer = csv.writer(f)
writer.writerow([datetime.now().isoformat(), "motion_detected"])
print(f"Logged at {datetime.now()}")
pir.when_motion = log_motion
pause()
Run this as a systemd service so it survives reboots. Create a unit file at /etc/systemd/system/motion-detector.service, set ExecStart to your Python script, enable it with systemctl enable motion-detector, and the Pi starts logging motion events on every boot.
Store and query motion data from any edge device
Upload PIR sensor logs and camera captures to a shared workspace with built-in AI search and an MCP-ready API for your scripts. Starts with a 14-day free trial.
Triggering Camera Recordings on Motion
Combining a PIR sensor with a Raspberry Pi Camera Module turns a $40 setup into a motion-activated security camera. The picamera2 library, which replaced the legacy picamera module, handles camera initialization and recording on modern Raspberry Pi OS.
from gpiozero import MotionSensor
from picamera2 import Picamera2
from datetime import datetime
import time
pir = MotionSensor(4)
camera = Picamera2()
config = camera.create_video_configuration(main={"size": (1280, 720)})
camera.configure(config)
while True:
pir.wait_for_motion()
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
filename = f"motion_{timestamp}.h264"
camera.start_and_record_video(filename)
print(f"Recording: {filename}")
pir.wait_for_no_motion()
camera.stop_recording()
print("Stopped recording")
This script waits until the PIR sensor fires, records H.264 video, and stops when the sensor returns to idle. The hold time potentiometer on the HC-SR501 controls how long the sensor stays active after the last detected movement, so set it to at least 10 seconds to avoid clipping short recordings.
Capturing still images instead of video is simpler and produces smaller files that are easier to send as notifications:
pir.wait_for_motion()
camera.start()
camera.capture_file(f"capture_{timestamp}.jpg")
camera.stop()
If you are running a Pi Zero 2 W with limited storage, consider deleting recordings older than 24 hours with a cron job, or syncing them to cloud storage where they will not fill your SD card.
How to Send Motion Alerts and Push Notifications
A motion detector that only writes to local storage is not useful if you are away from home. Two practical notification channels work well with a Raspberry Pi: Telegram bots and email via SMTP.
Telegram bot alerts are the simplest push notification path. Create a bot through BotFather on Telegram, grab the API token, and send messages with the requests library:
import requests
TELEGRAM_TOKEN = "your_bot_token"
CHAT_ID = "your_chat_id"
def send_alert(message):
url = f"https://api.telegram.org/bot{TELEGRAM_TOKEN}/sendMessage"
requests.post(url, data={"chat_id": CHAT_ID, "text": message})
pir.when_motion = lambda: send_alert("Motion detected at front door")
You can extend this to send a photo by using sendPhoto with the captured image file. Several open-source projects on GitHub, including m0by314/raspberry_pi_home_security_system, package this pattern into a complete home security system with start/stop commands via Telegram.
Email alerts work through Python's smtplib. Create a dedicated email account for sending rather than using your personal account, because scripted sends from new IP addresses can trigger account lockouts. Gmail app passwords or a transactional email service like SendGrid avoid this problem.
Rate limiting matters. Without a cooldown, a PIR sensor in a busy hallway will spam your phone with hundreds of alerts per hour. Add a minimum interval between notifications:
import time
last_alert = 0
COOLDOWN = 300 # 5 minutes
def throttled_alert():
global last_alert
now = time.time()
if now - last_alert > COOLDOWN:
send_alert("Motion detected")
last_alert = now
Syncing Motion Logs to Cloud Storage
Local CSV files and video clips on an SD card are fragile. Cards corrupt, Pis lose power, and nobody wants to SSH into a closet-mounted Pi to pull log files. Syncing motion data to cloud storage solves the durability problem and opens the door to querying motion patterns with AI tools.
You could use rsync to a VPS, or push files to S3 or Google Drive with their respective CLI tools. For teams that want to query and collaborate on motion data alongside other project files, Fast.io provides shared workspaces where both humans and AI agents can access the same files. Upload motion logs and capture images through the Fast.io MCP server or the REST API, and anyone on the team can browse, search, or ask questions about the data from the web interface.
Fast.io's Intelligence Mode auto-indexes uploaded files for semantic search and RAG chat. Upload a month of motion logs and you can ask "How many motion events happened between midnight and 6 AM last week?" and get a cited answer without writing a SQL query. Metadata Views can extract structured fields from your CSV logs, turning raw timestamps into a sortable, filterable spreadsheet.
Plans start with a 14-day free trial (Solo at $29/month, Business at $99/month), with storage and monthly credits scaled to each tier, which is more than enough for a single-sensor PIR setup writing a few megabytes of CSV per month. For multi-camera setups producing video, the credit-based bandwidth pricing scales with actual usage rather than a fixed monthly fee.
A Python upload script on the Pi can run after each motion event or on a cron schedule. Push the CSV and any captured images, and the data becomes immediately available in the workspace, searchable, and ready for AI queries.
Battery Power and Outdoor Deployment
The Raspberry Pi Zero 2 W draws around 80mA when running headless with HDMI and LEDs disabled. Combined with the HC-SR501's 65 microamp quiescent current, a 10,000mAh USB battery bank can power the pair for roughly 48 hours of continuous operation.
For longer deployments, a small solar panel (6V, 1W) connected through an Adafruit PowerBoost 1000C charges a LiPo battery during the day. This combination lets you deploy a motion sensor at a property entrance, a wildlife monitoring station, or a warehouse dock without running power cables.
Power optimization tips:
- Disable HDMI output:
sudo tvservice -o - Disable the onboard LED: add
dtparam=act_led_trigger=noneto/boot/config.txt - Run headless with no desktop environment, use Raspberry Pi OS Lite
- Use a Witty Pi HAT to wake the Pi only when the PIR sensor triggers, cutting idle power to near zero
- Set the HC-SR501 to single-trigger mode so the output drops low between events, allowing wake-on-GPIO
Weatherproofing for outdoor installs means an IP65-rated enclosure with the sensor lens exposed through a cutout. Keep the Fresnel lens clean because dust, spider webs, and condensation reduce detection range. Point the sensor away from surfaces that radiate heat (sun-baked walls, dryer vents) to minimize false triggers.
For remote deployments without Wi-Fi, an LTE modem connected to the Pi can upload motion data over cellular. Combined with Telegram notifications, this gives you a fully autonomous motion monitoring station that reports back over mobile data.
Frequently Asked Questions
How do you connect a PIR sensor to Raspberry Pi?
Connect VCC on the HC-SR501 to a 5V pin on the Pi, GND to a ground pin, and OUT to any GPIO pin (GPIO4 is conventional). Use three jumper wires and a breadboard. Wait 30 to 60 seconds after powering on for the sensor to calibrate before trusting the readings.
What is the range of a Raspberry Pi motion sensor?
The HC-SR501 PIR sensor detects warm bodies at distances from 3 to 7 meters, adjustable via the sensitivity potentiometer. The detection cone spans approximately 120 degrees through the Fresnel lens. Actual range depends on the size and speed of the moving object, ambient temperature, and whether anything obstructs the sensor's line of sight.
Can Raspberry Pi detect motion without a camera?
Yes. A PIR sensor detects motion by sensing changes in infrared radiation from warm bodies. It outputs a digital signal to a GPIO pin that Python code can read. No camera, no image processing, and no machine learning model required. PIR sensors also draw far less power than a camera-based detection system.
How do I make a Raspberry Pi motion alarm?
Wire an HC-SR501 PIR sensor to a GPIO pin, then use the gpiozero MotionSensor class to trigger an action when motion is detected. For a local alarm, drive a buzzer or LED from another GPIO pin. For a remote alarm, send a Telegram message or email using Python's requests or smtplib libraries. Add a cooldown timer to avoid alert fatigue.
What causes false triggers on a PIR motion sensor?
Common causes include pets or other warm-bodied animals, HVAC vents blowing warm or cold air across the sensor, direct sunlight or reflections hitting the Fresnel lens, and nearby electronics generating infrared interference. Reduce sensitivity with the onboard potentiometer, reposition the sensor away from heat sources, and increase the gpiozero queue_len parameter to filter out brief spikes.
Can I run a PIR motion sensor on a Raspberry Pi Zero?
Yes. The Pi Zero and Pi Zero 2 W both support GPIO and run the same gpiozero Python library. The Pi Zero 2 W is the better choice for battery-powered deployments because it draws around 80mA headless and supports Wi-Fi for sending notifications. The HC-SR501 itself draws negligible current, so the Pi is the primary power consumer.
Related Resources
Store and query motion data from any edge device
Upload PIR sensor logs and camera captures to a shared workspace with built-in AI search and an MCP-ready API for your scripts. Starts with a 14-day free trial.