---
title: GPIO & Real Boards
description: This tutorial explains how to use GPIO in RIOT to control LEDs or read button presses.
code_folder: examples/guides/gpio/
---

import Contact from '@components/contact.astro';
import WorkingVideo from './img/gpio/05_working_video.mp4';
import ButtonVideo from './img/gpio/08_buttons.mp4';

So far we have been running all our code using the `arduino-feather-nrf52840-sense` board.

In this tutorial, we will learn how to use the GPIO pins on a real board
to control LEDs or read button presses.

For this tutorial we will be using a the `arduino-feather-nrf52840-sense` board
with the [Teamagochi PCB](https://github.com/smartuni/teamagochi),
but you can use any board that has GPIO pins.

## Step 1: Configuring our Board

<Contact />

First, we need to inform RIOT about the board we are using.

To do this we adapt the `BOARD` variable in our `Makefile` to the board we are using.

```make
BOARD ?= arduino-feather-nrf52840-sense
```

:::tip
If you ever want to just quickly test something
on a board without having to change the `Makefile`,
you can also set the `BOARD` variable in the terminal before running `make`.
```bash
BOARD=arduino-feather-nrf52840-sense make all
```

![The board variable in Visual Studio Code](img/gpio/01_define_board.png)

First we want to make sure that we can build the code for the board.
To do this we can simply run `make all` in the terminal,
this will build the code for the board and check if everything is set up correctly.

If everything is set up correctly,
we should now be able to move on to flashing the board.

:::tip
It might return something like:

```
/bin/sh: line 1: arm-none-eabi-gcc: command not found
Compiler arm-none-eabi-gcc is required but not found in PATH.  Aborting.
```

This means that you need to install the ARM toolchain,
you can do this by running `sudo apt install gcc-arm-none-eabi` on Ubuntu
or `sudo pacman -S arm-none-eabi-gcc` on Arch Linux.

If it still doesn't work,
consider running it via the Docker container using `BUILD_IN_DOCKER=1 make flash`.

Please refer to the [Flashing a RIOT Application](/getting-started/flashing)
guide for more information.
:::

Try flashing the board with `make flash` and see if it works,
it should still execute the same code as before when running RIOT natively on your
own hardware but now using the actual board.

![Flash output in Visual Studio Code](img/gpio/02_flash_output.png)

Now if we type `make term` we should see the output of the board in the terminal.
Make sure that you have the board connected to your computer via USB and
that your user has the necessary permissions to access the serial port.

:::tip
On Arch Linux, you might need to add your user to the `uucp` group
to access the serial port.

```bash
sudo usermod -a -G uucp $USER
```
:::

## Step 2: Controlling LEDs

Now that we have the board working, let's try to control the LEDs on the board.

The exact pins that control the LEDs might vary depending on the board you are using,
but in the case of the `arduino-feather-nrf52840-sense` board,
the LED would be connected on Port 1 Pin 9.

You can often find these defines either in the board schematics or in the board
configuration headers in RIOT.

First we need to include the necessary modules in our projects `Makefile`.

```make
# Add the gpio module to the build
USEMODULE += periph_gpio
USEMODULE += periph_gpio_irq

# Enable the milliseconds timer.
USEMODULE += ztimer
USEMODULE += ztimer_msec
```

![The Makefile with the GPIO modules](img/gpio/03_gpio_modules.png)

This allows us to both control the GPIO pins and timers.

Specifically, we need the `periph_gpio` module to control the GPIO pins,
`periph_gpio_irq` to handle GPIO interrupts,
and `ztimer` to use the timer functionality for blinking the LED.
In this case, we also need the `ztimer_msec` module to use the millisecond timer.

Now we need to actually define the pin that we want to control in our code.
To do this include the following lines **before** the `main` function.
{/*<!--skip ci-->*/}
```c
#include "board.h"
#include "periph/gpio.h"
#include "ztimer.h"

/* Define the LED0 pin and mode */
gpio_t led0 = GPIO_PIN(1, 9);
gpio_mode_t led0_mode = GPIO_OUT;
```

Now we can control the LED. First we initialize the pin
and afterwards we turn the LED off by clearing the pin.

{/*<!--skip ci-->*/}
```c
int main(void) {
  /* Initialize the LED0 pin */
  gpio_init(led0, led0_mode);
  /* Turn off the LED0 pin */
  gpio_clear(led0);

  /* Loop forever */
  while (1) {

  }
}
```

Turning the LED off when the board starts is quite boring,
so let's make it blink by adding a delay and toggling the LED.

```c
  /* Loop forever */
  while (1) {
    /* Toggle the LED0 pin every 500 milliseconds */
    gpio_toggle(led0);
    ztimer_sleep(ZTIMER_MSEC, 500);
  }
```

![The Code in Visual Studio Code](img/gpio/04_code.png)

If we now `make flash` and then `make term` we should see the LED blinking.

<video controls>
  <source src={WorkingVideo} type="video/mp4" />
  Your browser does not support the video tag.
</video>

## Step 3: Reading Button Presses

If you remember what we did in the timers tutorial,
we can use quite similar code to read button presses.

On a constrained device you usually don't want to poll the button state,
which is why we will use an interrupt to detect the button press,
that way we can drastically reduce the power consumption of the device.


First we need to define the callback function
that will be called when the button is pressed.

```c title="Define the button callback function"
/* This callback function will be called when the button state changes */
void button_callback(void *arg) {
  /* the argument is not used */
  (void)arg;

  /* Toggle the LED1 pin based on the button state */
  if (gpio_read(button)) {
    gpio_clear(led1);
  } else {
    gpio_set(led1);
  }
}
```

Now we need to define the button and led1 pin and mode and initialize it.

```c {1-6, 27-33}
/* Define the LED1 pin and mode */
gpio_t led1 = GPIO_PIN(1, 10);
gpio_mode_t led1_mode = GPIO_OUT;

/* Define the button pin */
gpio_t button = GPIO_PIN(1, 2);

/* This callback function will be called when the button state changes */
void button_callback(void *arg) {
  /* the argument is not used */
  (void)arg;

  /* Toggle the LED1 pin based on the button state */
  if (gpio_read(button)) {
    gpio_clear(led1);
  } else {
    gpio_set(led1);
  }
}

int main(void) {
  /* Initialize the LED0 pin */
  gpio_init(led0, led0_mode);
  /* Turn off the LED0 pin */
  gpio_clear(led0);

  /* Initialize the LED1 pin */
  gpio_init(led1, led1_mode);
  /* Turn off the LED1 pin */
  gpio_clear(led1);

  /* Initialize the button pin */
  gpio_init_int(button, GPIO_IN_PU, GPIO_BOTH, button_callback, NULL);

  /* Loop forever */
  while (1) {
    /* Toggle the LED0 pin every 500 milliseconds */
    gpio_toggle(led0);
    ztimer_sleep(ZTIMER_MSEC, 500);
  }
}
```

This code will initialize the button pin and call the `button_callback`
function whenever the button is pressed.

![Full Code in Visual Studio Code](img/gpio/07_full_code.png)

If we now `make flash` and then `make term` we should see the LED turn on
when the button is pressed.

:::tip
If you are using the `arduino-feather-nrf52840-sense` board,
the button is on the back of the board.

Be careful to not press the reset button by mistake 😉
:::

<video controls>
  <source src={ButtonVideo} type="video/mp4" />
  Your browser does not support the video tag.
</video>

## Extra: Board Specific Defines

Most boards (if they have LEDs or buttons) will have
specific defines for the GPIO pins that are used.
For example, the `arduino-feather-nrf52840-sense` board has the `LED0_TOGGLE`
define that allows you to toggle the LED without having to define the pin yourself.

In most cases using gpio like we did in this tutorial is the best way to go,
it's more flexible, documented and standardized for all boards.
However, using these defines can often be easier at the start,
esp. when the GPIO pins are not documented well or the board support is not complete yet.

Using this define, we can simplify our code to the following:

{/*<!--skip ci-->*/}
```c title="Using the LED0_TOGGLE define"
int main(void) {
  /* Initialize the LED0 pin */
  gpio_init(led0, led0_mode);
  /* Turn off the LED0 pin */
  gpio_clear(led0);

  /* Loop forever */
  while (1) {
    /* Toggle the LED0 pin every 500 milliseconds */
    LED0_TOGGLE;
    ztimer_sleep(ZTIMER_MSEC, 500);
  }
}
```

## Conclusion

In this tutorial we learned how to use the GPIO pins on a real board to
control LEDs and read button presses.

This is a very basic example, but it should give you a good starting point
to build more complex applications.

<Contact />

:::note
The source code for this tutorial can be found
[HERE](https://github.com/RIOT-OS/RIOT/tree/master/examples/guides/gpio).

If your project is not working as expected,
you can compare your code with the code in this repository to see if you missed anything.
:::
