Web Server con esp8266 e esp32: pagine gzipped da SPIFFS e byte array – 2

Spread the love

Scrivere codice html direttamente sullo sketch è fastidioso e non puoi creare pagine complesse senza avere problemi.

Per semplificare le persone convertono le pagine in un array di byte, questa è una soluzione migliore, e puoi usare la pagina gzipped per risparmiare spazio.

WebServer Esp8266 ESP32 byte array gzipped pages and SPIFFS
WebServer Esp8266 ESP32 byte array gzipped pages and SPIFFS

Naturalmente per creare un array gzippato devi usare un convertitore.

Stream di un file da SPIFFS

Ma inizia con un semplice esempio, una pagina index.html da gestire come root predefinita.

Per prima cosa ti consiglio di leggere l’articolo “WeMos D1 mini (esp8266): SPIFFS FileSystem integrato” per esp8266 e “ESP32: fileSystem integrato SPIFFS” per ESP32, qui ti verrà spiegato come usare il filesystem SPIFFS.

Quindi ecco una semplice pagina index.html.

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Demo index page</title>
</head>
<body style="background-color: azure">
    <h1 style="text-align: center">Here the demo page</h1>
    <h2 style="text-align: center">www.mischianti.org</h2>
    <div style="text-align: justify">

    </div>
    Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer nec odio. Praesent libero. Sed cursus ante dapibus diam. Sed nisi. Nulla quis sem at nibh elementum imperdiet. Duis sagittis ipsum. Praesent mauris. Fusce nec tellus sed augue semper porta. Mauris massa. Vestibulum lacinia arcu eget nulla. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Curabitur sodales ligula in libero. Sed dignissim lacinia nunc. Curabitur tortor. Pellentesque nibh. Aenean quam. In scelerisque sem at dolor. Maecenas mattis. Sed convallis tristique sem. Proin ut ligula vel nunc egestas porttitor. Morbi lectus risus, iaculis vel, suscipit quis, luctus non, massa. Fusce ac turpis quis ligula lacinia aliquet. Mauris ipsum. Nulla metus metus, ullamcorper vel, tincidunt sed, euismod in, nibh.
    </div>
</body>
</html>

Creeremo una cartella data su progetto, e creeremo un file index.html e la inseriremo all’interno (leggi l’articolo postato prima su come mettere il file in SPIFFS) .

Per l’esp32 è necessario modificare solo queste righe

#include <ESP8266WiFi.h>
#include <ESP8266WebServer.h>
#include <FS.h>
[...]
ESP8266WebServer server(80);

in

#include <WiFi.h>
#include <WebServer.h>
#include <SPIFFS.h>
[...]
WebServer server(80);

Ora possiamo istruire il dispositivo alla lettura di quella pagina.

/*
 *  WeMos D1 mini (esp8266)
 *  Simple web server an html page stored on SPIFFS
 *  stream on browser
 *  
 *  by Mischianti Renzo <https://mischianti.org>
 *
 *  https://mischianti.org/it/category/guide/come-creare-un-server-web-esp8266-esp32/
 *
 */
#include <ESP8266WiFi.h>
#include <ESP8266WebServer.h>
#include <FS.h>

const char* ssid = "<your-ssid>";
const char* password = "<your-passwd>";

ESP8266WebServer server(80);

void handleNotFound() {
  String message = "File Not Found\n\n";
  message += "URI: ";
  message += server.uri();
  message += "\nMethod: ";
  message += (server.method() == HTTP_GET) ? "GET" : "POST";
  message += "\nArguments: ";
  message += server.args();
  message += "\n";

  for (uint8_t i = 0; i < server.args(); i++) {
    message += " " + server.argName(i) + ": " + server.arg(i) + "\n";
  }

  server.send(404, "text/plain", message);
}

bool loadFromSPIFFS(String path) {
  String dataType = "text/html";

  Serial.print("Requested page -> ");
  Serial.println(path);
  if (SPIFFS.exists(path)){
	  File dataFile = SPIFFS.open(path, "r");
	  if (!dataFile) {
		  handleNotFound();
		  return false;
	  }

	  if (server.streamFile(dataFile, dataType) != dataFile.size()) {
	    Serial.println("Sent less data than expected!");
	  }else{
		  Serial.println("Page served!");
	  }

	  dataFile.close();
  }else{
	  handleNotFound();
	  return false;
  }
  return true;
}


void handleRoot() {
	loadFromSPIFFS("/index.html");
}

void setup(void) {
  Serial.begin(115200);
  WiFi.mode(WIFI_STA);
  WiFi.begin(ssid, password);
  Serial.println("");

  // Wait for connection
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }

  Serial.println("");
  Serial.print("Connected to ");
  Serial.println(ssid);
  Serial.print("IP address: ");
  Serial.println(WiFi.localIP());

  Serial.print(F("Inizializing FS..."));
  if (SPIFFS.begin()){
	Serial.println(F("done."));
  }else{
	Serial.println(F("fail."));
  }

  server.on("/", handleRoot);

  server.on("/inline", []() {
    server.send(200, "text/plain", "this works as well");
  });

  server.onNotFound(handleNotFound);
  server.begin();
  Serial.println("HTTP server started");
}

void loop(void) {
  server.handleClient();
}

Come puoi vedere il codice è molto semplice, e la maggior parte del lavoro è svolto dal comando server.streamFile che vuole per primo parametro il file e per secondo il tipo mime.

Il codice core è:

bool loadFromSPIFFS(String path) {
  String dataType = "text/html";

  Serial.print("Requested page -> ");
  Serial.println(path);
  if (SPIFFS.exists(path)){
	  File dataFile = SPIFFS.open(path, "r");
	  if (!dataFile) {
		  handleNotFound();
		  return false;
	  }

	  if (server.streamFile(dataFile, dataType) != dataFile.size()) {
	    Serial.println("Sent less data than expected!");
	  }else{
		  Serial.println("Page served!");
	  }

	  dataFile.close();
  }else{
	  handleNotFound();
	  return false;
  }
  return true;
}

Questa funzione estrae il file dallo SPIFFS e chiede procede allo streaming del contenuto con la funzione server.streamFile(dataFile, dataType), dove dataFile è il contenuto e dataType è il tipo MIME.

Here the demo page

www.mischianti.org

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer nec odio. Praesent libero. Sed cursus ante dapibus diam. Sed nisi. Nulla quis sem at nibh elementum imperdiet. Duis sagittis ipsum. Praesent mauris. Fusce nec tellus sed augue semper porta. Mauris massa. Vestibulum lacinia arcu eget nulla. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Curabitur sodales ligula in libero. Sed dignissim lacinia nunc. Curabitur tortor. Pellentesque nibh. Aenean quam. In scelerisque sem at dolor. Maecenas mattis. Sed convallis tristique sem. Proin ut ligula vel nunc egestas porttitor. Morbi lectus risus, iaculis vel, suscipit quis, luctus non, massa. Fusce ac turpis quis ligula lacinia aliquet. Mauris ipsum. Nulla metus metus, ullamcorper vel, tincidunt sed, euismod in, nibh.

AsyncWebServer

Ora lo sketch precedente ora una libreria molto popolare: AsyncWebServer, esiste una versione per esp8266 ed esp32, è necessario modificare solo la libreria di base.

Per l’ESP8266 va usata la libreria ESPAsyncTCP e per utilizzare questa libreria potrebbe essere necessario disporre delle ultime versioni git di  ESP8266  Arduino Core

Per l’ESP32 sarà richiesta per funzionare l’AsyncTCP. Per utilizzare questa libreria potrebbe essere necessario disporre delle ultime versioni git del core Arduino dell’ESP32.

Questo è un server completamente asincrono e come tale non viene eseguito sul thread del ciclo.

Ecco l’esempio precedente per esp32 con questa libreria.

/*
 *  ESP32
 *  Simple web server
 *  an html page stored on SPIFFS
 *  stream on browser
 *
 *  by Mischianti Renzo <https://mischianti.org>
 *
 *  https://mischianti.org/it/category/guide/come-creare-un-server-web-esp8266-esp32/
 *
 */
#include <WiFi.h>
#include <AsyncTCP.h>
#include <ESPAsyncWebServer.h>
#include <SPIFFS.h>

const char* ssid = "<your-ssid>";
const char* password = "<your-passwd>";

AsyncWebServer server(80);

void handleNotFound(AsyncWebServerRequest *request) {
  String message = "File Not Found\n\n";
  message += "URI: ";
  message += request->url();
  message += "\nMethod: ";
  message += (request->method() == HTTP_GET) ? "GET" : "POST";
  message += "\nArguments: ";
  message += request->args();
  message += "\n";

  for (uint8_t i = 0; i < request->args(); i++) {
    message += " " + request->argName(i) + ": " + request->arg(i) + "\n";
  }

  request->send(404, "text/plain", message);
}

bool loadFromSPIFFS(AsyncWebServerRequest *request, String path) {
  String dataType = "text/html";

  Serial.print("Requested page -> ");
  Serial.println(path);
  if (SPIFFS.exists(path)){
	  File dataFile = SPIFFS.open(path, "r");
	  if (!dataFile) {
		  handleNotFound(request);
		  return false;
	  }

	    AsyncWebServerResponse *response = request->beginResponse(SPIFFS, path, dataType);
	    Serial.print("Real file path: ");
	    Serial.println(path);

	    request->send(response);


	  dataFile.close();
  }else{
	  handleNotFound(request);
	  return false;
  }
  return true;
}


void handleRoot(AsyncWebServerRequest *request) {
	loadFromSPIFFS(request, "/index.html");
}

void setup(void) {
  Serial.begin(115200);
  WiFi.mode(WIFI_STA);
  WiFi.begin(ssid, password);
  Serial.println("");

  // Wait for connection
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }

  Serial.println("");
  Serial.print("Connected to ");
  Serial.println(ssid);
  Serial.print("IP address: ");
  Serial.println(WiFi.localIP());

  Serial.print(F("Inizializing FS..."));
  if (SPIFFS.begin()){
	Serial.println(F("done."));
  }else{
	Serial.println(F("fail."));
  }

  server.on("/", handleRoot);

  server.on("/inline", [](AsyncWebServerRequest *request) {
	  request->send(200, "text/plain", "this works as well");
  });

  server.onNotFound(handleNotFound);
  server.begin();
  Serial.println("HTTP server started");
}

void loop(void) {

}

Utilizzo di un convertitore da file ad array.

Se non vuoi usare il file system SPIFFS, puoi usare una semplice utility per creare un array da file, aggiungo un programmino usato per l’esempio del server web esp32-cam,

filetoarray "index.html" > web_index.h

ad un repository (aggiungo il file exe che nel repository originale non è presente) e con questo programma il file diventa.

// Filename web_index.h
// File stored is index.html, Size: 1187
#define index_html_len 1187
const uint8_t index_html[] PROGMEM = {
 0x3C, 0x21, 0x44, 0x4F, 0x43, 0x54, 0x59, 0x50, 0x45, 0x20, 0x68, 0x74, 0x6D, 0x6C, 0x3E, 0x0D,
 0x0A, 0x3C, 0x68, 0x74, 0x6D, 0x6C, 0x20, 0x6C, 0x61, 0x6E, 0x67, 0x3D, 0x22, 0x65, 0x6E, 0x22,
 0x3E, 0x0D, 0x0A, 0x3C, 0x68, 0x65, 0x61, 0x64, 0x3E, 0x0D, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x3C,
 0x6D, 0x65, 0x74, 0x61, 0x20, 0x63, 0x68, 0x61, 0x72, 0x73, 0x65, 0x74, 0x3D, 0x22, 0x55, 0x54,
 0x46, 0x2D, 0x38, 0x22, 0x3E, 0x0D, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x3C, 0x74, 0x69, 0x74, 0x6C,
 0x65, 0x3E, 0x44, 0x65, 0x6D, 0x6F, 0x20, 0x69, 0x6E, 0x64, 0x65, 0x78, 0x20, 0x70, 0x61, 0x67,
 0x65, 0x3C, 0x2F, 0x74, 0x69, 0x74, 0x6C, 0x65, 0x3E, 0x0D, 0x0A, 0x3C, 0x2F, 0x68, 0x65, 0x61,
 0x64, 0x3E, 0x0D, 0x0A, 0x3C, 0x62, 0x6F, 0x64, 0x79, 0x20, 0x73, 0x74, 0x79, 0x6C, 0x65, 0x3D,
 0x22, 0x62, 0x61, 0x63, 0x6B, 0x67, 0x72, 0x6F, 0x75, 0x6E, 0x64, 0x2D, 0x63, 0x6F, 0x6C, 0x6F,
 0x72, 0x3A, 0x20, 0x61, 0x7A, 0x75, 0x72, 0x65, 0x22, 0x3E, 0x0D, 0x0A, 0x20, 0x20, 0x20, 0x20,
 0x3C, 0x68, 0x31, 0x20, 0x73, 0x74, 0x79, 0x6C, 0x65, 0x3D, 0x22, 0x74, 0x65, 0x78, 0x74, 0x2D,
 0x61, 0x6C, 0x69, 0x67, 0x6E, 0x3A, 0x20, 0x63, 0x65, 0x6E, 0x74, 0x65, 0x72, 0x22, 0x3E, 0x48,
 0x65, 0x72, 0x65, 0x20, 0x74, 0x68, 0x65, 0x20, 0x64, 0x65, 0x6D, 0x6F, 0x20, 0x70, 0x61, 0x67,
 0x65, 0x3C, 0x2F, 0x68, 0x31, 0x3E, 0x0D, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x3C, 0x68, 0x32, 0x20,
 0x73, 0x74, 0x79, 0x6C, 0x65, 0x3D, 0x22, 0x74, 0x65, 0x78, 0x74, 0x2D, 0x61, 0x6C, 0x69, 0x67,
 0x6E, 0x3A, 0x20, 0x63, 0x65, 0x6E, 0x74, 0x65, 0x72, 0x22, 0x3E, 0x77, 0x77, 0x77, 0x2E, 0x6D,
 0x69, 0x73, 0x63, 0x68, 0x69, 0x61, 0x6E, 0x74, 0x69, 0x2E, 0x6F, 0x72, 0x67, 0x3C, 0x2F, 0x68,
 0x32, 0x3E, 0x0D, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x3C, 0x64, 0x69, 0x76, 0x20, 0x73, 0x74, 0x79,
 0x6C, 0x65, 0x3D, 0x22, 0x74, 0x65, 0x78, 0x74, 0x2D, 0x61, 0x6C, 0x69, 0x67, 0x6E, 0x3A, 0x20,
 0x6A, 0x75, 0x73, 0x74, 0x69, 0x66, 0x79, 0x22, 0x3E, 0x0D, 0x0A, 0x0D, 0x0A, 0x20, 0x20, 0x20,
 0x20, 0x3C, 0x2F, 0x64, 0x69, 0x76, 0x3E, 0x0D, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x4C, 0x6F, 0x72,
 0x65, 0x6D, 0x20, 0x69, 0x70, 0x73, 0x75, 0x6D, 0x20, 0x64, 0x6F, 0x6C, 0x6F, 0x72, 0x20, 0x73,
 0x69, 0x74, 0x20, 0x61, 0x6D, 0x65, 0x74, 0x2C, 0x20, 0x63, 0x6F, 0x6E, 0x73, 0x65, 0x63, 0x74,
 0x65, 0x74, 0x75, 0x72, 0x20, 0x61, 0x64, 0x69, 0x70, 0x69, 0x73, 0x63, 0x69, 0x6E, 0x67, 0x20,
 0x65, 0x6C, 0x69, 0x74, 0x2E, 0x20, 0x49, 0x6E, 0x74, 0x65, 0x67, 0x65, 0x72, 0x20, 0x6E, 0x65,
 0x63, 0x20, 0x6F, 0x64, 0x69, 0x6F, 0x2E, 0x20, 0x50, 0x72, 0x61, 0x65, 0x73, 0x65, 0x6E, 0x74,
 0x20, 0x6C, 0x69, 0x62, 0x65, 0x72, 0x6F, 0x2E, 0x20, 0x53, 0x65, 0x64, 0x20, 0x63, 0x75, 0x72,
 0x73, 0x75, 0x73, 0x20, 0x61, 0x6E, 0x74, 0x65, 0x20, 0x64, 0x61, 0x70, 0x69, 0x62, 0x75, 0x73,
 0x20, 0x64, 0x69, 0x61, 0x6D, 0x2E, 0x20, 0x53, 0x65, 0x64, 0x20, 0x6E, 0x69, 0x73, 0x69, 0x2E,
 0x20, 0x4E, 0x75, 0x6C, 0x6C, 0x61, 0x20, 0x71, 0x75, 0x69, 0x73, 0x20, 0x73, 0x65, 0x6D, 0x20,
 0x61, 0x74, 0x20, 0x6E, 0x69, 0x62, 0x68, 0x20, 0x65, 0x6C, 0x65, 0x6D, 0x65, 0x6E, 0x74, 0x75,
 0x6D, 0x20, 0x69, 0x6D, 0x70, 0x65, 0x72, 0x64, 0x69, 0x65, 0x74, 0x2E, 0x20, 0x44, 0x75, 0x69,
 0x73, 0x20, 0x73, 0x61, 0x67, 0x69, 0x74, 0x74, 0x69, 0x73, 0x20, 0x69, 0x70, 0x73, 0x75, 0x6D,
 0x2E, 0x20, 0x50, 0x72, 0x61, 0x65, 0x73, 0x65, 0x6E, 0x74, 0x20, 0x6D, 0x61, 0x75, 0x72, 0x69,
 0x73, 0x2E, 0x20, 0x46, 0x75, 0x73, 0x63, 0x65, 0x20, 0x6E, 0x65, 0x63, 0x20, 0x74, 0x65, 0x6C,
 0x6C, 0x75, 0x73, 0x20, 0x73, 0x65, 0x64, 0x20, 0x61, 0x75, 0x67, 0x75, 0x65, 0x20, 0x73, 0x65,
 0x6D, 0x70, 0x65, 0x72, 0x20, 0x70, 0x6F, 0x72, 0x74, 0x61, 0x2E, 0x20, 0x4D, 0x61, 0x75, 0x72,
 0x69, 0x73, 0x20, 0x6D, 0x61, 0x73, 0x73, 0x61, 0x2E, 0x20, 0x56, 0x65, 0x73, 0x74, 0x69, 0x62,
 0x75, 0x6C, 0x75, 0x6D, 0x20, 0x6C, 0x61, 0x63, 0x69, 0x6E, 0x69, 0x61, 0x20, 0x61, 0x72, 0x63,
 0x75, 0x20, 0x65, 0x67, 0x65, 0x74, 0x20, 0x6E, 0x75, 0x6C, 0x6C, 0x61, 0x2E, 0x20, 0x43, 0x6C,
 0x61, 0x73, 0x73, 0x20, 0x61, 0x70, 0x74, 0x65, 0x6E, 0x74, 0x20, 0x74, 0x61, 0x63, 0x69, 0x74,
 0x69, 0x20, 0x73, 0x6F, 0x63, 0x69, 0x6F, 0x73, 0x71, 0x75, 0x20, 0x61, 0x64, 0x20, 0x6C, 0x69,
 0x74, 0x6F, 0x72, 0x61, 0x20, 0x74, 0x6F, 0x72, 0x71, 0x75, 0x65, 0x6E, 0x74, 0x20, 0x70, 0x65,
 0x72, 0x20, 0x63, 0x6F, 0x6E, 0x75, 0x62, 0x69, 0x61, 0x20, 0x6E, 0x6F, 0x73, 0x74, 0x72, 0x61,
 0x2C, 0x20, 0x70, 0x65, 0x72, 0x20, 0x69, 0x6E, 0x63, 0x65, 0x70, 0x74, 0x6F, 0x73, 0x20, 0x68,
 0x69, 0x6D, 0x65, 0x6E, 0x61, 0x65, 0x6F, 0x73, 0x2E, 0x20, 0x43, 0x75, 0x72, 0x61, 0x62, 0x69,
 0x74, 0x75, 0x72, 0x20, 0x73, 0x6F, 0x64, 0x61, 0x6C, 0x65, 0x73, 0x20, 0x6C, 0x69, 0x67, 0x75,
 0x6C, 0x61, 0x20, 0x69, 0x6E, 0x20, 0x6C, 0x69, 0x62, 0x65, 0x72, 0x6F, 0x2E, 0x20, 0x53, 0x65,
 0x64, 0x20, 0x64, 0x69, 0x67, 0x6E, 0x69, 0x73, 0x73, 0x69, 0x6D, 0x20, 0x6C, 0x61, 0x63, 0x69,
 0x6E, 0x69, 0x61, 0x20, 0x6E, 0x75, 0x6E, 0x63, 0x2E, 0x20, 0x43, 0x75, 0x72, 0x61, 0x62, 0x69,
 0x74, 0x75, 0x72, 0x20, 0x74, 0x6F, 0x72, 0x74, 0x6F, 0x72, 0x2E, 0x20, 0x50, 0x65, 0x6C, 0x6C,
 0x65, 0x6E, 0x74, 0x65, 0x73, 0x71, 0x75, 0x65, 0x20, 0x6E, 0x69, 0x62, 0x68, 0x2E, 0x20, 0x41,
 0x65, 0x6E, 0x65, 0x61, 0x6E, 0x20, 0x71, 0x75, 0x61, 0x6D, 0x2E, 0x20, 0x49, 0x6E, 0x20, 0x73,
 0x63, 0x65, 0x6C, 0x65, 0x72, 0x69, 0x73, 0x71, 0x75, 0x65, 0x20, 0x73, 0x65, 0x6D, 0x20, 0x61,
 0x74, 0x20, 0x64, 0x6F, 0x6C, 0x6F, 0x72, 0x2E, 0x20, 0x4D, 0x61, 0x65, 0x63, 0x65, 0x6E, 0x61,
 0x73, 0x20, 0x6D, 0x61, 0x74, 0x74, 0x69, 0x73, 0x2E, 0x20, 0x53, 0x65, 0x64, 0x20, 0x63, 0x6F,
 0x6E, 0x76, 0x61, 0x6C, 0x6C, 0x69, 0x73, 0x20, 0x74, 0x72, 0x69, 0x73, 0x74, 0x69, 0x71, 0x75,
 0x65, 0x20, 0x73, 0x65, 0x6D, 0x2E, 0x20, 0x50, 0x72, 0x6F, 0x69, 0x6E, 0x20, 0x75, 0x74, 0x20,
 0x6C, 0x69, 0x67, 0x75, 0x6C, 0x61, 0x20, 0x76, 0x65, 0x6C, 0x20, 0x6E, 0x75, 0x6E, 0x63, 0x20,
 0x65, 0x67, 0x65, 0x73, 0x74, 0x61, 0x73, 0x20, 0x70, 0x6F, 0x72, 0x74, 0x74, 0x69, 0x74, 0x6F,
 0x72, 0x2E, 0x20, 0x4D, 0x6F, 0x72, 0x62, 0x69, 0x20, 0x6C, 0x65, 0x63, 0x74, 0x75, 0x73, 0x20,
 0x72, 0x69, 0x73, 0x75, 0x73, 0x2C, 0x20, 0x69, 0x61, 0x63, 0x75, 0x6C, 0x69, 0x73, 0x20, 0x76,
 0x65, 0x6C, 0x2C, 0x20, 0x73, 0x75, 0x73, 0x63, 0x69, 0x70, 0x69, 0x74, 0x20, 0x71, 0x75, 0x69,
 0x73, 0x2C, 0x20, 0x6C, 0x75, 0x63, 0x74, 0x75, 0x73, 0x20, 0x6E, 0x6F, 0x6E, 0x2C, 0x20, 0x6D,
 0x61, 0x73, 0x73, 0x61, 0x2E, 0x20, 0x46, 0x75, 0x73, 0x63, 0x65, 0x20, 0x61, 0x63, 0x20, 0x74,
 0x75, 0x72, 0x70, 0x69, 0x73, 0x20, 0x71, 0x75, 0x69, 0x73, 0x20, 0x6C, 0x69, 0x67, 0x75, 0x6C,
 0x61, 0x20, 0x6C, 0x61, 0x63, 0x69, 0x6E, 0x69, 0x61, 0x20, 0x61, 0x6C, 0x69, 0x71, 0x75, 0x65,
 0x74, 0x2E, 0x20, 0x4D, 0x61, 0x75, 0x72, 0x69, 0x73, 0x20, 0x69, 0x70, 0x73, 0x75, 0x6D, 0x2E,
 0x20, 0x4E, 0x75, 0x6C, 0x6C, 0x61, 0x20, 0x6D, 0x65, 0x74, 0x75, 0x73, 0x20, 0x6D, 0x65, 0x74,
 0x75, 0x73, 0x2C, 0x20, 0x75, 0x6C, 0x6C, 0x61, 0x6D, 0x63, 0x6F, 0x72, 0x70, 0x65, 0x72, 0x20,
 0x76, 0x65, 0x6C, 0x2C, 0x20, 0x74, 0x69, 0x6E, 0x63, 0x69, 0x64, 0x75, 0x6E, 0x74, 0x20, 0x73,
 0x65, 0x64, 0x2C, 0x20, 0x65, 0x75, 0x69, 0x73, 0x6D, 0x6F, 0x64, 0x20, 0x69, 0x6E, 0x2C, 0x20,
 0x6E, 0x69, 0x62, 0x68, 0x2E, 0x0D, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x3C, 0x2F, 0x64, 0x69, 0x76,
 0x3E, 0x0D, 0x0A, 0x3C, 0x2F, 0x62, 0x6F, 0x64, 0x79, 0x3E, 0x0D, 0x0A, 0x3C, 0x2F, 0x68, 0x74,
 0x6D, 0x6C, 0x3E
};

Ho scritto un semplice script html per semplificare il processo, fai l’upload e copia il risultato all’interno del file web_index.h.

Filearray converter

Select file



Generated filearray

Ed ora puoi eseguire lo streaming del file in questo modo

/*
 *  WeMos D1 mini (esp8266)
 *  Simple web server an html page in array format
 *  stream on browser
 *  
 *  by Mischianti Renzo <https://mischianti.org>
 *
 *  https://mischianti.org/
 *
 */
#include <ESP8266WiFi.h>
#include <ESP8266WebServer.h>
#include "web_index.h"

const char* ssid = "<your-ssid>";
const char* password = "<your-passwd>";

ESP8266WebServer server(80);

void handleNotFound() {
  String message = "File Not Found\n\n";
  message += "URI: ";
  message += server.uri();
  message += "\nMethod: ";
  message += (server.method() == HTTP_GET) ? "GET" : "POST";
  message += "\nArguments: ";
  message += server.args();
  message += "\n";

  for (uint8_t i = 0; i < server.args(); i++) {
    message += " " + server.argName(i) + ": " + server.arg(i) + "\n";
  }

  server.send(404, "text/plain", message);
}

void handleRoot() {
	String dataType = "text/html";

	Serial.println("Stream the array!");
	server.send(200, dataType, (const char*)index_html);
}

void setup(void) {
  Serial.begin(115200);
  WiFi.mode(WIFI_STA);
  WiFi.begin(ssid, password);
  Serial.println("");

  // Wait for connection
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }

  Serial.println("");
  Serial.print("Connected to ");
  Serial.println(ssid);
  Serial.print("IP address: ");
  Serial.println(WiFi.localIP());

  server.on("/", handleRoot);

  server.on("/inline", []() {
    server.send(200, "text/plain", "this works as well");
  });

  server.onNotFound(handleNotFound);
  server.begin();
  Serial.println("HTTP server started");
}

void loop(void) {
  server.handleClient();
}

Funziona, puoi pensare che non sia una soluzione così bella, ed è meglio scrivere una pagina html direttamente come una stringa, ma ora capiremo perché questa soluzione è migliore.

Pagina gzippata

Ma un file come una pagina html o una libreria JavaScript può essere molto grande, e nei moderni web server o application server è possibile abilitare la compressione gzip in maniera trasparente, ma l’esp8266 e l’esp32 non sono abbastanza potenti per eseguire la compressione in tempo reale.

Ma possiamo comprimere con gzip il file e specificare il tipo MIME corretto per il browser, ed otterrai prestazioni migliori e un throughput ridotto.

Per il test è possibile utilizzare un semplice compressore online per ottenere la pagina index.html.gz compressa.

Ora carichiamo il file sullo SPIFFS.

E devi cambiare solo la radice dell’handle

void handleRoot() {
	loadFromSPIFFS("/index.html.gz");
}

Il comportamento non cambia, ma invece di trasferire un file di 1,15Kb trasferisci solo 615byte circa la metà, ma questa differenza aumenta quando hai un file di dimensioni maggiori, nel mio Monito Web per l’inverter ABB Aurora (WIM) la dimensione del js da 2,15Mb si passa a 610kb, una grande differenza.

È possibile utilizzare questa soluzione compressa con gzip per tutti i tipi di file, è necessario impostare solo il tipo MIME corretto.

Gzipped per il byte array

Allo stesso modo puoi trasformare la pagina index in formato gzip in un array

filetoarray "index.html.gz" > web_index.h

Oppure puoi usare direttamente il mio convertitore da file a array di byte che ti dà la versione standard e la versione gzippata “Convertitore online: da File a gzip byte array (cpp)“.

sostituisci il contenuto del file web_index.h e allo sketch sopra cambia il nome dell’array

void handleRoot() {
	const char* dataType = "text/html";

	Serial.println("Stream the array!");

	server.sendHeader(F("Content-Encoding"), F("gzip"));
	server.send(200, dataType, (const char*)index_html_gz, index_html_gz_len);
}

per l’esp32 devi cambiare il metodo send in send_P.

void handleRoot() {
	const char* dataType = "text/html";

	Serial.println("Stream the array!");
	
	server.sendHeader(F("Content-Encoding"), F("gzip"));
	server.send_P(200, dataType, (const char*)index_html_gz, index_html_gz_len);
}

funziona alla grande e la dimensione del file è ridotta.

//File: index.html.gz, Size: 674
#define index_html_gz_len 674
const uint8_t index_html_gz[] PROGMEM = {
 0x1F, 0x8B, 0x08, 0x08, 0xEB, 0xAF, 0x30, 0x5E, 0x00, 0x03, 0x69, 0x6E, 0x64, 0x65, 0x78, 0x2E,
 0x68, 0x74, 0x6D, 0x6C, 0x00, 0x7D, 0x54, 0x5D, 0x6F, 0xD3, 0x40, 0x10, 0x7C, 0x47, 0xE2, 0x3F,
 0x2C, 0x79, 0x76, 0x1D, 0xD1, 0x27, 0x84, 0xD2, 0x4A, 0xA8, 0x05, 0x51, 0x89, 0x8F, 0x4A, 0x14,
 0x24, 0x1E, 0xD7, 0x77, 0x8B, 0xB3, 0x70, 0xBE, 0x4B, 0xEF, 0xF6, 0xFA, 0xC1, 0xAF, 0x67, 0xCE,
 0x4E, 0x4A, 0x2B, 0x21, 0xA2, 0xC8, 0x8E, 0xD7, 0xBB, 0xB3, 0xB3, 0x33, 0x9B, 0xDB, 0xBC, 0x38,
 0xFF, 0x7C, 0x76, 0xF5, 0xFD, 0xF2, 0x2D, 0x6D, 0x6D, 0x0A, 0xA7, 0xCF, 0x9F, 0x6D, 0xDA, 0x9D,
 0x02, 0xC7, 0xF1, 0x64, 0x25, 0x71, 0x35, 0x47, 0x84, 0x3D, 0xEE, 0x84, 0xCF, 0x66, 0x12, 0x63,
 0x72, 0x5B, 0xCE, 0x45, 0xEC, 0x64, 0xF5, 0xF5, 0xEA, 0xDD, 0xD1, 0xAB, 0xD5, 0xE1, 0x9D, 0xA9,
 0x05, 0x39, 0x3D, 0x97, 0x29, 0x91, 0x46, 0x2F, 0x77, 0xB4, 0xE3, 0x51, 0x36, 0xEB, 0x25, 0x0C,
 0x9C, 0xF5, 0x1E, 0x68, 0x33, 0x24, 0x7F, 0x4F, 0xC5, 0xEE, 0x83, 0x9C, 0xAC, 0x06, 0x76, 0xBF,
 0xC6, 0x9C, 0x6A, 0xF4, 0x47, 0x2E, 0x85, 0x94, 0x5F, 0x13, 0xFF, 0xAE, 0x59, 0x1E, 0x40, 0xB7,
 0x2F, 0x0F, 0x99, 0x26, 0x77, 0x76, 0xC4, 0x41, 0xC7, 0xF8, 0x9A, 0x9C, 0x44, 0x93, 0xBC, 0x3A,
 0x7D, 0x2F, 0x59, 0xC8, 0xB6, 0x42, 0xBE, 0x75, 0x5D, 0xFA, 0x6D, 0x5F, 0x3E, 0xD4, 0x1E, 0xFF,
 0xAF, 0xF6, 0xF6, 0xF6, 0xB6, 0x9F, 0xB4, 0xB8, 0xAD, 0x72, 0x34, 0xED, 0x53, 0x1E, 0x51, 0x7B,
 0x7C, 0xA8, 0xF5, 0x7A, 0xF3, 0xAF, 0xE2, 0x9F, 0xB5, 0x98, 0xFE, 0xB8, 0x6F, 0xF4, 0xF6, 0x89,
 0x6B, 0x64, 0xEE, 0x8B, 0x3E, 0xA4, 0x2C, 0x13, 0xE9, 0xAE, 0xD4, 0x89, 0x7C, 0x1B, 0x86, 0x8A,
 0x1A, 0x31, 0x34, 0xEB, 0xC8, 0xA5, 0x58, 0xC4, 0x99, 0x58, 0xCD, 0xC4, 0x5E, 0x77, 0x68, 0xAC,
 0x71, 0x24, 0x09, 0x6A, 0x3D, 0x5D, 0x80, 0xD1, 0x28, 0x99, 0xA2, 0x38, 0x4A, 0x5E, 0x53, 0x4F,
 0x97, 0x99, 0xA5, 0x80, 0x28, 0x05, 0x1D, 0x24, 0x23, 0xF0, 0x45, 0x3C, 0xB9, 0x9A, 0x4B, 0x2D,
 0x04, 0xBA, 0x18, 0x98, 0x77, 0x3A, 0xE0, 0xC1, 0x2B, 0x4F, 0xCB, 0xDB, 0xA8, 0x45, 0x7B, 0xFA,
 0x54, 0x43, 0x60, 0xBA, 0xAE, 0x5A, 0xA8, 0x80, 0x0C, 0x1B, 0xE2, 0xC3, 0x16, 0x7D, 0x64, 0x02,
 0x1C, 0x88, 0xE9, 0xB4, 0x93, 0xEC, 0x55, 0xD0, 0xF6, 0x7C, 0xCE, 0xE2, 0x51, 0xCD, 0xF0, 0x63,
 0xE6, 0xFD, 0xA8, 0xF3, 0xC4, 0x35, 0x6B, 0xE9, 0xE9, 0x5D, 0x2D, 0x4E, 0x66, 0x6A, 0x26, 0x21,
 0xD4, 0x86, 0xEB, 0x89, 0xEB, 0x58, 0xA5, 0x75, 0x00, 0x18, 0xED, 0x52, 0x36, 0xEE, 0xE9, 0xE3,
 0x5C, 0x80, 0xBA, 0x52, 0xF0, 0xF4, 0x4D, 0xA0, 0xD4, 0x50, 0x03, 0x5A, 0x06, 0xC6, 0xAC, 0xCA,
 0xC4, 0xD9, 0x55, 0xC2, 0xA0, 0xE0, 0xD4, 0x58, 0xF6, 0x74, 0x16, 0x90, 0x4B, 0xBC, 0xB3, 0xD6,
 0xCF, 0x90, 0x65, 0x4A, 0x25, 0x39, 0x4D, 0xE5, 0xBA, 0x42, 0x25, 0x0C, 0x6F, 0x29, 0x33, 0xE1,
 0x72, 0x5D, 0x5B, 0x4A, 0x6B, 0x06, 0x21, 0xEB, 0x00, 0xB0, 0x98, 0x8A, 0x65, 0xEE, 0xE6, 0x98,
 0x46, 0x27, 0x3B, 0x4B, 0x85, 0xB6, 0x8A, 0x29, 0x59, 0x12, 0x68, 0x9F, 0xD5, 0xCC, 0x83, 0x36,
 0xB9, 0x4B, 0xF2, 0x1C, 0xA4, 0x00, 0x6D, 0xAC, 0xD0, 0x46, 0xE3, 0x13, 0x51, 0x3D, 0x7C, 0xD5,
 0x52, 0xF4, 0x2F, 0xCD, 0x58, 0xA3, 0x7B, 0x5C, 0x8F, 0xF6, 0xF8, 0x42, 0x19, 0x8C, 0xDF, 0x76,
 0x07, 0xE4, 0x64, 0x56, 0xB5, 0xA7, 0x37, 0x12, 0x85, 0x23, 0xE4, 0x6E, 0x26, 0x5C, 0x44, 0x82,
 0x52, 0x41, 0x20, 0xC2, 0xF5, 0x22, 0x4E, 0x93, 0x7F, 0x5E, 0x84, 0x26, 0x8E, 0x60, 0xF1, 0xB8,
 0xC9, 0xD3, 0xD4, 0xDE, 0x1B, 0x9A, 0xE2, 0x0D, 0x87, 0x00, 0xD1, 0x0C, 0x45, 0xA6, 0xFB, 0xB2,
 0x66, 0x42, 0x02, 0xCD, 0x6A, 0x07, 0xCE, 0x37, 0x12, 0x66, 0x5A, 0x4D, 0xBD, 0x62, 0x40, 0x69,
 0x92, 0x9B, 0xCE, 0xB4, 0x3E, 0xA6, 0x3C, 0x28, 0x05, 0x2C, 0x17, 0xBC, 0x01, 0x4C, 0x2D, 0x1D,
 0x29, 0xBB, 0xDA, 0x60, 0x51, 0xD7, 0x11, 0x22, 0x0E, 0x0B, 0x67, 0xF3, 0x56, 0x74, 0x14, 0xEA,
 0x9C, 0x19, 0x53, 0xEC, 0x0E, 0x5E, 0x2D, 0x16, 0x33, 0x1C, 0xAE, 0x19, 0x9B, 0xB9, 0xAC, 0xCF,
 0xBE, 0xF5, 0x83, 0x79, 0xA1, 0xD1, 0xB3, 0x07, 0x9B, 0xF7, 0xEB, 0xB2, 0xEC, 0x1B, 0x56, 0x1C,
 0x90, 0xF3, 0xB5, 0xA3, 0x16, 0x99, 0x5C, 0xCA, 0xCD, 0x99, 0x99, 0x80, 0xC1, 0x1F, 0xF5, 0x15,
 0x06, 0x62, 0x75, 0x3A, 0x12, 0xA0, 0x4F, 0xC9, 0xC3, 0x88, 0x6E, 0x91, 0xF1, 0xE9, 0x3F, 0x69,
 0xB3, 0x6E, 0xE7, 0xC3, 0x72, 0x60, 0xB4, 0x33, 0xE9, 0x0F, 0xCC, 0x5A, 0xC7, 0xBB, 0xA3, 0x04,
 0x00, 0x00
};

Come puoi capire la soluzione array può essere utilizzata in molte casistiche, io l’ho scoperta ed utilizzata per modificare l’esempio “WebCameraServer” per aggiungere la funzionalità di flash sull’esp32-cam.

AsyncWebServer

Qui l’implementazione tramite AsyncWebServer della compressione gzip.

#include <WiFi.h>
#include <AsyncTCP.h>
#include <ESPAsyncWebServer.h>
#include "web_index.h"

const char* ssid = "<your-ssid>";
const char* password = "<your-passwd>";

AsyncWebServer server(80);

void handleNotFound(AsyncWebServerRequest *request) {
  String message = "File Not Found\n\n";
  message += "URI: ";
  message += request->url();
  message += "\nMethod: ";
  message += (request->method() == HTTP_GET) ? "GET" : "POST";
  message += "\nArguments: ";
  message += request->args();
  message += "\n";

  for (uint8_t i = 0; i < request->args(); i++) {
    message += " " + request->argName(i) + ": " + request->arg(i) + "\n";
  }

  request->send(404, "text/plain", message);
}

void handleRoot(AsyncWebServerRequest *request) {
	const char* dataType = "text/html";

	Serial.println("Stream the array!");

	AsyncWebServerResponse *response = request->beginResponse_P(200, dataType,index_html_gz, index_html_gz_len);
	response->addHeader("Content-Encoding", "gzip");
	request->send(response);
}

void setup(void) {
  Serial.begin(115200);
  WiFi.mode(WIFI_STA);
  WiFi.begin(ssid, password);
  Serial.println("");

  // Wait for connection
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }

  Serial.println("");
  Serial.print("Connected to ");
  Serial.println(ssid);
  Serial.print("IP address: ");
  Serial.println(WiFi.localIP());

  server.on("/", handleRoot);

  server.onNotFound(handleNotFound);
  server.begin();
  Serial.println("HTTP server started");
}

void loop(void) {

}

Immagine all’interno della pagina

All’interno di una pagina puoi avere delle immagini, ma anche le immagini sono semplici URL servito con il tipo MIME specifico, guarda gli esempi

Qui la pagina index.html da inserire nella cartella data.

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Demo index page</title>
</head>
<body style="background-color: azure;text-align: center">
    <h1 style="text-align: center">Here the demo page</h1>
    <img style="text-align: center" src="logo256.jpg" width="128"/>
    <h2 style="text-align: center">www.mischianti.org</h2>
    <div style="text-align: justify">

    </div>
    Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer nec odio. Praesent libero. Sed cursus ante dapibus diam. Sed nisi. Nulla quis sem at nibh elementum imperdiet. Duis sagittis ipsum. Praesent mauris. Fusce nec tellus sed augue semper porta. Mauris massa. Vestibulum lacinia arcu eget nulla. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Curabitur sodales ligula in libero. Sed dignissim lacinia nunc. Curabitur tortor. Pellentesque nibh. Aenean quam. In scelerisque sem at dolor. Maecenas mattis. Sed convallis tristique sem. Proin ut ligula vel nunc egestas porttitor. Morbi lectus risus, iaculis vel, suscipit quis, luctus non, massa. Fusce ac turpis quis ligula lacinia aliquet. Mauris ipsum. Nulla metus metus, ullamcorper vel, tincidunt sed, euismod in, nibh.
    </div>
</body>
</html>

E qui l’immagine da mettere nella stessa cartella data 

Ed ecco lo sketch

/*
 *  WeMos D1 mini (esp8266)
 *  Simple web server that read from SPIFFS and
 *  stream on browser (page and image)
 *  
 *  by Mischianti Renzo <https://mischianti.org>
 *
 *  https://mischianti.org/
 *
 */
#include <ESP8266WiFi.h>
#include <ESP8266WebServer.h>
#include <FS.h>

const char* ssid = "<your-ssid>";
const char* password = "<your-passwd>";

ESP8266WebServer server(80);

void handleNotFound() {
  String message = "File Not Found\n\n";
  message += "URI: ";
  message += server.uri();
  message += "\nMethod: ";
  message += (server.method() == HTTP_GET) ? "GET" : "POST";
  message += "\nArguments: ";
  message += server.args();
  message += "\n";

  for (uint8_t i = 0; i < server.args(); i++) {
    message += " " + server.argName(i) + ": " + server.arg(i) + "\n";
  }

  server.send(404, "text/plain", message);
}

bool loadFromSPIFFS(String path, String dataType) {
  Serial.print("Requested page -> ");
  Serial.println(path);
  if (SPIFFS.exists(path)){
	  File dataFile = SPIFFS.open(path, "r");
	  if (!dataFile) {
		  handleNotFound();
		  return false;
	  }

	  if (server.streamFile(dataFile, dataType) != dataFile.size()) {
	    Serial.println("Sent less data than expected!");
	  }else{
		  Serial.println("Page served!");
	  }

	  dataFile.close();
  }else{
	  handleNotFound();
	  return false;
  }
  return true;
}


void handleRoot() {
	loadFromSPIFFS("/index.html", "text/html");
}
void handleLogo() {
	loadFromSPIFFS("/logo256.jpg", "image/jpeg");
}

void setup(void) {
  Serial.begin(115200);
  WiFi.mode(WIFI_STA);
  WiFi.begin(ssid, password);
  Serial.println("");

  // Wait for connection
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }

  Serial.println("");
  Serial.print("Connected to ");
  Serial.println(ssid);
  Serial.print("IP address: ");
  Serial.println(WiFi.localIP());

  Serial.print(F("Inizializing FS..."));
  if (SPIFFS.begin()){
	Serial.println(F("done."));
  }else{
	Serial.println(F("fail."));
  }

  server.on("/", handleRoot);
  server.on("/logo256.jpg", handleLogo);

  server.on("/inline", []() {
    server.send(200, "text/plain", "this works as well");
  });

  server.onNotFound(handleNotFound);
  server.begin();
  Serial.println("HTTP server started");
}

void loop(void) {
  server.handleClient();
}

Hai inserito il link all’immagine nella pagina html tramite questo tag

<img style="text-align: center" src="logo256.jpg" width="128"/>

questo significa che quando la pagina viene renderizzata dal browser cerca di scaricare l’immagine con un url relativa, perciò l’url assoluta sarà http://192.168.1.146/logo256.jpg. Dovrai istruire il microcontrollore su come per gestire questa richiesta.

server.on("/logo256.jpg", handleLogo);
[...]
void handleLogo() {
	loadFromSPIFFS("/logo256.jpg", "image/jpeg");
}

Ecco il risultato

Here the demo page

www.mischianti.org

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer nec odio. Praesent libero. Sed cursus ante dapibus diam. Sed nisi. Nulla quis sem at nibh elementum imperdiet. Duis sagittis ipsum. Praesent mauris. Fusce nec tellus sed augue semper porta. Mauris massa. Vestibulum lacinia arcu eget nulla. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Curabitur sodales ligula in libero. Sed dignissim lacinia nunc. Curabitur tortor. Pellentesque nibh. Aenean quam. In scelerisque sem at dolor. Maecenas mattis. Sed convallis tristique sem. Proin ut ligula vel nunc egestas porttitor. Morbi lectus risus, iaculis vel, suscipit quis, luctus non, massa. Fusce ac turpis quis ligula lacinia aliquet. Mauris ipsum. Nulla metus metus, ullamcorper vel, tincidunt sed, euismod in, nibh.

Thanks

Questo è divertente ma come puoi vedere non hai interazioni con la pagina, è solo un contenuto statico è pressoché inutile. Puoi iniziare a vedere come dare dinamicità con servizi RESTFull sbirciando questo articolo “Come creare un server REST su esp8266 o esp32“.

Comunque nei prossimi articoli creeremo un Web Server completo di Back-end.

  1. Web Server su esp8266 e esp32: servire pagine e gestire LEDs
  2. Web Server su esp8266 e esp32: servire pagine compresse come byte array e SPIFFS
  3. Web Server su esp8266 e esp32: web server generico multiuso
  4. Web Server su esp8266 e esp32: gestione sicurezza ed autenticazione
  5. Web Server su esp8266 e esp32: aggiunta di un back-end REST protetto
  6. Web Server su esp8266 e esp32: Interfaccia Web sicura per temperatura ed umidità di un DHT

Codice ed esempio su repository GitHub


Spread the love

2 Risposte

  1. gabriele ha detto:

    Salve, ottimo tutorial! l’ho seguito programmare un web server in un wemos D1 mini. La mia domanda è se è possibile aggiornare sia firmware che file su LittleFS con OTA update. Grazie

Lascia un commento

Il tuo indirizzo email non sarà pubblicato. I campi obbligatori sono contrassegnati *