Para comenzar este taller y entender el funcionamiento de las comunicaciones, nos podemos dirigir a un post anterior acerca de la teoría de redes.
También hay información de como utilizar el ESP8266, accediendo a un post anterior.
Vamos a realizar un nuevo taller relacionado con este módulo para el GAMAKER DAY.
El ejercicio va a consistir en encender y apagar los leds desde una página web construida con una placa Arduino.
Ésta página web gestionará los datos para recibir diferentes peticiones, en este caso la programaremos para encender y apagar los LEDs dentro de una red local.
Los pasos a hacer serán los siguientes.
Realizar el circuito.
Organizar nuestra aplicación.
Establecer la configuración del circuito mediante software.
Establecer comunicación mediante comandos AT (no usaremos librerías externas).
Más información aquí.
Crear la página web en el arduino.
Recibir los comandos desde la página web e interpretarlos.
1º Nuestro circuito
Lo primero es el montaje de los leds.
El sistema de montaje de los leds es por lógica negativa. Es decir se pone el ánodo (+) a común y serán los cátodos (-) los que se conecten a los pines del Arduino.
Para encender un led lo que tenemos que hacer poner a LOW el pin correspondiente al LED.
Este montaje no es el más adecuado para montar leds en serie, pero si es muy simple, si los leds son de distinta intensidad no los controlamos correctamente, es solo un circuito para hacer unas pruebas rápidas, y reduce un poco los costes, solo se usa una resistencia para todos ellos.
La lógica negativa hace que para encender un pin se deba poner LOW, por ello haremos algunas funciones para evitar errores.
void enciende(int pin) { digitalWrite(pin,LOW); } void apaga(int pin) { digitalWrite(pin,HIGH); }
Aunque en este caso, utilizaremos los botones de la página web como interruptores y pasaremos de un estado a otro.
void switchLed(int pin) {//cambiaremos el estado entre encendido y apagado // aunque el pinMode nos diga que es de salida, podemos leer su valor, y con "!" negamos su valor digitalWrite(pin,!digitalRead(pin)); }
Ahora vamos a emplear un modelo del ESP8266 -07 que dispone; además de la conexión serie; de una serie de GPIOs.
El modelo ESP8266-07 es el modelo más cómodo para pinchar en una placa de prototipado y realizar nuestros proyectos.
Este es el modelo comprado ESP8266 -07 con su placa.
Es una placa que no está definida en la aplicación fritzing, pero es muy fácil de colocar.
Vamos a utilizar el softwareSerial.h, que es una librería estándar que viene en la instalación de Arduino.
El softwareSerial, nos permite comunicar 2 pines de nuestro arduino como pines de recepción y transmisión de datos RX y TX basados en RS232, muy utilizado para comunicaciones entre dispositivos.
De éste modo nos quedan libres los pines 0(RX) y 1(TX) que son los empleados por el arduino para la comunicación con el ordenador.
Aquí podéis encontrar información sobre el softwareSerial y sus limitaciones
2º Organizar nuestro programa
El programa no es complejo, pero si largo, ya que tiene varios temas a tratar.
Lo que hacemos es dividir nuestro programa en varias librerías locales, lo que nos permitirá, tener el código separado según su funcionalidad, y tener más claro donde van las cosas.
Programa principal
Donde están las funciones de void setup() y void loop().
Este será el programa que inicie todo y llame a las funciones que están en librerías propias.
void setup()
{
//configurar los leds.
//configurar la conexión.
}
void loop()
{
//esperar la llegada de datos.
//interpretar la llegada de datos.
}
luces.h
Se encuentran las funciones y definiciones de los pines utilizados para el control de los leds.
wifi.h
Es la librería donde se encuentran las funciones para la conexión wifi.
Se utilizan los comandos AT directamente, para evitar sobrecargar de librerías, y no tener que instalar adicionales.
Aquí es donde se configura la SSID de la wifi y la password para conectarse.
También se debe configurar una IP distinta para cada módulo.
Puede que no proporcione correctamente los mensajes de respuesta entregando algo de basurilla pero el resultado es correcto.
webServer.h
Es básicamente donde se define la página web, en el arduino mediante comandos AT y el envío de datos creará una página web con 5 botones.
Y aquí está el código para bajárnoslo
3º Establecer la configuración de nuestro circuito (luces.h)
//Definimos la posición de los leds //Utilizamos defines ya que no ocupan espacio en la SRAM sino en la FLASH #define LED_BLANCO 8 #define LED_VERDE 9 #define LED_AMARILLO 10 #define LED_ROJO 11 #define LED_AZUL 12 void apagar(int pin) {//Al ser circuito con lógica negativa para apagarlo //se pone a HIGH el pin digitalWrite(pin,HIGH); } void configurarLuces() { //Estamos usando lógica negativa para la conexión de los LEDS pinMode(LED_BLANCO,OUTPUT); pinMode(LED_VERDE,OUTPUT); pinMode(LED_AMARILLO,OUTPUT); pinMode(LED_ROJO,OUTPUT); pinMode(LED_AZUL,OUTPUT); //Al ser circuito con lógica negativa  para apagarlo se pone en mod HIGH apagar(LED_BLANCO); apagar(LED_VERDE); apagar(LED_AMARILLO); apagar(LED_ROJO); apagar(LED_AZUL); } void switchLed(int pin) {//Conmuta el LED encendido/apagado digitalWrite(pin,!digitalRead(pin)); }
4º Establecer comunicación. wifi.h
//Comandos at para la conexión de la wifi //hacemos un listado de las órdenes AT que se le pasarán por serie al módulo esp8266 //Hay que cambiar la IP para cada módulo String comandosAT[]={ "AT", "AT+CWMODE=1", "AT+CWJAP=\"mi_ssid\",\"mi_password\"", "AT+CIPSTA=\"192.168.1.30\",\"191.168.1.1.\",\"255.255.255.0\"",//Poner la IP en el dispositivo "AT+CIFSR",//Comprobar la IP "AT+CIPMUX=1",//Poner en modo servidor "AT+CIPSERVER=1,80",//Crear el servidor "AT+CIPSTO=5",//Timeout "END" }; //La función de configuración envía los comandos AT según las ordenes //y también espera un timeout, por si hubiera problemas int configurarWifi(String *ordenes,long timeOut) { int i=0; while (ordenes[i]!="END")//Leemos las ordenes hasta llegar al  comando 'END' que no es AT sino de control { rx_empty();//función para vaciar de basura el buffer serie wifiSerial.println(ordenes[i++]);//envía la orden y hace un postincremento de la orden long flag=millis();//cogemos una marca de tiempo while (true)//bucle infinito muy peligroso { String s= getLineWifi();//Recoge el comando recibido del  módulo if (s!="")Serial.println(s); if (s.startsWith("no change")) {break;}//si no hay cambio es correcto y salimos del bucle if (s.startsWith("OK")){break;}//da señal correcta y salimos del bucle if (s.startsWith("ready")){break;}//da valor correcto y salimos del bucle if (s.startsWith("+CIFSR")){break;}//da la ip y salimos del bucle if ((millis() - flag) > timeOut) //control del timeout. se pasa el tiempo de espera y salimos del bucle { dbg.println("TIEMPO"); break; } }//fin del bucle while(true) rx_empty(); dbg.println(".........................."); } }
5º Crear página web en un arduino. webserver.h
La creación de una página web en Arduino, no es muy eficiente ni recomendable, ya que los navegadores mandan mucha información que puede llegar a colapsar la memoria de nuestra placa, pero podemos utilizarlo para aplicaciones sencillas.
Esta es la respuesta al recibir la conexión desde un navegador como firefox, que nos hace dos peticiones:
una para los datos donde nos ha mandado 320bytes
otra de 301bytes para pedir el favicon.ico, del que evidentemente no disponemos,.
Debemos controlar la memoria de estas comunicaciones para evitar saturar los; apenas; 883 bytes que nos han quedado libres al hacer el programa.
+IPD,0,320:GET / HTTP/1.1
ID:0:160
STR_AUX:+IPD,0,320:GET / HTTP/1.1
+IPD,0,301:GET /favicon.ico HTTP/1.1
ID:0:535
STR_AUX:+IPD,0,301:GET /favicon.ico HTTP/1.1
//Enviar una instruccion http para generar nuestra página //El envio se hace mediante el comando AT+CIPSEND = id_conexion,longitud_de_datos_a_enviar //estos últimos dan un warning por un usar una comparación desaconsejada void http(int ch_id, String output) { wifiSerial.print("AT+CIPSEND=" + String(ch_id) + ","); wifiSerial.println(output.length()); if (wifiSerial.find(">")) { wifiSerial.println(output); delay(10); while (wifiSerial.available() > 0) { if (wifiSerial.find("SEND OK"))break; } } else { dbg.println("no escrito"); } } //Muestra la página web y manda la información para montar la página web void webServer(int ch_id) {//Nos crea el documento html http(ch_id, "<!DOCTYPE HTML>"); http(ch_id, "<html>"); http(ch_id, "<body><H1>Taller IOT- GAMAKER 2016</H1>"); http(ch_id, "<head><title>www.zaragozamakerspace.com</title>"); http(ch_id, "<form action=\"blanco\">");//definición del formulario http(ch_id, "<input type=\"submit\" value=\"blanco\">");//creación de un botón con valor blanco http(ch_id, "</form>"); http(ch_id, "<form action=\"verde\">"); http(ch_id, "<input type=\"submit\" value=\"verde\">"); http(ch_id, "</form>"); http(ch_id, "<form action=\"amarillo\">"); http(ch_id, "<input type=\"submit\" value=\"amarillo\">"); http(ch_id, "</form>"); http(ch_id, "<form action=\"rojo\">"); http(ch_id, "<input type=\"submit\" value=\"rojo\">"); http(ch_id, "</form>"); http(ch_id, "<form action=\"azul\">"); http(ch_id, "<input type=\"submit\" value=\"azul\">"); http(ch_id, "</form>"); http(ch_id, "</body>");//Cerrar la página delay(1); wifiSerial.println("AT+CIPCLOSE=" + String(ch_id));//cerrar la conexión para evitar dejar el wifi bloqueado y a la espera de timeout }
6º Gestión de las comunicaciones. Programa principal
El programa principal lo que debe hacer.
- Cargar las libreríassoftwareSerial.h
- Librería estándar para emular puerto serie con pines.
- Definir los pines de comunicación.
- Instanciar el objeto softwareSerial wifiSerial(RXPIN,TXPIN).
- Cargar las librerías locales.wifi.h, webserver.h, luces.h
- void setup()Inicializar los puertos serie.
- configurar las luces.
- configurar la wifi.
- Variables globales para la comunicación (se podría mejorar haciéndolas locales y pasando los datos como parámetros, pero hemos simplificado).
- void loop()Comprobar si hay datos disponibles.
- Comprobar el id de conexión , el ESP8266 admite hasta 5 conexiones simultaneas.
- Recibir la información de la pulsación.+IPD,0,357:GET /blanco HTTP/1.1
ID:0:1280
STR_AUX:+IPD,0,357:GET /blanco HTTP/1.1 - Interpretar la orden recibida.
- Servir la página web
#include <SoftwareSerial.h> #define RXPIN 2 #define TXPIN 3 #define LEDPIN 13 #define dbg Serial SoftwareSerial wifiSerial(RXPIN,TXPIN); #include "wifi.h" #include "webServer.h" #include "luces.h" void setup() { Serial.begin(9600); wifiSerial.begin(9600); configurarWifi(comandosAT,10000); configurarLuces(); } #define BUFFER_SIZE 64 char buffer[BUFFER_SIZE]={}; bool cargar = true; void loop() { int8_t chId; int16_t packetLen; char c; int i=0; float tiempo; //comprobamos si hay datos disponibles if (wifiSerial.available()) { //Leemos en modo carácter c= wifiSerial.read(); while(c!='\n')//mientras el caracter no sea un final de carro { if (c > 32 && c < 126)//El carácter ASCII esta entre el "espacio" y la "~" { buffer[i]=c;//lo añadimos a nuestros datos recibidos i++; } c=wifiSerial.read();//continuamos leyendo } buffer[i]='\0';//añadimos un cero terminador a nuestra cadena Serial.println(buffer);//Nos muestra el buffer recibido } if (strncmp(buffer,"+IPD,",5)==0)//Comprobamos que el mensaje recibido que nos interesa es el de la página web { sscanf(buffer+5,"%dm%d", &chId, &packetLen);//Sacamos el identificador de mensaje Serial.println("ID:"+String(chId)+":"+String(packetLen)); //Mostramos la comunicación y el tamaño String strAux=String(buffer); Serial.println("STR_AUX:"+strAux); if (strAux.indexOf("blanco")!=-1) { switchLed(LED_BLANCO); //Enciende o apaga } if (strAux.indexOf("verde")!=-1) { switchLed(LED_VERDE); } if (strAux.indexOf("amarillo")!=-1) { switchLed(LED_AMARILLO); } if (strAux.indexOf("rojo")!=-1) { switchLed(LED_ROJO); } if (strAux.indexOf("azul")!=-1) { switchLed(LED_AZUL); } rx_empty();//vacia el buffer de datos que no, nos interesan webServer(chId); //muestra la página web. } }
Una vez compilado nos podemos hacer una idea del tamaño de nuestra aplicación.
11.700bytes de memoria flash, disponemos de unos 32 kB.
1.175 bytes de memoria SRAM, disponemos de 2048 bytes.
El Sketch usa 11.700 bytes (36%) del espacio de almacenamiento de programa. El máximo es 32.256 bytes.
Las variables Globales usan 1.175 bytes (57%) de la memoria dinámica, dejando 873 bytes para las variables locales. El máximo es 2.048 bytes.
Buenas tardes
Me ha gustado mucho tu artículo y me gustaría saber si los leds se podrían encender y apagar desde cualquier lugar del mundo, eso siempre con una conexión a Internet. Lo que no se como configurar en el router pues supongo que habrá que abrir algún puerto.
Espero me ayudes
Un saludo
Buenas, hoy en día se puede hacer facilmente con un servidor MQTT.
Puedes instalartelo tú mismo, pero existen páginas que ofrecen este servicio gratuitamente para conectar a través de Internet tus dispositivos, solamente hace falta registrarse.
https://io.adafruit.com
Desde la página de IFTTT, también tienes disponibles otros tipos de comunicación para extender el uso de tus dispositivos IOT.