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.
🔬 No hardware needed — simulate the full security system free in your browser
▶ Open Free Wokwi SimulationWhat 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
How It Works
The system runs a continuous detection loop with smart debouncing
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 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.
Components Required
All available as virtual parts in Wokwi — or build on real hardware!
Raspberry Pi Pico
Main microcontroller running MicroPython — polls PIR, controls LED, buzzer, and OLED
PIR Motion Sensor (HC-SR501)
Passive infrared sensor — detects changes in infrared radiation caused by moving warm bodies. Output goes HIGH on detection.
SSD1306 128×64 I2C OLED Display
Shows boot screen, live status (STANDBY / MOTION!), and running event count
Red LED (Motion Indicator)
Visual alarm — turns on when motion is detected, off when system resets to standby
Active Buzzer
Produces 3 short beeps on each motion detection event as an audio alarm
220Ω Resistor
Current-limiting resistor in series with the red LED — protects both the LED and GP14
Breadboard + Jumper Wires
For physical builds — all connections are pre-wired in the Wokwi diagram.json
Pin Wiring Reference
All GPIO connections for the Raspberry Pi Pico
| Component | Pico Pin | Notes |
|---|---|---|
| OLED SDA | GP0 | I2C data line |
| OLED SCL | GP1 | I2C clock line |
| OLED VCC | 3V3 | 3.3V power |
| OLED GND | GND.8 | Ground |
| PIR VCC | VBUS | ⚠️ 5V — HC-SR501 needs 5V |
| PIR GND | GND.3 | Ground |
| PIR OUT | GP15 | Digital input, PULL_DOWN |
| LED Anode (+) | GP14 | Via 220Ω resistor |
| LED Cathode (−) | GND.6 | Ground |
| 🔊 Buzzer (+) | GP13 | (−) to GND.5 |
VBUS pin (USB 5V), not the 3V3 pin. The sensor's OUT signal is still 3.3V-compatible with the Pico GPIO.
diagram.json
Paste this into Wokwi to auto-load the full circuit — no manual wiring needed
Open Wokwi
Go to wokwi.com → New Project → Raspberry Pi Pico. Log in with Google to save and share your work.
Paste diagram.json
Click the diagram.json tab → Ctrl+A to select all → Delete → paste the JSON below → Ctrl+S to save.
Paste main.py
Switch to the main.py tab → paste the full MicroPython code → click ▶ Play.
Trigger Motion
Click the PIR sensor component in Wokwi and set its output HIGH to simulate motion — watch the OLED and LED react instantly.
{
"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": {}
}
MicroPython Code
Full main.py — paste into Wokwi's editor tab
① Imports, Pin Setup & State Variables
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
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
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
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()
What You Will Learn
Core embedded programming concepts covered by this project
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.
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.
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.
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.
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.
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
Comments
Post a Comment