Guides · 18 min de lecture

Serveur web ESP32 asynchrone et WebSocket : pilotage en direct

Serveur web asynchrone ESP32 et WebSocket : pilote un relais, vois tes capteurs en direct sans recharger. Installation, PROGMEM, LittleFS, esp32.local, erreurs.

Un serveur web asynchrone ESP32 (ESP32 async web server), basé sur la bibliothèque ESPAsyncWebServer, transforme ton ESP32 en un serveur web rapide capable de répondre à plusieurs téléphones et ordinateurs en même temps. Grâce à la prise en charge de WebSocket intégrée à la bibliothèque, le navigateur et la carte gardent une ligne ouverte en permanence : tu appuies sur le bouton de la page et le relais colle aussitôt, et quand la température change, le chiffre affiché se met à jour tout seul, sans recharger la page. Tous les autres appareils qui ont ouvert le même tableau de bord voient ton changement dans la même seconde. En bref, ce duo est la façon la plus pratique de créer ton propre panneau domotique au sein de ton réseau local, sans aucun service cloud.

Dans ce guide, tu verras d'abord ce qui distingue un serveur asynchrone de la bibliothèque WebServer classique et pourquoi WebSocket est plus rapide que de simples requêtes HTTP. Ensuite, tu installeras les bonnes bibliothèques, tu lanceras le serveur le plus simple possible, puis tu construiras un tableau de bord en temps réel avec un relais et un capteur DHT22. Tu apprendras ensuite à stocker le HTML dans PROGMEM ou dans LittleFS, à utiliser esp32.local au lieu de l'adresse IP, à gérer plusieurs clients, à protéger le panneau par un mot de passe simple et à corriger les erreurs de compilation et de connexion les plus courantes. Le code est écrit pour fonctionner avec les versions 2.x et 3.x du cœur ESP32 dans l'Arduino IDE.

Carte de développement ESP32 pour un projet de serveur web
ESP32 DevKit : grâce à sa puce Wi-Fi, elle fait tourner un serveur web à elle seule ; le tableau de bord que tu ouvres sur ton téléphone vient directement de la mémoire flash de cette carte.

Qu'est-ce que le serveur web asynchrone de l'ESP32 et comment fonctionne-t-il ?

La bibliothèque WebServer.h fournie avec le cœur ESP32 est synchrone : les requêtes entrantes ne sont traitées, une par une et dans l'ordre, que lorsque server.handleClient() est appelé dans loop(). Si loop() contient un delay() de 2 secondes, la page attend 2 secondes elle aussi ; si deux personnes se connectent en même temps, l'une attend que l'autre ait fini. ESPAsyncWebServer, lui, tourne dans une tâche FreeRTOS séparée (async_tcp) créée par la bibliothèque AsyncTCP. Quand une requête arrive, ses en-têtes sont lus en arrière-plan, la fonction que tu as écrite (le callback) est appelée dès que la requête est prête, et la réponse part elle aussi en arrière-plan. Pendant ce temps, loop() continue son travail. Plusieurs connexions peuvent ainsi rester ouvertes à la fois, et les connexions permanentes comme WebSocket et les Server-Sent Events (SSE) deviennent possibles. Si les deux cœurs de l'ESP32 et les tâches t'intéressent, jette un œil à l'article sur FreeRTOS et l'ESP32.

CaractéristiqueWebServer.h (synchrone)ESPAsyncWebServer (asynchrone)
InstallationFournie avec le cœur ESP322 bibliothèques via le gestionnaire de bibliothèques
Traitement des requêtesUne par une avec handleClient() dans loop()Dans la tâche async_tcp en arrière-plan, plusieurs à la fois
delay() dans loop()Retarde la réponse de la pageN'affecte pas le serveur
WebSocketNon ; bibliothèque à part nécessaire (par ex. WebSockets de Markus Sattler)Intégré (AsyncWebSocket)
Server-Sent EventsNonIntégré (AsyncEventSource)
Fichiers statiquesAvec serveStaticserveStatic, prise en charge automatique du .gz et gestion du cache
Point de vigilanceLa plus simple à apprendrePas de delay() ni de traitement long dans les callbacks

Qu'est-ce que WebSocket et en quoi diffère-t-il d'une requête HTTP ?

Lors d'une requête HTTP normale, le navigateur ouvre une connexion, dit "donne-moi cette page", reçoit la réponse et la conversation s'arrête. Le serveur ne peut rien dire au navigateur de lui-même ; pour voir une nouvelle valeur, le navigateur doit redemander. WebSocket (RFC 6455) est une poignée de main qui commence en HTTP : le navigateur ajoute l'en-tête Upgrade: websocket à sa requête, l'ESP32 accepte avec la réponse 101 Switching Protocols et la même connexion TCP reste ouverte. Dès lors, les deux côtés peuvent envoyer un message quand ils le veulent (full-duplex). Chaque message n'ajoute que 2 à 14 octets, alors que chaque requête HTTP transporte des centaines d'octets d'en-têtes. C'est pourquoi les messages WebSocket arrivent en quelques millisecondes et que c'est la méthode la plus efficace pour les tableaux de bord qui envoient plusieurs mises à jour par seconde. Voici comment se comparent les quatre façons de tenir une page à jour avec les valeurs d'un capteur :

MéthodeFonctionnementLatence et chargeQuand l'utiliser ?
Rechargement de page (meta refresh)Le navigateur retélécharge toute la page toutes les quelques secondesÉlevées ; la page clignote à chaque foisÀ éviter
Interrogation avec fetch (polling)JavaScript demande un petit JSON toutes les 1 à 2 secondesMoyennes ; chaque requête porte de nouveaux en-têtesUne seule valeur qui change rarement
Server-Sent Events (SSE)Le serveur pousse des événements dans un seul sens sur une connexion ouverteFaibles ; du serveur vers le navigateur seulementTableaux de bord de simple affichage
WebSocketMessages dans les deux sens sur une connexion ouverteLes plus faibles ; trames de quelques octetsBoutons, curseurs et données en direct réunis

Bibliothèques nécessaires : installer ESPAsyncWebServer et AsyncTCP

  1. Vérifie que le paquet de cartes ESP32 (Espressif Systems) est installé dans l'Arduino IDE ; les étapes se trouvent dans Qu'est-ce que l'ESP32 ?
  2. Ouvre le gestionnaire de bibliothèques (Ctrl+Maj+I) et cherche ESPAsyncWebServer. Installe la bibliothèque ESP Async WebServer dont l'auteur est ESP32Async.
  3. Cette bibliothèque ne déclare pas sa dépendance au gestionnaire de bibliothèques, l'IDE ne te proposera donc rien d'autre : installe séparément la bibliothèque Async TCP, publiée elle aussi par ESP32Async. Sur un ESP8266, il te faut à la place la bibliothèque ESP Async TCP d'ESP32Async (ESPAsyncTCP.h).
  4. Pour l'exemple du tableau de bord, installe la DHT sensor library d'Adafruit et la dépendance Adafruit Unified Sensor qu'elle demande. Si c'est la première fois que tu installes une bibliothèque, consulte le guide pour ajouter une bibliothèque Arduino.
  5. Si le dossier Documents/Arduino/libraries contient des dossiers ESPAsyncWebServer-master, AsyncTCP-master ou ESPAsyncWebSrv ajoutés autrefois depuis un ZIP, supprime-les. Deux copies de la même bibliothèque poussent le compilateur à choisir la mauvaise.
La plupart des anciens tutoriels en ligne renvoient vers la bibliothèque ESPAsyncWebServer d'origine, sur le compte me-no-dev. Cette bibliothèque n'est plus maintenue et ne compile pas avec la version 3.x du cœur ESP32 ; la compilation s'arrête sur l'erreur 'mbedtls_md5_starts_ret' was not declared in this scope. La solution consiste à supprimer l'ancienne copie et à installer les versions d'ESP32Async. Dans la nouvelle version, send_P() est en outre dépréciée (deprecated) : sur l'ESP32, tu peux envoyer un tableau PROGMEM directement avec request->send(200, "text/html", index_html).

Première étape : le serveur le plus simple avec AsyncWebServer

Avant de passer au tableau de bord, voyons la logique de la bibliothèque avec un petit exemple. Le code ci-dessous connecte l'ESP32 à ton réseau domestique, affiche un message d'accueil sur la page principale et allume ou éteint la LED intégrée via les adresses /led?durum=1 et /led?durum=0. Téléverse-le, ouvre le moniteur série à 115200 bauds et tape l'adresse affichée dans le navigateur d'un téléphone connecté au même Wi-Fi. Si la carte ne rejoint pas le réseau, utilise le code de diagnostic du guide l'ESP32 ne se connecte pas au Wi-Fi.

C++ (Arduino IDE)
1#include <WiFi.h>
2#include <AsyncTCP.h>
3#include <ESPAsyncWebServer.h>
4
5const char* ssid = "AgAdi";          // Wi-Fi adın / your Wi-Fi name
6const char* password = "Sifre123";   // Wi-Fi şifren / your Wi-Fi password
7const int LED_PIN = 2;               // çoğu DevKit'te dahili LED / onboard LED on most DevKits
8
9AsyncWebServer server(80);           // 80 = standart HTTP portu / standard HTTP port
10bool ledAcik = false;
11
12void setup() {
13  Serial.begin(115200);
14  pinMode(LED_PIN, OUTPUT);
15
16  WiFi.mode(WIFI_STA);
17  WiFi.begin(ssid, password);
18  while (WiFi.status() != WL_CONNECTED) {
19    delay(500);
20    Serial.print(".");
21  }
22  Serial.println();
23  Serial.print("Adres / address: http://");
24  Serial.println(WiFi.localIP());
25
26  // Ana sayfa / home page
27  server.on("/", HTTP_GET, [](AsyncWebServerRequest *request) {
28    request->send(200, "text/plain", "Merhaba, ben ESP32 / Hello from ESP32");
29  });
30
31  // /led?durum=1 yakar, /led?durum=0 söndürür / turns the LED on or off
32  server.on("/led", HTTP_GET, [](AsyncWebServerRequest *request) {
33    if (request->hasParam("durum")) {
34      ledAcik = request->getParam("durum")->value() == "1";
35      digitalWrite(LED_PIN, ledAcik ? HIGH : LOW);
36    }
37    request->send(200, "text/plain", ledAcik ? "LED: ON" : "LED: OFF");
38  });
39
40  // Tanımsız adresler / unknown addresses
41  server.onNotFound([](AsyncWebServerRequest *request) {
42    request->send(404, "text/plain", "Sayfa yok / Not found");
43  });
44
45  server.begin();
46}
47
48void loop() {
49  // Sunucu arka planda çalışır, loop() boş kalabilir / the server runs in the background
50}
InstructionRôle
AsyncWebServer server(80)Crée le serveur qui écoute sur le port HTTP 80
server.on("/led", HTTP_GET, ...)Enregistre la fonction (callback) à exécuter pour une adresse et un type de requête
request->hasParam("durum")Vérifie si l'adresse contient un paramètre ?durum=...
request->getParam("durum")->value()Renvoie la valeur du paramètre sous forme de String
request->send(code, type, contenu)Envoie la réponse : 200 OK, 404 introuvable ; type text/plain, text/html ou application/json
server.onNotFound(...)S'exécute pour toute adresse non définie
server.begin()Démarre le serveur ; à appeler après toutes les lignes server.on()
Une loop() vide n'est pas une erreur ; le serveur tourne dans sa propre tâche. En revanche, n'utilise pas de delay(), de longues boucles while ni de requêtes qui récupèrent des données sur internet dans les callbacks. La tâche async_tcp se retrouve bloquée et, quelques secondes plus tard, l'ESP32 redémarre avec le message Task watchdog got triggered. Confie les traitements longs à loop() grâce à une variable drapeau (flag) et gère le temps avec millis().

Matériel et câblage du tableau de bord

Dans l'exemple du tableau de bord, tu allumeras et éteindras une lampe ou un ventilateur avec un module relais et tu mesureras la température et l'humidité avec un DHT22. Il te faut une ESP32 DevKit, un module relais 5 V à un canal, un module capteur DHT22 (ou DHT11), une breadboard et des câbles Dupont. Pour le relais, préfère les modules à optocoupleur. Le DHT22 mesure de −40 à 80 °C avec une précision de ±0,5 °C et ne peut être lu qu'une fois toutes les 2 secondes au maximum ; le DHT11 est moins cher, mais n'offre que ±2 °C entre 0 et 50 °C. Pour les détails du capteur, vois l'article sur le capteur de température et d'humidité DHT11, et pour le brochage de ta carte, l'outil Pinout.

Broche du moduleESP32 DevKitESP8266 NodeMCURemarque
VCC du relaisVIN (5 V)VIN ou VU (5 V)La bobine du relais demande 5 V ; alimentée par l'USB
GND du relaisGNDGNDMasse commune
IN du relaisGPIO 26D1 (GPIO 5)La plupart des modules collent sur un niveau LOW
VCC (+) du DHT223V33V3Alimenté en 3,3 V, la ligne de données est aussi en 3,3 V
DATA (OUT) du DHT22GPIO 4D2 (GPIO 4)Capteur nu : résistance de 10 kΩ entre DATA et VCC
GND (−) du DHT22GNDGNDMasse commune
Les broches de l'ESP32 sortent du 3,3 V. Certains modules relais 5 V ne reconnaissent pas ce niveau comme HIGH, et le relais ne décolle jamais ; avec un tel module, utilise un relais compatible 3,3 V, un module avec cavalier JD-VCC ou un transistor intermédiaire. Si tu commutes la tension secteur (230 V) avec le relais, câble hors tension, ne laisse aucun bornier à nu et, en cas de doute, teste d'abord avec une charge basse tension (ruban LED, ventilateur 12 V). Les détails sont dans Qu'est-ce qu'un module relais ?

Tableau de bord en temps réel : relais, DHT22 et WebSocket

Passons maintenant au projet principal. Le code ci-dessous tient en un seul fichier : le HTML, le CSS et le JavaScript y sont intégrés sous forme de tableau PROGMEM. À l'ouverture de la page, le navigateur établit une connexion WebSocket vers /ws. Toutes les 2 secondes, l'ESP32 lit le DHT22 et envoie à tous les navigateurs la température, l'humidité, l'état du relais et le nombre d'appareils connectés dans un seul message JSON. Quand tu appuies sur le bouton, le navigateur envoie le message toggle, l'ESP32 inverse le relais et annonce de nouveau l'état à tout le monde. Modifie le nom du réseau et le mot de passe, téléverse, puis ouvre http://esp32.local ou l'adresse IP du moniteur série dans ton navigateur.

C++ (Arduino IDE)
1#include <WiFi.h>
2#include <AsyncTCP.h>
3#include <ESPAsyncWebServer.h>
4#include <ESPmDNS.h>
5#include <DHT.h>
6
7const char* ssid = "AgAdi";          // Wi-Fi adın / your Wi-Fi name
8const char* password = "Sifre123";   // Wi-Fi şifren / your Wi-Fi password
9
10const int ROLE_PIN = 26;             // röle modülü IN / relay module IN
11const bool ROLE_AKTIF_LOW = true;    // çoğu modül LOW ile çeker / most modules switch on LOW
12const int DHT_PIN = 4;               // DHT22 veri pini / DHT22 data pin
13
14DHT dht(DHT_PIN, DHT22);             // DHT11 için DHT11 yaz / write DHT11 for a DHT11
15AsyncWebServer server(80);
16AsyncWebSocket ws("/ws");            // WebSocket adresi / WebSocket path
17
18bool roleAcik = false;
19float sicaklik = NAN;
20float nem = NAN;
21volatile bool gonder = false;        // loop() yayın yapsın mı? / should loop() broadcast?
22unsigned long sonOlcum = 0;
23unsigned long sonTemizlik = 0;
24
25// Tarayıcıya giden sayfa / the page sent to the browser
26const char index_html[] PROGMEM = R"rawliteral(
27<!DOCTYPE html>
28<html lang="tr">
29<head>
30<meta charset="utf-8">
31<meta name="viewport" content="width=device-width, initial-scale=1">
32<title>ESP32 Panel</title>
33<style>
34body{font-family:sans-serif;background:#0f172a;color:#e2e8f0;text-align:center;margin:0;padding:16px}
35.kart{background:#1e293b;border-radius:12px;padding:16px;margin:12px auto;max-width:320px}
36.deger{font-size:2.4em;font-weight:bold}
37button{font-size:1.2em;padding:14px 32px;border:0;border-radius:8px;color:#fff;width:100%}
38.acik{background:#16a34a}.kapali{background:#475569}
39</style>
40</head>
41<body>
42<h2>ESP32 Panel</h2>
43<p id="baglanti">Bağlanıyor / Connecting...</p>
44<div class="kart">Sıcaklık / Temperature<div class="deger"><span id="sicaklik">--</span> &deg;C</div></div>
45<div class="kart">Nem / Humidity<div class="deger"><span id="nem">--</span> %</div></div>
46<div class="kart">Röle / Relay<p><button id="dugme" class="kapali" onclick="degistir()">KAPALI / OFF</button></p></div>
47<p>Bağlı cihaz / Clients: <span id="istemci">0</span></p>
48<script>
49let ws;
50const $ = (id) => document.getElementById(id);
51function baglan() {
52  ws = new WebSocket('ws://' + location.host + '/ws');
53  ws.onopen = () => { $('baglanti').textContent = 'Bağlı / Connected'; };
54  ws.onclose = () => {
55    $('baglanti').textContent = 'Koptu, yeniden deneniyor / Reconnecting...';
56    setTimeout(baglan, 2000);
57  };
58  ws.onmessage = (e) => {
59    const d = JSON.parse(e.data);
60    $('sicaklik').textContent = d.sicaklik === null ? '--' : d.sicaklik.toFixed(1);
61    $('nem').textContent = d.nem === null ? '--' : d.nem.toFixed(0);
62    $('istemci').textContent = d.istemci;
63    $('dugme').textContent = d.role ? 'AÇIK / ON' : 'KAPALI / OFF';
64    $('dugme').className = d.role ? 'acik' : 'kapali';
65  };
66}
67function degistir() {
68  if (ws.readyState === WebSocket.OPEN) ws.send('toggle');
69}
70baglan();
71</script>
72</body>
73</html>
74)rawliteral";
75
76void roleAyarla(bool acik) {
77  roleAcik = acik;
78  digitalWrite(ROLE_PIN, ROLE_AKTIF_LOW ? !acik : acik);
79}
80
81// Durumu JSON metnine çevir / build the state as JSON text
82String durumJson() {
83  String s = "{\"role\":";
84  s += roleAcik ? "true" : "false";
85  s += ",\"sicaklik\":";
86  s += isnan(sicaklik) ? String("null") : String(sicaklik, 1);
87  s += ",\"nem\":";
88  s += isnan(nem) ? String("null") : String(nem, 0);
89  s += ",\"istemci\":";
90  s += (unsigned long)ws.count();
91  s += "}";
92  return s;
93}
94
95// WebSocket olayları (async_tcp görevinde çalışır) / WebSocket events (run in the async_tcp task)
96void wsOlay(AsyncWebSocket *sunucu, AsyncWebSocketClient *client, AwsEventType type,
97            void *arg, uint8_t *data, size_t len) {
98  switch (type) {
99    case WS_EVT_CONNECT:
100      Serial.printf("Istemci #%u baglandi / connected: %s\n",
101                    (unsigned)client->id(), client->remoteIP().toString().c_str());
102      gonder = true;                 // yeni gelen güncel durumu alsın / newcomer gets the state
103      break;
104    case WS_EVT_DISCONNECT:
105      Serial.printf("Istemci #%u ayrildi / disconnected\n", (unsigned)client->id());
106      gonder = true;
107      break;
108    case WS_EVT_DATA: {
109      AwsFrameInfo *info = (AwsFrameInfo *)arg;
110      // Tek parça, tam bir metin mesajı mı? / a complete single-frame text message?
111      if (info->final && info->index == 0 && info->len == len && info->opcode == WS_TEXT) {
112        if (len == 6 && memcmp(data, "toggle", 6) == 0) {
113          roleAyarla(!roleAcik);
114          gonder = true;             // tüm ekranlar eşitlensin / sync every screen
115        }
116      }
117      break;
118    }
119    default:                         // PONG, ERROR vb. / PONG, ERROR etc.
120      break;
121  }
122}
123
124void setup() {
125  Serial.begin(115200);
126  pinMode(ROLE_PIN, OUTPUT);
127  roleAyarla(false);                 // açılışta röle kapalı / relay off at start
128  dht.begin();
129
130  WiFi.mode(WIFI_STA);
131  WiFi.begin(ssid, password);
132  Serial.print("Baglaniyor / connecting");
133  while (WiFi.status() != WL_CONNECTED) {
134    delay(500);
135    Serial.print(".");
136  }
137  WiFi.setSleep(false);              // daha hızlı tepki / lower latency
138  Serial.println();
139  Serial.print("http://");
140  Serial.println(WiFi.localIP());
141
142  if (MDNS.begin("esp32")) {         // http://esp32.local
143    MDNS.addService("http", "tcp", 80);
144    Serial.println("http://esp32.local");
145  }
146
147  ws.onEvent(wsOlay);
148  server.addHandler(&ws);
149
150  server.on("/", HTTP_GET, [](AsyncWebServerRequest *request) {
151    request->send(200, "text/html", index_html);
152  });
153  server.onNotFound([](AsyncWebServerRequest *request) {
154    request->send(404, "text/plain", "Sayfa yok / Not found");
155  });
156  server.begin();
157}
158
159void loop() {
160  if (millis() - sonOlcum >= 2000) {       // DHT22 en fazla 2 sn'de bir / DHT22: once per 2 s
161    sonOlcum = millis();
162    float t = dht.readTemperature();
163    float h = dht.readHumidity();
164    if (!isnan(t) && !isnan(h)) {          // hatalı okumayı atla / skip failed reads
165      sicaklik = t;
166      nem = h;
167    }
168    gonder = true;
169  }
170
171  if (gonder) {
172    gonder = false;
173    ws.textAll(durumJson());               // tüm tarayıcılara yolla / send to every browser
174  }
175
176  if (millis() - sonTemizlik >= 1000) {
177    sonTemizlik = millis();
178    ws.cleanupClients();                   // kopuk istemcileri temizle / drop stale clients
179  }
180}

Comment fonctionne le code ?

  • AsyncWebSocket ws("/ws") crée le point d'accès WebSocket et server.addHandler(&ws) le rattache au serveur. L'adresse utilisée dans le JavaScript doit correspondre exactement à ce chemin.
  • La fonction wsOlay() est appelée à chaque connexion, déconnexion et message entrant. WS_EVT_CONNECT signale un nouveau client, WS_EVT_DISCONNECT un client qui part et WS_EVT_DATA un message reçu.
  • AwsFrameInfo contient les informations de la trame reçue. Le contrôle de final, index == 0 et len confirme que le message est arrivé complet en une seule trame, et WS_TEXT qu'il s'agit de texte. Les commandes courtes tiennent toujours dans une seule trame.
  • Les données reçues sont comparées avec leur longueur grâce à memcmp() ; tu ne dépends donc pas de la présence d'un caractère nul à la fin des données.
  • Comme les callbacks tournent dans la tâche async_tcp, ils se contentent d'inverser le relais et de lever le drapeau gonder. L'envoi proprement dit se fait dans loop() avec ws.textAll() ; ainsi, tous les messages partent d'un seul endroit, dans l'ordre.
  • ws.cleanupClients() est appelé une fois par seconde : il libère la mémoire des connexions coupées et ferme la plus ancienne connexion quand le nombre de clients dépasse la limite.
  • Côté JavaScript, l'événement onclose relance la connexion au bout de 2 secondes. Même si l'ESP32 redémarre ou si le Wi-Fi coupe un instant, la page se rétablit toute seule. Comme l'adresse est construite à partir de location.host, la page fonctionne aussi bien avec l'IP qu'avec esp32.local.
  • WiFi.setSleep(false) désactive l'économie d'énergie du Wi-Fi. La mise en veille du modem par défaut peut ajouter jusqu'à plusieurs centaines de millisecondes de retard ; la couper sur un tableau de bord alimenté sur secteur rend les boutons instantanés.

Choisir JSON comme format de message te simplifie la vie : le navigateur transforme le texte reçu en objet en une seule ligne avec JSON.parse(). Un message type ressemble à ceci : {"role":true,"sicaklik":23.4,"nem":48,"istemci":2}. Quand le capteur ne peut pas être lu, la valeur part sous la forme null et la page affiche "--" ; si une erreur de lecture partait sous forme de NaN, le JSON invalide empêcherait toute mise à jour de la page. Comme chaque message pèse environ 60 octets, même plusieurs mises à jour par seconde ne chargent pas le réseau. Pour des messages plus complexes, tu peux passer à la bibliothèque ArduinoJson, mais pour un tableau de bord de cette taille, concaténer des String suffit largement. Tu peux aussi essayer le même code sans rien changer en branchant une LED ou un buzzer à la place du relais.

Où stocker le HTML : PROGMEM ou LittleFS ?

Dans le code du tableau de bord, la page se trouve dans une chaîne brute écrite sous la forme R"rawliteral( ... )rawliteral". Cette syntaxe te permet d'utiliser guillemets et retours à la ligne sans caractères d'échappement. Sur les Arduino à base d'AVR, PROGMEM est nécessaire pour garder le texte en flash plutôt qu'en RAM ; sur l'ESP32, les tableaux constants sont de toute façon lus depuis la flash, donc PROGMEM n'est écrit que par compatibilité et ne gêne pas. Quand la page grossit, modifier du HTML au milieu du code devient pénible ; à ce stade, il est plus propre de déplacer les fichiers dans le système de fichiers LittleFS de la flash.

CritèrePROGMEM (dans le code)LittleFS (système de fichiers)
Mise en placeAucune étape en plus, un seul fichierDossier data et plugin de téléversement nécessaires
ÉditionHTML dans un tableau C++Fichiers index.html, style.css et app.js séparés
Mise à jourTu téléverses le croquis à chaque modificationTu téléverses seulement le système de fichiers
TailleIdéal pour des pages de quelques KoEnviron 1,5 Mo d'espace avec le partitionnement par défaut
Images et policesPeu pratiqueIcônes, images et fichiers .gz possibles
Quand ?Premiers essais et petits tableaux de bordInterfaces multipages ou qui grandissent

Servir des fichiers HTML, CSS et JavaScript avec LittleFS

  1. Dans le dossier du croquis (celui qui contient le fichier .ino), crée un dossier nommé data et enregistre dans data/index.html le HTML placé entre R"rawliteral( et )rawliteral" dans le code du tableau de bord. Si tu veux, tu peux séparer le CSS et le JavaScript dans leurs propres fichiers.
  2. L'Arduino IDE 2 ne prend pas en charge l'ancien menu ESP32 Sketch Data Upload. À la place, télécharge le fichier .vsix du plugin arduino-littlefs-upload et place-le dans C:\Users\(ton nom d'utilisateur)\.arduinoIDE\plugins sous Windows ou dans ~/.arduinoIDE/plugins sous macOS et Linux (crée le dossier s'il n'existe pas), puis redémarre l'IDE. Le plugin demande l'IDE 2.2.1 ou plus récent.
  3. Ferme le moniteur série s'il est ouvert, puis ouvre la palette de commandes avec Ctrl+Maj+P (Cmd+Maj+P sur macOS) et choisis Upload LittleFS to Pico/ESP8266/ESP32. Les fichiers sont écrits dans la partition du système de fichiers en flash.
  4. Vérifie que le menu Outils > Partition Scheme indique un schéma avec une partition de système de fichiers (par exemple Default 4MB with spiffs). LittleFS utilise cette partition, nommée spiffs.
  5. Téléverse le code ci-dessous comme d'habitude. Tant que l'option Erase All Flash du menu Outils est désactivée, téléverser le croquis n'efface pas le système de fichiers ; quand tu modifies les fichiers, il suffit de refaire l'étape 3.
C++ (Arduino IDE)
1#include <WiFi.h>
2#include <AsyncTCP.h>
3#include <ESPAsyncWebServer.h>
4#include <LittleFS.h>
5
6const char* ssid = "AgAdi";          // Wi-Fi adın / your Wi-Fi name
7const char* password = "Sifre123";   // Wi-Fi şifren / your Wi-Fi password
8
9AsyncWebServer server(80);
10
11void setup() {
12  Serial.begin(115200);
13
14  if (!LittleFS.begin(true)) {       // true: bağlanamazsa biçimlendir / format if mount fails
15    Serial.println("LittleFS hatasi / LittleFS error");
16    return;
17  }
18
19  WiFi.mode(WIFI_STA);
20  WiFi.begin(ssid, password);
21  while (WiFi.status() != WL_CONNECTED) {
22    delay(500);
23  }
24  Serial.println(WiFi.localIP());
25
26  // data/ klasörünün içeriğini sun / serve the contents of the data/ folder
27  server.serveStatic("/", LittleFS, "/")
28        .setDefaultFile("index.html")    // varsayılan index.htm'dir / default is index.htm
29        .setCacheControl("max-age=600"); // tarayıcı 10 dk önbelleğe alsın / cache for 10 min
30
31  server.onNotFound([](AsyncWebServerRequest *request) {
32    request->send(404, "text/plain", "Sayfa yok / Not found");
33  });
34  server.begin();
35}
36
37void loop() {
38}
Quand un fichier est demandé, serveStatic() cherche d'abord une version compressée portant le même nom avec l'extension .gz. Si tu compresses index.html avec gzip et que tu le téléverses sous le nom index.html.gz, la page tombe généralement entre un tiers et un quart de sa taille et se charge plus vite. Quand l'adresse d'un dossier est demandée, le nom de fichier par défaut est index.htm : n'oublie donc pas la ligne setDefaultFile("index.html"). Si tu utilises la fonction de modèles (template) de la bibliothèque, les repères comme %DEGER% sont remplacés dans la page ; dans ce cas, écris les signes de pourcentage de ton CSS, comme width:100%, sous la forme %%.

mDNS : esp32.local au lieu de l'adresse IP

Ta box peut attribuer une adresse IP différente à l'ESP32 à chaque démarrage, et des nombres comme 192.168.1.47 sont difficiles à retenir. Le mDNS (multicast DNS) permet à un appareil d'annoncer son propre nom sur le réseau local. Grâce à la ligne MDNS.begin("esp32") du code du tableau de bord, les appareils du même réseau peuvent ouvrir la page à l'adresse http://esp32.local, et MDNS.addService("http", "tcp", 80) signale aux applis d'exploration du réseau qu'un serveur web se trouve ici. N'ajoute pas ".local" au nom et n'utilise que des minuscules, des chiffres et des tirets. Si tu utilises plusieurs ESP32, donne à chacun un nom différent (cuisine, salon, etc.), car deux appareils portant le même nom se gênent. Le mDNS n'est pas pris en charge de la même façon partout :

SystèmePrise en charge de .localSi ça ne marche pas
iPhone, iPad, macOSIntégrée (Bonjour)Vérifie que tu es sur le même réseau Wi-Fi
Windows 10 et 11Fonctionne en général sur les versions à jourInstalle le service Bonjour ou utilise l'IP
LinuxFonctionne si avahi-daemon est installéInstalle les paquets avahi-daemon et nss-mdns
AndroidDépend de la version et du navigateurUtilise l'adresse IP ou crée un bail DHCP fixe sur la box

Plusieurs clients : téléphone, tablette et ordinateur en même temps

Ce qu'on apprécie le plus avec WebSocket, c'est que tous ceux qui ouvrent le tableau de bord voient le même état. Quand tu actives le relais depuis ton téléphone, l'ESP32 envoie le nouvel état à toutes les connexions avec textAll(), et l'onglet de ton ordinateur passe au vert au même moment. Chaque onglet est un client distinct : ouvre trois onglets dans le même navigateur et le compteur affiche 3. Par défaut, la bibliothèque autorise 8 clients WebSocket simultanés sur l'ESP32 et 4 sur l'ESP8266 (DEFAULT_MAX_WS_CLIENTS). Cette limite ne s'applique pas toute seule ; c'est l'appel à cleanupClients() qui ferme la plus ancienne connexion au-delà de la limite. Garde aussi ceci en tête :

  • Quand le téléphone se met en veille : un téléphone dont l'écran s'éteint peut ne pas fermer proprement la connexion. Ces connexions à moitié ouvertes sont nettoyées avec le temps grâce à cleanupClients() et à l'envoi régulier de données.
  • Rythme d'envoi : jusqu'à 32 messages peuvent attendre dans la file de chaque client (8 sur l'ESP8266). Si tu appelles textAll() à chaque tour de loop(), la file se remplit et les nouveaux messages sont jetés sans prévenir ; si tu règles Outils > Core Debug Level sur Warn ou un niveau plus détaillé, l'avertissement Too many messages queued apparaît dans le moniteur série. N'envoie une valeur que lorsqu'elle change, ou quelques fois par seconde.
  • Client fraîchement connecté : envoie tout de suite l'état actuel à chaque page qui se connecte ; sinon, l'utilisateur fixe un tableau de bord vide jusqu'à la première mesure. Dans le code, c'est le drapeau levé dans WS_EVT_CONNECT qui s'en charge.
  • Répondre à un seul client : pour ne répondre qu'au navigateur qui a demandé et non à tout le monde, utilise client->text("...") ; client->id() te donne le numéro du client.
  • Mémoire : chaque connexion ouverte occupe un socket TCP et de la mémoire tampon. Les 520 Ko de RAM de l'ESP32 suffisent largement pour quelques clients, mais afficher de temps en temps la mémoire libre avec ESP.getFreeHeap() t'aide à repérer tôt les fuites.

Protection simple par mot de passe

N'importe qui sur ton réseau domestique connaissant l'adresse du tableau de bord peut actionner le relais. Avec les fonctions request->authenticate() et request->requestAuthentication() de la bibliothèque, tu peux protéger la page par un identifiant et un mot de passe ; le navigateur ouvre sa propre fenêtre de connexion et retient les informations pour la session. La méthode par défaut est l'authentification Digest : le mot de passe ne circule donc pas en clair sur le réseau. Mais protéger seulement la page ne suffit pas : l'adresse WebSocket est une porte distincte, et quelqu'un pourrait se connecter directement à ws://ip/ws. L'exemple ci-dessous ferme aussi cette porte : à chaque démarrage, l'ESP32 génère une clé aléatoire, ne la donne qu'au navigateur connecté via /anahtar et rejette avec setFilter() les connexions WebSocket qui n'apportent pas la bonne clé.

C++ (Arduino IDE)
1#include <WiFi.h>
2#include <AsyncTCP.h>
3#include <ESPAsyncWebServer.h>
4
5const char* ssid = "AgAdi";          // Wi-Fi adın / your Wi-Fi name
6const char* password = "Sifre123";   // Wi-Fi şifren / your Wi-Fi password
7const char* KULLANICI = "admin";     // panel kullanıcı adı / panel username
8const char* PAROLA = "Gizli-2026";   // panel parolası / panel password
9const int LED_PIN = 2;
10
11AsyncWebServer server(80);
12AsyncWebSocket ws("/ws");
13String wsAnahtar;                    // her açılışta yeni anahtar / a new key on every boot
14bool ledAcik = false;
15
16const char index_html[] PROGMEM = R"rawliteral(
17<!DOCTYPE html>
18<html><head><meta charset="utf-8">
19<meta name="viewport" content="width=device-width, initial-scale=1"></head>
20<body style="font-family:sans-serif;text-align:center">
21<h2>Korumali panel / Protected panel</h2>
22<button style="font-size:1.4em;padding:12px 32px" onclick="ws && ws.send('toggle')">LED</button>
23<p id="durum">...</p>
24<script>
25let ws;
26fetch('/anahtar').then(r => r.text()).then(k => {
27  ws = new WebSocket('ws://' + location.host + '/ws?k=' + k);
28  ws.onmessage = (e) => { document.getElementById('durum').textContent = e.data; };
29});
30</script>
31</body></html>
32)rawliteral";
33
34bool girisVar(AsyncWebServerRequest *request) {
35  return request->authenticate(KULLANICI, PAROLA);
36}
37
38void setup() {
39  Serial.begin(115200);
40  pinMode(LED_PIN, OUTPUT);
41
42  WiFi.mode(WIFI_STA);
43  WiFi.begin(ssid, password);
44  while (WiFi.status() != WL_CONNECTED) {
45    delay(500);
46  }
47  Serial.println(WiFi.localIP());
48
49  // Wi-Fi açıkken random() donanım üretecini kullanır / random() uses the hardware RNG
50  wsAnahtar = String(random(0x7FFFFFFF), HEX) + String(random(0x7FFFFFFF), HEX);
51
52  // Sayfa: tarayıcı kullanıcı adı ve parola sorar / page: the browser asks for login
53  server.on("/", HTTP_GET, [](AsyncWebServerRequest *request) {
54    if (!girisVar(request)) return request->requestAuthentication();
55    request->send(200, "text/html", index_html);
56  });
57
58  // Anahtar yalnız giriş yapana verilir / the key is given only after login
59  server.on("/anahtar", HTTP_GET, [](AsyncWebServerRequest *request) {
60    if (!girisVar(request)) return request->requestAuthentication();
61    request->send(200, "text/plain", wsAnahtar);
62  });
63
64  // Doğru anahtarı getirmeyen bağlantıyı reddet / reject sockets without the right key
65  ws.setFilter([](AsyncWebServerRequest *request) {
66    return request->hasParam("k") && request->getParam("k")->value() == wsAnahtar;
67  });
68
69  ws.onEvent([](AsyncWebSocket *sunucu, AsyncWebSocketClient *client, AwsEventType type,
70                void *arg, uint8_t *data, size_t len) {
71    if (type == WS_EVT_DATA && len == 6 && memcmp(data, "toggle", 6) == 0) {
72      ledAcik = !ledAcik;
73      digitalWrite(LED_PIN, ledAcik ? HIGH : LOW);
74      sunucu->textAll(ledAcik ? "LED: ON" : "LED: OFF");
75    }
76  });
77
78  server.addHandler(&ws);
79  server.begin();
80}
81
82void loop() {
83  ws.cleanupClients();               // saniyede bir yeterli / once per second is enough
84  delay(1000);
85}
Cette protection est un verrou suffisant pour un réseau domestique, mais ce n'est pas du chiffrement : ESPAsyncWebServer ne propose pas de HTTPS sur l'ESP32 et les messages WebSocket circulent en clair. Comme le mot de passe se trouve dans le code, pense à l'effacer avant de partager ton code. N'ouvre pas le tableau de bord sur internet par une redirection de port sur ta box ; pour piloter à distance, un bot Telegram avec ESP32, MQTT et Home Assistant ou la fonction VPN de ta box sont bien plus sûrs. Dans les versions récentes de la bibliothèque, tu peux aussi faire la même chose avec la classe AsyncAuthenticationMiddleware.

Remarques pour l'ESP8266 et le Raspberry Pi Pico W

La même bibliothèque fonctionne aussi sur l'ESP8266 (NodeMCU, Wemos D1 mini). Voici ce qu'il faut changer : installe la bibliothèque ESP Async TCP d'ESP32Async au lieu d'Async TCP ; inclus ESP8266WiFi.h au lieu de WiFi.h, ESPAsyncTCP.h au lieu d'AsyncTCP.h et ESP8266mDNS.h au lieu d'ESPmDNS.h, et ajoute la ligne MDNS.update() dans loop(). Remplace WiFi.setSleep(false) par WiFi.setSleepMode(WIFI_NONE_SLEEP) et adapte les broches d'après la colonne NodeMCU du tableau de câblage. L'ESP8266 n'a qu'environ 80 Ko de RAM utilisable : garde la page légère et souviens-toi que la limite est de 4 clients. Si tu utilises un Raspberry Pi Pico W, tu trouveras comment créer un serveur web en MicroPython dans le guide Raspberry Pi Pico W et Wi-Fi ; les versions récentes d'ESPAsyncWebServer prennent aussi en charge les puces RP2040 et RP2350 côté Arduino.

Idées de projets avec le serveur web ESP32

  • Prise connectée multicanal : avec une carte 4 relais, crée des boutons séparés pour une lampe, un ventilateur et une prise ; distingue les commandes par de courts textes comme "r1" et "r2". Pour choisir le relais, consulte l'article sur le module relais.
  • Tableau de bord d'arrosage : affiche en direct la valeur du capteur d'humidité du sol, règle le seuil d'arrosage avec un curseur sur la page et lance la pompe à la main quand tu veux.
  • Station météo : utilise un BME280 à la place du DHT22 pour ajouter la pression, et affiche les 60 dernières mesures sous forme de petite courbe sur la page.
  • Lampe à sélecteur de couleur : envoie par WebSocket la valeur d'un sélecteur de couleur de la page et colore instantanément un ruban WS2812B NeoPixel.
  • Servo au curseur : envoie la valeur du curseur à chaque mouvement et fais tourner un servomoteur ; idéal pour orienter une caméra ou actionner un loquet.
  • Journal horodaté : ajoute à chaque mesure l'heure obtenue par NTP et mets ton code à jour sans fil par OTA plutôt que par USB à chaque fois.

Erreurs fréquentes et solutions

SymptômeCauseSolution
'mbedtls_md5_starts_ret' was not declared in this scopeAncienne bibliothèque me-no-dev avec le cœur ESP32 3.xInstalle ESP Async WebServer et Async TCP d'ESP32Async, supprime les anciens dossiers
Multiple libraries were found for "AsyncTCP.h"Deux copies de la même bibliothèqueSupprime les anciennes copies -master du dossier libraries
Avertissement deprecated sur send_Psend_P est en cours de retrait dans la nouvelle versionUtilise request->send(200, "text/html", index_html)
La page ne s'ouvre pas du toutTéléphone sur le réseau invité ou en 4G, ou adresse IP changéeRejoins le même Wi-Fi et utilise l'IP actuelle du moniteur série
La page s'ouvre mais les valeurs restent à --WebSocket ne se connecte pas : chemin différent ou addHandler oubliéOuvre la console avec F12 et aligne ws("/ws") sur l'adresse du JavaScript
Task watchdog got triggered avec async_tcpdelay(), longue boucle ou requête HTTP dans un callbackGarde le callback court et déplace le travail dans loop() avec un drapeau
Too many messages queuedtextAll() est appelé trop souventN'envoie qu'en cas de changement ou quelques fois par seconde
La page LittleFS renvoie 404Dossier data non téléversé ou setDefaultFile manquantTéléverse les fichiers avec le plugin et ajoute setDefaultFile("index.html")
does not exist, no permits for creation dans le moniteur sérieLe fichier demandé n'est pas dans LittleFSVérifie le nom du fichier, majuscules et minuscules comprises ; les lignes avec .gz sont sans danger
Le relais ne se coupe jamaisLe module 5 V ne reconnaît pas 3,3 V comme HIGHUtilise un module compatible 3,3 V, JD-VCC ou un transistor
L'ESP32 redémarre quand le relais colleLe courant de la bobine fait chuter l'alimentation (brownout)Utilise un bon bloc 5 V et un condensateur de 470 µF
esp32.local ne s'ouvre pasAppareil sans prise en charge mDNS ou nom écrit avec .localÉcris MDNS.begin("esp32") et utilise l'adresse IP si besoin
Le bouton réagit avec du retardLa veille du modem Wi-Fi est activeAppelle WiFi.setSleep(false) après la connexion
Quand quelque chose ne marche pas, ouvre les outils de développement du navigateur avec F12. L'onglet Console affiche des erreurs comme WebSocket connection to 'ws://192.168.1.50/ws' failed ; dans l'onglet Réseau, clique sur la connexion WebSocket et la section Messages affiche chaque message entrant et sortant. Côté ESP32, afficher dans le moniteur série les clients qui se connectent et se déconnectent te dit en quelques secondes si le problème vient du navigateur ou de la carte. Si la carte redémarre au moment où le relais colle, consulte le guide sur le brownout de l'ESP32.

Le duo serveur web asynchrone ESP32 et WebSocket est le chemin le plus court vers un tableau de bord rapide, qui fonctionne sans service cloud ni appli mobile à écrire, et que plusieurs appareils peuvent partager. Commence par le tableau de bord PROGMEM en un seul fichier, passe à LittleFS quand l'interface grandit, et donne à chaque nouvel appareil de ton réseau son propre nom mDNS. Si tu découvres l'ESP32, tu trouveras les bases dans Qu'est-ce que l'ESP32 ? Dans l'application BlueGrays, tu peux aussi suivre pas à pas, avec schémas de câblage et code prêt à l'emploi, des projets ESP32 comme un serveur web asynchrone, un compteur WebSocket, l'accès par mDNS, une horloge NTP et la mise à jour OTA.

Questions fréquentes

Quelle est la différence entre ESPAsyncWebServer et WebServer sur l'ESP32 ?

La bibliothèque WebServer traite les requêtes une par une via l'appel à handleClient() dans loop() ; si loop() ralentit, la page ralentit aussi. ESPAsyncWebServer traite les requêtes dans une tâche séparée en arrière-plan, supporte plusieurs connexions à la fois et intègre WebSocket et SSE. Pour les tableaux de bord en temps réel, la version asynchrone est la plus adaptée.

Combien de clients peuvent se connecter en même temps à un serveur web ESP32 ?

Par défaut, ESPAsyncWebServer autorise 8 clients WebSocket sur l'ESP32 et ferme les clients en trop lors de l'appel à cleanupClients() ; sur l'ESP8266, la limite est de 4. Comme les requêtes de page classiques sont brèves, davantage d'appareils peuvent ouvrir la page les uns après les autres. En pratique, un tableau de bord domestique tourne sans problème avec 3 à 5 appareils simultanés.

Puis-je accéder à mon serveur web ESP32 depuis internet ?

C'est techniquement possible avec une redirection de port sur ta box, mais ce n'est pas conseillé : la bibliothèque ne propose pas de HTTPS sur l'ESP32 et l'appareil reste exposé aux attaques venues d'internet. Pour piloter à distance, un bot Telegram, un système basé sur MQTT ou la fonction VPN de ta box sont bien plus sûrs.

Pourquoi esp32.local ne fonctionne pas ?

La cause la plus fréquente est que le téléphone ou l'ordinateur ne sait pas résoudre les noms mDNS ; c'est surtout le cas sur certains téléphones Android et sur les anciennes versions de Windows. Écrire MDNS.begin("esp32.local") dans le code est aussi une erreur, car le nom doit être donné sans .local. Si le nom ne se résout toujours pas, utilise l'adresse IP du moniteur série ou réserve une IP fixe pour l'ESP32 sur ta box.

Un serveur web ESP32 fonctionne-t-il sans box ni routeur ?

Oui. Avec WiFi.softAP("ESP32-Panel", "12345678"), l'ESP32 crée son propre réseau Wi-Fi ; connecte ton téléphone à ce réseau et ouvre 192.168.4.1 dans le navigateur. Dans ce mode point d'accès, il n'y a pas d'internet, mais le tableau de bord et WebSocket fonctionnent de la même façon. Le mot de passe du réseau doit compter au moins 8 caractères.

Quelle bibliothèque ESPAsyncWebServer faut-il installer ?

Dans le gestionnaire de bibliothèques de l'Arduino IDE, installe ESP Async WebServer et Async TCP dont l'auteur est ESP32Async. La version d'origine du compte me-no-dev n'est plus maintenue et ne compile pas avec le cœur ESP32 3.x. Supprime aussi de ton dossier libraries les anciennes copies ajoutées depuis un ZIP.

Voir tous les projets
Serveur Web asynchrone ESP32 (avec affichage de l'adresse IP sur l'écran LCD)
#129Avancé

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

4.8(16)
26
Compteur ESP32 WebSocket (2 boutons + écran LCD + affichage Web en temps réel)
#130Avancé
Premium

Compteur ESP32 WebSocket (2 boutons + écran LCD + affichage Web en temps réel)

Dans ce projet, deux boutons reliés à l'ESP32 (GPIO 5 incrémente, GPIO 4 décrémente) modifient un compteur. La valeur se met à jour instantanément sur un écran LCD I2C 16x2 et, grâce au support WebSocket d'ESPAsyncWebServer, dans tous les navigateurs connectés sans recharger la page. Au lieu du polling HTTP, une seule connexion persistante est utilisée et l'ESP32 envoie la nouvelle valeur à tous avec ws.textAll().

4.5(14)
23
ESP32 mDNS : accès à l'appareil via bluegrays-esp.local (avec écran LCD)
#131Avancé
Premium

ESP32 mDNS : accès à l'appareil via bluegrays-esp.local (avec écran LCD)

Dans ce projet, en ajoutant un service mDNS (Multicast DNS) à l'ESP32, nous permettons d'accéder à l'appareil via un nom d'hôte lisible, tel que bluegrays-esp.local, plutôt qu'à l'aide d'une adresse IP. Même si l'adresse IP attribuée par le DHCP venait à changer, vous pourrez toujours accéder à l'appareil sous le même nom. En exécutant également un petit serveur web sur l'appareil, nous envoyons des commandes de contrôle des LED depuis un navigateur via l'adresse http://bluegrays-esp.local/.

4.8(14)
23
Connexion Wi-Fi STA ESP32 (avec écran LCD)
#127Avancé

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.

4.4(21)
34
ESP32 DHT22 Température et humidité (avec écran LCD)
#117Intermédiaire

ESP32 DHT22 Température et humidité (avec écran LCD)

Il s'agit d'un projet permettant de mesurer la température ambiante et le taux d'humidité à l'aide d'un capteur DHT22 et d'afficher ces données en temps réel sur un écran LCD I2C 16x2. Les données du capteur sont lues à l'aide du protocole one-wire grâce à la bibliothèque Adafruit DHT. Ce projet constitue la base des stations météo IoT, de la domotique ou des systèmes de surveillance de l'environnement dans les serres.

4.8(38)
56
Éclairage Intelligent ESP32 (PIR + LDR + Relais + OLED)
#141Avancé
Premium

Éclairage Intelligent ESP32 (PIR + LDR + Relais + OLED)

Transformez votre couloir, votre salle de bain ou votre garde-manger en éclairage intelligent avec l'ESP32 ! En utilisant un capteur de mouvement PIR, un capteur de lumière LDR et un module relais, nous construisons le scénario classique "la lumière s'allume lorsqu'un mouvement est détecté dans l'obscurité" — si l'environnement est déjà lumineux, le mouvement N'ACTIVE PAS la lumière, économisant ainsi de l'énergie pendant la journée. La lumière reste allumée pendant 30 secondes ; si le mouvement

4.3(6)
11
Mise à jour OTA de l'ESP32 — Installation sans fil du firmware (avec écran LCD)
#132Avancé
Premium

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

4.6(14)
22
ESP32 : heure réelle depuis Internet via NTP
#128Avancé
Premium

ESP32 : heure réelle depuis Internet via NTP

L'ESP32 se connecte au Wi-Fi et récupère l'heure réelle avec une précision de la milliseconde depuis des serveurs NTP (Network Time Protocol). Grâce à configTzTime(), le fuseau horaire de la Turquie (TRT, UTC+3) est appliqué automatiquement et le passage DST est géré via la chaîne POSIX TZ. L'heure est affichée sur le moniteur série et sur un écran LCD I2C en option. configTzTime() démarre le service SNTP ; des resynchronisations périodiques ont lieu tant que l'accès au réseau est disponible

4.5(13)
21