Raspberry Pi GPIO Programming — Motion Sensor Camera with Python and Cloud Upload
Raspberry Pi is a single-board computer that runs Linux and provides general-purpose input/output (GPIO) pins for connecting sensors, motors, and other electronics, making it the most versatile platform for IoT prototyping.
Why Raspberry Pi Matters
Unlike microcontrollers (Arduino, ESP32) that run one program at a time, Raspberry Pi runs a full operating system. You can install Python, Node.js, OpenCV, TensorFlow, and databases on the same board. It’s the go-to IoT gateway and edge computing device. The Raspberry Pi 5 has a quad-core 2.4GHz processor, up to 8GB RAM, and hardware video encoding — capable of running real-time object detection at 30fps. Over 45 million Raspberry Pis have been sold since 2012.
Plain-Language Explanation
If an Arduino is a pocket calculator — specialized, efficient, single-purpose — then a Raspberry Pi is a full desktop computer the size of a credit card. It boots Linux from an SD card, connects to HDMI displays, USB keyboards, and WiFi. What makes it special for IoT are the 40 GPIO pins that let you read sensors and control electronics directly from Python.
Think of the Raspberry Pi as the “brain” of an IoT system. It can read a motion sensor, capture a photo with a camera module, run face detection using OpenCV, upload the image to cloud storage, AND send you a notification — all in one Python script.
graph TD
PIR[PIR Motion Sensor] -->|GPIO 17| RPI[Raspberry Pi]
RPI -->|GPIO 18| LED[Alert LED]
RPI -->|CSI Port| Camera[Camera Module]
RPI -->|USB| Key[USB Keyboard]
RPI -->|HDMI| Display[Monitor]
RPI -->|WiFi| Cloud[Cloud Storage
S3 / Google Drive]
Cloud --> Notification[Push Notification]
style RPI fill:#bc1142,color:#fff
style Camera fill:#3498db,color:#fff
style Cloud fill:#27ae60,color:#fff
GPIO Programming with RPi.GPIO
# gpio_basics.py — basic GPIO input/output
import RPi.GPIO as GPIO
import time
GPIO.setmode(GPIO.BCM) # Use Broadcom pin numbering
# Setup pins
LED_PIN = 18
GPIO.setup(LED_PIN, GPIO.OUT)
# Blink LED
for _ in range(5):
GPIO.output(LED_PIN, GPIO.HIGH)
print("LED ON")
time.sleep(0.5)
GPIO.output(LED_PIN, GPIO.LOW)
print("LED OFF")
time.sleep(0.5)
GPIO.cleanup()Expected output:
LED ON
LED OFF
LED ON
LED OFF
... (5 blinks)Motion Sensor + Camera Project
Components: Raspberry Pi (any model), PIR motion sensor (HC-SR501), Raspberry Pi Camera Module, LED, 220Ω resistor, jumper wires.
Wiring:
- PIR VCC → 5V pin
- PIR GND → GND
- PIR OUT → GPIO 17
- LED anode → GPIO 18 (through 220Ω resistor)
- LED cathode → GND
- Camera → CSI ribbon cable
# motion_camera.py — detect motion, capture photo, upload
import RPi.GPIO as GPIO
import time
import subprocess
import requests
from datetime import datetime
# Configuration
PIR_PIN = 17
LED_PIN = 18
UPLOAD_URL = "https://api.example.com/upload"
API_KEY = "your-api-key"
GPIO.setmode(GPIO.BCM)
GPIO.setup(PIR_PIN, GPIO.IN)
GPIO.setup(LED_PIN, GPIO.OUT)
def capture_photo():
"""Capture image with Raspberry Pi camera"""
filename = f"motion_{datetime.now().strftime('%Y%m%d_%H%M%S')}.jpg"
subprocess.run([
"libcamera-still", "-o", filename,
"--width", 1280, "--height", 720,
"--nopreview"
])
print(f"Photo captured: {filename}")
return filename
def upload_to_cloud(filename: str):
"""Upload image to cloud storage"""
try:
with open(filename, 'rb') as f:
response = requests.post(
UPLOAD_URL,
files={'file': f},
headers={'Authorization': f'Bearer {API_KEY}'}
)
if response.status_code == 200:
print(f"Uploaded: {response.json().get('url', 'unknown')}")
else:
print(f"Upload failed: {response.status_code}")
except Exception as e:
print(f"Upload error: {e}")
def alert():
"""Flash LED to indicate motion detected"""
for _ in range(3):
GPIO.output(LED_PIN, GPIO.HIGH)
time.sleep(0.1)
GPIO.output(LED_PIN, GPIO.LOW)
time.sleep(0.1)
print("Motion detection started. Ctrl+C to exit.")
print("Waiting for motion...")
try:
while True:
if GPIO.input(PIR_PIN) == GPIO.HIGH:
print(f"Motion detected at {datetime.now()}")
alert()
filename = capture_photo()
upload_to_cloud(filename)
print("Waiting 10 seconds before next detection...")
time.sleep(10) # Cooldown to prevent rapid triggers
time.sleep(0.1)
except KeyboardInterrupt:
print("\nStopped by user")
finally:
GPIO.cleanup()Expected output:
Motion detection started. Ctrl+C to exit.
Waiting for motion...
Motion detected at 2026-06-15 14:32:05
Photo captured: motion_20260615_143205.jpg
Uploaded: https://storage.example.com/motion_20260615_143205.jpg
Waiting 10 seconds before next detection...Reading Multiple Sensors
# multi_sensor.py — read temperature, humidity, and light
import RPi.GPIO as GPIO
import time
import Adafruit_DHT # DHT sensor library
DHT_PIN = 4
def read_dht22():
humidity, temperature = Adafruit_DHT.read_retry(Adafruit_DHT.DHT22, DHT_PIN)
return temperature, humidity
def read_ldr(adc_channel: int = 0):
"""Read light level from LDR through MCP3008 ADC"""
# Using MCP3008 SPI ADC
import spidev
spi = spidev.SpiDev()
spi.open(0, 0)
spi.max_speed_hz = 1350000
r = spi.xfer2([1, (8 + adc_channel) << 4, 0])
value = ((r[1] & 3) << 8) + r[2]
spi.close()
return value
while True:
temp, hum = read_dht22()
light = read_ldr()
print(f"Temp: {temp:.1f}°C, Humidity: {hum:.1f}%, Light: {light}")
time.sleep(5)Expected output:
Temp: 24.7°C, Humidity: 55.2%, Light: 512
Temp: 24.8°C, Humidity: 55.0%, Light: 480
Temp: 24.9°C, Humidity: 54.8%, Light: 523GPIO Pins and Their Functions
| Pin | Function | Best For |
|---|---|---|
| GPIO 2/3 | I2C (SDA/SCL) | Sensors (BME280, MPU6050) |
| GPIO 9/10/11 | SPI (MISO/MOSI/SCLK) | High-speed ADC, displays |
| GPIO 14/15 | UART (TX/RX) | Serial GPS, console |
| GPIO 17-27 | General digital I/O | Buttons, LEDs, relays |
| GPIO 12/13/18/19 | PWM | Servos, dimmable LEDs |
Common Mistakes
Pin numbering confusion: BCM vs BOARD numbering.
GPIO.setmode(GPIO.BCM)uses GPIO numbers (GPIO 17 = physical pin 11).GPIO.BOARDuses physical pin numbers. Mixing them breaks everything.No pull-up/pull-down resistors: Unconnected input pins float randomly. Use
GPIO.setup(pin, GPIO.IN, pull_up_down=GPIO.PUD_UP)for internal pull-up.GPIO cleanup not called: If a script crashes without
GPIO.cleanup(), pins retain their last state. Always usetry/finallyto clean up.Power supply insufficient: Raspberry Pi 5 needs 5V/3A USB-C. Using a phone charger delivering 5V/1A causes voltage drops that corrupt the SD card.
Running as root: GPIO requires root access. Either run with
sudo python script.pyor add the user to thegpiogroup.
Practice Questions
What is the difference between Raspberry Pi and Arduino? Raspberry Pi is a full Linux computer with GPIO pins — suitable for complex processing, cameras, and networking. Arduino is a microcontroller — simpler, more power-efficient, better for real-time control.
How do you handle multiple timing tasks without blocking? Use
GPIO.add_event_detect()for edge-triggered callbacks instead of polling in a loop. Or usetime.monotonic()for non-blocking timing.What is the purpose of GPIO.cleanup()? It resets all configured GPIO pins to their default input state, preventing erratic behavior after the script ends.
Why use an external ADC (MCP3008) with Raspberry Pi? Raspberry Pi has no analog input pins. An external ADC converts analog sensor voltages to digital values via SPI.
How do you enable a GPIO interrupt/callback?
GPIO.add_event_detect(pin, GPIO.RISING, callback=my_handler, bouncetime=300)callsmy_handlerwhen the pin transitions from LOW to HIGH.
Mini Project
Build a GPIO-based security system:
import RPi.GPIO as GPIO
import time
from datetime import datetime
MAGNETIC_SWITCH_PIN = 17 # Door sensor
BUZZER_PIN = 18 # Piezo buzzer
ARM_STATUS_PIN = 27 # LED shows armed status
GPIO.setmode(GPIO.BCM)
GPIO.setup(MAGNETIC_SWITCH_PIN, GPIO.IN, pull_up_down=GPIO.PUD_UP)
GPIO.setup(BUZZER_PIN, GPIO.OUT)
GPIO.setup(ARM_STATUS_PIN, GPIO.OUT)
armed = True
GPIO.output(ARM_STATUS_PIN, GPIO.HIGH)
print("System ARMED")
def door_callback(channel):
if GPIO.input(MAGNETIC_SWITCH_PIN) == GPIO.HIGH:
print(f"INTRUSION DETECTED at {datetime.now()}")
for _ in range(10):
GPIO.output(BUZZER_PIN, GPIO.HIGH)
time.sleep(0.1)
GPIO.output(BUZZER_PIN, GPIO.LOW)
time.sleep(0.1)
GPIO.add_event_detect(MAGNETIC_SWITCH_PIN, GPIO.BOTH,
callback=door_callback, bouncetime=300)
try:
while True:
door_status = "OPEN" if GPIO.input(MAGNETIC_SWITCH_PIN) else "CLOSED"
print(f"Door: {door_status}", end='\r')
time.sleep(0.5)
except KeyboardInterrupt:
GPIO.cleanup()Cross-References
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro