Raspberry Pi Simple Alarm Project

This project wires a motion sensor and arm/disarm buttons into a small alarm system that sounds a buzzer and flashing LED, showing how to combine sensor input and actuator output around a clear state machine.

What This Project Builds

The alarm combines the concepts from earlier lessons: a button as an input trigger, a PIR (passive infrared) motion sensor as a second input, and an LED plus a buzzer as outputs. Two buttons control whether the system is watching for motion at all - one arms it, the other disarms it - so a false trigger, or simply walking past your own project, does not immediately set it off. The system only reacts to the motion sensor while it is in the armed state.

The Alarm Logic

  1. Start in a DISARMED state, where motion from the PIR sensor is ignored entirely.
  2. When the arm button is pressed, switch to ARMED and begin reacting to the motion sensor.
  3. While ARMED, if the sensor reports motion, switch to ALERT: flash the LED and sound the buzzer continuously.
  4. Stay in ALERT until the disarm button is pressed, which silences the outputs and returns the system to DISARMED.
  5. Ignore additional motion events while already in ALERT, since the alarm is already sounding and there is nothing more to trigger.

Wiring Overview

ComponentGPIO PinRole
PIR motion sensorGPIO4Detects movement while the system is armed
Arm buttonGPIO2Switches the system into the ARMED state
Disarm buttonGPIO3Switches the system back to DISARMED and silences the alarm
LED (alert indicator)GPIO17Flashes rapidly during an alert
BuzzerGPIO27Sounds continuously during an alert

Event-Driven Alarm With gpiozero

from gpiozero import Button, LED, Buzzer, MotionSensor
from signal import pause

arm_button = Button(2, bounce_time=0.1)
disarm_button = Button(3, bounce_time=0.1)
pir = MotionSensor(4)
siren_led = LED(17)
buzzer = Buzzer(27)

armed = False

def arm_system():
    global armed
    armed = True
    print('Alarm armed')

def disarm_system():
    global armed
    armed = False
    siren_led.off()
    buzzer.off()
    print('Alarm disarmed')

def motion_detected():
    if armed:
        siren_led.blink(on_time=0.1, off_time=0.1)
        buzzer.on()
        print('Intruder detected!')

arm_button.when_pressed = arm_system
disarm_button.when_pressed = disarm_system
pir.when_motion = motion_detected

pause()

Notice that the whole program never sits in a busy loop polling pins - each device's when_pressed or when_motion attribute is simply assigned a function, and gpiozero calls that function automatically the moment the matching event occurs. The final pause() call just keeps the program alive so those event callbacks can keep firing in the background; without it, the script would immediately reach its end and exit.

Note: PIR sensors typically need thirty to sixty seconds after power-up to calibrate to the room's ambient infrared level - if you get a false trigger immediately on boot, that warm-up period is almost always the cause. The bounce_time argument on the buttons similarly prevents one physical press from registering as several rapid presses.
Note: A gpiozero Buzzer is meant for small piezo buzzers that draw very little current; a louder 5V or 12V siren-style buzzer needs to be switched through a transistor or relay rather than driven directly from a GPIO pin, or it can draw more current than the pin is rated for.

Extending the Alarm

  • Add a numeric keypad so disarming requires entering a correct code, not just pressing a button.
  • Log every trigger with a timestamp to a file so activity can be reviewed later.
  • Send a notification - email, SMS, or a chat webhook - the moment the ALERT state begins.
  • Add a short entry delay after arming so you have a few seconds to leave the room before the sensor starts reacting.