To get you started with how to program your Pico in CircuitPython, especially for those who may have started out with the official MicroPython setup, we've 'ported' the Getting Started with MicroPython on Pico book examples to CircuitPython. The book is awesome, please download/purchase it to support Raspberry Pi Press!

Another common use of microcontrollers is in alarm systems, including home security systems. While they can get complex with a significant number of sensors, a basic motion sensing alarm is quite easy to build with a microcontroller and a few components.

This section will show you how to use your Raspberry Pi Pico board to build a basic motion sensor, and then turn it into a burglar alarm by adding lights and sound.

Wiring the Basic Motion Sensor

For this example you'll need your Pico board, and a PIR sensor.

PIR (motion) sensor with a cable around it.
PIR sensors are used to detect motion from pets/humanoids from about 20 feet away (possibly works on zombies, not guaranteed). This one has an adjustable delay before firing (approx...
$9.95
In Stock

The first step is to connect the PIR sensor to your board. Wire them up as shown below. If your PIR sensor comes with a cable, you can connect the cable and push the ends of the wires directly into the breadboard. If your PIR sensor does not have a cable, use male-to-female jumper wires to connect from the header on the sensor to the breadboard.

  • Board GND to PIR sensor GND
  • Board VBUS to PIR sensor 5V
  • Board GP28 to PIR sensor data pin

Programming the Basic Motion Sensor

Now that you've wired up your motion sensor, you can begin programming it.

Update your code.py to the following and save.

# SPDX-FileCopyrightText: 2021 Kattni Rembor for Adafruit Industries
#
# SPDX-License-Identifier: MIT

"""
Simple motion sensor example for Pico. Prints to serial console when PIR sensor is triggered.

REQUIRED HARDWARE:
* PIR sensor on pin GP28.
"""
import board
import digitalio

pir = digitalio.DigitalInOut(board.GP28)
pir.direction = digitalio.Direction.INPUT

while True:
    if pir.value:
        print("ALARM! Motion detected!")
        while pir.value:
            pass

Now open the serial console and wave your hand in front of the sensor. Motion detected!

If you sit still and wait a bit before waving your hand in front of the sensor, you'll see ALARM! Motion detected! printed to the serial console again. However, you'll notice that if you keep waving your hand in front of the sensor, it takes a while for it to print another message. There is no delay in the code, so what's causing this behavior? The PIR sensor has a delay built in. The sensor sends a signal to your Pico board's GPIO pin when motion is detected, and maintains that signal for a period of time before stopping it. Until the signal stops, it cannot detect further motion, and therefore cannot trigger your motion detection code. Many PIR sensors have a potentiometer on them you can use to adjust the length of this delay, though there is always a minimum delay. Here is a detailed explanation.

Now, a more detailed look at the code. First import the two necessary modules.

import board
import digitalio

Next, you set up the PIR sensor using digitalio. You create the object, provide it a pin, and set the pin direction to input.

pir = digitalio.DigitalInOut(board.GP28)
pir.direction = digitalio.Direction.INPUT

pir.value returns True when there is motion detected, and False when no motion is detected. Inside your loop, you check whether pir.value is True. If it is, you print ALARM! Motion detected!. Then there is a second loop checking pir.value, which runs continuously as long as the condition is True, which is a way to say "wait until pir.value is False" and then move on. When you have a loop inside of another loop, they are referred to as nested loops.

while True:
    if pir.value:
        print("ALARM! Motion detected!")
        while pir.value:
            pass

This code sequence helps the code only react to the initial change in signal from the PIR sensor, instead of reporting motion detected for the entire time the signal is sent. It means ALARM! Motion detected! is only printed once instead of being spammed to the serial console for the entire duration of the signal event.

Printing to the serial console is enough to test that your PIR sensor is working, but it doesn't amount to a useful alarm. Actual burglar alarms have lights and sirens that notify everyone within range that something is wrong. With a few more components, you can add the same functionality to your setup.

Burglar Alarm

Alarms are only useful if they have some way to notify you that they have been triggered. A blinking LED is a simple way to see when the motion sensor has been triggered. So, the first thing you'll do is add an LED to your motion sensing setup.

Wiring the Burglar Alarm with Light

This example builds on the previous. For this example, you'll need your current wiring setup, an LED of any color, a resistor, and a number of male-to-male jumper wires. A 220Ω-1.0KΩ resistor will work; a 220Ω resistor is shown in the diagram.

Connect the LED to your current setup as shown below. You'll need to modify your current setup slightly by moving the PIR sensor GND. PIR signal and power do not need to be moved.

  • Board GND to breadboard ground rail (using a jumper wire)
  • Board GP13 to 220Ω resistor
  • LED+ to 220Ω resistor
  • LED- to breadboard ground rail (using a jumper wire)
  • PIR sensor GND to breadboard ground rail (using a jumper wire)

Programming the Burglar Alarm with Light

Now that you've wired up your burglar alarm with light, it's time to program it.

Update your code.py to the following and save.

# SPDX-FileCopyrightText: 2021 Kattni Rembor for Adafruit Industries
#
# SPDX-License-Identifier: MIT

"""
A burglar alarm example for Pico. Quick flashing LED indicates alarm has been triggered.

REQUIRED HARDWARE:
* PIR sensor on pin GP28.
* LED on pin GP13.
"""
import time
import board
import digitalio

pir = digitalio.DigitalInOut(board.GP28)
pir.direction = digitalio.Direction.INPUT
led = digitalio.DigitalInOut(board.GP13)
led.direction = digitalio.Direction.OUTPUT

motion_detected = False
while True:
    if pir.value and not motion_detected:
        print("ALARM! Motion detected!")
        motion_detected = True

    if pir.value:
        led.value = True
        time.sleep(0.1)
        led.value = False
        time.sleep(0.1)

    motion_detected = pir.value

Now when you save your hand in front of the sensor, as well as printing the message to the serial console, the LED will blink quickly to let you know motion has been detected! Once the sensor signal stops, the LED will stop blinking.

Now, a more detailed look at the code. First you import the same modules as before, but now you include time as well.

import time
import board
import digitalio

Then, in addition to the PIR sensor setup, you set up the LED.

pir = digitalio.DigitalInOut(board.GP28)
pir.direction = digitalio.Direction.INPUT
led = digitalio.DigitalInOut(board.GP13)
led.direction = digitalio.Direction.OUTPUT

You create a variable called motion_detected and set it to False before the loop.

motion_detected = False

Inside the loop, you first check to see if pir.value is True AND motion_detected is False. If BOTH of these conditions are valid, you print ALARM! Motion detected! to the serial console and set motion_detected to True. Next, you check if pir.value is True, and if so, you blink the LED on and off every 0.1 seconds. Finally, you set motion_detected equal to pir.value. This resets motion_detected to False once motion is no longer detected, and allows for motion to be detected again.

while True:
    if pir.value and not motion_detected:
        print("ALARM! Motion detected!")
        motion_detected = True

    if pir.value:
        led.value = True
        time.sleep(0.1)
        led.value = False
        time.sleep(0.1)

    motion_detected = pir.value

To make your alarm even more effective, you could warn intruders that the alarm is active by making the LED flash slowly when there is no motion detected. To do this, simply replace the last line of the example with an else block that includes code similar to the if pir.value: block, except with a longer time.sleep().

[...]
    else:
        motion_detected = False
        led.value = True
        time.sleep(0.5)
        led.value = False
        time.sleep(0.5)

With the above update, your code.py file should look like the following.

# SPDX-FileCopyrightText: 2021 Kattni Rembor for Adafruit Industries
#
# SPDX-License-Identifier: MIT

"""
A burglar alarm example for Pico. Slow flashing LED indicates alarm is ready. Quick flashing LED
indicates alarm has been triggered.

REQUIRED HARDWARE:
* PIR sensor on pin GP28.
* LED on pin GP13.
"""
import time
import board
import digitalio

pir = digitalio.DigitalInOut(board.GP28)
pir.direction = digitalio.Direction.INPUT
led = digitalio.DigitalInOut(board.GP13)
led.direction = digitalio.Direction.OUTPUT

motion_detected = False
while True:
    if pir.value and not motion_detected:
        print("ALARM! Motion detected!")
        motion_detected = True

    if pir.value:
        led.value = True
        time.sleep(0.1)
        led.value = False
        time.sleep(0.1)

    else:
        motion_detected = False
        led.value = True
        time.sleep(0.5)
        led.value = False
        time.sleep(0.5)

Your alarm system notifies everyone visually that it's running and lets you know when motion is detected. Now it needs sound!

Wiring the Burglar Alarm with Light and Sound

This example builds on the previous. For this example, you'll need your current wiring setup, and a piezo buzzer.

Connect the piezo buzzer to your current setup as shown below. Direction for the piezo buzzer does not matter.

  • Board GP14 to one leg of piezo buzzer
  • Other leg of piezo buzzer to breadboard ground rail

Programming the Burglar Alarm with Light and Sound

Now that you've wired up your burglar alarm with light and sound, it's time to program it.

Update your code.py to the following and save.

# SPDX-FileCopyrightText: 2021 Kattni Rembor for Adafruit Industries
#
# SPDX-License-Identifier: MIT

"""
A burglar alarm example for Pico. Slow flashing LED indicates alarm is ready. Quick flashing LED
and beeping buzzer indicate alarm has been triggered.

REQUIRED HARDWARE:
* PIR sensor on pin GP28.
* LED on pin GP13.
* Piezo buzzer on pin GP14.
"""
import time
import board
import digitalio
import pwmio

pir = digitalio.DigitalInOut(board.GP28)
pir.direction = digitalio.Direction.INPUT
led = digitalio.DigitalInOut(board.GP13)
led.direction = digitalio.Direction.OUTPUT
buzzer = pwmio.PWMOut(board.GP14, frequency=660, duty_cycle=0, variable_frequency=True)

motion_detected = False
while True:
    if pir.value and not motion_detected:
        print("ALARM! Motion detected!")
        motion_detected = True

    if pir.value:
        led.value = True
        buzzer.duty_cycle = 2 ** 15
        time.sleep(0.1)
        led.value = False
        buzzer.duty_cycle = 0
        time.sleep(0.1)

    else:
        motion_detected = False
        led.value = True
        time.sleep(0.5)
        led.value = False
        time.sleep(0.5)

Now wave your hand in front of the sensor. Motion detected, the LED blinks rapidly, and now it beeps along with the blinking for an auditory indicator that it's been triggered!

This code will look very similar to the previous example with a few modifications. First you import the necessary modules, this time including pwmio.

import time
import board
import digitalio
import pwmio

Next, in addition to the PIR sensor and LED setup, you set up the piezo buzzer.

pir = digitalio.DigitalInOut(board.GP28)
pir.direction = digitalio.Direction.INPUT
led = digitalio.DigitalInOut(board.GP13)
led.direction = digitalio.Direction.OUTPUT
buzzer = pwmio.PWMOut(board.GP14, frequency=660, duty_cycle=0, variable_frequency=True)

Finally, you add two lines of code into the if block of the loop to turn the buzzer on and off along with the LED.

while True:
    if pir.value and not motion_detected:
        print("ALARM! Motion detected!")
        motion_detected = True

    if pir.value:
        led.value = True
        buzzer.duty_cycle = 2 ** 15
        time.sleep(0.1)
        led.value = False
        buzzer.duty_cycle = 0
        time.sleep(0.1)

The rest of the loop remains the same. That's all there is to creating a burglar alarm with light and sound!

Home security systems rarely cover only one room or area. They are often made up of a network of many sensors connected to a single alarm system. You can easily add more sensors to your setup to monitor multiple areas at the same time.

Wiring the Extended Burglar Alarm with Light and Sound

For this example, you'll need your current wiring setup and a second PIR sensor.

Connect the second PIR sensor as shown below. Since the VBUS connection is already being used by the first PIR sensor, you'll need to make a slight modification. Move both the Pico board's VBUS connection to the breadboard power rail, and the 5V pin on the first PIR sensor to the breadboard power rail.

  • Board VBUS to breadboard power rail
  • First PIR sensor 5V to breadboard power rail
  • Second PIR sensor GND to breadboard ground rail
  • Second PIR sensor 5V to breadboard power rail
  • Board GP22 to second PIR sensor data pin

Programming the Extended Burglar Alarm

Now that you've added another sensor to your setup, it's time to program it.

Update your code.py to the following, and save.

# SPDX-FileCopyrightText: 2021 Kattni Rembor for Adafruit Industries
#
# SPDX-License-Identifier: MIT

"""
A burglar alarm with two motion sensors example for Pico. Slow flashing LED indicates alarm is
ready. Quick flashing LED and beeping buzzer indicate alarm has been triggered.

REQUIRED HARDWARE:
* PIR sensor on pin GP28.
* PIR sensor on pin GP22.
* LED on pin GP13.
* Piezo buzzer on pin GP14.
"""
import time
import board
import digitalio
import pwmio

pir_one = digitalio.DigitalInOut(board.GP28)
pir_one.direction = digitalio.Direction.INPUT
pir_two = digitalio.DigitalInOut(board.GP22)
pir_two.direction = digitalio.Direction.INPUT
led = digitalio.DigitalInOut(board.GP13)
led.direction = digitalio.Direction.OUTPUT
buzzer = pwmio.PWMOut(board.GP14, frequency=660, duty_cycle=0, variable_frequency=True)

motion_detected_one = False
motion_detected_two = False
while True:
    if pir_one.value and not motion_detected_one:
        print("ALARM! Motion detected in bedroom!")
        motion_detected_one = True

    if pir_two.value and not motion_detected_two:
        print("ALARM! Motion detected in living room!")
        motion_detected_two = True

    if pir_one.value or pir_two.value:
        led.value = True
        buzzer.duty_cycle = 2 ** 15
        time.sleep(0.1)
        led.value = False
        buzzer.duty_cycle = 0
        time.sleep(0.1)

    else:
        motion_detected_one = False
        motion_detected_two = False

        led.value = True
        time.sleep(0.5)
        led.value = False
        time.sleep(0.5)

Wave your hand over the first sensor to see motion reported in the bedroom. Wave your hand over the second sensor to see motion reported in the living room. Multi-area coverage!

Now a look at the code. The code will look very similar, but a number of modifications are needed to make it both work properly and be easily understood. Imports remain the same.

Setup now includes two PIR sensors, so it makes sense to update the variable names to indicate the presence of two sensors. Below the first PIR sensor, include another two lines of setup for the second one, with the appropriate pins. LED and buzzer setup remain the same.

pir_one = digitalio.DigitalInOut(board.GP28)
pir_one.direction = digitalio.Direction.INPUT
pir_two = digitalio.DigitalInOut(board.GP22)
pir_two.direction = digitalio.Direction.INPUT

Before the loop, you create two variables to track whether motion has been detected, and set them to False initially.

motion_detected_one = False
motion_detected_two = False

The loop begins with two if blocks. They each check a sensor value and the respective motion detected variable. If the sensor value is returning True and the motion_detected variable is False, then it prints the appropriate ALARM! message to the serial console and sets the respective motion_detected variable to True.

while True:
    if pir_one.value and not motion_detected_one:
        print("ALARM! Motion detected in bedroom!")
        motion_detected_one = True

    if pir_two.value and not motion_detected_two:
        print("ALARM! +Motion detected in living room!")
        motion_detected_two = True

Then, the code checks whether the sensors are returning True, and if they are blink the LED and beep the buzzer on and off every 0.1 seconds.

[...]
    if pir_one.value or pir_two.value:
        led.value = True
        buzzer.duty_cycle = 2 ** 15
        time.sleep(0.1)
        led.value = False
        buzzer.duty_cycle = 0
        time.sleep(0.1)

Finally, once motion is no longer detected, the motion_detected variables are set to False, and the LED blinks on and off more slowly, every 0,.5 seconds.

[...]
    else:
        motion_detected_one = False
        motion_detected_two = False

        led.value = True
        time.sleep(0.5)
        led.value = False
        time.sleep(0.5)

Now you know how to extend your alarm system to cover more than one area. You can add more sensors as needed!

This guide was first published on Jan 21, 2021. It was last updated on Mar 03, 2021.

This page (Burglar Alarm) was last updated on Sep 24, 2023.

Text editor powered by tinymce.