INTRODUCCIÓN
Los dos métodos mas habituales para la comunicación (peticiones-respuestas) entre servidor y cliente son GET y POST. Cuando se usan estos métodos de comunicación dan como resultado que el navegador actualiza la página WEB que se visualiza, con el consiguiente inconveniente del tiempo (latencia) y recursos de procesamiento necesarios para establecer una conexión TCP así como transmitir toda la información de la página que se tiene que visualizar en el navegador.
Hay situaciones en las que se desea enviar datos sin actualizar la página. Esto es posible mediante solicitudes AJAX y XMLHTTP, aunque para enviar cada mensaje es necesario establecer una nueva conexión TCP. Sin embargo se mejora respecto a las comunicaciones GET y POST al reducirse de manera sustancial los datos a enviar, por lo tanto la comunicación tiene una menor latencia.
Este problema desaparece prácticamente con la tecnología WebSocket, que permite establecer y mantener abierta una conexión TCP, por lo que se pueden enviar y recibir datos constantemente entre el servidor y cliente con muy baja latencia a costa de una pequeña sobrecarga en el protocolo.
De una manera práctica se va a utilizar la tecnología WebSocket para regular la intensidad de un LED desde nuestro navegador WEB (Mozzilla, Google Chrome, Safari, Microsoft Edge…), conectado en modo Access Point con el servidor (ESP8266).
La página WEB de control se va a implementar en el código del sketch de Arduino y alojar en la memoria flash (mediante la función PROGMEM) del ESP8266, en lugar de la memoria SRAM, donde normalmente va el sketch, lo que facilita en gran medida su escritura, compresión y uso.
MATERIAL NECESARIO
CONEXIONADO
CODIGO WEB (HTML Y JAVASCRIPT)
El código de la página WEB para generar una «barra de desplazamiento –range slider-» para regular la intensidad del LED sería el siguiente:
<!DOCTYPE html> <html> <head> <meta charset=utf-8> <title>WebSocket ESP8266 - REGULACIÓN DE INTENSIDAD DE LED</title> </head> <body> <h1>Regulación de intensidad de LED</h1> <p>Comunicación vía WebSocket: Servidor (ESP8266) <---> Cliente</p> <input type='range' min='0' max='255' value='127' id='miValor' oninput='verValor()'> <p id='valor'></p> <script> verValor(); function verValor() { var x = document.getElementById('miValor').value; document.getElementById('valor').innerHTML = 'Intensidad (0-255): ' + x; } </script> </body> </html>
El intervalo de valores de la «barra de desplazamiento» se ha establecido entre 0 y 255, aunque se podría haber utilizado cualquier intervalo. Cada vez que se mueva se ejecutará la función «verValor()» que convierte el valor del «barra de desplazamiento» (miValor) en un número y lo imprime en pantalla.
El resultado del anterior código en un navegador se visualizará así:
Si al anterior código le añadimos la programación necesaria para establecer la conexión WebSocket con el servidor, tendremos lo siguiente:
<!DOCTYPE html> <html> <head> <meta charset=utf-8> <title>WebSocket ESP8266 - REGULACIÓN DE INTENSIDAD DE UN LED</title> </head> <body> <h1>Regulación de intensidad de LED</h1> <p>Comunicación vía WebSocket: Servidor (ESP8266) <---> Cliente</p> <input type='range' min='0' max='255' value='127' id='miValor' oninput='verValor()'> <p id='valor'></p> <script> var x; var connection = new WebSocket('ws://'+location.hostname+':81/', ['arduino']); connection.onopen = function () { connection.send('Conectado - ' + new Date()); verValor(); } connection.onmessage = function (event) { console.log('Servidor (recibe): ', event.data); } connection.onerror = function (error) { console.log('WebSocket Error!!!', error); } function verValor() { x = document.getElementById('miValor').value; document.getElementById('valor').innerHTML = 'Intensidad (0-255): ' + x; enviarValor(); } function enviarValor(){ console.log('Cliente (envía): ' + x); connection.send(x); } </script> </body> </html>
Lo mas importante que se ha hecho ha sido crear la variable para establecer la conexión WebSocket (la tenemos en la línea 14). También se han creado las funciones para controlar su estado y el envío y recepción de datos. Las analizamos a continuación:
Por supuesto que las acciones de enviar la fecha y hora desde el cliente al servidor o enviar datos a la consola del navegador no aportan nada a la comunicación WebSocket, aunque nos facilitan su control y nos permiten detectar fallos de comunicación.
Abriendo la consola del navegador (por ejemplo utilizando Google Chrome con Windows la secuencia sería: botón derecho del ratón / inspeccionar / console), en una comunicación correcta entre servidor y cliente, se vería como se muestra a continuación:
SKETCH
El código definitivo queda recogido en el siguiente sketch de Arduino:
/* Ejemplo de comunicación WebSocket Servidor <---> Cliente. Escrito por Dani No www.esploradores.com Este sofware está escrito bajo la licencia CREATIVE COMMONS con Reconocimiento - CompartirIgual (CC BY-SA) https://creativecommons.org/ -Redistributions of source code must retain the above creative commons and this list of conditions and the following disclaimer. -Redistributions in binary form must reproduce the above creative commons notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include <ESP8266WiFi.h> #include <WebSocketsServer.h> #include <ESP8266WebServer.h> #define LED 05 // D1 const char* ssid = "LED"; const char* password = "12345678"; static const char PROGMEM INDEX_HTML[] = R"( <!DOCTYPE html> <html> <head> <meta charset=utf-8> <title>WebSocket ESP8266 - REGULACIÓN DE INTENSIDAD DE UN LED</title> </head> <body> <h1>Regulación de intensidad de LED</h1> <p>Comunicación vía WebSocket: Servidor (ESP8266) <---> Cliente</p> <input type='range' min='0' max='255' value='127' id='miValor' oninput='verValor()'> <p id='valor'></p> <script> var x; var connection = new WebSocket('ws://'+location.hostname+':81/', ['arduino']); connection.onopen = function () { connection.send('Conectado - ' + new Date()); verValor(); } connection.onmessage = function (event) { console.log('Servidor (recibe): ', event.data); } connection.onerror = function (error) { console.log('WebSocket Error!!!', error); } function verValor() { x = document.getElementById('miValor').value; document.getElementById('valor').innerHTML = 'Intensidad (0-255): ' + x; enviarValor(); } function enviarValor(){ console.log('Cliente (envía): ' + x); connection.send(x); } </script> </body> </html> )"; ESP8266WebServer server (80); WebSocketsServer webSocket = WebSocketsServer(81); void webSocketEvent(uint8_t num, WStype_t type, uint8_t * payload, size_t length) { switch(type) { case WStype_CONNECTED: { IPAddress ip = webSocket.remoteIP(num); Serial.printf("[%u] Conectado a la URL: %d.%d.%d.%d - %s\n", num, ip[0], ip[1], ip[2], ip[3], payload); } break; case WStype_DISCONNECTED: Serial.printf("[%u] Desconectado!\n", num); break; case WStype_TEXT: Serial.printf("Número de conexión: %u - Carácteres recibidos: %s\n ", num, payload); webSocket.sendTXT(num, payload); //Devuelve el mensaje recibido al cliente String str = (char*)payload; //Conversión de los carácteres recibidos a cadena de texto int intensidad = str.toInt(); //Conversión de la cadena de texto a número analogWrite(LED, intensidad); break; } } void setup() { Serial.begin(115200); Serial.println(); WiFi.softAP(ssid, password); IPAddress myIP = WiFi.softAPIP(); Serial.print("IP del access point: "); Serial.println(myIP); // start webSocket server webSocket.begin(); webSocket.onEvent(webSocketEvent); // handle index server.on("/", []() { server.send_P(200, "text/html", INDEX_HTML); }); server.begin(); Serial.println("WebServer iniciado..."); pinMode(LED, OUTPUT); digitalWrite(LED, 0); analogWriteRange(255); } void loop() { webSocket.loop(); server.handleClient(); }
Como se indicaba en la introducción, toda la programación de la página WEB va alojada en la memoria flash (mediante la función PROGMEM) del ESP8266, la tenemos entre las líneas 31 y 69 del sketch.
La función para gestionar el WebSocket (webSocketEvent) la tenemos entre las líneas 71 y 94. Se establecen tres «paths» (opciones) para gestionar la comunicación:
Para terminar señalaremos que en las línea 106 y 107 esta el código par inicializar el servidor WebSocket, a cuya librería llama en la línea 23.
Una vez que hemos cargado el sketch en el ESP8266, podremos comprobar con nuestro smartphone, tablet u ordenador, que crea un red WiFi. El nombre de la red es «LED» y la clave de conexión «12345678«.
Cuando nos conectamos, si accedemos a la dirección IP del punto de acceso: http://192.168.4.1 o simplemente 192.168.4.1, desde cualquier navegador WEB (Mozzilla, Google Chrome, Safari, Internet Explorer, Microsoft Edge…), se ejecutará el código WEB y se visualizará la «barra de desplazamiento» para controlar el LED en el navegador.
6 Comments
Leave your reply.