Attention : 3,3 V, pas 5 V
Les GPIO de l’ESP32-S3 supportent 3,3 V maximum. Une broche mise en sortie produit 3,3 V, pas 5 V comme sur Arduino Uno. Un signal 5 V appliqué en entrée peut endommager la puce.
GPIO & monde numérique
Comprendre les GPIO numériques de l'ESP32-S3 : niveaux logiques, pull-up/pull-down et état flottant.
Pré-requis :
Jusqu’ici, le microcontrôleur réfléchissait dans son coin. Mais une puce qui ne fait que calculer ne sert à rien : pour être utile, elle doit agir sur le monde (allumer une LED, faire tourner un moteur) et le percevoir (savoir si tu appuies sur un bouton).
Ce contact avec le monde physique passe par les broches de la puce, ces petites pattes métalliques alignées sur les bords de la carte. Les plus polyvalentes s’appellent les GPIO, et ce concept est consacré aux plus simples d’entre elles : celles qui ne manipulent que du tout-ou-rien, comme un interrupteur mural qu’on bascule entre allumé et éteint. Pas de demi-mesure, et c’est justement ce qui rend le numérique si fiable.
À la fin de ce concept, tu sauras :
Le monde numérique ne connaît que deux états : 0 ou 1, LOW ou HIGH, 0 V ou 3,3 V. Tout signal intermédiaire est interprété selon un seuil.
Sur l’ESP32-S3 (logique 3,3 V) :
| État | Tension | Arduino |
|---|---|---|
| LOW | 0 V | 0 / false / LOW |
| HIGH | 3,3 V | 1 / true / HIGH |
En entrée, une broche ne bascule pas exactement à 0 V ou 3,3 V : elle compare la tension lue à deux seuils, et entre les deux l’état est indéterminé. La datasheet de l’ESP32-S3 (alimenté en 3,3 V) donne :
| Seuil | Valeur | Interprétation |
|---|---|---|
| $V_{IL}$ (entrée LOW max) | ≤ 0,25 × VDD ≈ 0,8 V | En dessous : lu comme LOW |
| Zone indéterminée | ~0,8 V à ~2,5 V | À éviter : lecture imprévisible |
| $V_{IH}$ (entrée HIGH min) | ≥ 0,75 × VDD ≈ 2,5 V | Au-dessus : lu comme HIGH |
Tout ce qui est sous ~0,8 V vaut 0, tout ce qui dépasse ~2,5 V vaut 1. Cette zone grise au milieu explique pourquoi une entrée flottante (voir plus bas) donne des lectures erratiques : sa tension traîne dans cette plage indéterminée.
En sortie, la puce garantit à l’inverse des niveaux francs : proche de 0 V pour LOW ($V_{OL}$) et proche de 3,3 V pour HIGH ($V_{OH}$).
Ces valeurs viennent du tableau DC Characteristics de la datasheet de l’ESP32-S3 (VDD = 3,3 V, 25 °C). C’est là qu’on trouve la réponse à la plupart des questions sur une broche :
| Symbole | Ce que c’est | Valeur |
|---|---|---|
| $V_{IH}$ | Tension minimale lue comme HIGH |
0,75 × VDD |
| $V_{IL}$ | Tension maximale lue comme LOW |
0,25 × VDD |
| $V_{OH}$ | Tension minimale garantie en sortie HIGH |
0,8 × VDD |
| $V_{OL}$ | Tension maximale garantie en sortie LOW |
0,1 × VDD |
| $I_{OH}$ | Courant que peut fournir une sortie HIGH |
40 mA |
| $R_{PU}$, $R_{PD}$ | Résistances de tirage internes | 45 kΩ |
Juste avant, le tableau des valeurs maximales absolues donne la limite à ne jamais dépasser : 3,6 V sur une broche. Au-delà, la puce peut être détruite ; entre 3,3 V et 3,6 V, elle survit mais n’est plus dans ses conditions normales.
Exemple : un capteur qui signale un HIGH par 2,2 V n’est pas lu de façon fiable, car 2,2 V est sous $V_{IH}$ ≈ 2,5 V.
Chaque broche GPIO (General Purpose Input/Output, broche d’entrée/sortie à usage général) peut être configurée en entrée ou en sortie par le logiciel.
pinMode(LED_PIN, OUTPUT); // broche configurée en sortie
pinMode(BTN_PIN, INPUT); // broche configurée en entrée
Une broche en sortie peut sourcer ou drainer du courant (le fournir ou l’absorber) pour piloter un composant.
digitalWrite(LED_PIN, HIGH); // LED allumée (3,3 V sur la broche)
digitalWrite(LED_PIN, LOW); // LED éteinte (0 V)
Courant maximal par broche ESP32-S3 : ~40 mA. En pratique, limiter à 12 mA avec une résistance série.
Calcul de la résistance pour une LED rouge (Vf, la tension de seuil de la LED, ≈ 2 V ; I = 10 mA) :
Valeur normalisée : 150 Ω (série E12).
Calculateur de résistance série pour LED
R ≈ 130 Ω valeur E12 conseillée : 150 Ω
Par défaut, OUTPUT configure la broche en push-pull : deux transistors internes permettent de tirer activement la broche à HIGH ou à LOW. C’est le mode utilisé jusqu’ici.
Il existe un second mode, open-drain : la broche ne peut que tirer vers LOW (un seul transistor, côté masse). Pour lire ou produire un HIGH, il faut une résistance de tirage (interne ou externe) qui « relâche » la broche vers HIGH quand personne ne la tire vers le bas.
pinMode(PIN, OUTPUT_OPEN_DRAIN); // la broche ne peut que tirer vers 0 V
digitalWrite(PIN, LOW); // tire activement à 0 V
digitalWrite(PIN, HIGH); // relâche la broche (haute impédance)
Push-pull (OUTPUT) |
Open-drain (OUTPUT_OPEN_DRAIN) |
|
|---|---|---|
| Peut tirer vers HIGH | Oui, activement | Non, nécessite un pull-up |
| Peut tirer vers LOW | Oui, activement | Oui, activement |
| Usage typique | LED, moteur, sortie numérique classique | Bus partagé (I2C), interfaçage avec un niveau logique différent |
Une broche en entrée mesure le niveau de tension présent sur la broche.
int etat = digitalRead(BTN_PIN); // retourne HIGH ou LOW
Une broche en entrée non connectée à rien est dans un état flottant : sa tension est indéterminée, elle peut lire HIGH ou LOW de façon aléatoire selon les interférences électromagnétiques.
flowchart TD
A["pinMode(PIN, INPUT)\nsans pull-up ni pull-down"] --> B["Broche en haute impédance\n(quasiment aucun courant ne peut y circuler)"]
B --> C{"Un signal externe\nimpose-t-il une tension ?"}
C -->|"Non - rien de branché"| D["Tension flottante\ncapte le moindre bruit ambiant"]
D --> E["digitalRead() imprévisible\nLOW, HIGH, ou oscille sans raison"]
C -->|"Oui - via une résistance de rappel"| F["Tension définie et stable\nLOW ou HIGH garanti au repos"]
Une broche en haute impédance présente une résistance d’entrée très élevée (plusieurs mégaohms) : elle n’absorbe quasiment aucun courant, donc rien ne la ramène vers 0 V ou 3,3 V. La moindre interférence électromagnétique environnante (un câble voisin, le secteur 50 Hz, ta propre main qui s’approche) suffit alors à déplacer sa tension et à faire basculer sa lecture. D’où le terme « flottant » : rien ne fixe sa tension à une valeur précise.
Une résistance de rappel (pull) force la broche à un niveau défini quand aucun signal n’est appliqué.
La résistance relie la broche au +3,3 V. Au repos, la broche lit HIGH. Quand le bouton est pressé (connexion à GND), elle passe à LOW.
graph LR
V33["3,3 V"] -->|"R (10 kΩ)"| NODE(("Broche GPIO"))
NODE -->|"Bouton"| GND["GND"]
La résistance relie la broche au GND. Au repos, la broche lit LOW. Quand le bouton est pressé (connexion à +3,3 V), elle passe à HIGH.
graph LR
GND["GND"] -->|"R (10 kΩ)"| NODE(("Broche GPIO"))
NODE -->|"Bouton"| V33["3,3 V"]
L’ESP32-S3 intègre des résistances de pull-up (et pull-down) en interne, activables par logiciel : pas besoin de résistance externe dans la plupart des cas.
pinMode(BTN_PIN, INPUT_PULLUP); // pull-up interne activé (~45 kΩ)
// ou
pinMode(BTN_PIN, INPUT_PULLDOWN); // pull-down interne activé
Avec INPUT_PULLUP : le bouton doit connecter la broche à GND pour être détecté (logique inversée : LOW = pressé).
Quand tu presses un bouton mécanique, le contact ne s’établit pas net : les lamelles métalliques rebondissent pendant quelques millisecondes, générant une rafale de HIGH/LOW avant de se stabiliser. Un digitalRead() naïf voit alors plusieurs appuis là où tu n’en as fait qu’un.
La parade la plus simple est logicielle : après un changement d’état, on ignore les lectures pendant un court délai (20 à 50 ms), grâce à millis() (le nombre de millisecondes écoulées depuis le démarrage), le temps que le contact se stabilise.
const unsigned long ANTIREBOND = 30; // ms
int dernierEtat = HIGH;
unsigned long dernierChangement = 0;
void loop() {
int etat = digitalRead(BTN_PIN); // INPUT_PULLUP : LOW = pressé
if (etat != dernierEtat) {
dernierChangement = millis(); // le signal vient de bouger
dernierEtat = etat;
}
if (millis() - dernierChangement > ANTIREBOND && etat == LOW) {
// bouton enfoncé de façon stable depuis 30 ms
// (vrai à chaque tour tant que le bouton reste enfoncé)
}
}
Scruter un bouton à chaque tour de loop() fonctionne, mais peut rater un appui si la boucle est occupée ailleurs. Une interruption exécute une petite fonction dès que la broche change, sans attendre (voir Architecture d’un microcontrôleur).
volatile bool appui = false;
void IRAM_ATTR surAppui() { appui = true; } // ISR : la plus courte possible
void setup() {
pinMode(BTN_PIN, INPUT_PULLUP);
attachInterrupt(digitalPinToInterrupt(BTN_PIN), surAppui, FALLING);
}
L’ISR (Interrupt Service Routine) se contente de lever un drapeau ; le vrai traitement (et l’anti-rebond) se fait ensuite tranquillement dans loop().
Au-delà de digitalRead()/digitalWrite(), une broche peut être prise en charge par un périphérique : UART, SPI, I2C, PWM (LEDC)… Sur beaucoup de microcontrôleurs, chaque fonction est câblée en dur sur des broches précises (« fonction alternative » fixe).
L’ESP32-S3 est plus flexible : une GPIO matrix interne permet de router presque n’importe quel signal numérique de périphérique vers presque n’importe quelle broche, par logiciel.
Serial1.begin(115200, SERIAL_8N1, D2, D3); // UART1 routé sur D2 (RX) / D3 (TX) de la XIAO
// une autre paire de broches fonctionnerait tout aussi bien
Si aucun périphérique ne convient, il reste possible de piloter les broches à la main, bit après bit, pour émuler un bus : c’est le bit-banging, plus lent et plus gourmand en temps processeur (voir Les bus de communication).
Malgré la souplesse de la GPIO matrix, certaines broches ont un rôle réservé au démarrage ou au fonctionnement interne de l’ESP32-S3. Les détourner en GPIO ordinaire peut empêcher la carte de démarrer, de se flasher ou de communiquer en USB.
| Broches | Rôle réservé | Précaution |
|---|---|---|
| GPIO 0, 3, 45, 46 | Strapping : lues au reset pour choisir le mode de boot | Éviter, ou ne rien y imposer comme niveau au démarrage |
| GPIO 19, 20 | USB natif (D− / D+) | Éviter si tu utilises l’USB (flash, port série) |
| GPIO 43, 44 | UART0 par défaut (TX / RX du moniteur série) | Libres si le port série n’est pas utilisé, sinon à éviter |
| GPIO 26–32 | Reliées à la Flash SPI interne | Ne jamais utiliser |
| GPIO 33–37 | Utilisées par la PSRAM sur les modules « octal » | Éviter sur les modules équipés de PSRAM |
Sur la XIAO ESP32S3 de l’atelier, ce tri est déjà fait par la carte : seules 11 broches sont sorties (D0 à D10) et aucune broche critique n’en fait partie. GPIO0 est relié au bouton BOOT, GPIO19/20 au connecteur USB-C, la Flash et la PSRAM restent internes. Deux cas particuliers :
| Limite | Valeur ESP32-S3 |
|---|---|
| Courant max par broche | 40 mA (recommandé : ≤ 12 mA) |
| Tension max en entrée | 3,3 V (3,6 V en valeur maximale absolue) |
Un moteur, un ruban de LED ou un relais demandent bien plus que les 12 mA d’une broche. La broche ne fournit donc pas la puissance : elle commande un transistor, qui la fournit depuis une alimentation séparée.
C’est le montage côté bas : un MOSFET canal N coupe ou rétablit la masse de la charge.
analogWrite() sur la grille, le PWM règle la vitesse du moteur (voir ADC & PWM).| Paramètre | Ce que c’est | À viser |
|---|---|---|
| $V_{GS(th)}$ | Tension de grille où il commence à conduire | Bien sous 3,3 V |
| $R_{DS(on)}$ | Résistance une fois passant : elle chauffe | Donnée à 2,5 V, quelques dizaines de mΩ |
| $I_D$ | Courant maximal | Au moins 2 × celui de la charge |
| $V_{DS}$ | Tension maximale | Au-dessus de l’alimentation de la charge |
Avec 3,3 V sur la grille, il faut un MOSFET « logic level », dont la datasheet donne $R_{DS(on)}$ à 2,5 V : AO3400 (CMS) ou IRLB8721 (traversant), par exemple. Un IRF540 classique attend 10 V sur sa grille : à 3,3 V, il conduit à peine et chauffe.
Une bobine (moteur, relais, électro-aimant) s’oppose à toute variation brutale de courant. Quand le MOSFET coupe, elle produit une pointe de plusieurs dizaines de volts qui détruit le transistor. La parade est une diode placée aux bornes de la charge, cathode côté + : elle laisse le courant s’éteindre en tournant dans la bobine.
Les GPIO de l’ESP32-S3 sont en logique 3,3 V : appliquer 5 V sur une entrée peut endommager la puce (voir l’avertissement plus haut). Pour dialoguer avec un composant 5 V, il faut adapter le niveau :
HIGH valide ; sinon, un level shifter s’impose.1. Une entrée non connectée lit des valeurs aléatoires. Comment y remédier ?
2. Avec INPUT_PULLUP, quel niveau lit-on quand le bouton est pressé ?
3. Pourquoi un digitalRead() compte-t-il parfois plusieurs appuis pour un seul ?
INPUT) ou sortie (OUTPUT).0 V (LOW) ou 3,3 V (HIGH) ; ~40 mA au maximum absolu, 12 mA au plus en pratique.INPUT_PULLUP / INPUT_PULLDOWN activent la résistance interne de l’ESP32-S3.LOW quand pressé.millis()) pour ne pas compter plusieurs appuis.attachInterrupt) réagit à un changement de broche sans scruter ; l’ISR reste minimale.