Electronics Software Development

How To Use Buttons With Your Raspberry Pi (Using Python, CircuitPython, C, And C++)

Raspberry Pi Buttons Graphic
Written by John Woolsey

Skill Level: Beginner

Table Of Contents

Introduction

This tutorial will show you how to connect and use a button with your Raspberry Pi. It is meant as a follow-on beginner article to the Blink: Making An LED Blink On A Raspberry Pi tutorial (that described how to blink an LED on a Raspberry Pi using the Python and C/C++ programming languages with various libraries) and the Getting Started With CircuitPython On Raspberry Pi With Blinka tutorial (that described how to blink an LED using the CircuitPython language). In this tutorial, we will continue the journey by adding a button to control the LED used in those tutorials. I will first describe what a button is and how to connect it to your Raspberry Pi. Then I will provide two examples for each programming language and library combination.

  1. Turning on the LED only when the button is pressed, and
  2. Changing the LED’s blink rate on successive button presses.

If you haven’t already, please review those tutorials before proceeding with this one as we will be applying what we learned in those tutorials.

A basic understanding of electronics and programming is expected along with some familiarity with the Raspberry Pi platform. In addition, this tutorial uses a solderless breadboard to build a circuit from a schematic diagram. The All About Circuit’s Understanding Schematics, SparkFun’s How to Read a Schematic, Core Electronics’ How to Use Breadboards, and Science Buddies’ How to Use a Breadboard guides are good resources for learning how to translate a schematic to a breadboard.

The resources created for this tutorial are available on GitHub for your reference.

What Is Needed

Background Information

A typical switch, or button, is a mechanical device that conducts electricity through metal contacts when activated. There are many different types of switches (e.g. toggle, pushbutton, relay, rotary, etc.) used for various applications. They can be either normally-open (NO) or normally-closed (NC), meaning that the contacts are normally disconnected (does not conduct electricity) or connected (does conduct electricity) respectively. Multiple distinct switches can be combined into a single device that has multiple poles (separate switches) and/or multiple throws (separate conducting paths). For instance, a single-pole double-throw (SPDT) switch will conduct electricity in two distinct paths when the single switch is activated. Switches can also be constant (flip style) or momentary (only “active” while being depressed). Switches are considered “on” when they are conducting electricity and “off” when the contacts are disconnected. This tutorial will use a momentary normally-open single-pole single-throw pushbutton type of switch.

The contacts of mechanical switches “bounce” for a short time period after initial activation. This means that when the electrical contacts of a switch are first connected (or disconnected), they rapidly alternate, or bounce, between their “on” and “off” states until the switch settles down into its new stable state. Typical switches have bounce times of 5-20 milliseconds, but some can take longer. Bouncing can cause problems with switch sensing circuitry or microcontrollers that are trying to read the switch’s state. The sensing device can erroneously detect that a switch is in a different state than what was expected. A fast device can even detect multiple switch flips during the time a switch is bouncing. The process of eliminating this effect is called switch debouncing. Various methods can be used to implement debouncing. They can be implemented in hardware (e.g. digital logic gates or analog filters) or software (e.g. delayed readings or digital filters). Most microcontroller based projects use software based debouncing to conserve the cost of additional electrical components and is what we will use in this tutorial. Please see Switch on Wikipedia for more information about switches.

When reading a button, we are interested in knowing what state, “on” or “off”, the button is in so that we can apply its selection in our application. Generally, the “on” state is represented by a high voltage (power) level and the “off” state is represented by a low voltage (ground) level, but it can be the other way around. To implement the button state selection, circuit designers use a large pull-up (or pull-down) resistor to pull up (or down) the sensing terminal of a button to power (or ground) while tying the other button’s terminal directly to ground (or power). You know what they say, “a picture is worth a thousand words”, so let’s see how this works by looking at the typical pull-up and pull-down momentary pushbutton configurations shown below.

Schematic Diagram Of Typical Pull-Up And Pull-Down Pushbutton Configurations
Schematic Diagram Of Typical Pull-Up And Pull-Down Pushbutton Configurations

Here, we are looking at two types of momentary pushbuttons (normally-open and normally-closed) each used with both pull-up and pull-down resistors. The A) and D) circuits are both active low, meaning they present a low value to the GPIO pin while the button is being pressed. Specifically, A) is pulled high with resistor R1 while inactive and driven low when the SW1 momentary normally-open pushbutton is pressed. Conversely, D) is driven high while inactive and pulled low with resistor R4 when the SW4 momentary normally-closed pushbutton is pressed. The opposite is true for configurations B) and C).

While logically it would make sense to use active high buttons in our design, most microcontrollers incorporate pull-up resistors on their GPIO pins, but not necessarily pull-down resistors, and most designers use normally-open pushbuttons, so the active low A) configuration is the most typically used. The Raspberry Pi has both pull-up and pull-down resistors, of approximately 50 KΩ each, on most of its GPIO pins, but we will still use the typical A) choice in this tutorial using the integrated pull-up resistor within the Raspberry Pi’s microcontroller.

In addition, most pushbuttons utilize 4 pins, one on each corner of the button. This may sound strange since only two connections are required for switches, but they are intended to provide mechanical stability when the buttons are mounted to printed circuit boards or breadboards. Most buttons connect two pins together internally for each side of the integrated switch. The terminals protruding from one side of a button are typically both sides of the switch, meaning they are connected when the button is activated. Likewise, the terminals on the opposite side of the button also constitute a switch connection. This can sometimes be confusing and not all buttons are built the same, so it is usually good practice to consult the datasheet for the button or use a multimeter to check the connections for the button before incorporating it into your circuit.

My development system is the Raspberry Pi 3 Model B running the Raspberry Pi OS operating system. If you are using a different Raspberry Pi model or a different OS that is similar to Raspberry Pi OS, the vast majority of this tutorial should still apply, however, some minor changes may be necessary. If you need assistance with your particular setup, post a question in the comments section below and I, or someone else, can try to help you.

Building The Circuit

Before connecting any circuitry to your Raspberry Pi, shut it down and disconnect it from power. This avoids accidental damage during wiring.

Construct the button and LED circuit on a breadboard and connect it to your Raspberry Pi’s GPIO pins according to the schematic shown below. The LED connection is the same as what we used in the Blink: Making An LED Blink On A Raspberry Pi and Getting Started With CircuitPython On Raspberry Pi With Blinka tutorials. You just need to add the button, which is connected to BCM pin 5 (physical pin 29). Reminder, the anode (positive terminal) of an LED is longer than the cathode (negative terminal) if you are reconnecting the LED.

Schematic Diagram Of The Button And LED Circuit Connected To A Raspberry Pi
Schematic Diagram Of The Button And LED Circuit Connected To A Raspberry Pi

The circuit should look similar to the one shown below once completed.

Completed Button And LED Circuit Connected To A Raspberry Pi
Completed Button And LED Circuit Connected To A Raspberry Pi

Once the circuit is built, connect power to your Raspberry Pi and boot it up.

Using Python With The RPi.GPIO Library

Now, let’s move on to the programming phase. We will begin with the Python language using the RPi.GPIO library. As a reminder, we will be utilizing a lot of what we learned in each section of the Blink: Making An LED Blink On A Raspberry Pi tutorial. I don’t plan to go into all of the details of each program listed here, just those relevant to adding a button, so you may want to refer back to that tutorial for additional information not covered here. This section, for instance, corresponds to the Using Python With The RPi.GPIO Library section of that tutorial.

Open your favorite code editor and create a Python program named button_rpigpio_basic.py with the code shown below.

import RPi.GPIO as GPIO

BUTTON = 5
RED_LED = 21

def button_changed(button):
    if GPIO.input(button) == GPIO.LOW:
        print("Button pressed.")
        GPIO.output(RED_LED, GPIO.HIGH)
    else:
        print("Button released.")
        GPIO.output(RED_LED, GPIO.LOW)

GPIO.setmode(GPIO.BCM)
GPIO.setup(BUTTON, GPIO.IN, pull_up_down=GPIO.PUD_UP)
GPIO.setup(RED_LED, GPIO.OUT)
GPIO.output(RED_LED, GPIO.LOW)
GPIO.add_event_detect(BUTTON, GPIO.BOTH, callback=button_changed, bouncetime=10)
print("Press CTRL-C to exit.")
try:
    while True:
        pass
finally:
    GPIO.cleanup()

This example will turn on the LED when you press and hold the button and turn off the LED when you release the button.

We first import the library (line 1) and then define our pin connections to the button and LED (lines 3 and 4).

Lines 14-16 configure our pins for use. Line 14 tells the library that we are using the BCM pin numbering scheme (GPIO.BCM). Line 15 specifies the BUTTON pin as an input (GPIO.IN) using the microcontroller’s integrated pull-up resistor (GPIO.PUD_UP) on the Raspberry Pi’s GPIO pin. Line 16 specifies the RED_LED pin as an output (GPIO.OUT).

Line 17 sets the RED_LED pin low (GPIO.LOW) thereby turning off the LED so that we have a known initial state when starting our program.

The add_event_detect() method, used on line 18, is used to detect pin transitions and calls a specific function, defined in our program, when each transition is detected. Transitions can be specified as high-to-low (GPIO.FALLING), low-to-high (GPIO.RISING), or both (GPIO.BOTH). This method even allows us to specify a time for software based debouncing. For this program, we are specifying the detection of both rising and falling transitions (GPIO.BOTH) on the BUTTON pin, a callback function named button_changed(), and a debounce time of 10 ms.

When the button_changed() function (defined on lines 6-12) is called, it first reads the value of the button, passed into the function by the add_event_detect() method, to determine the button state and then prints a message and turns on or off the LED as appropriate for the button state change.

The try/finally exception block (defined on lines 20-24) runs an empty endless loop until a keyboard interrupt signal (CTRL-C) is received and then resets all GPIO pins back to their stable default states with the RPi.GPIO library’s cleanup() method before exiting the program.

If there is something that needs further explanation, please let me know in the comment section and I will try to answer your question.

Save your program when you are done editing and then open a terminal window and run your program.

$ python button_rpigpio_basic.py

The LED should turn on each time you press and hold the button and turn off whenever you release the button.

Press CTRL-C to exit the program when you are done.

Debouncing buttons is not an exact science. A software based debounce time of 10 milliseconds usually works well for most buttons and applications. However, If you find that you are getting some extra button pressed or released events, you may want to increase the bounce time to 50 ms or more. If the bounce time becomes too large, in the range of 200 ms or so, you may end up missing some events.

Let’s move on to the second example that will change the blink rate for the LED each time you press the button. Create a program named button_rpigpio_multifunction.py in your code editor with the code shown below.

from time import sleep
import RPi.GPIO as GPIO

BUTTON = 5
RED_LED = 21

quick_blink = False

def button_pressed(button):
    global quick_blink
    print("Button pressed.")
    quick_blink = not quick_blink

GPIO.setmode(GPIO.BCM)
GPIO.setup(BUTTON, GPIO.IN, pull_up_down=GPIO.PUD_UP)
GPIO.setup(RED_LED, GPIO.OUT)
GPIO.output(RED_LED, GPIO.LOW)
GPIO.add_event_detect(BUTTON, GPIO.FALLING, callback=button_pressed, bouncetime=10)
print("Press CTRL-C to exit.")
try:
    while True:
        GPIO.output(RED_LED, not GPIO.input(RED_LED))
        sleep(0.5) if quick_blink else sleep(1)
finally:
    GPIO.cleanup()

In this example, we follow the same process for configuring and initializing our pins as we did in the previous example, but this time we will call the button_pressed() function (defined on lines 9-12) instead that will toggle the blink rate (the quick_blink boolean global variable defined on line 7) each time the button is pressed. The endless loop (lines 21-23) was also modified to toggle the LED state and add a time delay that is dependent on the value of the quick_blink variable. Also notice that we had to import the time library (line 1) to use the sleep() method along with changing the add_event_detect() method (line 18) to use GPIO.FALLING instead of GPIO.BOTH since we are only interested in the button being pressed, not released.

Save and run your program.

$ python button_rpigpio_multifunction.py

The LED should begin blinking and then blink faster when you press the button. Each successive button press should alternate between fast and slow blinking.

Press CTRL-C to exit the program.

Now that you know how to incorporate a button into your design, play around with the code and try creating a new program that can perform different operations. For instance, try changing the blink rate only while the button is being pressed or providing three distinct operations on each button press, like 1) turning on the LED, 2) turning off the LED, and 3) blinking the LED.

Using Python With The GPIO Zero Library

In this section, we will continue to use the Python language, but switch to using the GPIO Zero library instead.

Create a Python program named button_gpiozero_basic.py with the code shown below. Like in the first example above, this one will turn on the LED when you press and hold the button and turn off the LED when you release the button.

from signal import pause
from gpiozero import Button, LED

button = Button(5)
red_led = LED(21)

def button_pressed():
    print("Button pressed.")
    red_led.on()

def button_released():
    print("Button released.")
    red_led.off()

red_led.off()
button.when_pressed = button_pressed
button.when_released = button_released
print("Press CTRL-C to exit.")
pause()

Line 2 imports the Button and LED component classes of the GPIO Zero library that we will use to control our button and LED.

Lines 4 and 5 define the button and red_led instances, along with their associated pins, that we will interact with in our program.

Lines 16 and 17 tell the button to execute either the button_pressed(), beginning on line 7, or button_released(), beginning on line 11, functions depending on whether it was pressed or released respectively.

We then pause() at the end of the program until CTRL-C is pressed.

Save and run your program.

$ python button_gpiozero_basic.py

The LED should turn on each time you press and hold the button and turn off whenever you release the button.

Press CTRL-C to exit the program.

Note, the GPIO Zero library’s Button class enables a pull-up resistor by default (that we utilized), but does not have a default debounce time. My experience showed that the program did not work as well when I added a bounce_time=0.01 argument to the button definition.

As you can see from this example, using the high level component classes, e.g. Button and LED, of the GPIO Zero library makes it much easier to work with components in your program. If we did not care about printing to the screen, we could have made our program even simpler. Check out the Button controlled LED section in the library’s documentation to find out how.

Now, let’s move on to blinking the LED. Create a program named button_gpiozero_multifunction.py with the code shown below that changes the blink rate for the LED each time you press the button.

from signal import pause
from gpiozero import Button, LED

button = Button(5)
red_led = LED(21)

quick_blink = False

def button_pressed():
    global quick_blink
    print("Button pressed.")
    quick_blink = not quick_blink
    red_led.blink(on_time=0.5, off_time=0.5) if quick_blink else red_led.blink()

red_led.off()
button.when_pressed = button_pressed
print("Press CTRL-C to exit.")
red_led.blink()
pause()

Here, we create our button and red_led objects as we did previously, but are only interested in the button being pressed this time. The button_pressed() function utilizes the quick_blink global variable, defined on line 7, to determine the blink rate that we use with the LED class’ useful blink() method as seen on line 13.

Line 18 initiates the default blinking state, 1 second on and 1 second off, of the LED upon starting the program.

Save and run your program.

$ python button_gpiozero_multifunction.py

The LED blink rate should change each time you press the button.

Press CTRL-C to exit the program.

Again, play around with the code to see what else you can do.

Using CircuitPython (Python With The Blinka Library)

This next section uses the CircuitPython language. Technically, it is still just Python with the Blinka library that enables CircuitPython constructs. CircuitPython is much more similar to using the RPi.GPIO library than the GPIO Zero library, but it has an extensive collection of separate component libraries, called bundles, for use with more advanced components. As a reminder, this section is a follow-on to the Getting Started With CircuitPython On Raspberry Pi With Blinka tutorial.

Before writing our CircuitPython program, we first need to install a couple of libraries that we will use in our program: the Blinka library and the Adafruit_CircuitPython_Debouncer library used to debounce buttons.

$ pip install adafruit-blinka
$ pip install adafruit-circuitpython-debouncer

Once the libraries are installed, we can create our button_blinka_basic.py program that will turn on and off the LED when you press and release the button.

import board
from digitalio import DigitalInOut, Direction, Pull
from adafruit_debouncer import Debouncer
import RPi.GPIO as GPIO

button_pin = DigitalInOut(board.D5)
red_led = DigitalInOut(board.D21)

button_pin.direction = Direction.INPUT
button_pin.pull = Pull.UP
button = Debouncer(button_pin)
red_led.direction = Direction.OUTPUT
red_led.value = False
print("Press CTRL-C to exit.")
try:
    while True:
        button.update()
        if button.fell:
            print("Button pressed.")
            red_led.value = True
        if button.rose:
            print("Button released.")
            red_led.value = False
finally:
    GPIO.cleanup()

Lines 1-4 import the libraries we are using in our program. We are even importing the RPi.GPIO library used in our first program. Can you guess why? To use its cleanup() method, of course.

Next, we move on to the pin definitions and configurations. Lines 6 and 7 define our button_pin and red_led instances. button_pin? Why did I use button_pin, you ask. Because it will be used with the Debouncer class (line 11) of the adafruit_debouncer library we imported on line 3 to create the better named button object that will be automatically debounced for us. The rest of the pin configurations, lines 9-13, set up the button_pin as an input (Direction.INPUT) using the microcontroller’s integrated pull-up resistor (Pull.UP) along with setting the red_led as an output (Direction.OUTPUT) with an initial value of False.

The endless loop (lines 16-23) first reads the value of the button (line 17) and then prints a message and turns on or off the LED depending on whether the button was pressed (fell on line 18) or released (rose on line 21).

Save and run your program.

$ python button_blinka_basic.py

The LED should turn on each time you press and hold the button and turn off whenever you release the button.

Press CTRL-C when you are done.

The next example implements our blink rate changing program named button_blinka_multifunction.py.

from time import monotonic
import board
from digitalio import DigitalInOut, Direction, Pull
from adafruit_debouncer import Debouncer
import RPi.GPIO as GPIO

button_pin = DigitalInOut(board.D5)
red_led = DigitalInOut(board.D21)

quick_blink = False
previous_blink_time = 0

button_pin.direction = Direction.INPUT
button_pin.pull = Pull.UP
button = Debouncer(button_pin)
red_led.direction = Direction.OUTPUT
red_led.value = False
print("Press CTRL-C to exit.")
try:
    while True:
        button.update()
        if button.fell:
            print("Button pressed.")
            quick_blink = not quick_blink
        current_time = monotonic()
        delay = 0.5 if quick_blink else 1
        if current_time - previous_blink_time >= delay:
            red_led.value = not red_led.value
            previous_blink_time = current_time
finally:
    GPIO.cleanup()

All of the pin setup is the same along with the addition and usage of the quick_blink variable (defined on line 10) as we would have expected. The difference this time is that instead of using delays with the sleep() method like we used in the RPi.GPIO version, we have to keep track of the delay (lines 26-27) and time (lines 11, 25, 27, and 29) ourselves so as not to block the reading of the button’s value (line 21). This is accomplished through the use of the monotonic() method of the time library imported on line 1. It is a running clock that cannot go backwards so that we can more easily make time comparisons.

Save and run your program.

$ python button_blinka_multifunction.py

The LED blink rate should change each time you press the button.

Press CTRL-C to exit the program.

Using C With The pigpio Library

Up until this point, we have been using the Python language in various ways to interact with the button and LED connected to the Raspberry Pi. In this section, we will turn our attention to using the C language with the pigpio library.

Open your code editor and create a C program named button_pigpio_basic.c with the code shown below.

#include <signal.h>
#include <stdio.h>
#include <pigpio.h>

const int Button = 5;
const int RedLED = 21;

volatile sig_atomic_t signal_received = 0;

void sigint_handler(int signal) {
   signal_received = signal;
}

void buttonChanged(int gpio, int level, uint32_t tick) {
   static uint32_t previousTimeButtonChanged = 0;
   uint32_t currentTime = tick;
   if (currentTime - previousTimeButtonChanged > 10000) {
      if (level == PI_LOW) {
         printf("Button pressed.\n");
         gpioWrite(RedLED, PI_HIGH);
      } else if (level == PI_HIGH) {
         printf("Button released.\n");
         gpioWrite(RedLED, PI_LOW);
      } else if (level == PI_TIMEOUT) {
         printf("Timeout occurred.\n");
      }
      previousTimeButtonChanged = currentTime;
   }
}

int main() {
   if (gpioInitialise() == PI_INIT_FAILED) {
      printf("ERROR: Failed to initialize the GPIO interface.\n");
      return 1;
   }
   gpioSetMode(Button, PI_INPUT);
   gpioSetPullUpDown(Button, PI_PUD_UP);
   gpioSetMode(RedLED, PI_OUTPUT);
   gpioWrite(RedLED, PI_LOW);
   gpioSetAlertFunc(Button, buttonChanged);
   signal(SIGINT, sigint_handler);
   printf("Press CTRL-C to exit.\n");
   while (!signal_received);
   gpioSetMode(RedLED, PI_INPUT);
   gpioTerminate();
   printf("\n");
   return 0;
}

This example will turn on the LED when you press and hold the button and turn off the LED when you release the button.

We first include the libraries (lines 1-3) and then define our pin connections to the button and LED (lines 5 and 6).

We also set up our signal interrupt handler and associated cleanup code, to exit cleanly when a CTRL-C keyboard interrupt is detected, on lines 8, 10-12, and 41-46, as we did in the Using C With The pigpio Library section of the Blink: Making An LED Blink On A Raspberry Pi tutorial.

Lines 36-38 configure our pins for use. Lines 36 and 37 specify the Button pin as an input (PI_INPUT) using the microcontroller’s integrated pull-up resistor (PI_PUD_UP) on the Raspberry Pi’s GPIO pin. Line 38 specifies the RedLED pin as an output (PI_OUTPUT).

Line 39 sets the RedLED pin low (PI_LOW) turning off the LED so that we have a known initial state when starting our program.

The gpioSetAlertFunc() method, used on line 40, is used to detect GPIO pin transitions and calls a specific function, defined in our program, when each transition is detected. Here, we are specifying that it watch the Button pin and call the buttonChanged() function when a transition occurs.

The buttonChanged() callback function, defined on lines 14-29, receives the GPIO pin whose state change was detected (gpio), the new state of the pin (level), and the time, in microseconds, that the pin transition occurred (tick). Unlike in the Python programs we used earlier, this time we have to take care of button debouncing ourselves. Lines 15-17 and 27 manage keeping track of the button transition times to ensure a button debounce time of 10 milliseconds (10000 microseconds as shown on line 17). We also check the new state (level) of the button (PI_LOW or PI_HIGH) and print a message and update the LED appropriately. The level parameter can also receive a timeout (PI_TIMEOUT) value where we just print a notification message to the screen if it occurs.

Save your program when you are done editing and then compile and run your program in a terminal window.

$ gcc -Wall -o button_pigpio_basic_c button_pigpio_basic.c -lpigpio
$ sudo ./button_pigpio_basic_c

The LED should turn on each time you press and hold the button and turn off whenever you release the button.

Press CTRL-C to exit the program when you are done.

The second example will change the blink rate for the LED each time you press the button. Create a program named button_pigpio_multifunction.c in your code editor with the code shown below.

#include <signal.h>
#include <stdio.h>
#include <pigpio.h>

const int Button = 5;
const int RedLED = 21;

volatile sig_atomic_t signal_received = 0;
int quick_blink = 0;

void sigint_handler(int signal) {
   signal_received = signal;
}

void buttonChanged(int gpio, int level, uint32_t tick) {
   static uint32_t previousTimeButtonChanged = 0;
   uint32_t currentTime = tick;
   if (currentTime - previousTimeButtonChanged > 10000) {
      if (level == PI_LOW) {
         printf("Button pressed.\n");
         quick_blink = (quick_blink) ? 0 : 1;
      } else if (level == PI_TIMEOUT) {
         printf("Timeout occurred.\n");
      }
      previousTimeButtonChanged = currentTime;
   }
}

int main() {
   if (gpioInitialise() == PI_INIT_FAILED) {
      printf("ERROR: Failed to initialize the GPIO interface.\n");
      return 1;
   }
   gpioSetMode(Button, PI_INPUT);
   gpioSetPullUpDown(Button, PI_PUD_UP);
   gpioSetMode(RedLED, PI_OUTPUT);
   gpioWrite(RedLED, PI_LOW);
   gpioSetAlertFunc(Button, buttonChanged);
   signal(SIGINT, sigint_handler);
   printf("Press CTRL-C to exit.\n");
   while (!signal_received) {
      gpioWrite(RedLED, !gpioRead(RedLED));
      (quick_blink) ? time_sleep(0.5) : time_sleep(1);
   }
   gpioSetMode(RedLED, PI_INPUT);
   gpioTerminate();
   printf("\n");
   return 0;
}

The large majority of this program is the same as the previous one. We just needed to add our quick_blink global variable (defined on line 9), update the buttonChanged() function to toggle the blink rate (line 21) only when the button is pressed, and update the endless loop (lines 41-44) to blink the LED depending on the state of the quick_blink variable.

Save, compile, and run your program.

$ gcc -Wall -o button_pigpio_multifunction_c button_pigpio_multifunction.c -lpigpio
$ sudo ./button_pigpio_multifunction_c

The LED should begin blinking and then blink faster when you press the button. Each successive button press should alternate between fast and slow blinking.

Press CTRL-C to exit the program.

Please review the library’s documentation for a complete description of the C language interface. It is quite extensive in the number of operations that it can handle.

Using C++ With The pigpio Library

In this section, we will continue using the pigpio library, but switch to using the C++ language instead.

These C++ programs are almost identical to the C programs used in the previous section. They have just been modified to use more typical C++ language constructs.

Create and save the button_pigpio_basic.cpp program

#include <csignal>
#include <iostream>
#include <pigpio.h>

const int Button = 5;
const int RedLED = 21;

volatile sig_atomic_t signal_received = 0;

void sigint_handler(int signal) {
   signal_received = signal;
}

void buttonChanged(int gpio, int level, uint32_t tick) {
   static uint32_t previousTimeButtonChanged = 0;
   uint32_t currentTime = tick;
   if (currentTime - previousTimeButtonChanged > 10000) {
      if (level == PI_LOW) {
         std::cout << "Button pressed." << std::endl;
         gpioWrite(RedLED, PI_HIGH);
      } else if (level == PI_HIGH) {
         std::cout << "Button released." << std::endl;
         gpioWrite(RedLED, PI_LOW);
      } else if (level == PI_TIMEOUT) {
         std::cout << "Timeout occurred." << std::endl;
      }
      previousTimeButtonChanged = currentTime;
   }
}

int main() {
   if (gpioInitialise() == PI_INIT_FAILED) {
      std::cout << "ERROR: Failed to initialize the GPIO interface." << std::endl;
      return 1;
   }
   gpioSetMode(Button, PI_INPUT);
   gpioSetPullUpDown(Button, PI_PUD_UP);
   gpioSetMode(RedLED, PI_OUTPUT);
   gpioWrite(RedLED, PI_LOW);
   gpioSetAlertFunc(Button, buttonChanged);
   signal(SIGINT, sigint_handler);
   std::cout << "Press CTRL-C to exit." << std::endl;
   while (!signal_received);
   gpioSetMode(RedLED, PI_INPUT);
   gpioTerminate();
   std::cout << std::endl;
   return 0;
}

and the button_pigpio_multifunction.cpp program.

#include <csignal>
#include <iostream>
#include <pigpio.h>

const int Button = 5;
const int RedLED = 21;

volatile sig_atomic_t signal_received = 0;
bool quick_blink = false;

void sigint_handler(int signal) {
   signal_received = signal;
}

void buttonChanged(int gpio, int level, uint32_t tick) {
   static uint32_t previousTimeButtonChanged = 0;
   uint32_t currentTime = tick;
   if (currentTime - previousTimeButtonChanged > 10000) {
      if (level == PI_LOW) {
         std::cout << "Button pressed." << std::endl;
         quick_blink = !quick_blink;
      } else if (level == PI_TIMEOUT) {
         std::cout << "Timeout occurred." << std::endl;
      }
      previousTimeButtonChanged = currentTime;
   }
}

int main() {
   if (gpioInitialise() == PI_INIT_FAILED) {
      std::cout << "ERROR: Failed to initialize the GPIO interface." << std::endl;
      return 1;
   }
   gpioSetMode(Button, PI_INPUT);
   gpioSetPullUpDown(Button, PI_PUD_UP);
   gpioSetMode(RedLED, PI_OUTPUT);
   gpioWrite(RedLED, PI_LOW);
   gpioSetAlertFunc(Button, buttonChanged);
   signal(SIGINT, sigint_handler);
   std::cout << "Press CTRL-C to exit." << std::endl;
   while (!signal_received) {
      gpioWrite(RedLED, !gpioRead(RedLED));
      (quick_blink) ? time_sleep(0.5) : time_sleep(1);
   }
   gpioSetMode(RedLED, PI_INPUT);
   gpioTerminate();
   std::cout << std::endl;
   return 0;
}

Only the highlighted lines are different from their C language counterparts. They mostly only relate to the differences in how the two languages include libraries and print to the screen. The only exception is that C++ provides a built-in boolean primitive datatype (bool) that we are using for the quick_blink variable instead of the integer datatype (int) specified for the C based version.

Compile and run your programs to see them in action

$ g++ -Wall -o button_pigpio_basic_cpp button_pigpio_basic.cpp -lpigpio
$ sudo ./button_pigpio_basic_cpp
$ g++ -Wall -o button_pigpio_multifunction_cpp button_pigpio_multifunction.cpp -lpigpio
$ sudo ./button_pigpio_multifunction_cpp

and then press CTRL-C when you are done.

Summary

In this tutorial, we learned how to connect and read a button to control an LED on a Raspberry Pi using various programming languages and libraries.

Specifically, we learned

  • about the different types of switches and buttons available,
  • how their electrical contacts bounce,
  • how to implement debouncing methods to reduce reading errors,
  • how to connect a button to your Raspberry Pi,
  • how to read a button in a Python program using the RPi.GPIO library,
  • how to read a button in a Python program using the GPIO Zero library,
  • how to read a button in a CircuitPython program (Python using Blinka library),
  • how to read a button in a C program using the pigpio library, and
  • how to read a button in a C++ program using the pigpio library.

We also implemented two programs for each language and library combination that

  1. turned on the LED when the button is pressed and turned off the LED when the button was released, and
  2. changed the blink rate of the LED each time the button was pressed.

The final source code used for this tutorial is available on GitHub. The GitHub versions of the code are fully commented to include additional information, such as the program’s description, circuit connections, and code clarifications.

Thank you for joining me on this journey and I hope you enjoyed the experience. Please feel free to share your thoughts or questions in the comments section below.

This tutorial is provided as a free service to our valued readers. Please help us continue this endeavor by considering a donation.

About the author

John Woolsey

John is an electrical engineer who loves science, math, and technology and teaching it to others even more.
 
He knew he wanted to work with electronics from an early age, building his first robot when he was in 8th grade. His first computer was a Timex/Sinclair 2068 followed by the Tandy 1000 TL (aka really old stuff).
 
He put himself through college (The University of Texas at Austin) by working at Motorola where he worked for many years afterward in the Semiconductor Products Sector in Research and Development.
 
John started developing mobile app software in 2010 for himself and for other companies. He has also taught programming to kids for summer school and enjoyed years of judging kids science projects at the Austin Energy Regional Science Festival.
 
Electronics, software, and teaching all culminate in his new venture to learn, make, and teach others via the Woolsey Workshop website.

2 Comments

Leave a Comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.