Note
Hello, welcome to the SunFounder Raspberry Pi & Arduino & ESP32 Enthusiasts Community on Facebook! Dive deeper into Raspberry Pi, Arduino, and ESP32 with fellow enthusiasts.
Why Join?
Expert Support: Solve post-sale issues and technical challenges with help from our community and team.
Learn & Share: Exchange tips and tutorials to enhance your skills.
Exclusive Previews: Get early access to new product announcements and sneak peeks.
Special Discounts: Enjoy exclusive discounts on our newest products.
Festive Promotions and Giveaways: Take part in giveaways and holiday promotions.
👉 Ready to explore and create with us? Click [here] and join today!
4.1 Camera
Introduction
This project demonstrates how to create a simple camera system with a shutter button using the Raspberry Pi Zero. When you press the button, the camera captures a photo, and an LED flashes to indicate the action. This is a great way to get hands-on experience with GPIO control and the Raspberry Pi Camera Module.
What You’ll Need
The following components are required for this project:
COMPONENT INTRODUCTION |
PURCHASE LINK |
|---|---|
- |
|
Raspberry Pi |
- |
Circuit Diagram
Below is the GPIO pin mapping for this project:
Wiring Diagram
To use camera module conveniently, Assemble the Pan-tilt (For Camera) is recommended.
Note
Assembling the pan-tilt may obscure some pins, so it is recommended to assemble it only when using the camera, or place it on the outside after assembly.
Follow this wiring diagram to set up the circuit:
Running the Example
Access the Raspberry Pi Desktop:
Remote Desktop: Use VNC for a full desktop experience.
Raspberry Pi Connect: Use Raspberry Pi Connect to access your Pi securely from any browser.
Open a Terminal and go to the code folder:
cd ~/ai-lab-kit/python
Run the script to start the camera:
sudo python3 4.1_Camera.py
After running the program, the camera starts working.
Press the button to take a photo:
The LED lights up.
One photo is captured and saved to the
Picturesfolder (for example,photo_001.jpg,photo_002.jpg).
Release the button:
The LED turns off.
The program keeps running until you press Ctrl + C, then exits cleanly without error messages.
Note
QT preview requires a desktop environment. If the preview cannot be started (for example, over SSH), the camera can still capture and save photos normally.
Code
Here is the Python code used for this project:
#!/usr/bin/env python3
import os
import time
import threading
from picamera2 import Picamera2, Preview
from fusion_hat.pin import Pin, Mode, Pull
# Resolve the correct user's home directory (works with sudo)
REAL_USER = os.getenv("SUDO_USER") or os.getlogin()
USER_HOME = f"/home/{REAL_USER}"
PICTURES_DIR = os.path.join(USER_HOME, "Pictures")
os.makedirs(PICTURES_DIR, exist_ok=True)
# Initialize camera
camera = Picamera2()
camera.configure(camera.create_preview_configuration(main={"size": (800, 600)}))
# Photo counter with thread safety
photo_index = 1
photo_lock = threading.Lock()
# Track whether preview was started successfully
preview_started = False
# Initialize LED and button
led = Pin(17, mode=Mode.OUT)
button = Pin(4, mode=Mode.IN, pull=Pull.DOWN)
def take_photo():
"""Capture one photo and increment the index."""
global photo_index
with photo_lock:
filepath = os.path.join(PICTURES_DIR, f"photo_{photo_index:03d}.jpg")
print(f"\nCapturing: {filepath}")
camera.capture_file(filepath)
print("Saved.")
photo_index += 1
def main():
global preview_started
# Start preview only when a GUI display is available (remote SSH often has no DISPLAY)
preview_started = False
if os.getenv("DISPLAY"):
try:
camera.start_preview(Preview.QT)
preview_started = True
except Exception as e:
preview_started = False
print(f"Preview start failed (continue without preview): {e}")
else:
print("No DISPLAY detected (running headless without preview).")
camera.start()
print("Camera is running.")
print("Press the button to take a photo.")
print(f"Photos will be saved to: {PICTURES_DIR}")
print("Press Ctrl+C to exit.\n")
try:
while True:
if button.value(): # Button pressed (HIGH)
led.on() # LED on
take_photo() # Take photo
time.sleep(0.3) # Simple debounce (avoid multiple shots)
while button.value(): # Wait until button is released
time.sleep(0.01)
led.off() # LED off after release
time.sleep(0.01)
except KeyboardInterrupt:
print("\nExiting...")
finally:
# Turn off LED
try:
led.off()
except Exception:
pass
# Stop the camera first
try:
camera.stop()
except Exception:
pass
# Stop preview only if it was started
if preview_started:
try:
camera.stop_preview()
except Exception:
pass
try:
camera.close()
except Exception:
pass
if __name__ == "__main__":
main()
Understanding the Code
Imports and Purpose
import os import time import threading from picamera2 import Picamera2, Preview from fusion_hat.pin import Pin, Mode, Pull
These modules are responsible for:
os: handling environment variables and file paths.time: providing delays and keeping the main loop running.threading: ensuring photo capture is thread-safe.Picamera2andPreview: controlling the Raspberry Pi camera and preview modes.Pin,Mode, andPull: controlling the LED and reading the push button via Fusion HAT.
Resolving the Save Directory (Works with sudo)
REAL_USER = os.getenv("SUDO_USER") or os.getlogin() USER_HOME = f"/home/{REAL_USER}" PICTURES_DIR = os.path.join(USER_HOME, "Pictures") os.makedirs(PICTURES_DIR, exist_ok=True)
This section ensures photos are always saved to the real user’s
~/Picturesdirectory:When the script is run with
sudo,SUDO_USERpoints to the original user.When not using
sudo, the current login user is used.The
Picturesfolder is created automatically if it does not exist.
Camera Initialization and Configuration
camera = Picamera2() camera.configure(camera.create_preview_configuration(main={"size": (800, 600)}))
The camera is initialized and configured for preview and photo capture:
A preview resolution of
800 × 600is used.This configuration works both with and without a visible preview window.
Photo Index and Thread Safety
photo_index = 1 photo_lock = threading.Lock()
photo_indexstores the current photo number.photo_lockprevents multiple button presses from triggering overlapping photo captures.
Preview State Tracking
preview_started = False
This flag records whether a preview window was successfully started, so the program only tries to stop the preview when it actually exists.
LED and Button Setup
led = Pin(17, mode=Mode.OUT) button = Pin(4, mode=Mode.IN, pull=Pull.DOWN)
The LED is connected to GPIO 17 and configured as an output.
The button is connected to GPIO 4 and configured as an input with an internal pull-down resistor.
A HIGH signal indicates the button is pressed.
Photo Capture Function
def take_photo(): """Capture one photo and increment the index.""" global photo_index with photo_lock: filepath = os.path.join(PICTURES_DIR, f"photo_{photo_index:03d}.jpg") print(f"\nCapturing: {filepath}") camera.capture_file(filepath) print("Saved.") photo_index += 1
This function:
Builds a filename such as
photo_001.jpg.Captures an image using the camera.
Increments the photo index so the next photo uses a new name.
Conditional Preview for Local and Remote Use
preview_started = False if os.getenv("DISPLAY"): try: camera.start_preview(Preview.QT) preview_started = True except Exception as e: preview_started = False print(f"Preview start failed (continue without preview): {e}") else: print("No DISPLAY detected (running headless without preview).")
If a graphical desktop is available, a QT preview window is shown.
If running remotely over SSH without a display, the preview is skipped.
Photo capture works normally in both cases.
Main Loop and Button Logic
try: while True: if button.value(): # Button pressed (HIGH) led.on() # LED on take_photo() # Take photo time.sleep(0.3) # Simple debounce (avoid multiple shots) while button.value(): # Wait until button is released time.sleep(0.01) led.off() # LED off after release time.sleep(0.01)
Inside the main loop:
Pressing the button turns on the LED.
A photo is captured immediately.
The program waits until the button is released to avoid repeated shots.
The LED turns off after the button is released.
Clean Exit and Resource Cleanup
except KeyboardInterrupt: print("\nExiting...") finally: # Turn off LED try: led.off() except Exception: ...
When
Ctrl+Cis pressed:The LED is turned off.
The camera pipeline is stopped.
The preview is stopped only if it was started.
Camera resources are released cleanly.
This ensures the program exits without errors, whether running locally with a screen or remotely in headless mode.
Troubleshooting
Photo Not Captured:
Cause: The button is not wired correctly or the camera is not initialized.
Solution:
Ensure the button is connected to GPIO pin 4 and ground.
Verify that the camera is properly connected and enabled via
raspi-config.
LED Does Not Blink:
Cause: Incorrect LED wiring or GPIO configuration.
Solution:
Ensure the LED is connected to GPIO pin 17 with an appropriate resistor.
Test the LED separately to confirm it functions correctly.
Script Crashes with Camera Error:
Cause: The camera module is not detected or in use by another process.
Solution:
Ensure the camera is properly connected and restart the Raspberry Pi.
Check for conflicting processes using
sudo lsof /dev/video*.
Extendable Ideas
Multiple Photos: Allow multiple photos to be captured in a session, each with a unique filename:
counter = 0 camera.capture_file(f'{user_home}/photo_{counter}.jpg') counter += 1
Video Recording: Extend the functionality to record videos when the button is pressed:
camera.start_recording(f'{user_home}/my_video.h264') time.sleep(10) camera.stop_recording()
LED Status Indicator: Use the LED to indicate the camera’s readiness or status:
Solid light: Ready.
Blinking: Capturing a photo.
Photo Gallery Management: Organize captured photos into folders based on date or event.
Timelapse Photography: Capture photos at regular intervals to create a timelapse:
for i in range(10): camera.capture_file(f'{user_home}/timelapse_{i}.jpg') time.sleep(5)
Conclusion
This project introduces a basic camera setup with a button-triggered shutter mechanism. It combines GPIO control with the Picamera2 library to demonstrate how to create interactive projects. Experiment further to expand its functionality and create more engaging applications.