Prérequis:

systemd , service ,daemon

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