domingo, 25 de noviembre de 2018

Manejo de potencia con TRIAC

Hola a todos, hoy les traigo sobre el manejo de potencia con un triac. Esto lo vamos a hacer con los componentes bt137 y moc3021. El bt137 es el triac y el encargado de manejar la potencia, para este ejemplo (y para cuidar al componente) vamos a utilizar disipador.

En cuanto al moc3021 es el que se va a encargar de disparar el triac. Este componente aisla la parte de potencia con la de control (está optoacoplado) y nos permite operar de forma "segura" con un microcontrolador. Básicamente el moc3021 es un diac controlado por luz, un "fotodiac".

El circuito que vamos a utilizar es el siguiente:



Este circuito nos va a permitir manejar potencia desde un microcontrolador, y variando el pulso de disparo antes o después vamos a poder variar en qué momento comienza a conducir el triac y por lo tanto cuánta potencia entrega. Si queremos utilizarlo sin disipador hay que tener en cuenta cuánta corriente vamos a controlar, por ejemplo, si queremos utilizarlo con una carga de 10W, vamos a tener que:

10W / 220v = 0,0455A

Y como la caída de tensión sobre el componente es de 1,65v constantes, entonces vamos a tener:

1,65v * 0,0455A = 0,0751W

O lo que es lo mismo 75mW. Para ese tipo de encapsulado (TO220) la potencia máxima que puede disipar es de 2,5W que nos da una corriente máxima para este triac de:

2,5W / 1.65v = 1,52 A

Pero lo más seguro es que si hacemos conducir 1,52A, sin ningún tipo de disipador, el componente se queme de inmediato (y en caso de que no se queme, recomiendo no tocarlo). Como regla general hay que tomar la mitad (o incluso un tercio) del valor máximo como el valor a utilizar, es decir que si este componente puede disipar hasta 2,5W entonces como máximo deberíamos tomar entre 0,8W y 1,25W (yo usaría el 0,8W como máximo), a continuación les dejo una tabla aproximada de las potencias máximas que pueden disipar los distintos encapsulados:


TO5/TO32 0,8W
TO202 1,7W
TO220 2,5W
TO3            3,5W

Estos valores son muchas veces para casos ideales, por lo que yo tomaría los siguientes valores (para usar sin disipador)

TO5/TO32   0,3W
TO202 0.65W
TO220 0.8W
TO3            1.2W

Esa es la máxima potencia que puede disipar cada tipo de encapsulado y para tener una idea aproximada de como dimensionar nuestros circuitos (si es necesario que lleven disipador o no). Luego el próximo dato que hay que ver es cuál es la máxima corriente que es capaz de conducir el modelo de nuestro componente, para el caso del bt137 es de 8A y dependiendo del modelo va a poder conducir hasta un máximo de 600v, por lo que si queremos hacer pasar eso, nuestro componente va a tener que disipar:

1.65v * 8A = 13.2W (el 1,65v es la tensión que cae en el componente)

que es casi 17 veces más de lo que está preparado el encapsulado, por lo que hay que usar un disipador.

domingo, 18 de noviembre de 2018

estación meteorológica con base de datos

Hoy les traigo como hacer que la estación meteorológica almacene los datos con una base de datos.

Esto va a ser muy similar a lo ya implementado con el ejemplo de la base de datos, pero la idea es tener los siguientes campos:

temperatura
humedad
lux
hora
fecha
localización

Estos datos nos van a permitir discriminar la ubicación de las lecturas. Entonces vamos a crear una nueva base de datos, con la ubicación en donde querramos y vamos a poner el nodo inject con lo siguiente en el tópico

CREATE TABLE clima(temperatura NUMERIC, humedad NUMERIC, lux NUMERIC, hora TIME, fecha DATE, localizacion TEXT)

Una vez creada la base de datos ese módulo de injección ya no nos sirve para nada, por lo que podemos eliminar el nodo (sólo para no tener cosas inútiles en la pantalla). Luego vamos a poner un nodo de función en el cuál vamos a poner el siguiente código (para que funcione de la mejor forma posible vamos a modificar el código del esp para que mande todos los mensajes con el formato: temperatura&&&humedad&&&lux&&&localizacion, el dato de la localización lo vamos a embeber en el código de forma manual (sin gps) para no complicar las cosas, quizá algún día hagamos algo similar):


var mensaje = msg.payload.split("&&&");
if((mensaje[0] !== "nan") && (mensaje[1] !== "nan") && (mensaje[2] !== "nan"))
{
    msg.payload = null;
    msg.topic = "INSERT INTO clima(temperatura, humedad, lux) VALUES(" + mensaje[0] + "," + mensaje[1] + "," + mensaje[2] + ")";
    return msg
}
return null;



Que nos va a permitir administrar la información y generar el comando necesario para poder guardarlo con la base de datos, y la segunda parte nos va a permitir graficar todos los datos en un mismo gráfico (queda muy bien, además se van a poder comparar los datos más directamente).


En la imágen está el flujo de ejemplo (en realidad se podría usar, y en lugar del nodo de inyección habría que poner un nodo de MQTT que nos entregue los datos de los sensores), en la cuál concentramos todos los datos en una sola gráfica, y realizando ciertos ajustes podríamos ver qué datos representa cada color o incluso cambiar el color de cada dato.

Para completar el proyecto habría que modificar el código de nuestro micro para que nos mande los lúmenes y los datos de la ubicación. Como no vamos a agregarle datos de ubicación reales (todavía no me meto en el mundo del gps), solo vamos a utilizar identificadores del estilo "casa" o alguna otra cosa. En cuanto a los lúmenes, sólo hay que realizar una lectura analógica siguiendo esta librería. El código final es el siguiente:

/*
 * Este programa es una estación meteorológica que verifica los niveles de 
 * temperatura y humedad del ambiente con el sensor DTH11. Se alimenta con 
 * una celda fotovoltáica, y posee un cargador de baterías y alimentación 
 * para el equipo.
 */


#include <ESP8266WiFi.h>
#include <PubSubClient.h>

//---------------------------------------------------------------------------

#include "DHT.h"

#define DHTPIN            5         // Pin de datos
#define DHTTYPE           DHT11     // DHT 11

// El tercer parámetro va en función de la velocidad
// del microprocesador, cuanto más rápido sea, más 
// grande el valor (6 es para un micro de 16Mhz).

DHT dht(DHTPIN, DHTTYPE, 11);

//---------------------------------------------------------------------------

// verificar el ejemplo de la librería para saber como 
//conectar la resistencia.
// quizá lo mejor sea poner una resistencia de 10k.
#include <LightDependentResistor.h>

#define OTHER_RESISTOR 3300 //ohms
#define USED_PIN A0
#define USED_PHOTOCELL LightDependentResistor::GL5528

// Create a GL5528 photocell instance (on A0 pin)
LightDependentResistor photocell(USED_PIN, OTHER_RESISTOR, USED_PHOTOCELL);

//---------------------------------------------------------------------------

// Las siguientes tres constantes definen, nombre
// de red wifi y su contraseña.

const char* ssid = "NombreRed";
const char* password = "Contraseña";
const char* mqtt_server = "DirecciónServidor";

// Acá definimos los pines.

const int d1 = 5, d2 = 4, d3 = 0, pin_led = 2;

long tiempo = 0;

// A continuación declaramos el nombre del cliente 
// que se va a conectar.

WiFiClient espClient;
PubSubClient client(espClient);

// En esta variable se almacena el mensaje recibido
// notar que tiene un máximo de 50 (en realidad son
// 49) caracteres.

char msg[50];

void setup() {
  pinMode(pin_led, OUTPUT);   //Pin del LED 2
  digitalWrite(pin_led, LOW);
  pinMode(d1, INPUT);         //Pin D1
  pinMode(d2, INPUT);         //Pin D2
  pinMode(d3, INPUT);         //Pin D3
  
  Serial.begin(115200);
  setup_wifi();
  client.setServer(mqtt_server, 1883);
  client.setCallback(callback);

  dht.begin();
}

void setup_wifi() {

    Serial.println();
    Serial.print("Conectando a: ");
    Serial.println(ssid);
  
    WiFi.begin(ssid, password);

    while (WiFi.status() != WL_CONNECTED) 
    {
      delay(500);
      Serial.print(".");
    }
  
    digitalWrite(pin_led, HIGH);
  
    Serial.println("");
    Serial.println("WiFi conectado");
    Serial.println("IP: ");
    Serial.println(WiFi.localIP());

}

// En esta función se administra la llegada de 
// mensajes, nos avisa en qué tópico llegó y 
// cuál es el mensaje.

void callback(char* topic, byte* payload, unsigned int length) {
  Serial.print("Mensaje recibido [");
  Serial.print(topic);
  Serial.print("] ");

  if ((char)payload[0] == 'O') {  // si recibo como primer caracter una O
    if ((char)payload[1] == 'N'){  // si recibo como segundo caracter una N
      Serial.println("El boton esta apagado"); 
      digitalWrite(pin_led, LOW);
    } 
    else {
      Serial.println("El boton esta encendido");  
      digitalWrite(pin_led, HIGH);
    }
  }
}

// Esta función se encarga de conectar con el 
// mqtt broker, si lo consigue se subscribe a
// los tópicos que definamos (no es necesario
// subscribirnos en esta función, pero si lo
// recomiendo para organizar mejor el código.
// Prueba reconectarse cada cinco segundos si
// no lo consigue.

void reconnect() {
  // Loop hasta lograr la conexión
  while (!client.connected()) {
    Serial.print("Intentando conectar al servicio MQTT...");
    
    if (client.connect("ESP8266Client")) {
      Serial.println("Conectado!");
      client.publish("temperatura", "0");    //envío dato
      client.publish("humedad", "0");    //envío dato
    } 
    else 
    {
      Serial.print("Fallo, Resultado=");
      Serial.print(client.state());
      Serial.println("Intetando nuevamente en 5 segundos");
      delay(5000);
    }
  }
}

// En el loop principal no hacemos nada más 
// que verificar si perdimos la conexión con
// el servicio mqtt, que lleguen mensajes y
// actualizamos la temperatura y humedad. 
// El "client.loop();" se encarga de revisar 
// si llegaron mensajes, por lo que habría
// que invocar esta función regularmente.

void loop() {

  if (WiFi.status() != WL_CONNECTED)
  {
    setup_wifi();
  }
  if (!client.connected()) 
  {
    reconnect();
  }
  
  //-------------------------------------------------------
  if ((millis() - tiempo) > 2500)
  {
    tiempo = millis();
    float humedad = dht.readHumidity();
    float temperatura = dht.readTemperature();
    float lux = photocell.getCurrentLux();
    client.publish("estacion/datos", (String(temperatura) + "&&&" + String(humedad) + "&&&" + String(lux)).c_str(),true);
    Serial.print(temperatura);
    Serial.print(" ");
    Serial.print(humedad);
    Serial.print(" ");
    Serial.println(lux);
  }
  //-------------------------------------------------------
}


De esta forma sólo obtenemos los valores sin promediar (lo cual significa que hay muchísimos más datos que de otra forma) por lo que si no disponemos de mucha memoria ram (como es en el caso de la raspberry pi) habría que realizar un promedio cada cierta cantidad de horas, como es el caso de la gráfica de valores históricos.

De esta forma podemos mostrar los valores históricos de los tres sensores (con lo cuál se podrían hacer análisis de las variaciones de temperatura, humedad y luz a lo largo de un año).

A continuación les dejo el código del flujo de la nueva estación meteorológica (sin los valores promedio):

https://mega.nz/#!7kIjGCqB!4Mvq78SucQepGiCdXp9osuNRZIeh3hODLgFcOVFkybk

Acá les dejo el código del flujo de la parte que maneja los valores históricos (con promedio):

https://mega.nz/#!7pRzkQAQ!GKUfib8z-Z7judA3EvsLSXDgjk5Gse-wYoIhKtz5-II

El nuevo código para el micro lo tienen arriba así que no lo subo a mega.

domingo, 11 de noviembre de 2018

base de datos para node red (guardar tus datos)

Hola, hoy les traigo algo que me tomó su tiempo encontrar (y lo encontré todo fragmentado) así que me pareció bueno compartirlo, sobre todo si se almacenan los datos de los sensores.

La base de datos que vamos a aplicar en el node red se llama SQLite, y lo vamos a buscar como "node-red-node-sqlite". Una vez que se termine de instalar nos va a aparecer el siguiente nodo:


Para poder usar este nodo vamos a mandarle mensajes en la entrada, pero en lugar de enviarlos por msg.payload como de costumbre, vamos a hacerlo a través del tópico (msg.topic).
Lo que voy a explicarles es como crear tablas nuevas, insertar valores, modificar esos valores, seleccionarlos, borrarlos, agregar nuevas columnas a la tabla y borrar por completo a la tabla.
Para todo esto vamos a usar un único ejemplo, por lo que siempre nos vamos a referir a la misma. El ejemplo va a ser una tabla con los datos del clima que podríamos obtener de nuestra estación meteorológica.

Crear una tabla

Para crear una tabla, el comando es "CREATE TABLE", luego hay que continuar con el nombre de la tabla, para nuestro ejemplo lo vamos a llamar "clima", luego vamos a poner, entre paréntesis, los nombres de las variables que se van a usar seguido por el tipo de variable, si tiene auto incremento y el valor por defecto, el auto incremento y el valor por defecto son opcionales.
Para continuar con nuestro ejemplo, la tabla se va a llamar "clima" y va a tener las variables id, temperatura y humedad, y nos va a quedar de la siguiente forma:

CREATE TABLE clima(id INTEGER PRIMARY KEY AUTOINCREMENT, temperatura NUMERIC, humedad NUMERIC)

Entonces, vamos a tener la variable "id" que va a ser del tipo entero (INTEGER) y que se va a incrementar sola cada vez que se agregue una nueva fila a la tabla (PRIMARY KEY AUTOINCREMENT), luego vamos a tener la variable "temperatura" que va a ser del tipo numérico y la variable "humedad" que al igual que la anterior también va a ser numérica.

Insertar nuevos valores en la tabla

El ingreso de nuevos datos en la tabla se va a realizar mediante el comando "INSERT INTO", luego hay que indicar el nombre de la tabla al que le queremos ingresar los nuevos datos, en nuestro caso "clima" y entre paréntesis las variables que querramos editar, cerramos paréntesis y se pone "VALUES" y entre paréntesis se ponen los valores que queremos asignarlo a cada variable (el orden importa mucho).
Para nuestro ejemplo supongamos que queremos agregar los datos de temperatura en 24,5º y humedad en 48%, entonces la línea a enviar sería la siguiente:

INSERT INTO clima(temperatura, humedad) VALUES(24.5, 48)

Acá hay que aclarar dos cosas, lo primero es que los valores numéricos no pueden llevar símbolos como º o %, sólo el valor numérico, y segundo, se usa el punto en lugar de la coma para los valores decimales.
Finalmente el valor de id, para este caso, se completa solo, si hubiesemos declarado un valor por defecto, se hubiese asignado sólo.


Modificar los valores ya ingresados

No siempre los valores ingresados se mantienen constantes, si nuestra tabla estuviese compuesta por nombres de personas y sus estados civiles, este último podría estar sujeto a cambios en función de (justamente) su estado civil. Para llevarlo a nuestro ejemplo, supongamos que el valor que se ingresó en la variable de la temperatura es incorrecto, entonces lo que vamos a hacer es editarlo. Esto lo vamos a conseguir con el comando "UPDATE", luego le vamos a decir el nombre de la tabla, para nuestro caso sería "clima", y le vamos a indicar cambie el valor de la variable temperatura con el comando "SET" seguido del nombre de la variable "temperatura", y para asignarle el valor vamos a poner un igual y el valor, ahora es cuando el valor de id entra en juego, porque nos va a ayudar a identificar en cuál de todas nuestras filas queremos hacer la modificación, como nosotros sabemos que tenemos una sola fila, el valor del id va a ser 1, entonces vamos a terminar nuestra sentencia con "WHERE id = 1", quedando de la siguiente forma:

UPDATE clima SET temperatura = 25 WHERE id = 1

Como dato, el autoincremento de una variable siempre comienza en 1.


Seleccionar los valores (mostrarlos)

Para poder acceder a los valores guardados en la tabla lo vamos a hacer a través del siguiente comando:

SELECT * FROM clima

en donde el asterisco (*) va a indicar que queremos recuperar todas las variables de la tabla de todas las filas, en caso de que querramos obtener solamente el id, o la temperatura, debemos ingresar el nombre de la variable en lugar del asterisco, y si queremos recuperar más de una variable, hay que separarlas con comas, ejemplo:

SELECT id FROM clima

Esto nos va a devolver todas las filas que tienen la variable id. Ahora si hacemos lo siguiente:

SELECT id, temperatura FROM clima

obtendremos tanto la variable "id" como "temperatura". Todo esto siempre se devuelve como un arreglo de objetos, es decir que el formato de la variable va a ser el siguiente:

msg.payload[0].temperatura;

En la linea de arriba accedemos a la variable temperatura del índice 0 del arreglo que se encuentra almacenado en la variable msg.payload. Al final de esta entrada les voy a dejar un ejemplo para que puedan exportar con las explicaciones necesarias.

Borrar filas

Antes que nada, voy a avisar que este comando no se puede deshacer, una vez que lo ejecutemos habremos perdido lo borrado, dependiendo del caso, quizá lo mejor sea no usarlo y guardar el índice o agregar una variable para saber qué índices no se quieren usar.

Supongamos que nuestra tabla está de la siguiente manera:


Para borrar una fila completa (es decir, todas las variables de un cierto índice) el comando que vamos a utilizar es:

DELETE from clima WHERE id = 7

Y nuestra tabla va a quedar así:


Como podemos apreciar, el comando es "DELETE from" luego indicamos el nombre de la tabla, seguido de "WHERE" y la variable con el valor que querramos buscar, en el ejemplo de arriba se va a borrar de la tabla clima la fila que contenga el valor 7 en la variable "id", es decir que si ponemos lo siguiente:

DELETE from clima WHERE temperatura = 23

Se van a borrar todas las filas cuya variable "temperatura" tenga un valor de 23, si parece confuso a continuación está la tabla actualizada:


Agregar nuevas columnas a la tabla

Para agregar columnas hay dos caminos, uno es más simple que el otro (esto me costó encontrarlo, al menos para mi que no sé (o no sabía) nada sobre bases de datos, y quizá sea la razón por la cuál estoy escribiendo esta entrada), el primero (el más simple) consiste en agregar una columna (hablando mal y pronto, una nueva variable) nueva al final de todas las demás y siguiendo el ejemplo de la base de datos del clima, supongamos que querramos ingresar la cantidad de luz medida en luxes (INGRESAR LINK DE LUXES), lo vamos a hacer bajo el comando:

ALTER TABLE clima ADD COLUMN luz NUMERIC

En donde el comando es "ALTER TABLE", seguido por el nombre de la table, en nuestro caso "clima" y luego le indicamos que queremos agregar una nueva columna agregarndo "ADD COLUMN" seguido del nombre de la misma y del tipo de variable (en nuestro caso el nombre sería "luz" y el tipo "NUMERIC", es decir, numérica). Luego de ingresar ese comando la tabla nos va a quedar así:


Opcionalmente si queremos agregar un valor por defecto, debemos poner, al final de la sentencia, DEFAULT y seguido por el valor, en el siguiente ejemplo vamos a asignarle el valor 1:

ALTER TABLE clima ADD COLUMN luz NUMERIC DEFAULT 1

Y quedaría así:


La segunda opción es la más compleja, porque debemos hacer varios pasos que involucran renombrar, crear, insertar y borrar tablas (este último lo voy a explicar al final de la entrada), pero nos permite agregar la nueva columna en donde querramos, lo que nos da una mayor prolijidad. Primero vamos a renombrar nuestra tabla con la línea:

ALTER TABLE clima RENAME TO TemporalClimaViejo

Como se puede apreciar utilizamos el "ALTER TABLE" pero esta vez lo que hacemos es cambiar el nombre mediante el comando "RENAME TO" seguido del nuevo nombre, en este caso "TemporalClimaViejo", luego vamos crear una nueva tabla con la variable "luz" en la posición que deseemos, en nuetro caso entre el "id" y "temperatura":

CREATE TABLE clima(id INTEGER PRIMARY KEY AUTOINCREMENT, luz NUMERIC, temperatura NUMERIC, humedad NUMERIC)

Luego lo que vamos a hacer es copiar los viejos datos en la nueva tabla:

INSERT INTO clima (id, temperatura, humedad) SELECT id, temperatura, humedad FROM TemporalClimaViejo

Acá utilizamos el comando "INSERT INTO" para asignar valores de "id", "temperatura" y "humedad" de la tabla "clima" (recordemos que esta no es la tabla clima original, la original la renombramos y luego creamos esta), desde la tabla TemporalClimaViejo. Quedando:


Hay que recordar que el órden que indicamos en dónde vamos a guardar y de dónde vamos a leer las variables importa, no es lo mismo:

INSERT INTO clima (id, temperatura, humedad) SELECT id, temperatura, humedad FROM TemporalClimaViejo

que:

INSERT INTO clima (id, temperatura, humedad) SELECT temperatura, id, humedad FROM TemporalClimaViejo

o:

INSERT INTO clima (temperatura, id, humedad) SELECT id, temperatura, humedad FROM TemporalClimaViejo

En el primer caso se van a realizar las siguientes asignaciones:
id = id
temperatura = temperatura
humedad = humedad

En el segundo:
id = temperatura
temperatura = id
humedad = humedad

Y en el tercero:

temperatura = id
id = temperatura
humedad = humedad

Podemos ver que el segundo y tercer caso tienen el mismo resultado, pero lo puse porque vale la pena mencionarlo. Gráficamente va a quedar así:


Finalmente lo que vamos a realizar es borrar la tabla vieja mediante el comando:

DROP TABLE TemporalClimaViejo

Esta opción es la más prolija porque nos permite editar la posición de las variables dentro de la tabla, aunque no siempre es necesario porque si queremos agregar la variable al final de todas las columnas simplemente utilizamos a primer opción.

Borrar por completo a la tabla

Antes que nada voy a avisar que este comando sirve para borrar la tabla completa, con todos sus datos y es irreversible, mi recomendación es que se renombre la lista para no perder los datos.
Si bien este punto en cierta medida ya lo vimos, lo voy a explicar un poco más en profundidad. El comando es:

DROP TABLE clima

En donde la parte "DROP TABLE" le indica a la base de datos que va a tener que borrar una tabla completa, y luego le sigue el nombre de la tabla, en nuestro caso "clima".


Como podemos apreciar en la imágen, esa es la estructura más básica para utilizar la base de datos, en los nodos de inyección tenemos los comandos mencionados más arriba, estos nodos pueden reemplazarse por un nodo de función para hacerlo más interesante. Lo primero que vamos a hacer es hacer doble click sobre el nodo de sqlite y vamos a crear una nueva base de datos en la ruta que querramos, luego vamos mandar los datos contenidos por el nodo de inyección llamado "CREATE" que va a crear la tabla propiamente dicha, y luego se puede jugar con los siguientes comandos, como podremos observar los resultados salen por la pestaña de debug, por lo que podremos ver qué está sucediendo.



Sé que esta entrada se hizo un poco larga, pero me parece que el tema daba para tanto (o incluso más), espero que le saquen provecho.

domingo, 4 de noviembre de 2018

Servidor web simple con ESP8266

Hola nuevamente, en esta publicación voy a explicar cómo hacer un servidor web muy simple que sólo muestra texto estático (mejor dicho html), si bien esto es básico, creo que hay que empezar por algo.
Aclaro, no voy a explicar cómo hacer páginas web, sólo voy a explicar cómo montar una en el micro ESP8266, para aprender pueden ir aquí.

Las librerías que vamos a usar son la "ESP8266WebServer", viene con las dependencias del ESP8266 para arduino por lo que no hace falta instalarlo aparte, "DNSServer" que sirve para administrar las conexiones de los clientes y la "ESP8266WiFi" que se encarga de configurar el micro para que funcione correctamente.


#include <ESP8266WiFi.h>
#include <DNSServer.h>
#include <ESP8266WebServer.h>

ESP8266WebServer servidorweb(80);

const char* red = "red"/*ingrese aquí el nombre de id*/;
const char* contra = "contra"/*ingrese aquí la contraseña*/;
String pagina = 

  "<!DOCTYPEhtml>"
  "<html>"
    "<head>"
      "<title>"
        "Tutorial de servidor web"
      "</title>"
    "</head>"
    "<h3>"
      "Esto es un servidor web (de ejemplo)"
      " con el micro ESP8266"
    "</h3>"
  "<html>";

void setup() {

  Serial.begin(115200);

  WiFi.begin(red, contra);

  while (WiFi.status() != WL_CONNECTED) 
  {
    delay(500);
    Serial.print(".");
  }
  
  Serial.println("");
  Serial.print("Conectado a: ");
  Serial.println(red);
  Serial.print("Direccion IP: ");
  Serial.println(WiFi.localIP());
  
  servidorweb.on("/", []()
  {
    servidorweb.send(200, "text/html", pagina);
  });

  servidorweb.begin();

}

void loop() {
  
  servidorweb.handleClient();
}


Como podemos ver se debe declarar el objeto:

ESP8266WebServer servidorweb(80);

y es el que vamos a utilizar cuando hagamos referencia a la librería. Luego se define el nombre de la red a la que nos vamos a conectar, la contraseña y por último definimos lo que va a ser la página web en sí.
Como podemos observar la página está escrita de una forma poco convencional:

String pagina = 

  "<!DOCTYPEhtml>"
  "<html>"
    "<head>"
      "<title>"
        "Tutorial de servidor web"
      "</title>"
    "</head>"
    "<h3>"
      "Esto es un servidor web (de ejemplo)"
      " con el micro ESP8266"
    "</h3>"
  "<html>";

pero esto es equivalente a escribir todo en una sola línea (si bien acá aparece en 3, es por el formato de la página):


String pagina= "<!DOCTYPEhtml><html><head><title>Tutorial de 

servidor web</title></head><h3>Esto es un servidor web (de ejemplo)

con el micro ESP8266</h3><html>";

Cuál de las dos formas se use va a quedar a su gusto, para mi se ve más claro de la primera forma porque tiene una estructura tabulada y ordenada, lo que lo hace más fácil de visualizar.

Luego de definir la página web, vamos a dar inicio al programa, inicializando el puerto serie (cambiar la velocidad acá no infiere en nada) e inicializar la conexión a la red wifi predefinida, esperamos a que se conecte y damos notificación por el puerto serie de que se conectó a la red deseada y dejamos la dirección IP que el router le asignó. Todo esto básico y ya lo hicimos en otro post.

A continuación tenemos la siguiente porción de código (cuya sintaxis no es la normal):

  servidorweb.on("/", []()
  {
    servidorweb.send(200, "text/html", pagina);
  });

acá lo que estamos haciendo es indicarle al servidor cómo responder ante el "llamado" del cliente bajo la ruta "/", en este caso el servidor va a ejectuar la línea:

servidorweb.send(200, "text/html", pagina);

y lo que va a hacer es enviar como respuesta la página que tenemos almacenada en la variable "pagina" (vale aclarar que en lugar de poner "/" podríamos poner "/encender" y vamos a usar esa otra ruta, incluso podríamos declarar ambas, más adelante hablaremos de ello en más detalle). Una vez indicado cómo tiene que respoder podemos dar inicio al servidor web bajo el comando:

servidorweb.begin();

En el loop lo que tenemos que hacer es revisar constantemente si hay algún cliente esperando, y esto lo vamos a hacer con la siguiente línea:

servidorweb.handleClient();

Esta última línea debemos llamarla constantemente para que se manejen los clientes de forma regular.

domingo, 28 de octubre de 2018

Módulo GSM con mqtt

Bueno, hoy (en continuación al post del módulo GSM) les traigo cómo utilizar mqtt con un módulo gsm, hay una librería que se encarga de hacer todo el manejo de gprs y las conexiones y nos da un sistema simplificado. El código es el siguiente:



/**************************************************************
 *
 * For this example, you need to install PubSubClient library:
 *   https://github.com/knolleary/pubsubclient/releases/latest
 *   or from http://librarymanager/all#PubSubClient
 *
 * TinyGSM Getting Started guide:
 *   http://tiny.cc/tiny-gsm-readme
 *
 **************************************************************
 * Use Mosquitto client tools to work with MQTT
 *   Ubuntu/Linux: sudo apt-get install mosquitto-clients
 *   Windows:      https://mosquitto.org/download/
 *
 * Subscribe for messages:
 *   mosquitto_sub -h test.mosquitto.org -t GsmClientTest/init -t 
 *   GsmClientTest/ledStatus -q 1
 * Toggle led:
 *   mosquitto_pub -h test.mosquitto.org -t GsmClientTest/led -q 1 -m 
 *   "toggle"
 *
 * You can use Node-RED for wiring together MQTT-enabled devices
 *   https://nodered.org/
 * Also, take a look at these additional Node-RED modules:
 *   node-red-contrib-blynk-websockets
 *   node-red-dashboard
 *
 **************************************************************/

// elegimos el modem:
#define TINY_GSM_MODEM_SIM800
// #define TINY_GSM_MODEM_SIM808
// #define TINY_GSM_MODEM_SIM900
// #define TINY_GSM_MODEM_A6
// #define TINY_GSM_MODEM_A7
// #define TINY_GSM_MODEM_M590
// #define TINY_GSM_MODEM_ESP8266
// #define TINY_GSM_MODEM_XBEE

#include <TinyGsmClient.h>
#include <PubSubClient.h>

// credenciales de gprs
// en caso de no tener usuario y contraseña no llenar
const char apn[]  = "tuapn";
const char user[] = "";
const char pass[] = "";

// para las placas Mega, Leonardo, Micro utilizar el serie por hardware
#define SerialAT Serial1

// o Software Serial para Uno y Nano
//#include <SoftwareSerial.h>
//SoftwareSerial SerialAT(2, 3); // RX, TX

TinyGsm modem(SerialAT);
TinyGsmClient client(modem);
PubSubClient mqtt(client);

const char* broker = "test.mosquitto.org";

const char* topicLed = "GsmClientTest/led";
const char* topicInit = "GsmClientTest/init";
const char* topicLedStatus = "GsmClientTest/ledStatus";

#define LED_PIN 13
int ledStatus = LOW;

long lastReconnectAttempt = 0;

void setup() {
  pinMode(LED_PIN, OUTPUT);

  // elegimos una velocidad de datos
  Serial.begin(115200);
  delay(10);

  // elegimos la velocidad para el módulo
  SerialAT.begin(115200);
  delay(3000);

  // reiniciamos el módulo, demora mucho
  Serial.println("inicializando modem...");
  modem.restart();

  String modemInfo = modem.getModemInfo();
  Serial.print("Modem: ");
  Serial.println(modemInfo);

  // si la sim tiene contraseña ponerla acá
  //modem.simUnlock("1234");

  Serial.print("esperando por red...");
  if (!modem.waitForNetwork()) {
    Serial.println(" fallo");
    while (true);
  }
  Serial.println(" OK");

  Serial.print("conectando a ");
  Serial.print(apn);
  if (!modem.gprsConnect(apn, user, pass)) {
    Serial.println(" fallo");
    while (true);
  }
  Serial.println(" OK");

  // MQTT Broker setup
  mqtt.setServer(broker, 1883);
  mqtt.setCallback(mqttCallback);
}

boolean mqttConnect() {
  Serial.print("conectando a ");
  Serial.print(broker);
  if (!mqtt.connect("GsmClientTest")) {
    Serial.println(" fallo");
    return false;
  }
  Serial.println(" OK");
  mqtt.publish(topicInit, "GsmClientTest iniciado");
  mqtt.subscribe(topicLed);
  return mqtt.connected();
}

void loop() {

  if (mqtt.connected()) {
    mqtt.loop();
  } else {
    // Reconnect every 10 seconds
    unsigned long t = millis();
    if (t - lastReconnectAttempt > 10000L) {
      lastReconnectAttempt = t;
      if (mqttConnect()) {
        lastReconnectAttempt = 0;
      }
    }
  }

}

void mqttCallback(char* topic, byte* payload, unsigned int len) {
  Serial.print("mensaje recibido [");
  Serial.print(topic);
  Serial.print("]: ");
  Serial.write(payload, len);
  Serial.println();

  // solo lo ejecutamos si coinciden los tópicos
  if (String(topic) == topicLed) {
    ledStatus = !ledStatus;
    digitalWrite(LED_PIN, ledStatus);
    mqtt.publish(topicLedStatus, ledStatus ? "1" : "0");
  }
}



En donde dice "tuapn" en la línea 43, tendremos que ingresar el APN de nuestro proveedor de telefonía, a continuación les dejo los apn de las compañías de argentina:

CLARO: igprs.claro.com.ar
MOVISTAR: wap.gprs.unifon.com.ar usuario: wap contraseña: wap
PERSONAL: datos.personal.com usuario: datos contraseña: datos
TUENTI: internet.movil usuario:internet contraseña: internet

Para otros países solamente hay que googlear: apn nombre_compañia de nombre_país (en mi caso busqué "apn de todas las compañias de argentina").

Esto código sirve para encender un led a través de mqtt por internet (las aplicaciones podrían llegar a ser increíbles, como encender algo a distancia), para hacerlo funcionar tendremos que subscribirnos a los tópicos "GsmClientTest/init" y "GsmClientTest/ledStatus", en el primer tópico el equipo va a notificar cuando se conecte y en el segundo nos va a notificar el estado del led (encendido o apagado). Si publicamos en el tópico "GsmClientTest/led" podremos cambiar el estado del led mandando un mensaje cualquiera en ese tópico.