Prérequis:
Utilisation des GPIO (libgpiod) version inférieure 2 (bookworm)
But:
Piloter Gpio17 (c’est mon choix) , projet à but pédagogique pour les projets !
Programmation système Linux : Maîtrise des services systemd et gestion des démons
- Gestion des GPIO : Compréhension approfondie du contrôle des entrées/sorties
- Communication inter-processus : Utilisation des FIFO (named pipes) pour l’échange de données
- Scripting shell avancé : Développement de scripts robustes pour l’automatisation
Systèmes embarqués : Contrôle de dispositifs électroniques via interface unifiée
IoT et Domotique : Pilotage à distance de capteurs et actionneurs
Automation industrielle : Prototypage rapide de systèmes de contrôle
Interface Homme-Machine : Création d’interfaces web ou mobiles pour le contrôle GPIO
va permettre de gérer une led sur Gpio17 du RPI
echo 'a' > /tmp/gpio_fifo #allumer
echo 'e' > /tmp/gpio_fifo #eteint
echo 'c' > /tmp/gpio_fifo #clignote a 1 Hz
On dispose des sources du service ici
gpio_service_V3 au format .tar pour Debian Bookworm
Projet gpio_service
bruno@work:~/Works/gpio_service_V3 $ tree
.
├── control_gpio.sh
├── gpio_service
├── gpio-service.service
├── Makefile
├── README
└── src
└── gpio_service.c
2 directories, 6 files
gpio_service.c
/* ___ ____ __ __ ____ ____ ____ _ _ __ ___ ____
/ __)( _ \( )/ \ / ___)( __)( _ \/ )( \( )/ __)( __)
( (_ \ ) __/ )(( O )____ \___ \ ) _) ) /\ \/ / )(( (__ ) _)
\___/(__) (__)\__/(____)(____/(____)(__\_) \__/ (__)\___)(____)
(c)bogt 2025
*/
//#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <signal.h>
#include <string.h>
#include <sys/stat.h>
#include <errno.h>
#include <time.h>
#include <syslog.h>
#include <sys/select.h>
#include <gpiod.h>
#define FIFO_PATH "/dev/gpio_service"
// Variables GPIO
struct gpiod_chip *chip = NULL;
struct gpiod_line *line = NULL;
volatile sig_atomic_t keep_running = 1;
int blink_enabled = 0;
int current_state = 0;
time_t last_blink_time = 0;
// Fonction de debug
#ifdef DEBUG
#define DEBUG_LOG(fmt, ...) syslog(LOG_DEBUG, "DEBUG: " fmt, ##__VA_ARGS__)
#else
#define DEBUG_LOG(fmt, ...)
#endif
void signal_handler(int sig) {
(void)sig;
DEBUG_LOG("Signal %d reçu", sig);
syslog(LOG_INFO, "Signal de terminaison reçu");
keep_running = 0;
}
// Initialisation GPIO avec libgpiod
int init_gpio() {
DEBUG_LOG("Initialisation GPIO...");
chip = gpiod_chip_open_by_name("gpiochip0");
if (!chip) {
syslog(LOG_ERR, "Erreur: Impossible d'ouvrir gpiochip0");
return -1;
}
DEBUG_LOG("Chip GPIO ouvert");
line = gpiod_chip_get_line(chip, 17);
if (!line) {
syslog(LOG_ERR, "Erreur: Impossible d'obtenir GPIO17");
gpiod_chip_close(chip);
chip = NULL;
return -1;
}
DEBUG_LOG("Ligne GPIO17 obtenue");
if (gpiod_line_request_output(line, "gpio_service", 0) < 0) {
syslog(LOG_ERR, "Erreur: Impossible de configurer GPIO17 en sortie");
gpiod_chip_close(chip);
chip = NULL;
line = NULL;
return -1;
}
DEBUG_LOG("GPIO17 configurée en sortie");
syslog(LOG_INFO, "GPIO17 initialisée avec libgpiod");
return 0;
}
// Nettoyage GPIO
void cleanup_gpio() {
DEBUG_LOG("Nettoyage GPIO...");
if (line) {
gpiod_line_set_value(line, 0);
gpiod_line_release(line);
line = NULL;
}
if (chip) {
gpiod_chip_close(chip);
chip = NULL;
}
DEBUG_LOG("GPIO nettoyée");
}
// Version optimisée avec libgpiod
void set_gpio_state(int state) {
if (line) {
DEBUG_LOG("set_gpio_state(%d) appelé", state);
int ret = gpiod_line_set_value(line, state);
if (ret < 0) {
syslog(LOG_ERR, "Erreur écriture GPIO17: %d", ret);
} else {
current_state = state;
syslog(LOG_INFO, "GPIO17 → %s", state ? "ALLUMÉ" : "ÉTEINT");
DEBUG_LOG("GPIO17 écrite: %d, retour: %d", state, ret);
}
} else {
syslog(LOG_ERR, "GPIO17 non initialisée");
}
}
int read_gpio_state() {
if (line) {
int state = gpiod_line_get_value(line);
DEBUG_LOG("Lecture GPIO17: %d", state);
return state;
}
return -1;
}
void process_command(char cmd) {
DEBUG_LOG("Traitement commande: '%c' (ASCII: %d)", cmd, cmd);
switch(cmd) {
case 'a': // Allumer
blink_enabled = 0;
set_gpio_state(1);
break;
case 'e': // Éteindre
blink_enabled = 0;
set_gpio_state(0);
break;
case 'c': // Clignotant
blink_enabled = 1;
last_blink_time = time(NULL);
syslog(LOG_INFO, "GPIO17 → CLIGNOTANT");
DEBUG_LOG("Mode clignotant activé");
break;
case 's': // Status
{
int state = read_gpio_state();
if (state >= 0) {
syslog(LOG_INFO, "GPIO17 ÉTAT: %s", state ? "ALLUMÉ" : "ÉTEINT");
} else {
syslog(LOG_ERR, "Erreur lecture GPIO17");
}
}
break;
case 'q': // Quitter
keep_running = 0;
syslog(LOG_INFO, "ARRÊT demandé");
break;
default:
syslog(LOG_WARNING, "Commande inconnue: '%c'", cmd);
}
DEBUG_LOG("Commande '%c' traitée - blink_enabled=%d, current_state=%d",
cmd, blink_enabled, current_state);
}
int create_fifo() {
DEBUG_LOG("Création FIFO: %s", FIFO_PATH);
unlink(FIFO_PATH);
if (mkfifo(FIFO_PATH, 0666) == -1) {
syslog(LOG_ERR, "Erreur création FIFO: %m");
return -1;
}
if (chmod(FIFO_PATH, 0666) == -1) {
syslog(LOG_ERR, "Erreur chmod FIFO: %m");
unlink(FIFO_PATH);
return -1;
}
DEBUG_LOG("FIFO créé avec succès");
syslog(LOG_INFO, "FIFO créé: %s", FIFO_PATH);
return 0;
}
int main() {
// Configuration syslog
openlog("gpio-service", LOG_PID | LOG_CONS, LOG_DAEMON);
#ifdef DEBUG
syslog(LOG_INFO, "=== DÉMARRAGE SERVICE GPIO (DEBUG + libgpiod) ===");
DEBUG_LOG("Compilation DEBUG activée");
#else
syslog(LOG_INFO, "=== DÉMARRAGE SERVICE GPIO (libgpiod) ===");
#endif
// Configuration des signaux
signal(SIGINT, signal_handler);
signal(SIGTERM, signal_handler);
// Initialisation GPIO
if (init_gpio() != 0) {
syslog(LOG_ERR, "Échec initialisation GPIO");
closelog();
return 1;
}
// Création FIFO
if (create_fifo() != 0) {
cleanup_gpio();
closelog();
return 1;
}
// Initialisation état
set_gpio_state(0);
syslog(LOG_INFO, "Service initialisé - En attente de commandes");
// Ouverture FIFO
int fifo_fd = open(FIFO_PATH, O_RDONLY | O_NONBLOCK);
if (fifo_fd == -1) {
syslog(LOG_ERR, "Erreur ouverture FIFO: %m");
cleanup_gpio();
closelog();
return 1;
}
DEBUG_LOG("FIFO ouvert avec fd: %d", fifo_fd);
char buffer[10];
// Boucle principale
while (keep_running) {
DEBUG_LOG("Début boucle - blink_enabled=%d, current_state=%d",
blink_enabled, current_state);
// Gestion du clignotement
if (blink_enabled) {
time_t now = time(NULL);
if (now - last_blink_time >= 1) { // 1 seconde
current_state = !current_state;
DEBUG_LOG("Changement état clignotement: %d", current_state);
set_gpio_state(current_state);
last_blink_time = now;
}
}
// Configuration select() pour lecture non-bloquante
fd_set readfds;
FD_ZERO(&readfds);
FD_SET(fifo_fd, &readfds);
struct timeval timeout;
timeout.tv_sec = 0;
timeout.tv_usec = 100000; // 100ms
int ready = select(fifo_fd + 1, &readfds, NULL, NULL, &timeout);
if (ready > 0 && FD_ISSET(fifo_fd, &readfds)) {
// Données disponibles
ssize_t bytes_read = read(fifo_fd, buffer, sizeof(buffer)-1);
if (bytes_read > 0) {
buffer[bytes_read] = '\0';
DEBUG_LOG("%ld bytes lus: '%s'", bytes_read, buffer);
for (int i = 0; i < bytes_read; i++) {
if (buffer[i] != '\n' && buffer[i] != ' ' && buffer[i] != '\r') {
syslog(LOG_INFO, "Commande reçue: '%c'", buffer[i]);
process_command(buffer[i]);
}
}
}
} else if (ready == 0) {
DEBUG_LOG("Select timeout (pas de données)");
} else {
DEBUG_LOG("Select erreur: %s", strerror(errno));
}
}
// Nettoyage
DEBUG_LOG("Début nettoyage...");
close(fifo_fd);
unlink(FIFO_PATH);
cleanup_gpio();
syslog(LOG_INFO, "Service arrêté");
closelog();
return 0;
}
gpio-service.service
[Unit]
Description=GPIO Control Service (libgpiod)
After=multi-user.target
Wants=network.target
[Service]
Type=simple
ExecStart=/usr/local/bin/gpio_service
WorkingDirectory=/usr/local/bin
Restart=always
RestartSec=5
User=root
Group=root
# Sécurité
NoNewPrivileges=yes
PrivateTmp=yes
ProtectSystem=strict
ProtectHome=yes
# Logging
StandardOutput=journal
StandardError=journal
SyslogIdentifier=gpio-service
[Install]
WantedBy=multi-user.target
Makefile
# Configuration CC=gcc CFLAGS=-Wall -Wextra -std=gnu99 -D_GNU_SOURCE RELEASE_FLAGS=-O2 DEBUG_FLAGS=-g -O0 -DDEBUG LIBS=-lgpiod TARGET=gpio_service SRCDIR=src SOURCES=$(SRCDIR)/gpio_service.c BINDIR=/usr/local/bin SERVICEDIR=/etc/systemd/system # Cible par défaut (release) $(TARGET): $(SOURCES) $(CC) $(CFLAGS) $(RELEASE_FLAGS) -o $(TARGET) $(SOURCES) $(LIBS) # Cible debug debug: CFLAGS += $(DEBUG_FLAGS) debug: $(TARGET) @echo "🔧 Version DEBUG compilée (symboles: -g, optimisation: -O0)" # Cible release release: CFLAGS += $(RELEASE_FLAGS) release: $(TARGET) @echo "🚀 Version RELEASE compilée (optimisation: -O2)" # Installation install: $(TARGET) @echo "📦 Installation du service GPIO..." sudo install -d $(BINDIR) sudo install -m 755 $(TARGET) $(BINDIR)/ sudo install -m 644 gpio-service.service $(SERVICEDIR)/ sudo systemctl daemon-reload sudo systemctl enable gpio-service @echo "✅ Installation terminée" # Installation version debug install-debug: debug @echo "🐛 Installation version DEBUG..." sudo install -d $(BINDIR) sudo install -m 755 $(TARGET) $(BINDIR)/ sudo systemctl daemon-reload @echo "✅ Version DEBUG installée" # Désinstallation uninstall: @echo "🗑️ Désinstallation du service GPIO..." sudo systemctl stop gpio-service 2>/dev/null || true sudo systemctl disable gpio-service 2>/dev/null || true sudo rm -f $(BINDIR)/$(TARGET) sudo rm -f $(SERVICEDIR)/gpio-service.service sudo systemctl daemon-reload sudo rm -f /dev/gpio_service @echo "✅ Désinstallation terminée" # Nettoyage clean: rm -f $(TARGET) @echo "🧹 Fichiers de compilation nettoyés" # Installation et démarrage setup: install start start: sudo systemctl start gpio-service @echo "🚀 Service démarré" stop: sudo systemctl stop gpio-service @echo "🛑 Service arrêté" restart: stop start status: sudo systemctl status gpio-service --no-pager logs: sudo journalctl -u gpio-service -f # Debug avec GDB gdb-debug: debug @echo "🐛 Lancement avec GDB..." sudo systemctl stop gpio-service 2>/dev/null || true sudo gdb --args ./$(TARGET) gdb-attach: @echo "🔗 Attachement GDB au processus..." sudo gdb -p $(shell pgrep gpio_service) # Informations info: @echo "📋 Informations de compilation:" @echo " Cible: $(TARGET)" @echo " Compilateur: $(CC)" @echo " Flags: $(CFLAGS)" @echo " Librairies: $(LIBS)" @if file $(TARGET) | grep -q debug; then \ echo " Type: DEBUG"; \ else \ echo " Type: RELEASE"; \ fi .PHONY: all debug release install install-debug uninstall clean setup start stop restart status logs gdb-debug gdb-attach info
pour tester le service rapidement
control_gpio.sh
#!/bin/bash
FIFO="/dev/gpio_service"
SERVICE="gpio-service"
show_help() {
echo "Usage: $0 {on|off|blink|status|restart|stop|start|logs|debug|help}"
echo ""
echo " on, a - Allumer GPIO17 (libgpiod)"
echo " off, e - Éteindre GPIO17 (libgpiod)"
echo " blink, c - Mode clignotant (1s)"
echo " status, s - État GPIO17"
echo " restart - Redémarrer service"
echo " stop - Arrêter service"
echo " start - Démarrer service"
echo " logs - Voir les logs"
echo " debug - Logs debug"
echo " help - Cette aide"
}
case "$1" in
"on"|"a")
echo "a" > $FIFO
echo "💡 GPIO17 ALLUMÉ (libgpiod)"
;;
"off"|"e")
echo "e" > $FIFO
echo "⚫ GPIO17 ÉTEINT (libgpiod)"
;;
"blink"|"c")
echo "c" > $FIFO
echo "🔁 GPIO17 CLIGNOTANT"
;;
"status"|"s")
echo "s" > $FIFO
sleep 0.5
sudo journalctl -u $SERVICE -n 5 --no-pager | grep -E "(GPIO17 ÉTAT:|ALLUMÉ|ÉTEINT)"
;;
"restart")
sudo systemctl restart $SERVICE
echo "🔄 Service redémarré"
;;
"stop")
echo "q" > $FIFO 2>/dev/null || true
sleep 1
sudo systemctl stop $SERVICE
echo "🛑 Service arrêté"
;;
"start")
sudo systemctl start $SERVICE
echo "🚀 Service démarré"
;;
"logs")
sudo journalctl -u $SERVICE -n 15 --no-pager
;;
"debug")
sudo journalctl -u $SERVICE -p debug --no-pager
;;
"follow")
sudo journalctl -u $SERVICE -f
;;
"service-status")
sudo systemctl status $SERVICE --no-pager
;;
"help"|"--help"|"-h")
show_help
;;
*)
echo "❌ Commande invalide: $1"
show_help
exit 1
;;
esac
