L’ESP32 est livré avec 2 microprocesseurs Xtensa 32 bits LX6 : core 0 et core 1. Il est donc dual core. Lorsque nous exécutons du code sur l’IDE Arduino, par défaut, il s’exécute sur le noyau 1. Dans ce didacticiel, nous allons vous montrer comment exécuter du code sur le deuxième noyau ESP32 en créant des tâches FreeRTOS. Vous pouvez exécuter des morceaux de code simultanément sur les deux cœurs et rendre votre ESP32 multitâche.

Mis à jour le 26 novembre 2025.
Remarque : vous n’avez pas nécessairement besoin d’exécuter un processeur dual-core pour réaliser le multitâche.
ESP32 double cœur – Introduction
L’ESP32 est livré avec 2 microprocesseurs Xtensa 32 bits LX6 : core 0 et core 1.*

* Certains modèles ESP32 spécifiques ne sont pas dual-core. Vérifiez la carte ESP32 que vous utilisez avant de suivre ce tutoriel. Voici quelques-uns des modèles les plus populaires qui ne sont pas dual core :
- Basé sur ESP32-S2 :
- ESP32-S2-Saola-1 (carte de développement Expressif)
- ESP32-S2-Kaluga-1 (kit de développement multimédia avec prise en charge d’affichage/caméra)
- Basé sur ESP32-C3 :
- ESP32-C3-DevKitM-1 (mini kit de développement Expressif)
- ESP32-C3-WROOM-0
- Seeed StudioXIAO ESP32-C3
- Waveshare ESP32-C3-Zéro
- Basé sur ESP32-C6 :
- ESP32-C6-DevKitC-1 (carte de développement Expressif)
- Seeed StudioXIAO ESP32-C6
- Basé sur ESP32-H2 :
Par défaut, lorsque nous exécutons du code sur l’ESP32, il s’exécute sur le noyau 1. Lorsque nous téléchargeons du code sur l’ESP32 à l’aide de l’IDE Arduino, il s’exécute simplement – nous n’avons pas à nous soucier du noyau qui exécute le code.
Il existe une fonction qui renvoie dans quel noyau le code est exécuté :
xPortGetCoreID()
Si vous utilisez cette fonction dans un croquis Arduino, vous verrez que setup() et loop() s’exécutent sur le noyau 1. Testez-le vous-même en téléchargeant le croquis suivant sur votre ESP32.
/*********
Rui Santos
Complete project details at https://raspberryme.com
*********/
void setup() {
Serial.begin(115200);
Serial.print("setup() running on core ");
Serial.println(xPortGetCoreID());
}
void loop() {
Serial.print("loop() running on core ");
Serial.println(xPortGetCoreID());
}
Afficher le code brut
Ouvrez le moniteur série à un débit en bauds de 115 200 et vérifiez le cœur sur lequel le croquis Arduino s’exécute.

Créer des tâches
L’IDE Arduino prend en charge FreeRTOS pour l’ESP32, qui est un système d’exploitation en temps réel. Cela nous permet de gérer plusieurs tâches en parallèle qui s’exécutent indépendamment.
En savoir plus sur les tâches FreeRTOS : ESP32 avec FreeRTOS (Arduino IDE) – Mise en route : Créer des tâches.
Les tâches sont des morceaux de code qui exécutent quelque chose. Par exemple, il peut s’agir de faire clignoter une LED, de faire une requête réseau, de mesurer les relevés d’un capteur, de publier les relevés d’un capteur, etc…
Pour attribuer des parties spécifiques de code à un noyau spécifique, vous devez créer des tâches. Lors de la création d’une tâche, vous pouvez choisir dans quel noyau elle s’exécutera, ainsi que sa priorité. Les valeurs de priorité commencent à 0, 0 étant la priorité la plus basse. Le processeur exécutera en premier les tâches ayant la priorité la plus élevée. Pour créer des tâches, vous devez suivre les étapes suivantes :
Pour créer des tâches, vous devez suivre les étapes suivantes :
1. Créez un handle de tâche. Un exemple pour la tâche 1 :
TaskHandle_t Task1;
2. Dans setup(), créez une tâche assignée à un noyau spécifique à l’aide de la fonction xTaskCreatePinnedToCore. Cette fonction prend plusieurs arguments, notamment la priorité et le noyau sur lequel la tâche doit s’exécuter (le dernier paramètre).
xTaskCreatePinnedToCore(
Task1code, /* Function to implement the task */
"Task1", /* Name of the task */
10000, /* Stack size in words */
NULL, /* Task input parameter */
0, /* Priority of the task */
&Task1, /* Task handle. */
0); /* Core where the task should run */
3. Après avoir créé la tâche, vous devez créer une fonction contenant le code de la tâche créée. Dans cet exemple, vous devez créer la fonction Task1code(). Voici à quoi ressemble la fonction de tâche :
Void Task1code( void * parameter) {
for(;;) {
Code for task 1 - infinite loop
(...)
}
}
Le for(;;) crée une boucle infinie. Ainsi, cette fonction fonctionne de la même manière que la fonction loop(). Vous pouvez l’utiliser comme deuxième boucle dans votre code, par exemple.
Si lors de l’exécution de votre code vous souhaitez supprimer la tâche créée, vous pouvez utiliser la fonction vTaskDelete(), qui accepte le handle de tâche (Task1) comme argument :
vTaskDelete(Task1);
Pour un didacticiel plus approfondi sur la gestion des tâches, lisez ce guide de démarrage FreeRTOS pour l’ESP32.
Voyons comment ces concepts fonctionnent avec un exemple simple.
Créer des tâches dans différents cœurs – Exemple
Pour suivre cet exemple, vous avez besoin des pièces suivantes :
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 !

Pour créer différentes tâches exécutées sur différents cœurs, nous allons créer deux tâches qui font clignoter les LED avec des délais différents. Câblez deux LED à l’ESP32 comme indiqué dans le schéma suivant :

Nous allons créer deux tâches exécutées sur des cœurs différents :
- La tâche 1 s’exécute sur le noyau 0 ;
- La tâche 2 s’exécute sur le noyau 1 ;

Téléchargez le croquis suivant sur votre ESP32 pour faire clignoter chaque LED dans un noyau différent :
/*********
Rui Santos
Complete project details at https://raspberryme.com
*********/
TaskHandle_t Task1;
TaskHandle_t Task2;
// LED pins
const int led1 = 2;
const int led2 = 4;
void setup() {
Serial.begin(115200);
pinMode(led1, OUTPUT);
pinMode(led2, OUTPUT);
//create a task that will be executed in the Task1code() function, with priority 1 and executed on core 0
xTaskCreatePinnedToCore(
Task1code, /* Task function. */
"Task1", /* name of task. */
10000, /* Stack size of task */
NULL, /* parameter of the task */
1, /* priority of the task */
&Task1, /* Task handle to keep track of created task */
0); /* pin task to core 0 */
delay(500);
//create a task that will be executed in the Task2code() function, with priority 1 and executed on core 1
xTaskCreatePinnedToCore(
Task2code, /* Task function. */
"Task2", /* name of task. */
10000, /* Stack size of task */
NULL, /* parameter of the task */
1, /* priority of the task */
&Task2, /* Task handle to keep track of created task */
1); /* pin task to core 1 */
delay(500);
}
//Task1code: blinks an LED every 1000 ms
void Task1code( void * pvParameters ){
Serial.print("Task1 running on core ");
Serial.println(xPortGetCoreID());
for(;;){
digitalWrite(led1, HIGH);
delay(1000);
digitalWrite(led1, LOW);
delay(1000);
}
}
//Task2code: blinks an LED every 700 ms
void Task2code( void * pvParameters ){
Serial.print("Task2 running on core ");
Serial.println(xPortGetCoreID());
for(;;){
digitalWrite(led2, HIGH);
delay(700);
digitalWrite(led2, LOW);
delay(700);
}
}
void loop() {
}
Afficher le code brut
Comment fonctionne le code ?
Remarque : dans le code, nous créons deux tâches et attribuons une tâche au noyau 0 et une autre au noyau 1. Les croquis Arduino s’exécutent sur le noyau 1 par défaut. Ainsi, vous pouvez écrire le code de la tâche 2 dans la boucle() (il n’était pas nécessaire de créer une autre tâche). Dans ce cas, nous créons deux tâches différentes à des fins d’apprentissage.
Mais, en fonction des exigences de votre projet, il peut être plus pratique d’organiser votre code en tâches, comme le montre cet exemple.
Le code commence par créer un handle de tâche pour Task1 et Task2 appelé Task1 et Task2.
TaskHandle_t Task1;
TaskHandle_t Task2;
Attribuez GPIO 2 et GPIO 4 aux LED :
const int led1 = 2;
const int led2 = 4;
Dans setup(), initialisez le Serial Monitor à un débit en bauds de 115 200 :
Serial.begin(115200);
Déclarez les LED comme sorties :
pinMode(led1, OUTPUT);
pinMode(led2, OUTPUT);
Ensuite, créez la tâche 1 à l’aide de la fonction xTaskCreatePinnedToCore() :
xTaskCreatePinnedToCore(
Task1code, /* Task function. */
"Task1", /* name of task. */
10000, /* Stack size of task */
NULL, /* parameter of the task */
1, /* priority of the task */
&Task1, /* Task handle to keep track of created task */
0); /* pin task to core 0 */
Task1 sera implémenté avec la fonction Task1code(). Nous devons donc créer cette fonction plus tard dans le code. Nous donnons à la tâche la priorité 1 et l’avons épinglée au noyau 0.
Nous créons la Tâche2 en utilisant la même méthode, mais nous l’attribuons au noyau 1 :
xTaskCreatePinnedToCore(
Task2code, /* Task function. */
"Task2", /* name of task. */
10000, /* Stack size of task */
NULL, /* parameter of the task */
1, /* priority of the task */
&Task2, /* Task handle to keep track of created task */
1); /* pin task to core 0 */
Après avoir créé les tâches, nous devons créer les fonctions qui exécuteront ces tâches.
void Task1code( void * pvParameters ){
Serial.print("Task1 running on core ");
Serial.println(xPortGetCoreID());
for(;;){
digitalWrite(led1, HIGH);
delay(1000);
digitalWrite(led1, LOW);
delay(1000);
}
}
La fonction de Task1 s’appelle Task1code() (vous pouvez l’appeler comme vous voulez). À des fins de débogage, nous imprimons d’abord le noyau dans lequel la tâche s’exécute :
Serial.print("Task1 running on core ");
Serial.println(xPortGetCoreID());
Ensuite, nous avons une boucle infinie similaire à loop() sur le croquis Arduino. Dans cette boucle, nous faisons clignoter la LED1 toutes les secondes.
La même chose se produit pour la tâche 2, mais nous faisons clignoter la LED avec un délai différent.
void Task2code( void * pvParameters ){
Serial.print("Task2 running on core ");
Serial.println(xPortGetCoreID());
for(;;){
digitalWrite(led2, HIGH);
delay(700);
digitalWrite(led2, LOW);
delay(700);
}
}
Finalement, la fonction loop() est vide :
void loop() { }
Remarque : comme mentionné précédemment, la boucle Arduino() s’exécute sur le noyau 1. Ainsi, au lieu de créer une tâche à exécuter sur le noyau 1, vous pouvez écrire votre code dans la boucle(). Mais il peut être plus pratique d’organiser votre code en tâches FreeRTOS.
Démonstration
Téléchargez le code sur votre ESP32. Assurez-vous d’avoir sélectionné la bonne carte et le bon port COM.
Ouvrez le moniteur série à un débit en bauds de 115 200. Vous devriez recevoir les messages suivants :

Comme prévu, la tâche 1 s’exécute sur le cœur 0, tandis que la tâche 2 s’exécute sur le cœur 1.
Dans votre circuit, une LED doit clignoter toutes les secondes et l’autre toutes les 700 millisecondes.

Conclusion
En résumé, dans ce tutoriel, vous avez appris que :
- La plupart des modèles ESP32 sont dual-core.
- Les croquis Arduino s’exécutent sur le noyau 1 par défaut.
- Pour utiliser le core 0, vous devez créer des tâches FreeRTOS.
- Vous pouvez utiliser la fonction xTaskCreatePinnedToCore() pour épingler une tâche spécifique sur un noyau spécifique.
- Grâce à cette méthode, vous pouvez exécuter deux tâches différentes indépendamment et simultanément en utilisant les deux cœurs.
Dans ce didacticiel, nous avons fourni un exemple simple avec des LED. L’idée est d’utiliser cette méthode avec des projets plus avancés avec des applications réelles. Par exemple, il peut être utile d’utiliser un noyau pour effectuer les relevés des capteurs et un autre pour publier ces relevés sur un système domotique.
Si vous souhaitez en savoir plus sur les tâches et la programmation FreeRTOS, vous pouvez consulter tous nos autres tutoriels FreeRTOS :
En savoir plus sur l’ESP32 :
Si vous souhaitez en savoir plus sur l’ESP32, n’hésitez pas à consulter notre cours : Apprendre l’ESP32 avec l’IDE Arduino.
Cette vidéo vous emmène dans l’histoire de Raspberry Pi :

-
ESP32 Lot de 3 cartes de dérivation ESP32 pour 30 broches, carte de développement ESP32 2,4 GHz WiFi double cœur + Bluetooth 2 en 1 microcontrôleur ESP-WROOM-32 puce pour Arduino (3)
-
Carte de développement ESP32S ESP-WROOM-32 avec carte d'extension à bornes à vis (1 ensemble), module WiFi et Bluetooth 38 broches, USB Type-C et CP2102, double cœur ESP32 pour projets Arduino IDE et
