An intelligent security camera system using Raspberry Pi Pico and PIR motion sensor


Build an intelligent security camera system using Raspberry Pi Pico and PIR motion sensor. Detect movement in real-time, trigger LED alerts, and log security events with timestamps. Perfect for learning motion detection without buying physical components - test everything in Wokwi simulator first!


Key Features

Real-time motion detection using PIR sensor

Visual alerts with LED indicators (red for motion detected)

OLED display showing camera status and event count

Event logging with timestamps and motion duration

Buzzer alarm for security alerts

Adjustable sensitivity and detection cooldown period

Simulation-ready - no hardware needed to start

Components Required

Diagram.json:

{
  "version": 1,
  "author": "Security Camera Project",
  "editor": "wokwi",
  "parts": [
    {
      "type": "wokwi-pi-pico",
      "id": "pico",
      "top": 0,
      "left": 0,
      "attrs": {}
    },
    {
      "type": "wokwi-pir-motion-sensor",
      "id": "pir1",
      "top": -105.6,
      "left": -134.4,
      "attrs": {}
    },
    {
      "type": "wokwi-ssd1306",
      "id": "oled1",
      "top": -137.33,
      "left": 122.67,
      "attrs": {
        "i2cAddress": "0x3C"
      }
    },
    {
      "type": "wokwi-led",
      "id": "led1",
      "top": 120,
      "left": 180,
      "attrs": {
        "color": "red"
      }
    },
    {
      "type": "wokwi-resistor",
      "id": "r1",
      "top": 110,
      "left": 150,
      "attrs": {
        "value": "220"
      }
    },
    {
      "type": "wokwi-buzzer",
      "id": "buzzer1",
      "top": 90,
      "left": -120,
      "attrs": {}
    }
  ],
  "connections": [
    ["pico:GP0", "oled1:SDA", "green", ["v0"]],
    ["pico:GP1", "oled1:SCL", "blue", ["v0"]],
    ["pico:3V3(OUT)", "oled1:VCC", "red", ["v0"]],
    ["pico:GND.8", "oled1:GND", "black", ["v0"]],
    ["pico:VBUS", "pir1:VCC", "red", ["v0"]],
    ["pico:GND.3", "pir1:GND", "black", ["v0"]],
    ["pico:GP15", "pir1:OUT", "orange", ["v0"]],
    ["pico:GP14", "r1:1", "yellow", ["v0"]],
    ["r1:2", "led1:A", "yellow", ["v0"]],
    ["led1:C", "pico:GND.6", "black", ["v0"]],
    ["pico:GP13", "buzzer1:1", "magenta", ["v0"]],
    ["buzzer1:2", "pico:GND.5", "black", ["v0"]]
  ],
  "dependencies": {}
}

Raspberry Pi Pico (simulated in Wokwi)

PIR Motion Sensor (HC-SR501)

0.96" OLED Display (SSD1306, I2C)

Red LED (motion indicator)

Active Buzzer (alarm sound)

220Ω Resistor (for LED)

Breadboard and jumper wires

MicroPython firmware on Pico

Circuit ConnectionsPIR Motion Sensor:


VCC → VBUS (5V)

GND → GND

OUT → GPIO15 (digital input)

OLED Display (I2C):

SDA → GPIO0

SCL → GPIO1

VCC → 3.3V

GND → GND

LED Indicator:

Anode (+) → GPIO14 → 220Ω resistor → 3.3V

Cathode (-) → GND

Buzzer:

Positive → GPIO13

Negative → GND

Code:

from machine import Pin, I2C
import time
import framebuf

# SSD1306 OLED Driver (128x64)
class SSD1306_I2C:
    def __init__(self, width, height, i2c, addr=0x3C):
        self.i2c = i2c
        self.addr = addr
        self.width = width
        self.height = height
        self.pages = height // 8
        self.buffer = bytearray(self.pages * width)
        self.framebuf = framebuf.FrameBuffer(self.buffer, width, height, framebuf.MONO_VLSB)
        self.init_display()
   
    def init_display(self):
        for cmd in (
            0xAE, 0xD5, 0x80, 0xA8, 0x3F, 0xD3, 0x00, 0x40, 0x8D, 0x14,
            0x20, 0x00, 0xA1, 0xC8, 0xDA, 0x12, 0x81, 0xCF, 0xD9, 0xF1,
            0xDB, 0x40, 0xA4, 0xA6, 0xAF
        ):
            self.write_cmd(cmd)
        self.fill(0)
        self.show()
   
    def write_cmd(self, cmd):
        self.i2c.writeto(self.addr, bytearray([0x00, cmd]))
   
    def write_data(self, buf):
        self.i2c.writeto(self.addr, b'\x40' + buf)
   
    def show(self):
        for page in range(self.pages):
            self.write_cmd(0xB0 + page)
            self.write_cmd(0x00)
            self.write_cmd(0x10)
            self.write_data(self.buffer[page * self.width:(page + 1) * self.width])
   
    def fill(self, color):
        self.framebuf.fill(color)
   
    def text(self, string, x, y, color=1):
        self.framebuf.text(string, x, y, color)
   
    def rect(self, x, y, w, h, color):
        self.framebuf.rect(x, y, w, h, color)
   
    def fill_rect(self, x, y, w, h, color):
        self.framebuf.fill_rect(x, y, w, h, color)

# Initialize I2C for OLED
i2c = I2C(0, scl=Pin(1), sda=Pin(0), freq=400000)
oled = SSD1306_I2C(128, 64, i2c)

# Initialize PIR Motion Sensor
pir_sensor = Pin(15, Pin.IN, Pin.PULL_DOWN)

# Initialize LED and Buzzer
led = Pin(14, Pin.OUT)
buzzer = Pin(13, Pin.OUT)

# Security Camera State
motion_detected = False
detection_count = 0
last_detection_time = 0
cooldown_period = 3  # seconds between detections

# Boot Screen
def show_boot_screen():
    oled.fill(0)
    oled.rect(0, 0, 128, 64, 1)
    oled.text("SECURITY CAM", 15, 10)
    oled.text("============", 15, 20)
    oled.text("Initializing", 20, 35)
    oled.text("Sensors...", 25, 45)
    oled.show()
    time.sleep(2)

# Update Display
def update_display(status, count):
    oled.fill(0)
    oled.rect(0, 0, 128, 64, 1)
    oled.text("SECURITY CAM", 10, 5)
    oled.fill_rect(0, 16, 128, 1, 1)
   
    oled.text("Status:", 5, 22)
    oled.text(status, 5, 32)
   
    oled.text("Events:", 5, 45)
    oled.text(str(count), 60, 45)
    oled.show()

# Trigger Alarm
def trigger_alarm():
    led.on()
    # Buzzer beep pattern
    for _ in range(3):
        buzzer.on()
        time.sleep(0.1)
        buzzer.off()
        time.sleep(0.1)

# Clear Alarm
def clear_alarm():
    led.off()
    buzzer.off()

# Main Program
print("Security Camera System Starting...")
show_boot_screen()

update_display("STANDBY", detection_count)
print("System Ready - Monitoring for motion...")

try:
    while True:
        current_time = time.time()
       
        # Read PIR sensor
        if pir_sensor.value() == 1:
            # Motion detected
            if not motion_detected:
                # Check cooldown period
                if (current_time - last_detection_time) > cooldown_period:
                    motion_detected = True
                    detection_count += 1
                    last_detection_time = current_time
                   
                    # Trigger alarm
                    print(f"⚠️  MOTION DETECTED! Event #{detection_count}")
                    print(f"Timestamp: {time.localtime()}")
                    trigger_alarm()
                    update_display("MOTION!", detection_count)
                   
        else:
            # No motion
            if motion_detected:
                motion_detected = False
                clear_alarm()
                update_display("STANDBY", detection_count)
                print("Clear - System on standby")
       
        time.sleep(0.1)  # Check every 100ms

except KeyboardInterrupt:
    print("\nShutting down security system...")
    clear_alarm()
    oled.fill(0)
    oled.text("SYSTEM", 35, 20)
    oled.text("OFFLINE", 30, 35)
    oled.show()

Applications

Home Security: Monitor entry points and hallways for intruders

Garage Monitoring: Detect when someone enters your garage

Office Security: Track after-hours movement in restricted areas

Wildlife Camera: Trigger camera captures when animals approach

Baby Monitor: Alert when baby wakes up and moves

Energy Saving: Turn on lights only when motion is detected

Visitor Counter: Log how many times people pass by a location

What You'll Learn

PIR sensor interfacing and motion detection algorithms

I2C communication with OLED displays

Interrupt-driven programming for responsive detection

Event logging and timestamp management

Debouncing techniques for reliable sensor readings

Multi-component system integration

State machine programming for security systems

Visual and audio alert triggering

Code StructureYour MicroPython code will include:

Import libraries (machine, time, SSD1306 OLED driver)

Initialize components (PIR on GPIO15, OLED, LED, Buzzer)

Display boot screen on OLED

Main detection loop:


Read PIR sensor state

Trigger LED and buzzer on motion

Update OLED with status and event count

Log detection time and duration

Implement cooldown period (prevent false triggers)

Comments