Raspberry Pi Pico: Detect Motion using a PIR Sensor (MicroPython)

Learn how to interface a PIR motion sensor with the Raspberry Pi Pico to detect motion in your surroundings. We’ll show you how to wire the sensor to the Pico board and we’ll program the board using MicroPython firmware to execute a certain task when motion is detected.

Raspberry Pi Pico Detect Motion using a PIR Sensor MicroPython

Table of Contents:

Throughout this tutorial, we’ll cover the following topics:

Prerequisites – MicroPython Firmware

To follow this tutorial you need MicroPython firmware installed in your Raspberry Pi Pico board. You also need an IDE to write and upload the code to your board.

The recommended MicroPython IDE for the Raspberry Pi Pico is Thonny IDE. Follow the next tutorial to learn how to install Thonny IDE, flash MicroPython firmware, and upload code to the board.

Introducing the PIR Motion Sensor

A PIR motion sensor detects changes in infrared light in its field of view. This makes it ideal for detecting humans or animals because it will pick up living things (or heat-emitting objects) that move within their range but not inanimate objects.

You can program the Pi to react to changes in infrared light by triggering an event such as turning on a light, sounding an alarm, sending a notification, or any other task. In this tutorial, we’ll print a message on the console, and we’ll light up an LED.

AM312 PIR Motion Sensor Pinout labeled
Mini AM312 PIR Motion Sensor

There are different PIR motion sensor modules, but all act in a similar way. You’ll have a power pin, GND, and data.

The PIR motion sensor outputs a HIGH signal on the Data pin when it detects movement, or a LOW signal if it doesn’t. It only has three pins: VCC, GND, and Data.

How a PIR Motion sensor works

Some models like the HC-SR501 might have two potentiometers (those two orange potentiometers in the picture below) to adjust the sensitivity and the time delay.

HC-SR501 PIR Motion Sensor
HC-SR501 PIR Motion Sensor
  • Sensitivity potentiometer: this adjusts the sensor’s detection range. Clockwise increases sensitivity, counterclockwise decreases it.
  • Time delay potentiometer: this controls how long the sensor remains triggered after detecting motion. Clockwise increases the delay, and counterclockwise decreases it.

Wiring a PIR Motion Sensor to the Pi Pico

The PIR motion sensor has three pins: VCC, GND, and Data. You should connect VCC to the 3V3 pin (or 5V depending on the model), GND to a GND pin, and the Data pin to a suitable Raspberry Pi Pico GPIO—we’ll connect it to GPIO 22 (take a look at the Raspberry Pi Pico pinout). We’ll also add an LED to GPIO 21 to add visual feedback to your project.

PIR Motion Sensor with Raspberry Pi Pico Circuit
PIR Motion SensorRaspberry Pi
Vin/3v33.3V or 5V depending on the model
DataGPIO 22 (or another GPIO of your choice)
GNDGND

We’ll use the AM312 Mini PIR Motion sensor because it works with 3V3 volts, which is perfect for the Raspberry Pi. But you can use any other PIR motion sensor module. The working principle is the same.

Here’s a list of components you need for this project:

You can use the preceding links or go directly to MakerAdvisor.com/tools to find all the parts for your projects at the best price!

Raspberry Pi Pico Motion Sensor Circuit

PIR Motion Sensor – Detect Motion (MicroPython)

There are many different ways to write a program to detect motion using a PIR motion sensor. The simplest way is to simply read the state of the PIR sensor as you would read any other digital input.

For example:

# Rui Santos & Sara Santos - Random Nerd Tutorials
# Complete project details at https://RandomNerdTutorials.com/raspberry-pi-pico-motion-pir-micropython/

from machine import Pin
from time import sleep

pir_pin = Pin(22, Pin.IN)
led_pin = Pin (21, Pin.OUT)

while True:
    if pir_pin.value():
        print("Motion detected!")
        led_pin.value(1)
    sleep(1)
    led_pin.value(0)

View raw code

However, this might not be the best solution, because the PIR motion sensor data pin stays in a HIGH state for a few seconds before getting back to LOW. The best way is to detect the precise moment when the state goes from LOW to HIGH (rising mode) and have a better responsiveness. For that, we can use interrupts.

Introducing Interrupts

Interrupts are useful for making things happen automatically in microcontroller programs and can help solve timing problems. With interrupts, you don’t need to constantly check the current pin value. When a change is detected, an event is triggered (a function is called—this function is often called an ISR (interrupt service routine).

When an interrupt happens, the processor stops the execution of the main program to execute a task, and then gets back to the main program as shown in the figure below.

Interrupts Raspberry Pi Pico

This is especially useful to trigger an action whenever motion is detected or whenever a pushbutton is pressed without the need for constantly checking its state.

Raspberry Pi Pico Setting Up an Interrupt in MicroPython

To set up an interrupt in MicroPython, you need to follow the next steps:

1. Define an interrupt handling function (ISR). The interrupt handling function should be as simple as possible, so the processor gets back to the execution of the main program quickly. The best approach is to signal the main code that the interrupt has happened by using a global variable, for example.

The interrupt handling function should accept a parameter of type Pin. This parameter is returned to the callback function and it refers to the GPIO that caused the interrupt. You should avoid doing tasks that take a long time inside the ISR. You should also avoid using the regular print function inside.

def handle_interrupt(pin):

2. Set up the GPIO that will act as an interrupt pin as an input. For example, in our case we’re using GPIO 22:

pir = Pin(22, Pin.IN)

3. Attach an interrupt to that pin by calling the irq() method:

pir.irq(trigger=Pin.IRQ_RISING, handler=handle_interrupt)

The irq() method accepts the following arguments:

  • trigger: this defines the trigger mode. There are different conditions in MicroPython:
    • Pin.IRQ_FALLING: to trigger the interrupt whenever the pin goes from HIGH to LOW;
    • Pin.IRQ_RISING: to trigger the interrupt whenever the pin goes from LOW to HIGH.
    • Pin.IRQ_LOW_LEVEL: interrupt occurs when a pin is LOW (not supported on the Pico board at the time of writing this article).
    • Pin.IRQ_HIGH_LEVEL: interrupt occurs when a pin is HIGH (not supported on the Pico board at the time of writing this article).
    • Pin.IRQ_FALLING | Pin.IRQ_RISING: interrupt on both the rising edge and the falling edge.
  • handler: this is a function that will be called when an interrupt is detected, in this case, the handle_interrupt() function.

Raspberry Pi Pico with PIR Motion Sensor Using Interrupts – MicroPython Code

The following MicroPython code for the Raspberry Pi Pico monitors a PIR motion sensor connected. When the PIR sensor detects motion, it turns on an LED for visual feedback and prints “Motion detected!” to the console. When motion stops, it turns off the LED and prints “Motion stopped” to the console. The code continuously runs in a loop until interrupted by a keyboard command or when an exception occurs.

# Rui Santos & Sara Santos - Random Nerd Tutorials
# Complete project details at https://RandomNerdTutorials.com/raspberry-pi-pico-motion-pir-micropython/

from machine import Pin
from time import sleep

# Define the PIR sensor input pin
pir_pin = Pin(22, Pin.IN)

# Define the LED pin (optional, for visual feedback)
led_pin = Pin(21, Pin.OUT)

# Flags to indicate motion detection state
motion_detected = False

motion_stopped_printed = False

# Callback function to handle motion detection
def pir_interrupt(pin):
    global motion_detected
    if pin.value() == 1:  # Rising edge (motion detected)
        motion_detected = True
        led_pin.value(1)  # Turn on the LED
    else:  # Falling edge (motion stopped)
        motion_detected = False
        led_pin.value(0)  # Turn off the LED

# Configure the interrupt on the PIR pin for both rising and falling edges
pir_pin.irq(trigger=(Pin.IRQ_RISING | Pin.IRQ_FALLING), handler=pir_interrupt)

try:
    while True:
        if motion_detected and not motion_stopped_printed:
            print("Motion detected!")
            motion_stopped_printed = True  # Set the flag to indicate motion detected

        elif not motion_detected and motion_stopped_printed:
            print("Motion stopped")
            motion_stopped_printed = False  # Reset the flag

        sleep(0.1)  # Main loop delay

except KeyboardInterrupt:
    print("Keyboard interrupt")
    pir_pin.irq(trigger=0)  # Disable the interrupt
    led_pin.value(0)  # Turn off the LED
    
except Exception as e:
    print("An unexpected exception occurred:", e)	

View raw code

How Does the Code Work?

Continue reading to learn how the code works or skip to the Demonstration section.

Importing Libraries

The following lines import the necessary libraries for working with GPIO pins and implementing time delays.

from machine import Pin
from time import sleep

Pin Definitions

In this section, the code defines the pins for the PIR sensor input and an optional LED for visual feedback. We’re connecting the sensor to GPIO 22 and the LED to GPIO 21. Alternatively, you can use any other suitable pins (just make sure you modify the code with the pins you’re using).

# Define the PIR sensor input pin
pir_pin = Pin(22, Pin.IN)

# Define the LED pin (optional, for visual feedback)
led_pin = Pin(21, Pin.OUT)

Motion Detection Flags

We create two variables to indicate the state of motion detection. The motion_detected is set to True when motion is detected, and the motion_stopped_printed is used to track whether the “Motion stopped” message has been printed on the console (this is to prevent writing the same message multiple times when motion is not being detected).

# Flags to indicate motion detection state
motion_detected = False

motion_stopped_printed = False

Callback Function for Motion Detection

A callback function named pir_interrupt is defined to handle the interrupts generated by the PIR sensor. This function will be called every time motion is detected.

# Callback function to handle motion detection
def pir_interrupt(pin):
    global motion_detected
    if pin.value() == 1:  # Rising edge (motion detected)
        motion_detected = True
        led_pin.value(1)  # Turn on the LED
    else:  # Falling edge (motion stopped)
        motion_detected = False
        led_pin.value(0)  # Turn off the LED

The pir_interrupt function has an input parameter (pin) in which an object of class Pin will be passed when the interrupt happens (it indicates which pin caused the interrupt).

This function toggles the motion_detected variable and controls the LED based on the rising and falling edges of the PIR sensor signal. If the pin.value() is 1, which means the PIR sensor data pin is HIGH, we set the motion_detected variable to True and turn on the LED.

if pin.value() == 1:  # Rising edge (motion detected)
    motion_detected = True
    led_pin.value(1)  # Turn on the LED

If pin.value() is not 1, it means that the PIR motion sensor data pin went from HIGH to LOW. So, we can set the motion_detected variable to False and turn off the LED because motion is no longer detected.

else:  # Falling edge (motion stopped)
    motion_detected = False
    led_pin.value(0)  # Turn off the LED

Configuring Interrupts

The following line configures the interrupt on the PIR pin to trigger on both rising and falling edges. The pir_interrupt function will be called when these edges are detected. This means we’ll detect when motion is detected and when motion stops being detected.

pir_pin.irq(trigger=(Pin.IRQ_RISING | Pin.IRQ_FALLING), handler=pir_interrupt)

While Loop

The main loop continuously checks the state of the motion_detected variable and provides feedback accordingly. It prints messages when motion is detected or stopped. To prevent printing the same message multiple times, we also check the state of the motion_stopped_printed variable.

try:
    while True:
        if motion_detected and not motion_stopped_printed:
            print("Motion detected!")
            motion_stopped_printed = True  # Set the flag to indicate motion detected

        elif not motion_detected and motion_stopped_printed:
            print("Motion stopped")
            motion_stopped_printed = False  # Reset the flag

        sleep(0.1)  # Main loop delay

Exception Handling

The code includes exception handling to handle keyboard interrupts (e.g., when stopping the script) and other unexpected exceptions. It disables the interrupt and turns off the LED when the script is stopped.

except KeyboardInterrupt:
    print("Keyboard interrupt")
    pir_pin.irq(trigger=0)  # Disable the interrupt
    led_pin.value(0)  # Turn off the LED
    
except Exception as e:
    print("An unexpected exception occurred:", e)

Demonstration

Save the code to your Raspberry Pi Pico board using Thonny IDE or any other MicroPython IDE of your choice.

Copy the code provided to a new file on Thonny IDE.

With the code copied to the file, click on the Save icon. Then, select Raspberry Pi Pico.

Saving file to Raspberry Pi Pico MicroPython IDE

Save the file with the following name: main.py.

Micropython saving main.py file Thonny IDE

Note: when you name a file main.py, the Raspberry Pi Pico will run that file automatically on boot. If you call it a different name, it will still be saved on the board filesystem, but it will not run automatically on boot.

Click the little green button “Run Current Script” or press F5.

run script on thonny ide

Now, test your setup. Wave your hand in front of the motion sensor. It will print “Motion Detected!” in the console.

Raspberry Pi Pico MicroPython Thonny IDE PIR Motion sensor detect motion

And the LED will light up. The LED will be lit as long as the PIR sensor data pin is high (this might depend on the PIR sensor model you’re using).

Raspberry Pi Pico PIR Motion Sensor Detect Motion Demonstration Example

After a few seconds, if motion is no longer detected. You’ll get a “motion stopped” message and the LED will turn off.

Wrapping Up

In this tutorial, you learned how to use a PIR sensor with the Raspberry Pi Pico to detect motion. You learned how to use interrupts to execute tasks when motion is detected.

To keep things simple, we just turned an LED on and off. In a real-world scenario, you may want to send a notification to your smartphone, send an email, turn on a lamp, send a message via MQTT, or any other task.

We hope you’ve found this tutorial useful. We have other Raspberry Pi Pico tutorials that you may like:

Check out all our Raspberry Pi Pico Guides »



Learn how to build a home automation system and we’ll cover the following main subjects: Node-RED, Node-RED Dashboard, Raspberry Pi, ESP32, ESP8266, MQTT, and InfluxDB database DOWNLOAD »
Learn how to build a home automation system and we’ll cover the following main subjects: Node-RED, Node-RED Dashboard, Raspberry Pi, ESP32, ESP8266, MQTT, and InfluxDB database DOWNLOAD »

Enjoyed this project? Stay updated by subscribing our newsletter!

Leave a Comment

Download Our Free eBooks and Resources

Get instant access to our FREE eBooks, Resources, and Exclusive Electronics Projects by entering your email address below.