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

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 write an Arduino sketch to execute a certain task when motion is detected.

Raspberry Pi Pico: Detect Motion using a PIR Sensor Arduino IDE

Table of Contents:

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

Prerequisites

You need to install the Raspberry Pi Pico boards on Arduino IDE and you must know how to upload code to the board. Check out the following tutorial first if you haven’t already:

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. They 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).

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 3.3V, which is perfect for the Raspberry Pi. But you can use any other PIR motion sensor module. The working principle is the same.

We’ll also connect an LED to GPIO 21 to add visual feedback to our project.

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 PIR Motion Sensor Circuit

PIR Motion Sensor – Detect Motion

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-arduino/

  Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files.
  The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*********/

int led = 21;                // the pin that the LED is atteched to
int sensor = 22;              // the pin that the sensor is atteched to
int state = LOW;             // by default, no motion detected
int val = 0;                 // variable to store the sensor status (value)

void setup() {
  pinMode(led, OUTPUT);      // initalize LED as an output
  pinMode(sensor, INPUT);    // initialize sensor as an input
  Serial.begin(9600);        // initialize serial
}

void loop(){
  val = digitalRead(sensor);   // read sensor value
  if (val == HIGH) {           // check if the sensor is HIGH
    digitalWrite(led, HIGH);   // turn LED ON
    delay(100);                // delay 100 milliseconds 
    
    if (state == LOW) {
      Serial.println("Motion detected!"); 
      state = HIGH;       // update variable state to HIGH
    }
  } 
  else {
      digitalWrite(led, LOW); // turn LED OFF
      delay(200);             // delay 200 milliseconds 
      
      if (state == HIGH){
        Serial.println("Motion stopped!");
        state = LOW;       // update variable state to LOW
    }
  }
}

View raw code

However, this might not be the best solution because you need to constantly poll the state of the pin that the sensor is connected to. Usually, the best way to detect the precise moment when the state goes from LOW to HIGH (rising mode) and have a better responsiveness is to use interrupts. But, there’s nothing wrong with using this previous simplified code, if it works good for your project application.

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 MicroPython 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 Arduino IDE

Before going to the complete example, let’s take a quick look at how to set up an interrupt on one of the Raspberry Pi Pico GPIOs.

attachInterrupt() Function

To set an interrupt in the Arduino IDE, you use the attachInterrupt() function, which accepts as arguments: the GPIO interrupt pin, the name of the function to be executed, and the mode.

attachInterrupt(digitalPinToInterrupt(GPIO), ISR, mode);

GPIO interrupt pin

The first argument is a GPIO interrupt. You should use digitalPinToInterrupt(GPIO) to set the actual GPIO as an interrupt pin. For example, if you want to use GPIO 22 as an interrupt, use:

digitalPinToInterrupt(22)

ISR

The second argument of the attachInterrupt() function is the name of the function that will be called every time the interrupt is triggered – the interrupt service routine (ISR).

The ISR function should be as simple as possible, so the processor gets back to the execution of the main program quickly. You should avoid tasks like printing to the serial monitor, delays, and other tasks that might take time.

The best approach is to signal the main code that the interrupt has happened by using a global variable and within the loop() check and clear that flag, and execute code.

Interrupt modes

The third argument is the mode and there are 3 different modes:

  • CHANGE: to trigger the interrupt whenever the pin changes value – for example from HIGH to LOW or LOW to HIGH;
  • FALLING: for when the pin goes from HIGH to LOW;
  • RISING: to trigger when the pin goes from LOW to HIGH.

For our example, we’ll use the RISING mode, because when the PIR motion sensor detects motion, the GPIO it is connected to goes from LOW to HIGH.

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

The following Arduino sketch for the Raspberry Pi Pico monitors a PIR motion sensor connected to GPIO 22. When the PIR sensor detects motion, the Pico turns on an LED for visual feedback and prints “Motion detected!” to the console. When the motion stops, it turns off the LED and prints “Motion stopped” to the console.

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

  Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files.
  The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*********/

#define timeSeconds 10

// Set GPIOs for LED and PIR Motion Sensor
const int led = 21;
const int motionSensor = 22;

// Timer: Auxiliary variables
unsigned long now = millis();
unsigned long lastTrigger = 0;
boolean startTimer = false;
boolean motion = false;

// Runs when motion is detected, sets LED HIGH and starts a timer
void detectsMovement() {
  digitalWrite(led, HIGH);
  startTimer = true;
  lastTrigger = millis();
}

void setup() {
  // Serial port for debugging purposes
  Serial.begin(115200);
  
  // PIR Motion Sensor mode INPUT_PULLUP
  pinMode(motionSensor, INPUT_PULLUP);
  // Set motionSensor pin as interrupt, assign interrupt function and set RISING mode
  attachInterrupt(digitalPinToInterrupt(motionSensor), detectsMovement, RISING);

  // Set LED to LOW
  pinMode(led, OUTPUT);
  digitalWrite(led, LOW);
}

void loop() {
  // Current time
  now = millis();
  if((digitalRead(led) == HIGH) && (motion == false)) {
    Serial.println("MOTION DETECTED!!!");
    motion = true;
  }
  // Turn off the LED after the number of seconds defined in the timeSeconds variable
  if(startTimer && (now - lastTrigger > (timeSeconds*1000))) {
    Serial.println("Motion stopped...");
    digitalWrite(led, LOW);
    startTimer = false;
    motion = false;
  }
}

View raw code

How Does the Code Work?

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

The previous code detects motion using a PIR motion sensor. When motion is detected, the Pico turns on an LED and starts a timer. If no further motion is detected within a defined time period, it turns off the LED. Let’s break down the code step by step.

Constants and Variables

You start by defining several constants and variables that are used throughout the program.

#define timeSeconds 10

// Set GPIOs for LED and PIR Motion Sensor
const int led = 21;
const int motionSensor = 22;

// Timer: Auxiliary variables
unsigned long now = millis();
unsigned long lastTrigger = 0;
boolean startTimer = false;
boolean motion = false;

The timeSeconds defines the duration (in seconds) for which the LED will remain on when motion is detected – you can change that value.

#define timeSeconds 10

We also create variables to hold the GPIOs that the LED and sensor are connected to. We’re connecting the sensor to GPIO 22 and the LED to GPIO 21, but you can use any other suitable Raspberry Pi Pico pins.

Then, create variables that will allow you to set a timer to turn the LED off after motion is detected.

// Timer: Auxiliar variables
long now = millis();
long lastTrigger = 0;
boolean startTimer = false;

The now variable holds the current time. The lastTrigger variable holds the time when the PIR sensor detects motion. The startTimer is a boolean variable that starts the timer when motion is detected.

detectsMovement()

The detectsMovement() function will be called when motion is detected by the PIR sensor. It turns on the LED, sets the startTimer flag to true (so that the time starts counting), and records the current time as the lastTrigger time.

// Runs when motion is detected, sets LED HIGH and starts a timer
void detectsMovement() {
  digitalWrite(led, HIGH);
  startTimer = true;
  lastTrigger = millis();
}

setup()

In the setup(), start by initializing the Serial port at 115200 baud rate.

Serial.begin(115200);

Set the PIR Motion sensor as an INPUT_PULLUP.

pinMode(motionSensor, INPUT_PULLUP);

To set the PIR sensor pin as an interrupt, use the attachInterrupt() function as described earlier.

attachInterrupt(digitalPinToInterrupt(motionSensor), detectsMovement, RISING);

The pin that will detect motion is GPIO 22 and it will call the function detectsMovement() on RISING mode.

The LED is an OUTPUT whose state starts at LOW.

pinMode(led, OUTPUT);
digitalWrite(led, LOW);

loop()

The loop() function is constantly running over and over again. In every loop, the now variable is updated with the current time.

now = millis();

Nothing else is done in the loop(). But, when motion is detected, the detectsMovement() function is called because we’ve set an interrupt previously on the setup().

The detectsMovement() function, as we’ve mentioned previously prints a message in the Serial Monitor, turns the LED on, sets the startTimer boolean variable to true, and updates the lastTrigger variable with the current time.

void IRAM_ATTR detectsMovement() {
  Serial.println("MOTION DETECTED!!!");
  digitalWrite(led, HIGH);
  startTimer = true;
  lastTrigger = millis();
}

After this step, the code goes back to the loop().

This time, the startTimer variable is true. So, when the time defined in seconds has passed (since motion was detected), the following if statement will be true.

if(startTimer && (now - lastTrigger > (timeSeconds*1000))) {
  Serial.println("Motion stopped...");
  digitalWrite(led, LOW);
  startTimer = false;
}

The “Motion stopped…” message will be printed in the Serial Monitor, the LED is turned off, and the startTimer variable is set to false.

And that’s a summary of how the code works.

Uploading the Code to the Raspberry Pi Pico

For you to be able to upload code to the Raspberry Pi Pico, it needs to be in bootloader mode.

If the Raspberry Pi is currently running MicroPython firmware, you need to manually put it into bootloader mode. For that, connect the Raspberry Pi Pico to your computer while holding the BOOTSEL button at the same time.

Raspberry Pi Pico Bootloader mode

For future uploads using Arduino IDE, the board should go automatically into bootloader mode without the need to press the BOOTSEL button.

Now, select your COM port in Tools > Port. It may be the case that the COM port is grayed out. If that’s the case, don’t worry it will automatically find the port once you hit the upload button.

Raspberry Pi Pico - COM port not found Arduino IDE

Upload the code.

Arduino 2.0 Upload Button

You should get a success message.

Done uploading Arduino IDE Raspberry Pi Pico

Demonstration

After uploading the code, open the Serial Monitor at a baud rate of 115200. Now, test your setup. Wave your hand in front of the motion sensor. It will print “MOTION DETECTED!” in the Serial Monitor.

And the LED will light up for 10 seconds (or whatever value you defined on the timeSeconds variable).

Raspberry Pi Pico PIR Motion Sensor Detect Motion Demonstration Example

After 10 seconds (or the specified time), the LED will turn off and you’ll get a “Motion stopped…” message.

Detect Motion Raspberry Pi Pico Arduino IDE

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!

2 thoughts on “Raspberry Pi Pico: Detect Motion using a PIR Sensor (Arduino IDE)”

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.