Raspberry Pi Camera Project
How to Build a Residential Intrusion Detection System #raspiSeries — Episode 2
Discover how to create a robust
residential intrusion detection system
using a Raspberry Pi camera.
This guide will walk you through
setting up the hardware,
configuring the software,
and leveraging intelligent algorithms
to detect unauthorized access effectively.
By the end, you'll have a fully functional
system that enhances home security using affordable,
readily available technology.
Main Features
**Motion Detection:**
Utilizes PIR sensors to detect movements at critical entry points.
Captures images upon detecting motion and records the events.
**Web Interface:**
Provides a user-friendly interface to view captured images and manage the system.
Features buttons to check the most recent photos and clear the camera directory.
**Security:**
Implements user authentication and session management for secure access.
Utilizes secret keys for enhanced web application security.
1 Here is the first Code:
# cam6.py
import RPi.GPIO as GPIO
import time
PIR_PIN = 4
GPIO.setmode(GPIO.BCM)
GPIO.setup(PIR_PIN, GPIO.IN)
MOV_DETECT_THREASHOLD = 3.0
last_pir_state = GPIO.input(PIR_PIN)
movement_timer = time.time()
try:
while True:
time.sleep(0.01)
pir_state = GPIO.input(PIR_PIN)
if last_pir_state == GPIO.LOW and pir_state == GPIO.HIGH:
movement_timer = time.time()
if last_pir_state == GPIO.HIGH and pir_state == GPIO.HIGH:
if time.time() - movement_timer > MOV_DETECT_THREASHOLD:
print("Take Photo and Send it by Email")
last_pir_state = pir_state
except KeyboardInterrupt:
GPIO.cleanup()
This Python script sets up a PIR motion sensor on GPIO pin 4 of a Raspberry Pi to detect motion. When motion is detected and sustained for more than 3 seconds (MOV_DETECT_THRESHOLD
), it prints Take Photo and Send it by Email
. The script continuously monitors the PIR sensor state, tracking changes to determine when the threshold is exceeded, and gracefully exits by cleaning up the GPIO settings when interrupted.
2 To prevent multiple photos:
To prevent the script from taking too many photos, you need to add a delay between taking photos after motion is detected. Currently, the script only checks if motion is detected for a sustained period but does not implement a cool-down period to prevent multiple photos from being taken in rapid succession.
Here’s how you can modify the script to include these changes:
# cam7.py
import RPi.GPIO as GPIO
import time
PIR_PIN = 4
GPIO.setmode(GPIO.BCM)
GPIO.setup(PIR_PIN, GPIO.IN)
MOV_DETECT_THRESHOLD = 3.0 # Time threshold for sustained motion
MIN_DURATION_BETWEEN_PHOTOS = 5.0 # Minimum time between two photos (in seconds)
last_pir_state = GPIO.input(PIR_PIN)
movement_timer = time.time()
last_time_photo_taken = 0 # Initialize last photo time to 0
try:
while True:
time.sleep(0.01)
pir_state = GPIO.input(PIR_PIN)
# Detecting the start of motion
if last_pir_state == GPIO.LOW and pir_state == GPIO.HIGH:
movement_timer = time.time()
# Sustained motion detection
if last_pir_state == GPIO.HIGH and pir_state == GPIO.HIGH:
if time.time() - movement_timer > MOV_DETECT_THRESHOLD:
# Check if enough time has passed since the last photo
if time.time() - last_time_photo_taken > MIN_DURATION_BETWEEN_PHOTOS:
print("Take Photo and Send it by Email")
last_time_photo_taken = time.time() # Update the last photo taken time
last_pir_state = pir_state
except KeyboardInterrupt:
GPIO.cleanup()
Here’s how you can fix this issue:
- Introduce a Minimum Time Interval Between Photos: Add a variable to keep track of the time when the last photo was taken.
- Check the Time Since the Last Photo: Before taking a new photo, check if the required interval has passed since the last photo.
# Check if enough time has passed since the last photo
if time.time() - last_time_photo_taken > MIN_DURATION_BETWEEN_PHOTOS:
# now take a photo
Here is the result:
3 Set a visual indicator by adding an LED to signal when movement is detected:
# cam8.py
import RPi.GPIO as GPIO
import time
PIR_PIN = 4
LED_PIN = 17
# Setup GPIOs
GPIO.setmode(GPIO.BCM)
GPIO.setup(PIR_PIN, GPIO.IN)
GPIO. setup(LED_PIN, GPIO.OUT)
GPIO.output(LED_PIN, GPIO.LOW)
print("GPIOs setup ok.")
MOV_DETECT_THRESHOLD = 3.0 # Time threshold for sustained motion
MIN_DURATION_BETWEEN_PHOTOS = 5.0 # Minimum time between two photos (in seconds)
last_pir_state = GPIO.input(PIR_PIN)
movement_timer = time.time()
last_time_photo_taken = 0 # Initialize last photo time to 0
try:
while True:
time.sleep(0.01)
pir_state = GPIO.input(PIR_PIN)
# Activate LED when movement is detected.
if pir_state == GPIO.HIGH:
GPIO.output(LED_PIN, GPIO.HIGH)
else:
GPIO.output(LED_PIN, GPIO.LOW)
# Detecting the start of motion
if last_pir_state == GPIO.LOW and pir_state == GPIO.HIGH:
movement_timer = time.time()
# Sustained motion detection
if last_pir_state == GPIO.HIGH and pir_state == GPIO.HIGH:
if time.time() - movement_timer > MOV_DETECT_THRESHOLD:
# Check if enough time has passed since the last photo
if time.time() - last_time_photo_taken > MIN_DURATION_BETWEEN_PHOTOS:
print("Take Photo and Send it by Email")
last_time_photo_taken = time.time() # Update the last photo taken time
last_pir_state = pir_state
except KeyboardInterrupt:
GPIO.cleanup()
Modified Parts Explanation:
- LED Setup:
- An LED is added as a visual indicator for motion detection. The LED is connected to
GPIO 17
(LED_PIN
), and the GPIO pin is set up as an output (GPIO.setup(LED_PIN, GPIO.OUT)
). Initially, the LED is turned off (GPIO.output(LED_PIN, GPIO.LOW)
).
- LED Activation:
- Inside the
while
loop, the LED is turned on (GPIO.output(LED_PIN, GPIO.HIGH)
) whenever motion is detected (pir_state == GPIO.HIGH
). If no motion is detected, the LED is turned off (GPIO.output(LED_PIN, GPIO.LOW)
).
4 Configuring the Camera:
# cam9.py
import RPi.GPIO as GPIO
import time
from picamera2 import Picamera2
PIR_PIN = 4
LED_PIN = 17
# Setup camera
camera = Picamera2()
camera.resolution = (720, 480)
camera.rotation = 180
print("Waiting 2 seconds to init the camera...")
time.sleep(2)
print("Camera setup ok.")
# Setup GPIOs
GPIO.setmode(GPIO.BCM)
GPIO.setup(PIR_PIN, GPIO.IN)
GPIO. setup(LED_PIN, GPIO.OUT)
GPIO.output(LED_PIN, GPIO.LOW)
print("GPIOs setup ok.")
MOV_DETECT_THRESHOLD = 3.0 # Time threshold for sustained motion
MIN_DURATION_BETWEEN_PHOTOS = 5.0 # Minimum time between two photos (in seconds)
last_pir_state = GPIO.input(PIR_PIN)
movement_timer = time.time()
last_time_photo_taken = 0 # Initialize last photo time to 0
print("Everything has been setup.")
try:
while True:
time.sleep(0.01)
pir_state = GPIO.input(PIR_PIN)
# Activate LED when movement is detected.
if pir_state == GPIO.HIGH:
GPIO.output(LED_PIN, GPIO.HIGH)
else:
GPIO.output(LED_PIN, GPIO.LOW)
# Detecting the start of motion
if last_pir_state == GPIO.LOW and pir_state == GPIO.HIGH:
movement_timer = time.time()
# Sustained motion detection
if last_pir_state == GPIO.HIGH and pir_state == GPIO.HIGH:
if time.time() - movement_timer > MOV_DETECT_THRESHOLD:
# Check if enough time has passed since the last photo
if time.time() - last_time_photo_taken > MIN_DURATION_BETWEEN_PHOTOS:
print("Take Photo and Send it by Email")
last_time_photo_taken = time.time() # Update the last photo taken time
last_pir_state = pir_state
except KeyboardInterrupt:
GPIO.cleanup()
Modified Parts Explanation:
The added code introduces camera functionality using the Picamera2
library to capture photos when motion is detected:
- Camera Setup:
camera = Picamera2()
initializes the Picamera2 object to interact with the camera hardware.camera.resolution = (720, 480)
sets the camera resolution to 720x480 pixels.camera.rotation = 180
rotates the camera image by 180 degrees to match the desired orientation.- The script pauses for 2 seconds with
time.sleep(2)
to allow the camera to initialize properly.
2 . Taking Photos on Motion Detection:
- The camera setup is now ready to take pictures when motion is detected by the PIR sensor, as managed in the motion detection loop. However, note that the actual code for capturing and sending photos is not provided in this snippet, but the camera initialization steps are necessary for its integration.
This additional setup ensures that the camera is correctly initialized and ready for use when motion is detected.
5 Captured the actual photo:
# cam10.py
import RPi.GPIO as GPIO
import time
from picamera2 import Picamera2
from libcamera import Transform
import os
PIR_PIN = 4
LED_PIN = 17
def take_photo(picam2):
# Ensure the directory exists
if not os.path.exists("/home/pi/Camera"):
os.makedirs("/home/pi/Camera")
file_name = "/home/pi/Camera/img_" + str(time.time()) + ".jpg"
picam2.capture_file(file_name)
print(f"Photo saved: {file_name}")
return file_name
# Setup camera
picam2 = Picamera2()
picam2.configure(picam2.create_still_configuration(transform=Transform(rotation=180)))
picam2.start() # Start the camera
# Pause for 2 seconds to allow the camera to stabilize
time.sleep(2)
print("Camera setup ok.")
# Setup GPIOs
GPIO.setmode(GPIO.BCM)
GPIO.setup(PIR_PIN, GPIO.IN)
GPIO.setup(LED_PIN, GPIO.OUT)
GPIO.output(LED_PIN, GPIO.LOW)
print("GPIOs setup ok.")
MOV_DETECT_THRESHOLD = 3.0 # Time threshold for sustained motion
MIN_DURATION_BETWEEN_PHOTOS = 60.0 # Minimum time between two photos (in seconds)
last_pir_state = GPIO.input(PIR_PIN)
movement_timer = time.time()
last_time_photo_taken = 0 # Initialize last photo time to 0
print("Everything has been set up.")
try:
while True:
time.sleep(0.01)
pir_state = GPIO.input(PIR_PIN)
# Activate LED when movement is detected.
GPIO.output(LED_PIN, GPIO.HIGH if pir_state == GPIO.HIGH else GPIO.LOW)
# Detecting the start of motion
if last_pir_state == GPIO.LOW and pir_state == GPIO.HIGH:
movement_timer = time.time()
# Sustained motion detection
if last_pir_state == GPIO.HIGH and pir_state == GPIO.HIGH:
if time.time() - movement_timer > MOV_DETECT_THRESHOLD:
# Check if enough time has passed since the last photo
if time.time() - last_time_photo_taken > MIN_DURATION_BETWEEN_PHOTOS:
print("Take Photo and Send it by Email")
take_photo(picam2)
last_time_photo_taken = time.time() # Update the last photo taken time
last_pir_state = pir_state
except KeyboardInterrupt:
GPIO.cleanup()
picam2.stop()
Explanation of the Added Parts:
1. Directory Check and Creation in take_photo(picam2)
Function:
if not os.path.exists("/home/pi/Camera"):
os.makedirs("/home/pi/Camera")
- This code ensures that the directory
/home/pi/Camera
exists before saving a photo. If the directory does not exist, it is created usingos.makedirs()
. This prevents errors when the script attempts to save a photo to a non-existent directory.
2. Camera Configuration with Transform
:
picam2.configure(picam2.create_still_configuration(transform=Transform(rotation=180)))
picam2.start() # Start the camera
- The camera is configured with a specific setting that includes a rotation transformation (
rotation=180
). This rotates the captured images by 180 degrees, which may be necessary if the camera is mounted upside down or in a specific orientation.
3. Starting the Camera:
picam2.start() # Start the camera
- After configuring the camera, the script starts it with
picam2.start()
, making it ready to capture photos.
4. Added Cleanup for Camera:
picam2.stop()
- When the script is interrupted (e.g., by pressing Ctrl+C), this line stops the camera properly, ensuring that all resources are released and the camera is safely shut down.
6 Setting timestamp:
# cam11.py
import RPi.GPIO as GPIO
import time, cv2
from picamera2 import Picamera2, MappedArray
from libcamera import Transform
import os
PIR_PIN = 4
LED_PIN = 17
resolution = (800, 600)
def apply_text(request):
# Text options
colour = (255, 255, 255)
origin = (0, 60)
font = cv2.FONT_HERSHEY_SIMPLEX
scale = 1
thickness = 1
# text = "17082024 09:07"
# Get the current time in the format "DDMMYYYY HH:MM"
text = time.strftime("%d%m%Y %H:%M")
# Calculate the text size
text_size, _ = cv2.getTextSize(text, font, scale, thickness)
# Calculate the bottom-right origin
x = resolution[0] - text_size[0] - 10 # 10 pixels padding from the right
y = resolution[1] - 10 # 10 pixels padding from the bottom
origin = (x, y)
with MappedArray(request, "main") as m:
cv2.putText(m.array, text, origin, font, scale, colour, thickness)
def take_photo(picam2):
# Ensure the directory exists
if not os.path.exists("/home/pi/Camera"):
os.makedirs("/home/pi/Camera")
file_name = "/home/pi/Camera/img_" + str(time.time()) + ".jpg"
# picam2.capture_file(file_name)
picam2.switch_mode_and_capture_file(capture_config, file_name)
print(f"Photo saved: {file_name}")
return file_name
# Setup camera
picam2 = Picamera2()
# picam2.configure(picam2.create_still_configuration(transform=Transform(rotation=180)))
# Create two separate configs - one for preview and one for capture.
# Make sure the preview is the same resolution as the capture, to make
# sure the overlay stays the same size
capture_config = picam2.create_still_configuration({"size": resolution}, transform=Transform(hflip=True, vflip=True))
preview_config = picam2.create_preview_configuration({"size": resolution}, transform=Transform(hflip=True, vflip=True))
# Set the current config as the preview config
picam2.configure(preview_config)
# Add the timestamp
picam2.pre_callback = apply_text
# Start the camera
picam2.start(show_preview=True)
picam2.start() # Start the camera
# Pause for 2 seconds to allow the camera to stabilize
time.sleep(2)
print("Camera setup ok.")
# Setup GPIOs
GPIO.setmode(GPIO.BCM)
GPIO.setup(PIR_PIN, GPIO.IN)
GPIO.setup(LED_PIN, GPIO.OUT)
GPIO.output(LED_PIN, GPIO.LOW)
print("GPIOs setup ok.")
MOV_DETECT_THRESHOLD = 3.0 # Time threshold for sustained motion
MIN_DURATION_BETWEEN_PHOTOS = 60.0 # Minimum time between two photos (in seconds)
last_pir_state = GPIO.input(PIR_PIN)
movement_timer = time.time()
last_time_photo_taken = 0 # Initialize last photo time to 0
print("Everything has been set up.")
try:
while True:
time.sleep(0.01)
pir_state = GPIO.input(PIR_PIN)
# Activate LED when movement is detected.
GPIO.output(LED_PIN, GPIO.HIGH if pir_state == GPIO.HIGH else GPIO.LOW)
# Detecting the start of motion
if last_pir_state == GPIO.LOW and pir_state == GPIO.HIGH:
movement_timer = time.time()
# Sustained motion detection
if last_pir_state == GPIO.HIGH and pir_state == GPIO.HIGH:
if time.time() - movement_timer > MOV_DETECT_THRESHOLD:
# Check if enough time has passed since the last photo
if time.time() - last_time_photo_taken > MIN_DURATION_BETWEEN_PHOTOS:
print("Take Photo and Send it by Email")
take_photo(picam2)
last_time_photo_taken = time.time() # Update the last photo taken time
last_pir_state = pir_state
except KeyboardInterrupt:
GPIO.cleanup()
picam2.stop()
Explanation of the Modified Parts:
1. Text Overlay on Photos:
- The
apply_text
function has been added to overlay the current date and time on each photo. It uses the OpenCV library (cv2
) to draw text on the image.
2. Key Points in apply_text
Function:
- Text Formatting: The color, position, font, scale, and thickness of the text are defined.
- Dynamic Timestamp: The timestamp format is set to “DDMMYYYY HH”, and the text size is calculated to position it correctly at the bottom right of the image.
- Text Overlay: The text is overlaid onto the image buffer using
cv2.putText
.
3. Camera Configuration Changes:
- Multiple Configurations: Two configurations are created for the camera:
capture_config
: Used when taking a still photo with horizontal and vertical flips.preview_config
: Used for the live preview, ensuring the resolution matches the capture configuration to maintain consistent overlay size.- Camera Initialization:
- The camera is initialized with the
preview_config
, and the timestamp overlay is added usingpicam2.pre_callback = apply_text
. - The camera preview is started with
show_preview=True
.
3. Photo Capture with Text Overlay:
- The
take_photo
function has been updated to useswitch_mode_and_capture_file
instead of a simple capture call. This ensures that the configured settings (including the text overlay) are applied when the photo is taken.
4. Camera Setup Enhancements:
- Camera is stabilized for 2 seconds with
time.sleep(2)
to ensure a smooth startup. - Camera transformation has been adjusted with
Transform(hflip=True, vflip=True)
to correctly orient the image.
These modifications allow the program to display a live preview with text overlay and save photos with the current timestamp whenever movement is detected.
That’s a wrap for now!
In the next installment, we’ll implement logging facilities and Web Interface.
See you soon!
👉GitHub Repo Episode 2
Note:
In Raspberry Pi programming, the GPIO.setmode(GPIO.BCM)
function is used to specify how you want to refer to the General Purpose Input/Output (GPIO) pins on the Raspberry Pi.
The Raspberry Pi has two main numbering systems for its GPIO pins:
- BCM (Broadcom SOC Channel) Mode: This refers to the GPIO numbers based on the Broadcom system-on-a-chip (SOC) used in the Raspberry Pi. Each GPIO pin on the Raspberry Pi’s header corresponds to a Broadcom SOC channel, which is identified by a unique number. For example, GPIO pin 2 on the Broadcom SOC is known as GPIO2 in BCM mode.
- BOARD Mode: This refers to the physical pin numbers on the Raspberry Pi’s GPIO header. For example, pin 1 is the first pin on the header, pin 2 is the second pin, and so on.
Why Use BCM Mode?
- Consistency Across Models: Using BCM mode ensures that your code refers to the same GPIO pins regardless of the Raspberry Pi model. Different models have different pin layouts, but the BCM numbering remains consistent for the corresponding SOC channels.
- Standard Practice: Many tutorials and examples use BCM mode, making it easier to follow and use existing resources.
[Ubuntu Terminal - Host]
sudo scp pi@192.168.0.114:/home/pi/Desktop/*.py /home/j3/Documents/raspi_projects/Episode_2
[Change the Owner for your User Name]
sudo chown -R j3:j3 /home/j3/Documents/raspi_projects/Episode_2
Credits & References
Raspberry Pi OS by raspberrypi.com
Related Posts
Raspberry PI
0#Episode — #raspiSeries — Raspberry Pi Intro — Quick And Easy Way To Install Raspberry Pi OS
1#Episode — #raspiSeries — Raspberry Pi Camera Module — How To Connect the Rpicam, Take Pictures, Record Video, and Apply image effects
2#Episode — #raspiSeries — Raspberry Pi Camera Project — How to Build a Residential Intrusion Detection System (this one)
3#Episode — #raspiSeries — Raspberry Pi Camera Project — How to Build a Residential Intrusion Detection System
OpenCV
00 Episode#Hi Python Computer Vision — PIL! — An Intro To Python Imaging Library #PyVisionSeries
01 Episode# Jupyter-lab — Python — OpenCV — Image Processing Exercises #PyVisionSeries
02 Episode# OpenCV — Image Basics — Create Image From Scratch #PyVisionSeries
03 Episode# OpenCV — Morphological Operations — How To Erode, Dilate, Edge Detect w/ Gradient #PyVisionSeries
04 Episode# OpenCV — Histogram Equalization — HOW TO Equalize Histograms Of Images — #PyVisionSeries
05 Episode# OpenCV — OpenCV — Resize an Image — How To Resize Without Distortion
07 Episode# YOLO — Object Detection — The state of the art in object detection Framework!
08 Episode# OpenCV — HaashCascate — Object Detection — Viola–Jones object detection framework — #PyVisionSeries