como-servir-una-pagina-web-con-el-esp8266-desde-spiffs

Cómo servir una página web con el ESP8266 o ESP32 desde SPIFFS

En esta entrada vamos a ver cómo configurar un servidor Web que sirva contenido estático desde el sistema de ficheros del ESP8266 o el ESP32 desde el sistema de archivos SPIFFS.

Haremos referencia al ESP8266, pero el mismo código es compatible para el ESP32, ajustando el nombre de las librerías. Al final tenéis el código tanto para el ESP8266 como para el ESP32.

Recordar que llevamos varias entradas dedicadas a hacer un servidor con el ESP8266. Por su parte, en la entrada anterior presentamos el sistema de ficheros SPIFFS.

Por fin nos toca juntar ambas cosas para, ahora sí, empezar a jugar de verdad con este Soc. Y es que, aunque aún nos queda mucho por delante, vamos a empezar a sacar el potenciar y disfrutar del ESP8266.

Desde el SPIFFS podemos servir únicamente contenido estático. Es decir, contenido que no varía entre peticiones. Pero eso no significa, ni mucho menos, que sea un simple fichero de texto. De hecho, muchas web modernas son así.

Lo que vamos a hacer va a ser servir ficheros (html, css, js) de forma similar a como hacemos en un servidor “convencional”. Normalmente, el cliente carga este contenido y el funcionamiento dinámico se consigue mediante el código JavaScript servido, y llamadas al Backend.

Pero bueno, no nos adelantemos. Veremos todo eso en su momento. De momento ¿Cómo empezamos?

Nuestro programa principal es similar al que vimos en la entrada anterior, con la salvedad de que hemos añadido una referencia a ‘FS.h’.

#include <ESP8266WiFi.h>
#include <ESP8266WebServer.h>
#include <FS.h>   // Include the SPIFFS library

#include "config.h"  // Sustituir con datos de vuestra red
#include "Server.hpp"
#include "ESP8266_Utils.hpp"

void setup(void)
{
  Serial.begin(115200);
  SPIFFS.begin();

  ConnectWiFi_STA();

  InitServer();
}

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

Por otro lado, hemos modificado el fichero ‘Server.hpp’ para definir un ruteo cuando la dirección no se encuentra. Como no tenemos ningún otro ruteo a un Endpoint, se dará en todos los casos.

En la función de Callback, en primer lugar, comprobamos si existe un fichero en el SPIFFS que coincida con la petición, con la función ‘HandleFileRead’. Si no existe, devolvemos un error de 404, not found.

ESP8266WebServer server(80);

#include "ESP8266_Utils_Server.hpp"

void handleNotFound() {
  server.send(404, "text/plain", "Not found");
}

void InitServer()
{
   server.onNotFound([]() {                             // If the client requests any URI
     if (!HandleFileRead(server.uri()))                 // send it if it exists
      handleNotFound();                  // otherwise, respond with a 404 (Not Found) error
   });
  
   server.begin();
   Serial.println("HTTP server started");
}

Finalmente, hemos incluido el fichero ‘ESP8266_Utils_Server’, que contiene la función HandleFileRead, y las funciones necesarias para servir el fichero desde el SPIFFS.

String GetContentType(String filename)
{
  if(filename.endsWith(".htm")) return "text/html";
  else if(filename.endsWith(".html")) return "text/html";
  else if(filename.endsWith(".css")) return "text/css";
  else if(filename.endsWith(".js")) return "application/javascript";
  else if(filename.endsWith(".png")) return "image/png";
  else if(filename.endsWith(".gif")) return "image/gif";
  else if(filename.endsWith(".jpg")) return "image/jpeg";
  else if(filename.endsWith(".ico")) return "image/x-icon";
  else if(filename.endsWith(".xml")) return "text/xml";
  else if(filename.endsWith(".pdf")) return "application/x-pdf";
  else if(filename.endsWith(".zip")) return "application/x-zip";
  else if(filename.endsWith(".gz")) return "application/x-gzip";
  return "text/plain";
}

void ServeFile(String path)
{
   File file = SPIFFS.open(path, "r");
   size_t sent = server.streamFile(file, GetContentType(path));
   file.close();
}

void ServeFile(String path, String contentType)
{
   File file = SPIFFS.open(path, "r");
   size_t sent = server.streamFile(file, contentType);
   file.close();
}

bool HandleFileRead(String path) 
{ 
  if (path.endsWith("/")) path += "index.html";
  Serial.println("handleFileRead: " + path);
  
  if (SPIFFS.exists(path)) 
  {
    ServeFile(path);
    return true;
  }
  Serial.println("\tFile Not Found");
  return false;
}

Compilamos el código y lo subimos a nuestro ESP8266. Por otro lado, creamos una carpeta llamada ‘data’ dentro de la carpeta donde tengamos alojados los ficheros anteriores.

En esta carpeta, llamamos un fichero llamado ‘index.html’ con el siguiente contenido.

<!DOCTYPE html>
<html class="no-js" lang="">
   <head>
      <meta charset="utf-8">
      <meta http-equiv="x-ua-compatible" content="ie=edge">
      <title>¡Hola mundo!</title>
      <meta name="description" content="">
      <meta name="viewport" content="width=device-width, initial-scale=1">
   </head>
 
   <body>
        <h1>Esta es tu página web. ¡Enhorabuena!</h1>
    </body>
</html>

Resultado

Si accedemos desde el navegador al ESP8266 veremos nuestra sencilla (y a la vez que flamante) página web servida desde el SPIFFS del ESP8266. ¡Enhorabuena!

esp8266-servidor-spiffs-hello-world

Mientras que en el puerto serie podemos comprobar que efectivamente hemos recibido la petición.

esp8266-servidor-spiffs-serial-port

¿Esto ha acabado aquí? No, ni mucho menor ¡Acabamos de empezar! En la próxima entrada veremos cómo mejorar nuestro código sirviendo ficheros comprimidos en Gzip. ¡Esta la próxima entrada!

Descarga el código

Todo el código de esta entrada está disponible para su descarga en Github.

github-full

Versión para el ESP8266: https://github.com/luisllamasbinaburo/ESP8266-Examples

Versión para el ESP32: https://github.com/luisllamasbinaburo/ESP32-Examples