Motion-triggered wildlife camera with Raspberry Pi and a PIR sensor
Curious what’s visiting your garden or bird feeder at night or while you’re away? A DIY wildlife camera built on a Raspberry Pi costs a fraction of a dedicated commercial trail camera, and you fully control where the footage is stored — no cloud account, no subscription fee.
How it works
- A PIR sensor continuously watches for changes in infrared radiation in its field of view; when a warm-bodied object (a bird, squirrel, cat…) moves through, its output pin switches from LOW to HIGH.
- A Python script on the Raspberry Pi continuously polls the GPIO pin wired to the PIR.
- When it reads HIGH, the script uses
picamera2to capture a still photo (or record a few seconds of video) and saves it with a timestamped filename. - After each capture, the script waits out a cooldown period before resuming monitoring, so one animal lingering to feed doesn’t produce hundreds of near-duplicate files.
Parts list
- Raspberry Pi (a Zero 2 W is a cheap and capable choice; a Pi 4/5 if you want higher-resolution video)
- Pi Camera Module (v2 or v3, connected via the CSI port)
- PIR motion sensor (HC-SR501)
- A microSD card with enough capacity for photos/video (32GB or more recommended for multi-day runs)
- A weatherproof enclosure if mounting outdoors (see the note below)
- A power bank or a stable 5V supply if placed away from an outlet
Sample code
#!/usr/bin/env python3
import time
from datetime import datetime
from pathlib import Path
from gpiozero import MotionSensor
from picamera2 import Picamera2
PIR_PIN = 4
OUTPUT_DIR = Path("/home/pi/wildlife_photos")
COOLDOWN_SECONDS = 30 # avoid rapid-fire captures while an animal lingers
OUTPUT_DIR.mkdir(exist_ok=True)
pir = MotionSensor(PIR_PIN)
camera = Picamera2()
config = camera.create_still_configuration()
camera.configure(config)
camera.start()
time.sleep(2) # let the light sensor settle
print("Ready. Waiting for motion...")
try:
while True:
pir.wait_for_motion()
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
filepath = OUTPUT_DIR / f"animal_{timestamp}.jpg"
camera.capture_file(str(filepath))
print(f"Captured: {filepath}")
# wait out the cooldown before resuming monitoring,
# so an animal that lingers doesn't spam duplicate files
time.sleep(COOLDOWN_SECONDS)
except KeyboardInterrupt:
print("Stopping.")
finally:
camera.stop()
Run it persistently with systemd, or more simply with nohup:
nohup python3 wildlife_camera.py > log.txt 2>&1 &
To record a short video clip instead of a still photo, replace
camera.capture_file()withcamera.start_and_record_video(str(video_path), duration=8)from thepicamera2.encodersmodule.
Common pitfalls
- Not tuning the PIR’s stability settings: many HC-SR501 modules have trimmers for the HIGH hold time (delay) and sensitivity — set the sensitivity too high and it will trigger on leaves rustling in the wind.
- Skipping a software cooldown: relying only on the PIR’s hardware delay isn’t enough — always add a
time.sleep()after each capture in your code so you’re in control, or an animal feeding for five minutes can generate dozens of near-identical files. - Capturing before the camera warms up: calling
capture_file()immediately aftercamera.start()can produce a dark or color-shifted image because the light sensor hasn’t auto-adjusted yet — always wait 1-2 seconds before the first capture. - Filling up the SD card: running for weeks at high resolution can fill a card faster than expected — add a script to prune old files automatically, or sync periodically to a computer or NAS.
A note on outdoor placement
If you’re mounting the camera outside, use a weatherproof enclosure (IP65 or better) with an opening sized to the camera lens, and leave a small vent gap to prevent condensation from building up inside the box as temperatures swing between day and night — trapped moisture is a common cause of corroded boards after a few months outdoors.
Where to go from here
Add an IR illuminator so you get clear photos at night without startling animals with visible light, or run a lightweight species-detection model (TensorFlow Lite) on the Pi to automatically sort captures by species instead of reviewing them by hand.
Comments