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

Security Camera System with PIR Motion Detection – Raspberry Pi Pico MicroPython Guide | MakeMindz
🔐 Intermediate · Raspberry Pi Pico · MicroPython

Security Camera
PIR Motion Detection

Build an intelligent security system using Raspberry Pi Pico and PIR sensor. Real-time motion detection, LED + buzzer alerts, OLED event logging, and cooldown debouncing — fully simulated in Wokwi.

· By MakeMindz

Raspberry Pi Pico PIR HC-SR501 SSD1306 OLED MicroPython Wokwi Simulation

🔬 No hardware needed — simulate the full security system free in your browser

▶ Open Free Wokwi Simulation
// 01

What You'll Build

A real-time PIR motion detection security system that logs events, triggers multi-sensory alerts, and displays live status on an OLED screen.

🚨

Real-Time Detection

PIR sensor polled every 100ms — instant response to any motion

💡

LED Alert

Red LED lights up immediately when motion is detected

🔊

Buzzer Alarm

3-beep alarm pattern triggers on every new detection event

📟

OLED Status

SSD1306 shows STANDBY / MOTION! and running event count

📋

Event Logging

Timestamps and event numbers printed to Serial Monitor

⏱️

Cooldown Period

3-second debounce prevents duplicate detections from one trigger

🔁

Auto Reset

System returns to STANDBY automatically when motion clears

🔬

Wokwi Ready

Click the PIR component in Wokwi to simulate motion triggers

// 02

How It Works

The system runs a continuous detection loop with smart debouncing

System Flow

Boot → Standby → Detection → Alert → Reset

On power-up, a boot screen shows on the OLED for 2 seconds, then the system enters STANDBY mode. When the PIR sensor fires (value = 1) and the cooldown period has passed, the system triggers the LED + buzzer alarm, increments the event counter, logs a timestamp to Serial, updates the OLED to "MOTION!", then waits. When the PIR goes LOW, the alarm clears and STANDBY resumes.

⚡ Cooldown Period Explained The 3-second cooldown_period prevents one physical motion from registering as dozens of events. After each detection, time.time() is compared against last_detection_time — only if the difference exceeds 3 seconds is a new event logged.
🔬 Testing in Wokwi In the Wokwi simulator, click directly on the PIR sensor component and toggle its output HIGH to simulate motion. Watch the OLED switch from "STANDBY" to "MOTION!" and the red LED illuminate simultaneously.
// 03

Components Required

All available as virtual parts in Wokwi — or build on real hardware!

01

Raspberry Pi Pico

Main microcontroller running MicroPython — polls PIR, controls LED, buzzer, and OLED

02

PIR Motion Sensor (HC-SR501)

Passive infrared sensor — detects changes in infrared radiation caused by moving warm bodies. Output goes HIGH on detection.

03

SSD1306 128×64 I2C OLED Display

Shows boot screen, live status (STANDBY / MOTION!), and running event count

04

Red LED (Motion Indicator)

Visual alarm — turns on when motion is detected, off when system resets to standby

05

Active Buzzer

Produces 3 short beeps on each motion detection event as an audio alarm

06

220Ω Resistor

Current-limiting resistor in series with the red LED — protects both the LED and GP14

07

Breadboard + Jumper Wires

For physical builds — all connections are pre-wired in the Wokwi diagram.json

// 04

Pin Wiring Reference

All GPIO connections for the Raspberry Pi Pico

ComponentPico PinNotes
OLED SDAGP0I2C data line
OLED SCLGP1I2C clock line
OLED VCC3V33.3V power
OLED GNDGND.8Ground
PIR VCCVBUS⚠️ 5V — HC-SR501 needs 5V
PIR GNDGND.3Ground
PIR OUTGP15Digital input, PULL_DOWN
LED Anode (+)GP14Via 220Ω resistor
LED Cathode (−)GND.6Ground
🔊 Buzzer (+)GP13(−) to GND.5
⚠️ PIR Needs 5V — Use VBUS, Not 3V3 The HC-SR501 PIR sensor requires 5V DC to operate correctly. Connect its VCC to the Pico's VBUS pin (USB 5V), not the 3V3 pin. The sensor's OUT signal is still 3.3V-compatible with the Pico GPIO.
// 05

diagram.json

Paste this into Wokwi to auto-load the full circuit — no manual wiring needed

1

Open Wokwi

Go to wokwi.com → New Project → Raspberry Pi Pico. Log in with Google to save and share your work.

2

Paste diagram.json

Click the diagram.json tab → Ctrl+A to select all → Delete → paste the JSON below → Ctrl+S to save.

3

Paste main.py

Switch to the main.py tab → paste the full MicroPython code → click ▶ Play.

4

Trigger Motion

Click the PIR sensor component in Wokwi and set its output HIGH to simulate motion — watch the OLED and LED react instantly.

diagram.json — paste into Wokwi
{
  "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": {}
}
⚠️ Important Copy the full JSON including the opening and closing { } braces — Wokwi will throw a parse error if either is missing.
// 06

MicroPython Code

Full main.py — paste into Wokwi's editor tab

① Imports, Pin Setup & State Variables

main.py — setup
from machine import Pin, I2C
import time, framebuf

# I2C + SSD1306 OLED (128×64)
i2c  = I2C(0, scl=Pin(1), sda=Pin(0), freq=400000)
oled = SSD1306_I2C(128, 64, i2c)

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

# Red LED + Buzzer
led    = Pin(14, Pin.OUT)
buzzer = Pin(13, Pin.OUT)

# Security state
motion_detected    = False
detection_count    = 0
last_detection_time = 0
cooldown_period    = 3  # seconds between events

② Display Functions

main.py — OLED screens
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)

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()

③ Alarm Functions

main.py — alarm control
def trigger_alarm():
    led.on()
    # 3-beep alarm pattern
    for _ in range(3):
        buzzer.on();  time.sleep(0.1)
        buzzer.off(); time.sleep(0.1)

def clear_alarm():
    led.off()
    buzzer.off()

④ Main Detection Loop

main.py — main loop
show_boot_screen()
update_display("STANDBY", detection_count)
print("System Ready - Monitoring for motion...")

try:
    while True:
        current_time = time.time()

        if pir_sensor.value() == 1:   # Motion detected
            if not motion_detected:
                # Only log if cooldown has passed
                if (current_time - last_detection_time) > cooldown_period:
                    motion_detected     = True
                    detection_count    += 1
                    last_detection_time = current_time

                    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)   # Poll 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()
// 07

What You Will Learn

Core embedded programming concepts covered by this project

A — PIR Sensor Interfacing

Passive Infrared Motion Detection

The HC-SR501 PIR sensor outputs a digital HIGH signal when it detects a change in infrared radiation — caused by a warm body (human or animal) moving across its field of view. In MicroPython, Pin(15, Pin.IN, Pin.PULL_DOWN) reads this signal. PULL_DOWN ensures the pin reads 0 when no signal is present.

B — Cooldown / Debouncing

Preventing Duplicate Event Logging

Physical motion often keeps the PIR HIGH for several seconds. Without a cooldown, the loop would log hundreds of "events" for a single person walking past. The time.time() comparison — checking that at least 3 seconds have passed since last_detection_time — acts as a software debounce filter.

C — State Machine Design

STANDBY ↔ MOTION States

The motion_detected boolean flag prevents repeated alarm triggers for the same detection event. The system only fires trigger_alarm() on the rising edge (first detection), and only calls clear_alarm() on the falling edge (motion gone). This two-state machine is a fundamental embedded programming pattern.

D — I2C OLED Display

Real-Time Visual Feedback

The SSD1306 OLED uses a custom driver that communicates over I2C at 400 kHz. update_display() clears the screen, redraws the border, header, status text, and event counter on every state change — giving real-time visual feedback without flickering.

E — Event Logging

Timestamps with time.localtime()

Each detection event is logged to the Serial Monitor with its event number and timestamp using time.localtime(), which returns a tuple of (year, month, day, hour, minute, second…). In a real system, this data could be stored to flash memory or sent to a cloud server via Wi-Fi.

// 08

Real-World Applications

Where this security system can be deployed

🏠

Home Security

Monitor entry points and hallways — alert when intruders approach doors or windows

🚗

Garage Monitor

Detect when someone enters your garage or parking area after hours

🏢

Office Security

Track after-hours movement in restricted or sensitive areas

🦌

Wildlife Camera

Trigger photo captures or logging when animals enter a monitored zone

👶

Baby Monitor

Alert parents when a baby wakes up and starts moving in the crib

💡

Auto Lighting

Turn lights on automatically when someone enters a room — energy saving

🔢

Visitor Counter

Log how many times people pass a sensor — useful for footfall analytics

🌿

Plant Protector

Alert when animals or people approach protected garden zones

🔐 Raspberry Pi Pico · PIR Motion Detection · MicroPython
Security Camera System · MakeMindz

Comments

try for free