IOT – Controlando LEDs a través de internet

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
//Esperar la respuesta de ">" envio y el "SEND OK" de envio correcto
//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, lo que hace  es mandar 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&amp><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 &lt;SoftwareSerial.h&gt;;

#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 &gt; 32 &amp;&amp; c &lt; 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",&amp;amp;amp;chId,&amp;amp;amp;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.

 

 

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

dieciocho − 8 =

Este sitio usa Akismet para reducir el spam. Aprende cómo se procesan los datos de tus comentarios.