Desarrollo de aplicaciones IoT en la nube para Arduino y ESP8266

Text
Read preview
Mark as finished
How to read the book after purchase
Font:Smaller АаLarger Aa

2.2 COMUNICACIONES WEB CON ESP-01

Actualmente, la forma más común de conexión a Internet es por WiFi. Pero Arduino no tiene capacidad para este tipo de conexiones, por lo que tendrán que conectarlo a un componente que realice dicha labor. En concreto, utilizarán el ESP-01, cuyo aspecto se puede ver en la siguiente imagen.


El ESP-01, fabricado por Espressif, es un módulo con un SoC (System on a Chip) ESP8266 cuyo microcontrolador trabaja a 80 MHz, un chip de memoria de 512 Kb o 1 Mb (según el modelo) y WiFi 802.11 b/g/n integrado. Aunque conectado con Arduino su función sea únicamente la de proporcionar conectividad WiFi, pueden imaginar que un componente con semejante potencia podría utilizarse de forma independiente en proyectos para los que se requiera un número pequeño de pines digitales, tal como se verá más adelante.

Un SoC es un término utilizado para referirse a un chip en el que se integran diversos componentes que forman parte de un ordenador o sistema informático (por ejemplo, CPU, memoria, WiFi, etc.).

2.2.1 Características del ESP-01

Desde el punto de vista electrónico, el ESP-01 tiene una tensión de alimentación de 3.3 V. Aunque parezca que pueda funcionar con 5 V, no lo conecten nunca a esta tensión, porque terminarían dañándolo. Además, tiene un consumo muy elevado de corriente que en algunas situaciones puede superar los 200 mA. Arduino solo puede proporcionar hasta 50 mA, por lo que se recomienda usar una fuente de alimentación independiente. En la siguiente imagen pueden ver una utilizada habitualmente en este tipo de proyectos.


Esta pequeña fuente de alimentación tiene dos salidas independientes que se pueden configurar a 5 o 3.3 V según la posición de sus jumpers. La corriente suministrada llega a los 700 mA, suficiente para alimentar el ESP-01. La entrada de energía puede venir de un puerto USB, de un adaptador de red o de una batería con una tensión entre 6.5 y 12 V. Además, dispone de un interruptor para encenderla y apagarla.

Siempre que se utilicen dos fuentes de alimentación independientes, como la de Arduino y la necesaria para el ESP-01, se han de conectar entre sí los pines GND para que ambas tengan la misma referencia de tensión y los circuitos funcionen correctamente.

El ESP-01 se compone de 8 pines.


Vean la función de cada uno de ellos:

• GND, VCC. Son los pines de alimentación. No olviden que funcionan a 3.3 V.

• GPIO0, GPIO2. Pines digitales de E/S. También trabajan con 3.3 V.

• RX, TX. Son los pines de recepción y transmisión serie del Soc. Sirven tanto para su programación como para la comunicación con otras placas, como Arduino. También pueden funcionar como pines digitales con el número 3 (RX) y 1 (TX). De nuevo, recuerden que dichos pines trabajan a 3.3 V, por lo que los componentes que conecten a ellos deberán estar adaptados a esta tensión.

• CH_PD. Si la tensión de dicho pin es 0V (nivel bajo), el ESP-01 se apaga, mientras que si lo conecta a 3.3 V (nivel alto), se enciende.

• RESET. Sirve para reiniciar el ESP-01 cuando lo conecte a GND.

En realidad, el SoC ESP8266 dispone de más puertos GPIO de los que únicamente se deben utilizar los indicados (GPIO0, GPIO1, GPIO2, GPIO3), ya que el resto nos son accesibles para su programación. Tratar de usarlos daría como resultado errores de ejecución.

2.2.2 Programación del ESP-01 desde el IDE Arduino

Tanto si utilizan el módulo ESP-01 de forma independiente o como interfaz WiFi para su Arduino, tendrán que programarlo. Pero no se preocupen, no tendrán que aprender ningún nuevo lenguaje de programación ni instalar un IDE adicional al de Arduino. El ESP-01 se programa igual que Arduino en su mismo IDE. Pero antes tendrán que instalar un plugin específico, para lo que es imprescindible tener una versión del IDE superior a la 1.6.4, a partir de la cual se permite incorporar las definiciones de nuevas placas.

Para instalar el plugin del ESP8266, pulsen en Archivo → Preferencias y, en el campo « Gestor de URLs Adicionales de Tarjetas» escriban el URL

http://arduino.esp8266.com/stable/package_esp8266com_index.json, tal como se muestra en la siguiente ventana.


No se olviden de pulsar Ok en la ventana para consolidar el cambio realizado.

Lo siguiente que tendrán que hacer es incorporar la nueva placa al IDE pulsando en Herramientas → Placa:”***” → Gestor de tarjetas… (Los asteriscos serían el nombre de la placa que actualmente tengan seleccionada). Finalizada la instalación, el resultado debe ser el mostrado en la siguiente pantalla.


A partir de ahora, cuando quieran programar esta placa, deberán seleccionarla en Herramientas → Placa: “***” → Generic ESP8266 Module, tal como aparece a continuación.


Con esta operativa ya tienen el IDE Arduino preparado para programar el módulo ESP-01. Ahora queda montar el circuito que permita dicha programación, en el que deberán incorporar necesariamente un adaptador FTDI que habilite la comunicación serie desde su ordenador al ESP-01 usando un cable USB. Esto es así porque, a diferencia de Arduino, este pequeño componente no viene con el adaptador incorporado, por lo que deberán conectarlo externamente. En la imagen se aprecia el aspecto de uno de ellos.


En uno de sus extremos se encuentra el conector utilizado para la conexión USB con el ordenador. En el otro extremo están los pines de alimentación (GND y VCC) y los de recepción y transmisión de datos (RX y TX). La conexión entre el ESP-01 y el adaptador será a través del protocolo serie.

A continuación se muestra la conexión del módulo ESP-01 con el adaptador en modo grabación.


Como pueden observar, se conectan los pines RX/TX de ambos elementos de forma cruzada, ya que lo que se envíe por el pin TX de uno de los componentes deberá ser recibido por el pin RX del otro.

Ahora sí, ya están en condiciones de cargar su primer programa en el ESP8266, pero antes es necesario hacer una última consideración. A diferencia de Arduino, al que no hay que indicarle de forma explícita si lo que se quiere es cargar un programa o ejecutarlo, en el caso del ESP-01 sí debe hacerse. Para ello, siempre que programen el módulo, tendrán que poner el pin GPIO0 a nivel bajo (0V).

Por el contrario, cuando lo que quieran es ejecutar un programa, lo deberán dejar desconectado. Además, en ejecución ya no es necesario el adaptador FTDI. El siguiente gráfico muestra el conexionado del módulo ESP-01 en modo ejecución.


Cuando se desconecta el pin GPIO0, realmente lo que está haciendo es dejarlo a nivel alto (3.3 V), ya que dicho pin tiene internamente una resistencia de pull up que lo mantiene en ese nivel.

2.2.3 Librería ESP8266WiFi

Como han comprobado en el apartado de introducción sobre las comunicaciones web, se requiere el manejo de una serie protocolos que aseguren el entendimiento entre clientes y servidores. Esa labor resultaría complicada si no hubiera librerías que la facilitaran. Existen varias, por lo que ha sido necesario decantarse por una de ellas: la ESP8266WiFi de Ivan Grokhotkov. Antes de ejecutar cualquier programa en el ESP-01, deberán haberla cargado en el IDE de Arduino pulsando Programa → Incluir Librería → Gestionar Librerías.


Esta librería se utilizará tanto para la conexión con la red WiFi como en las comunicaciones web. En el primer caso, harán uso de la clase Wifi (representa la conexión de un dispositivo ESP8266 a la red Wifi), y en concreto, de los siguientes métodos de clase:

 

• begin(SSID, contraseña): inicia el proceso de conexión a la red Wifi identificada con el SSID y la contraseña pasados como argumento al metodo.

• status(): devuelve un código cuyo valor indica lo que está sucediendo con la conexión Wifi. Si este fuera 3 (contenido en la constante WL_CONNECTED) significaría que la conexión se ha realizado con éxito.

En lo que respecta a las comunicaciones web, como saben, están basadas en un modelo petición/respuesta, en el que un cliente realiza la petición a un servidor que la atiende y devuelve una respuesta. Ambos roles quedarán reflejados en las dos clases principales de la librería ESP8266WiFi.

En el paradigma de programación orientada a objetos, una clase es la representación abstracta de algo, en este caso, un cliente o un servidor web. Un objeto es una instancia concreta de una clase. Siguiendo con el mismo ejemplo, el cliente o el servidor web que se crea en cada programa.

La clase que representa a un cliente web se llama WiFiClient. En ella se destacan los siguientes métodos.

• connect(servidor, puerto). Conecta el cliente a un servidor por un puerto determinado. El argumento servidor puede ser una dirección IP o un URL. El valor de retorno indica éxito o fracaso.

• connected(). Devuelve true si el cliente está conectado a un servidor y false en caso contrario.

• available(). Devuelve el número de bytes disponibles para leer, es decir, la cantidad de datos que el servidor ha dado como respuesta al cliente.

• readStringUntil(carácter terminador). A partir de la respuesta del servidor, retorna la cadena de caracteres pendientes de leer hasta encontrar el que se le pasa como argumento.

• print(datos) o println(datos). Envía al servidor los datos pasados como argumento. Dichos datos pueden ser de tipo char, byte, int, long, or string.

• flush(). Desecha los datos enviados por el servidor que aún no han sido leídos.

• stop(). Desconecta al cliente del servidor.

Cualquier clase u objeto puede contener funciones que realicen determinadas tareas o comportamientos. En terminología orientada a objetos, a dichas funciones se las llama métodos. La forma de invocar un método es la siguiente:

objeto.método (argumentos)

clase.método (argumentos)

En el primer caso el método es de objeto, mientras que en el segundo es de clase. La diferencia entre ambos es que el código que se ejecuta cuando se invoca un método de clase es el mismo para todos los objetos. En los métodos de objeto, cada uno de ellos tiene su propio código (aunque las sentencias sean las mismas), por lo que la ejecución del método de un objeto es independiente de la del mismo método de otro objeto.

La clase que representa a un servidor web se llama WiFiServer. En ella sobresalen los siguientes métodos:

• begin(). Le indica al servidor que comience a escuchar conexiones entrantes.

• available(). Devuelve a un cliente que está conectado al servidor y tiene datos disponibles para leer. Si no hubiera ninguno, devolvería null.

2.2.4 Integración del ESP-01 con Arduino

La integración del ESP-01 con Arduino se realiza implementando un código (su estructura será la misma en ambos dispositivos) consistente en la lectura y escritura de datos en el puerto serie. A nivel físico, esto supondrá la conexión de los pines Rx y Tx del ESP-01 con los pines Tx y Rx de Arduino. Es imprescindible que ambos se conecten de forma cruzada, porque lo que se envíe desde el pin Tx del ESP-01 se debe recibir por el Rx de Arduino y viceversa.

Para ejemplarizar dicha integración, realizará un circuito en el que tanto el ESP-01 como Arduino tendrán conectado un pulsador y un led. Cuando se presione el pulsador de Arduino, se encenderá el led del ESP-01 y viceversa. El circuito empleado será el siguiente:


En dicho circuito, cada uno de los leds se conectan al ESP-01/Arduino mediante una resistencia de 220 Ω. Por otra parte, se ha puesto una resistencia de 4.7 KΩ entre GND y el pin del pulsador que va al ESP-01/Arduino para mantenerlo a nivel bajo en estado de reposo. Al presionarlo, éste pasará a nivel alto, ya que el otro pin del pulsador está conectado a VCC. La resistencia de 4.7 KΩ evita que se produzca un cortocircuito, ya que sin ella estaríamos conectando directamente VCC a GND.

Observen que el ESP-01 utiliza una fuente de alimentación propia debido a que Arduino no sería capaz de aportar la corriente que necesita.

Recuerden que, siempre que utilicen fuentes de alimentación diferentes, deberán conectar los polos GND de ambas para tener una referencia de tensión única en el circuito.

El programa que se ejecutaría en el ESP-01 sería el siguiente:

int pinPulsador = 0; int pinLed = 2; void setup(){ Serial.begin(9600); pinMode(pinPulsador, INPUT); pinMode(pinLed, OUTPUT); } void loop(){ if (Serial.available()) digitalWrite(pinLed, (int)Serial.read()); else digitalWrite(pinLed, LOW); if (digitalRead(pinPulsador)) Serial.print(HIGH); delay(10); }

Al principio del programa se declaran las variables que identifican los pines en los que se conectan el pulsador (pinPulsador) y el led (pinLed), que coinciden con los del circuito mostrado más arriba.

int pinPulsador = 0; int pinLed = 2;

En el bloque setup() únicamente se determina la velocidad de conexión entre el dispositivo Arduino y el ESP-01 (9600 baudios), y se configuran los pines del pulsador y el led como entrada y salida, respectivamente.

En el bloque loop() lo primero que se hace es verificar si Arduino ha enviado algún dato por el puerto serie. Si fuera así, lo leería y asignaría el valor leído al estado del led. Puesto que Arduino solo enviará datos cuando quiera encender el led, de no haber ninguno, se procederá a apagarlo.

if (Serial.available()) digitalWrite(pinLed, (int)Serial.read()); else digitalWrite(pinLed, LOW);

Arduino solo enviará el valor HIGH al ESP-01, por lo que podría pensarse en utilizar la siguiente sentencia en vez de la anterior:

if (Serial.available()) digitalWrite(pinLed, HIGH);

Sin embargo, siempre será necesario leer el puerto serie con el método read() para eliminar dicho dato del búfer de entrada y dejar sitio al siguiente.

A continuación se comprueba si se ha presionado el pulsador, en cuyo caso se enviaría por el puerto serie el valor HIGH para que Arduino encendiera el led que tiene conectado.

if (digitalRead(pinPulsador)) Serial.print(HIGH);

Puesto que el monitor serie utiliza, como su nombre indica, el puerto serie y este se halla ocupado por la integración con Arduino, no podrá mostrarse ningún tipo de información de trazas con comandos del estilo Serial.println().

El programa que deberán cargar en Arduino es muy similar.

int pinPulsador = 13; int pinLed = 12; void setup(){ Serial.begin(9600); pinMode(pinPulsador, INPUT); pinMode(pinLed, OUTPUT); } void loop(){ if (Serial.available()) digitalWrite(pinLed, (int)Serial.read()); else digitalWrite(pinLed, LOW); if (digitalRead(pinPulsador)) Serial.print(HIGH); delay(10); }

Lo único que cambia son los pines a los que está conectado el pulsador y el led. En el bloque setup() se debe prestar especial atención a que la velocidad del puerto serie sea la misma que la del ESP-01.

El bloque loop() es idéntico al de dicho dispositivo, ya que, al igual que este, únicamente escucha si se le ha enviado algún dato por el puerto serie, en cuyo caso encendería el led. De la misma manera, también comprobaría si se ha presionado el pulsador y, en caso afirmativo, enviaría al ESP-01 un valor alto para que encendiera el led que tiene conectado.

Puesto que la carga del programa se realiza por el puerto serie, no tengan nada conectado a los pines Rx y Tx de Arduino hasta no haber finalizado dicha carga. En una práctica posterior se enseñará el uso de la librería SoftwareSerial, que permitirá utilizar otros pines diferentes al Rx/Tx como puerto serie de entrada/salida. De esa forma, no solo podrán tener el circuito cableado en el momento de la carga del programa, sino que también podrán sacar por el monitor serie información de trazas.

2.2.5 Prácticas

Una vez que tienen configurado el IDE de Arduino para la programación del ESP-01, conocen la operativa para hacerlo y la librería necesaria para establecer comunicaciones HTTP, están en condiciones de realizar los dos elementos fundamentales de dicha comunicación: un cliente y un servidor web.

2.2.5.1 Cliente web

En esta práctica van a crear un cliente web que acceda cada 10 segundos a un servidor que proporcione su IP pública. El URL del servidor es www.vermiip.es. Si se conectaran a él desde un navegador web, este mostraría la siguiente página:


Ahora harán lo mismo desde un cliente web dentro de un programa que se ejecuta en el ESP-01, pero, en vez de mostrar la página HTML, analizarán su código para buscar el texto “Tu IP pública es: “, ya que la dirección IP que están buscando viene a continuación. Vean el resultado que obtendrían en el monitor serie de Arduino.


El código del programa ESP8266 es el siguiente:

#include <ESP8266WiFi.h> // SSID de la red WIFI a la que desea conectarse const char* ssid = “*********”; //contraseña de dicha red const char* password = “*********”; const char* servidorWeb = “www.vermiip.es”; void setup() { Serial.begin(115200); //Inicializa la conexión WIFI Serial.print(“Conectando a “ + String(ssid) + “ “); WiFi.begin(ssid, password); while (WiFi.status() != WL_CONNECTED) { delay(500); Serial.print(“.”); } Serial.println(“ Conectado”); } void loop() { WiFiClient clienteWeb; //Establece la conexión con el servidor if (clienteWeb.connect(servidorWeb, 80)) { //Realiza la petición de la página HTML clienteWeb.print(String(“GET /”) + “HTTP/1.1\n” + “Host: “ + servidorWeb + “\n” + “Connection: close\r\n” +“Connection: close\n\n”); //Lee la respuesta del servidor línea a línea while (clienteWeb.connected()) { if (clienteWeb.available()) { String linea = clienteWeb.readStringUntil(‘\n’); if(linea.indexOf(“<h2>Tu IP p&uacute;blica es: “) != -1) Serial.println(“Su IP pública es: “+ linea.substring(linea.indexOf(“<h2>Tu IP p&uacute;blica es: “)+28, (linea.indexOf(“</h2>”)))); } } //Finaliza la conexión con el servidor clienteWeb.stop(); } else Serial.println(“Problemas de conexión con el servidor”); delay(10000); }

Los tiempos de compilación y carga de programas en el ESP-01 son más altos que los de Arduino. Si ven que el proceso tarda algunos minutos, no significa que haya problemas en el IDE.

En primer lugar, deberán cargar la librería utilizada para gestionar las comunicaciones WiFi con el SoC ESP8266.

#include <ESP8266WiFi.h>

A continuación declararán las variables que contienen el nombre (SSID) y la contraseña de la red WiFi a la que van a conectar el ESP8266.

 

const char* ssid = “*********”; const char* password = “ *********”;

Si utilizan el código que acompaña al libro, no olviden actualizar los valores de dichas variables con los de su red WiFi.

Finalmente, declararán la variable con el URL del servidor web al que va a conectarse, en concreto www.vermiip.es.

const char* servidorWeb = “www.vermiip.es”;

En el bloque setup() establecerán la conexión con su red WiFi. Para ello, lo primero que harán será iniciar la conexión con el método begin() de la clase WiFi, cuyos argumentos son el nombre y la contraseña de la red a la que deben conectarse.

WiFi.begin(ssid, password);

A continuación tendrán que esperar a que se haya establecido dicha conexión y comprobar cada medio segundo en un bucle while si el método status() de la clase WiFi devuelve como resultado el valor de la constante WL_CONNECTED.

while (WiFi.status() != WL_CONNECTED) { delay(500); Serial.print(“.”); } Serial.println(“ Conectado”);

Ya dentro del bloque loop(), lo primero que harán es declarar dicho cliente (clienteWeb) como un objeto de la clase WiFiClient.

WiFiClient clienteWeb;

Para que dicho cliente pueda efectuar peticiones a un servidor, es necesario haber establecido previamente una conexión con él. Eso se consigue con el método connect() de dicho objeto, al que le pasarán como argumento el URL del servidor y el número del puerto por el que está escuchando (generalmente el 80). Como este método devolverá true cuando se haya establecido dicha conexión, las peticiones se realizarán dentro de la condición if.

if (clienteWeb.connect(servidorWeb, 80)){ … }

Para hacer las peticiones, utilizarán el comando print() del objeto clienteWeb, al que pasarán como argumento el mensaje de solicitud HTTP, que en este caso será de tipo GET.

clienteWeb.print(String(“GET /”) + “ HTTP/1.1\n” + “Host: “ + servidorWeb + “\n” + “Connection: close\r\n” +”Connection: close\n\n”);

La comunicación entre el cliente y el servidor se realiza utilizando mensajes HTTP cuya estructura se explicará más adelante, por lo que ahora limítense únicamente a realizar las peticiones tal como aparece en la sentencia anterior.

Una vez hecha la petición, ya solo queda esperar la respuesta del servidor web, que será una página HTML. Pero antes de leer el contenido de dicha página, deberán asegurarse de que sigue conectado con el método connected(), que devolverá true en caso afirmativo. También tendrán que confirmar que el servidor haya enviado algún dato, para lo que se utilizará el método available(), que devolverá el número de bytes de la respuesta obtenida. Si es mayor que cero, significará que ha recibido datos, por lo que la segunda condición será cierta y podrán empezar la lectura de dicha información.

while (clienteWeb.connected()) { if (clienteWeb.available()) { … } }

La lectura de la página web la realizarán línea a línea.

String linea = clienteWeb.readStringUntil(‘\n’);

Ahora, lo que tendrán que hacer es localizar su dirección IP pública en cada una de estas líneas. Para ello utilizarán una condición que busque el texto “(“<h2>Tu IP p&uacute;blica es: “ ya que su IP se encuentra entre dicho texto y la etiqueta HTML “</h2>”.

if(linea.indexOf(“<h2>Tu IP p&uacute;blica es: “) != -1) Serial.println(“Su IP p&uacute;blica es: “+ linea.substring(linea.indexOf(“<h2>Tu IP pública es: “)+28, linea.indexOf(“</h2>”)));

El número 28 que aparece en la sentencia de más arriba es la posición del carácter siguiente al del texto “<h2>Tu IP p&uacute;blica es: “ (empezando a contar desde 0).

Para entenderlo mejor, vean el código de la página HTML devuelta por el servidor.


Con una flecha se ha indicado la línea donde aparece el texto que busca en la condición para extraer su IP pública.

<h2>Tu IP p&uacute;blica es: xx.xx.xxx.xxx</h2>

La última sentencia relevante es la que finaliza la conexión con el servidor una vez leída la página HTML.

clienteWeb.stop();