La PWM sur GPIO :
Je ne vais pas expliquer ici ce que c'est, mais comment en générer une, en 2025 sur un Odroid-C2, matériel qui n'est plus supporté par le fabriquant Hardkernel.
- En effet plus aucun système d'exploitation n'est disponible officiellement, et ce n'est que grâce à la communauté DietPi qu'il est possible d'avoir un OS à jour avec des possibilités très intéressantes permettant de faire revivre ce µ-ordinateur… Formidable !
Oubliez tout ce que vous aviez appris à l'époque par contre, j'y ai passé 3h pour comprendre que les librairies permettant de piloter les GPIO sur un OS récent, telles que WiringPi ou RPi.GPIO sont dépréciées, cassées et inutilisables. Ça m'apprendra à ne plus retourner en premier lieu sur mes notes et à interroger l'état de l'art à l'instant présent.
- La solution que j'ai pour l'heure adoptée est l'utilisation de la librairie libgpiod, qui donne entière satisfaction en langage C. Cependant je n'ai pas réussi à faire fonctionner son pendant en Python gpiod.
Mais elle ne permet malheureusement pas d'exploiter les deux broches PWM matérielles embarquées dans l'Odroid-C2, tant pis, nous allons en créer une logicielle.
- Pour l'installation de la librairie, c'est très facile :
sudo apt install libgpiod-dev g++ gcc
Ensuite j'ai demandé à un outil moderne, Le Chat de me générer le code via ce prompt :
un code en C qui fait varier la vitesse de la pwm en fonction de la température du CPU
Un code qui fonctionne (presque) directement, et sans erreur ! Incroyable… (ci-après)
(Par contre oubliez ChatGPT, son code ne fonctionne jamais… on préférera des outils plus spécialisés en la matière qu'une LLM généraliste.)
- Il faut donc tout de même spécifier la broche sur laquelle est connectée notre transistor, et aussi sur quel contrôleur « gpiochip ».
#define GPIO_CHIP "gpiochip???" #define GPIO_PIN ???
On trouve des explications très utiles et claires sur le blog de Christophe Blaess, à propos de la manière de déterminer ces deux éléments cruciaux.
- En effet la commande :
sudo gpioinfo
- Nous retourne un tas d'information, dont :
[…] gpiochip1 - 119 lines: […] line 98: "J2 Header Pin33" unused output active-high […]
Que nous pouvons corréler à l'implantation du connecteur J2 de l'Odroid-C2 :
C'est donc le chip 1 qui adresse le connecteur J2 qu'on doit utiliser, et le nombre 98 qu'on doit indiquer, correspondant à la pin33.
Notez bien que j'utilise la pin33, qui correspond à la PWM matérielle, qui ne sera pas usitée en tant que telle (cf. plus haut), et que j'aurais pu utiliser une autre pin, puisque la PWM est générée logiciellement, par le code C ci-après.
- On peut maintenant créer le fichier ventilateur.c qui contiendra le programme :
sudo nano /root/ventilateur.c
- Et y copier le code C suivant :
#include <stdio.h>
#include <stdlib.h>
#include <gpiod.h>
#include <unistd.h>
#include <time.h>
#define TEMPERATURE_FILE "/sys/class/thermal/thermal_zone0/temp"
#define GPIO_CHIP "gpiochip1"
#define GPIO_PIN 98
void simulate_pwm(struct gpiod_chip *chip, int pin, int duty_cycle) {
struct gpiod_line *line;
struct timespec sleep_time_on, sleep_time_off;
int ret;
line = gpiod_chip_get_line(chip, pin);
if (!line) {
perror("Récupération de la ligne GPIO échouée");
return;
}
ret = gpiod_line_request_output(line, "pwm_sim", 0);
if (ret < 0) {
perror("Configuration de la ligne GPIO en sortie échouée");
gpiod_line_release(line);
return;
}
sleep_time_on.tv_sec = 0;
sleep_time_on.tv_nsec = (long)(duty_cycle * 1000000L);
sleep_time_off.tv_sec = 0;
sleep_time_off.tv_nsec = (long)((100 - duty_cycle) * 1000000L);
for (int i = 0; i < 100; i++) {
gpiod_line_set_value(line, 1);
nanosleep(&sleep_time_on, NULL);
gpiod_line_set_value(line, 0);
nanosleep(&sleep_time_off, NULL);
}
gpiod_line_release(line);
}
int read_cpu_temperature() {
FILE *temperature_file;
int temperature;
temperature_file = fopen(TEMPERATURE_FILE, "r");
if (temperature_file == NULL) {
perror("Erreur lors de l'ouverture du fichier de température");
return -1;
}
if (fscanf(temperature_file, "%d", &temperature) != 1) {
perror("Erreur lors de la lecture de la température");
fclose(temperature_file);
return -1;
}
fclose(temperature_file);
return temperature;
}
int main(void) {
struct gpiod_chip *chip;
int temperature, duty_cycle;
chip = gpiod_chip_open_by_name(GPIO_CHIP);
if (!chip) {
perror("Ouverture de la puce GPIO échouée");
return EXIT_FAILURE;
}
while (1) {
temperature = read_cpu_temperature();
if (temperature == -1) {
gpiod_chip_close(chip);
return EXIT_FAILURE;
}
// Convertir la température en degrés Celsius
float temperature_c = temperature / 1000.0;
printf("Température du CPU : %.2f °C\n", temperature_c);
// Calculer le cycle de travail en fonction de la température
// Par exemple, augmenter le cycle de travail de 20% à 100% en fonction de la température
duty_cycle = 20 + (int)((temperature_c / 80.0) * 80);
if (duty_cycle > 100) {
duty_cycle = 100;
}
printf("Cycle de travail PWM : %d%%\n", duty_cycle);
simulate_pwm(chip, GPIO_PIN, duty_cycle);
}
gpiod_chip_close(chip);
return EXIT_SUCCESS;
}
- Pour compiler le code on entre cette commande :
sudo gcc -o /root/ventilateur /root/ventilateur.c -lgpiod
- Et pour l'exécuter :
sudo /root/ventilateur
- Ce qui au fil du temps renvoie ceci au terminal :
Température du CPU : 43.00 °C Cycle de travail PWM : 63% Température du CPU : 42.00 °C Cycle de travail PWM : 62% Température du CPU : 41.00 °C Cycle de travail PWM : 61% Température du CPU : 40.00 °C Cycle de travail PWM : 60% Température du CPU : 39.00 °C Cycle de travail PWM : 59% Température du CPU : 38.00 °C Cycle de travail PWM : 58% Température du CPU : 38.00 °C Cycle de travail PWM : 58% Température du CPU : 37.00 °C Cycle de travail PWM : 57% Température du CPU : 36.00 °C Cycle de travail PWM : 56% Température du CPU : 35.00 °C Cycle de travail PWM : 55% Température du CPU : 35.00 °C Cycle de travail PWM : 55% Température du CPU : 34.00 °C Cycle de travail PWM : 54% Température du CPU : 33.00 °C Cycle de travail PWM : 53%
Automatisation :
Si tout à bien fonctionné à l'étape précédente, maintenant il s'agit d'exécuter le programme à chaque démarrage du DietPi, ce que nous allons faire en créant un service systemd :
- Écrire le fichier suivant :
sudo nano /etc/systemd/system/ventilateur.service
- Contenant ceci :
[Unit] Description=Démarre le programme « ventilateur » [Service] ExecStart=/root/ventilateur Restart=on-failure User=root [Install] WantedBy=multi-user.target
- Puis exécuter ces commandes pour respectivement recharger systemd, installer le service au démarrage, démarrer le service pour la session en cours, consulter l'état du service :
sudo systemctl daemon-reload sudo systemctl enable ventilateur.service sudo systemctl start ventilateur.service sudo service ventilateur status
Voilà, à chaque démarrage du µ-ordinateur, le ventilateur se mettra en route et régulera sa vitesse en fonction de la température du processeur ARM.
Modifications utiles :
Si on souhaite baisser la vitesse du ventilo par rapport à la température, on peut modifier le code comme ceci :
- Repérer la ligne :
duty_cycle = 20 + (int)((temperature_c / 80.0) * 80);
- Qui donne ceci :
Température du CPU : 29.00 °C
Cycle de travail PWM : 49%
- Et modifier les valeurs comme ceci :
duty_cycle = 20 + (int)((temperature_c / 100.0) * 60);
- Ce qui donne alors :
Température du CPU : 29.00 °C
Cycle de travail PWM : 37%




