
En esta entrada de la sección sobre el ESP8266 y el ESP32 vamos a ver cómo servir contenido desde la memoria Flash, para evitar un consumo innecesario de RAM.
En entradas anteriores hemos visto cómo hacer un servidor sencillo, cómo distinguir entre peticiones y leer los parámetros, y cómo servir contenido dinámico generando un String en la petición.
Ya dijimos que uno de los problemas de generar el contenido dinámicamente es que supone una carga al procesador (frente a generar un fichero estático) y que, si la respuesta es larga, ocupa mucha memoria en el espacio de variables.
Una forma de evitar esto último es almacenar las partes estáticas de la respuesta en la memoria Flash y combinarlas con aquellas que realmente van a cambiar. Esto alivia en parte el problema del uso de memoria.
Anuncio:
Remarcar que esto debemos usarlo únicamente para contenido dinámico, es decir, que va a cambiar entre peticiones. Para servir contenido estático tenemos alternativas mejores como el SPIFFS, que veremos en la siguiente entrada.
Servir contenido desde Flash
Servir el contenido desde la memoria Flash es realmente sencillo. Nuestro Sketch es prácticamente el mismo, únicamente hemos incluido un nuevo fichero 'index.h' que contendrá el String a servir.
#include <ESP8266WiFi.h> #include <ESP8266WebServer.h> bool ledStatus = false; // Variable de ejemplo #include "config.h" // Sustituir con datos de vuestra red #include "index.h" #include "ESP8266_Utils.hpp" #include "Server.hpp" void setup(void) { Serial.begin(115200); ConnectWiFi_STA(); InitServer(); } void loop() { server.handleClient(); }
Por otro lado, creamos el fichero 'index.h', con el siguiente contenido.
const char MAIN_page[] PROGMEM = R"=====( <!DOCTYPE html> <html> <body> <div> <h1>Hola mundo!</h1> </div> </body> </html> )=====";
Como vemos, simplemente hemos creado un array de Char que contiene la página web de "Hola mundo" que queremos servir. Para guardarlo en la memoria flash hemos empleado 'PROGMEN', de forma que esta constante no se almacene en la memoria dedicada a variables.
Finalmente, nuestro fichero 'Server.hpp' queda de la siguiente forma.
ESP8266WebServer server(80); void handleRoot() { String response = MAIN_page; server.send(200, "text/html", response); } void handleNotFound() { server.send(404, "text/plain", "Not found"); } void InitServer() { server.on("/", handleRoot); server.onNotFound(handleNotFound); server.begin(); Serial.println("HTTP server started"); }
En este ejemplo sencillo, únicamente hemos ruteado la URL root a la variable MAIN_page anterior. El contenido servido es una constante y no varía (es estático). A continuación, veremos un ejemplo ligeramente más realista.
Servir contenido dinámico desde Flash
Entonces, ¿en qué circunstancias puede ser esto útil? Para servir contenido 'pseudo-dinámico', es decir, para alojar las partes que no varían de una respuesta dinámica reduciendo la memoria dedicada a variables empleada.
Por ejemplo, supongamos que, siguiendo con el ejemplo de la entrada anterior, queremos servir una página Web que muestre el estado de una variable cualquiera. En este ejemplo la variable será 'ledStatus'.
En la entrada anterior devolvíamos un escueto String informando del estado de la variable. Pero si queremos servir una página Web, hay mucho contenido que es similar, y únicamente cambia una parte de la página Web servida.
En este caso, puede tener sentido almacenar las partes 'estáticas' de la respuesta en variables PROGMEM.
Por ejemplo, si modificamos 'index.h' para que contenga
const char HTML_PART_1[] PROGMEM = R"=====( <!DOCTYPE html> <html> <body> <div> <h1>Hola mundo!</h1> <p>LED: )====="; const char HTML_PART_2[] PROGMEM = R"=====( </p> </div> </body> </html> )=====";
Ahora podemos servir 'HTML_PART_1', luego nuestro contenido dinámico, y terminar con 'HTML_PART_2'. Para ello, modificamos el fichero 'Server.hpp' con el siguiente contenido.
ESP8266WebServer server(80); void handleRoot() { String response = ledStatus ? "ON" : "OFF"; server.setContentLength(sizeof(HTML_PART_1) + sizeof(response) + sizeof(HTML_PART_2)); server.send(200, "text/html", HTML_PART_1); server.sendContent(response); server.sendContent(HTML_PART_2); } void handleNotFound() { server.send(404, "text/plain", "Not found"); } void InitServer() { server.on("/", handleRoot); server.onNotFound(handleNotFound); server.begin(); Serial.println("HTTP server started"); }
Y si ahora accedemos con el navegador veremos que, en lugar de un simple "LED: OFF" hemos servido una página Web completa en la que únicamente modificamos el contenido que queremos.
Por supuesto, tenemos otras formas de realizar una aplicación de este tipo. Normalmente serviremos una página Web estática, y el frontend realizara una consulta a un Endpoint para obtener el contenido de la variable.
Sin embargo, aunque en próximas entradas veremos cómo conseguir un resultado igual al del ejemplo de forma más conveniente, lo importante es ilustrar que es posible emplear la memoria Flash para servir contenido y aligerar el consumo de RAM.
Todo esto lo veremos dentro de poco. En la próxima entrada aprenderemos a usar el sistema de ficheros SPIFFS para servir contenido estático. ¡Hasta pronto!
Descarga el código
Todo el código de esta entrada está disponible para su descarga en Github.
Versión para el ESP8266: https://github.com/luisllamasbinaburo/ESP8266-Examples
Versión para el ESP32: https://github.com/luisllamasbinaburo/ESP32-Examples
Anuncio:
Primero agradecerte el esfuerzo e interés en facilitar a otros avanzar rápido en el conocimiento de temas tan diversos. Estoy siguiendo tus tutoriales de ESP8286, bien estructurados y sobre todo amenos. Enhorabuena.
En este en concreto se genera un error en el ejemplo 1, y tomándolo de GITHUB se reproduce.
void handleRoot() {
String response = MAIN_page;
server.send(200, "text/html", MAIN_page);
}
No he profundizado en Server.send pero parece que no le sienta bien declarar response, así que lo he anulado y funciona.
void handleRoot() {
//String response = MAIN_page;
server.send(200, "text/html", MAIN_page);
}