Raspberry Pi Camera Project
How to Build a Residential Intrusion Detection System #raspiSeries — Episode 3
This continues from the previous episode.
Now, let’s add logging functionality to support future use with artificial intelligence algorithms.
Then we will send photos taken by Raspberry Pi via email.
Let’s get started!
1 Logging Functionality:
The script cam12.py
uses a Raspberry Pi with a PIR sensor, LED, and camera to detect motion and capture photos. When motion is detected, the camera takes a photo with a timestamp overlay using OpenCV, and the image is saved to a specified directory. The script ensures necessary directories exist for image and log storage, and controls the LED based on motion detection. It continuously monitors the PIR sensor, taking a new photo only if a specified duration has passed since the last capture.
# cam12.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)
LOG_FILE_NAME = "/home/pi/camera/log/photo_logs.txt"
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
# Ensure that the directory exists before attempting to write to the log file
def update_photo_log_file(photo_file_name):
# Ensure the directory exists
log_directory = os.path.dirname(LOG_FILE_NAME)
if not os.path.exists(log_directory):
os.makedirs(log_directory)
with open(LOG_FILE_NAME, "a") as f:
f.write(photo_file_name)
f.write("\n")
# 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=False)
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")
photo_file_name = take_photo(picam2)
update_photo_log_file(photo_file_name)
last_time_photo_taken = time.time() # Update the last photo taken time
last_pir_state = pir_state
except KeyboardInterrupt:
GPIO.cleanup()
picam2.stop()
The logging functionality in the script involves recording the filenames of the photos taken by the Raspberry Pi camera when motion is detected. This is done in the update_photo_log_file
function. The function first checks if the directory for the log file (/home/pi/camera/log/
) exists, and if not, creates it to ensure the log file can be written. It then appends the name of each photo taken, including its full path, to the photo_logs.txt
file, each entry on a new line, allowing the user to keep a record of all captured photos.
2 Sending Email:
to prevent this error:
ModuleNotFoundError: No module named 'yagmail'
Open Raspberry Pi Terminal and type:
pip3 install yagmail
If it doesn’t work (I’m currently running into issues on bookworm), you’ll need to manually download and install the yagmail
package.
This is my OS version:
cat /etc/os-release
PRETTY_NAME="Debian GNU/Linux 12 (bookworm)"
NAME="Debian GNU/Linux"
VERSION_ID="12"
VERSION="12 (bookworm)"
VERSION_CODENAME=bookworm
ID=debian
HOME_URL="https://www.debian.org/"
SUPPORT_URL="https://www.debian.org/support"
BUG_REPORT_URL="https://bugs.debian.org/"
To install Yagmail manually, first visit the Yagmail PyPI page to find the latest version (mine is 0.15.293).
Then, run these commands:
sudo apt-get update
[1-Steps to Install yagmail Using Updated Methods]
[Find the Latest Version of yagmail:]
[2-Download the Latest Source Code:]
wget https://files.pythonhosted.org/packages/source/y/yagmail/yagmail-0.15.293.tar.gz
tar -xzvf yagmail-0.15.293.tar.gz
[3-Extract and Install the Package:]
tar -xzvf yagmail-0.15.293.tar.gz
cd yagmail-0.15.293
sudo python3 setup.py install
[4-Install Dependencies:]
sudo apt-get install python3-keyring python3-requests
[5-Verify the Installation:]
python3 -c "import yagmail; print(yagmail.__version__)"
0.15.293
Once you install Yagmail sucessfully now let’s Securely Save Google Email Credentials in Python.
First, you’ll need to learn how to create app passwords. Follow this instructions. Create one secure token and proceed.
Second, Open your Raspberry Pi terminal.
- Edit the profile file (e.g.,
~/.bashrc
or~/.bash_profile
)
nano ~/.bashrc
Add the following line at the end of the file:
export FROM_EMAIL='your_email_account_here'
export TO_EMAIL='other_email_destination_here'
export EMAIL_TOKEN='your_secure_token_here'
- Save the file and close the editor.
- Reload the file to apply the changes:
source ~/.bashrc
3 Now let’s code!
# cam13.py
import RPi.GPIO as GPIO
import time, cv2
from picamera2 import Picamera2, MappedArray
from libcamera import Transform
import os
import yagmail
email_token = os.getenv('EMAIL_TOKEN')
if not email_token:
raise ValueError("No EMAIL TOKEN found. Set the EMAIL_TOKEN environment variable.")
from_email = os.getenv('FROM_EMAIL')
if not from_email:
raise ValueError("No FROM EMAIL ACCOUNT found. Set the FROM EMAIL environment variable.")
to_email = os.getenv('TO_EMAIL')
if not to_email:
raise ValueError("No TO EMAIL ACCOUNT found. Set the TO_EMAIL environment variable.")
PIR_PIN = 4
LED_PIN = 17
resolution = (800, 600)
LOG_FILE_NAME = "/home/pi/Camera/log/photo_logs.txt"
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
# Ensure that the directory exists before attempting to write to the log file
def update_photo_log_file(_photo_file_name):
# Ensure the directory exists
log_directory = os.path.dirname(LOG_FILE_NAME)
if not os.path.exists(log_directory):
os.makedirs(log_directory)
with open(LOG_FILE_NAME, "a", encoding="utf-8") as f:
f.write(_photo_file_name)
f.write("\n")
def send_email_with_photo(yagmail_client, file_name):
yagmail_client.send(to= to_email,
subject="Movement detected!",
contents="Here's a photo taken by your Raspberry Pi",
attachments=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=False)
picam2.start() # Start the camera
# Pause for 2 seconds to allow the camera to stabilize
time.sleep(2)
print("Camera setup ok.")
# Remove log file
if os.path.exists(LOG_FILE_NAME):
os.remove(LOG_FILE_NAME)
print("Log file removed.")
# Setup yagmail
password = email_token
yag = yagmail.SMTP(from_email, password)
print("Email sender 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")
photo_file_name = take_photo(picam2)
update_photo_log_file(photo_file_name)
send_email_with_photo(yag, photo_file_name)
last_time_photo_taken = int(time.time()) # Update the last photo taken time
last_pir_state = pir_state
except KeyboardInterrupt:
GPIO.cleanup()
picam2.stop()
In the script, the send_email_with_photo
function uses the yagmail
library to send an email with an attached photo. It sends an email to to_emal
with the subject Movement detected!
and includes the photo as an attachment. The yagmail_client
parameter is an instance of yagmail.SMTP
configured with the sender's email and password. This function is called after a photo is taken, ensuring that each captured image is emailed to the specified address.
Run the script in the terminal instead of using the Thonny app.
The result:
In the next installment, we’ll implement the web interface.
For now, thank you!
Goodbye!
👉GitHub Repo Episode 3
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
3#Episode — #raspiSeries — Raspberry Pi Camera Project — How to Build a Residential Intrusion Detection System (this one)
[Ubuntu Terminal - Host]
sudo scp pi@192.168.0.114:/home/pi/Desktop/*.py /home/j3/Documents/raspi_projects/Episode_3
[Change the Owner for your User Name]
sudo chown -R j3:j3 /home/j3/Documents/raspi_projects/Episode_3