ESP 8266 - WebServer
Table of Contents
Premessa
Un server web per ESP32 è un programma che consente al microcontrollore ESP32 di rispondere alle richieste HTTP provenienti da client, come browser web o applicazioni. Il server web ESP32 può essere utilizzato per servire pagine web, dati o interfaccia utente da visualizzare in un browser o da utilizzare in applicazioni IoT. Ecco come funziona in generale:
- Configurazione iniziale: Per creare un server web su ESP32, devi includere la libreria WiFi e la libreria ESPAsyncWebServer nel tuo progetto Arduino o ESP-IDF. Inoltre, è necessario connettersi a una rete Wi-Fi o creare un hotspot Wi-Fi con il tuo ESP32 in modo che i client possano comunicare con il server.
- Creazione del server: Devi istanziare un oggetto `AsyncWebServer` (se stai usando ESPAsyncWebServer) o `WebServer` (se stai usando la libreria ESP8266WebServer) e specificare la porta su cui il server ascolterà le richieste HTTP.
- Gestione delle richieste: Devi definire delle funzioni di gestione (route) per le diverse richieste HTTP. Queste funzioni vengono eseguite quando il server riceve richieste per specifici percorsi URL. Ad esempio, puoi definire una funzione per gestire una richiesta GET a "/pagina" e un'altra funzione per gestire una richiesta POST a "/dati".
- Avvio del server: Devi avviare il server chiamando il metodo `begin()` o `start()`. In questo momento, il server è pronto per accettare richieste HTTP dai client.
- Ascolto delle richieste: Il server rimane in ascolto di richieste HTTP in arrivo dai client, come browser web o applicazioni, sulla porta specificata. Quando riceve una richiesta, cerca una corrispondenza con una delle funzioni di gestione delle route definite in precedenza.
- Risposta alle richieste: Quando il server riceve una richiesta corrispondente, esegue la funzione di gestione associata e può generare una risposta HTTP da inviare al client. La risposta può includere HTML, JSON, immagini o qualsiasi altra informazione richiesta.
- Invio della risposta: Una volta generata la risposta, il server la invia al client attraverso la connessione HTTP. Il client riceve e interpreta la risposta, che può includere l'HTML per visualizzare una pagina web, dati JSON per un'applicazione, o qualsiasi altra informazione richiesta.
- Chiusura della connessione: Dopo l'invio della risposta, il server chiude la connessione con il client, a meno che la connessione debba essere mantenuta aperta per future interazioni (ad esempio, per streaming di dati).
- Ciclo continuo: Il server continua a rimanere in ascolto di nuove richieste HTTP e a rispondere fino a quando non viene interrotto o arrestato.
Questi sono i passaggi generali di base per creare un server web su ESP32. Tuttavia, l'implementazione esatta può variare in base alla libreria specifica che stai utilizzando (ad esempio, ESPAsyncWebServer o ESP8266WebServer) e ai dettagli del tuo progetto, come le route e le risposte specifiche che desideri gestire.
Differenze tra ESPAsyncWebServer e WebServer
La differenza principale tra AsyncWebServer (se stai usando ESPAsyncWebServer) e WebServer (se stai usando la libreria ESP8266WebServer) sta nell'approccio alla gestione delle richieste web. Questi due server web sono progettati per lavorare su due piattaforme diverse:
ESPAsyncWebServer:
È una libreria progettata per l'ESP8266 e l'ESP32. È basata su un approccio asincrono, il che significa che è in grado di gestire molteplici richieste web simultaneamente senza bloccare il microcontrollore. Utilizza la libreria AsyncTCP per gestire le connessioni TCP in modo asincrono. È adatto per applicazioni che richiedono alta reattività, come il controllo di dispositivi IoT con un elevato volume di richieste web. È più recente rispetto a ESP8266WebServer ed è particolarmente adatto per ESP32.
ESP8266WebServer:
È una libreria progettata principalmente per l'ESP8266, ma è stata utilizzata su ESP32 con alcune modifiche. Utilizza un approccio sincrono, il che significa che gestisce le richieste una alla volta, attendendo che ogni richiesta venga elaborata prima di passare alla successiva. È adatto per applicazioni meno complesse in cui non è necessario gestire molteplici richieste contemporaneamente e la reattività non è un problema critico. È una libreria più consolidata ed è stata utilizzata su ESP8266 per molto tempo. In breve, se stai lavorando con ESP32 e desideri un server web più reattivo e in grado di gestire più richieste contemporaneamente, è consigliabile utilizzare AsyncWebServer (ESPAsyncWebServer). Se stai utilizzando ESP8266 o desideri una soluzione più semplice e meno impegnativa, puoi optare per WebServer (ESP8266WebServer). Tuttavia, entrambe le librerie sono potenti e possono essere utilizzate per una varietà di applicazioni basate su microcontrollori ESP. La scelta dipende dalle tue esigenze specifiche e dalla piattaforma su cui stai lavorando.
ESPAsyncWebServer
Ecco alcuni dei metodi principali per dispositivi ESP8266 e ESP32:
- on(const String &uri, WebRequestMethodComposite method, handlerFunction onRequest): Questo metodo consente di definire un gestore di route per un percorso URI specifico e per un metodo HTTP specifico (ad esempio, GET o POST). handlerFunction è una funzione o una lambda function che verrà chiamata quando viene effettuata una richiesta corrispondente.
- serveStatic(const String &uri, fs::FS &fs, const String &path, const String &cacheheader): Questo metodo consente di servire file statici (come HTML, immagini, CSS, JavaScript, ecc.) da un percorso specificato. È utile per gestire risorse web statiche.
- onNotFound(handlerFunction onRequest): Questo metodo consente di definire un gestore di route speciale che viene chiamato quando nessuna corrispondenza per il percorso richiesto viene trovata. Può essere utilizzato per gestire errori 404 personalizzati.
- begin(): Questo metodo avvia il server web e lo rende pronto per gestire le richieste in arrivo.
- close(): Questo metodo chiude il server web, interrompendo la gestione delle richieste in arrivo.
- onRequestBody(bodyHandlerFunction onRequest): Questo metodo consente di definire un gestore per il corpo della richiesta HTTP in arrivo quando si ricevono dati POST o PUT dal client.
- onFileUpload(uploadHandlerFunction onUpload): Questo metodo consente di definire un gestore per l'upload di file da parte del client.
- onWebSocket(webSocketHandlerFunction onWebSocket): Questo metodo consente di definire un gestore per le connessioni WebSocket in arrivo.
- onEvent(eventHandlerFunction onEvent): Questo metodo consente di definire un gestore per gli eventi generici del server web.
on(const String &uri, WebRequestMethodComposite method, handlerFunction onRequest)
Il metodo è uno dei metodi chiave della libreria ESPAsyncWebServer utilizzato per definire un gestore di route per un percorso URI specifico e per un metodo HTTP specifico e deve essere usato prima di begin()
. Il primo esempio e' per le pagine mentre il secondo per le risorse:
Ecco i dettagli su ciascuno dei parametri di questo metodo:
- uri (const String &): Questo è il percorso URI che il gestore di route deve corrispondere. Ad esempio, se imposti uri su "/pagina", il gestore di route verrà attivato quando viene effettuata una richiesta a "http://indirizzo-del-server/pagina".
- method (WebRequestMethodComposite): Questo parametro specifica il metodo HTTP al quale il gestore di route risponderà. Puoi utilizzare le costanti definite nella libreria per specificare il metodo desiderato. Le modalità HTTP (Hypertext Transfer Protocol) sono i diversi metodi o verbi utilizzati nelle richieste HTTP per comunicare con un server web. Ogni modalità rappresenta un'azione specifica che deve essere eseguita dal server su una risorsa identificata. Ecco alcune delle modalità HTTP più comuni:
- GET: La modalità GET è utilizzata per richiedere dati da una risorsa specificata. Le richieste GET sono spesso utilizzate per recuperare pagine web e dati, e sono generalmente considerate "sicure" in quanto non modificano lo stato del server.
- POST: La modalità POST è utilizzata per inviare dati al server per elaborazione. Le richieste POST sono spesso utilizzate per inviare dati di un modulo HTML o per eseguire operazioni che modificano lo stato del server.
- PUT: La modalità PUT è utilizzata per caricare una risorsa specificata sul server o per aggiornare una risorsa esistente con i dati forniti.
- DELETE: La modalità DELETE è utilizzata per rimuovere una risorsa specificata dal server.
- HEAD: La modalità HEAD è simile a GET, ma richiede solo l'invio delle intestazioni della risposta HTTP, senza il corpo della risposta. È spesso utilizzata per ottenere informazioni sull'oggetto senza scaricarne il contenuto completo.
- OPTIONS: La modalità OPTIONS è utilizzata per richiedere informazioni sulle opzioni di comunicazione disponibili per una risorsa specificata. È spesso utilizzata per scoprire quali metodi HTTP sono supportati da una risorsa.
- PATCH: La modalità PATCH è utilizzata per applicare modifiche parziali a una risorsa, consentendo agli utenti di inviare solo le parti dei dati che desiderano aggiornare.
- CONNECT: La modalità CONNECT è utilizzata per stabilire una connessione di rete con una risorsa, di solito attraverso un proxy. È spesso utilizzata per scopi di tunneling.
- TRACE: La modalità TRACE è utilizzata per ottenere una diagnostica di una richiesta. In genere, il server risponderà con una copia della richiesta ricevuta, consentendo di visualizzare come viene elaborata.
- COPY: Anche se meno comune, la modalità COPY è utilizzata per copiare una risorsa da una posizione a un'altra.
Queste sono alcune delle modalità HTTP più utilizzate. Ciascuna modalità ha un uso specifico e il suo significato è definito nello standard HTTP. Quando si effettuano richieste HTTP, è importante utilizzare la modalità corretta in base all'azione che si desidera eseguire sul server.
onRequest (handlerFunction): Questo parametro è una funzione o una lambda function che verrà chiamata quando viene effettuata una richiesta HTTP corrispondente al percorso URI e al metodo specificati. La funzione deve avere il seguente formato:
void onRequest(AsyncWebServerRequest *request) { // Codice per gestire la richiesta HTTP }
- AsyncWebServerRequest *request`: Questo è un puntatore all'oggetto *AsyncWebServerRequest che rappresenta la richiesta HTTP in arrivo. Puoi utilizzare questo oggetto per accedere a informazioni sulla richiesta, come il percorso URI, i parametri, gli header e il corpo della richiesta.
Esempi
// Questo è un gestore di route per richieste GET a "/pagina" // la lambda function verrà chiamata e il server invierà una risposta HTML server.on("/pagina", HTTP_GET, [](AsyncWebServerRequest *request){ // Puoi inserire il codice per gestire la richiesta qui request->send(200, "text/plain", "Benvenuto nella pagina!"); });
// Configurazione del server // Questo è un gestore di route per richieste GET al file index.html //la lambda function verrà chiamata e il server invierà una risposta HTML server.on("/", HTTP_GET, [](AsyncWebServerRequest *request){ // Gestore per la radice del sito // Invia il file "index.html" come risposta request->send(SPIFFS, "/index.html", "text/html"); });
// da qualche parte nel file void handle_OnConnect() { LED1status = LOW; LED2status = LOW; Serial.println("GPIO4 Status: OFF | GPIO5 Status: OFF"); server.send(200, "text/html", SendHTML(LED1status,LED2status)); } void handle_led1on() { LED1status = HIGH; Serial.println("GPIO4 Status: ON"); server.send(200, "text/html", SendHTML(true,LED2status)); } void handle_led1off() { LED1status = LOW; Serial.println("GPIO4 Status: OFF"); server.send(200, "text/html", SendHTML(false,LED2status)); } void handle_led2on() { LED2status = HIGH; Serial.println("GPIO5 Status: ON"); server.send(200, "text/html", SendHTML(LED1status,true)); } void handle_led2off() { LED2status = LOW; Serial.println("GPIO5 Status: OFF"); server.send(200, "text/html", SendHTML(LED1status,false)); } void handle_NotFound(){ server.send(404, "text/plain", "Not found"); } // Configurazione del server server.on("/", handle_OnConnect); server.on("/led1on", handle_led1on); server.on("/led1off", handle_led1off); server.on("/led2on", handle_led2on); server.on("/led2off", handle_led2off); server.onNotFound(handle_NotFound);
server.on("/immagine1.jpg", HTTP_GET, [](AsyncWebServerRequest *request){ // Serve "immagine1.jpg" quando si richiede "/immagine1.jpg" request->send(SPIFFS, "/immagine1.jpg", "image/jpeg"); }); server.on("/immagine2.jpg", HTTP_GET, [](AsyncWebServerRequest *request){ // Serve "immagine2.jpg" quando si richiede "/immagine2.jpg" request->send(SPIFFS, "/immagine2.jpg", "image/jpeg"); }); server.on("/immagine3.jpg", HTTP_GET, [](AsyncWebServerRequest *request){ // Serve "immagine3.jpg" quando si richiede "/immagine3.jpg" request->send(SPIFFS, "/immagine3.jpg", "image/jpeg"); });
La sintassi [](AsyncWebServerRequest *request) rappresenta una lambda function in C++. Una lambda function è una funzione anonima che può essere definita direttamente nel punto in cui viene utilizzata, senza la necessità di dichiarare una funzione separata. Questo è particolarmente utile quando si passano funzioni come argomenti a altre funzioni o quando si definiscono gestori di eventi o callback.
Nel contesto della libreria AsyncWebServer per Arduino, [](AsyncWebServerRequest *request) rappresenta una lambda function utilizzata come gestore di route. Questa lambda function accetta un parametro di tipo AsyncWebServerRequest *request, che rappresenta la richiesta HTTP in arrivo.
Ecco cosa fa questa lambda function:
- AsyncWebServerRequest *request: Questo è il parametro della funzione che contiene tutte le informazioni sulla richiesta HTTP in arrivo, come il percorso URL, il metodo HTTP (GET, POST, ecc.), gli header e altro ancora.
Dentro la lambda function, puoi definire il comportamento che il server web dovrebbe avere quando viene effettuata una richiesta corrispondente al percorso e al metodo specificati. Ad esempio, puoi leggere i dati dalla richiesta, generare una risposta personalizzata e inviarla al client.
onNotFound(handlerFunction onRequest)
Ecco un esempio di come utilizzare server.onNotFound per gestire richieste HTTP per percorsi non trovati utilizzando la libreria ESPAsyncWebServer:
#include <WiFi.h> #include <ESPAsyncWebServer.h> const char *ssid = "IlTuoSSID"; // Cambia con il tuo SSID WiFi const char *password = "LaTuaPassword"; // Cambia con la tua password WiFi const int port = 80; AsyncWebServer server(port); void setup() { Serial.begin(115200); // Connessione WiFi WiFi.begin(ssid, password); while (WiFi.status() != WL_CONNECTED) { delay(1000); Serial.println("Connessione WiFi in corso..."); } Serial.println("Connesso a WiFi"); // Configurazione del server server.on("/", HTTP_GET, [](AsyncWebServerRequest *request){ // Gestore per la radice del sito String html = "<html><body><h1>Benvenuto nella pagina principale!</h1></body></html>"; request->send(200, "text/html", html); }); // Gestione delle richieste per percorsi non trovati server.onNotFound([](AsyncWebServerRequest *request){ // Questo gestore verrà chiamato quando viene effettuata una richiesta per un percorso non esistente String html = "<html><body><h1>Errore 404: Pagina non trovata</h1></body></html>"; request->send(404, "text/html", html); }); server.begin(); } void loop() { // Il loop è vuoto in questo esempio }
In questo esempio:
- Stabilisce una connessione WiFi utilizzando le credenziali SSID e password specificate.
- Configura il server web sulla porta 80 utilizzando `AsyncWebServer`.
- Utilizza `server.on("", HTTPGET, …)` per definire un gestore di route per il percorso di base ("") e il metodo HTTP GET. Quando una richiesta GET viene effettuata alla radice del server, il gestore verrà chiamato.
- Utilizza `server.onNotFound(…)` per definire un gestore per le richieste HTTP per percorsi non trovati. Questo gestore verrà chiamato quando il percorso richiesto non corrisponde a nessuna delle route definite. Nel nostro caso, stiamo restituendo una risposta con codice di stato 404 (Pagina non trovata) e un messaggio di errore.
Ora, quando accedi a un percorso non esistente sull'indirizzo IP del tuo dispositivo ESP32, vedrai il messaggio "Errore 404: Pagina non trovata" visualizzato nel browser. Assicurati di sostituire "IlTuoSSID" e "LaTuaPassword" con le tue credenziali WiFi.