Guide · 17 min di lettura

Web server asincrono ESP32 e WebSocket: controllo in tempo reale

Web server asincrono ESP32 con WebSocket: comanda un relè e guarda i sensori in tempo reale senza ricaricare. Librerie, PROGMEM o LittleFS, esp32.local, errori.

Un web server asincrono ESP32 (ESP32 async web server), realizzato con la libreria ESPAsyncWebServer, trasforma il tuo ESP32 in un web server veloce che può rispondere a più smartphone e computer contemporaneamente. Con il supporto a WebSocket incluso nella libreria, browser e scheda mantengono una linea sempre aperta: premi il pulsante sulla pagina e il relè scatta subito, e quando la temperatura cambia il numero sulla pagina si aggiorna da solo, senza ricaricare. Ogni altro dispositivo che ha aperto lo stesso pannello vede la tua modifica nello stesso secondo. In breve, questa coppia è il modo più pratico per costruire il tuo pannello domotico all'interno della rete di casa, senza alcun servizio cloud.

In questa guida vedrai prima in cosa un server asincrono si differenzia dalla classica libreria WebServer e perché WebSocket è più veloce delle normali richieste HTTP. Poi installerai le librerie giuste, avvierai il server più semplice possibile e costruirai un pannello di controllo in tempo reale con un relè e un sensore DHT22. Successivamente imparerai a tenere l'HTML in PROGMEM o in LittleFS, a usare esp32.local al posto dell'indirizzo IP, a gestire più client, a proteggere il pannello con una semplice password e a risolvere gli errori di compilazione e di connessione più comuni. Il codice è scritto per funzionare con le versioni 2.x e 3.x del core ESP32 nell'Arduino IDE.

Scheda di sviluppo ESP32 per un progetto di web server
ESP32 DevKit: grazie al chip Wi-Fi fa girare da sola un web server; il pannello che apri sullo smartphone arriva direttamente dalla memoria flash di questa scheda.

Che cos'è il web server asincrono dell'ESP32 e come funziona?

La libreria WebServer.h fornita con il core ESP32 è sincrona: le richieste in arrivo vengono gestite, una alla volta e in ordine, solo quando dentro loop() viene chiamato server.handleClient(). Se loop() contiene un delay() di 2 secondi, anche la pagina aspetta 2 secondi; se due persone si collegano insieme, una aspetta che l'altra finisca. ESPAsyncWebServer, invece, gira in un task FreeRTOS separato (async_tcp) creato dalla libreria AsyncTCP. Quando arriva una richiesta, le intestazioni vengono lette in background, la funzione che hai scritto (il callback) viene chiamata quando la richiesta è pronta e anche la risposta parte in background. Nel frattempo loop() continua il suo lavoro. Così più connessioni possono restare aperte contemporaneamente e diventano possibili le connessioni sempre aperte come WebSocket e Server-Sent Events (SSE). Se ti incuriosiscono i due core dell'ESP32 e i task, dai un'occhiata all'articolo su FreeRTOS e l'ESP32.

CaratteristicaWebServer.h (sincrono)ESPAsyncWebServer (asincrono)
InstallazioneInclusa nel core ESP322 librerie dal Gestore librerie
Gestione delle richiesteUna alla volta con handleClient() in loop()Nel task async_tcp in background, più richieste insieme
delay() dentro loop()Ritarda la risposta della paginaNon influisce sul server
WebSocketNo; serve una libreria a parte (ad es. WebSockets di Markus Sattler)Integrato (AsyncWebSocket)
Server-Sent EventsNoIntegrato (AsyncEventSource)
File staticiCon serveStaticserveStatic, supporto .gz automatico e controllo della cache
Attenzione aLa più facile da imparareNiente delay() né operazioni lunghe nei callback

Che cos'è WebSocket e in cosa differisce da una richiesta HTTP?

In una normale richiesta HTTP il browser apre una connessione, dice "dammi questa pagina", riceve la risposta e la conversazione finisce. Il server non può dire nulla al browser di sua iniziativa; per vedere un nuovo valore, il browser deve chiedere di nuovo. WebSocket (RFC 6455) è invece una stretta di mano che inizia come HTTP: il browser aggiunge alla richiesta l'intestazione Upgrade: websocket, l'ESP32 accetta con la risposta 101 Switching Protocols e la stessa connessione TCP resta aperta. Da quel momento entrambe le parti possono inviare messaggi quando vogliono (full-duplex). Ogni messaggio aggiunge solo da 2 a 14 byte di overhead, mentre ogni richiesta HTTP si porta dietro centinaia di byte di intestazioni. Per questo i messaggi WebSocket arrivano in pochi millisecondi ed è il metodo più efficiente per i pannelli che inviano diversi aggiornamenti al secondo. Ecco come si confrontano i quattro modi per tenere una pagina aggiornata con i valori di un sensore:

MetodoCome funzionaLatenza e caricoQuando usarlo
Ricarica della pagina (meta refresh)Il browser riscarica l'intera pagina ogni pochi secondiAlti; la pagina sfarfalla ogni voltaMeglio evitarlo
Interrogazione con fetch (polling)JavaScript chiede un piccolo JSON ogni 1–2 secondiMedi; ogni richiesta porta nuove intestazioniUn singolo valore che cambia di rado
Server-Sent Events (SSE)Il server invia eventi in una sola direzione su una connessione apertaBassi; solo dal server al browserPannelli di sola visualizzazione
WebSocketMessaggi nelle due direzioni su una connessione apertaI più bassi; frame di pochi bytePulsanti, slider e dati in tempo reale insieme

Librerie necessarie: installare ESPAsyncWebServer e AsyncTCP

  1. Assicurati che nell'Arduino IDE sia installato il pacchetto di schede ESP32 (Espressif Systems); i passaggi si trovano in Che cos'è l'ESP32?
  2. Apri il Gestore librerie (Ctrl+Maiusc+I) e cerca ESPAsyncWebServer. Installa la libreria ESP Async WebServer il cui autore è ESP32Async.
  3. Questa libreria non dichiara la sua dipendenza al Gestore librerie, quindi l'IDE non ti propone altro: installa separatamente la libreria Async TCP, pubblicata anch'essa da ESP32Async. Su un ESP8266 ti serve invece la libreria ESP Async TCP di ESP32Async (ESPAsyncTCP.h).
  4. Per l'esempio del pannello, installa la DHT sensor library di Adafruit e la dipendenza Adafruit Unified Sensor che richiede. Se è la prima volta che installi una libreria, consulta la guida su come aggiungere librerie ad Arduino.
  5. Se nella cartella Documenti/Arduino/libraries ci sono cartelle ESPAsyncWebServer-master, AsyncTCP-master o ESPAsyncWebSrv aggiunte in passato da un file ZIP, eliminale. Due copie della stessa libreria portano il compilatore a scegliere quella sbagliata.
La maggior parte dei vecchi tutorial online rimanda alla libreria ESPAsyncWebServer originale dell'account me-no-dev. Quella libreria non viene più aggiornata e non si compila con la versione 3.x del core ESP32; la compilazione si ferma con l'errore 'mbedtls_md5_starts_ret' was not declared in this scope. La soluzione è eliminare la vecchia copia e installare le versioni di ESP32Async. Nella nuova versione, inoltre, send_P() è deprecata (deprecated): sull'ESP32 puoi inviare un array PROGMEM direttamente con request->send(200, "text/html", index_html).

Primo passo: il server più semplice con AsyncWebServer

Prima di passare al pannello, vediamo la logica della libreria con un piccolo esempio. Il codice qui sotto collega l'ESP32 alla rete di casa, mostra un saluto nella pagina principale e accende e spegne il LED integrato tramite gli indirizzi /led?durum=1 e /led?durum=0. Caricalo, apri il Monitor seriale a 115200 baud e scrivi l'indirizzo che compare nel browser di uno smartphone collegato alla stessa Wi-Fi. Se la scheda non si collega alla rete, usa il codice di diagnosi della guida l'ESP32 non si connette al 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}
IstruzioneA cosa serve
AsyncWebServer server(80)Crea il server in ascolto sulla porta HTTP 80
server.on("/led", HTTP_GET, ...)Registra la funzione (callback) da eseguire per un indirizzo e un tipo di richiesta
request->hasParam("durum")Controlla se l'indirizzo contiene il parametro ?durum=...
request->getParam("durum")->value()Restituisce il valore del parametro come String
request->send(codice, tipo, contenuto)Invia la risposta: 200 riuscita, 404 non trovato; tipo text/plain, text/html o application/json
server.onNotFound(...)Viene eseguita per ogni indirizzo non definito
server.begin()Avvia il server; va chiamata dopo tutte le righe server.on()
Un loop() vuoto non è un errore; il server gira nel suo task. Al contrario, non usare delay(), lunghi cicli while o richieste che scaricano dati da internet dentro i callback. Il task async_tcp resta bloccato e, dopo qualche secondo, l'ESP32 si riavvia con il messaggio Task watchdog got triggered. Affida i lavori lunghi a loop() tramite una variabile flag e gestisci i tempi con millis().

Materiali e collegamenti per il pannello di controllo

Nell'esempio del pannello accenderai e spegnerai una lampada o una ventola con un modulo relè e misurerai temperatura e umidità con un DHT22. Ti servono un ESP32 DevKit, un modulo relè a 5 V a un canale, un modulo sensore DHT22 (o DHT11), una breadboard e cavetti jumper. Per il relè, preferisci i moduli con fotoaccoppiatore. Il DHT22 misura da −40 a 80 °C con una precisione di ±0,5 °C e si può leggere al massimo una volta ogni 2 secondi; il DHT11 costa meno, ma offre ±2 °C nell'intervallo 0–50 °C. Per i dettagli sul sensore guarda l'articolo sul sensore di temperatura e umidità DHT11, e per la piedinatura della tua scheda lo strumento Pinout.

Pin del moduloESP32 DevKitESP8266 NodeMCUNota
VCC del relèVIN (5 V)VIN o VU (5 V)La bobina del relè vuole 5 V; alimentata da USB
GND del relèGNDGNDMassa comune
IN del relèGPIO 26D1 (GPIO 5)La maggior parte dei moduli scatta con un segnale LOW
VCC (+) del DHT223V33V3Alimentato a 3,3 V, anche la linea dati è a 3,3 V
DATA (OUT) del DHT22GPIO 4D2 (GPIO 4)Sensore nudo: resistenza da 10 kΩ tra DATA e VCC
GND (−) del DHT22GNDGNDMassa comune
I pin dell'ESP32 escono a 3,3 V. Alcuni moduli relè a 5 V non riconoscono questo livello come HIGH e il relè non si rilascia mai; con un modulo del genere usa un relè compatibile con 3,3 V, un modulo con ponticello JD-VCC o un transistor in mezzo. Se con il relè devi commutare la tensione di rete (230 V), collega i cavi senza corrente, non lasciare morsetti scoperti e, se non sei sicuro, prova prima con un carico a bassa tensione (una striscia LED o una ventola a 12 V). I dettagli sono in Che cos'è un modulo relè?

Pannello di controllo in tempo reale: relè, DHT22 e WebSocket

Passiamo ora al progetto vero e proprio. Il codice qui sotto è un unico file: HTML, CSS e JavaScript sono al suo interno come array PROGMEM. All'apertura della pagina il browser stabilisce una connessione WebSocket verso /ws. Ogni 2 secondi l'ESP32 legge il DHT22 e invia a tutti i browser temperatura, umidità, stato del relè e numero di dispositivi collegati in un unico messaggio JSON. Quando premi il pulsante, il browser invia il messaggio toggle, l'ESP32 inverte il relè e comunica di nuovo il nuovo stato a tutti. Cambia nome della rete e password, carica il codice e poi apri nel browser http://esp32.local o l'indirizzo IP mostrato nel Monitor seriale.

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}

Come funziona il codice?

  • AsyncWebSocket ws("/ws") crea l'endpoint WebSocket e server.addHandler(&ws) lo collega al server. L'indirizzo usato nel JavaScript deve corrispondere esattamente a questo percorso.
  • La funzione wsOlay() viene chiamata a ogni connessione, disconnessione e messaggio in arrivo. WS_EVT_CONNECT indica un nuovo client, WS_EVT_DISCONNECT un client che se ne va e WS_EVT_DATA un messaggio in arrivo.
  • AwsFrameInfo contiene le informazioni sul frame ricevuto. Il controllo di final, index == 0 e len conferma che il messaggio è arrivato completo in un solo frame, e WS_TEXT che si tratta di testo. I comandi brevi stanno sempre in un solo frame.
  • I dati in arrivo vengono confrontati insieme alla loro lunghezza con memcmp(); così non dipendi dalla presenza o meno di un carattere nullo alla fine dei dati.
  • Poiché i callback girano nel task async_tcp, si limitano a invertire il relè e ad alzare il flag gonder. L'invio vero e proprio avviene in loop() con ws.textAll(); in questo modo tutti i messaggi partono da un solo punto, in ordine.
  • ws.cleanupClients() viene chiamata una volta al secondo: libera la memoria delle connessioni cadute e chiude la connessione più vecchia quando il numero di client supera il limite.
  • Lato JavaScript, l'evento onclose si ricollega dopo 2 secondi. Anche se l'ESP32 si riavvia o la Wi-Fi cade per un attimo, la pagina si riprende da sola. Poiché l'indirizzo è costruito con location.host, la pagina funziona sia con l'IP sia con esp32.local.
  • WiFi.setSleep(false) disattiva il risparmio energetico della Wi-Fi. La sospensione del modem predefinita può aggiungere ritardi fino a qualche centinaio di millisecondi; disattivarla su un pannello alimentato da rete rende i pulsanti istantanei.

Scegliere JSON come formato dei messaggi ti semplifica la vita: il browser trasforma il testo ricevuto in un oggetto con una sola riga grazie a JSON.parse(). Un messaggio di esempio è questo: {"role":true,"sicaklik":23.4,"nem":48,"istemci":2}. Quando il sensore non si riesce a leggere, il valore viene inviato come null e la pagina mostra "--"; se un errore di lettura partisse come NaN, il JSON non valido bloccherebbe del tutto l'aggiornamento della pagina. Dato che ogni messaggio occupa circa 60 byte, nemmeno diversi aggiornamenti al secondo appesantiscono la rete. Per messaggi più complessi puoi passare alla libreria ArduinoJson, ma per un pannello di queste dimensioni unire delle String è più che sufficiente. Puoi anche provare lo stesso codice senza modifiche collegando un LED o un buzzer al posto del relè.

Dove tenere l'HTML: PROGMEM o LittleFS?

Nel codice del pannello la pagina si trova in una stringa raw scritta nella forma R"rawliteral( ... )rawliteral". Questa sintassi ti permette di usare virgolette e a capo senza caratteri di escape. Sugli Arduino basati su AVR, PROGMEM serve per tenere il testo nella flash invece che nella RAM; sull'ESP32 gli array costanti vengono comunque letti dalla flash, quindi PROGMEM si scrive solo per compatibilità e non fa danni. Quando la pagina cresce, modificare l'HTML dentro il codice diventa scomodo; a quel punto è più ordinato spostare i file nel file system LittleFS della flash.

CriterioPROGMEM (nel codice)LittleFS (file system)
PreparazioneNessun passaggio in più, un solo fileServono la cartella data e un plugin di caricamento
ModificaHTML dentro un array C++File index.html, style.css e app.js separati
AggiornamentoRicarichi lo sketch a ogni modificaRicarichi solo il file system
DimensioniIdeale per pagine di pochi KBCirca 1,5 MB di spazio con la partizione predefinita
Immagini e fontPoco praticoSi possono servire icone, immagini e file .gz
Quando?Primi test e pannelli piccoliInterfacce multipagina o in crescita

Servire file HTML, CSS e JavaScript con LittleFS

  1. Nella cartella dello sketch (quella che contiene il file .ino) crea una cartella chiamata data e salva come data/index.html l'HTML che nel codice del pannello si trova tra R"rawliteral( e )rawliteral". Se vuoi, puoi dividere CSS e JavaScript in file separati.
  2. L'Arduino IDE 2 non supporta il vecchio menu ESP32 Sketch Data Upload. Scarica invece il file .vsix del plugin arduino-littlefs-upload e mettilo in C:\Users\(tuo nome utente)\.arduinoIDE\plugins su Windows o in ~/.arduinoIDE/plugins su macOS e Linux (crea la cartella se non esiste), poi riavvia l'IDE. Il plugin richiede l'IDE 2.2.1 o successivo.
  3. Chiudi il Monitor seriale se è aperto, poi apri la tavolozza dei comandi con Ctrl+Maiusc+P (Cmd+Maiusc+P su macOS) e scegli Upload LittleFS to Pico/ESP8266/ESP32. I file vengono scritti nella partizione del file system della flash.
  4. Controlla che in Strumenti > Partition Scheme sia selezionato uno schema con una partizione per il file system (ad esempio Default 4MB with spiffs). LittleFS usa questa partizione, chiamata spiffs.
  5. Carica il codice qui sotto come al solito. Finché l'opzione Erase All Flash del menu Strumenti è disattivata, caricare lo sketch non cancella il file system; quando modifichi i file basta ripetere il passaggio 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}
Quando viene richiesto un file, serveStatic() cerca prima una versione compressa con lo stesso nome e l'estensione .gz. Se comprimi index.html con gzip e lo carichi come index.html.gz, la pagina di solito si riduce a un valore tra un terzo e un quarto delle sue dimensioni e si apre più in fretta. Quando viene richiesto l'indirizzo di una cartella, il nome di file predefinito è index.htm, quindi non dimenticare la riga setDefaultFile("index.html"). Se usi la funzione di template della libreria, i segnaposto come %DEGER% nella pagina vengono sostituiti; in quel caso devi scrivere come %% i segni di percentuale del CSS, ad esempio in width:100%.

mDNS: esp32.local al posto dell'indirizzo IP

Il router può assegnare all'ESP32 un indirizzo IP diverso a ogni avvio, e numeri come 192.168.1.47 sono difficili da ricordare. Il mDNS (multicast DNS) permette a un dispositivo di annunciare il proprio nome sulla rete locale. Grazie alla riga MDNS.begin("esp32") del codice del pannello, i dispositivi sulla stessa rete possono aprire la pagina all'indirizzo http://esp32.local, mentre MDNS.addService("http", "tcp", 80) segnala alle app che esplorano la rete che qui c'è un web server. Non aggiungere ".local" al nome e usa solo lettere minuscole, cifre e trattini. Se usi più ESP32, dai a ciascuno un nome diverso (cucina, salotto e così via), perché due dispositivi con lo stesso nome si disturbano a vicenda. Il mDNS non è supportato allo stesso modo ovunque:

SistemaSupporto .localSe non funziona
iPhone, iPad, macOSIntegrato (Bonjour)Controlla di essere sulla stessa rete Wi-Fi
Windows 10 e 11Di solito funziona nelle versioni aggiornateInstalla il servizio Bonjour o usa l'IP
LinuxFunziona se avahi-daemon è installatoInstalla i pacchetti avahi-daemon e nss-mdns
AndroidDipende dalla versione e dal browserUsa l'indirizzo IP o imposta una prenotazione DHCP sul router

Più client: smartphone, tablet e computer insieme

La cosa più apprezzata di WebSocket è che chiunque apra il pannello vede lo stesso stato. Quando accendi il relè dallo smartphone, l'ESP32 invia il nuovo stato a tutte le connessioni con textAll() e la scheda del browser sul computer diventa verde nello stesso istante. Ogni scheda del browser è un client separato: se apri tre schede nello stesso browser, il contatore mostra 3. Per impostazione predefinita la libreria consente 8 client WebSocket contemporanei sull'ESP32 e 4 sull'ESP8266 (DEFAULT_MAX_WS_CLIENTS). Questo limite non viene applicato da solo; quando si chiama cleanupClients(), la connessione più vecchia oltre il limite viene chiusa. Tieni inoltre presente quanto segue:

  • Quando lo smartphone va in standby: uno smartphone con lo schermo spento potrebbe non chiudere bene la connessione. Queste connessioni rimaste a metà vengono ripulite con il tempo grazie a cleanupClients() e all'invio regolare di dati.
  • Frequenza di invio: per ogni client possono restare in coda al massimo 32 messaggi (8 sull'ESP8266). Se chiami textAll() a ogni giro di loop(), la coda si riempie e i nuovi messaggi vengono scartati senza avviso; se imposti Strumenti > Core Debug Level su Warn o su un livello più dettagliato, nel Monitor seriale compare l'avviso Too many messages queued. Invia un valore solo quando cambia, oppure poche volte al secondo.
  • Client appena collegato: invia subito lo stato attuale a ogni pagina che si collega; altrimenti l'utente guarda un pannello vuoto fino alla prima misura. Nel codice del pannello se ne occupa il flag alzato in WS_EVT_CONNECT.
  • Risposta a un solo client: per rispondere solo al browser che ha chiesto e non a tutti, usa client->text("..."); il numero del client te lo dà client->id().
  • Memoria: ogni connessione aperta occupa un socket TCP e della memoria buffer. I 520 KB di RAM dell'ESP32 bastano e avanzano per alcuni client, ma stampare ogni tanto la memoria libera con ESP.getFreeHeap() ti aiuta a scoprire per tempo eventuali perdite.

Semplice protezione con password

Chiunque sulla rete di casa conosca l'indirizzo del pannello può accendere e spegnere il relè. Con le funzioni request->authenticate() e request->requestAuthentication() della libreria puoi proteggere la pagina con nome utente e password; il browser apre la sua finestra di accesso e ricorda i dati per tutta la sessione. Il metodo predefinito è l'autenticazione Digest, quindi la password non viaggia in chiaro sulla rete. Proteggere solo la pagina però non basta: l'indirizzo WebSocket è una porta a parte e qualcuno potrebbe collegarsi direttamente a ws://ip/ws. L'esempio qui sotto chiude anche questa porta: a ogni avvio l'ESP32 genera una chiave casuale, la consegna solo al browser che ha effettuato l'accesso tramite /anahtar e con setFilter() rifiuta le connessioni WebSocket che non portano la chiave giusta.

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}
Questa protezione è una serratura sufficiente per la rete di casa, ma non è cifratura: ESPAsyncWebServer non offre HTTPS sull'ESP32 e i messaggi WebSocket viaggiano in chiaro. Poiché la password è scritta nel codice, ricordati di toglierla prima di condividerlo. Non aprire il pannello a internet con un port forwarding sul router; per il controllo da remoto, un bot Telegram con ESP32, MQTT e Home Assistant o la funzione VPN del router sono molto più sicuri. Nelle versioni più recenti della libreria puoi ottenere lo stesso risultato anche con la classe AsyncAuthenticationMiddleware.

Note per ESP8266 e Raspberry Pi Pico W

La stessa libreria funziona anche sull'ESP8266 (NodeMCU, Wemos D1 mini). Ecco cosa devi cambiare: installa la libreria ESP Async TCP di ESP32Async al posto di Async TCP; includi ESP8266WiFi.h al posto di WiFi.h, ESPAsyncTCP.h al posto di AsyncTCP.h ed ESP8266mDNS.h al posto di ESPmDNS.h, e aggiungi la riga MDNS.update() dentro loop(). Sostituisci WiFi.setSleep(false) con WiFi.setSleepMode(WIFI_NONE_SLEEP) e adatta i pin secondo la colonna NodeMCU della tabella dei collegamenti. L'ESP8266 ha solo circa 80 KB di RAM utilizzabile, quindi tieni la pagina leggera e ricorda che il limite di client è 4. Se usi un Raspberry Pi Pico W, trovi come creare un web server con MicroPython nella guida Raspberry Pi Pico W e Wi-Fi; le versioni recenti di ESPAsyncWebServer supportano anche i chip RP2040 e RP2350 lato Arduino.

Idee di progetto con il web server ESP32

  • Presa smart multicanale: con una scheda a 4 relè crea pulsanti separati per una lampada, una ventola e una presa; distingui i comandi con testi brevi come "r1" e "r2". Per scegliere il relè consulta l'articolo sul modulo relè.
  • Pannello di irrigazione: mostra in tempo reale il valore del sensore di umidità del terreno, regola la soglia di irrigazione con uno slider sulla pagina e avvia la pompa a mano quando vuoi.
  • Stazione meteo: usa un BME280 al posto del DHT22 per aggiungere la pressione e mostra le ultime 60 misure in un piccolo grafico a linee sulla pagina.
  • Lampada con selettore di colore: invia via WebSocket il valore di un selettore di colore sulla pagina e colora all'istante una striscia WS2812B NeoPixel.
  • Servo con slider: invia il valore dello slider a ogni movimento e fai ruotare un servomotore; ideale per orientare una telecamera o per un chiavistello.
  • Registro con data e ora: aggiungi a ogni misura l'ora ottenuta via NTP e aggiorna il codice in modalità wireless via OTA invece di passare ogni volta dalla USB.

Errori comuni e soluzioni

SintomoCausaSoluzione
'mbedtls_md5_starts_ret' was not declared in this scopeVecchia libreria me-no-dev con il core ESP32 3.xInstalla ESP Async WebServer e Async TCP di ESP32Async ed elimina le vecchie cartelle
Multiple libraries were found for "AsyncTCP.h"Ci sono due copie della stessa libreriaElimina le vecchie copie -master nella cartella libraries
Avviso deprecated per send_Psend_P sta per essere rimossa nella nuova versioneUsa request->send(200, "text/html", index_html)
La pagina non si apre affattoSmartphone sulla rete ospiti o in rete mobile, oppure l'IP è cambiatoCollegati alla stessa Wi-Fi e usa l'IP attuale del Monitor seriale
La pagina si apre ma i valori restano su --WebSocket non si collega: percorso diverso o addHandler dimenticatoApri la console con F12 e allinea ws("/ws") all'indirizzo nel JavaScript
Task watchdog got triggered con async_tcpdelay(), ciclo lungo o richiesta HTTP dentro un callbackTieni il callback breve e sposta il lavoro in loop() con un flag
Too many messages queuedtextAll() viene chiamata troppo spessoInvia solo quando cambia qualcosa o poche volte al secondo
La pagina LittleFS restituisce 404Cartella data non caricata o setDefaultFile mancanteCarica i file con il plugin e aggiungi setDefaultFile("index.html")
does not exist, no permits for creation nel Monitor serialeIl file richiesto non è in LittleFSControlla il nome del file e le maiuscole e minuscole; le righe con .gz sono innocue
Il relè non si spegne maiIl modulo a 5 V non riconosce 3,3 V come HIGHUsa un modulo compatibile con 3,3 V, JD-VCC o un transistor
L'ESP32 si riavvia quando scatta il relèLa corrente della bobina fa crollare l'alimentazione (brownout)Usa un alimentatore a 5 V affidabile e un condensatore da 470 µF
esp32.local non si apreDispositivo senza supporto mDNS o nome scritto con .localScrivi MDNS.begin("esp32") e, se serve, usa l'indirizzo IP
Il pulsante risponde in ritardoLa sospensione del modem Wi-Fi è attivaChiama WiFi.setSleep(false) dopo la connessione
Quando qualcosa non funziona, apri gli strumenti per sviluppatori del browser con F12. La scheda Console mostra errori come WebSocket connection to 'ws://192.168.1.50/ws' failed; nella scheda Rete, se fai clic sulla connessione WebSocket, la sezione Messaggi mostra ogni messaggio in entrata e in uscita. Sul lato ESP32, stampare nel Monitor seriale i client che si collegano e se ne vanno ti dice in pochi secondi se il problema sta nel browser o nella scheda. Se la scheda si riavvia appena scatta il relè, consulta la guida sul brownout dell'ESP32.

La coppia web server asincrono ESP32 e WebSocket è la strada più breve per un pannello di controllo veloce, che funziona senza servizi cloud né app da scrivere per lo smartphone e che più dispositivi possono condividere. Comincia con il pannello PROGMEM in un solo file, passa a LittleFS quando l'interfaccia cresce e dai a ogni nuovo dispositivo della tua rete il suo nome mDNS. Se hai appena conosciuto l'ESP32, trovi le basi in Che cos'è l'ESP32? Nell'app BlueGrays puoi anche seguire passo dopo passo, con schemi elettrici e codice pronto, progetti ESP32 come un web server asincrono, un contatore WebSocket, l'accesso tramite mDNS, un orologio NTP e l'aggiornamento OTA.

Domande frequenti

Qual è la differenza tra ESPAsyncWebServer e WebServer sull'ESP32?

La libreria WebServer gestisce le richieste una alla volta tramite la chiamata a handleClient() in loop(); se loop() rallenta, rallenta anche la pagina. ESPAsyncWebServer gestisce le richieste in un task separato in background, regge più connessioni insieme e integra già WebSocket e SSE. Per i pannelli in tempo reale la versione asincrona è la scelta migliore.

Quanti client possono collegarsi contemporaneamente a un web server ESP32?

Per impostazione predefinita ESPAsyncWebServer consente 8 client WebSocket sull'ESP32 e chiude quelli in più quando si chiama cleanupClients(); sull'ESP8266 il limite è 4. Poiché le normali richieste di pagina durano poco, più dispositivi possono aprire la pagina uno dopo l'altro. In pratica un pannello domestico funziona senza problemi con 3–5 dispositivi contemporanei.

Posso accedere al mio web server ESP32 da internet?

Tecnicamente è possibile con un port forwarding sul router, ma non è consigliato: la libreria non offre HTTPS sull'ESP32 e il dispositivo resta esposto agli attacchi da internet. Per il controllo da remoto, un bot Telegram, un sistema basato su MQTT o la funzione VPN del router sono molto più sicuri.

Perché esp32.local non funziona?

Il motivo più frequente è che lo smartphone o il computer non riesce a risolvere i nomi mDNS; succede soprattutto su alcuni telefoni Android e sulle vecchie versioni di Windows. Anche scrivere MDNS.begin("esp32.local") nel codice è un errore, perché il nome va indicato senza .local. Se il nome continua a non essere risolto, usa l'indirizzo IP del Monitor seriale o riserva un IP fisso all'ESP32 sul router.

Un web server ESP32 funziona senza router?

Sì. Con WiFi.softAP("ESP32-Panel", "12345678") l'ESP32 crea una propria rete Wi-Fi; collega lo smartphone a questa rete e apri 192.168.4.1 nel browser. In questa modalità access point non c'è internet, ma pannello e WebSocket funzionano allo stesso modo. La password della rete deve avere almeno 8 caratteri.

Quale libreria ESPAsyncWebServer devo installare?

Nel Gestore librerie dell'Arduino IDE installa ESP Async WebServer e Async TCP il cui autore è ESP32Async. La versione originale dell'account me-no-dev non viene più aggiornata e non si compila con il core ESP32 3.x. Elimina anche dalla cartella libraries le vecchie copie aggiunte da un file ZIP.

Vedi tutti i progetti
Server web asincrono ESP32 (con indicazione dell'IP sul display LCD)
#129Avanzato

Server web asincrono ESP32 (con indicazione dell'IP sul display LCD)

In questo progetto trasformiamo l'ESP32 in un server web asincrono che potrete controllare dal vostro telefono o computer collegato alla stessa rete Wi-Fi. Grazie alla libreria ESPAsyncWebServer, il server è in grado di elaborare più richieste contemporaneamente (a differenza del classico WebServer.h, che si blocca su una singola richiesta).

4.8(16)
26
Contatore WebSocket ESP32 (2 pulsanti + LCD + Web in tempo reale)
#130Avanzato
Premium

Contatore WebSocket ESP32 (2 pulsanti + LCD + Web in tempo reale)

In questo progetto due pulsanti collegati all'ESP32 (GPIO 5 incrementa, GPIO 4 decrementa) modificano un contatore. Il valore si aggiorna all'istante su un display LCD I2C 16x2 e, grazie al supporto WebSocket di ESPAsyncWebServer, in tutti i browser collegati senza ricaricare la pagina. Al posto del polling HTTP si usa un'unica connessione persistente e l'ESP32 invia il nuovo valore a tutti con ws.textAll().

4.5(14)
23
ESP32 mDNS: accesso al dispositivo tramite bluegrays-esp.local (con schermo LCD)
#131Avanzato
Premium

ESP32 mDNS: accesso al dispositivo tramite bluegrays-esp.local (con schermo LCD)

In questo progetto, aggiungendo il servizio mDNS (Multicast DNS) all'ESP32, consentiamo l'accesso tramite un nome host leggibile, come bluegrays-esp.local, anziché tramite l'indirizzo IP. Anche se l'IP assegnato dal DHCP dovesse cambiare, potrete sempre raggiungere il dispositivo con lo stesso nome. Eseguendo anche un piccolo server web, inviamo comandi di controllo dei LED dal browser all’indirizzo http://bluegrays-esp.local/. Sul display LCD I2C vengono visualizzati il nome host, l’IP e lo

4.8(14)
23
Connessione Wi-Fi STA con ESP32 (con display LCD)
#127Avanzato

Connessione Wi-Fi STA con ESP32 (con display LCD)

In questo progetto colleghiamo l'ESP32 alla rete Wi-Fi domestica in modalità Station (STA) in modo sicuro e tollerante agli errori. Separiamo SSID e password in un file secrets.h per impedire fughe su Git, impostiamo un meccanismo di riconnessione automatica e visualizziamo il livello del segnale RSSI e l'indirizzo IP su un display LCD I2C. Il LED interno funge da indicatore di stato.

4.4(21)
34
ESP32 DHT22 Temperatura e umidità (con display LCD)
#117Intermedio

ESP32 DHT22 Temperatura e umidità (con display LCD)

Si tratta di un progetto che misura la temperatura e l'umidità dell'ambiente tramite il sensore DHT22 e le visualizza in tempo reale su un display LCD I2C 16x2. I dati vengono letti dal sensore utilizzando il protocollo one-wire tramite la libreria Adafruit DHT. Questo progetto costituisce la base per stazioni meteorologiche IoT, sistemi di automazione domestica intelligente o sistemi di monitoraggio ambientale nelle serre. La comunicazione con il DHT22 avviene tramite il pin GPIO 4 dell'ESP32.

4.8(38)
56
Illuminazione Intelligente ESP32 (PIR + LDR + Relè + OLED)
#141Avanzato
Premium

Illuminazione Intelligente ESP32 (PIR + LDR + Relè + OLED)

Trasforma il tuo corridoio, bagno o dispensa in illuminazione intelligente con ESP32! Utilizzando un sensore di movimento PIR, un sensore di luce LDR e un modulo relè, costruiamo lo scenario classico "la luce si accende quando viene rilevato movimento al buio" — se l'ambiente è già luminoso, il movimento NON attiva la luce, risparmiando energia durante il giorno. La luce rimane accesa per 30 secondi; se il movimento continua, il timer si azzera e la luce rimane accesa più a lungo. Il display OLE

4.3(6)
11
Aggiornamento OTA dell'ESP32 — Installazione wireless del firmware (con display LCD)
#132Avanzato
Premium

Aggiornamento OTA dell'ESP32 — Installazione wireless del firmware (con display LCD)

In questo progetto imparerai a installare il nuovo firmware sull'ESP32 tramite Wi-Fi, senza bisogno di un cavo USB. Grazie alla libreria ArduinoOTA, l'ESP32 diventa visibile sulla rete e viene automaticamente elencato come bluegrays-ota nella scheda "Network port" dell'IDE di Arduino. Il primo caricamento avviene tramite USB, mentre tutti gli aggiornamenti successivi saranno wireless — una caratteristica fondamentale per i dispositivi sul campo. Sul display LCD I2C vengono visualizzati, sia

4.6(14)
22
ESP32: ora reale da Internet tramite NTP
#128Avanzato
Premium

ESP32: ora reale da Internet tramite NTP

L'ESP32 si connette al Wi-Fi e recupera l'ora reale con precisione al millisecondo dai server NTP (Network Time Protocol). Grazie a configTzTime(), il fuso orario della Turchia (TRT, UTC+3) viene applicato automaticamente e il passaggio all'ora legale è gestito tramite la stringa POSIX TZ. L'ora viene mostrata sul Serial Monitor e su un display LCD I2C opzionale. configTzTime() avvia il servizio SNTP; vengono eseguite risincronizzazioni periodiche finché è disponibile l'accesso alla rete, e

4.5(13)
21