The basic parts for a motion sensing camera
While you can also drive a motion sensing camera via image processing on a webcam, a less processor and power intensive means is to only activate the camera when motion is sensed instead. For this a PIR Motion Sensor can be used and processing can track the signal from that device instead.
For this example I used:
- A Raspberry Pi 3 Model B Rev 1.2 (newer models will work, but in combination with Raspbian 11 Bullseye, I found myself having to make adjustments to my former
raspicam
method) - D-SUN PIR Sensor (similar to this HC-SR501 sensor)
- 3 jumper/DuPont female to female wires to connect GPIO pins to PIR sensor leads.
- Raspberry Pi v2.1 camera module
Schematic

If the PIR sensor is oriented sensor upward and pins on the bottom and sensitivity and timing potentiometers on the top in the drawing:
- The top left pin (5V) on the GPIO header goes to the left pin on the PIR sensor.
- The 3rd pin from the top left on the top row of the GPIO header (GND) goes to the right pin on the PIR sensor.
- The middle pin on the PIR sensor connects to the 6th pin from the left on the bottom row of the GPIO header (GPIO 17)
Enabling the Motion Sensing Camera
- You may need to enable SSH if you have a fresh full install of Raspbian 11 so that you can remotely manage things.
dtoverlay=imx219
may have to be added to/boot/config.txt
to properly recognize the camera:
.
.
# Enable DRM VC4 V3D driver
dtoverlay=vc4-kms-v3d
dtoverlay=imx219
max_framebuffers=2
.
.
- If you’re trying to use
libcamera-hello
to test the camera out, you may need to enableGlamor
:
To do this enter
https://www.raspberrypi.com/documentation/accessories/camera.html#getting-startedsudo raspi-config
at a terminal window and then chooseAdvanced Options
,Glamor
andYes
. Finally quitraspi-config
and let it reboot your Raspberry Pi.
Capturing on motion detect
The following python code will capture images to /home/pi/image_captures
when the motion sensor is triggered.
import RPi.GPIO as GPIO
import time
import os
import datetime
SENSOR_PIN=17
GPIO.setmode(GPIO.BCM)
GPIO.setup(SENSOR_PIN, GPIO.IN)
def my_callback(channel):
# yes, datetime.datetime... the first is the datetime module
today = datetime.datetime.now()
# format a timestamp for each image capture
date_time = today.strftime("%Y-%d-%m-%H:%M:%S")
# be sure /home/pi/image_captures exists!
os.system(f'libcamera-still -n -t 1 -o /home/pi/image_captures/capture{date_time}.jpg')
try:
GPIO.add_event_detect(SENSOR_PIN, GPIO.RISING, callback=my_callback)
while True:
time.sleep(100)
except KeyboardInterrupt:
print("Finish...")
GPIO.cleanup()
Adjusting sensitivity and debouncing/time delay
The left potentiometer (when facing it with the dome up) adjusts the delay between successive triggers. Turning the farthest counterclockwise reduces the time delay between triggers to the shortest time possible and clockwise the longest. If the triggers are too frequent, turn clockwise. I left mine turned to shortest time possible.
The right potentiometer adjusts the sensitivity (amount of motion) required to trigger an event. I found a couple different PIR sensors behaved significantly differently, but generally wanted this as far clockwise (most sensitive) as possible, but I wanted to trigger with as little motion as possible.
Auto starting the script
For experimentation, you’ll probably SSH in or connect to a keyboard and monitor… but for a more “production” setup, you’ll want the script to start running as soon as it boots. You can add an invocation to just before the exit 0
in /etc/rc.local
:
.
.
.
sudo -u pi python /home/pi/pir.py &
exit 0
Note that I’m switching to the pi
user with sudo -u pi
so that I write to the pi
user’s home directory with pi
user permissions.
Next Steps
- Synchronize the output from your motion sensing camera to cloud storage (with S3)
- Notify that new motion has been detected using SNS
- Explore a less expensive option using a Linode server with a flat bandwidth allocation instead of PUT object and per GB egress charges