Objetivos
Material requerido.
Un ESP32 |
ESP32 WIFI STATION MODE
Hasta ahora, hemos visto, en las últimas sesiones que podemos acceder a nuestro ESP32 tanto en modo STATION como ACCESS POINT, lo que nos da una enorme versatilidad cuando tenemos proyectos IOT en los que el coste de la instalación es importante (O sea, en todos. La pasta es la pasta).
La idea básica de estos humildes tutos, es ayudaros a arrancar vuestros proyectos, y que los podáis usar como plantillas básicas los ejemplos que os vamos presentando. Y con esa idea en mente alguien podría decirme que hemos aprendido a sacar mensajes web muy bonitos en nuestros terminales WIFI y si me apuras hasta a presentar datos en el propio servidor como mensajes, pero que si lo que quiero es ver el estado de algún accionamiento (Como el interruptor de una lampara o motor) lo que llevamos visto no nos sirve de mucho… y tendría razón
Por eso mismo vamos a dedicar esta sesión a ver como podemos manejar este tipo de interruptores web, que suele ser una de las preguntas mas habituales que me hacen: ¿Cómo hago yo para arrancar la calefacción de mi segunda vivienda o apagarla desde el móvil?
En esta ocasión vamos a ver precisamente eso y espero que os sirva de punto de partida para más cosas, porque veréis que es igual de fácil que hasta ahora y que en seguida estaremos pintando botones. Pero no quisiera empezar este tema sin agradecer cordialmente a los chicos de lastminuteengineers por los magníficos tutoriales que nos regalan, y de los que me he servido en mas de una ocasión para mis historias y en la que se basa toda esta sesión.
Botones en la web
En esta ocasión vamos a conectar un par de LEDs a nuestro ESP32 de modo que podamos encenderlos y apagarlos desde la pagina web que sirva nuestro servidor. Es el clásico ejemplo de la calefacción de pueblo y un encendido/ apagado remoto.
Para ello veremos que podemos mostrar un par de botones directamente en la web y que al tocarlos enviemos un mensaje al ESP32 para encienda/apague los LEDs o lo que tengamos enganchados a los pines (Recuerda que con un sencillo relé puede mover hasta un motor grande)
La gran virtud del HTML5 es su enorme disponibilidad de instrucciones para hacer las cosas mas habituales y algunas que no lo son tanto, y entre ellas no podía faltar una de hacer botones. Por eso empezaremos viendo como podemos crear el código de un par de botones y también como crear e invocar las funciones que se encarguen del encendido y apagado de los pines correspondientes.
Vamos a empezar montando un circuito con un par de leds conectados a los pines 4 y 5 de nuestro ESP32 con una resistencia de modo que podamos encenderlos activando el botón correspondiente desde la web.
Y poco mas vamos a ver como podemos usar esto.
Programando un servidor web con botones
En las dos sesiones anteriores usamos unas funciones de formateo HTML5 de los textos. Cosas sencillas como cambiar colores y tipos de letra. El tema de los botones es igual de sencillo, pero usando unos códigos HTML5 diferentes asi que vamos a repasar como construimos la pagina web que mostramos a quien se conecta.
Un pagina HTML es simplemente texto Unicode (Como el ASCII pero extendido para mostrar Ñ,Ç y otros símbolos extraños que la mayor parte de los lenguajes escritos usan) que va describiendo como definir la página a mostrar. Ese texto conforma una descripción inequívoca de cómo representar una pagina web y tiene ciertas normas.
Por eso lo que vamos haciendo con nuestro programa es crear un puntero a un String que va a contener todo el testo que representa nuestra pagina y vamos añadiendo líneas a medida que las necesitamos. El truco es que estas líneas pueden ser unas u otras dependiendo de lo que nos convenga por lo que pueden ser dinámicas. Tenemos que crear un String que tenga el texto necesario para que sea una pagina web valida.
Aqui os dejo el programa completo:
Empezamos diciendo que es una pagina HTML, y definimos cabeceras, títulos, tipo de letra, margenes y demás (Este no es el mejor sitio para un curso HTML5 y desde luego yo no soy un experto, pero si tiras de Google encontraras descripciones razonables de todo esto):
String ptr = "<!DOCTYPE html> <html>\n"; ptr +="<head><meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, user-scalable=no\">\n"; ptr +="<title>LED Control</title>\n"; ptr +="<style>html { font-family: Helvetica; display: inline-block; margin: 0px auto; text-align: center;}\n"; ptr +="body{margin-top: 50px;} h1 {color: #444444;margin: 50px auto 30px;} h3 {color: #444444;margin-bottom: 50px;}\n";
Todas las paginas web del mundo empiezan por algo asi y luego podemos ir describiendo otras características de la misma. Por ejemplo, podemos definer un stilo css para los botones que pongamos mas adelante, con dos colores diferentes para destacar cuando se ha pulsado o no.
ptr +=".button {display: block;width: 80px;background-color: #3498db;border: none;color: white;padding: 13px 30px;text-decoration: none;font-size: 25px;margin: 0px auto 35px;cursor: pointer;border-radius: 4px;}\n"; ptr +=".button-on {background-color: #3498db;}\n"; ptr +=".button-on:active {background-color: #2980b9;}\n"; ptr +=".button-off {background-color: #34495e;}\n"; ptr +=".button-off:active {background-color: #2c3e50;}\n"; ptr +="p {font-size: 14px;color: #888;margin-bottom: 10px;}\n";
Si queremos poner una cabecera y mensajes de bienvenida:
ptr +="</style>\n"; ptr +="</head>\n"; ptr +="<body>\n"; ptr +="<h1>ESP32 Web Server</h1>\n"; ptr +="<h3>Using Access Point(AP) Mode</h3>\n";
Pero cuando queremos pintar los botones, nos encontramos que pueden ser de un color cuando están apagados y de otro al estar encendidos. Parece buena idea que definamos unas variables de status para controlarlo (En el programa Arduino, no en la pagina HTML)
bool LED1status = LOW; bool LED2status = LOW;
De este modo podremos controlar la situación de las luces y cambiarla cuando nos convenga. La cuestión clave ahora, es que a medida que montamos el String ptr, podemos comprobar el status de los leds y añadir lo que nos interesa con estas lineas:
if(led1stat) {ptr +="<p>LED1 Status: ON</p><a class=\"button button-off\" href=\"/led1off\">OFF </a>\n";} else {ptr +="<p>LED1 Status: OFF</p><a class=\"button button-on\" href=\"/led1on\">ON</a>\n";} if(led2stat) {ptr +="<p>LED2 Status: ON</p><a class=\"button button-off\" href=\"/led2off\">OFF</a>\n";} else {ptr +="<p>LED2 Status: OFF</p><a class=\"button button-on\" href=\"/led2on\">ON</a>\n";}
No te preocupes mucho si estas líneas te parecen confusas (No me extraña) pero la idea básica es que muestran el botón encendido o el botón apagado según el valor de Led status.
Al final, la función SendHTML() que vamos a usar para encender y apagar los botones y los LEDs, va a aceptar dos parámetros que son el estado de los LED y quedaría poco mas o menos como esto:
String SendHTML(uint8_t led1stat,uint8_t led2stat) { String ptr = "<!DOCTYPE html> <html>\n"; ptr +="<head><meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, user-scalable=no\">\n"; ptr +="<title>LED Control</title>\n"; ptr +="<style>html { font-family: Helvetica; display: inline-block; margin: 0px auto; text-align: center;}\n"; ptr +="body{margin-top: 50px;} h1 {color: #444444;margin: 50px auto 30px;} h3 {color: #444444;margin-bottom: 50px;}\n"; ptr +=".button {display: block;width: 80px;background-color: #3498db;border: none;color: white;padding: 13px 30px;text-decoration: none;font-size: 25px;margin: 0px auto 35px;cursor: pointer;border-radius: 4px;}\n"; ptr +=".button-on {background-color: #3498db;}\n"; ptr +=".button-on:active {background-color: #2980b9;}\n"; ptr +=".button-off {background-color: #34495e;}\n"; ptr +=".button-off:active {background-color: #2c3e50;}\n"; ptr +="p {font-size: 14px;color: #888;margin-bottom: 10px;}\n"; ptr +="</style>\n"; ptr +="</head>\n"; ptr +="<body>\n"; ptr +="<h1>ESP32 Web Server</h1>\n"; ptr +="<h3>Using Access Point(AP) Mode</h3>\n"; if(led1stat) {ptr +="<p>LED1 Status: ON</p><a class=\"button button-off\" href=\"/led1off\">OFF</a>\n";} else {ptr +="<p>LED1 Status: OFF</p><a class=\"button button-on\" href=\"/led1on\">ON</a>\n";} if(led2stat) {ptr +="<p>LED2 Status: ON</p><a class=\"button button-off\" href=\"/led2off\">OFF</a>\n";} else {ptr +="<p>LED2 Status: OFF</p><a class=\"button button-on\" href=\"/led2on\">ON</a>\n";} ptr +="</body>\n"; ptr +="</html>\n"; return ptr; }
Controlando los LED
<
Vale, me diréis, lo de arriba es capaz de encender y apagar los botones en la aplicación web pero aun no sabemos como gestionar la parte física del servidor para que de la orden de encendido / Apagado. ¿Cómo hacemos esto?
Pues de la forma mas sencilla. Recordad que en las sesiones anteriores ya comentamos que teníamos que definir al menos un par de funciones Call-back para que se ejecuten cuando alguien llega a la pagina principal y para el caso de error que podían ser como estas:
void handle_OnConnect() { Serial.println("GPIO4 Status: OFF | GPIO5 Status: OFF"); server.send(200, "text/html", SendHTML(LED1status,LED2status)); } void handle_NotFound() { server.send(404, "text/plain", "Not found"); }
La primera atiende a quien llega a la pagina “/” la de entrada y la segunda a quien pida algo que no sabemos de que habla. ¿Y para encender el LED1? Podemos definir una pagina llamada /led1on que será atendida por la función handle_led1on():
void handle_led1on() { LED1status = HIGH; Serial.println(«GPIO4 Status: ON»); server.send(200, «text/html», SendHTML(true,LED2status)); }
Que como ves simplemente manda un mensaje a la puerta serie (Por comprobación, no porque haga falta) y llama a la función SendHTML() poniendo a on el primer led y dejando el segundo como está. ¿Y para apagarlo? Podemos escribir otra función:
void handle_led1off() { LED1status = LOW; Serial.println("GPIO4 Status: OFF"); server.send(200, "text/html", SendHTML(false,LED2status)); }
Y para el LED2 tendremos:
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)); }
Que como ves tiene poco misterio. Bastará con acceder con el navegador a las paginas de nuestro servidor web en el ESP32:
/led1on para encender el LED1 /led1off Para apagar LED1 /led2on Para encender LED2 /led2off Para apagar LED2
Como ya hemos definido todas las funciones instrumentales de nuestro programa solo nos queda escribir la función setup y el loop, y las líneas iniciales que ya vimos en las sesiones previas:
#include <WiFi.h> #include <WebServer.h> const char* ssid = "ESP32"; // Enter SSID here const char* password = "12345678"; //Enter Password here IPAddress local_ip(192,168,1,1); IPAddress gateway(192,168,1,1); IPAddress subnet(255,255,255,0); WebServer server(80);
Definimos los pines que vamos a usar para manejar los leds (Y los ponemos en estado de apagado cuando arrancamos el programa):
uint8_t LED1pin = 4; bool LED1status = LOW; uint8_t LED2pin = 5; bool LED2status = LOW;
Y empezamos con el setup muy parecido a las sesiones anteriores:
Serial.begin(115200); pinMode(LED1pin, OUTPUT); pinMode(LED2pin, OUTPUT); WiFi.softAP(ssid, password); WiFi.softAPConfig(local_ip, gateway, subnet); delay(100);
La diferencia es que ahora tenemos mas funciones que registrar en el servidor:
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.begin(); Serial.println("HTTP server started");
Como el Status de las variables que representan el estado de los pines se maneja desde las funciones handle_ledXYY, lo único que le queda por hacer al loop es encender o apagar los leds en función de ese estado:
void loop() { server.handleClient(); if(LED1status) { digitalWrite(LED1pin, HIGH);} else { digitalWrite(LED1pin, LOW);} if(LED2status) { digitalWrite(LED2pin, HIGH);} else { digitalWrite(LED2pin, LOW);} }
Y eso es todo. Parece un poco lioso, pero si le das dos vueltas veras como todo encaja.
Probando el programa
Ya solo nos queda ver cómo probamos todo esto. En primer lugar, tenemos que conectarnos a la red WIFI llamada ESP32_ de nuestro programa Arduino:
Hasta aquí es lo mismo que en la sesión anterior, como nuestro ESP esta en modo Acces point o AP, necesitamos conectarnos a el antes de intentar acceder y una vez hecho, saltamos con el navegador a la dirección IP definida, 192.168.1.1 y esto es lo que vamos a ver:
Si ahora pruebas a pulsar los botones, veras que se encienden y apagan siguiendo tus órdenes y encenderán y apagaran los leds de forma acorde. Además puedes comprobar las operaciones en la consola:
Aqui os dejo un minivideo con el resultado de todo el asunto: