Dans ce guide, nous allons vous présenter et vous expliquer comment utiliser les sémaphores FreeRTOS avec l’ESP32, à l’aide de l’IDE Arduino. Les sémaphores sont comme des signaux (ou drapeaux) qui permettent de synchroniser des tâches et de gérer des événements. Ils peuvent être utilisés pour indiquer qu’un événement s’est produit ou qu’une ressource est disponible. Contrairement aux files d’attente, les sémaphores ne transportent pas de données.

Il existe deux types de sémaphores : les sémaphores binaires et les sémaphores de comptage. Dans ce didacticiel, nous allons créer et explorer deux exemples différents pour vous montrer comment fonctionnent ces deux types de sémaphores.
Vous êtes nouveau sur FreeRTOS ? Commencez par ce didacticiel : ESP32 avec FreeRTOS (Arduino IDE) – Guide de démarrage : Création de tâches.
Présentation des sémaphores
Les sémaphores sont des outils de signalisation dans FreeRTOS utilisés pour coordonner les tâches. Ils sont couramment utilisés pour indiquer qu’un événement s’est produit ou qu’une ressource est disponible. Contrairement aux files d’attente, les sémaphores ne transportent pas de données, seulement un « compte », ou un « drapeau », ou un « signal » (ou peu importe comment vous voulez l’appeler) utilisé pour déclencher des actions lorsque quelque chose se produit.

Ils permettent aux tâches d’attendre des événements, tels qu’une pression sur un bouton ou une détection de mouvement, ou de signaler qu’un événement s’est produit. Cela les rend particulièrement utiles dans les scénarios impliquant des interruptions et la synchronisation des tâches.
Étant donné que les sémaphores ne stockent pas de données, ils consomment moins de mémoire que les files d’attente et sont idéaux pour une signalisation légère d’événements entre les tâches.
Il existe deux types de sémaphores :
- Sémaphore binaire : signale un seul événement. C’est un outil de synchronisation qui peut être vide (0) ou plein (1). C’est comme un signal qu’une tâche attend avant de pouvoir se poursuivre.
- Sémaphore de comptage : suit plusieurs événements (il peut s’agir du même événement plusieurs fois). C’est comme une file d’attente d’événements jusqu’à un nombre maximum que vous définissez. Contrairement aux files d’attente FreeRTOS, celles-ci ne transportent pas de données. Seulement un signal de synchronisation. Vous comprendrez mieux comment cela fonctionne plus loin dans l’exemple.
Fonctions de base des sémaphores
Voici quelques fonctions de base des sémaphores binaires et de comptage lors de l’utilisation de l’ESP32 avec l’IDE Arduino. Nous explorerons ensuite ces fonctions dans des exemples pratiques.
Création d’un sémaphore binaire
Pour créer un sémaphore binaire, utilisez la fonction xSemaphoreCreateBinary(). Il renvoie un handle SemaphoreHandle_t en cas de succès, ou NULL si la création échoue.
Création d’un sémaphore de comptage
Pour créer un sémaphore de comptage, utilisez la fonction xSemaphoreCreateCounting(). Il renvoie un handle SemaphoreHandle_t en cas de succès, ou NULL si la création échoue. Passez en argument le nombre maximum.
Prendre un sémaphore (obtenir du sémaphore)
Utilisez la fonction xSemaphoreTake(semaphore, timeout) dans une tâche pour attendre ou prendre un sémaphore. Pour un sémaphore binaire, il bloque jusqu’à ce que le sémaphore soit disponible (état 1), le mettant à 0 lorsqu’il est pris.
Pour un sémaphore de comptage, il décrémente le compte s’il est supérieur à 0, ou se bloque si le compte est 0. Le paramètre timeout spécifie la durée d’attente (en ticks) ; portMAX_DELAY signifie attendre indéfiniment. Cela signifie que la tâche sera bloquée jusqu’à ce qu’il y ait une valeur de sémaphore à prendre.
Donner un sémaphore
Pour donner un sémaphore, utilisez la fonction xSemaphoreGive() si elle est à l’intérieur d’une tâche, ou xSemaphoreGiveFromISR() si elle est utilisée dans les ISR (fonctions de routine de service d’interruption).
Pour un sémaphore binaire, il met l’état à 1, débloquant une tâche en attente (ou ignorée si le sémaphore est déjà à 1). Pour un sémaphore de comptage, il incrémente le compte jusqu’à maxCount, débloquant une tâche en attente (ignorée si à maxCount).
Exemple 1 : Sémaphore binaire – Basculer une LED en appuyant sur un bouton
Dans cette section, nous allons construire un exemple simple pour démontrer comment fonctionne un sémaphore binaire et comment l’implémenter dans une application pratique. Cet exemple fera basculer une LED une fois lorsque le bouton-poussoir est enfoncé. Pour signaler la pression sur le bouton, nous utiliserons un sémaphore. Simultanément, nous aurons une autre tâche faisant clignoter une LED pour démontrer que nous pouvons exécuter plusieurs tâches simultanément.

Voici donc un aperçu de l’exemple :
- Nous allons ajouter une interruption à un bouton-poussoir. En appuyant sur le bouton poussoir, l’ISR correspondant donnera le sémaphore (le mettra à 1).
- Il existe une autre tâche, appelée LEDToggleTask(), qui attendra que le sémaphore change l’état de la LED. Lorsque le sémaphore est donné par l’ISR, cette tâche s’exécutera et le sémaphore sera réinitialisé à 0. Ce n’est que lorsque le sémaphore sera mis à 1, lorsque le bouton sera enfoncé, que cette tâche sera réexécutée.
- Simultanément, nous avons une autre tâche appelée LEDBlinkTask() qui va augmenter et diminuer la luminosité d’une autre LED.
Pièces requises
Voici une liste des pièces requises pour cet exemple :
Vous pouvez utiliser les liens précédents ou vous rendre directement sur MakerAdvisor.com/tools pour trouver toutes les pièces pour vos projets au meilleur prix !

Câblage du circuit
Câblez le circuit suivant :
- LED rouge connectée au GPIO 2
- LED bleue connectée au GPIO 4
- Bouton poussoir connecté au GPIO 23
Vous pouvez suivre le diagramme schématique suivant.

Code
Téléchargez le code suivant sur l’IDE Arduino.
/*
Rui Santos & Sara Santos - Raspberryme.com
Complete project details at https://Raspberryme.com/esp32-freertos-semaphores-arduino/
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files.
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*/
#define BUTTON_PIN 23
#define LED1_PIN 2 // Toggled LED
#define LED2_PIN 4 // Blinking LED
#define DEBOUNCE_DELAY 200
SemaphoreHandle_t buttonSemaphore = NULL;
volatile uint32_t lastInterruptTime = 0;
void IRAM_ATTR buttonISR() {
uint32_t currentTime = millis();
if (currentTime - lastInterruptTime > DEBOUNCE_DELAY) {
BaseType_t higherPriorityTaskWoken = pdFALSE;
xSemaphoreGiveFromISR(buttonSemaphore, &higherPriorityTaskWoken);
lastInterruptTime = currentTime;
if (higherPriorityTaskWoken) {
portYIELD_FROM_ISR();
}
}
}
void LEDToggleTask(void *parameter) {
pinMode(LED1_PIN, OUTPUT);
bool ledState = false;
for (;;) {
if (xSemaphoreTake(buttonSemaphore, portMAX_DELAY)) {
ledState = !ledState;
digitalWrite(LED1_PIN, ledState ? HIGH : LOW);
Serial.print("LEDToggleTask: LED1 ");
Serial.println(ledState ? "ON" : "OFF");
}
}
}
void LEDBlinkTask(void *parameter) {
pinMode(LED2_PIN, OUTPUT);
for (;;) {
digitalWrite(LED2_PIN, HIGH);
Serial.println("LEDBlinkTask: LED2 ON");
vTaskDelay(250 / portTICK_PERIOD_MS);
digitalWrite(LED2_PIN, LOW);
Serial.println("LEDBlinkTask: LED2 OFF");
vTaskDelay(250 / portTICK_PERIOD_MS);
}
}
void setup() {
Serial.begin(115200);
// Defining the button as an interrupt
pinMode(BUTTON_PIN, INPUT_PULLUP);
attachInterrupt(digitalPinToInterrupt(BUTTON_PIN), buttonISR, FALLING);
buttonSemaphore = xSemaphoreCreateBinary();
if (buttonSemaphore == NULL) {
Serial.println("Failed to create semaphore!");
while (1);
}
xTaskCreatePinnedToCore(
LEDToggleTask, // Task function
"LEDToggleTask", // Task name
3000, // Stack size
NULL, // Task parameters
2, // Higher priority
NULL, // Task handle
1 // Core ID
);
xTaskCreatePinnedToCore(
LEDBlinkTask, // Task function
"LEDBlinkTask", // Task name
3000, // Stack size
NULL, // Task parameters
1, // Medium priority
NULL, // Task handle
1 // Core ID
);
}
void loop() {
}
Afficher le code brut
Comment fonctionne le code ?
Commencez par définir les broches du bouton-poussoir, de la LED basculée et de la LED clignotante.
#define BUTTON_PIN 23
#define LED1_PIN 2 // Toggled LED
#define LED2_PIN 4 // Blinking LED
Définissez le délai anti-rebond du bouton-poussoir en millisecondes.
#define DEBOUNCE_DELAY 200
Créez un handle pour le sémaphore appelé boutonSemaphore.
SemaphoreHandle_t buttonSemaphore = NULL;
installation()
Expliquons d’abord setup(), puis analysons les tâches.
Tout d’abord, définissez le bouton-poussoir comme interruption et définissez sa fonction de rappel (ISR). Dans ce cas, cela s’appelle boutonISR.
pinMode(BUTTON_PIN, INPUT_PULLUP);
attachInterrupt(digitalPinToInterrupt(BUTTON_PIN), buttonISR, FALLING);
Nous créons un sémaphore binaire en utilisant la fonction xSemaphoreCreateBinary() sur le handle ButtonSemaphore que nous avons créé précédemment.
buttonSemaphore = xSemaphoreCreateBinary();
if (buttonSemaphore == NULL) {
Serial.println("Failed to create semaphore!");
while (1);
}
Ensuite, nous créons la LedToggleTask et la LEDBlinkTask avec des priorités différentes.
xTaskCreatePinnedToCore(
LEDToggleTask, // Task function
"LEDToggleTask", // Task name
3000, // Stack size
NULL, // Task parameters
2, // Higher priority
NULL, // Task handle
1 // Core ID
);
xTaskCreatePinnedToCore(
LEDBlinkTask, // Task function
"LEDBlinkTask", // Task name
3000, // Stack size
NULL, // Task parameters
1, // Medium priority
NULL, // Task handle
1 // Core ID
);
boutonISR()
Les lignes suivantes créent la fonction buttonISR().
void IRAM_ATTR buttonISR() {
uint32_t currentTime = millis();
if (currentTime - lastInterruptTime > DEBOUNCE_DELAY) {
BaseType_t higherPriorityTaskWoken = pdFALSE;
xSemaphoreGiveFromISR(buttonSemaphore, &higherPriorityTaskWoken);
lastInterruptTime = currentTime;
if (higherPriorityTaskWoken) {
portYIELD_FROM_ISR();
}
}
}
Lorsque le bouton est enfoncé, la fonction buttonISR() s’exécutera. Si nous avons une pression valide, il utilise les fonctions de sémaphore FreeRTOS pour signaler à une autre tâche que l’événement du bouton s’est produit.
Donner le sémaphore
La ligne suivante est l’action clé du sémaphore. Il « donne » le sémaphore. Ce sémaphore est comme un signal qui indique à une autre partie du programme (généralement une tâche FreeRTOS – dans notre cas, il s’agit de la LEDToggleTask) que le bouton a été enfoncé.
xSemaphoreGiveFromISR(buttonSemaphore, &higherPriorityTaskWoken);
upperPriorityTaskWoken et portYIELD_FROM_ISR
La variable upperPriorityTaskWoken est utilisée pour vérifier si le fait de donner le sémaphore réveillera une tâche qui a une priorité plus élevée que la tâche en cours d’exécution. Si c’est le cas, nous appelons portYIELD_FROM_ISR() pour permettre au système de passer immédiatement à cette tâche de priorité plus élevée juste après la fin de l’interruption. Dans notre cas, nous souhaitons passer immédiatement à la LEDToggleTask.
Autrement dit:
- Fondamentalement, upperPriorityTaskWoken est utilisé pour vérifier si donner au sémaphore débloqué une tâche plus importante.
- Nous le transmettons à xSemaphoreGiveFromISR() afin qu’il puisse mettre à jour la valeur.
- Si c’est pdTRUE, nous appelons portYIELD_FROM_ISR() pour permettre à FreeRTOS de passer immédiatement à cette tâche.
C’est ainsi que FreeRTOS permet aux interruptions de déclencher en toute sécurité des tâches hautement prioritaires sans causer de problèmes de planification des tâches.
LEDToggleTâche
LEDToggleTask() basculera l’état de LED1 lorsqu’il y aura une valeur sur le sémaphore.
void LEDToggleTask(void *parameter) {
pinMode(LED1_PIN, OUTPUT);
bool ledState = false;
for (;;) {
if (xSemaphoreTake(buttonSemaphore, portMAX_DELAY)) {
ledState = !ledState;
digitalWrite(LED1_PIN, ledState ? HIGH : LOW);
Serial.print("LEDToggleTask: LED1 ");
Serial.println(ledState ? "ON" : "OFF");
}
}
}
Lorsque la tâche LEDToggleTask() s’exécute, elle configure la broche LED comme sortie et démarre une boucle infinie. A l’intérieur de la boucle, il attend le sémaphore en utilisant xSemaphoreTake(buttonSemaphore, portMAX_DELAY). Le portMAX_DELAY signifie que la tâche attendra indéfiniment jusqu’à ce qu’il y ait une valeur sur le sémaphore (jusqu’à ce que le bouton soit enfoncé).
if (xSemaphoreTake(buttonSemaphore, portMAX_DELAY)) {
Lorsque le sémaphore est reçu, la tâche basculera l’état de la LED et l’imprimera sur le moniteur série.
if (xSemaphoreTake(buttonSemaphore, portMAX_DELAY)) {
ledState = !ledState;
digitalWrite(LED1_PIN, ledState ? HIGH : LOW);
Serial.print("LEDToggleTask: LED1 ");
Serial.println(ledState ? "ON" : "OFF");
LEDBlinkTâche
Outre l’autre tâche, nous avons la LEDBlinkTask qui s’exécute indépendamment et simultanément, faisant clignoter une LED indéfiniment.
void LEDBlinkTask(void *parameter) {
pinMode(LED2_PIN, OUTPUT);
for (;;) {
digitalWrite(LED2_PIN, HIGH);
Serial.println("LEDBlinkTask: LED2 ON");
vTaskDelay(250 / portTICK_PERIOD_MS);
digitalWrite(LED2_PIN, LOW);
Serial.println("LEDBlinkTask: LED2 OFF");
vTaskDelay(250 / portTICK_PERIOD_MS);
}
}
Démonstration
Téléchargez le code sur votre tableau. Après le téléchargement, ouvrez le moniteur série à une vitesse de transmission de 115 200 bauds. Appuyez sur le bouton ESP32 RST pour qu’il commence à exécuter le code.

La LED connectée au GPIO 4 clignotera toutes les 250 millisecondes. Appuyez sur le bouton poussoir pour basculer l’état de la LED connectée au GPIO 2. Vous pouvez voir une petite démonstration dans la courte vidéo ci-dessous.
Dans le moniteur série, vous devriez obtenir quelque chose de similaire.

Exemple 2 : compter le sémaphore
Dans cette section, nous allons créer un exemple simple pour démontrer le fonctionnement du comptage des sémaphores. Nous allons créer un sémaphore de comptage avec un nombre maximum de 5. Ce sémaphore prendra jusqu’à 5 pressions sur des boutons. Il existe une autre tâche qui consommera ce sémaphore pour faire clignoter une LED autant de fois que les valeurs du sémaphore. Lorsqu’une valeur est consommée depuis le sémaphore, une autre valeur peut être ajoutée.
En résumé, voici un aperçu du fonctionnement du projet :
- Nous allons attacher une interruption à un bouton-poussoir. Lorsque le bouton-poussoir est enfoncé, la routine de service d’interruption (ISR) donnera un sémaphore, jusqu’à un nombre maximum de 5.
- La LEDBlinkTask attendra le sémaphore. Chaque fois qu’il en reçoit un, la LED clignote. La LED clignotera une fois pour chaque compte actuellement disponible dans le sémaphore.
- Lorsque la LEDBlinkTask consomme une valeur du sémaphore, il y a un « espace » pour un nouveau compte ajouté en appuyant sur un bouton-poussoir.
- Simultanément, nous aurons une autre tâche appelée LEDFadeTask qui fera disparaître une autre LED. Cette tâche est utilisée pour démontrer la puissance de FreeRTOS pour gérer le multitâche.
Pièces requises et schéma de câblage
Identique à l’exemple précédent.
Code
Copiez le code suivant dans l’IDE Arduino.
/*
Rui Santos & Sara Santos - Raspberryme.com
Complete project details at https://Raspberryme.com/esp32-freertos-semaphores-arduino/
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files.
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*/
#define BUTTON_PIN 23
#define LED1_PIN 2 // Blinking LED
#define LED2_PIN 4 // Fading LED
#define DEBOUNCE_DELAY 200 // debounce for the pushbutton in milliseconds
#define SEMAPHORE_MAX_COUNT 5
SemaphoreHandle_t buttonSemaphore = NULL;
volatile uint32_t lastInterruptTime = 0;
void IRAM_ATTR buttonISR() {
uint32_t currentTime = millis();
if (currentTime - lastInterruptTime > DEBOUNCE_DELAY) {
BaseType_t higherPriorityTaskWoken = pdFALSE;
if (xSemaphoreGiveFromISR(buttonSemaphore, &higherPriorityTaskWoken) == pdTRUE) {
Serial.println("buttonISR: Gave semaphore token");
} else {
Serial.println("buttonISR: Semaphore full");
}
lastInterruptTime = currentTime;
if (higherPriorityTaskWoken) {
portYIELD_FROM_ISR();
}
}
}
void LEDBlinkTask(void *parameter) {
pinMode(LED1_PIN, OUTPUT);
for (;;) {
// Get and print the current semaphore count
UBaseType_t count = uxSemaphoreGetCount(buttonSemaphore);
Serial.print("LEDBlinkTask: Current semaphore count = ");
Serial.println(count);
if (xSemaphoreTake(buttonSemaphore, portMAX_DELAY)) {
Serial.println("LEDBlinkTask: Blinking LED1 for button press");
vTaskDelay(500 / portTICK_PERIOD_MS);
digitalWrite(LED1_PIN, HIGH);
vTaskDelay(1000 / portTICK_PERIOD_MS);
digitalWrite(LED1_PIN, LOW);
vTaskDelay(500 / portTICK_PERIOD_MS);
}
}
}
void LEDFadeTask(void *parameter) {
pinMode(LED2_PIN, OUTPUT);
for (;;) {
// Fade up (0 to 255)
for (int duty = 0; duty <= 255; duty += 5) {
analogWrite(LED2_PIN, duty);
if (duty % 50 == 0) { // Print every 10th step
Serial.print("LEDFadeTask: Fading LED2, duty=");
Serial.println(duty);
}
vTaskDelay(50 / portTICK_PERIOD_MS);
}
// Fade down (255 to 0)
for (int duty = 255; duty >= 0; duty -= 5) {
analogWrite(LED2_PIN, duty);
if (duty % 50 == 0 || duty == 255) {
Serial.print("LEDFadeTask: Fading LED2, duty=");
Serial.println(duty);
}
vTaskDelay(50 / portTICK_PERIOD_MS);
}
}
}
void setup() {
Serial.begin(115200); // Higher baud rate
delay(1000);
Serial.println("Starting FreeRTOS: Counting Semaphore");
pinMode(BUTTON_PIN, INPUT_PULLUP);
attachInterrupt(digitalPinToInterrupt(BUTTON_PIN), buttonISR, FALLING);
buttonSemaphore = xSemaphoreCreateCounting(SEMAPHORE_MAX_COUNT, 0);
if (buttonSemaphore == NULL) {
Serial.println("Failed to create semaphore!");
while (1);
}
xTaskCreatePinnedToCore(
LEDBlinkTask, // Task function
"LEDBlinkTask", // Task name
3000, // Stack size
NULL, // Task parameters
2, // Higher priority
NULL, // Task handle
1 // Core ID
);
xTaskCreatePinnedToCore(
LEDFadeTask, // Task function
"LEDFadeTask", // Task name
3000, // Stack size
NULL, // Task parameters
1, // Lower priority
NULL, // Task handle
1 // Core ID
);
}
void loop() {}
Afficher le code brut
Comment fonctionne le code ?
Ce code est assez similaire au précédent. Nous allons juste jeter un œil aux sections importantes liées au sémaphore de comptage.
Création du sémaphore de comptage
Dans setup(), nous créons un sémaphore de comptage avec un nombre maximum de 5 (SEMAPHORE_MAX_COUNT) commençant à 0. Nous faisons cela en utilisant la fonction xSemaphoreCreateCounting().
buttonSemaphore = xSemaphoreCreateCounting(SEMAPHORE_MAX_COUNT, 0);
if (buttonSemaphore == NULL) {
Serial.println("Failed to create semaphore!");
while (1);
}
Création de tâches
Toujours dans le setup(), nous créons nos tâches et les assignons à un core. La LEDBlinkTask a une priorité plus élevée que la LEDFadeTask.
xTaskCreatePinnedToCore(
LEDBlinkTask, // Task function
"LEDBlinkTask", // Task name
3000, // Stack size
NULL, // Task parameters
2, // Higher priority
NULL, // Task handle
1 // Core ID
);
xTaskCreatePinnedToCore(
LEDFadeTask, // Task function
"LEDFadeTask", // Task name
3000, // Stack size
NULL, // Task parameters
1, // Lower priority
NULL, // Task handle
1 // Core ID
);
Bouton ISR et sémaphore de comptage
Lorsque vous appuyez sur le bouton-poussoir, la fonction buttonISR() s’exécutera. Si nous avons une pression valide sur un bouton, nous la donnons au sémaphore de comptage. Le sémaphore prendra jusqu’à cinq comptes. Nous utilisons la même fonction que celle utilisée dans l’exemple précédent xSemaphoreGiveFromISR().
if (xSemaphoreGiveFromISR(buttonSemaphore, &higherPriorityTaskWoken) == pdTRUE) {
Serial.println("buttonISR: Gave semaphore token");
} else {
Serial.println("buttonISR: Semaphore full");
}
LEDBlinkTask et prise du sémaphore
La LEDBlinkTask attend indéfiniment jusqu’à ce que nous ayons un décompte sur le sémaphore. Quand il y a un décompte sur le sémaphore, on le prend et on fait clignoter la LED.
if (xSemaphoreTake(buttonSemaphore, portMAX_DELAY)) {
Serial.println("LEDBlinkTask: Blinking LED1 for button press");
vTaskDelay(500 / portTICK_PERIOD_MS);
digitalWrite(LED1_PIN, HIGH);
vTaskDelay(1000 / portTICK_PERIOD_MS);
digitalWrite(LED1_PIN, LOW);
vTaskDelay(500 / portTICK_PERIOD_MS);
}
Puisque nous avons un sémaphore de comptage, la LED clignotera autant de fois que les comptes actuellement disponibles sur le sémaphore.
Chaque fois qu’il extrait du sémaphore, un nouvel espace est disponible pour ajouter un nouveau compte (via une pression sur un bouton-poussoir).
Dans cette tâche, nous imprimons également le nombre de sémaphores actuel en appelant la fonction uxSemaphoreGetCount() et en passant le handle du sémaphore comme argument.
// Get and print the current semaphore count
UBaseType_t count = uxSemaphoreGetCount(buttonSemaphore);
Serial.print("LEDBlinkTask: Current semaphore count = ");
Serial.println(count);
Tâche de fondu LED
Simultanément, nous avons une autre tâche indépendante appelée LEDFadeTask qui atténue simplement l’autre LED.
void LEDFadeTask(void *parameter) {
pinMode(LED2_PIN, OUTPUT);
for (;;) {
// Fade up (0 to 255)
for (int duty = 0; duty <= 255; duty += 5) {
analogWrite(LED2_PIN, duty);
if (duty % 50 == 0) { // Print every 10th step
Serial.print("LEDFadeTask: Fading LED2, duty=");
Serial.println(duty);
}
vTaskDelay(50 / portTICK_PERIOD_MS);
}
// Fade down (255 to 0)
for (int duty = 255; duty >= 0; duty -= 5) {
analogWrite(LED2_PIN, duty);
if (duty % 50 == 0 || duty == 255) {
Serial.print("LEDFadeTask: Fading LED2, duty=");
Serial.println(duty);
}
vTaskDelay(50 / portTICK_PERIOD_MS);
}
}
}
Démonstration
Téléchargez le code sur votre tableau. Après le téléchargement, ouvrez le moniteur série à une vitesse de transmission de 115 200 bauds. Appuyez sur le bouton ESP32 RST pour qu’il commence à exécuter le code.

La LED connectée au GPIO 4 s’estompera constamment.
Appuyez plusieurs fois sur le bouton poussoir pour faire clignoter la LED connectée au GPIO 2 autant de fois que d’appuis sur le bouton de la file d’attente du sémaphore.
Vous pouvez voir une petite démonstration dans la courte vidéo ci-dessous.
Dans le moniteur série, vous devriez obtenir quelque chose de similaire. Le comptage du sémaphore diminuera au fur et à mesure que la LED clignote (si vous ne continuez pas à appuyer sur le bouton poussoir).

Conclusion
Dans ce guide, vous avez découvert les sémaphores binaires et de comptage FreeRTOS et comment les implémenter avec l’ESP32 programmé avec l’IDE Arduino.
Les sémaphores nous permettent de synchroniser des tâches pour signaler quand une ressource est disponible, quand un événement s’est produit ou un point d’une tâche où l’autre doit s’exécuter.
Nous vous avons montré deux exemples simples pour démontrer le fonctionnement des sémaphores. Cela peut être appliqué à des applications beaucoup plus complexes avec plusieurs tâches donnant et prenant du sémaphore.
Nous espérons que vous avez trouvé ce didacticiel utile pour commencer à implémenter la programmation FreeRTOS sur vos croquis ESP32. Nous avons d’autres tutoriels sur cette série FreeRTOS qui pourraient vous plaire :
Pour en savoir plus sur l’ESP32, assurez-vous de consulter nos ressources :
Cette vidéo vous emmène dans l’histoire de Raspberry Pi :

-
ELEGOO Carte Starter Kit de Démarrage ESP-32 avec Tutoriel et Carte de Développement Microcontrôleur Double Cœur USB-C Prise en Charge AP/STA/AP+STA, Compatible avec Arduino IDE
-
Kit de démarrage Basique pour ESP32, kit de démarrage électronique Compatible avec Arduino, kit de démarrage Basique ESP32 ESP-32S, pour débutants et passionnés d'électronique
