material-design-esp8266

Servir páginas Material Design desde ESP32

  • 5 min

Material Design Lite permite servir una interfaz con estética Material Design desde una página web alojada en un ESP32.

Hasta ahora hemos servido páginas muy simples, suficientes para comprobar que todo funciona, pero bastante feas. Y eso está bien para probar, pero en cuanto queremos hacer un panel de control un poco usable, conviene cuidar mínimamente la interfaz.

En este artículo vamos a ver cómo añadir Material Design Lite a una página servida desde la placa, tanto desde CDN como desde ficheros guardados en SPIFFS.

El ejemplo está orientado a ESP32. En muchos casos también puede adaptarse a ESP8266 cambiando librerías y algunos detalles de pines.

Hemos dedicado varias entradas a ver cómo servir páginas web desde el ESP8266, y como conectar el frontend con el backend a través de llamadas AJAX o Websockets.

Pero, reconozcámoslo, las páginas web que hemos servido hasta ahora son bastante feas. ¡Eso está a punto de cambiar! y empezaremos con la sencilla librería Material Design Lite que ya vimos en esta entrada.

¡Así que manos a la obra! En cuanto a la parte del backend, es decir, el código del ESP8266, es idéntico al que hemos venido usando. Esto es,

#include <WiFi.h>
#include <ESPAsyncWebServer.h>
#include <FS.h>

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

void setup(void)
{
	Serial.begin(115200);
	SPIFFS.begin();
	
	ConnectWiFi_STA();
	
	InitServer();
}

void loop(void)
{
}
Copied!
AsyncWebServer server(80);

void InitServer()
{
  server.serveStatic("/", SPIFFS, "/").setDefaultFile("index.html");

  server.onNotFound([](AsyncWebServerRequest *request) {
    request->send(400, "text/plain", "Not found");
  });

  server.begin();
  Serial.println("HTTP server started");
}
Copied!

La diferencia de verdad en este ejemplo está en el fichero ‘index.html’ y en los ficheros que subiremos a la memoria SPIFFS. Hasta ahora nuestro código html hubiera sido así,

<!doctype html>
<html class="no-js" lang="">

<head>
  <meta charset="utf-8">
  <meta http-equiv="x-ua-compatible" content="ie=edge">
  <title></title>
  <meta name="description" content="">
  <meta name="viewport" content="width=device-width, initial-scale=1">
</head>

<body>
  <div>
    <div>
      <form action="#">
        <div>
          <input type="text" id="sample3">
          <label for="sample3">Text...</label>
        </div>
      </form>
    </div>
  </div>

  <div>
    <div>
      <button>
        <i class="material-icons">add</i>
      </button>
    </div>
    <div>
      <button>Button</button>
    </div>
  </div>

  <div>
    <div>
      <input type="range" min="0" max="100" value="25" tabindex="0">
    </div>
  </div>

  <div >
    <div>
      <label or="checkbox-1">
        <input type="checkbox" id="checkbox-1" checked>
        <span>Checkbox</span>
      </label>
      <label for="checkbox-2">
        <input type="checkbox" id="checkbox-2">
        <span>Checkbox2</span>
      </label>
      <label for="checkbox-3">
        <input type="checkbox" id="checkbox-3">
        <span>Checkbox3</span>
      </label>
    </div>
  </div>

  <div>
    <div>
      <label for="option-1">
        <input type="radio" id="option-1" name="options" value="1" checked>
        <span>First</span>
      </label>
      <label for="option-2">
        <input type="radio" id="option-2" name="options" value="2">
        <span>Second</span>
      </label>
      <label for="option-3">
        <input type="radio" id="option-3" name="options" value="3">
        <span>Third</span>
      </label>
    </div>
  </div>

  <div>
    <div>
      <label or="switch-1">
        <input type="checkbox" id="switch-1" checked>
        <span>Switch</span>
      </label>
    </div>
  </div>
</body>

</html>
Copied!

Que daría este terriblemente feo resultado.

material-design-lite-test-befor

Y ahora ahora va a pasar a ser

<!doctype html>
<html class="no-js" lang="">
 
<head>
   <title>ESP8266 Material Lite</title>
   <meta charset="utf-8">
   <meta http-equiv="x-ua-compatible" content="ie=edge">

   <meta name="description" content="">
   <meta name="viewport" content="width=device-width, initial-scale=1">
</head>
 
<body>
  <link rel="stylesheet" href="vendor/google-fonts.css">
  <link rel="stylesheet" href="vendor/material.css">
  <!--<link rel="stylesheet" href="https://fonts.googleapis.com/icon?family=Material+Icons">
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/[email protected]/dist/material.indigo-pink.min.css">-->
 
   <div class="mdl-grid">
      <div class="mdl-cell mdl-cell--4-col">
         <form action="#">
            <div class="mdl-textfield mdl-js-textfield mdl-textfield--floating-label">
               <input class="mdl-textfield__input" type="text" id="sample3">
               <label class="mdl-textfield__label" for="sample3">Text...</label>
            </div>
         </form>
      </div>
   </div>
 
   <div class="mdl-grid">
      <div class="mdl-cell mdl-cell--1-col">
         <button class="mdl-button mdl-js-button mdl-button--fab mdl-js-ripple-effect mdl-button--colored">
            <i class="material-icons">add</i>
         </button>
      </div>
      <div class="mdl-cell mdl-cell--1-col">
         <button class="mdl-button mdl-js-button mdl-button--raised mdl-js-ripple-effect mdl-button--colored">Button</button>
      </div>
   </div>
 
   <div class="mdl-grid">
      <div class="mdl-cell mdl-cell--4-col">
         <input class="mdl-slider mdl-js-slider" type="range" min="0" max="100" value="25" tabindex="0">
      </div>
   </div>
 
   <div class="mdl-grid">
      <div class="mdl-cell mdl-cell--4-col">
         <label class="mdl-checkbox mdl-js-checkbox mdl-js-ripple-effect" for="checkbox-1">
            <input type="checkbox" id="checkbox-1" class="mdl-checkbox__input" checked>
            <span class="mdl-checkbox__label">Checkbox</span>
         </label>
         <label class="mdl-checkbox mdl-js-checkbox mdl-js-ripple-effect" for="checkbox-2">
            <input type="checkbox" id="checkbox-2" class="mdl-checkbox__input">
            <span class="mdl-checkbox__label">Checkbox2</span>
         </label>
         <label class="mdl-checkbox mdl-js-checkbox mdl-js-ripple-effect" for="checkbox-3">
            <input type="checkbox" id="checkbox-3" class="mdl-checkbox__input">
            <span class="mdl-checkbox__label">Checkbox3</span>
         </label>
      </div>
   </div>
 
   <div class="mdl-grid">
      <div class="mdl-cell mdl-cell--4-col">
         <label class="mdl-radio mdl-js-radio mdl-js-ripple-effect" for="option-1">
            <input type="radio" id="option-1" class="mdl-radio__button" name="options" value="1" checked>
            <span class="mdl-radio__label">First</span>
         </label>
         <label class="mdl-radio mdl-js-radio mdl-js-ripple-effect" for="option-2">
            <input type="radio" id="option-2" class="mdl-radio__button" name="options" value="2">
            <span class="mdl-radio__label">Second</span>
         </label>
         <label class="mdl-radio mdl-js-radio mdl-js-ripple-effect" for="option-3">
            <input type="radio" id="option-3" class="mdl-radio__button" name="options" value="3">
            <span class="mdl-radio__label">Third</span>
         </label>
      </div>
   </div>
 
   <div class="mdl-grid">
      <div class="mdl-cell mdl-cell--4-col">
         <label class="mdl-switch mdl-js-switch mdl-js-ripple-effect" for="switch-1">
            <input type="checkbox" id="switch-1" class="mdl-switch__input" checked>
            <span class="mdl-switch__label">Switch</span>
         </label>
      </div>
   </div>
 
  <script src="vendor/material.js"></script>
  <!--<script src="https://cdn.jsdelivr.net/combine/npm/[email protected],npm/[email protected]"></script>-->
</body>
 
</html>
Copied!

Con el que conseguiremos esta estética.

esp8266-material-design-formulario

Respecto a los ficheros empleados por el framework Material Design Lite (JS y CSS) podemos o bien cargarlos desde una CDN, o bien subirlos comprimidos a la memoria del ESP8266.

En el ejemplo tenéis ambas opciones, estando comentada la opción de CDN

En principio preferiremos la CDN siempre que sea posible, es decir, cuando el cliente final disponga de conexión a Internet para bajar los ficheros. De esta forma liberamos al ESP8266 del trabajo de tener que servir los ficheros.

Pero no siempre va a ser posible. Por ejemplo, tendremos que servirlos nosotros cuando el ESP8266 esté actuando como en modo AP porque el cliente no va a tener acceso a Internet para descargarse los ficheros por desde el CDN.

¡Así de sencillo ha sido!. Por supuesto, estamos usando el framework Material Design Lite pero podéis usar cualquier otro framework o template que queráis para servir las páginas web siguiendo el procedimiento que hemos visto para cargar los ficheros.

Se acabaron las excusas para servir páginas “feas” desde nuestra placa. Con una librería ligera y unos pocos cambios podemos conseguir una interfaz bastante más limpia, sin complicar demasiado el proyecto.

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