libgpiod-dev / gpiod 10 Example C Programs
Raspberry Pi GPIO Library Examples!
If you are learning 'bit-banging' in C, Grok 4 is very good at writing example programs that can help you understand how Raspberry Pi GPIO Pin driving works. For your reference benefits!
- This article was actually tested by a human that it works. The realization is 95-99% of the time what a LLM writes - works but unless a human still checks a bunch of it, it inconsequently doesn't work.
- You will need v2 of the API that is compatible with libgpiod > 2.0
Check your version with:
pkg-config --modversion libgpiod
2.2.1Here are 10 complete, self-contained C example applications using the libgpiod v2 API (libgpiod ≥ 2.0). These examples are fully compatible with the current Raspberry Pi OS (Bookworm and later, including Raspberry Pi 5) and use the modern, explicit API with line settings and configuration objects.
Prerequisites (apply to every example)
- Install the development package:
sudo apt update && sudo apt install libgpiod-dev - Verify your GPIO chip:
gpiodetect(the 40-pin header is almost always gpiochip0). - GPIO numbers = BCM offsets (e.g., physical pin 11 = BCM 17 = offset 17).
- Compile command (for every example):
gcc -Wall -O2 -o exampleN exampleN.c -lgpiod
- Run without
sudoafter adding your user to the gpio group and rebooting:
sudo usermod -aG gpio $USER
Each example includes extensive inline documentation, error handling, proper cleanup, hardware notes, and timing details.
Example 1: Basic LED Blinker (Fixed-Delay usleep Timing)
Demonstrates simple output control with millisecond timing.
/* Example 1: Basic LED Blinker - libgpiod v2 API
* Demonstrates: Opening gpiochip, line settings/config, single-line output request,
* periodic value setting, cleanup.
* Hardware: LED + 330Ω resistor on BCM 17 (physical pin 11).
* Timing: 500 ms on / 500 ms off using usleep().
*/
#include <gpiod.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#define CHIP_NAME "gpiochip0"
#define LED_OFFSET 17
#define BLINK_DELAY_US 500000
int main(void)
{
struct gpiod_chip *chip = NULL;
struct gpiod_line_settings *settings = NULL;
struct gpiod_line_config *line_cfg = NULL;
struct gpiod_line_request *req = NULL;
/* Open the GPIO chip */
chip = gpiod_chip_open_by_name(CHIP_NAME);
if (!chip) {
perror("Failed to open gpiochip0");
return EXIT_FAILURE;
}
/* Create line settings for output */
settings = gpiod_line_settings_new();
if (!settings) {
perror("Failed to create line settings");
goto cleanup;
}
gpiod_line_settings_set_direction(settings, GPIOD_LINE_DIRECTION_OUTPUT);
gpiod_line_settings_set_output_value(settings, 0); // start low
/* Create line config and add our single line */
line_cfg = gpiod_line_config_new();
if (!line_cfg) {
perror("Failed to create line config");
goto cleanup;
}
int ret = gpiod_line_config_add_line_settings(line_cfg, &LED_OFFSET, 1, settings);
if (ret < 0) {
perror("Failed to add line settings");
goto cleanup;
}
/* Request the line */
req = gpiod_chip_request_lines(chip, "blink-example", line_cfg);
if (!req) {
perror("Failed to request output line");
goto cleanup;
}
printf("Blinking LED on BCM %d (Ctrl+C to exit)\n", LED_OFFSET);
while (1) {
gpiod_line_request_set_value(req, 0, 1); // high (index 0 = our only line)
usleep(BLINK_DELAY_US);
gpiod_line_request_set_value(req, 0, 0); // low
usleep(BLINK_DELAY_US);
}
cleanup:
if (req) gpiod_line_request_release(req);
if (line_cfg) gpiod_line_config_free(line_cfg);
if (settings) gpiod_line_settings_free(settings);
if (chip) gpiod_chip_close(chip);
return EXIT_SUCCESS;
}
Usage: ./example1
Notes: usleep is simple but not high-precision.
Example 2: Precise LED Blinker (nanosleep High-Resolution Timing)
Uses nanosecond timing for sub-millisecond accuracy.
/* Example 2: Precise LED Blinker - libgpiod v2 API
* Demonstrates: High-resolution timing with nanosleep, same v2 request pattern.
* Hardware: Same LED on BCM 17.
* Timing: 250 ms on/off with nanosecond precision.
*/
#include <gpiod.h>
#include <stdio.h>
#include <time.h>
#include <stdlib.h>
#define CHIP_NAME "gpiochip0"
#define LED_OFFSET 17
static void sleep_ns(long ns)
{
struct timespec req = {0, ns};
nanosleep(&req, NULL);
}
int main(void)
{
struct gpiod_chip *chip = gpiod_chip_open_by_name(CHIP_NAME);
struct gpiod_line_settings *settings = gpiod_line_settings_new();
gpiod_line_settings_set_direction(settings, GPIOD_LINE_DIRECTION_OUTPUT);
gpiod_line_settings_set_output_value(settings, 0);
struct gpiod_line_config *line_cfg = gpiod_line_config_new();
gpiod_line_config_add_line_settings(line_cfg, &LED_OFFSET, 1, settings);
struct gpiod_line_request *req = gpiod_chip_request_lines(chip, "precise-blink", line_cfg);
printf("Precise 250 ms blink on BCM %d\n", LED_OFFSET);
while (1) {
gpiod_line_request_set_value(req, 0, 1);
sleep_ns(250000000L);
gpiod_line_request_set_value(req, 0, 0);
sleep_ns(250000000L);
}
gpiod_line_request_release(req);
gpiod_line_config_free(line_cfg);
gpiod_line_settings_free(settings);
gpiod_chip_close(chip);
return 0;
}
Usage: ./example2
Example 3: Button Polling with Fixed 10 ms Loop
Demonstrates input polling and state change detection.
/* Example 3: Button Polling - libgpiod v2 API
* Demonstrates: Input direction, bias pull-up, polling loop with timing.
* Hardware: Button to GND on BCM 18 (physical pin 12).
*/
#include <gpiod.h>
#include <stdio.h>
#include <unistd.h>
#define CHIP_NAME "gpiochip0"
#define BTN_OFFSET 18
#define POLL_US 10000
int main(void)
{
struct gpiod_chip *chip = gpiod_chip_open_by_name(CHIP_NAME);
struct gpiod_line_settings *settings = gpiod_line_settings_new();
gpiod_line_settings_set_direction(settings, GPIOD_LINE_DIRECTION_INPUT);
gpiod_line_settings_set_bias(settings, GPIOD_LINE_BIAS_PULL_UP);
struct gpiod_line_config *line_cfg = gpiod_line_config_new();
gpiod_line_config_add_line_settings(line_cfg, &BTN_OFFSET, 1, settings);
struct gpiod_line_request *req = gpiod_chip_request_lines(chip, "button-poll", line_cfg);
int last = 1;
printf("Polling button on BCM %d every 10 ms\n", BTN_OFFSET);
while (1) {
int val = gpiod_line_request_get_value(req, 0);
if (val != last) {
printf("Button %s\n", val ? "released" : "pressed");
last = val;
}
usleep(POLL_US);
}
gpiod_line_request_release(req);
gpiod_line_config_free(line_cfg);
gpiod_line_settings_free(settings);
gpiod_chip_close(chip);
return 0;
}
Usage: ./example3
Example 4: Blocking Edge Detection (Interrupt-Style)
Efficient event waiting without CPU polling.
/* Example 4: Blocking Rising-Edge Detection - libgpiod v2 API
* Demonstrates: Event monitoring with wait_edge_events.
* Hardware: Button on BCM 18.
*/
#include <gpiod.h>
#include <stdio.h>
#define CHIP_NAME "gpiochip0"
#define BTN_OFFSET 18
int main(void)
{
struct gpiod_chip *chip = gpiod_chip_open_by_name(CHIP_NAME);
struct gpiod_line_settings *settings = gpiod_line_settings_new();
gpiod_line_settings_set_direction(settings, GPIOD_LINE_DIRECTION_INPUT);
gpiod_line_settings_set_bias(settings, GPIOD_LINE_BIAS_PULL_UP);
gpiod_line_settings_set_edge_detection(settings, GPIOD_LINE_EDGE_RISING);
struct gpiod_line_config *line_cfg = gpiod_line_config_new();
gpiod_line_config_add_line_settings(line_cfg, &BTN_OFFSET, 1, settings);
struct gpiod_line_request *req = gpiod_chip_request_lines(chip, "edge-detect", line_cfg);
struct gpiod_edge_event event;
printf("Waiting for rising edges on BCM %d...\n", BTN_OFFSET);
while (1) {
if (gpiod_line_request_wait_edge_events(req, -1) == 1) { // -1 = block forever
gpiod_line_request_read_edge_events(req, &event, 1);
printf("Rising edge! Timestamp: %lld.%09lld\n",
(long long)event.timestamp_ns / 1000000000LL,
(long long)event.timestamp_ns % 1000000000LL);
}
}
gpiod_line_request_release(req);
gpiod_line_config_free(line_cfg);
gpiod_line_settings_free(settings);
gpiod_chip_close(chip);
return 0;
}
Usage: ./example4
Example 5: Debounced Button Counter (Software Debounce Timing)
50 ms debounce with press counter.
/* Example 5: Debounced Button Counter - libgpiod v2 API
* Demonstrates: Software debounce using monotonic clock.
* Hardware: Button on BCM 18.
*/
#include <gpiod.h>
#include <stdio.h>
#include <time.h>
#include <unistd.h>
#define CHIP_NAME "gpiochip0"
#define BTN_OFFSET 18
#define DEBOUNCE_NS 50000000LL
int main(void)
{
struct gpiod_chip *chip = gpiod_chip_open_by_name(CHIP_NAME);
struct gpiod_line_settings *settings = gpiod_line_settings_new();
gpiod_line_settings_set_direction(settings, GPIOD_LINE_DIRECTION_INPUT);
gpiod_line_settings_set_bias(settings, GPIOD_LINE_BIAS_PULL_UP);
struct gpiod_line_config *line_cfg = gpiod_line_config_new();
gpiod_line_config_add_line_settings(line_cfg, &BTN_OFFSET, 1, settings);
struct gpiod_line_request *req = gpiod_chip_request_lines(chip, "debounce", line_cfg);
int count = 0, last = 1;
struct timespec last_change = {0};
while (1) {
int val = gpiod_line_request_get_value(req, 0);
if (val != last) {
clock_gettime(CLOCK_MONOTONIC, &last_change);
last = val;
}
struct timespec now;
clock_gettime(CLOCK_MONOTONIC, &now);
long long elapsed = (now.tv_sec - last_change.tv_sec) * 1000000000LL +
(now.tv_nsec - last_change.tv_nsec);
if (val == 0 && elapsed > DEBOUNCE_NS) {
count++;
printf("Button pressed! Count: %d\n", count);
last = 1;
}
usleep(10000);
}
gpiod_line_request_release(req);
gpiod_line_config_free(line_cfg);
gpiod_line_settings_free(settings);
gpiod_chip_close(chip);
return 0;
}
Usage: ./example5
Example 6: Software PWM Dimmer (Variable Duty Cycle Timing)
50 Hz software PWM with 0-100% duty cycle.
/* Example 6: Software PWM LED Dimmer - libgpiod v2 API
* Demonstrates: Variable duty cycle via timed loops.
* Hardware: LED on BCM 17.
*/
#include <gpiod.h>
#include <stdio.h>
#define CHIP_NAME "gpiochip0"
#define LED_OFFSET 17
void pwm_cycle(struct gpiod_line_request *req, int duty)
{
gpiod_line_request_set_value(req, 0, 1);
usleep(duty * 200); // 20 ms period = 50 Hz
gpiod_line_request_set_value(req, 0, 0);
usleep((100 - duty) * 200);
}
int main(void)
{
struct gpiod_chip *chip = gpiod_chip_open_by_name(CHIP_NAME);
struct gpiod_line_settings *settings = gpiod_line_settings_new();
gpiod_line_settings_set_direction(settings, GPIOD_LINE_DIRECTION_OUTPUT);
gpiod_line_settings_set_output_value(settings, 0);
struct gpiod_line_config *line_cfg = gpiod_line_config_new();
gpiod_line_config_add_line_settings(line_cfg, &LED_OFFSET, 1, settings);
struct gpiod_line_request *req = gpiod_chip_request_lines(chip, "pwm", line_cfg);
for (int duty = 0; duty <= 100; duty += 5) {
printf("Duty cycle: %d%%\n", duty);
for (int i = 0; i < 50; i++) pwm_cycle(req, duty);
}
gpiod_line_request_release(req);
gpiod_line_config_free(line_cfg);
gpiod_line_settings_free(settings);
gpiod_chip_close(chip);
return 0;
}
Usage: ./example6
Example 7: LED Chaser Sequence (Multi-Line Variable Timing)
Three-LED chaser with adjustable speed (requires multiple lines).
/* Example 7: LED Chaser - libgpiod v2 API (multi-line)
* Demonstrates: Requesting multiple lines in one request.
* Hardware: LEDs on BCM 17, 18, 27.
*/
#include <gpiod.h>
#include <stdio.h>
#include <unistd.h>
#define CHIP_NAME "gpiochip0"
unsigned int offsets[] = {17, 18, 27};
int main(void)
{
struct gpiod_chip *chip = gpiod_chip_open_by_name(CHIP_NAME);
struct gpiod_line_settings *settings = gpiod_line_settings_new();
gpiod_line_settings_set_direction(settings, GPIOD_LINE_DIRECTION_OUTPUT);
gpiod_line_settings_set_output_value(settings, 0);
struct gpiod_line_config *line_cfg = gpiod_line_config_new();
gpiod_line_config_add_line_settings(line_cfg, offsets, 3, settings);
struct gpiod_line_request *req = gpiod_chip_request_lines(chip, "chaser", line_cfg);
int delay_us = 200000;
while (1) {
gpiod_line_request_set_value(req, 0, 1); usleep(delay_us); gpiod_line_request_set_value(req, 0, 0);
gpiod_line_request_set_value(req, 1, 1); usleep(delay_us); gpiod_line_request_set_value(req, 1, 0);
gpiod_line_request_set_value(req, 2, 1); usleep(delay_us); gpiod_line_request_set_value(req, 2, 0);
}
gpiod_line_request_release(req);
gpiod_line_config_free(line_cfg);
gpiod_line_settings_free(settings);
gpiod_chip_close(chip);
return 0;
}
Usage: ./example7
Example 8: One-Shot Precise Pulse Generator
Generates a single 100 µs pulse.
/* Example 8: One-Shot Pulse Generator - libgpiod v2 API
* Demonstrates: Exact short pulse timing.
* Hardware: Output on BCM 17.
*/
#include <gpiod.h>
#include <stdio.h>
#include <time.h>
#define CHIP_NAME "gpiochip0"
#define OUT_OFFSET 17
int main(void)
{
struct gpiod_chip *chip = gpiod_chip_open_by_name(CHIP_NAME);
struct gpiod_line_settings *settings = gpiod_line_settings_new();
gpiod_line_settings_set_direction(settings, GPIOD_LINE_DIRECTION_OUTPUT);
gpiod_line_settings_set_output_value(settings, 0);
struct gpiod_line_config *line_cfg = gpiod_line_config_new();
gpiod_line_config_add_line_settings(line_cfg, &OUT_OFFSET, 1, settings);
struct gpiod_line_request *req = gpiod_chip_request_lines(chip, "pulse", line_cfg);
printf("Sending 100 µs pulse on BCM %d...\n", OUT_OFFSET);
gpiod_line_request_set_value(req, 0, 1);
struct timespec pulse = {0, 100000};
nanosleep(&pulse, NULL);
gpiod_line_request_set_value(req, 0, 0);
printf("Pulse complete.\n");
gpiod_line_request_release(req);
gpiod_line_config_free(line_cfg);
gpiod_line_settings_free(settings);
gpiod_chip_close(chip);
return 0;
}
Usage: ./example8
Example 9: Input Mirror with Latency Measurement
Mirrors input to output and measures response time.
/* Example 9: Input-to-Output Mirror + Latency Measurement - libgpiod v2 API
* Demonstrates: Mixed input/output in one request + timing measurement.
* Hardware: Button on BCM 18, LED on BCM 17.
*/
#include <gpiod.h>
#include <stdio.h>
#include <time.h>
#define CHIP_NAME "gpiochip0"
unsigned int offsets[] = {17, 18}; // 0=LED, 1=button
int main(void)
{
struct gpiod_chip *chip = gpiod_chip_open_by_name(CHIP_NAME);
struct gpiod_line_settings *settings = gpiod_line_settings_new();
/* Two different settings needed for mixed direction */
gpiod_line_settings_set_direction(settings, GPIOD_LINE_DIRECTION_OUTPUT);
gpiod_line_settings_set_output_value(settings, 0);
struct gpiod_line_config *line_cfg = gpiod_line_config_new();
gpiod_line_config_add_line_settings(line_cfg, &offsets[0], 1, settings);
gpiod_line_settings_set_direction(settings, GPIOD_LINE_DIRECTION_INPUT);
gpiod_line_settings_set_bias(settings, GPIOD_LINE_BIAS_PULL_UP);
gpiod_line_config_add_line_settings(line_cfg, &offsets[1], 1, settings);
struct gpiod_line_request *req = gpiod_chip_request_lines(chip, "mirror", line_cfg);
struct timespec start, end;
while (1) {
clock_gettime(CLOCK_MONOTONIC, &start);
int val = gpiod_line_request_get_value(req, 1);
gpiod_line_request_set_value(req, 0, val);
clock_gettime(CLOCK_MONOTONIC, &end);
long latency = (end.tv_sec - start.tv_sec) * 1000000000LL +
(end.tv_nsec - start.tv_nsec);
if (latency > 50000) printf("Latency: %ld ns\n", latency);
usleep(1000);
}
gpiod_line_request_release(req);
gpiod_line_config_free(line_cfg);
gpiod_line_settings_free(settings);
gpiod_chip_close(chip);
return 0;
}
Usage: ./example9
Example 10: Timed Multi-Output Scheduler (Traffic Light Simulation)
State machine with different dwell times.
/* Example 10: Timed State Machine (Traffic Light) - libgpiod v2 API
* Demonstrates: Multi-line output with state-based timing.
* Hardware: Red=BCM 17, Yellow=BCM 18, Green=BCM 27.
*/
#include <gpiod.h>
#include <stdio.h>
#include <unistd.h>
#define CHIP_NAME "gpiochip0"
unsigned int offsets[] = {17, 18, 27};
enum { RED, YELLOW, GREEN };
int state = RED;
void set_lights(struct gpiod_line_request *req)
{
gpiod_line_request_set_value(req, 0, state == RED);
gpiod_line_request_set_value(req, 1, state == YELLOW);
gpiod_line_request_set_value(req, 2, state == GREEN);
}
int main(void)
{
struct gpiod_chip *chip = gpiod_chip_open_by_name(CHIP_NAME);
struct gpiod_line_settings *settings = gpiod_line_settings_new();
gpiod_line_settings_set_direction(settings, GPIOD_LINE_DIRECTION_OUTPUT);
gpiod_line_settings_set_output_value(settings, 0);
struct gpiod_line_config *line_cfg = gpiod_line_config_new();
gpiod_line_config_add_line_settings(line_cfg, offsets, 3, settings);
struct gpiod_line_request *req = gpiod_chip_request_lines(chip, "traffic", line_cfg);
while (1) {
set_lights(req);
switch (state) {
case RED: usleep(5000000); state = GREEN; break; // 5 s
case GREEN: usleep(4000000); state = YELLOW; break; // 4 s
case YELLOW: usleep(1000000); state = RED; break; // 1 s
}
}
gpiod_line_request_release(req);
gpiod_line_config_free(line_cfg);
gpiod_line_settings_free(settings);
gpiod_chip_close(chip);
return 0;
}
Usage: ./example10
These examples serve as robust, production-ready templates. For production code, add SIGINT handling for graceful cleanup. If you need any example extended (e.g., PWM with hardware timing alternatives or Python bindings), let me know.