viernes, 18 de diciembre de 2015

Servidor MQTT en Gateway. Resumen alternativas


Como fin de esta serie de mini referencias resumo lo explicado respecto a las posibilidades de conexión de un sistema integrado:

Problemática: 


Disponemos de n sensores y un gateway que es el punto de interacción principal con el usuario a través de internet. Este gateway es un ordenador PC o Una rapsberry PI que tienen un broker de mensajería basado en colas, en este caso MQTT. 

Caso de uso:

A) Petición de Plaza
  1. EL usuario realiza una petición de servicio   web/twiter/otros  genera un evento del tipo "Quiero una plaza"
  2. El gateway recibe este evento y el arduino controlador devuelve un evento de "hay plaza" y abre la puerta o devuelve un mensaje de no hay plaza
B) Una plaza cambia de estado


  1. El arduino que gobierna la plaza comunica un evento "hay plaza libre" o "plaza ocupada"
  2. El gateway recibe este evento y el arduino controlador lo recibe por estar suscrito a el
  3. El arduino actualiza el estado del panel de evento/Web y actualiza la variable interna de estado 
Estas comunicaciones se pueden realizar con colas:
1) a nivel de IP:
  Raspberry/PC : Wifi /Ethernet
 Comunicación arduino-Arduino y arduino-gateway :  ESP8286/ENC28J60. 
 Software:  Raspberry/PC : servidor MQTT completo 
Ejemplos: 
      https://geekytheory.com/tutorial-raspberry-pi-gpio-y-mqtt-parte-1/
      https://geekytheory.com/tutorial-raspberry-pi-gpio-y-mqtt-parte-2/


2) A nivel de sensor NO-IP
 - Comunicación arduino-Arduino y arduino-gateway: módulos X-bee 
  -Software :Raspberry/PC :servidor de colas no-ip: MQTT-SN

 Ejemplos : 
      https://github.com/boriz/MQTT-SN-Arduino 


Sobre XBee:

http://www.andresduarte.com/arduino-y-xbee


Librerias y código para conexión wifi

https://www.dropbox.com/sh/a5hu5ilta37urea/AACibPAUtWXRCnLF0fwcT8TQa?dl=0

En este enlace tenéis las librerías que funcionan para el adaptador a ethernet 28j60.

Hay una prueba de utilización del propio arduino como servidor web. Esta funciona pero los comandos utilizados no son compatibles con el shield ethernet que suele utilizar la mayoría de los ejemplos encontrados en blogs y foros.

la libreria UUIP tiene comandos compatibles pero no esta completamente probada.



Otro tutorial mas de conexión a esp8286 desde Arduino

http://blog.theinventorhouse.org/mi-primer-acercamiento-al-modulo-wifi-esp8266/

ARDUINO Y WIFI ESP8266 Conectado Arduino a las redes WIFI

http://www.prometec.net/arduino-wifi/

Tutorial completo de uso de esp8286 con comandos at

Servidor web en un arduino con esp8286

https://github.com/yOPERO/ESP8266/blob/master/webserver.ino

En está entrada podéis ver el uso de un esp8286 para servir una pequeña página web.

No queda claro si lo hace directamente sobre el esp o a través del arduino. Pero no hay mucha diferencia

Ejemplo de uso de módulo wifi esp8286

http://quenotienes.com/tag/esp8286/

Ejemplo de uso de módulo wifi esp8086 desde un arduino para comunicación

lunes, 30 de noviembre de 2015

Objetivo de diseño MQTT

en esta entrada vamos a describir el entorno final del proyecto del parking pasando a un control externo. 

El desarrollo del parking completo consta de :
  • Dos plazas conectadas localmente a un arduino
  • Un arduino controlador que recibe información de las plazas y gestiona un panel de control y una barrera de entradas
  • Conexión I2C entre los sensores y el controlador.
  • Conexión Ethernet arduino y Raspberry Pi
  • Conexión externa internet a Websocket y android
El proceso tiene las siguientes fases:
  1. Calibración de los sensores 
  2. Medida con eliminación de ruido por media móvil 
  3. Fusión de sensores con establecimiento de etiquetas por calibración 
  4. Control del proceso mediante máquina  de estados 
  5. Establecimiento de servidor de colas MQTT en Raspberry/pc. Clientes mqtt en arduinos y Raspberry.  
  6. Clientes Web/android sencillos  (MyMqtt, webclients genéricos) 
  7. Plataformas y Ontologías 


Para la siguiente semana llevaremos a clase la Raspberry pi, con cable ethernet para probar las condiciones de conectividad en la clase. Habrá disponible un router para organizar una red local separada y probar los adaptadores ethernet de los Arduino. 
En caso de tener conectividad se mostrará la plataforma thingspeak como prueba de conexión de arduino y código de ejemplo.

domingo, 29 de noviembre de 2015

Ejemplo de máquina de Estados para arduino


Para controlar adecuadamente un proceso es importante estructurar los cambios de estado con una máquina de Estados.
El funcionamiento es simple.  Los estados definen una situación y permiten definir que acciones son factibles y por tanto simplifica la programación.
Este es un ejemplo de librería muy simple que puede servir para el proyecto en curso. (gracias a Igor Real por su implementación. 

ver aquí fuentes y el pdf del que he extraído la información publicada








 En el proyecto de las plazas, la máquina de estados se encuentra en el controlador de las dos plazas, posibles eventos/estados (depende de cómo se traten y reflejen) son:
Eventos:
- Petición de plaza (hay que definir si es genérica, corta , larga)
- Vehiculo entrando en garaje
- Vehiculo saliendo de garaje
- Vehiculo Detectado en plaza 
- Detección de plaza libre (cuando se libera)

Estados

- Plaza/s libre/s (al iniciar las plazas o cuando se libera)
- Plaza/s ocupada/s. no es lo mismo reservada que ocupada. detalles a resolver sobre la implementación . Hay que definir si se refleja el estado por plaza o del garaje completo. plazas ocupadas significa que no hay ninguna plaza, plaza ocupada es un estado de la plaza no del parking. 
- Vehiculo en parking.


A veces no es fácil manejar una sola máquina de estados y en necesario tener información en otras variables u otras máquinas de estados.  En esta implementación la parte que debe quedar bien definida son los eventos ya que será lo que se porte a colas y posteriormente a ontologías y plataforma de IOT. 


Links de interés:

   http://arduining.com/2015/09/18/traffic-light-states-machine-with-arduino/   Ejemplo de implementación de un cruce de semáforos con máquina de estados




martes, 17 de noviembre de 2015

Fusión de Sensores

Hoy vamos a ver varias técnicas de fusión de información, y vamos a poner en práctica la fusión con lógica difusa de la información proveniente de dos sensores.


Como parte de las notas opcionales dejo en el blog dos entradas de artículos científicos de fusión de información. La tarea consistirá en hacer un resumen aplicado a la práctica que estamos realizando . Ver en que sentido es aprovechable .

Hay una semana para entregarlo y la entrega consistirá en una presentación en ppt o pdf que se expondrá en clase , no debe exceder en tiempo de exposición los 15 minutos, no hay longitud máxima o mínima.


Articulo1
Articulo2



Luego repasaremos los conceptos de integración de la información presentes en esta Tesis.

Finalmente, según la planificación hay que integrar las medidas de luz y de distancia en la plaza de garaje mediante un controlador difuso. Explicaremos en clase los rudimentos y el código necesario para hacerlo desde cero o utilizar una biblioteca.



Para la implementación del controlador difuso se puede utilizar este código de ejemplo (es de procesing, pero puede ser adaptado fácilmente  a arduino ),  la librería Libreria Fuzzy, o cualquier otra disponible.

Como resumen de la clase comentar que tras la certeza de la situación de una plaza realizada por calibración, medida con media movil, y finalmente un proceso de fusión sensorial, hay que utilizar esta información para actualizar el estado del sistema. Para ello es necesario mantener una máquina de estados que dependiendo de los eventos producidos ("plaza ocupada", " Entra coche" "Sale Coche", "plaza desocupada") realice acciones o emita otros eventos .

Por ejemplo una vez detectada la ocupación de la plaza , si ya estaba ocupada antes no cambiamos de estado, pero si antes estaba desocupada, puede cambiar el estado del aparcamiento a lleno , si ya no hay más plazas libres, o quedarse en el mismo estado si hay más plazas libres.

La ejecución de acciones se puede realizar en la propia máquina de estados, o bien en un "handler" que consume los eventos. Ambas implementaciones facilitan la posterior migración a la ejecución mediante gestión de eventos en colas, sea esta local o a través de un servidor.

La definición de estos eventos es ahora un simple valor , pero posteriormente los definiremos en una ontología.


Update: éste articulo muestra la utilidad de un sistema difuso para detectar y estimar plazas libres en la calle en base a la fusión de información de sensores de magnetismo adyacentes.
Resumen en inglés:
  Abstract
This paper proposes “KATHODIGOS”, a novel smart parking system relying on wireless sensor networks. More specifically, its general architecture and desired properties are presented while its differences from other related research studies and projects are delineated. Based on the aforementioned architecture, the interfaces and technical requirements between the subsystems of “KATHODIGOS” are fully defined. Finally, a promising property of these subsystems which is based on fuzzy inference systems is studied and its preliminary results are shown.









viernes, 30 de octubre de 2015

Fase 1. Calibrado de sensores



En esta fase del proyecto se va a proceder a calibrar los sensores seleccionados para poder detectar si existe un coche en la plaza de garaje o no. En esta ocasión se ha seleccionado una fotorresistencia y un sensor ultrasónico para tener más variedad de detección e intentar evitar falsos positivos.
Una fotorresistencia es un componente eléctrico cuya resistencia disminuye con el aumento de la intensidad de la luz. Es decir, cuando apenas incide luz ofrece una resistencia de varios mega ohmios y en el caso contrario la resistencia es de unos 50 ohmios, dependiendo del modelo. Para poder hacer una medición y posterior calibrado se ha creado un divisor de tensión. De esta forma se pueden medir la caída de tensión que ofrece la fotorresistencia y usando varios niveles de luminosidad crear una tabla con la que poder hacer el calibrado. Los elementos usados para realizar esta medición han sido:

  • Fotorresistencia modelo gl55
  • Resistencia de 10 kilo ohmios
  • Arduino Leonardo
  • Cables macho-macho y macho-hembra
  • Tablet con un programa que mide lúmenes (Para tener una referencia)

Los valores recogidos se pueden ver en la siguiente imagen:

Donde el primer valor, el voltaje, es el valor que leía el arduino y el segundo, los lúmenes, la intensidad de luz que recibía el fotorresistor. Como se puede apreciar la curva que representa este sensor no es lineal, pero si se acude al datasheet del fotorresistor se puede apreciar que aplicando logaritmos la curva se hace lineal. En este caso se va a proceder a actuar como si no se conociera este dato y para poder calibrar el sensor se va a usar una tabla look-up.  El procedimiento consiste en buscar una zona de la curva lo más lineal posible y poner esos valores en una tabla. Cuando se mida un nuevo valor se buscará el primer valor que lo sobrepase y el siguiente a este usando para ello los valores de la tabla en amarillo, que son los que se corresponden a los valores más lineales y se usará la siguiente función para devolver el valor:

Donde los valores se corresponden con lo siguiente:
  • y0 es el valor del offset, que en este caso es 40
  • x es el valor medido
  • x0 es el valor anterior al medido
  • x1 es el valor siguiente al medido
  • y0 e y1 son el valor correspondiente a la salida en la tabla
  • yx es el valor de salida
De esta forma se obtendría el valor aproximado de la entrada recibida.
En el caso del sensor ultrasónico, éste se ha utilizado para medir la distancia, se trata del típico sensor ultrasónico HC-SR04.  Como se puede observar en el datasheet (http://e-radionica.com/productdata/HCSR04.pdf), el principio básico de trabajo es el siguiente:

  1. Usa IO trigger durante al menos 10 microsegundos de señal a nivel alto.
  2. El módulo automáticamente manda ocho señales a 40KHz y detecta si hay algún pulso de la señal de vuelta.
  3. El tiempo de salida a nivel alto es el tiempo de mandar el ultrasonido y volver.


Según el fabricante la ecuación necesaria para la identificación de la distancia es la siguiente:
  • Distancia = (Tiempo en nivel alto x velocidad del sonido (340m/s))/2 
De donde se puede obtener que:
  • Distancia (cm) = microsegundos tomados / 58 
Una vez comenzado el calibrado podemos observar como los valores obtenidos oscilan en comparación de los apreciados en el datasheet. El rango real obtenido se encuentra entre los 3 cm y apróximadamente los 4 m. Al realizar dos comprobaciones para obtener una gráfica lineal, asumiendo que el comportamiento será lineal, se obtiene lo siguiente:

·         Distancia real= 30cm  distancia sensor = 27cm
·         Distancia real 20 cm  distancia sensor 18 cm


Como se puede apreciar en la gráfica superior, se encuentra un offset de aproximadamente +2 cm.

Una vez obtenida esta información, se procede al cálculo de la media, mediana y moda.  La elección se hará tomando la famosa "Regla del pulgar" que se puede ver en la siguiente imagen:

Teniendo en cuenta que los datos no son categorías, todos los datos no son de interés ya que en ciertas ocasiones por a naturaleza del sensor es posible recoger valores erróneos y que no se trata de una distribución muy sesgada. Se debería hacer un uso de la media para la calibración del sensor. El uso de la media sin más, puede acarrear una serie de problemas ya que al ser una distancia relativamente pequeña, una medición errónea en la calibración puede suponer el no correcto funcionamiento del sistema completo. Como la definición del problema es bien conocida, se puede hacer uso de la media móvil para evitar valores no deseados, tal como describe el siguiente diagrama:


Consiguiendo de este modo unos resultados más acordes con la realidad, evitado de este modo problemas con las mediciones en la calibración.

El diagrama final del arduino quedaría de la siguiente forma:


Y el código correspondiente será el siguiente:
También se puede consultar en el git correspondiente:
https://github.com/iiotUHU/iiot/blob/master/calibracion.ino



  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
//variables donde se almacenan la distancia en cm y el tiempo en us
long distancia;
long tiempo;

//definicion de las constantes utilizadas
const int echo=6; //pin de arduino donde se conecta echo
const int trigger=7;// pin de arduino donde se conecta trigger
const int offsetultra=2; // offset calculado de forma manual
const int NMEDIDAS=10; //numero de medidas para la calibracion
const int NMODAS=400;//numero de medidas posibles en la calibracion
const int maxmediamovil=17;//valor maximo correcto para el ultrasonido (media movil)
const int minmediamovil=9;//valor minimo correcto para el ultrasonido (media movil)

const int ESCENARIOSCALIBRAR=5; //numero de escenarios para calibrar
const char escenarios [ESCENARIOSCALIBRAR][6]={"VACIO","CORTO","LARGO","ALTO","BAJO"}; //nombre de los escenarios a calibrar
int mediasfoto[ESCENARIOSCALIBRAR];
int mediasultra[ESCENARIOSCALIBRAR];

// estructuras para la media movil para ultrasonido
int mediamovilultra=0;
int nmediamovilultra=0;

int medidas [NMEDIDAS];//arreglo para calcular media y mediana
int media;
int valoresmoda[NMODAS]; //ya que los valores se encuentran entre 2 y 400, cada vez que se mida se inserta en este arreglo y al final en el procesamiento, se comprueba la moda.
int moda;

//Para el LDR
const int foto1=A0;//pin para la medicion del LDR
const int fotooffset=40; //offset calculado experimentalmente para el LDR
const int NUMVALORESFOTO=8;

int valorfoto=0;

//creacion de la tabla look-up insertando los valores experimentales
int tablalu[NUMVALORESFOTO][2]={{306,423},{362,473},{471,491},{578,595},{647,611},{1033,656},{1303,699},{1343,719}};

void setup(){
 
 
  Serial.begin(9600);
  pinMode(trigger, OUTPUT); /*activación del pin 9 como salida: para el pulso ultrasónico*/
  pinMode(echo, INPUT); /*activación del pin 8 como entrada: tiempo del rebote del ultrasonido*/

}

void loop(){
 
  int mediafoto;
  int mediaultra;
  int opcion;
     
     for(int i=0;i<ESCENARIOSCALIBRAR;i++)
     {
         while (Serial.read() !='0') {
             Serial.println("INTRODUZCA 0 PARA CONTINUAR");

         }
       Serial.println(escenarios[i]);
       Serial.println("calibrando LDR");
       mediasfoto[i]=calibraLDR();
       Serial.println("calibrando ULTRA");
       mediasultra[i]=calibraUltrasonico(); 
  
     }
    
    break;
  }
 
  delay(500);
}

int valorLookUp(int entrada){
  int resultado=0;
 
  int valorfiltrado=0;
 
  if(entrada<tablalu[0][1] || entrada>tablalu[NUMVALORESFOTO-1][1]){
   
    entrada=constrain(entrada,tablalu[0][1],tablalu[NUMVALORESFOTO-1][1]);
    if(entrada==tablalu[0][1]){
      resultado=tablalu[0][0];
    }else{
       resultado= tablalu[NUMVALORESFOTO-1][0];
    }
   
  }else{
   
      //La calibracion se realiza a traves de una regresion por cuadrados minimos.
      int i=0;
      boolean encontrado=false;
      while(i+1<NUMVALORESFOTO && !encontrado){
       
        if(tablalu[i+1]>=entrada)
        {
         
          float x=entrada;
          float x0=tablalu[i][1];
          float x1=tablalu[i+1][1];
          float y0=tablalu[i][0];
          float y1=tablalu[i+1][0];
          encontrado=true;
          resultado= y0+(((x-x0)/(x1-x0))*(y1-y0));
        }
        i++;
       
      }
  }
   
  return resultado;

}

int calibraLDR(){
 
  //inicializacion de media,mediana y moda al comenzar el ciclo de medicion.
  media=0;

  for(int i=0;i<NMEDIDAS;i++)
  {
    medidas[i]=0;
  }
 
 
  //comienzo del ciclo de medicion
 
  for(int i=0;i<NMEDIDAS;i++)
  {
     
      valorfoto=analogRead(foto1);
      int valor=valorLookUp(valorfoto);
      media+=valor;
   
      //insertamos de forma ordenada
       
        int j=0;
        int k;
   
        while( j<i && valor<medidas[j] ){
            j++; 
         };
        
          if(j==i){
            medidas[j]=valor;
           
          }else{
            k=i;
           
            while(k>=j){
              medidas[k]=medidas[k-1];
              k--;
            }
            medidas[j]=valor;
       
         
        }
    delay(5000);
     
  }

    Serial.println();
    Serial.print("MEDIANA: distancia ");
    Serial.print(medidas[NMEDIDAS/2]);
    Serial.println(" lux");
    Serial.print("MEDIA: distancia ");
   int resultado=media/NMEDIDAS;
    Serial.print(resultado);
    Serial.println(" lux");
   
    return resultado;
   
}

int calibraUltrasonico(){

   //inicializacion de media,mediana y moda al comenzar el ciclo de medicion.
  media=0;
  //moda=0;
  for(int i=0;i<NMODAS;i++)
  {
    valoresmoda[i]=0;
  }
  for(int i=0;i<NMEDIDAS;i++)
  {
    medidas[i]=0;
  }
 
 
  //comienzo del ciclo de medicion
 
  for(int i=0;i<NMEDIDAS;i++)
  {
      digitalWrite(trigger,LOW); /* se estabiliza el sensor*/
      delayMicroseconds(10);// ya que tarda segun las especificaciones unos 10us en pasar de un estado al otro
      digitalWrite(trigger, HIGH); /* envío del pulso ultrasónico*/
      delayMicroseconds(10); // ya que tarda segun las especificaciones unos 10us en pasar de un estado al otro
      tiempo=pulseIn(echo, HIGH); /* Función para medir la longitud del pulso entrante. Mide el tiempo que transcurrido entre el envío
      del pulso ultrasónico y cuando el sensor recibe el rebote, es decir: desde que el pin 12 empieza a recibir el rebote, HIGH, hasta que
      deja de hacerlo, LOW, la longitud del pulso entrante*/
      distancia= ((int(tiempo/58))+offsetultra);//distancia= int(0.017*tiempo); /*fórmula para calcular la distancia obteniendo un valor entero se trata de una aproximacin de us/58*/
      //definimos que se encuentre dentro del rango establecido
      distancia=constrain(distancia,3,400);
      //vamos preparando la media
      //Serial.println(distancia);
      media+=distancia;
      //Se adquieren los datos para una media movil simplificada
      if(minmediamovil>distancia && distancia<maxmediamovil){
          mediamovilultra=mediamovilultra+distancia;
          nmediamovilultra=nmediamovilultra+1;
      }
      int mediamovilultra[ESCENARIOSCALIBRAR];

      //preparamos la moda
      valoresmoda[distancia]++;
      //insertamos de forma ordenada
       
        int j=0;
        int k;
   
        while( j<i && distancia<medidas[j] ){
            j++; 
         };
        
          if(j==i){
            medidas[j]=distancia;
           
          }else{
            k=i;
           
            while(k>=j){
              medidas[k]=medidas[k-1];
              k--;
            }
            medidas[j]=distancia;
         
        }
    delay(50);
     
  }
    moda=0;
    valoresmoda[0]=1;
    for(int i=0;i<NMODAS;i++){
      if(valoresmoda[i]>moda){
        moda=i;
      }
    }
   
    Serial.println();
    Serial.print("MEDIANA: distancia ");
    Serial.print(medidas[NMEDIDAS/2]);
    Serial.println(" cm");
    Serial.print("MEDIA: distancia ");
    int resultado=media/NMEDIDAS;
    Serial.print(resultado);
    Serial.println(" cm");
    Serial.print("MEDIA MOVIL: distancia ");
    resultado=mediamovilultra/nmediamovilultra;
    Serial.print(resultado);
    Serial.println(" cm");
    Serial.print("MODA: distancia ");
    Serial.print(moda);
    Serial.println(" cm");
   
    return resultado;
}