domingo, 20 de noviembre de 2016

Configuracion y formatos

Antes de hacer nada más, debemos pararnos un momento para pensar en cómo vamos a intercambiar datos, cómo nuestros programas van a leerlos y gestionarlos.
Ya comentamos cuando describimos la clase Conexion que todas las clases heredaban de una clase ObjetoGeneral y que ésta nos ayudaría a gestionar los diferentes elementos de nuestra aplicación. Bueno, pues identificando los diferentes objetos y sus atributos, podemos identificar los datos que intercambiaremos para cada uno. En esta parte conviene pensar, para luego evitar tener que estar haciendo cambios continuamente cada vez que nos demos cuenta d que se nos había olvidado configurar algo que era necesario.

Si bien, hasta este momento sólo hemos hablado de nuestra clase Conexion, y que habría Actuadores y Sensores, dándole una vuelta más al problema es fácil ver que aparecerán otras clases en el sistema, en concreto, hasta ahora hemos identificado algunas más:

  1. Clase ObjetoControl: es un contenedor administrativo, nos servirá para agrupar actuadores y sensores de una unidad determinada (una piscina, un edificio, ...).
  2. Clase Grupo: esta clase es nueva, va a agrupar diferentes actuadores. ¿Para qué nos va a servir? Para simplificar comportamientos de actuadores que deben activarse a la vez. Imaginemos que tenemos un circuito de riego en un jardín, tiene una electroválvula maestra, una para cada circuito y finalmente una bomba de presión, bueno, pues para regar con ese circuito tenemos que activar los tres. Podríamos poner todos en el mismo actuador, pero normalmente, las electroválvulas son a 24VAC y la bomba será a 220VAC, la específica del circuito de riego debe estar separada (si no, cada vez que vayamos a regar se activaría) y así para cada circuito de riego ...
    • Más allá le daremos cierta inteligencia, especializaremos un tipo de grupo que sea secuencial, es decir, sus actuadores irán consecutivos, no simultáneos, y otro en el que podamos configurar desplazamientos temporales, es decir, van arrancando de forma asíncrona.
  3. Clase Programa: todo está muy bien, pero aparte de tomar medidas de sensores, hay que decirle al sistema cuándo y qué hacer, configuraremos diferentes tipos de temporizadores y disparos (nos interesa al menos tener tempos periódicos, semanales y mensuales). También habrá que ver qué hacer con umbrales de las medidas de los sensores.
  4. Clase FicheroLOG: recordemos que queríamos guardar eventos del sistema, medidas de los sensores, ...
  5. Clase Usuario: vamos a acceder de forma remota, más vale que pongamos cierta seguridad de acceso, también habrá usuarios con roles diferentes.
Todavía tendremos algunas clases más, pero son más accesorias y no tan importantes de momento. Con esta primera reflexión, nos queda algo así:
Como temas importantes, hemos asignado a cada clase un id (primera columna), luego, como casi todas heredan de ObjetoGeneral, está la parte sombreada en verde, que se implementará en la susodicha clase, depués hemos introducido datos específicos para cada clase, especial mención a los sombreados azules, que es lo que relacionará los ObejtosConectados con los objetos Conexion.

He añadido también un concepto de información general, que nos servirá para testar el estado general del sistema. La memoria libre, la hora, ...

Ya solo tenemos que escribir estas líneas, leerlas y crear los objetos correspondientes. También utilizaré estos formatos para intercambiar datos con las aplicaciones. 

Es evidente que si estos datos van a viajar por la red, convendría encriptarlos, pero de momento no es lo prioritario. Seguiremos con una primera configuración en la tarjeta SD en una próxima entrada.




viernes, 18 de noviembre de 2016

Programa principal (el .ino)

En esta publicación vamos  estructurar el .ino de nuestra programación. Debemos tener en cuenta que:

  1. Queremos hacer varias cosas, es decir, no nos vale con quedarnos en un while y esperar a que llegue algo.
  2. Queremos poder gestionar la plataforma.
  3. La placa MEGA tiene varios temporizadores.
Bueno, todo esto nos lleva al uso de las interrupciones. El micro de la placa MEGA (ATMega2560 http://www.atmel.com/Images/Atmel-2549-8-bit-AVR-Microcontroller-ATmega640-1280-1281-2560-2561_datasheet.pdf) tiene 3 temporizadores. Curiosenado un poco por internet he encontrado una librería (esta es la potencia de arduino) que encapsula el uso de éstos. La librería es la TimerThree https://www.pjrc.com/teensy/td_libs_TimerOne.html funciona bien para lo que yo la he utilizado.

He generado un fichero "Definiciones.h" que irá englobando todas las definiciones que vamos a meter en compilador (es decir, no gestionables externamente). Básicamente, para no volvernos locos buscando las constantes y demás.

// 

//     Fichero: Definiciones.h
// Version: 2.0
//     Autor: Raul Hermosa
// Fecha: 20 de julio de 2016
// Descripcion: Fichero que define los parámetros fundamentales del sistema
//
#ifndef _DEFINICIONES_h

#define _DEFINICIONES_h

#define VEL_PUERTO_SERIE 115200


// define el pin CS para la SD, funciona mediante bus SPI
#define CS_SD 53

// definicion de tiempos
#define TEMPO_POLL_SENS 30 // Cada 30 segundos
#define TEMPO_POLL_PROGS 10 // Cada 10 segundos
#define TEMPO_REINI_CONFIG 2  // Cada 2 segundos
#define TEMPO_POLL_MAN_DISP 0.2  // Cada 200 milisegundos
#define TEMPO_POLL_PET_REMOTA 0.3  // Cada 300 mseg

#define NUM_POLLS_CHECK_WIFI 500 // cada 500 veces que se cheque la conf, se chequea
// el servidor WIFI

#define TEMPO_TICK 10000 // define el tiempo de interrup (tick) en microseg en Timer3
#define USEGS_EN_SEG 1000000 // microsegs de 1 segundo
//definiciones de los numeros de ticks
#define NUM_TICKS_SEG USEGS_EN_SEG/TEMPO_TICK // Ticks cada 1 segundo
#define NUM_TICKS_SENS TEMPO_POLL_SENS*NUM_TICKS_SEG
#define NUM_TICKS_PROGS TEMPO_POLL_PROGS*NUM_TICKS_SEG
#define NUM_TICKS_CONFIG TEMPO_REINI_CONFIG*NUM_TICKS_SEG
#define NUM_TICKS_MAN_DISP TEMPO_POLL_MAN_DISP*NUM_TICKS_SEG
#define NUM_TICKS_PET_REMOTA TEMPO_POLL_PET_REMOTA*NUM_TICKS_SEG

#endif //_DEFINICIONES_h_


Bueno, la realidad es que nuestro .ino es de lo más sencillo. Sólo programa una serie de temporizadores y pone un flag para que se ejecuten diferentes métodos.


//

// Fichero: ControlDomoPlat.ino
// Version: 2.0
//     Autor: Raul Hermosa
// Fecha: 20 de julio de 2016
// Descripcion: Fichero principal de ejecucion de plataforma IoT
//

#include <TimerThree.h>

#include "GestorConfiguracion.h"
#include "Definiciones.h"

// Crea el Gestor de Configuracion para que habilite la configuracion del sistema
GestorConfiguracionClass _config;

// Variables locales
int num_ticks_seg;
int num_ticks_sens;
int num_ticks_progs;
int num_ticks_config;
int num_ticks_man_disp;
int num_ticks_pet_remota;

bool chequea_tick_seg;
bool chequea_tick_sens;
bool chequea_tick_progs;
bool chequea_tick_config;
bool chequea_tick_man_disp;
bool chequea_tick_pet_remota;

void setup()
{
Serial.begin(VEL_PUERTO_SERIE);
delay(200);

Serial.println(F("\n\nPUERTO SERIE INICIADO"));
_config.init();
Serial.println(F("\n\nINICIANDO TEMPOS"));
Timer3.initialize(TEMPO_TICK);
Timer3.attachInterrupt(ISR_tick);
// Configuro el temporizador para controlar los ticks donde controlar las tareas periodicas
num_ticks_seg = 0;
num_ticks_sens = 0;
num_ticks_config = 0;
num_ticks_man_disp = 0;
num_ticks_pet_remota = 0;

chequea_tick_seg = false;
chequea_tick_sens = false;
        chequea_tick_progs = false;
chequea_tick_config = false;
chequea_tick_man_disp = false;
chequea_tick_pet_remota = false;
}

void loop()
{
if (chequea_tick_sens)
{
_config.escribe_sensor_log();
chequea_tick_sens = false;
}
if (chequea_tick_config)
{
_config.configura_sistema();
chequea_tick_config = false;
}

if (chequea_tick_man_disp)
{
// TODO: ivocar un metodo de chequeo de peticion manual
// _config.chequea_pet_manual();
chequea_tick_man_disp = false;
}

if (chequea_tick_pet_remota)
{
//Serial.println(F("Chequeo peticiones web"));
//_config.chequea_peticion_remota();
chequea_tick_pet_remota = false;
}

if (chequea_tick_progs)
{
//Serial.print(F("Chequeando PROGS: ")); Serial.println(_rtc.getData().toString());
//_config.chequea_tempos();
chequea_tick_progs = false;
}
}

void ISR_tick()
{
if (num_ticks_seg++ >= NUM_TICKS_SEG)
{
chequea_tick_seg = true;
num_ticks_seg = 0;
}
else if (num_ticks_sens++ >= NUM_TICKS_SENS)
{
chequea_tick_sens = true;
num_ticks_sens = 0;
}
else if (num_ticks_config++ >= NUM_TICKS_CONFIG)
{
chequea_tick_config = true;
num_ticks_config = 0;
}
else if (num_ticks_progs++ >= NUM_TICKS_PROGS)
{
chequea_tick_progs = true;
num_ticks_progs = 0;
}
else if (num_ticks_man_disp++ >= NUM_TICKS_MAN_DISP)
{
chequea_tick_man_disp = true;
num_ticks_man_disp = 0;
}
else if (num_ticks_pet_remota++ >= NUM_TICKS_PET_REMOTA)
{
chequea_tick_pet_remota = true;
num_ticks_pet_remota = 0;
}
}


Como puede verse, al final lo que he hecho es utilizar un temporizador del micro para generar los temporizadores que a mi me interesan. Defino una serie de tiempos en Definiciones.h (lo he hecho para configurar en segundos y dar claridad), pongo unos contadores, configuro el tempo 3 y cada vez que salta la interrupción (invoca la rutina ISR_tick) controlo con los contadores si es el timeout de cada uno. Como siempre, en esa rutina no conviene hacer nada (evita comprometer datos), sólo pongo un flag y reinicio los contadores. En el loop es donde invoco los métodos que me interesan a través de un obejto de clase GestorConfiguracion, de la que todavía no hemos hablado.

Como puede verse todo muy fácil, cada vez que acabemos de hacer una cosa, esperamos a que nos avise el flag de qué toca revisar. De esta forma evitamos quedarnos enganchados con cosas como delay() o while (xx.available()), que sirven para otros propósitos.

Ahora ya podemos complicarnos la vida en los métodos invocados, si se quiere hacer pruebas, activar el monitor serie y lo podéis ir viendo, a efectos de prueba no hace falta la clase GestorConfiguracion, se puede imprimir algo y ya está.

Aunque es muy evidente, espero que os sirva para esto o para otros proyectos.

jueves, 17 de noviembre de 2016

Diagrama de clases (clase ObjetoConectado, PlacaHW)

Seguimos con el diagrama de clases, ahora con los actuadores y los sensores y placas auxiliares que nos servirán al sistema:

Es fácil ver cómo de nuevo la clase ObjetoConectado herede de ObjetoGeneral, que era la clase que encapsula algunos de los métodos de gestión, la clase ObjetoConectado añade un atributo que el un puntero a un objeto Conexion.

Conviene mencionar también la clase ObjetoControl, que no es sino un contenedor de sensores y actuadores. Esta clase nos va a dar posibilidades de agrupamiento. Por ejemplo, si tuviéramos que controlar una piscina, un jardín y un edificio, se podrían dar de alta 3 ObjetoControl, si el control es independiente. Es decir, los Actuadores y Sensores colgarán de los ObjetosControl.

La clase Actuador de momento no la desarrollaremos mucho, podrá ser un Contactor o un Telerruptor (es importante porque el modo de activación cambia) y todavía no entraremos en control de velocidad de motores o incluso de servos.

La clase PlacaHW nos va a servir para gestionar HW externo a la placa Arduino Mega. Esto es, he decidido introducir ademas de la ATMega de Arduino (aprox unos 11€ en amazon):

  • Placa DS3231: funcionalidad RTC con sensor de temperatura interno en un bus I2C): nos da el control de hora con una batería y funciona de maravilla (unos 2€ en amazon).

  • Placa SD para tarjeta microSD: bus SPI, nos va a permitir acceder a una tarjeta microSIM, en mi caso he metido una de 16GB, lo que nos da una versatilidad en almacenamiento de datos muy relevante. A su vez, esta tarjeta va a almacenar datos de configuración, ficheros log, eventualmente podríamos meter el código html de un servidor, ... lo que necesitemos (unos 2€ + tarjeta microSIM aparte que pueden ser otros 6€ una EVO Samsung de 16GB).

  • Placa ESP8266: implementa WLAN, interfaz serie. Esta es un poco más cara, unos 7-9€ en amazon.


  • Placa HC-05: implementa Bluetooth, interfaz serie. Unos 5€, ojo, no confundir con la HC-06, que algo más barata, pero solo nos vale como esclavo, no como master:
Finalmente conviene tener un modulo de alimentación MB102, son unos 5€ y te soluciona la vida con los 5V y los 3,3V, evitando inestabilidades en los módulos ESP sobre todo (esta placa no la vamos a gestionar de momento, ya veremos cómo aseguramos alimentación al sistema y la medimos, habrá que hacer algo de HW externo y conectar una sensor analógico para medir tensión en la entrada):

Entrando en la clase Sensor, vemos una especialización progresiva. Lo más importante es que debemos entender la naturaleza de los sensores que a priori pueden ser Binarios o de Rango, es decir, ON/OFF o que nos de un float como valor. La idea es que cada vez que invoquemos un método (actualiza_valor) nos devuelva el valor del sensor sin saber qué hay debajo o cómo se gestiona.

Conviene ver que para los sensores de Dallas, aprovechamos la libreria de Dallas, que implementa muchas de las funciones necesarias para escanear las direcciones, inicializar o leer las temperaturas, recordemos que estos sensores de temperatura pueden estar en el mismo bus 1-wire y lo mejor de todo, por un precio muy bajo (poco más de 2€), los tenemos hasta sumergibles (ds18b20 -  http://datasheets.maximintegrated.com/en/ds/DS18B20.pdf).


Todo esto nos configura una arquitectura HW como la siguiente:
Con la arquitectura SW que propongo, podemos también la posibilidad de tener actuadores y sensores vía WIFI o BT, lo que nos dará una flexibilidad importante, habida cuenta de que los pines son finitos. En este caso, también tendremos que diseñar los protocolos entre ellos.

Veremos cómo al final la limitación importante será la de la memoria del micro.

En resumidas cuentas, el sistema son unos 40€, equipandolo con RTC, un primer sensor de temperatura interno, WLAN, BT y alimentación estabilizada.

Bueno, hasta aquí esta parte, va tocando ponerse a codificar ...







miércoles, 16 de noviembre de 2016

Diagrama de Clases (clase Conexion)

A continuación vamos con el diagrama de clases (por aquello de programar en C++ y C#). Esta es la parte más espesa, pero qué le vamos a hacer, creedme que luego nos ahorrará mucho tiempo si lo hacemos bien.

Es necesario introducir que un sistema de estas características básicamente tiene dos tipos de objetos:

  1. Sensores: toman medidas del mundo exterior (temperatura, activaciones de pulsadores o interruptores, humedad, ...).
  2. Actuadores: realizan acciones sobre cosas del mundo exterior (contactores, telerruptores, accionadores de persianas, controladores de motores, ...)

Bueno, habíamos comentado que queríamos varias cosas:

  • Gestionar la plataforma
  • Englobar diferentes tipos de conexión 
  • Englobar diferentes tipos de actuadores y diferentes tipos de sensores
  • Acceder de forma remota
En este sentido, lo que haremos es intentar aislar las capas más bajas de los protocolos de comunicación de las más altas. Esto es, que desde las funcionalidades que demos a los sensores, quede encapsulado (no tengamos que preocuparnos) si se conectan por buses SPI o I2C o incluso directamente. En este sentido he dividido los objetos en dos, uno que describe su funcionalidad, y otro que describe como se conecta, uno será un ObjetoConectado, que tendrá como atributo o campo una Conexión.

A continuación se puede ver el cómo la clase Conexion se especializa en diferentes tipos de conexión:


Acudiendo a la parte inferior de las herencias, vemos los diferentes tipos de conexion que implementaremos. Según vamos bajando, se ve cómo se especializan las clases:

  1. Digital: es una conexión física a un pin digital.
  2. Digital_ext: es una conexión física a un pin digital, pero que además tiene otro pin asociado que permite activaciones directas.
  3. Analógica: es una conexión física a un pin analógico, meramente será utilizada por sensores analógicos.
  4. DHT: es una coenxión que implementa lecturas de los sensores DHT-11 y DHT-12. Es específica para estos.
  5. USB: conexión serie.
  6. Bluetooth: conexión serie pero a una placa HC-06 que implementa de forma quasi transparente la comunicación inhalambrica.
  7. WLAN: conexión serie pero a una placa ESP8266, que implementará las comunicaciones 802.11.
  8. CONEX_I2C: implementa las comunicaciones serie a través de 2 pines configurables. En el caso de la Mega, vienen ya dos pines a tal efecto.
  9. CONEX_SPI: igual pero para el bus SPI, en este caso solo configuraremos los pines comunes del bus, cuando demos de alta algún objeto conectado, habrá que proporcionar el SS respectivo. A priori lo utilizaremos para la placa controladora de la microSD, pero se podría conectar un display i2c o cualquier otro elemento con dirección distinta.
  10. CONEX_ONEWIRE: implementa el protocolo 1-wire. Nos vendrá muy bien para algunos sensores de Dallas, por ejemplo.
  11. ETHERNET: comunicaciones 802.3.


Conviene comentar la clase ObjetoGeneral, que es la clase de la que heredarán todas las demás clases de la plataforma. Esta clase nos va a dar algunas funciones pseudoadminsitrativas para la gestión, impresión y demás de los obejtos que luego creemos en el sistema. Con eso conseguimos una reutilización de código (esto es puro C++) y el encapsulado del mismo. Podéis ver la clase definida a la derecha.



Datos personales