Build a professional motion detection security system using Raspberry Pi Pico and PIR (Passive Infrared) sensor. Detect human movement, trigger alarms, activate lights, and display alerts on an LCD screen. Perfect for learning security system design and motion sensing without buying physical components - test everything in Wokwi simulator first!
Key Features
- Real-time motion detection using PIR sensor (detects humans up to 7 meters)
- Arm/Disarm functionality with push button control
- Visual alerts: Red LED blinks when motion detected
- Audible alarm: Buzzer sounds with warning pattern
- System status display on 16x2 LCD screen
- Armed indicator: Green LED shows system is active
- Detection counter: Tracks total number of intrusions
- Cooldown period: Prevents false alarm spam (3-second reset)
- Automatic alarm duration: 5-second alarm cycle
- Serial monitoring: Real-time status updates
- Debounced button: Professional button handling
Components Required
- Raspberry Pi Pico (simulated in Wokwi)
- HC-SR501 PIR Motion Sensor (Passive Infrared)
- 16x2 LCD Display (I2C connection)
- Red LED (Alert/Motion detected indicator)
- Green LED (System armed indicator)
- Buzzer (Alarm/Alert sound)
- Push Button (Arm/Disarm control)
- 2x 220Ω Resistors (for LEDs)
- Breadboard and jumper wires
- MicroPython firmware on Pico
Circuit Connections
PIR Motion Sensor (HC-SR501):
- VCC → 5V (VBUS Pin 40)
- GND → GND
- OUT → GPIO15 (Digital output - HIGH when motion detected)
LCD Display (I2C):
- SDA → GPIO0 (I2C0 SDA)
- SCL → GPIO1 (I2C0 SCL)
- VCC → 5V (VBUS)
- GND → GND
Red LED (Alert Indicator):
- Anode (+) → GPIO16 (through 220Ω resistor)
- Cathode (-) → GND
Green LED (Armed Indicator):
- Anode (+) → GPIO17 (through 220Ω resistor)
- Cathode (-) → GND
Buzzer:
- Positive → GPIO14
- Negative → GND
Push Button (ARM/DISARM):
- One terminal → GPIO18
- Other terminal → 3.3V
- Internal pull-down resistor enabled in code
Code:
diagram.json:
{
"version": 1,
"author": "Motion Detection Security System",
"editor": "wokwi",
"parts": [
{
"type": "wokwi-pi-pico",
"id": "pico",
"top": 0,
"left": 0,
"attrs": {}
},
{
"type": "wokwi-pir-motion-sensor",
"id": "pir1",
"top": -60,
"left": 300,
"attrs": {}
},
{
"type": "wokwi-lcd1602",
"id": "lcd1",
"top": 150,
"left": 300,
"attrs": {
"pins": "i2c"
}
},
{
"type": "wokwi-buzzer",
"id": "buzzer1",
"top": -100,
"left": 450,
"attrs": {}
},
{
"type": "wokwi-led",
"id": "led1",
"top": -100,
"left": -150,
"attrs": {
"color": "red",
"label": "ALERT"
}
},
{
"type": "wokwi-led",
"id": "led2",
"top": -50,
"left": -150,
"attrs": {
"color": "green",
"label": "ARMED"
}
},
{
"type": "wokwi-pushbutton",
"id": "btn1",
"top": 50,
"left": -150,
"attrs": {
"color": "blue",
"label": "ARM/DISARM"
}
},
{
"type": "wokwi-resistor",
"id": "r1",
"top": -90,
"left": -220,
"attrs": {
"value": "220"
}
},
{
"type": "wokwi-resistor",
"id": "r2",
"top": -40,
"left": -220,
"attrs": {
"value": "220"
}
}
],
"connections": [
[
"pico:GP0",
"lcd1:SDA",
"green",
[
"v0"
]
],
[
"pico:GP1",
"lcd1:SCL",
"blue",
[
"v0"
]
],
[
"lcd1:VCC",
"pico:VBUS",
"red",
[
"v0"
]
],
[
"lcd1:GND",
"pico:GND.8",
"black",
[
"v0"
]
],
[
"pir1:VCC",
"pico:VBUS",
"red",
[
"v0"
]
],
[
"pir1:GND",
"pico:GND.3",
"black",
[
"v0"
]
],
[
"pir1:OUT",
"pico:GP15",
"orange",
[
"v0"
]
],
[
"buzzer1:1",
"pico:GP14",
"purple",
[
"v0"
]
],
[
"buzzer1:2",
"pico:GND.3",
"black",
[
"v0"
]
],
[
"r1:1",
"pico:GP16",
"red",
[
"v0"
]
],
[
"r1:2",
"led1:A",
"",
[
"v0"
]
],
[
"led1:C",
"pico:GND.1",
"black",
[
"v0"
]
],
[
"r2:1",
"pico:GP17",
"green",
[
"v0"
]
],
[
"r2:2",
"led2:A",
"",
[
"v0"
]
],
[
"led2:C",
"pico:GND.1",
"black",
[
"v0"
]
],
[
"btn1:1.l",
"pico:GP18",
"blue",
[
"v0"
]
],
[
"btn1:1.r",
"pico:3V3",
"red",
[
"v0"
]
]
],
"dependencies": {}
}
Built-in LED (Status):
- Automatically connected to GPIO25 (blinks when armed)
Code:
Main.py:
"""
Motion Detection Security System
Raspberry Pi Pico with PIR Sensor
Wokwi Simulator Compatible
Components:
- Raspberry Pi Pico
- PIR Motion Sensor (HC-SR501)
- LCD Display (I2C 16x2)
- Buzzer (Alarm)
- Red LED (Alert indicator)
- Green LED (System armed indicator)
- Push Button (Arm/Disarm system)
"""
from machine import Pin, I2C
import time
# ============ PIN CONFIGURATION ============
PIR_PIN = 15 # PIR sensor output
BUZZER_PIN = 14 # Buzzer/Alarm
LED_ALERT = 16 # Red LED - Motion detected
LED_ARMED = 17 # Green LED - System armed
BUTTON_PIN = 18 # Push button to arm/disarm
LED_STATUS = 25 # Built-in LED - Status indicator
# Alarm settings
ALARM_DURATION = 5 # Alarm sounds for 5 seconds
COOLDOWN_TIME = 3 # 3 seconds cooldown between detections
# ============ HARDWARE INITIALIZATION ============
# Initialize PIR sensor
pir = Pin(PIR_PIN, Pin.IN)
# Initialize outputs
buzzer = Pin(BUZZER_PIN, Pin.OUT)
led_alert = Pin(LED_ALERT, Pin.OUT)
led_armed = Pin(LED_ARMED, Pin.OUT)
led_status = Pin(LED_STATUS, Pin.OUT)
# Initialize button with pull-down
button = Pin(BUTTON_PIN, Pin.IN, Pin.PULL_DOWN)
# Initialize I2C for LCD (GP0=SDA, GP1=SCL)
i2c = I2C(0, scl=Pin(1), sda=Pin(0), freq=400000)
# ============ LCD 16x2 I2C CLASS ============
class LCD_I2C:
def __init__(self, i2c, addr=0x27, rows=2, cols=16):
self.i2c = i2c
self.addr = addr
self.rows = rows
self.cols = cols
# LCD Commands
self.LCD_CLEARDISPLAY = 0x01
self.LCD_RETURNHOME = 0x02
self.LCD_ENTRYMODESET = 0x04
self.LCD_DISPLAYCONTROL = 0x08
self.LCD_FUNCTIONSET = 0x20
self.LCD_SETDDRAMADDR = 0x80
# Flags
self.LCD_DISPLAYON = 0x04
self.LCD_CURSOROFF = 0x00
self.LCD_BLINKOFF = 0x00
self.LCD_ENTRYLEFT = 0x02
self.LCD_ENTRYSHIFTDECREMENT = 0x00
self.LCD_4BITMODE = 0x00
self.LCD_2LINE = 0x08
self.LCD_5x8DOTS = 0x00
self.LCD_BACKLIGHT = 0x08
self.LCD_NOBACKLIGHT = 0x00
self.backlight_state = self.LCD_BACKLIGHT
# Initialize display
self.init_display()
def write_byte(self, byte, mode):
"""Write a byte to the LCD"""
high_bits = mode | (byte & 0xF0) | self.backlight_state
low_bits = mode | ((byte << 4) & 0xF0) | self.backlight_state
# Write high nibble
self.i2c.writeto(self.addr, bytearray([high_bits]))
self.toggle_enable(high_bits)
# Write low nibble
self.i2c.writeto(self.addr, bytearray([low_bits]))
self.toggle_enable(low_bits)
def toggle_enable(self, byte):
"""Toggle enable bit"""
time.sleep_us(1)
self.i2c.writeto(self.addr, bytearray([byte | 0x04]))
time.sleep_us(1)
self.i2c.writeto(self.addr, bytearray([byte & ~0x04]))
time.sleep_us(50)
def init_display(self):
"""Initialize LCD display"""
time.sleep_ms(50)
# Put LCD into 4-bit mode
self.write_byte(0x03, 0)
time.sleep_ms(5)
self.write_byte(0x03, 0)
time.sleep_us(150)
self.write_byte(0x03, 0)
self.write_byte(0x02, 0)
# Function set
self.write_byte(self.LCD_FUNCTIONSET | self.LCD_4BITMODE |
self.LCD_2LINE | self.LCD_5x8DOTS, 0)
# Display control
self.write_byte(self.LCD_DISPLAYCONTROL | self.LCD_DISPLAYON |
self.LCD_CURSOROFF | self.LCD_BLINKOFF, 0)
# Clear display
self.clear()
# Entry mode
self.write_byte(self.LCD_ENTRYMODESET | self.LCD_ENTRYLEFT |
self.LCD_ENTRYSHIFTDECREMENT, 0)
def clear(self):
"""Clear the display"""
self.write_byte(self.LCD_CLEARDISPLAY, 0)
time.sleep_ms(2)
def set_cursor(self, row, col):
"""Set cursor position"""
row_offsets = [0x00, 0x40]
self.write_byte(self.LCD_SETDDRAMADDR | (col + row_offsets[row]), 0)
def print(self, text, row=0, col=0):
"""Print text at specified position"""
self.set_cursor(row, col)
for char in str(text):
self.write_byte(ord(char), 1)
def backlight(self, state):
"""Turn backlight on/off"""
if state:
self.backlight_state = self.LCD_BACKLIGHT
else:
self.backlight_state = self.LCD_NOBACKLIGHT
self.i2c.writeto(self.addr, bytearray([self.backlight_state]))
# Initialize LCD
try:
lcd = LCD_I2C(i2c)
lcd_available = True
print("LCD initialized successfully")
except:
lcd_available = False
print("LCD not found - using Serial Monitor only")
# ============ GLOBAL VARIABLES ============
system_armed = False
motion_detected = False
detection_count = 0
last_detection_time = 0
# ============ HELPER FUNCTIONS ============
def startup_sequence():
"""Display startup message and test components"""
print("\n" + "="*50)
print(" MOTION DETECTION SECURITY SYSTEM")
print(" Raspberry Pi Pico + PIR Sensor")
print("="*50)
print("\nInitializing components...")
if lcd_available:
lcd.clear()
lcd.print("Security System", 0, 0)
lcd.print("Initializing...", 1, 0)
# Test LEDs
print("Testing LEDs...")
led_alert.on()
led_armed.on()
time.sleep(0.5)
led_alert.off()
led_armed.off()
# Test buzzer
print("Testing buzzer...")
buzzer.on()
time.sleep(0.2)
buzzer.off()
time.sleep(1)
if lcd_available:
lcd.clear()
lcd.print("Press Button", 0, 0)
lcd.print("to ARM system", 1, 0)
print("\nSystem ready!")
print("Press button to ARM/DISARM")
print("="*50 + "\n")
def toggle_system():
"""Toggle system armed/disarmed state"""
global system_armed, detection_count
system_armed = not system_armed
if system_armed:
led_armed.on()
detection_count = 0
print("\n🔒 SYSTEM ARMED - Monitoring for motion...")
if lcd_available:
lcd.clear()
lcd.print("SYSTEM ARMED", 0, 2)
lcd.print("Monitoring...", 1, 1)
# Beep to confirm
buzzer.on()
time.sleep(0.1)
buzzer.off()
else:
led_armed.off()
led_alert.off()
buzzer.off()
print("\n🔓 SYSTEM DISARMED - Standing by...")
if lcd_available:
lcd.clear()
lcd.print("DISARMED", 0, 3)
lcd.print("Press to ARM", 1, 1)
# Double beep to confirm
for _ in range(2):
buzzer.on()
time.sleep(0.1)
buzzer.off()
time.sleep(0.1)
def check_motion():
"""Check PIR sensor for motion"""
return pir.value() == 1
def trigger_alarm():
"""Trigger alarm when motion is detected"""
global detection_count, last_detection_time
detection_count += 1
current_time = time.time()
print("\n" + "!"*50)
print("⚠️ MOTION DETECTED! ⚠️")
print(f"Detection #{detection_count}")
print(f"Time: {current_time}")
print("!"*50)
if lcd_available:
lcd.clear()
lcd.print("!! ALERT !!", 0, 2)
lcd.print(f"Motion #{detection_count}", 1, 2)
# Activate alarm
led_alert.on()
# Sound alarm with pattern
alarm_start = time.time()
while time.time() - alarm_start < ALARM_DURATION:
buzzer.on()
led_status.on()
time.sleep(0.2)
buzzer.off()
led_status.off()
time.sleep(0.2)
led_alert.off()
last_detection_time = time.time()
print(f"Alarm stopped. Total detections: {detection_count}")
if lcd_available:
lcd.clear()
lcd.print("SYSTEM ARMED", 0, 2)
lcd.print(f"Detects: {detection_count}", 1, 2)
def display_status():
"""Display current system status"""
if lcd_available:
if system_armed:
lcd.clear()
lcd.print("ARMED - Ready", 0, 1)
lcd.print(f"Alerts: {detection_count}", 1, 2)
else:
lcd.clear()
lcd.print("DISARMED", 0, 3)
lcd.print("Press to ARM", 1, 1)
def print_status():
"""Print status to serial monitor"""
status = "ARMED" if system_armed else "DISARMED"
motion = "DETECTED" if check_motion() else "None"
print(f"\rStatus: {status} | Motion: {motion} | Detections: {detection_count}", end="")
# ============ MAIN PROGRAM ============
def main():
global motion_detected, last_detection_time
# Startup
startup_sequence()
# Turn off all outputs initially
led_alert.off()
led_armed.off()
buzzer.off()
led_status.off()
last_button_state = 0
last_button_time = 0
status_update_time = 0
print("System running. Monitoring for button press and motion...\n")
while True:
try:
current_time = time.time()
# Button debouncing and handling
button_state = button.value()
if button_state == 1 and last_button_state == 0:
if current_time - last_button_time > 0.3: # 300ms debounce
toggle_system()
last_button_time = current_time
last_button_state = button_state
# Check for motion only if system is armed
if system_armed:
if check_motion():
# Check cooldown period
if current_time - last_detection_time > COOLDOWN_TIME:
trigger_alarm()
time.sleep(COOLDOWN_TIME) # Wait before next detection
# Blink status LED slowly when armed
if int(current_time * 2) % 2 == 0:
led_status.on()
else:
led_status.off()
else:
led_status.off()
# Update status display every 2 seconds
if current_time - status_update_time > 2:
print_status()
status_update_time = current_time
time.sleep(0.1) # Small delay to reduce CPU usage
except KeyboardInterrupt:
print("\n\nSystem stopped by user")
# Turn off all outputs
led_alert.off()
led_armed.off()
buzzer.off()
led_status.off()
if lcd_available:
lcd.clear()
lcd.print("System Stopped", 0, 0)
break
except Exception as e:
print(f"\nError: {e}")
time.sleep(1)
# Run the program
if __name__ == "__main__":
main()
Applications
- Home Security: Detect intruders entering rooms, hallways, or doorways
- Office Security: Monitor restricted areas after business hours
- Smart Lighting: Turn on lights automatically when people enter
- Elderly Care: Alert caregivers when elderly person moves (fall detection)
- Retail Stores: Customer entry detection and counting
- Warehouse Security: Monitor unauthorized access to storage areas
- Smart Parking: Detect vehicle presence in parking spaces
- Museum Security: Protect valuable exhibits from unauthorized approach
- Automatic Doors: Trigger door opening when people approach
- Energy Saving: Turn off lights/AC when room is empty for extended period
- Pet Detection: Monitor pet activity when away from home
- Wildlife Monitoring: Detect animal movement in nature (camera trigger)
What You'll Learn
- PIR sensor operation and passive infrared detection principles
- Interrupt-driven programming for responsive motion detection
- State machine design (Armed/Disarmed states)
- Button debouncing techniques for reliable input
- I2C communication for LCD displays
- GPIO digital input reading and processing
- GPIO digital output control for LEDs and buzzer
- Alarm pattern generation with timing control
- Event counting and tracking
- User interface design for security systems
- Cooldown timers to prevent false alarms
- System status feedback (visual and audible)
- Using Wokwi simulator for security system prototyping
Code Structure
Your MicroPython code will include:
- Import necessary libraries (machine, time, LCD)
- Initialize PIR sensor on GPIO15 as digital input
- Initialize LCD on I2C pins (GPIO0, GPIO1)
- Initialize outputs: LEDs (GPIO16, 17), Buzzer (GPIO14)
- Initialize button on GPIO18 with pull-down resistor
- Define system states:
- DISARMED: System inactive, no monitoring
- ARMED: System active, monitoring for motion
- Create toggle_system() function:
- Switch between armed/disarmed
- Update LED indicators
- Provide audio feedback
- Create check_motion() function:
- Read PIR sensor output
- Return TRUE if motion detected
- Create trigger_alarm() function:
- Activate buzzer with pattern
- Flash alert LED
- Update detection counter
- Display alert on LCD
- Run for 5 seconds
- Main loop:
- Check button for arm/disarm
- If armed, monitor PIR sensor
- If motion detected, trigger alarm
- Respect cooldown period
- Update status display
Comments
Post a Comment