Jour 2 — Découverte de l'ESP32-S3 Super Mini et bases de l'électronique#

← Jour 1 · Programme · Jour 3 →

Intervenants : Antonin Fourneau & Guillaume Stagnaro — ils font quoi ?

La carte du workshop : ESP32-S3 Super Mini#

Dans tout le workshop, on utilise uniquement l'ESP32-S3 Super Mini.

ESP32-S3 Super Mini (~3–5 €) Arduino UNO (~25 €)
ESP32-S3 Super Mini Arduino UNO
Caractéristique Arduino Uno ESP32-S3 Super Mini
Processeur 8 bits – 16 MHz 32 bits – 240 MHz (2 cœurs)
Mémoire RAM 2 KB 512 KB + 2 MB de PSRAM
Mémoire Flash 32 KB 4 MB
Connectivité Aucune intégrée (besoin d'un shield) Wi-Fi + Bluetooth LE
USB via une puce de conversion USB natif (peut devenir clavier, souris, MIDI…)
E/S (GPIO) 14 numériques, 6 analogiques ADC 12 bits, touch, PWM sur la plupart des GPIO
Prix ~20–25 € ~3–5 €
Usage typique Projets simples : capteurs, LED, moteurs Objets connectés, contrôleurs USB, audio, affichage

Brochage (GPIO)#

Vérifiez que vous regardez bien le brochage de l'ESP32-S3 Super Mini : les autres cartes ESP32 (DevKit, C3 Super Mini…) n'ont pas les mêmes pins.

Brochage de l'ESP32-S3 Super Mini

Brochage officiel du fabricant : page ESP32S3 SuperMini (nologo.tech).

Ce qu'il faut retenir sur la Super Mini (vérifiez la sérigraphie de votre carte, qui peut varier selon les fabricants) :

Élément GPIO
LED RGB intégrée (WS2812) 48
Bouton BOOT 0
Série matérielle (UART) TX = 43, RX = 44
USB natif (ne pas utiliser) 19, 20
Pins à éviter (démarrage de la carte) 0, 3, 45, 46
I²C par défaut (VL53L0X…) SDA = 8, SCL = 9
Pins conseillées pour les capteurs 1, 2, 4, 5, 6, 7

Outils#

Plot Circuits — l'éditeur de montages#

Plot Circuits

Un éditeur de montages électroniques simple : on glisse des composants (carte ESP32-S3 Super Mini, breadboard, boutons, LED, capteurs…), on tire des fils d'une broche à l'autre, et on enregistre. Pas de simulation : il sert à dessiner et partager le câblage. Les montages de cette page s'ouvrent tous dedans (lien sous chaque schéma).

Exemple de montage

La Super Mini a sa LED RGB intégrée sur le GPIO 48.

VS Code + PlatformIO : l'atelier pour coder l'ESP32#

On écrit, compile et envoie le code avec VS Code et son extension PlatformIO. Le code reste du C++ « façon Arduino » (setup(), loop(), Serial, digitalRead…), mais on n'utilise pas le logiciel Arduino IDE.

1. Installer#

  1. Installez VS Code : https://code.visualstudio.com/
  2. Dans VS Code, onglet Extensions (les 4 carrés dans la barre de gauche), cherchez PlatformIO IDE et cliquez sur Install. Patientez : la première installation télécharge beaucoup de choses.

Installer PlatformIO

2. Créer un projet#

Cliquez sur l'icône PlatformIO (la tête de fourmi) puis Create New Project :

Create New Project

Project Wizard

3. Où écrire le code#

Le code va dans src/main.cpp. Pour chaque exemple de ce cours, remplacez tout le contenu de main.cpp par le code de l'exemple.

Structure du projet

Deux règles du C++ à connaître :

4. Le fichier platformio.ini#

C'est la fiche d'identité du projet : la carte, ses réglages, les librairies. Remplacez tout son contenu par celui-ci, prévu pour la Super Mini et pour tout le workshop :

[env:esp32-s3-supermini]
platform = espressif32
board = esp32-s3-devkitc-1
framework = arduino

; Mémoire de la Super Mini (4 MB de flash, 2 MB de PSRAM)
board_upload.flash_size = 4MB
board_build.partitions = default.csv
board_build.arduino.memory_type = qio_qspi

build_flags =
  -DBOARD_HAS_PSRAM
  -DARDUINO_USB_MODE=1
  -DARDUINO_USB_CDC_ON_BOOT=1  ; port série via l'USB natif (indispensable)

; Vitesse du moniteur série (la même que Serial.begin)
monitor_speed = 115200

; Librairies utilisées dans le workshop (téléchargées automatiquement)
lib_deps =
  adafruit/Adafruit NeoPixel
  pololu/VL53L0X
  cnmat/OSC
  lemmingdev/ESP32-BLE-Gamepad
  h2zero/NimBLE-Arduino

ARDUINO_USB_CDC_ON_BOOT=1 fait passer le port série par le câble USB de la Super Mini. Sans cette ligne, le moniteur série reste vide.

5. Compiler, envoyer, écouter : la barre d'état#

En bas de la fenêtre :

Icône Action
✓ Build : compiler (vérifier que le code est correct)
→ Upload : envoyer le programme dans l'ESP32
🔌 Serial Monitor : ouvrir le moniteur série (les messages de Serial.println)
🗑 Clean : tout recompiler depuis zéro (utile quand ça bug bizarrement)

Barre d'état

PlatformIO trouve la carte tout seul. Si plusieurs cartes sont branchées, précisez le port dans platformio.ini : upload_port = /dev/cu.usbmodem… sur Mac, upload_port = COM4 sur Windows.

6. Les librairies#

Une librairie est du code déjà écrit par d'autres (pour piloter des LEDs, un capteur…). Celles du workshop sont déjà listées dans lib_deps : PlatformIO les télécharge à la première compilation.

Pour en trouver d'autres : icône PlatformIO → Libraries, cherchez, puis Add to Project (ou ajoutez la ligne à la main dans lib_deps).

Chercher des librairies

Ajouter au projet

Si la carte n'est plus reconnue pour téléverser : maintenez BOOT, appuyez sur RST, relâchez BOOT, puis relancez Upload.


1er code#

Remplacez le contenu de src/main.cpp par :

#include <Arduino.h>

void setup() {                 // ≈ _ready() dans Godot
  Serial.begin(115200);        // Initialise le port série à 115200 bauds
  while (!Serial) {            // Attendre que le port série soit prêt (pas obligatoire)
    delay(10);
  }
  Serial.println("ESP32 prêt !");
}

void loop() {                  // ≈ _process() dans Godot
  // "Hello World" dans le moniteur série
  Serial.println("Hello World");
  // Attendre 1 seconde avant le prochain message
  delay(1000);
}

Cliquez sur → Upload, puis sur 🔌 Serial Monitor : « Hello World » s'affiche toutes les secondes.

Pour fermer le moniteur série : cliquez dans le terminal et faites Ctrl + C. Pensez à le fermer avant un nouvel Upload si le port est « occupé ».

1er code + if + variable#

#include <Arduino.h>

// Variable globale
int compteur = 0;  // Compteur qui s'incrémente

void setup() {
  Serial.begin(115200);
  while (!Serial) {
    delay(10);
  }
  Serial.println("ESP32 prêt !");
}

void loop() {
  // Incrémenter le compteur à chaque boucle
  compteur++;

  // Afficher le compteur actuel
  Serial.print("Compteur: ");
  Serial.println(compteur);

  // Vérifier si le compteur atteint 100
  if (compteur == 100) {
    Serial.println("=== HELLO WORLD ===");
    // Remettre le compteur à 0
    compteur = 0;
  }
}

Petite astuce pratique avec % (modulo, le reste de la division) :

  // Vérifier si le compteur est un multiple de 100
  if (compteur % 100 == 0) {
    Serial.println("=== HELLO WORLD ===");
  }

Détecter un bouton appuyé#

Pull-up interne ? La pin est « tirée » vers 3,3 V par une résistance intégrée : elle lit HIGH au repos et LOW quand le bouton la relie à GND.

#include <Arduino.h>

#define BUTTON_PIN 0  // sur quel GPIO (GPIO 0 = bouton BOOT de la carte)

void setup() {
  // Initialisation série
  Serial.begin(115200);

  // Définir le bouton en entrée avec pull-up interne
  pinMode(BUTTON_PIN, INPUT_PULLUP);

  Serial.println("Hello World !");
}

void loop() {
  // Lire l'état du bouton (LOW = appuyé, HIGH = relâché)
  int buttonState = digitalRead(BUTTON_PIN);

  if (buttonState == LOW) {
    Serial.println("Bouton appuyé !");
    delay(300);  // petit anti-rebond
  }
}

Montage sur breadboard#

Les trous d'une même ligne sont électriquement connectés.

Breadboard

Ouvrir ce montage dans Plot Circuits ↗

Même code avec un bouton externe sur le GPIO 9 :

Bouton sur GPIO 9

Ouvrir ce montage dans Plot Circuits ↗

#define BUTTON_PIN 9  // sur quel GPIO

Touch (capteur capacitif)#

#include <Arduino.h>

void setup() {
  Serial.begin(115200);
  delay(2000);
}

void loop() {
  int touch = touchRead(4);
  Serial.print("Touch GPIO 4: ");
  Serial.println(touch);
  delay(200);
}

Touch sur GPIO 4

Ouvrir ce montage dans Plot Circuits ↗

Touch + LED#

On utilise la librairie Adafruit NeoPixel (déjà dans lib_deps du platformio.ini).

#include <Arduino.h>
#include <Adafruit_NeoPixel.h>
#define LED_PIN 48

Adafruit_NeoPixel pixels(1, LED_PIN, NEO_GRB + NEO_KHZ800);

void setup() {
  Serial.begin(115200);
  delay(2000);
  pixels.begin();
  pixels.clear();
  pixels.show();
}

void loop() {
  int touch = touchRead(4);
  Serial.print("Touch GPIO 4: ");
  Serial.println(touch);

  if (touch >= 40000) {
    pixels.setPixelColor(0, pixels.Color(0, 0, 255));  // bleu
  } else {
    pixels.setPixelColor(0, pixels.Color(255, 0, 0));  // rouge
  }
  pixels.show();
  delay(200);
}

Même montage que pour le Touch ci-dessus : la LED utilisée est celle intégrée à la carte (GPIO 48).

Avec calibration#

#include <Arduino.h>
#include <Adafruit_NeoPixel.h>
#define LED_PIN 48

Adafruit_NeoPixel pixels(1, LED_PIN, NEO_GRB + NEO_KHZ800);
int seuil = 0;

void setup() {
  // Moyenne de 30 mesures au repos (ne pas toucher pendant 3 s)
  long sum = 0;
  for (int i = 0; i < 30; i++) {
    sum += touchRead(4);
    delay(100);
  }
  seuil = sum / 30;

  pixels.begin();
  pixels.clear();
  pixels.show();
}

void loop() {
  int touch = touchRead(4);
  int diff = touch - seuil;  // sur l'ESP32-S3, la valeur MONTE quand on touche

  if (diff > 100) {  // seuil fixe
    pixels.setPixelColor(0, pixels.Color(0, 0, 255));  // BLEU
  } else {
    pixels.setPixelColor(0, pixels.Color(255, 0, 0));  // ROUGE
  }
  pixels.show();
}

2 boutons + LED#

2 boutons + LED

Ouvrir ce montage dans Plot Circuits ↗

#include <Arduino.h>
#include <Adafruit_NeoPixel.h>

#define LED_PIN    48  // LED RGB de la Super Mini
#define NUM_PIXELS 1
#define BTN1_PIN   9   // boutons en pull-up (LOW = appuyé)
#define BTN2_PIN   10

Adafruit_NeoPixel pixels(NUM_PIXELS, LED_PIN, NEO_GRB + NEO_KHZ800);

void setup() {
  Serial.begin(115200);
  pinMode(BTN1_PIN, INPUT_PULLUP);
  pinMode(BTN2_PIN, INPUT_PULLUP);

  pixels.begin();
  pixels.clear();
  pixels.show();

  Serial.println("ESP32-S3 NeoPixel + 2 boutons prêt !");
}

void loop() {
  bool btn1 = digitalRead(BTN1_PIN);  // LOW = appuyé
  bool btn2 = digitalRead(BTN2_PIN);

  if (btn1 == LOW) {
    pixels.setPixelColor(0, pixels.Color(0, 0, 255));  // bleu
  } else if (btn2 == LOW) {
    pixels.setPixelColor(0, pixels.Color(0, 255, 0));  // vert
  } else {
    pixels.setPixelColor(0, pixels.Color(255, 0, 0));  // rouge
  }

  delay(10);  // petit anti-rebond / rafraîchissement
  pixels.show();
}

Capteur analogique : LDR (photorésistance)#

#include <Arduino.h>

void setup() {
  Serial.begin(115200);
  delay(2000);
}

void loop() {
  int ldrValue = analogRead(4);  // 0 à 4095 (ADC 12 bits)
  Serial.print("LDR GPIO 4: ");
  Serial.println(ldrValue);
  delay(200);
}

Montage LDR

Ouvrir ce montage dans Plot Circuits ↗

Capteur analogique : potentiomètre#

Un potentiomètre est un bouton qu'on tourne (ou un curseur qu'on fait glisser, un fader). Il a trois pattes :

En tournant, la tension sur la patte du milieu va de 0 à 3,3 V, et analogRead renvoie une valeur de 0 à 4095.

#include <Arduino.h>

#define POT_PIN 4  // entrée analogique (GPIO 1 à 10)

void setup() {
  Serial.begin(115200);
  delay(2000);
}

void loop() {
  int valeur = analogRead(POT_PIN);             // 0 à 4095
  int pourcent = map(valeur, 0, 4095, 0, 100);  // converti en 0 à 100 %
  Serial.printf("Potentiomètre : %d (%d %%)\n", valeur, pourcent);
  delay(100);
}

Branchez le bord du potentiomètre sur 3V3, jamais sur 5V : le GPIO n'accepte pas plus de 3,3 V.

Montage LDR

Ouvrir ce montage dans Plot Circuits ↗

Capteur de lumière + anneau de LEDs NeoPixel#

#include <Arduino.h>
#include <Adafruit_NeoPixel.h>

#define LDR_PIN  4   // entrée analogique de la LDR
#define LED_PIN  5   // pin de l'anneau NeoPixel
#define NUM_LEDS 16  // nombre de LEDs

Adafruit_NeoPixel strip(NUM_LEDS, LED_PIN, NEO_GRB + NEO_KHZ800);

void setup() {
  strip.begin();
  strip.show();  // éteint toutes les LEDs au démarrage
  pinMode(LDR_PIN, INPUT);
}

void loop() {
  // Lire la valeur de la LDR (0 à 4095 sur ESP32-S3)
  int ldrValue = analogRead(LDR_PIN);

  // Convertir la valeur en nombre de LEDs allumées (0 à NUM_LEDS)
  int ledsOn = map(ldrValue, 0, 4095, 0, NUM_LEDS);

  for (int i = 0; i < NUM_LEDS; i++) {
    if (i < ledsOn) {
      strip.setPixelColor(i, strip.Color(255, 0, 0));  // rouge
    } else {
      strip.setPixelColor(i, strip.Color(0, 0, 55));   // bleu foncé
    }
  }
  strip.show();
  delay(50);
}

LDR + anneau NeoPixel

Ouvrir ce montage dans Plot Circuits ↗

À noter : la LDR est sur le GPIO 4 (entrée analogique qui reste utilisable avec le Wi-Fi) et l'anneau sur le GPIO 5 (pin facilement accessible sur la Super Mini).

Capteurs à effet Hall#

Un capteur à effet Hall détecte un aimant. Dans le kit, il y en a deux sortes :

A3144 (numérique) SS49E (analogique / linéaire)
Ce qu'il dit aimant là / pas là (0 ou 1) la force du champ et le pôle (nord / sud)
Lecture digitalRead analogRead
Alimentation 5 V (pin 5V de la Super Mini) 3,3 V (pin 3V3)
Usage typique interrupteur sans contact, compte-tours curseur, joystick magnétique, proximité d'un aimant

Brochage des deux (face plate marquée vers vous, pattes en bas) : 1 = VCC, 2 = GND, 3 = OUT.

Les aimants du kit (néodyme 6 × 3 mm) sont très puissants : quelques millimètres à 2 cm suffisent.

A3144 : détecteur d'aimant (numérique)#

#include <Arduino.h>

#define HALL_PIN 4

void setup() {
  Serial.begin(115200);
  delay(2000);
  // Avec résistance pull-up interne (vers 3,3 V)
  pinMode(HALL_PIN, INPUT_PULLUP);
}

void loop() {
  int hallState = digitalRead(HALL_PIN);
  Serial.print("Hall GPIO 4: ");
  Serial.print(hallState);

  if (hallState == LOW) {
    Serial.println(" DETECTE");
  } else {
    Serial.println(" RIEN");
  }
  delay(200);
}
Module Capteur nu Câblage A3144
Module Hall Capteur Hall Câblage A3144

Câblage sur l'ESP32 : l'A3144 a besoin d'au moins 4,5 V : alimentez-le sur la pin 5V. Sa sortie ne fait que tirer vers GND (sortie « collecteur ouvert »), donc on peut la brancher directement sur le GPIO avec INPUT_PULLUP, qui la ramène à 3,3 V. Ne mettez pas la résistance de tirage du schéma vers 5 V : elle enverrait du 5 V dans le GPIO.

SS49E : capteur de champ magnétique (analogique)#

Au repos, la sortie est au milieu (environ la moitié de 3,3 V). Elle monte avec un pôle de l'aimant et descend avec l'autre.

#include <Arduino.h>

#define HALL_PIN 4  // entrée analogique (GPIO 1 à 10)

int repos = 0;      // valeur sans aimant

void setup() {
  Serial.begin(115200);
  delay(2000);

  // Calibration : moyenne de 50 mesures sans aimant
  long sum = 0;
  for (int i = 0; i < 50; i++) {
    sum += analogRead(HALL_PIN);
    delay(10);
  }
  repos = sum / 50;
  Serial.printf("Valeur au repos: %d\n", repos);
}

void loop() {
  int valeur = analogRead(HALL_PIN);
  int champ = valeur - repos;  // > 0 : un pôle, < 0 : l'autre

  Serial.print("Champ: ");
  Serial.println(champ);

  if (champ > 150) {
    Serial.println("Pole SUD proche");
  } else if (champ < -150) {
    Serial.println("Pole NORD proche");
  }
  delay(50);
}

Alimentez le SS49E en 3,3 V, pas en 5 V : sa sortie peut monter presque jusqu'à sa tension d'alimentation, et le GPIO n'accepte pas plus de 3,3 V. Selon le sens de l'aimant, « nord » et « sud » peuvent être inversés : l'important est le signe.

Capteur de distance laser VL53L0X (I²C)#

Mesure une distance de quelques centimètres à environ 1,2 m, avec un petit laser infrarouge invisible (mesure du « temps de vol »). Il communique en I²C : 2 fils de données seulement.

VL53L0X ESP32-S3 Super Mini
VIN 3V3
GND GND
SDA GPIO 8
SCL GPIO 9

On utilise la librairie VL53L0X de Pololu (déjà dans lib_deps du platformio.ini).

#include <Arduino.h>
#include <Wire.h>
#include <VL53L0X.h>

VL53L0X sensor;

void setup() {
  Serial.begin(115200);
  delay(2000);

  Wire.begin(8, 9);  // SDA = GPIO 8, SCL = GPIO 9

  sensor.setTimeout(500);
  if (!sensor.init()) {
    Serial.println("VL53L0X introuvable : vérifiez le câblage !");
    while (true) {
      delay(100);
    }
  }
  sensor.startContinuous();  // mesures en continu
}

void loop() {
  int distance = sensor.readRangeContinuousMillimeters();

  if (sensor.timeoutOccurred()) {
    Serial.println("timeout");
  } else if (distance > 2000) {
    Serial.println("hors de portée");
  } else {
    Serial.print("Distance: ");
    Serial.print(distance);
    Serial.println(" mm");
  }
  delay(50);
}

Retirez le petit film de protection sur le capteur avant de l'utiliser. Hors de portée, la librairie renvoie une valeur autour de 8190 mm.

Capteur de distance HC-SR04#

#include <Arduino.h>

#define TRIG_PIN 4
#define ECHO_PIN 5

void setup() {
  Serial.begin(115200);
  delay(2000);
  pinMode(TRIG_PIN, OUTPUT);
  pinMode(ECHO_PIN, INPUT);
}

void loop() {
  // Déclencher une mesure
  digitalWrite(TRIG_PIN, LOW);
  delayMicroseconds(2);
  digitalWrite(TRIG_PIN, HIGH);
  delayMicroseconds(10);
  digitalWrite(TRIG_PIN, LOW);

  // Mesurer la durée de l'écho (timeout 30 ms ≈ 5 m)
  long duration = pulseIn(ECHO_PIN, HIGH, 30000);

  // Calculer la distance (vitesse du son = 343 m/s = 0,034 cm/µs)
  float distance = (duration * 0.034) / 2;

  Serial.print("Distance: ");
  Serial.print(distance);
  Serial.println(" cm");
  delay(500);
}

HC-SR04

Ouvrir ce montage dans Plot Circuits ↗

Attention (matériel) : le HC-SR04 classique est alimenté en 5 V et sa broche Echo renvoie du 5 V, alors que l'ESP32 accepte au maximum 3,3 V sur ses GPIO. Mettez un pont diviseur sur Echo (par ex. 1 kΩ + 2 kΩ), ou utilisez une version 3,3 V (HC-SR04P, RCWL-1601).

Capteur piézo (knock sensor) avec calibration#

Le piézo est branché entre GND et le GPIO 1, avec une résistance de 1 MΩ en parallèle.

Montage piézo

Ouvrir ce montage dans Plot Circuits ↗

#include <Arduino.h>

// Calibration capteur piézo ESP32-S3 Super Mini
// Piézo connecté entre GND et GPIO 1
const int PIEZO_PIN = 1;
const int CALIBRATION_SAMPLES = 200;
const int CALIBRATION_DELAY = 25;

// Variables de calibration
int baseline_max = 0;         // valeur maximum au repos
int detection_threshold = 0;  // seuil de détection des coups
int THRESHOLD_MARGIN = 50;    // marge au-dessus du max observé

void calibratePiezoMaxThreshold() {
  int maxValue = 0;
  int minValue = 4095;  // max de l'ADC ESP32
  float sum = 0;

  // Collecte des échantillons pour trouver le MAX
  for (int i = 0; i < CALIBRATION_SAMPLES; i++) {
    int currentValue = analogRead(PIEZO_PIN);

    // Suivi des extrêmes
    if (currentValue > maxValue) {
      maxValue = currentValue;
    }
    if (currentValue < minValue) {
      minValue = currentValue;
    }
    sum += currentValue;
    delay(CALIBRATION_DELAY);
  }

  baseline_max = maxValue;
  float average = sum / CALIBRATION_SAMPLES;
  // Seuil = valeur max observée + marge de sécurité
  detection_threshold = baseline_max + THRESHOLD_MARGIN;
}

void setup() {
  Serial.begin(115200);
  delay(1000);

  Serial.println("CALIBRATION CAPTEUR PIEZO");
  calibratePiezoMaxThreshold();
  Serial.println("\n=== CALIBRATION TERMINEE ===");
  Serial.printf("Valeur max observée au repos: %d\n", baseline_max);
  Serial.printf("Seuil de détection: %d\n", detection_threshold);
}

void loop() {
  int rawValue = analogRead(PIEZO_PIN);

  // Détection uniquement si on dépasse le seuil
  if (rawValue > detection_threshold) {
    int intensity = rawValue - detection_threshold;
    Serial.printf("COUP DETECTE! Valeur: %d, Intensité: %d\n", rawValue, intensity);
    // Anti-rebond
    delay(150);
  }
  delay(5);
}

Même code avec un serveur web pour le réglage depuis un téléphone : voir L'ESP32 en réseau.


Capteurs en tout genre#

Kit de capteurs

Température / humidité#

Lumière / couleur / UV#

LDR

Distance / proximité#

Mouvement / présence#

Orientation / inertie#

Magnétisme / rotation#

Son / vibration#

Gaz / qualité de l'air#

Biométrie#

Position / force / interaction#

Liquides / environnement#

RF / optique courte portée#

Modules de capteurs

FSR


Et l'ESP32 comme clavier ou manette ?#

Grâce à son USB natif, la Super Mini peut se faire passer pour un clavier ou une manette USB, et piloter un jeu Godot sans rien installer. C'est le début du Jour 3.


Schémas de montage : Plot Circuits, avec les composants de la bibliothèque Fritzing (CC BY-SA 3.0, adaptés). Ces schémas sont partagés sous la même licence.