Skip to content

Instantly share code, notes, and snippets.

@ctrlcctrlv
Last active May 27, 2024 00:15
Show Gist options
  • Save ctrlcctrlv/5d75f1d8e7bb1dd32183074d06e3bbac to your computer and use it in GitHub Desktop.
Save ctrlcctrlv/5d75f1d8e7bb1dd32183074d06e3bbac to your computer and use it in GitHub Desktop.
Pimoroni Inky buttons example program (in C)
#include <gpiod.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <poll.h>
#define CHIP_BLOCKS 8
#define MAX_LINES 4
#define EVENT_BUF_SIZE 1024
struct gpiod_chip **open_gpio_chips(int *num_chips) {
struct gpiod_chip **chips = calloc(CHIP_BLOCKS, sizeof(struct gpiod_chip *));
for (int i = 0;; i++) {
char path[EVENT_BUF_SIZE];
snprintf(path, sizeof(path), "/dev/gpiochip%d", i);
printf("Opening GPIO №%d…\n", i);
chips[i] = gpiod_chip_open(path);
if (!chips[i])
break;
(*num_chips)++;
if (i > 0 && *num_chips % CHIP_BLOCKS == 0) {
chips = realloc(chips, sizeof(struct gpio_chip *) * (i / CHIP_BLOCKS + 1));
}
}
return chips;
}
const unsigned int INKY_BUTTONS[] = {5, 6, 16, 24};
enum {
INKY_BUTTON_A = 5,
INKY_BUTTON_B = 6,
INKY_BUTTON_C = 16,
INKY_BUTTON_D = 24,
INKY_BUTTON_INVALID = -1
};
const char INKY_GPIO_CONSUMER[] = "inky_buttons_example";
#define INKY_DEBOUNCE_PERIOD_MICROS 250
struct gpiod_line_request **request_lines(struct gpiod_chip **chips, int num_chips, int *num_requests, struct pollfd *pollfds, const unsigned int offsets[]) {
struct gpiod_line_request **requests = calloc(num_chips, sizeof(struct gpiod_line_request *));
for (int i = 0; i < num_chips; i++) {
struct gpiod_line_config *line_cfg = gpiod_line_config_new();
struct gpiod_line_settings *settings = gpiod_line_settings_new();
struct gpiod_request_config *req_cfg = gpiod_request_config_new();
if (!line_cfg || !settings || !req_cfg) {
perror("Failed to allocate line config structures");
exit(1);
}
gpiod_request_config_set_consumer(req_cfg, INKY_GPIO_CONSUMER);
struct gpiod_chip_info *chip_info = gpiod_chip_get_info(chips[i]);
gpiod_line_settings_set_edge_detection(settings, GPIOD_LINE_EDGE_BOTH);
gpiod_line_settings_set_debounce_period_us(settings, INKY_DEBOUNCE_PERIOD_MICROS);
gpiod_line_settings_set_bias(settings, GPIOD_LINE_BIAS_PULL_UP);
gpiod_line_config_add_line_settings(line_cfg, offsets, MAX_LINES, settings);
struct gpiod_line_request *req;
printf("Requesting lines on chip %d\n", i);
req = gpiod_chip_request_lines(chips[i], req_cfg, line_cfg);
if (!req) {
fprintf(stderr, "Could not request lines (at chip %d)\n", i);
continue;
}
requests[*num_requests] = req;
pollfds[*num_requests].fd = gpiod_line_request_get_fd(req);
pollfds[*num_requests].events = POLLIN;
(*num_requests)++;
gpiod_request_config_free(req_cfg);
gpiod_line_settings_free(settings);
gpiod_line_config_free(line_cfg);
}
return requests;
}
void print_event(int offset, enum gpiod_edge_event_type type) {
printf("Inky Button ");
switch (offset) {
case INKY_BUTTON_A:
printf("A");
break;
case INKY_BUTTON_B:
printf("B");
break;
case INKY_BUTTON_C:
printf("C");
break;
case INKY_BUTTON_D:
printf("D");
break;
default:
perror("Unrecognized button?");
}
if (type == GPIOD_EDGE_EVENT_FALLING_EDGE) {
printf(" pressed.\n");
} else {
printf(" released.\n");
}
}
void listen_for_events(struct gpiod_chip **chips, struct gpiod_line_request **requests, int num_requests, struct pollfd *pollfds) {
struct gpiod_edge_event_buffer *event_buffer = gpiod_edge_event_buffer_new(EVENT_BUF_SIZE);
if (!event_buffer) {
perror("Event buffer alloc failed");
exit(1);
}
printf("Listening… Press A, B, C or D.\n");
while (1) {
poll(pollfds, num_requests, -1);
for (int i = 0; i < num_requests; i++) {
if (pollfds[i].revents & POLLIN) {
struct gpiod_edge_event *event;
int num_events;
num_events = gpiod_line_request_read_edge_events(requests[i], event_buffer, EVENT_BUF_SIZE);
if (num_events < 0) {
perror("Failed to read events");
continue;
}
for (int j = 0; j < num_events; j++) {
event = gpiod_edge_event_buffer_get_event(event_buffer, j);
int offset = gpiod_edge_event_get_line_offset(event);
enum gpiod_edge_event_type type = gpiod_edge_event_get_event_type(event);
print_event(offset, type);
}
}
}
}
}
int main(int argc, char **argv) {
struct gpiod_chip **chips;
struct gpiod_line_request **requests;
struct pollfd pollfds[8];
int num_chips = 0, num_requests = 0;
chips = open_gpio_chips(&num_chips);
requests = request_lines(chips, num_chips, &num_requests, pollfds, INKY_BUTTONS);
listen_for_events(chips, requests, num_requests, pollfds);
return 0;
}
all: buttons_example
buttons_example: inky_buttons.c
clang -std=gnu17 -I/opt/include -L/usr/lib -L/opt/lib -static -static-libgcc -o $@ $< -l:libgpiod.a -O3 -flto=full -g
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment