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.

domingo, 21 de octubre de 2018

LDR

Hoy les traigo un post corto sobre LDR, o resistencias variables por luz. Estas resistencias van a variar según sea la cantidad de luz que reciben y las hay de dos tipos, las que aumentan la resistencia con la luz y las que disminuyen su resistencia con la luz. Estas resistencias son (o al menos eran) muy usadas para hacer lúces automáticas (las que se prenden automáticamente cuando oscurece), aunque el funcionamiento para esas aplicaciones es comparando con un valor de resistencia fijo (cuando el valor de la LDR está por debajo o por encima del valor con el cuál se compara enciende o apaga la luz), nosotros vamos a darle un uso un poco más amplio y vamos a intentar medir la cantidad de lúmenes en luxes. Esto nos va a permitir (saber si es de día o de noche) ampliar nuestra estación meteorológica y conocer las variaciones lumínicas en el tiempo.

Vamos a utilizar la librería "LightDependenResistor" que nos simplifica muchísimo el cálculo de todo. Esta librería se "alimenta" del valor de una resistencia en serie con la LDR y el modelo de la misma. Acá nos topamos con la primer barrera, como saber el modelo de nuestra resistencia variable por luz, esto es un poco difícil, en mi caso empecé a buscar imágenes que coincidieran con mi LDR y luego comencé a buscar los modelos asociados a esas imágenes, en mi caso era la GL5528 (justo coincide con el modelo del ejemplo). Una vez solucionado el tema del modelo realizamos el siguiente circuito:

Esta imágen la saqué del repositorio de github de la librería

y utilizamos el código de ejemplo de la librería, si abrimos el monitor serie de arduino, vamos a ver que nos entrega el valor en lux y en "foot candle" que es otra forma de medir la intensidad de la luz.

En el post de la semana que viene vamos a utilizar este nuevo sensor y la base de datos para realizarle una mejora a nuestra estación meteorológica.

domingo, 23 de julio de 2017

Fin de publicación

Esta vez vengo a comentar el fin de la tanda de publicaciones, espero el año que viene poder subir nuevo contenido. Hasta entonces, espero sus sugerencias.

domingo, 9 de julio de 2017

Sensor de humedad HIH-4000

Hoy les traigo el sensor de humedad HIH-4000, cuenta con una resolución de 31.483 mV por cada porcentaje de humedad relativa. Sus aplicaciones van desde la meteorología hasta (según la hoja de datos) equipamientos médicos.
Si se mira de frente (con la parte expuesta hacia nosotros) tenemos a la izquierda masa, en el centro la tensión de salida y a la derecha vcc. Se alimenta con 5V y entre el pin que va a masa y el de salida deberíamos agregarle una resistencia de 80Kohms.

El sensor recuadrado en rojo es la parte expuesta, y de
izquierda a derecha tenemos: GND, Salida, VCC
Este sensor tiene una respuesta bastante lineal, por lo que podemos tomar desde 0% hasta el 100% de humedad el incremento de la tensión que mencioné más arriba, pero para una humedad relativa de 0% tenemos 0,826V
A continuación les dejo un breve programa capaz de manejar los datos.

void setup()
{
  Serial.begin(9600);
}

void loop() 
{
  int humedad = 0, vout = 0;
  vout = analogRead(a0);
  humedad = ((vout - 0.826)/0.0315);
  Serial.println(humedad);  
}

Como el sensor se alimenta con 5V y tiene una tensión de salida que varía entre 0V y casi 4V es difícil implementarlo de forma directa (es decir sin ningún circuito adicional) con el microcontrolador ESP8266, recordemos que este micro se alimenta con 3,3V y es capaz de "leer" tensiones analógicas de hasta 1V. Con esto en mente, si alimentamos nuestro ESP8266 con 5V que luego regulamos, podríamos tomar esa tensión sin regular para alimentar el sensor de humedad, y si al pin de salida le agregamos un divisor resistivo con una atenuación de 1 a 4 podríamos usarlo sin ningún problema en ese microcontrolador. De más está decir que este sensor es de muchísima mejor calidad que la familia DHT.

domingo, 2 de julio de 2017

Cómo mantener las gráficas en node-red

Hoy les voy a explicar, como indica el título, cómo mantener las gráficas en node-red incluso si éste se detiene o se corta la luz.

Si realizaron la estación meteorológica simple (la versión que les dejé en el link de mega) quizá les pasó que las gráficas históricas se perdieron si se desconectaba el servidor o si detenían los servicios (hay algunos casos más en los que se pueden perder los datos, pero no los voy a mencionar porque el resultado siempre es el mismo, la pérdida de los datos), sin lugar a dudas a mi me pasó varias veces y tenía que mantener el servidor funcionando 24/7 si no quería perder esos datos, lo que hacía que la raspberry pi se me tildase o funcionase erráticamente luego de estar encendida por un par de meses (no me ejecutaba las funciones correctamente por lo que no filtraba los datos que se mostraban). Luego del último corte de luz me puse a pensar en cómo corregir eso, y decidí guardar los datos en un archivo, simplemente conectando los nodos de la siguiente forma


Así no perdía los datos, pero seguía sin poder mostrarlos nuevamente, revisando los datos que se guardaban, quedaba un objeto como el siguiente:

[{"key":"Series 1","values":[[1492920133027,32],[1492920135027, 33]...]}]

Si analizamos este objeto nos quedan las propiedades "Key" que guarda el valor "Series 1" y "values" que es un array de arrays (o un array de dos dimensiones) que contiene los milisegundos desde el año 1970 y la temperatura (o la humedad, si analizamos el otro archivo) correspondiente a esa fecha exacta.
Mi primera prueba fue crear una función que devuelva un objeto similar y mandarlo directo al graficador, pero no sirvió (lo más probable es que haya echo algo mal dado que no tengo experiencia trabajando con objetos), por lo que probé mandar el mismo objeto que guardo de la siguiente forma


Cuando presionaba el botón que inyecta el "timestamp" debería mandar los datos del archivo, de más está decir que esto no funciona porque lo que se guarda dentro del archivo es procesado como texto plano y no como un objeto, pero esto se resuelve poniendo el nodo json de la siguiente forma

Este nodo lo que hace es tomar el  texto plano y lo transforma en lo que es (en este caso un objeto). Esto sí es tomado de forma correcta por el nodo "chart" y nos muestra los datos subidos, entonces ahora nos faltaría hacer que se guarde en ese mismo archivo y la solución que se me ocurrió es la siguiente (sé que el tamaño de la imágen es bastante chico)


Cada tres horas mando el promedio de la humedad (en este caso) al nodo "chart" el cual me devuelve todos los puntos graficados, en la función siguiente verifico si lo que me manda está vacío, si lo está no devuelve nada y si no lo está guarda en el archivo todos los datos. Cada vez que se enciende el servidor el nodo "inject" manda una señal que hace que se lea el archivo donde se guardan los datos, estos se convierten a objeto y se mandan al nodo "chart" que los grafica.
Esta solución sigue teniendo el problema de que se muestra el último año de datos, y luego se van a ir descartando los datos más antiguos al año, por lo que no podríamos almacenar más que eso (si modificamos cuántos días queremos mostrar en el pasado podríamos hacer que se muestre, pero como el espacio para graficar es reducido llegaría un punto en el cuál no se podría apreciar ningún detalle).
La solución a esto podría ser crear un archivo por año y que se muestren todos los datos del año actual (del primero de enero al 31 de diciembre), y esto se podría implementar en la última función (la que está a la salida de la gráfica). Vamos a obtener el año actual y lo vamos a usar como nombre del archivo (o al menos como parte del nombre del archivo) y luego le vamos a pasar el nombre del archivo al nodo que guarda los datos para que los guarde en dicho archivo o cree el archivo si el mismo no existe. Un ejemplo de esto sería lo siguiente, los nodos función obtienen el año, le agregan la ruta y pasan ese string como nombre de archivo, el nodo que maneja archivos crea el archivo y guarda el dato, si ya está creado solo guarda el dato, el otro nodo de manejo de archivos sólo lo abre y lo muestra por consola.

El resultado final nos quedaría algo similar a la imágen anterior, pero con un nodo de función extra, deberíamos crear los archivos iniciales por como está planteado el sistema.

Acá les dejo el código de la versión actualizada de la estación meteorológica para que puedan importar y ver el código, o la configuración, de cada nodo. Vale aclarar que el sistema de archivos que estoy usando es el de linux que es el sistema operativo recomendado para la raspberry pi 3, si tienen montado este servidor en windows hay que cambiar la ruta de los archivos.

https://mega.nz/#!W5g1UBII!Xwh1HVaIYwi6lRhlVImE--tZM8qVIy2kR73KcwNCgDM