Mostrando entradas con la etiqueta python. Mostrar todas las entradas
Mostrando entradas con la etiqueta python. Mostrar todas las entradas

martes, 12 de febrero de 2019

0

Proyecto: Análisis de los datos

Con los datos obtenidos, y a modo de conclusión, se puede realizar un análisis, que es el objetivo principal de una aplicación IoT como esta. Por lo tanto, se ha elegido la realización de un control difuso para determinar la sensación térmica empleando las dos variables de las que se dispone: humedad y temperatura.

Un controlador difuso de estas características dispone de una serie de reglas que se van a evaluar para cada punto. De cada uno de ellos, se pretende extraer un estado como el se la siguiente tabla:



Pero antes de seleccionar uno de los estados resultado de esta tabla, se debe conocer a qué clase pertenecen la temperatura y humedad, y para ello se va a realizar una evaluación con funciones trapezoidales de cada uno de los puntos.
Para ello, se va a dividir en dos puntos:


Temperatura

La temperatura puede variar desde valores negativos pequeños hasta valores no por encima de los 40, debido a que la zona en la que residimos no tiene picos extremos, los datos tenderán a estar muy agrupados. Esto va a dificultar la calidad de la medida, ya que va a ser mas complicado establecer la diferencia entre un grupo y otro. La distinción que se ha realizado es la siguiente:



Para valores muy bajos se considera frío, las temperaturas medias estarán entre fresco y templado y las extremas por encima se situaran en caluroso. La manera de evaluar donde se sitúa el punto es la siguiente:
Cuando se analiza una temperatura, por ejemplo 17 grados, esta cruza varias rectas, en concreto la de fresco y templado. La clase que se le va a aplicar a esta temperatura, sera el valor más alto obtenido al cruzar por ellas, es decir, unos 0,7 en templado y unos 0,3 en fresco. Por lo tanto, el valor más alto indica que 17 grados es una temperatura templada mayoritariamente. 
Para el análisis de los puntos, se ha creado una estructura condicional en MatLAB:


Esta estructura devuelve siempre 4 valores, que corresponden a las clases. El valor más alto define la clase de temperatura.

Humedad

Para la humedad se va a realizar un análisis idéntico. Sin embargo, en este caso, se ha decidido realizar una división en tres clases de la zona de la gráfica, que oscila entre el 0 y el 100%:

Esto deja tres rangos situados entre baja, suave y alta. La función de evaluación va a ser idéntica a la anterior, devolviendo tres funciones con valor entre cero y la unidad:


Evaluación

Entonces, una vez analizadas ambas variables, toca a continuación realizar una serie de condiciones que encajen con la primera tabla expuesta:

  • Si la temperatura es fría y la humedad baja, entonces la sensación es soportable.
  • Si la temperatura es fría y la humedad suave, entonces la sensación es mala.
  • Si la temperatura es fría y la humedad alta, entonces la sensación es inclemente.
  • Si la temperatura es fresca y la humedad baja, entonces la sensación es agradable.
  • Si la temperatura es fresca y la humedad suave, entonces la sensación es soportable.
  • Si la temperatura es fresca y la humedad alta, entonces la sensación es mala.
Y así con todas las condiciones que dan como resultado un único valor dentro de la tabla de resultados.

Sin embargo, la forma de programar estas funciones, difiere de las anteriores. Entre otros métodos, se ha elegido la funciona mínima como método de evaluación, que consiste en buscar el valor mínimo de las funciones cruzadas que se han visto previamente. Por ejemplo:

  • Estado 1: La temperatura es fría y la humedad es baja. Las funciones previas darían como resultado 1 en temperatura y 1 en humedad, por lo que el valor mínimo de ambos es 1. Al ser el valor máximo, con toda probabilidad la sensación térmica es soportable.
Todas estas funciones se programan de esta manera:



Esta función entonces devuelve la probabilidad de que la temperatura pertenezca a uno de los estados de la tabla de sensaciones térmicas. Sin embargo, siguiendo con la programación, se puede depurar un programa que devuelva exactamente la sensación térmica:

La ejecución de esta función devuelve un caso como este:




Con esto se puede analizar del lote de datos, por ejemplo una muestra cada tiempo configurable:


Se ha realizado una división exacta cada 60 muestras, lo que equivale a 10 minutos. Esto devuelve la siguiente ejecución:



lunes, 11 de febrero de 2019

0

Proyecto: Lectura de datos y subida a ThingSpeak

A estas alturas ya se debería tener la Raspberry completamente configurada, el circuito montado, las librerías necesarias descargadas y la conexión a Internet realizada. Si ves que te falta algún paso, puedes volver a visitar las respectivas entradas, aunque si lo que te falta es la librería se recuerda a continuación:

En este proyecto se va a trabajar con Python 2.7, por lo que habrá que descargar la librería correspondiente que permite trabajar con estos sensores. Se trata de Adafruit_Python_DHT. La forma más fácil de instalar esta librería es mediante el comando sudo pip install Adafruit_DHT. En caso de no reconocerse el comando pip, habrá que instalar pip mediante sudo apt-get install pip y volver a intentarlo.

Recordamos también cuáles eran los requerimientos que debía cumplir el proyecto:
  • Creación automática del canal sin intervención del usuario.
  • Muestreo cada 10 segundos.

Ya se vio en la entrada de la Sesión 03 y 04 cómo subir datos ThingSpeak mediante un programa escrito en Python. Puede ser conveniente repasar esa entrada, más simple, antes de meterse de lleno en esta.

A continuación, se va a ir explicando paso a paso cómo se ha montado el código. Si quieres saltarte toda la explicación y solo te interesa el código, puedes ir directamente al final de la entrada o incluso al repositorio de GitHub.

Lectura del sensor
Primero hay que preparar las variables y librerías necesarias para trabajar con el sensor.

import Adafruit_DHT
sensor = Adafruit_DHT.DHT11
pin= 23 #conectado al PIN GPIO23

Con esto se tiene importada la librería, definido el Pin por el cual va a entrar los datos y definido el tipo de sensor que se va a usar. El siguiente paso es la lectura del sensor. Para ello se cuenta con dos funciones: Adafruit_DHT.read_retry(sensor, pin) y Adafruit_DHT.read(sensor, pin). La diferencia entre ambas en que retry se ejecuta cada 2 segundos hasta que haya dato, o bien hasta que haya habido 15 intentos. Read lee el dato, pero no asegura que vaya a devolver algo porque no reintenta la lectura.

Como se quieren datos cada 10 segundos y retry no podría asegurarlo, solo se va a usar retry en la primera medida y a partir de ahí read. En caso de que read no encontrara dato, se copiaría el dato anterior.

Como la humedad y la temperatura de una estación meteorológica no son datos que vayan a sufrir variaciones muy rápidas, se considera que es una buena aproximación, aunque exista riesgo de que una variación se detecte diez segundos más tarde. Así, esa parte del código será algo parecido a lo siguiente (recuerda que al final de la entrada tendrás el código completo y preparado para copiar):

#LECTURA DEL SENSOR
if primerflag == 0:
  hum, temp = Adafruit_DHT.read_retry(sensor, pin
  if hum is not None and temp is not None:
    print('Temperatura={0:0.1f}*C Humedad={1:0.1f}%'.format(temp, hum))
    hum01 = hum
    temp01 = temp
    primerflag = 1 #ya se ha obtenido el primer dato
else:
  # No se quiere perder el tiempo con reintento
  # Temp y Hum varían lento, se acepta el margen de error
  # La máxima diferencia con una medida real y una repetida serían 10 segundos
  hum, temp = Adafruit_DHT.read(sensor, pin)
  if hum is not None and temp is not None:
    print('Temperatura={0:0.1f}*C Humedad={1:0.1f}%'.format(temp, hum))
    hum01 = hum
    temp01 = temp
  else:
    hum01 = hum02
    temp01 = temp02
# FIN LECTURA DEL SENSOR

Creación de los canales
La plataforma de ThingSpeak solo permite la subida de datos cada 15 segundos. Por tanto, para “saltar” esa barrera y tener datos cada 10 segundos, se va a trabajar con dos canales. Los datos recogidos del sensor se irán subiendo de forma alterna a un canal y a otro. Por ello, cuando se quieran usar, habrá que montar toda la ristra de datos alternando entre un canal y otro.

La creación de los canales se hace de forma análoga a la entrada de la sesión 03 y 04: primero se crea la conexión TCP. Una vez se confirma se pasa a la creación del canal. La forma de crear los canales es idéntica.

#CREACIÓN DE CANAL 01
#petición HTTP y obtención de respuesta
print("Creando canal 01..."),
method = "POST"
uri = "/channels.json"
headers_01= {'Host': server,
            'Content-Type': 'application/x-www-form-urlencoded'}
payload_01= {'api_key': USER_API_KEY,
            'public_flag':'true',
            'name' : 'Canal 01 automático.',
            'field1' : 'Humedad [%RH]',
            'field2' : 'Temperatura [ºC]'}
payload_01_encoded = urllib.urlencode(payload_01)
headers_01['Content-Length'] = len(payload_01_encoded)
print("\t Enviando petición para crear canal 01...")
connTCP.request(method, uri, body=payload_01_encoded, headers=headers_01)

respuesta_01= connTCP.getresponse()
status_01 = respuesta_01.status
print ("\t Petición de creación canal 01: " + str(status_01))

#Hay que coger los datos del canal para usar más adelante
contenido_01= respuesta_01.read()
# copiamos contenido en formato json a un dict
contenido_01_json = json.loads(contenido_01)
CHANNEL_ID_01 = contenido_01_json['id'
WRITE_API_KEY_01 = contenido_01_json['api_keys'][0]['api_key']

# FIN CREACIÓN DE CANAL 01

ACTUALIZACIÓN (14/02/19): ¡Muy importante! Desde esta misma semana ThingSpeak ha limitado el número de canales en las cuentas gratuitas a cuatro. Si intentas crear un canal teniendo ya los cuatro, recibirás un código 402. 
En teoría ThingSpeak ha avisado a todos los usuarios, pero es posible que el email haya acabado en la bandeja de Spam.

Subida a los canales
De nuevo, este proceso es idéntico al explicado en entradas anteriores. La única diferencia radicará en que ahora, tras recibir la respuesta HTTP se usará el comando Respuesta.read() para asegurarnos de leer correctamente el contenido de la misma. Esto significa que el código será algo parecido a lo siguiente:

# SUBIR DATO AL CANAL 01
# se crea una petición HTTP que sube datos
method = "POST"
relativ_uri = "/update.json"
headers = {'Host' : server,
          'Content-Type' : 'application/x-www-form-urlencoded'}
           payload = {'api_key' : WRITE_API_KEY_01,
          'field1' : hum01,
          'field2' : temp01}
payload_encoded = urllib.urlencode(payload)
headers['Content-Length'] = len(payload_encoded)

# se envía la petición
print("Enviando petición HTTP para subir al canal 01...")
connTCP.request(method, relativ_uri, body=payload_encoded, headers=headers)
#print("Petición enviada!")

# se recibe la respuesta
print("Esperando respuesta HTTP...")
respuesta = connTCP.getresponse()
respuesta.read()
status = respuesta.status
print (str(status))
SubidaCanal01=time.time() #cuando se recibe el 200 se acaba de subir el dato
# FIN SUBIR DATO CANAL 01

Subida cada 10 segundos
Para realizar correctamente dicha subida deberá ocurrir en canales alternativos, y habrá que realizar un control del tiempo. Para ello se importará la librería time mediante el comando import time. Y se establece un timeout = 9.

Lo ideal para conseguir subir cada 10 segundos sería trabajar o bien con MQTT (ya que no se requiere de respuesta al trabajar en modo publisher/subscriber) o bien con real time. Debido a las limitaciones propias de ser novatos en esto, se va a usar TCP y se un valor de timeout menor para compensar el tiempo de ejecución el código. Además de ello, en cuanto se reciba una respuesta 200 que indique que los datos se han subido correctamente, se usará el comando: SubidaCanal01=time.time(). De esta forma en la variable SubidaCanal01 se tendrá el momento en el que ha terminado la subida. A continuación, se iniciará un bucle que medirá el tiempo y permitirá avanzar al código cuando se lleguen a esos 10 segundos de espera. Se hará de forma análoga con el canal 02.

# ESPERAR PARA SUBIR
Medida01 = time.time()
DeltaTiempo2 = Medida01 - SubidaCanal02
while (DeltaTiempo2 Medida01 = time.time() #loop 10 segundos
  Medida01 = time.time()
  DeltaTiempo2 = Medida01 - SubidaCanal02
# FIN ESPERAR PARA SUBIR


El resultado de la subida se puede visualizar como gráfica en la página de ThingSpeak. A continuación se muestra el del canal 01, pero el del canal 02 es similar:


El código completo lo tenéis a continuación. En lo que respecta al proyecto en sí, el siguiente paso es descargar estos datos y graficarlos.

sábado, 9 de febrero de 2019

0

Tema 03 - Sesion 06 (31/01/19)

En esta practica se va a realizar una subida y lectura de datos mediante otro protocolo: MQTT

Este protocolo se basa en el modelo publicista-subscriptor, en el que el que produce los datos, los sube a la red, y el subscriptor dispone de ellos cuando quiera. Por lo tanto, no existe un dialogo constante entre ambos, en el que cada cierto tiempo definido se consultan dichos datos.

Para esta práctica se va a definir un broker, que es el que produce los datos. A su vez, se va a crear un tema. Como subscriptor, te puedes inscribir en distintos temas de tu interés. En este ejemplo, hemos creado a un canal en el que se va a subir la información del consumo de CPU y RAM, mediante el protocolo MQTT:

lunes, 28 de enero de 2019

0

Tema 03 - Sesion 03 y 04 (24/01/19)


En la clase de hoy se ha hecho uso de la herramienta de programación Python para la realización de peticiones HTTP en ThingSpeak similar a lo realizado previamente. La sesión de hoy se va a dividir en dos ejercicios.


EJERCICIO 1: Visualización del uso de RAM y CPU del pc y envío de datos a ThingSpeak  

En este ejercicio, se va a realizar un programa en la herramienta Python que lea el consumo de CPU y RAM del ordenador para después subirlo a la red mediante la API REST que hemos visto con anterioridad, con un previo borrado del canal.

Lo primero de todo va a ser abrir Python y descargar la librería Psutil, que se realiza escribiendo en el terminal de la aplicación: pip install psutil. Este comando ejecuta la instalación directamente, sin necesidad de pasos adicionales. Una vez que esté instalada, estamos listos para la realización del ejercicio.  

En cualquier programa de Python, el primer paso es añadir las librerías:
import psutil
import httplib
import urllib
Estas tres librerías son las que van a permitir realizar, tanto la lectura del consumo, como las peticiones HTTP necesarias para subir los datos a la red.

A continuación, se debe realizar una conexión TCP al servidor deseado, por lo que se añade tanto la dirección del servidor como la definición de la conexión:

server = 'api.thingspeak.com'
connTCP = httplib.HTTPSConnection(server)
Write_apkey = 'GTXNVDI95SFSUDRQ'
Delete_apkey = 'KEJU9WAA6A7VQS2C'
Se pueden poner visualizaciones con print a modo de depuración, es decir, a la hora de ejecutar el código, se se ejecuta la linea print, significa que el programa hasta esa linea es correcto. Se realiza la conexión:
print("Estableciendo conexion TCP..."),
connTCP.connect()
print("Conexion TCP establecida")
Una vez realizada la conexión, debemos establecer las reglas del diálogo con el servidor. Empleando la información obtenida del REST API, se incluyen aquellos campos necesarios, que son el formato y la llave de escritura de la que disponemos en nuestra pagina de ThingSpeak.

En este código se puede ver el recurso DELETE, la URI relativa a la que apunta el programa, las cabeceras y el contenido del mensaje. Debido a que el contenido del mensaje es una librería Python que el servidor web puede no reconocer, se decodifica a json para realizar una petición que sí entienda.

La última línea sirve para añadir la longitud del mensaje en la librería de la cabecera.
methodC1 = "DELETE"
relative_uriC1 = "/channels/680589/feeds.json"
headersC1 = {'Host': server,
           'Content-Type': 'application/x-www-form-urlencoded'}
payloadC1 = {'api_key': Delete_apkey}
payload_encodedC1 = urllib.urlencode(payloadC1)
headersC1['Content-Length'] = len(payload_encodedC1)
Para realizar la conexión y el envío de la petición, se emplea el código a continuación:
connTCP.request(methodC1, relative_uriC1, body=payload_encodedC1, 
headers=headersC1)
respuestaC1 = connTCP.getresponse()
statusC1 = respuestaC1.status
print(str(statusC1))
El comando STATUS nos va a devolver el resultado de la conexión, siendo 200 una petición correcta.

Con esta petición, se ha solicitado el borrado del canal previo a la subida de los datos.
Una vez estén los datos borrados, se va a realizar una subida de nuevos datos, para lo cual se va a realizar una estructura WHILE que va a ejecutar indefinidamente el segmento siguiente:

try:
    while(True):
        cpuPercent = psutil.cpu_percent(interval=15)
        ramPercent = psutil.virtual_memory().percent

        print("%CPU: " + str(cpuPercent)),
        print("\t%RAM: " + str(ramPercent))

        method = "POST"
        relative_uri = "/update.json"
        headers = {'Host' : server,
                   'Content-Type' : 
                      'application/x-www-form-urlencoded'}
        payload ={'api_key' : Write_apkey,
                  'field1': cpuPercent,
                  'field2': ramPercent}

        payload_encoded = urllib.urlencode(payload)
        headers['Content-Length'] = len(payload_encoded)

        print("Enviando peticion HTTP..."),
        connTCP.request(method, relative_uri, body=payload_encoded, 
                        headers=headers)
        print("Peticion enviada")
        print("Esperando respuesta HTTP...")

        respuesta = connTCP.getresponse()
        status = respuesta.status
        print(str(status))
Con una estructura similar al borrado pero empleando el recurso POST, se envian los datos al servidor realizando la peticion HTTP.
Para detener la ejecución del programa, se presiona CTRL+C, y se adjunta una linea de comandos para la realización de una secuencia de apropiada, junto con el cierre de la conexión:

except KeyboardInterrupt:
    connTCP.close()
    print("Se ha pulsado CTRL+C. Saliendo del programa")


EJERCICIO 2: Creación de un canal, subida de datos y borrado del canal al finalizar

En este ejercicio, como indica en el título, se va a crear un código que cree un canal, obtenga la información necesaria para subir datos a ese canal, y después borrarlo a la salida de la ejecución. El código es el siguiente:

ir arriba