Skip to content

Button

In this lesson we will hook a single momentary push button up to our Raspberry Pi Nano. We will use it to toggle the built-in LED. We will start out with simply polling the button 10 times a second to check it's state. Then we will show how to use an interrupt handler function to monitor events from the button.

Momentary Button Press

In the example above, we are connecting the button on the left to the lower-left corner pin of the Raspberry Pi Pico. This is GPIO Pin 15 and is in row number 20 of our breadboard.

Momentary Switch Buttons

Momentary Switch

We use "B3F" tactile switch buttons that can be mounted directly on our breadboards. When the button is pressed, it connects a wire that joins two pins on one side to the two pins on the other side. The buttons can be mounted directly over the trough in the center of the breadboard. They typically cost under $2 for 10 buttons or about 20 cents per button.

Momentary Switch Internal Connection Diagram Here are the internal connections within the switch.

Momentary Switch External Connection Diagram This is the connection diagram that shows how the button is connected to the GPIO connector in the lower-left corner of the Raspberry Pi Pico. This corresponds to GP15 or Pin #15 in our code.

Sample Button Polling Code

Here is our fist example that uses a simple "watching" loop to check if the button value has change 10 times per second. In this case, the built-in LED is connected to pin 25.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
from machine import Pin
import time

# GPIO is the internal built-in LED
led = Pin(25, Pin.OUT)
# input on the lower left of the Pico using a built-in pull-down resistor to keep the value from floating
button = Pin(15, Pin.IN, Pin.PULL_DOWN) 

while True:
    if button.value(): # if the value changes
        led.toggle()
        time.sleep(0.1) # wait 1/10th of a second

Interrupt Handler Version

Although the polling version is simple, it does take a lot of the CPU resources. The button.value() is checked 10 times a second, even though the button might only be pressed once a day!

A more efficient version uses a strategy called an interrupt handler. This is a function that is "registered" by micropython to handel external events such as a button press.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
# Use an interrupt function count the number of times a button has been pressed
from machine import Pin
import micropython
import time

# global value
button_pressed_count = 0

# Interrupt Service Routine for Button Pressed Events - with no debounce
def button1_pressed(change):
    global button_pressed_count
    button_pressed_count += 1

button1 = Pin(14, Pin.IN, Pin.PULL_DOWN)
button1.irq(handler=button1_pressed, trigger=Pin.IRQ_FALLING)

button_pressed_count_old = 0
while True:
    if button_pressed_count_old != button_pressed_count:
       print('Button 1 value:', button_pressed_count)
       button_pressed_count_old = button_pressed_count

Interrupt Handler with a Debounce Feature

One of the problems with most switches is that they don't turn on and off perfectly each time. As the connection is getting close to closing some electrons jump the gap and the switch appears to turn on for a few microseconds. So to a computer, this looks like someone quickly pressing a button rapidly until it is firmly closed or completely open. This intermediate stage between completely open and closed is called the "bounce" stage of a switch opening and closing.

Debounce Transition

To remove this problem and get a clean signal, we can use either a hardware solution (wiring a capacitor to remove the high frequency noise) or we can be clever and solve the problem with a few extra lines of code.

The secret is to setup a timer when the switch is first closed or opened. We then ignore all the crazy stuff that happens for about 1/5th of a second (200 milliseconds). By then we usually have a solid indication that the button is changing state and we can return the new value.

Here is a example of this "Debounce" code in MicroPython:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
import utime
from machine import Pin

# Sample Raspberry Pi Pico MicroPython button press example with a debounce delay value of 200ms in the interrupt handler

button_presses = 0 # the count of times the button has been pressed
last_time = 0 # the last time we pressed the button

builtin_led = machine.Pin(25, Pin.OUT)
# The lower left corner of the Pico has a wire that goes through the buttons upper left and the lower right goes to the 3.3 rail
button_pin = machine.Pin(14, machine.Pin.IN, machine.Pin.PULL_DOWN)

# This function gets called every time the button is pressed.  The parameter "pin" is not used.
def button_pressed_handler(pin):
    global button_presses, last_time
    new_time = utime.ticks_ms()
    # if it has been more that 1/5 of a second since the last event, we have a new event
    if (new_time - last_time) > 200: 
        button_presses +=1
        last_time = new_time

# now we register the handler function when the button is pressed
button_pin.irq(trigger=machine.Pin.IRQ_FALLING, handler = button_pressed_handler)

# This is for only printing when a new button press count value happens
old_presses = 0
while True:
    # only print on change in the button_presses value
    if button_presses != old_presses:
        print(button_presses)
        builtin_led.toggle()
        old_presses = button_presses

Two Button Example

This example uses two buttons. One adds one to a counter, and the other decrements the counter. In the IRQ we look for the string '''14''' in the incoming pin event string. If the string is present, then we increment the button_presses variable. If it is not, then we decrement the counter.

This example is useful whenever you have two buttons that control the mode of a microcontroller.

Note that you can tune the delay period. If you have clean buttons and you want to allow for fast double-click events you can lower the time. 200 milliseconds is good for low-cost noisy buttons that may have many spikes in the open and closing transitions.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
import utime
from machine import Pin

# Sample two button Raspberry Pi Pico MicroPython example
# with a debounce delay value of 200ms in the interrupt handler
# https://www.coderdojotc.org/micropython/basics/03-button/

# these are the pins in the lower-left corner (USB on top)
BUTTON_PIN_A = 14
BUTTON_PIN_B = 15

button_presses = 0 # the count of times the button has been pressed.  A is +1, B is -1
last_time = 0 # the last time we pressed the button

# we toggle the builtin LED to get visual feedback
builtin_led = machine.Pin(25, Pin.OUT)

# The lower left corner of the Pico has a wire that goes through the buttons upper left and the lower right goes to the 3.3 rail
button_a = machine.Pin(BUTTON_PIN_A, machine.Pin.IN, machine.Pin.PULL_DOWN)
button_b = machine.Pin(BUTTON_PIN_B, machine.Pin.IN, machine.Pin.PULL_DOWN)

# this is the interrupt callback handler
# get in and out quickly
def button_callback(pin):
    global button_presses, last_time
    new_time = utime.ticks_ms()
    # if it has been more that 1/5 of a second since the last event, we have a new event
    if (new_time - last_time) > 200:
        # print(pin)
        if '14' in str(pin):
            button_presses +=1
        else:
            button_presses -= 1
        last_time = new_time

# now we register the handler functions when either of the buttons is pressed
button_a.irq(trigger=machine.Pin.IRQ_FALLING, handler = button_callback)
button_b.irq(trigger=machine.Pin.IRQ_FALLING, handler = button_callback)

# This is for only printing when a new button press count value happens
old_presses = 0

print(button_presses)
while True:
    # only print on change in the button_presses value
    if button_presses != old_presses:
        print(button_presses)
        builtin_led.toggle()
        old_presses = button_presses

References

  1. Raspberry Pi Pico Getting Started Guide Lab 6
  2. YouTube Video
  3. Sample eBay List of Switches with trough pins
  4. Sample B3F Button on eBay 10 pieces for $1.50