ESP32 Brownout Detector was triggered : causes et solutions
ESP32 Brownout detector was triggered et redémarrages en boucle : alimentation, câble, condensateur, codes rst, Guru Meditation, Task WDT et Exception Decoder.
Le message ESP32 Brownout detector was triggered signifie que la tension d'alimentation de la carte est passée un instant sous une limite sûre et que la puce a redémarré pour se protéger. Le problème ne vient presque jamais du code, mais de l'alimentation : un port USB faible, un câble fin et long, un régulateur trop juste, ou des servos et des rubans LED alimentés par la même ligne que l'ESP32. La deuxième grande raison pour laquelle un ESP32 redémarre sans arrêt, ce sont les plantages logiciels ; dans ce cas le moniteur série affiche des lignes comme Guru Meditation Error, task_wdt ou Stack canary.
La bonne nouvelle : l'ESP32 te dit lui-même pourquoi il a redémarré, à condition de savoir lire ses messages. Dans ce guide, tu vas d'abord apprendre à reconnaître les codes rst des lignes de démarrage et les messages d'erreur, puis régler le brownout pour de bon grâce à l'alimentation, au câble et à un condensateur. Ensuite, nous verrons les erreurs Guru Meditation comme LoadProhibited et StoreProhibited, le débordement de pile, les pointeurs nuls et les délais du watchdog, et tu remonteras un plantage jusqu'à la ligne de code avec ESP Exception Decoder. Pour finir, nous regarderons les broches de strapping (GPIO0, 2, 12 et 15) qui peuvent perturber le démarrage.

Pourquoi l'ESP32 redémarre-t-il sans arrêt ? Lis d'abord le message
Avant de commencer le diagnostic, ouvre le moniteur série à 115200 bauds et appuie sur le bouton EN de la carte. À chaque démarrage, le chargeur d'amorçage en ROM de l'ESP32 affiche d'abord une ligne de date comme "ets Jun 8 2016 00:22:57", puis une ligne du type rst:0x1 (POWERON_RESET),boot:0x13 (SPI_FAST_FLASH_BOOT). La partie rst indique pourquoi la carte a redémarré, la partie boot dans quel mode elle a démarré. Si la carte tourne en boucle, tu trouveras juste au-dessus de ces lignes le message d'erreur, les dernières paroles de l'exécution précédente. Le tableau résume les messages que tu verras et par où commencer :
| Ce que tu vois dans le moniteur série | Signification | Où chercher |
|---|---|---|
| Brownout detector was triggered | La tension d'alimentation est passée sous le seuil | Alimentation, câble, condensateur |
| Guru Meditation Error: Core 1 panic'ed (LoadProhibited) | Le code a voulu lire à une adresse invalide | Pointeur nul, limites d'un tableau |
| Guru Meditation Error: Core 1 panic'ed (StoreProhibited) | Le code a voulu écrire à une adresse invalide | Pointeur nul, tampon qui déborde |
| Stack canary watchpoint triggered (loopTask) | La pile (stack) d'une tâche est pleine | Grands tableaux locaux, récursivité |
| task_wdt: Task watchdog got triggered | Une tâche n'a pas libéré le processeur pendant plus de 5 secondes | Boucles sans pause, tâches bloquées |
| Interrupt wdt timeout on CPU0 / CPU1 | Une interruption ou des interruptions désactivées ont dépassé 300 ms | ISR trop longue, Serial dans une ISR |
| boot:0x3 (DOWNLOAD_BOOT...) waiting for download | La carte a démarré en mode programmation | GPIO0 à LOW au démarrage |
| invalid header ou flash read err | La mémoire flash n'a pas pu être lue | GPIO12 à HIGH au démarrage, téléversement interrompu |
Que signifie "Brownout detector was triggered" et d'où vient-il ?
Un brownout est une baisse de tension brève, sans coupure complète. L'ESP32 contient un détecteur de brownout qui surveille l'alimentation en permanence ; dans le core Arduino, son seuil est réglé au niveau le plus bas, environ 2,43 V. Dès que la tension passe sous cette valeur, la puce protège la radio et la mémoire flash, écrit "Brownout detector was triggered" dans le moniteur série et redémarre. La raison est simple : à basse tension, la flash peut être mal lue, les données enregistrées peuvent être corrompues et le processeur peut exécuter des instructions au hasard. Le détecteur fait redémarrer proprement la carte au lieu de la laisser se comporter de façon imprévisible. Le module ESP32-WROOM-32 est conçu pour fonctionner entre 3,0 et 3,6 V ; quand la ligne atteint 2,43 V, elle a donc quitté la zone sûre depuis longtemps.
Après un message de brownout, la ligne de démarrage affiche rst:0xc (SW_CPU_RESET) ou rst:0x3 (SW_RESET) selon la version du core Arduino, car l'interruption du détecteur écrit le message puis redémarre la puce par logiciel. Ne te laisse pas tromper : ce même code note dans la mémoire de la puce que la cause était un brownout, et esp_reset_reason() renvoie ESP_RST_BROWNOUT. Quand c'est la remise à zéro matérielle du brownout qui agit, la ligne indique rst:0xf (RTCWDT_BROWN_OUT_RESET). Dans les cores 3.x, basés sur ESP-IDF 5, cette ligne est devenue un message de journal (log) de niveau info (I BOD: Brownout detector was triggered). Comme les bibliothèques précompilées du core Arduino n'écrivent que les messages de niveau erreur, elle n'apparaît le plus souvent pas du tout et la carte redémarre en silence ; si tu vois un rst:0x3 ou rst:0xc inexpliqué au démarrage, vérifie la cause du redémarrage avec le croquis de diagnostic plus bas. Pour comprendre pourquoi la tension chute, il faut regarder le courant consommé par l'ESP32 :
| État de fonctionnement | Courant typique (puce ESP32) |
|---|---|
| Sommeil profond (minuterie RTC active) | Environ 10 µA |
| Sommeil léger (light sleep) | Environ 0,8 mA |
| Wi-Fi coupé, processeur à 240 MHz | 30–68 mA |
| Réception Wi-Fi (RX) | 95–100 mA |
| Émission Wi-Fi, 802.11b, 19,5 dBm | Environ 240 mA |
| Calibration radio au démarrage et moment de la connexion | Peut dépasser 400 mA lors de pics de quelques microsecondes |
Cet écart est le cœur du problème : tant que ton code n'active pas le Wi-Fi, la carte tourne tranquillement à environ 50 mA, mais à la ligne WiFi.begin() le courant est multiplié par cinq ou six en quelques microsecondes. Si l'alimentation, le câble et le régulateur ne suivent pas cette demande soudaine, la tension s'effondre. C'est pourquoi Espressif recommande pour l'ESP32 une alimentation capable de fournir au moins 500 mA. Le symptôme est bien connu : la carte démarre, écrit quelques lignes dans le moniteur série, redémarre au moment où le Wi-Fi ou le Bluetooth se lance, et cela tourne en boucle sans fin. La même chose peut arriver quand un servo bouge, qu'un relais commute ou qu'un ruban LED passe au blanc.
Les causes les plus fréquentes du brownout
- Câble USB fin ou long : les fils d'alimentation fins des câbles bon marché peuvent atteindre 0,5 Ω aller-retour. D'après la loi d'Ohm, un pic de 400 mA perd 0,2 V dans un tel câble.
- Port USB faible ou hub : les ports des claviers et des écrans, les hubs non alimentés et les ports en façade de certains ordinateurs portables laissent chuter la tension lors des pics de courant.
- Chute de tension du régulateur : l'AMS1117 de la plupart des cartes a besoin de 1,0 à 1,3 V d'écart entre son entrée et sa sortie. Si la tension sur la broche 5V descend sous environ 4,3 V, la sortie ne peut plus tenir 3,3 V.
- Brancher une batterie directement : une batterie lithium de 3,7 V sur la broche 5V fait tomber la sortie sous 3 V à cause de la chute du régulateur ; deux piles AA passent vite sous le minimum de l'ESP32 en charge.
- La broche 3.3V de l'Arduino Uno : il est conseillé de ne tirer qu'environ 50 mA de cette broche ; c'est loin d'être suffisant pour un ESP32 avec le Wi-Fi actif.
- Charges sur la même ligne : si des servos, des relais, un buzzer, des moteurs à courant continu ou un ruban WS2812B sont alimentés par le même 5 V que l'ESP32, chaque mouvement tire la tension vers le bas.
- Breadboard et fils de connexion : les contacts lâches et les fils dupont fins ajoutent un peu de résistance à chaque connexion ; faire passer l'alimentation par la breadboard aggrave le problème.
- ESP32-CAM : quand la caméra, le Wi-Fi et la LED flash fonctionnent en même temps, le courant monte beaucoup ; cette carte veut du 5 V et au moins 2 A. Plus de détails dans le guide ESP32-CAM.
Solution 1 : améliore l'alimentation et le câble
La première étape, et la plus efficace, consiste à renforcer l'alimentation. Branche la carte avec un câble de données court, épais et dont tu es sûr, directement sur un port USB à l'arrière de ton ordinateur ou sur un chargeur 5 V d'au moins 1 A. Les ports USB 2.0 sont prévus pour fournir 500 mA et les ports USB 3.0, 900 mA ; les 500 mA recommandés par Espressif sont pile la limite d'un port USB 2.0, et dès qu'on ajoute les pertes du câble, les ennuis commencent. Si changer seulement de câble règle l'erreur, sa résistance était très probablement en cause. Tu peux calculer la tension perdue dans un câble avec V = I × R grâce à l'outil Loi d'Ohm.

| Montage d'alimentation | Risque de brownout | Remarque |
|---|---|---|
| Câble court et épais, port USB arrière | Faible | La solution la plus simple pendant le développement |
| Câble long et fin, port en façade ou hub | Élevé | La résistance du câble fait chuter la tension lors des pics |
| Chargeur 5 V d'au moins 1 A sur la broche 5V ou VIN | Faible | Recommandé pour les projets permanents |
| Source 3,3 V toute prête directement sur la broche 3V3 | Dépend de la source | Doit fournir au moins 500 mA et ne jamais dépasser 3,6 V |
| Batterie lithium 3,7 V via l'AMS1117 | Élevé | Sous environ 4 V côté batterie, la sortie descend sous 3 V |
| Batterie lithium avec régulateur 3,3 V à faible chute (LDO) ou buck-boost | Faible | Fournit 3,3 V stables même quand la batterie se décharge |
| Deux piles AA directement sur la broche 3V3 | Très élevé | En charge, la tension passe vite sous le minimum de l'ESP32 |
Les projets sur batterie demandent de la prudence. Un accu lithium comme le 18650 donne 4,2 V chargé et environ 3,0 V déchargé. Cette plage ne convient ni à un branchement direct sur la broche 3V3 (4,2 V dépasse la limite de 3,6 V) ni à la broche 5V avec son AMS1117. La bonne solution consiste soit à envoyer la batterie sur la broche 5V via un module élévateur (boost) 5 V, soit à alimenter directement la ligne 3V3 avec un régulateur LDO à faible chute capable de fournir au moins 500 mA (par exemple ME6211 ou HT7833) et un condensateur. Tu peux estimer l'autonomie de ton projet avec l'outil Autonomie de batterie et découvrir le circuit de charge dans le guide du module de charge TP4056.
Solution 2 : ajoute un condensateur entre 3V3 et GND
Un condensateur agit comme un petit réservoir qui fournit lui-même l'énergie pendant un pic de courant, le temps que le régulateur et le câble réagissent. Place entre les broches 3V3 et GND, aussi près que possible de la carte, un condensateur électrolytique de 100 à 470 µF et, à côté, un condensateur céramique de 100 nF. L'électrolytique couvre les chutes de l'ordre de la milliseconde, le céramique les variations très brèves à haute fréquence. Tu peux estimer l'effet avec ΔV = I × Δt / C : même si un condensateur de 470 µF fournit à lui seul 200 mA de plus pendant 200 µs, la tension ne baisse que de 0,085 V. Le marquage 104 sur un condensateur céramique signifie 100 nF ; tu peux décoder les autres avec l'outil Code des condensateurs.

Solution 3 : alimente à part les servos, les moteurs et les rubans LED
Même si l'ESP32 lui-même est bien alimenté, les charges branchées sur la même ligne peuvent faire chuter la tension. Même un petit servo comme le SG90 tire quelques centaines de mA en démarrant ; une LED WS2812B consomme environ 60 mA en blanc maximal, donc un ruban de 30 LED peut monter à 1,8 A. Alimente ces charges non pas par la broche 5V ou 3V3 de l'ESP32, mais par une source 5 V séparée ; tu peux par exemple abaisser un chargeur 12 V à 5 V avec un module abaisseur LM2596. La règle la plus importante est de relier les GND des deux sources ; sans masse commune, la ligne de signal n'a pas de référence et le servo ou le ruban se comporte au hasard. Nous détaillons ce sujet dans Ton Arduino redémarre tout seul ? et GND et masse commune.

Désactiver le détecteur de brownout, est-ce une solution ?
La "solution" la plus souvent proposée sur Internet consiste à couper le détecteur en une seule ligne. Le code ci-dessous désactive le détecteur en remettant à zéro le registre de brownout de l'ESP32, et le message d'erreur disparaît. Mais les chutes de tension continuent ; simplement, plus personne ne te prévient. N'utilise ce code que pour le diagnostic : si la carte fonctionne bien avec le détecteur coupé, tu as la confirmation que le problème vient bien de l'alimentation. Supprime ensuite la ligne et répare l'alimentation.
1#include "soc/soc.h"
2#include "soc/rtc_cntl_reg.h"
3
4void setup() {
5 // YALNIZ TEŞHİS İÇİN: brownout dedektörünü kapat / DIAGNOSIS ONLY: disable the brownout detector
6 WRITE_PERI_REG(RTC_CNTL_BROWN_OUT_REG, 0);
7
8 Serial.begin(115200);
9 Serial.println("Brownout dedektoru kapali / brownout detector disabled");
10}
11
12void loop() {
13}Codes rst : l'ESP32 te dit pourquoi il a redémarré
La valeur rst: affichée par la ROM à chaque démarrage indique comment s'est terminée l'exécution précédente. Lors d'un démarrage normal, la valeur boot est 0x13 (SPI_FAST_FLASH_BOOT), c'est-à-dire que la carte lance son programme depuis la flash. Le tableau résume les codes que tu rencontreras le plus souvent. Les codes de la ROM ne suffisent pas toujours : ESP.restart() et le redémarrage après un Guru Meditation apparaissent tous deux avec le code 0xc, et un brownout s'affiche en 0xc ou en 0x3 selon la version du core. Pour les distinguer, utilise le croquis avec esp_reset_reason() de la section suivante.
| Code rst | Nom | Signification |
|---|---|---|
| 0x1 | POWERON_RESET | Première mise sous tension ou appui sur le bouton EN |
| 0x3 | SW_RESET | Le logiciel a réinitialisé tout le système numérique ; les versions récentes du core l'affichent aussi après un brownout |
| 0x5 | DEEPSLEEP_RESET | La carte s'est réveillée du sommeil profond (normal) |
| 0x7 / 0x8 | TG0WDT_SYS_RESET / TG1WDT_SYS_RESET | Un watchdog de groupe de minuteries a réinitialisé le système matériellement ; les interruptions sont restées désactivées trop longtemps ou même le gestionnaire de panique n'a pas pu s'exécuter |
| 0x9 | RTCWDT_SYS_RESET | Le watchdog du RTC a réinitialisé le cœur numérique |
| 0xc | SW_CPU_RESET | Redémarrage logiciel : ESP.restart(), après un Guru Meditation ou après un brownout dans le core Arduino |
| 0xf | RTCWDT_BROWN_OUT_RESET | Réinitialisation matérielle par brownout : tension d'alimentation instable |
| 0x10 | RTCWDT_RTC_RESET | Le watchdog du RTC a réinitialisé toute la puce, RTC compris ; s'observe surtout sur les cartes qui se bloquent au démarrage |
Croquis de diagnostic : afficher la cause du redémarrage, la mémoire et la pile
Téléverse le code ci-dessous comme croquis séparé, ou ajoute ses lignes dans le setup() et le loop() de ton projet à problème. À chaque démarrage, la carte écrit dans le moniteur série la cause du redémarrage en texte lisible, le nombre de démarrages et l'état de la mémoire. Le compteur marqué RTC_DATA_ATTR est stocké dans la mémoire RTC : il survit donc aux redémarrages logiciels et au sommeil profond, et n'est remis à zéro que lorsque l'alimentation est complètement coupée. Si le compteur augmente toutes les quelques secondes, tu sais tout de suite que la carte est coincée dans une boucle de redémarrages.
1#include <esp_system.h>
2
3RTC_DATA_ATTR uint32_t bootCount = 0; // yazılım resetinde korunur / survives software resets
4
5// Reset nedenini okunabilir yazıya çevir / turn the reset reason into readable text
6const char* reasonText(esp_reset_reason_t r) {
7 switch (r) {
8 case ESP_RST_POWERON: return "POWERON: guc acilisi ya da EN / power-on or EN button";
9 case ESP_RST_SW: return "SW: ESP.restart()";
10 case ESP_RST_PANIC: return "PANIC: Guru Meditation";
11 case ESP_RST_INT_WDT: return "INT_WDT: kesme watchdog'u / interrupt watchdog";
12 case ESP_RST_TASK_WDT: return "TASK_WDT: gorev watchdog'u / task watchdog";
13 case ESP_RST_WDT: return "WDT: diger watchdog / other watchdog";
14 case ESP_RST_DEEPSLEEP: return "DEEPSLEEP: derin uykudan uyanma / deep sleep wake-up";
15 case ESP_RST_BROWNOUT: return "BROWNOUT: gerilim dusmesi / supply voltage dropped";
16 default: return "UNKNOWN: bilinmiyor / unknown";
17 }
18}
19
20void setup() {
21 Serial.begin(115200);
22 delay(1000); // Seri Monitör'ün açılmasını bekle / wait for the Serial Monitor
23 bootCount++;
24 Serial.printf("Acilis sayisi / boot count: %u\n", (unsigned)bootCount);
25 Serial.printf("Reset nedeni / reset reason: %s\n", reasonText(esp_reset_reason()));
26}
27
28void loop() {
29 // Bellek durumu / memory status
30 Serial.printf("Bos heap / free heap: %u B, en dusuk / minimum: %u B, en buyuk blok / largest block: %u B\n",
31 (unsigned)ESP.getFreeHeap(), (unsigned)ESP.getMinFreeHeap(), (unsigned)ESP.getMaxAllocHeap());
32 // loopTask yığınında hiç kullanılmamış en küçük alan / smallest unused space on the loopTask stack
33 Serial.printf("loopTask bos yigin / free stack: %u B\n", (unsigned)uxTaskGetStackHighWaterMark(NULL));
34 delay(5000);
35}Les valeurs affichées dans la boucle révèlent aussi des problèmes silencieux. Si le tas libre (free heap) baisse un peu à chaque tour, ton programme a une fuite de mémoire et la carte finira par planter. Si le plus grand bloc est bien plus petit que le tas libre, la mémoire est fragmentée ; cela vient généralement d'objets String qui grossissent et rétrécissent sans cesse dans la boucle. Si la pile libre de loopTask descend sous quelques centaines d'octets, le débordement de pile n'est pas loin. Si la cause du redémarrage est BROWNOUT, concentre-toi sur les sections sur l'alimentation ; si c'est PANIC, TASK_WDT ou INT_WDT, sur les sections logicielles qui suivent.
Qu'est-ce qu'un Guru Meditation Error ? Lis le rapport de plantage ligne par ligne
Un Guru Meditation Error est le rapport de plantage qu'affiche le gestionnaire de panique quand le processeur de l'ESP32 rencontre une erreur dont il ne peut pas se remettre ; le nom vient de l'écran d'erreur des anciens ordinateurs Amiga. Le rapport fait peur au premier abord, mais seules quelques lignes comptent. Core 1 indique sur quel cœur l'erreur s'est produite : avec Arduino, setup() et loop() tournent sur le cœur 1, et la pile Wi-Fi sur le cœur 0. Le nom entre parenthèses est le type d'exception. À la fin du rapport, la carte écrit "Rebooting..." et redémarre ; tu verras rst:0xc sur la ligne de démarrage.
| Ligne | Signification |
|---|---|
| Guru Meditation Error: Core 1 panic'ed (LoadProhibited). Exception was unhandled. | Le code du cœur 1 a voulu lire à une adresse invalide et l'erreur n'a pas été interceptée |
| PC : 0x400d1a2c | Adresse de l'instruction qui a planté ; si elle commence par 0x400d, ton code ou une bibliothèque s'exécutait depuis la flash |
| EXCCAUSE: 0x0000001c | Code de l'exception ; 0x1c, soit 28, signifie LoadProhibited |
| EXCVADDR: 0x00000000 | Adresse à laquelle on a voulu accéder ; 0 ou un très petit nombre désigne un pointeur nul |
| Backtrace: 0x400d1a29:0x3ffb2270 0x400d0f6e:0x3ffb2290 ... | Chaîne d'appels ; chaque paire a la forme PC:SP et le décodeur la traduit en ligne de code |
| ELF file SHA256: ... | Empreinte du programme qui a planté ; le décodeur a besoin exactement de la même compilation |
| Rebooting... | Le gestionnaire de panique redémarre la puce par logiciel |
LoadProhibited, StoreProhibited et les autres types d'exception
| Exception | EXCCAUSE | Que s'est-il passé ? | Cause fréquente |
|---|---|---|---|
| LoadProhibited | 28 (0x1c) | Lecture à une adresse invalide | Pointeur nul ou non initialisé, objet supprimé |
| StoreProhibited | 29 (0x1d) | Écriture à une adresse invalide | Écriture via un pointeur nul, tampon qui dépasse le tableau |
| InstrFetchProhibited | 20 (0x14) | Lecture d'instruction à une adresse invalide | Pointeur de fonction ou callback non assigné |
| IllegalInstruction | 0 (0x0) | Instruction invalide | Pile corrompue, return oublié dans une fonction qui doit renvoyer une valeur |
| LoadStoreAlignment | 9 (0x9) | Accès mémoire non aligné | Convertir de force un tableau d'octets en un type comme uint32_t* |
| IntegerDivideByZero | 6 (0x6) | Division entière par zéro | Diviser par un compteur qui peut valoir zéro |
| Unhandled debug exception | - | Exception de débogage | Stack canary watchpoint triggered : débordement de pile |
| Interrupt wdt timeout on CPU0/CPU1 | - | Le watchdog des interruptions a expiré | ISR trop longue, Serial ou delay dans une ISR |
| Cache disabled but cached memory region accessed | - | Accès à du code en flash pendant que la flash était occupée | Fonction d'interruption sans IRAM_ATTR |
La plus courante de ces erreurs est l'utilisation d'un pointeur nul. NULL correspond à l'adresse 0, et sur l'ESP32 il n'y a pas de mémoire lisible autour de l'adresse 0 : lire là où pointe un pointeur vide provoque donc LoadProhibited, y écrire provoque StoreProhibited. Dans ce cas, EXCVADDR est un indice parfait : 0x00000000 désigne le pointeur lui-même, et de petites valeurs comme 0x00000004 ou 0x00000010 un champ à l'intérieur d'un pointeur vide (NULL plus la position du champ). Dans les projets Arduino, la cause la plus fréquente est une fonction qui renvoie NULL pour dire "pas trouvé", valeur ensuite utilisée sans vérification. Dans l'exemple ci-dessous, strstr() renvoie NULL s'il ne trouve pas le texte ; sans vérification, la carte plante avec EXCVADDR 0x00000005.
1const char* response = "temp=23.5;hum=40"; // sunucudan gelen yanıt / reply from a server
2
3void setup() {
4 Serial.begin(115200);
5 delay(500);
6
7 // YANLIŞ: "pres=" yoksa strstr NULL döner ve kart çöker (EXCVADDR 0x00000005)
8 // WRONG: if "pres=" is missing strstr returns NULL and the board crashes (EXCVADDR 0x00000005)
9 // float pressure = atof(strstr(response, "pres=") + 5);
10
11 // DOĞRU: işaretçiyi kullanmadan önce NULL kontrolü yap / RIGHT: check for NULL before using the pointer
12 const char* found = strstr(response, "pres=");
13 if (found != nullptr) {
14 float pressure = atof(found + 5);
15 Serial.println(pressure);
16 } else {
17 Serial.println("pres alani bulunamadi / pres field not found");
18 }
19}
20
21void loop() {
22}Débordement de pile : l'erreur "Stack canary watchpoint triggered"
Sur l'ESP32, chaque tâche (task) possède une pile (stack) de taille fixe, où sont stockés les variables locales, les appels de fonctions et les adresses de retour. Avec Arduino, la tâche loopTask qui exécute setup() et loop() a une pile de 8192 octets. Déclarer un tableau local de 10 000 octets dans loop(), une fonction qui s'appelle elle-même (récursive) ou une fonction de bibliothèque avec beaucoup de variables locales dépasse cette limite. À la fin de la pile se trouve une zone de garde (canary) ; quand la pile l'atteint, la carte écrit Guru Meditation Error: Core 1 panic'ed (Unhandled debug exception) et juste en dessous Debug exception reason: Stack canary watchpoint triggered (loopTask). Le nom entre parenthèses est celui de la tâche dont la pile a débordé. Le même problème apparaît parfois sous la forme ***ERROR*** A stack overflow in task loopTask has been detected. Voici comment le régler :
- Sors les grands tableaux de la pile : déclare le tableau en global ou ajoute static devant ; il est alors placé dans la RAM permanente et non sur la pile.
- Agrandis la pile de loopTask : écris SET_LOOP_TASK_STACK_SIZE(16 * 1024); tout en haut du croquis pour porter la pile de loop() à 16 Ko.
- Donne assez de place à tes tâches : sur l'ESP32, la taille de pile passée à xTaskCreate et xTaskCreatePinnedToCore s'exprime en octets, et non en mots (words) comme avec FreeRTOS sur d'autres plateformes. 1024 octets ne suffisent même pas pour une tâche qui utilise Serial.printf ; commence avec 4096.
- Mesure : uxTaskGetStackHighWaterMark(NULL) renvoie, en octets, la plus petite quantité de pile que la tâche n'a encore jamais utilisée ; si elle descend sous quelques centaines d'octets, agrandis la pile.
- Évite la récursivité : transforme les fonctions qui s'appellent elles-mêmes en boucles ou limite leur profondeur.
Watchdog des tâches : l'erreur "task_wdt: Task watchdog got triggered"
Un watchdog (chien de garde) est une minuterie qui détecte un programme bloqué et sauve la carte. Le watchdog des tâches (Task WDT) de l'ESP32 attend que les tâches qui lui sont inscrites donnent signe de vie à intervalles réguliers. Dans le core Arduino, il surveille par défaut la tâche inactive (IDLE) du cœur 0, avec un délai de 5 secondes. Si une tâche du cœur 0 ne libère pas le processeur pendant 5 secondes, la tâche IDLE ne peut pas s'exécuter et le moniteur série affiche un message qui commence par E (10257) task_wdt: Task watchdog got triggered. Viennent ensuite la liste des tâches qui n'ont pas réarmé le watchdog à temps (le plus souvent - IDLE (CPU 0)) puis, sous Tasks currently running:, les tâches en cours d'exécution à cet instant. Dans le core Arduino, cela déclenche une panique avec la ligne task_wdt: Aborting. et la carte redémarre.
Les causes les plus fréquentes sont : une boucle placée sur le cœur 0 avec xTaskCreatePinnedToCore sans le moindre vTaskDelay(), une boucle while qui attend la réponse d'un capteur sans délai maximal, ou des delay() et des opérations longues dans les fonctions de rappel (callbacks) de bibliothèques comme AsyncWebServer. Dans ce dernier cas, tu verras la tâche async_tcp dans la liste à la place d'IDLE. La solution consiste à découper les longs traitements en petits morceaux et à ajouter de courtes attentes comme vTaskDelay(1) dans les boucles. La fonction loop() elle-même n'est pas surveillée par défaut ; si tu veux, appelle enableLoopWDT() dans setup() pour que la carte redémarre aussi quand loop() reste bloquée plus de 5 secondes. Couper le watchdog avec disableCore0WDT() masque l'erreur mais ne corrige pas le code bloqué. Pour en savoir plus sur les tâches et les cœurs, consulte FreeRTOS et double cœur sur l'ESP32. Le code ci-dessous montre une structure de tâche correcte et les précautions contre le débordement de pile :
1SET_LOOP_TASK_STACK_SIZE(16 * 1024); // loop() yığını 8 KB yerine 16 KB / loop() stack 16 KB instead of 8 KB
2
3volatile uint32_t sampleCount = 0;
4
5// 0. çekirdekte çalışan görev / task running on core 0
6void sensorTask(void* parameter) {
7 for (;;) {
8 sampleCount = sampleCount + 1; // sensörünü burada oku / read your sensor here
9 vTaskDelay(pdMS_TO_TICKS(10)); // IDLE görevine sıra ver, task_wdt tetiklenmez / let IDLE run, no task_wdt
10 }
11}
12
13void setup() {
14 Serial.begin(115200);
15 // Yığın boyutu bayt cinsindendir: 4096 bayt / stack size is in bytes: 4096 bytes
16 xTaskCreatePinnedToCore(sensorTask, "sensorTask", 4096, NULL, 1, NULL, 0);
17}
18
19void loop() {
20 static uint8_t buffer[10000]; // static: yığında değil kalıcı RAM'de / static: in RAM, not on the stack
21 memset(buffer, 0, sizeof(buffer));
22
23 Serial.printf("Ornek / samples: %u, loopTask bos yigin / free stack: %u B\n",
24 (unsigned)sampleCount, (unsigned)uxTaskGetStackHighWaterMark(NULL));
25 delay(1000);
26}Interrupt wdt timeout et IRAM_ATTR : plantages dans les interruptions
Le watchdog des interruptions (Interrupt WDT) est bien plus impatient : si une routine d'interruption (ISR) ou un morceau de code avec les interruptions désactivées dépasse 300 ms, la carte plante avec Guru Meditation Error: Core 1 panic'ed (Interrupt wdt timeout on CPU1). Utiliser Serial.print, delay(), l'I2C ou le Wi-Fi dans une ISR est la cause la plus fréquente. La mémoire flash est un autre piège : pendant que Preferences, EEPROM ou LittleFS écrivent dans la flash, le cache de la flash est coupé un instant. Si une interruption arrive à ce moment et que sa fonction se trouve en flash, la carte affiche Cache disabled but cached memory region accessed. La solution consiste à marquer les ISR avec IRAM_ATTR pour les placer en RAM et à n'y lever qu'un drapeau ; le vrai travail est fait dans loop(). Les variables partagées entre une ISR et loop() doivent être volatile.
1const int BUTTON_PIN = 27; // buton GPIO 27 ile GND arasında / button between GPIO 27 and GND
2volatile bool buttonPressed = false; // ISR ile loop arasında paylaşılan bayrak / flag shared by the ISR and loop
3uint32_t pressCount = 0;
4
5void IRAM_ATTR onButton() { // IRAM_ATTR: fonksiyon flaşta değil RAM'de durur / the function lives in RAM, not flash
6 buttonPressed = true; // yalnızca bayrak kaldır, Serial yok / only set a flag, no Serial
7}
8
9void setup() {
10 Serial.begin(115200);
11 pinMode(BUTTON_PIN, INPUT_PULLUP);
12 attachInterrupt(digitalPinToInterrupt(BUTTON_PIN), onButton, FALLING);
13}
14
15void loop() {
16 if (buttonPressed) {
17 buttonPressed = false;
18 pressCount++;
19 Serial.printf("Butona basildi / button pressed: %u\n", (unsigned)pressCount); // yavaş iş burada / slow work here
20 }
21}Traduire le Backtrace en ligne de code avec ESP Exception Decoder
Les adresses de la ligne Backtrace ne disent rien à elles seules ; l'outil qui les traduit en fichier, fonction et numéro de ligne de ton code source s'appelle ESP Exception Decoder. Dans l'ancien IDE Arduino 1.8, c'était un module ajouté au menu Outils. Dans l'IDE Arduino 2, on utilise une extension de la communauté qui porte le même nom :
- Télécharge le fichier avec l'extension .vsix dans la section Releases du projet dankeboy36/esp-exception-decoder sur GitHub. Pour l'IDE Arduino 2, choisis une version 1.x ; les nouvelles versions 2.x sont prévues pour VS Code.
- Ferme l'IDE Arduino et copie le fichier dans %UserProfile%\.arduinoIDE\plugins sous Windows ou dans ~/.arduinoIDE/plugins sous macOS et Linux ; crée le dossier s'il n'existe pas.
- Ouvre l'IDE et le croquis qui plante, compile-le avec Vérifier et téléverse-le sur la carte.
- Ouvre le moniteur série et attends que le plantage se reproduise.
- Ouvre la palette de commandes avec Ctrl+Maj+P (Cmd+Shift+P sous macOS) et lance ESP Exception Decoder: Show Decoder Terminal.
- Copie dans le moniteur série le bloc Guru Meditation avec la ligne Backtrace et colle-le dans le terminal qui s'ouvre ; l'outil affiche chaque adresse avec le nom du fichier, la fonction et le numéro de ligne.
Si tu ne peux pas installer l'extension, tu peux faire la même chose à la main. Le menu Sketch > Export Compiled Binary dépose un fichier avec l'extension .elf dans le dossier build, à l'intérieur du dossier du croquis. La commande xtensa-esp32-elf-addr2line -pfiaC -e sketch.ino.elf 0x400d1a29 0x400d0f6e de la chaîne d'outils de l'ESP32 traduit ces adresses en noms de fonctions et numéros de ligne. Si tu utilises PlatformIO, il suffit d'ajouter la ligne monitor_filters = esp32_exception_decoder dans le fichier platformio.ini ; le rapport de plantage est alors décodé automatiquement dans le moniteur série.
Broches de strapping : comment GPIO0, GPIO2, GPIO12 et GPIO15 perturbent le démarrage
Au démarrage, l'ESP32 regarde le niveau de certaines broches pour décider comment démarrer. Ce sont les broches de strapping, et seul leur niveau au moment de la réinitialisation compte ; une fois le démarrage terminé, elles s'utilisent comme des GPIO normales. Le problème survient quand un capteur, un bouton ou un module branché dessus tire la broche au mauvais niveau au démarrage. La carte ne lance alors pas son programme, passe en mode programmation ou ne peut pas lire la flash ; vu de l'extérieur, cela ressemble aussi à "l'ESP32 ne démarre pas" ou à "il redémarre sans arrêt". Tu peux voir le brochage complet de ta carte dans l'outil Pinout.
| Broche | Résistance interne | Rôle au démarrage | Que se passe-t-il au mauvais niveau ? |
|---|---|---|---|
| GPIO0 | Pull-up | LOW active le mode programmation (download), HIGH le démarrage normal | boot:0x3 (DOWNLOAD_BOOT) et waiting for download ; le programme ne s'exécute pas |
| GPIO2 | Pull-down | Doit être à LOW ou flottante pour entrer en mode programmation ; sans effet au démarrage normal | Un circuit qui la tire à HIGH empêche le téléversement |
| GPIO12 (MTDI) | Pull-down | Choisit la tension de la flash : LOW 3,3 V, HIGH 1,8 V | À HIGH, une flash 3,3 V est mal alimentée ; la carte ne démarre pas ou entre dans une boucle de redémarrages |
| GPIO15 (MTDO) | Pull-up | À LOW, rend muets les messages de démarrage de la ROM | Les lignes de démarrage n'apparaissent pas dans le moniteur série, le diagnostic devient plus difficile |
| GPIO5 | Pull-up | Définit la temporisation SDIO | Pose rarement problème sur les cartes de développement |
Les cartes plus récentes ont d'autres broches de strapping : sur l'ESP32-S3, ce sont GPIO0, GPIO3, GPIO45 et GPIO46 qui influencent le démarrage, et sur l'ESP32-C3, GPIO2, GPIO8 et GPIO9. L'ESP32-S3 utilise les mêmes noms d'exception Xtensa que l'ESP32 classique. Sur l'ESP32-C3 à cœur RISC-V, en revanche, tu verras Load access fault au lieu de LoadProhibited et Store access fault au lieu de StoreProhibited, et le registre MTVAL joue le rôle d'EXCVADDR. Le message de brownout est le même sur tous les modèles.
ESP32 et Raspberry Pi Pico avec MicroPython
Si tu programmes l'ESP32 en MicroPython, le brownout se produit exactement de la même façon, car le problème est matériel et non logiciel. Les erreurs du code Python, elles, apparaissent sous forme de Traceback lisible au lieu d'un Guru Meditation, et la carte plante rarement. Tu peux lire la cause du redémarrage avec machine.reset_cause(), mais attention : la version de MicroPython pour ESP32 ne renvoie pas de valeur propre au brownout, elle le signale comme PWRON_RESET, au même titre qu'une mise sous tension. Si la carte redémarre sans cesse avec PWRON_RESET alors que tu n'y touches pas, soupçonne à nouveau l'alimentation. Le code ci-dessous est destiné à l'ESP32 ; sur le Raspberry Pi Pico, le module machine connaît moins de types de redémarrage. Sur le Pico, le régulateur buck-boost de l'entrée VSYS fonctionne avec toute tension entre 1,8 et 5,5 V, si bien que les problèmes de brownout y sont beaucoup plus rares ; pour découvrir la carte, lis Qu'est-ce que le Raspberry Pi Pico ?
1import machine, gc, time
2
3# Reset nedenleri (ESP32 için) / reset causes (for ESP32)
4causes = {
5 machine.PWRON_RESET: "PWRON: guc acilisi ya da brownout / power-on or brownout",
6 machine.HARD_RESET: "HARD: cokme ya da machine.reset() / crash or machine.reset()",
7 machine.WDT_RESET: "WDT: watchdog",
8 machine.DEEPSLEEP_RESET: "DEEPSLEEP: derin uykudan uyanma / deep sleep wake-up",
9 machine.SOFT_RESET: "SOFT: Ctrl+D ile yazilim reseti / soft reset with Ctrl+D",
10}
11print("Reset nedeni / reset cause:", causes.get(machine.reset_cause(), "bilinmiyor / unknown"))
12
13while True:
14 gc.collect() # çöp toplayıcıyı çalıştır / run the garbage collector
15 print("Bos RAM / free RAM:", gc.mem_free(), "bayt / bytes")
16 time.sleep(5)Erreurs fréquentes et solutions
| Symptôme | Cause probable | Solution |
|---|---|---|
| Brownout detector was triggered au démarrage du Wi-Fi | Le câble, le port ou le régulateur ne suit pas le pic de courant | Câble court et épais, chargeur 1 A, 470 µF + 100 nF entre 3V3 et GND |
| Redémarrage quand un servo tourne ou qu'un relais commute | La charge partage la ligne d'alimentation de l'ESP32 | Alimente la charge avec une source 5 V séparée et relie les GND |
| Les redémarrages commencent quand la batterie se vide un peu | Chute de tension de l'AMS1117 | Utilise un LDO ou un convertisseur buck-boost |
| Guru Meditation, EXCVADDR 0x00000000 | Pointeur nul | Vérifie le pointeur avant de l'utiliser |
| Stack canary watchpoint triggered (loopTask) | La pile de loop() est pleine | Rends les grands tableaux static ou utilise SET_LOOP_TASK_STACK_SIZE |
| task_wdt: - IDLE (CPU 0) | Boucle sans pause sur le cœur 0 | Ajoute vTaskDelay dans la boucle |
| Interrupt wdt timeout on CPU1 | ISR trop longue | Ne lève qu'un drapeau dans l'ISR, utilise IRAM_ATTR |
| Carte muette, boot:0x3 et waiting for download | GPIO0 à LOW au démarrage | Retire le circuit sur GPIO0 et appuie sur EN |
| La carte ne démarre pas du tout, erreur de lecture de la flash | GPIO12 à HIGH au démarrage | Déplace le pull-up ou le module de GPIO12 vers une autre broche |
| Failed to connect to ESP32 au téléversement | GPIO2 à HIGH ou la carte n'entre pas en mode programmation | Libère GPIO2 et maintiens BOOT enfoncé au début du téléversement |
Ta carte est stable : et maintenant ?
- Appareil économe sur batterie : avec le deep sleep de l'ESP32, endors la carte entre deux mesures et descends à quelques microampères.
- Mises à jour sans fil : téléverse ton code sans câble USB grâce à l'OTA sur ESP32.
- Tableau de bord en temps réel : suis les données des capteurs en direct dans le navigateur avec WebSocket sur ESP32.
- Envoyer des notifications : avec un bot Telegram sur ESP32, reçois sur ton téléphone la cause du redémarrage à chaque fois que la carte redémarre.
- Spectacle lumineux : pilote en toute sécurité un ruban WS2812B NeoPixel avec une alimentation séparée.
La plupart des problèmes de redémarrage de l'ESP32 sont soit un souci d'alimentation, soit quelques lignes de code fautives ; quand tu lis correctement le message, les deux se règlent vite. Pour revoir les bases de la carte, jette un œil à Qu'est-ce que l'ESP32 ? Dans l'application BlueGrays, tu trouveras des projets ESP32 avec des LED, des servos, des rubans NeoPixel, la connexion Wi-Fi, un serveur web, les mises à jour OTA et le deep sleep, chacun avec son schéma de câblage et son code pas à pas, pour essayer dans de vrais projets les techniques d'alimentation et de diagnostic apprises ici.
Questions fréquentes
Comment corriger ESP32 Brownout detector was triggered ?
Cette erreur signifie que la tension d'alimentation de l'ESP32 est passée un instant sous environ 2,43 V. Utilise d'abord un câble USB court et épais avec une source 5 V d'au moins 1 A, puis ajoute un condensateur électrolytique de 100 à 470 µF et un céramique de 100 nF entre 3V3 et GND. Alimente les charges comme les servos, les relais et les rubans LED avec une source séparée et relie les GND.
Pourquoi mon ESP32 redémarre-t-il en boucle ?
Les causes les plus fréquentes sont une alimentation insuffisante (brownout), des plantages logiciels (Guru Meditation), des délais du watchdog et des broches de strapping au mauvais niveau au démarrage. Ouvre le moniteur série à 115200 bauds et appuie sur EN ; le dernier message avant le redémarrage et le code rst au démarrage indiquent la cause. La fonction esp_reset_reason() permet aussi de la lire depuis ton code.
Que veut dire Guru Meditation Error LoadProhibited ?
Cela signifie que le code de l'ESP32 a voulu lire à une adresse mémoire invalide. Si EXCVADDR vaut 0 ou un très petit nombre, un pointeur nul a presque sûrement été utilisé. Colle la ligne Backtrace dans ESP Exception Decoder pour trouver le fichier et la ligne de l'erreur.
Peut-on désactiver le détecteur de brownout de l'ESP32 ?
Oui, avec la ligne WRITE_PERI_REG(RTC_CNTL_BROWN_OUT_REG, 0), mais cela ne fait que faire taire l'alerte. Comme les chutes de tension continuent, les données en flash peuvent être corrompues et la carte peut planter avec d'autres erreurs. Ne l'utilise que brièvement pour confirmer que le problème vient de l'alimentation.
Comment corriger ESP32 Task watchdog got triggered ?
Ce message signifie qu'une tâche surveillée n'a pas libéré le processeur pendant 5 secondes ; avec Arduino, la liste montre généralement la tâche IDLE du cœur 0. Ajoute vTaskDelay() dans les boucles de tes propres tâches, utilise des délais maximaux quand tu attends un capteur et évite delay() ou les opérations longues dans les callbacks du serveur web. Couper le watchdog masque l'erreur mais n'en corrige pas la cause.
Quelle alimentation faut-il pour un ESP32 ?
Espressif recommande pour l'ESP32 une alimentation capable de fournir au moins 500 mA ; en pratique, un chargeur 5 V de 1 A laisse une marge sûre. Si tu utilises un ESP32-CAM ou ajoutes des charges comme des servos ou des rubans LED, choisis une source de 2 A ou plus et alimente si possible ces charges par une ligne séparée.
Projets à réaliser après cet article
Voir tous les projets
Clignotement LED avec ESP32
Enseigne l'utilisation de base des sorties GPIO avec l'ESP32. Nous obtenons un effet de clignotement en allumant et éteignant une LED à partir de la broche numérique. Version ESP32 du projet blink classique sur Arduino UNO ; les commandes de base de blink dans l'environnement Arduino restent les mêmes, mais le brochage et les niveaux GPIO de 3,3 V de l'ESP32 diffèrent de ceux de l'Arduino UNO.

Connexion Wi-Fi STA ESP32 (avec écran LCD)
Dans ce projet, nous connectons l'ESP32 au réseau Wi-Fi domestique en mode Station (STA) de manière sécurisée et tolérante aux pannes. Nous isolons SSID/mot de passe dans un fichier secrets.h pour empêcher leur fuite sur Git, mettons en place une reconnexion automatique, et affichons le niveau RSSI et l'adresse IP sur un écran LCD I2C. La LED intégrée sert d'indicateur d'état.

Servomoteur ESP32 (commande par potentiomètre)
Dans ce projet, nous contrôlons un servomoteur SG90 à l'aide d'un potentiomètre. Nous réglons l'angle du servomoteur en mappant la valeur analogique lue par le potentiomètre sur une plage de 0 à 180 degrés. La bibliothèque ESP32Servo génère des signaux de servomoteur sur les GPIO appropriés de l'ESP32 pouvant fonctionner en sortie ; ce projet utilise GPIO 18. Il s'agit d'une méthode de contrôle fondamentale pour les bras robotiques, les cardans de caméra et les mécanismes de portes automatiques.

Contrôle d'une bande NeoPixel WS2812B avec l'ESP32
Les LED adressables WS2812B (NeoPixel) sont des LED RGB intelligentes pouvant être contrôlées en série via une seule broche de données. Dans ce projet, nous allons contrôler une bande WS2812B de 8 LED à l'aide de la bibliothèque Adafruit_NeoPixel afin de créer une animation fluide représentant un arc-en-ciel. Bien que le niveau logique 3,3 V de l'ESP32 fonctionne sans problème avec la plupart des composants WS2812B, nous présenterons une solution comprenant une résistance en série de 470 Ω et un

Serveur Web asynchrone ESP32 (avec affichage de l'adresse IP sur l'écran LCD)
Dans ce projet, nous transformons l'ESP32 en un serveur web asynchrone que vous pouvez contrôler depuis votre téléphone ou votre ordinateur connecté au même réseau Wi-Fi. Grâce à la bibliothèque ESPAsyncWebServer, le serveur peut traiter plusieurs requêtes simultanément (contrairement au classique WebServer.h qui se bloque sur une seule requête). Depuis votre navigateur, vous pourrez vous connecter à l'adresse IP de l'ESP32, puis allumer et éteindre la LED co

Mode veille profonde de l'ESP32 — Économie de batterie (OLED + compteur RAM RTC)
Le mode « Deep Sleep » de l'ESP32 réduit la consommation de courant à environ 10 µA en désactivant le processeur et la plupart des périphériques, ce qui revêt une importance cruciale pour les appareils IoT fonctionnant sur batterie. Dans ce projet, nous utilisons trois sources de réveil différentes : une minuterie (timer, 15 s), une broche externe (EXT0 — bouton GPIO 33) et un capteur tactile (T0/GPIO 4). Les variables marquées avec RTC_DATA_ATTR sont conservées pendant le mode Deep Sleep — le

Mise à jour OTA de l'ESP32 — Installation sans fil du firmware (avec écran LCD)
Dans ce projet, vous allez apprendre à installer un nouveau firmware sur l'ESP32 via Wi-Fi, sans câble USB. Grâce à la bibliothèque ArduinoOTA, l'ESP32 devient visible sur le réseau et apparaît automatiquement sous le nom bluegrays-ota dans l'onglet « Network port » de l'IDE Arduino. La première installation s'effectue via USB, puis toutes les mises à jour se font sans fil — une fonctionnalité essentielle pour les appareils sur le terrain. L'écran LCD I2C affiche l'état du Wi-Fi, l'adresse I

Système d'Alarme de Mouvement ESP32 (PIR + Buzzer + LED + Telegram)
Un système d'alarme de sécurité intelligent qui active le buzzer et la LED lorsque le capteur PIR détecte un mouvement, tout en envoyant une notification instantanée au propriétaire via Telegram. Il intègre un délai d'attente de 30 secondes pour limiter la fréquence des alarmes répétées ; ce mécanisme ne vérifie pas la validité des détections du capteur PIR et n'élimine pas les faux déclenchements. Idéal pour la sécurité domestique, la surveillance de garage, d'entrepôts ou de bureaux. Avec l'ES
Autres articles
Application BlueGrays
Réalise ce montage pas à pas sur ton téléphone
Schéma, code et liste des composants sur un seul écran. Plus de 200 projets Arduino, ESP32 et Pico à réaliser.
Scanne avec ton téléphone
Pointe l’appareil photo vers le code : la boutique d’applications de ton téléphone s’ouvre.
ou ouvre la boutique

