Raspberry Pi Pico Interrupts & Button Interfacing Tutorial Using MicroPython.

In this article, we shall discuss how to interface push buttons with Raspberry Pi Pico RP2040 and trigger interrupts. We can interface a push-button using either Polling or Interrupt. Here, you can learn the difference between the two methods and which one to use. We will also learn about debouncing a button to avoid unwanted inputs. The code in this article is written in MicroPython. This guide works for all variants of Raspberry Pi Pico including the Raspberry Pi Pico W.


Buttons act as input devices for a microcontroller and we can read their status. A button connected to the positive supply voltage of a microcontroller will be read as logical-high(1) and a button attached to the ground will be read as logical-low(0). Raspberry Pi Pico is powered by the RP2040 microcontroller, which has 36 multi-functional General Purpose Input / Output (GPIO) pins. All GPIO pins of Raspberry Pi Pico can be configured as either input or output. All GPIO pins can also be configured as external interrupts.

Polling Vs interrupt

Polling and Interrupts are the two methods used to interface external switches such as push-buttons, keypads, Numpad, etc. Polling is used when the application is not time sensitive. Interrupts are used when a device needs the attention of a microcontroller instantly.

Polling is when the CPU executes code sequentially to check if any change in state has occurred.In case of Interrupts, a device or a register notifies the CPU that it needs immediate attention.
CPU takes care of Polling.An interrupt handler handles interrupts.
Polling occurs at regular intervals due to the sequential execution of code.Interrupts can occur at any time.
Polling Vs Interrupt

Interrupts are handled by parts of software called Interrupt Service Routine(ISR). When an interrupt occurs, the CPU starts executing code within this routine. When the task in the routine is completed, the processor continues executing code from where it left off.

Interrupt Handling Diagram

Prerequisites For This Tutorial

  • A Raspberry Pi Pico running MicroPython.
  • Push button(such as a momentary tactile switch).
  • Connecting wires and breadboard.

Your Raspberry Pi Pico needs to be preloaded with a MicroPython UF2 file to program it in MicroPython. You can read our getting started guide for Raspberry Pi Pico where we show all the steps required to start programming Raspberry Pi Pico(RP2040) in MicroPython.

Read Buttons Using Polling In Raspberry Pi Pico(RP2040).

Let us first look at the polling method of reading a button. Connect a pushbutton to your Pico as shown in the diagram below.

Schematic of Raspberry Pi Pico with push-button. Designed in Fritzing.
Designed using Fritzing

The Raspberry Pi Pico has 26 easily accessible GPIOs which is explained in our in-depth article Raspberry Pi Pico Pinout Guide. You can use any of these GPIO pins to connect push buttons. Here is the pin diagram of Raspberry Pi Pico W for reference.

raspberry pi pico w pinout
Raspberry Pi Pico W Pinout. Source: Datasheet

We shall write a script to detect if the button is HIGH or LOW. Pico can pull a pin to the positive voltage with an internal resistor. Why do we need a pullup resistor? Because without the pull-up configured, a pin will give noisy input as it will be in a floating state.

With the connections done as shown in the schematic above, connect your Pico to your computer and upload the following script:

from machine import Pin
pin = Pin(5, Pin.IN, Pin.PULL_UP)
while True:
    if pin.value()==0:
        print("Button Pressed")
        print("Count={}".format(counter))Code language: Python (python)

Save the file on Raspberry Pi Pico with a ‘.py’ extension such as ‘main.py’.

save as main.py in Thonny

Upon pressing the button, you should see an output as shown below:

Alternatively, if you wish to detect a button press when the button is HIGH instead of LOW, use PULL_DOWN instead of PULL_UP and modify your code as shown below:

from machine import Pin
pin = Pin(5, Pin.IN, Pin.PULL_DOWN)
while True:
    if pin.value() is 1:
        print("Button Pressed")
        print("Count={}".format(counter))Code language: Python (python)

Code Explained:

  • We import the Pin class from the machine module which is required to interact with the GPIO pins. Then we create a variable called counter to keep count of how many times the switch has been pressed.
from machine import Pin
counter=0Code language: JavaScript (javascript)
  • An object called pin is created which takes 3 arguments. We designate GPIO 5 as an input with PULL_UP enabled.
pin = Pin(5, Pin.IN, Pin.PULL_UP)Code language: Python (python)
  • Inside the while loop, we see if pin.value() is equal to 0, which denotes that the pin has been connected to the ground(i.e the pushbutton is pressed). If a button press is detected, we increment the counter variable and print the value of the counter.
if pin.value() is 1:
        print("Button Pressed")
        print("Count={}".format(counter))Code language: Python (python)

Also read: Arduino vs MicroPython vs CircuitPython: Which One Will You Choose?


Debouncing A Pin In Raspberry Pi Pico

Did you notice any shortcomings in the script we wrote above? A single button press increments the counter by many counts. This is due to the nature of mechanical contacts in a button and is called Bouncing. Debouncing is a process of eliminating Bouncing by using software or hardware. For example, a resistor-capacitor combination can be used for debouncing a switch. In the case of software, the time elapsed between two consecutive button presses is measured, and if multiple inputs occur within a certain time interval, only one input is registered.

To implement debouncing, we need to modify our script so that button presses are detected with a small amount of delay in between. For this, we shall use the time module in MicroPython. It contains the function time.ticks_ms() which is described as “Returns an increasing millisecond counter with an arbitrary reference point, that wraps around after some value.” So the function returns a random value of time (in milliseconds) that increments up to a certain value, and we can use it to measure the time interval between two events.

Upload this modified script and save the file on Raspberry Pi Pico with a ‘.py’ filename extension:

from machine import Pin
import time
pin = Pin(5, Pin.IN, Pin.PULL_UP)
while True:
    if ((pin.value() is 0) and (time.ticks_ms()-debounce_time) > 300):
        print("Button Pressed")
        print("Count={}".format(counter))Code language: Python (python)

We import the time module and create a variable called debounce_time. Then, we check if the time elapsed since the last button press is more than 300 milliseconds, using (time.ticks_ms()-debounce_time) > 300). If this condition is satisfied, then we increment the button press counter and set the debounce_time to the current value returned by time.ticks_ms(). This is very similar to using the millis() function in Arduino.

External Interrupts In Raspberry Pi Pico(RP2040)

All GPIO pins in Raspberry Pi Pico support interrupts. The interrupts can be classified into three types:

  • Level High: An interrupt occurs when a pin is HIGH or at logic 1.
  • Level Low: An interrupt occurs when a pin is LOW or at logic 0.
  • Rising Edge: Interrupt occurs when a pin transitions from a LOW to HIGH.
  • Falling Edge: Interrupt occurs when a pin transitions from HIGH to LOW.

The level interrupts in Pico do not latch, i.e. the interrupt becomes inactive as soon the GPIO changes its state from HIGH to LOW or vice versa.

Also read: Raspberry Pi Pico Serial Communication Example(MicroPython)

Using Push-Buttons To Trigger Raspberry Pi Pico Interrupts

Let us now try to interface a pushbutton using an interrupt. For this example, we shall use the Falling Edge(or Edge Low) of a pin voltage to trigger an interrupt. The connections will be the same as we used in the previous example for Polling a button input i.e. connect a pushbutton between GP5 and GND pin.

  • In Thonny IDE, create a new project and upload the following interrupt example script to your Pi Pico.
from machine import Pin

pin = Pin(5,Pin.IN,Pin.PULL_UP)
def callback(pin):
    global interrupt_flag

pin.irq(trigger=Pin.IRQ_FALLING, handler=callback)
while True:
    if interrupt_flag is 1:
        print("Interrupt has occured")
        interrupt_flag=0Code language: Python (python)
  • Press the Run icon or F5 to run the script.
  • Save the script into your Pico as interrupt.py or any other name with a “.py” filename extension.
Thonny Save to

When the script runs, you must see the line “Interrupt has occurred” displayed in your Thonny shell window when the pushbutton is pressed.

External Interrupt Code Explained

We first import the Pin class and initialize a global variable called interrupt_flag to 0. We will use this variable to keep track of the occurrence of interrupts. Global variables in MicroPython can be accessed by all functions. Then we create a pin object of the Pin class. GPIO 5 is set as an input with PULL_UP enabled.

from machine import Pin
pin = Pin(5,Pin.IN,Pin.PULL_UP)Code language: Python (python)

Next, we define a function called callback() to handle interrupts. The code inside this function should not perform any complex task, as it needs to hand over CPU usage to the main program quickly. We set the interrupt_flag variable as 1. Note that we need to use the global keyword when we change a global variable inside a function.

 def callback(pin):
    global interrupt_flag
    interrupt_flag=1Code language: Python (python)

To attach the interrupt to the pin, we use irq() function. This function takes two arguments :

trigger : trigger can be of the following types-

(1) Pin.IRQ_FALLING– interrupt on falling edge.

(2) Pin.IRQ_RISING– interrupt on rising edge.

(3) Pin.IRQ_LOW_LEVEL – interrupt on LOW level.(Not supported in MicroPython for Pico at the time of publishing this article.)

(4) Pin.IRQ_HIGH_LEVEL - interrupt on HIGH level.(Not supported in MicroPython for Pico at the time of publishing this article.)

(5) Pin.IRQ_FALLING | Pin.IRQ_RISING – interrupt on both the rising edge and the falling edge.

handler : handler specifies the function which will be called when an interrupt occurs.

pin.irq(trigger=Pin.IRQ_FALLING, handler=callback)Code language: Python (python)

In the while loop, we continuously check the value of the interrupt_flag variable. If this value is detected as ‘1’, that means an interrupt has occurred. We reset the variable to ‘0’ so that the variable can be set again when an interrupt occurs.

You might have noticed that the line “Interrupt has occurred” is printed in multiple lines with just a single button press. This is similar to the button-bouncing case that we discussed earlier in this article. We shall solve this problem in the next step.

Using External Interrupt To Toggle An LED Using MicroPython

In the following example let us try to toggle the onboard LED of the Raspberry Pi Pico, and debounce the external interrupt button input. The code should be self-explanatory as if we have already discussed similar code earlier in this article.

# Source: Electrocredible.com, Language: MicroPython
from machine import Pin
import time
pin = Pin(5, Pin.IN, Pin.PULL_UP)
led = Pin("LED", Pin.OUT)

def callback(pin):
    global interrupt_flag, debounce_time
    if (time.ticks_ms()-debounce_time) > 500:
        interrupt_flag= 1

pin.irq(trigger=Pin.IRQ_FALLING, handler=callback)

while True:
    if interrupt_flag is 1:
        print("Interrupt Detected")
        led.toggle()Code language: Python (python)

Now, when the pushbutton is pressed, the onboard LED must change between ON and OFF states.

Polling or Interrupt? Which One Should I Use?

If you are interfacing external devices that are time-sensitive, you should use interrupts. If your program is simple, you can use polling to interface buttons. Some microcontrollers have few external interrupt-compatible pins. If you have to interface many buttons, such as a keypad, you sometimes have to opt for polling instead of using interrupts.

I hope you found this article on using external interrupts in Raspberry Pi Pico to be useful. Please share your thoughts in the comments below. Thank you for reading.

Also read:





4 responses to “Raspberry Pi Pico Interrupts & Button Interfacing Tutorial Using MicroPython.”

  1. Afzal Avatar

    Excellent tutorial on the Interrupts. I look forward to see in C++ for Pi Pico.

  2. Steve Avatar

    Good explanation but I think to avoid confusion it might be better to use a different name for pin, such as Button, since that is what the external physical function you are monitoring is.
    pin = Pin(5, Pin.IN, Pin.PULL_UP) is very software speak but
    Button = Pin(5, Pin.IN, Pin.PULL_UP) is more beginner understandable and separates the physical pin from the Pin function.

  3. David Avatar

    I came upon this article while chasing down noise on my PICO project. It would be nice indeed to have a command such as Pin.IRQ_LOW_LEVEL or Pin.IRQ_HIGH_LEVEL as this would help with stray electrical noise that could be viewed by the PICO as a rising or falling edge. However, these triggers are not available in the PICO command set. Perhaps they are available in other RPi products. Simply getting up from a chair with synthetic fiber in winter will trigger the GPIO on the PICO (i.e. you don’t even have to touch anything close to the PICO.)

  4. Ron Keno Avatar
    Ron Keno

    Please port your Pico Interrupt Routine for use with the Arduino IDE 2.2.1.

    Thank you.

Leave a Reply

Your email address will not be published. Required fields are marked *