Proyecto Estación Meteorológica 3: Obtención y Envío de Datos Mediante Python

Obtención y Envío de Datos Mediante Python

Una vez se ha configurado la Raspberry como es debido, se comienza la codificación. Lo primero que se codifica es el programa de Python. La función de este programa es el de crear automáticamente los canales necesarios en ThingSpeak, recoger datos del sensor y subir estos a los canales previamente mencionados.

Para ello, primero se observan las especificaciones del usuario para definir la problemática. Haciendo esto, se observa que hay que subir datos con una frecuencia de 10 segundos, pero los canales de ThingSpeak solo aceptan datos cada 15 segundos. Para solucionar este problema, se crean dos canales, a los cuales se suben datos alternamente.

En cuanto al código, lo primero es importar las librerías. Ya que la comunicación con ThingSpeak se hace mediante peticiones http, se importan las librerías urllib y httplib, además de json, la cual será necesaria para interpretar las respuestas. También se importan la librería de Adafruit_DHT para poder usar el sensor y time para tener un control del tiempo. Una vez importadas las librerías, se definen algunas constantes que se utilizarán durante el código, lo cual hará que el código esté más limpio y ayudará en caso de querer cambiar alguna constante.

########################################################################################################################
# Importar las librerias #
########################################################################################################################
import urllib
import httplib
import json
import Adafruit_DHT as DHT
import time
########################################################################################################################
# Definir constantes #
########################################################################################################################
server = 'api.thingspeak.com' # Servidor al cual se suben los datos
USER_API_KEY = 'xxxxxxxxxxxxxxxx' # En el perfil de ThingSpeak -> ThingSpeak settings -> User API Key
sensor_args = { '11' : DHT.DHT11,
'22' : DHT.DHT22,
'2302' : DHT.AM2302 } # Los distintos sensores en la libreria
sensor = sensor_args['11'] # El sensor especifico que se usa
pin = 4 # El pin al que se conecta el sensor
intervalo = 10 # Intervalo de tiempo entre los que se suben los datos

Después, se definen algunas funciones que se utilizarán durante el código. La primera de ellas es “create_channel”, la cual se encarga de crear los canales en ThingSpeak. Esta función tiene una entrada: el nombre que se desea poner al canal. En ella se crea una petición http de tipo POST y se envían las características que se desea que tenga el canal en formato formulario. Por último, se recibe la respuesta, la cual es traducida a json. Esto último es lo que devuelve la función.

La segunda función es “subir_datos”. Esta función se encarga de subir los datos leídos a los canales de ThingSpeak. Tiene 4 entradas, las cuales son el nombre del canal al que se quieren subir los datos, la clave API de escritura de dicho canal y los 2 datos que se desean subir. En este caso también se crea una petición http de tipo POST, en el cual se suben los datos en formato formulario.

########################################################################################################################
# Funciones #
########################################################################################################################
def create_channel(nombre_canal):
# Crear el canal para poder introducir nuevos datos
# https://es.mathworks.com/help/thingspeak/createchannel.html
print("Creando canal '" + nombre_canal + "'")
method = 'POST'
relative_uri = "/channels.json"
headers = {'Host': server,
'Content-Type': 'application/x-www-form-urlencoded'}
payload = {'api_key': USER_API_KEY,
'description': 'Informacion de temperatura y humedad obtenidas desde la estacion meteorologica',
'field1': 'Temperatura',
'field2': 'Humedad',
'name': nombre_canal,
'public_flag': 'true'}
# Codificar los datos a enviar en formato form ('field1'=5&...)
payload_encoded = urllib.urlencode(payload)
headers['Content-Length'] = len(payload_encoded)
# Enviar la peticion http
connTCP.request(method, relative_uri, body=payload_encoded, headers=headers)
# Guardar la respuesta recivida en una variable
respuesta = connTCP.getresponse()
# En caso de asi desearlo, se puede mostrar el estado en la respuesta (200 si todo ha ido bien)
#status = respuesta.status
# Leer la respuesta y guardarla en una variable (hay que hacerlo siempre que se haga una peticion)
contenido = respuesta.read()
contenido_json = json.loads(contenido) # Se copia contenido en formato json a un diccionario
return contenido_json
def subir_datos(canal, clave_API, dato1, dato2):
# Crear la peticion http que sube datos al canal de ThingSpeak
# https://es.mathworks.com/help/thingspeak/writedata.html
print("Subiendo datos a '" + canal + "'...")
method = "POST"
relative_uri = "/update.json"
headers = {'Host': server,
'Content-Type': 'application/x-www-form-urlencoded'}
payload = {'api_key': clave_API,
'field1': dato1,
'field2': dato2}
# Codificar los datos a enviar en formato form ('field1'=5&...)
payload_encoded = urllib.urlencode(payload)
headers['Content-Length'] = len(payload_encoded)
# Enviar la peticion http
connTCP.request(method, relative_uri, body=payload_encoded, headers=headers)
# Guardar la respuesta recivida en una variable
respuesta = connTCP.getresponse()
# En caso de asi desearlo, se puede mostrar el estado en la respuesta (200 si todo ha ido bien)
#status = respuesta.status
# Leer la respuesta y guardarla en una variable (hay que hacerlo siempre que se haga una peticion)
respuesta.read()
return

Una vez se ha definido todo lo necesario, comienza la ejecución acíclica del programa; es decir, la parte del código que solo se ejecuta una vez al comienzo de la ejecución. Lo primero es establecer una conexión TCP entre la Raspberry en la que correrá el programa y el servidor de ThingSpeak. Cuando la conexión ha sido establecida, se crean los canales con la función previamente definida. Desde la respuesta de dicha función, se pueden recuperar tanto las ID de los canales como las claves API de escritura de estos. Por último, se inicializan algunas variables que se utilizan durante la ejecución cíclica.

########################################################################################################################
# Establecer conexion TCP #
########################################################################################################################
# Con es la variable o el objeto que hace referencia a la conexion TCP
connTCP = httplib.HTTPSConnection(server)
print("Estableciendo conexion TCP...")
connTCP.connect() # Establecer conexion
print("Conexion TCP establecida\n")
########################################################################################################################
# Crear canales #
########################################################################################################################
contenido_json_1 = create_channel('Estacion meteorologica 1')
# Se consiguen las variables necesarias de la respuesta de la funcion
CHANNEL_ID_1 = contenido_json_1['id']
WRITE_API_KEY_1 = contenido_json_1['api_keys'][0]['api_key']
contenido_json_2 = create_channel('Estacion meteorologica 2')
# Se consiguen las variables necesarias de la respuesta de la funcion
CHANNEL_ID_2 = contenido_json_2['id']
WRITE_API_KEY_2 = contenido_json_2['api_keys'][0]['api_key']
########################################################################################################################
# Inicializacion de variables #
########################################################################################################################
hum = 0
temp = 0
humDefault = hum
tempDefault = temp
next_time = 0

Por último, se ejecuta el programa principal; es decir, la parte cíclica de este. En esta parte, se lee el sensor y se espera hasta que sea momento de subir los datos leídos. Durante esta espera, si los datos leídos por el sensor son erróneos o este no puede ser leído por algún fallo, es posible volver a leerlo hasta un máximo de 3 veces más. Tras estos intentos, si sigue habiendo problemas con los datos leídos, se sube un valor por defecto, el cual es el último dato leído. Al llegar el momento, se suben los datos con la función previamente definida. Esta parte se vuelve a repetir una vez más para subir los datos al segundo canal. Al finalizar este proceso, vuelve a iniciar el ciclo. Esta parte puede ser interrumpida en cualquier momento pulsando las teclas ctrl+c desde el teclado.

########################################################################################################################
# Programa principal #
########################################################################################################################
try:
while (True):
contador = 0
# Leer los datos del sensor
hum, temp = DHT.read(sensor, pin)
# El codigo se queda en bucle en el while hasta que haya pasado el margen de tiempo especificado por el usuario
while time.time() < next_time:
# Si no se han cogido medidas o estas son erroneas, se vuelven a tomar
if ((hum is None and temp is None) or (hum > 100)) and contador < 3:
time.sleep(2)
hum, temp = DHT.read(sensor, pin)
contador = contador + 1
else:
pass
# Si no hay datos o siguen siendo erroneos tras 3 intentos, se muestra un mensaje de error y
# se resube el ultimo valor
if ((hum is None and temp is None) or (hum > 100)) and contador == 3:
hum = humDefault
temp = tempDefault
print("Fallo de lectura. Se reenviaran los ultimos datos.")
# Los datos se suben a ThingSpeak
print("\nTemperatura: " + str(temp) + " C\tHumedad: " + str(hum) + "%")
subir_datos('Estacion meteorologica 1', WRITE_API_KEY_1, temp, hum)
humDefault = hum
tempDefault = temp
next_time = time.time() + intervalo - 0.1 # Se resta 0,1 porque se necesitara un tiempo para subir los datos
contador = 0
# Leer los datos del sensor
hum, temp = DHT.read(sensor, pin)
# El codigo se queda en bucle en el while hasta que haya pasado el margen de tiempo especificado por el usuario
while time.time() < next_time:
# Si no se han cogido medidas o estas son erroneas, se vuelven a tomar
if ((hum is None and temp is None) or (hum > 100)) and contador < 3:
time.sleep(2)
hum, temp = DHT.read(sensor, pin)
contador = contador + 1
else:
pass
# Si no hay datos o siguen siendo erroneos tras 3 intentos, se muestra un mensaje de error y
# se resube el ultimo valor
if hum is None and temp is None and contador == 3:
hum = humDefault
temp = tempDefault
print("Fallo de lectura. Se reenviaran los ultimos datos.")
# Los datos se suben a ThingSpeak
print("\nTemperatura: " + str(temp) + " C\tHumedad: " + str(hum) + "%")
subir_datos('Estacion meteorologica 2', WRITE_API_KEY_2, temp, hum)
humDefault = hum
tempDefault = temp
next_time = time.time() + intervalo - 0.1 # Se resta 0,1 porque se necesitara un tiempo para subir los datos
except KeyboardInterrupt:
# Cuando se interrumpe el programa con el teclado (ctrl + C), se interrumpe el programa principal y se cierra la
# conexion TCP establecida con el servidor, finalizando el programa
connTCP.close()
print("\nSe ha pulsado Ctrl+C. Saliendo del programa...\n")

Así acaba el código de Python, el cual, al juntarlo todo, tiene el siguiente aspecto:

# Para poder introducir tildes y caracteres especiales
# encoding=utf8
"""
@Autor: Alexander Osa
@Descripcion:
@ Este programa crea un canal en ThingSpeak, lee los datos de temperatura y humedad del sensor DHT22 y los sube al
@ canal creado.
"""
########################################################################################################################
# Importar las librerias #
########################################################################################################################
import urllib
import httplib
import json
import Adafruit_DHT as DHT
import time
########################################################################################################################
# Definir constantes #
########################################################################################################################
server = 'api.thingspeak.com' # Servidor al cual se suben los datos
USER_API_KEY = 'xxxxxxxxxxxxxxxx' # En el perfil de ThingSpeak -> ThingSpeak settings -> User API Key
sensor_args = { '11' : DHT.DHT11,
'22' : DHT.DHT22,
'2302' : DHT.AM2302 } # Los distintos sensores en la libreria
sensor = sensor_args['11'] # El sensor especifico que se usa
pin = 4 # El pin al que se conecta el sensor
intervalo = 10 # Intervalo de tiempo entre los que se suben los datos
########################################################################################################################
# Funciones #
########################################################################################################################
def create_channel(nombre_canal):
# Crear el canal para poder introducir nuevos datos
# https://es.mathworks.com/help/thingspeak/createchannel.html
print("Creando canal '" + nombre_canal + "'")
method = 'POST'
relative_uri = "/channels.json"
headers = {'Host': server,
'Content-Type': 'application/x-www-form-urlencoded'}
payload = {'api_key': USER_API_KEY,
'description': 'Informacion de temperatura y humedad obtenidas desde la estacion meteorologica',
'field1': 'Temperatura',
'field2': 'Humedad',
'name': nombre_canal,
'public_flag': 'true'}
# Codificar los datos a enviar en formato form ('field1'=5&...)
payload_encoded = urllib.urlencode(payload)
headers['Content-Length'] = len(payload_encoded)
# Enviar la peticion http
connTCP.request(method, relative_uri, body=payload_encoded, headers=headers)
# Guardar la respuesta recivida en una variable
respuesta = connTCP.getresponse()
# En caso de asi desearlo, se puede mostrar el estado en la respuesta (200 si todo ha ido bien)
#status = respuesta.status
# Leer la respuesta y guardarla en una variable (hay que hacerlo siempre que se haga una peticion)
contenido = respuesta.read()
contenido_json = json.loads(contenido) # Se copia contenido en formato json a un diccionario
return contenido_json
def subir_datos(canal, clave_API, dato1, dato2):
# Crear la peticion http que sube datos al canal de ThingSpeak
# https://es.mathworks.com/help/thingspeak/writedata.html
print("Subiendo datos a '" + canal + "'...")
method = "POST"
relative_uri = "/update.json"
headers = {'Host': server,
'Content-Type': 'application/x-www-form-urlencoded'}
payload = {'api_key': clave_API,
'field1': dato1,
'field2': dato2}
# Codificar los datos a enviar en formato form ('field1'=5&...)
payload_encoded = urllib.urlencode(payload)
headers['Content-Length'] = len(payload_encoded)
# Enviar la peticion http
connTCP.request(method, relative_uri, body=payload_encoded, headers=headers)
# Guardar la respuesta recivida en una variable
respuesta = connTCP.getresponse()
# En caso de asi desearlo, se puede mostrar el estado en la respuesta (200 si todo ha ido bien)
#status = respuesta.status
# Leer la respuesta y guardarla en una variable (hay que hacerlo siempre que se haga una peticion)
respuesta.read()
return
########################################################################################################################
# Establecer conexion TCP #
########################################################################################################################
# Con es la variable o el objeto que hace referencia a la conexion TCP
connTCP = httplib.HTTPSConnection(server)
print("Estableciendo conexion TCP...")
connTCP.connect() # Establecer conexion
print("Conexion TCP establecida\n")
########################################################################################################################
# Crear canales #
########################################################################################################################
contenido_json_1 = create_channel('Estacion meteorologica 1')
# Se consiguen las variables necesarias de la respuesta de la funcion
CHANNEL_ID_1 = contenido_json_1['id']
WRITE_API_KEY_1 = contenido_json_1['api_keys'][0]['api_key']
contenido_json_2 = create_channel('Estacion meteorologica 2')
# Se consiguen las variables necesarias de la respuesta de la funcion
CHANNEL_ID_2 = contenido_json_2['id']
WRITE_API_KEY_2 = contenido_json_2['api_keys'][0]['api_key']
########################################################################################################################
# Inicializacion de variables #
########################################################################################################################
hum = 0
temp = 0
humDefault = hum
tempDefault = temp
next_time = 0
########################################################################################################################
# Programa principal #
########################################################################################################################
try:
while (True):
contador = 0
# Leer los datos del sensor
hum, temp = DHT.read(sensor, pin)
# El codigo se queda en bucle en el while hasta que haya pasado el margen de tiempo especificado por el usuario
while time.time() < next_time:
# Si no se han cogido medidas o estas son erroneas, se vuelven a tomar
if ((hum is None and temp is None) or (hum > 100)) and contador < 3:
time.sleep(2)
hum, temp = DHT.read(sensor, pin)
contador = contador + 1
else:
pass
# Si no hay datos o siguen siendo erroneos tras 3 intentos, se muestra un mensaje de error y
# se resube el ultimo valor
if ((hum is None and temp is None) or (hum > 100)) and contador == 3:
hum = humDefault
temp = tempDefault
print("Fallo de lectura. Se reenviaran los ultimos datos.")
# Los datos se suben a ThingSpeak
print("\nTemperatura: " + str(temp) + " C\tHumedad: " + str(hum) + "%")
subir_datos('Estacion meteorologica 1', WRITE_API_KEY_1, temp, hum)
humDefault = hum
tempDefault = temp
next_time = time.time() + intervalo - 0.1 # Se resta 0,1 porque se necesitara un tiempo para subir los datos
contador = 0
# Leer los datos del sensor
hum, temp = DHT.read(sensor, pin)
# El codigo se queda en bucle en el while hasta que haya pasado el margen de tiempo especificado por el usuario
while time.time() < next_time:
# Si no se han cogido medidas o estas son erroneas, se vuelven a tomar
if ((hum is None and temp is None) or (hum > 100)) and contador < 3:
time.sleep(2)
hum, temp = DHT.read(sensor, pin)
contador = contador + 1
else:
pass
# Si no hay datos o siguen siendo erroneos tras 3 intentos, se muestra un mensaje de error y
# se resube el ultimo valor
if hum is None and temp is None and contador == 3:
hum = humDefault
temp = tempDefault
print("Fallo de lectura. Se reenviaran los ultimos datos.")
# Los datos se suben a ThingSpeak
print("\nTemperatura: " + str(temp) + " C\tHumedad: " + str(hum) + "%")
subir_datos('Estacion meteorologica 2', WRITE_API_KEY_2, temp, hum)
humDefault = hum
tempDefault = temp
next_time = time.time() + intervalo - 0.1 # Se resta 0,1 porque se necesitara un tiempo para subir los datos
except KeyboardInterrupt:
# Cuando se interrumpe el programa con el teclado (ctrl + C), se interrumpe el programa principal y se cierra la
# conexion TCP establecida con el servidor, finalizando el programa
connTCP.close()
print("\nSe ha pulsado Ctrl+C. Saliendo del programa...\n")

Comentarios

Entradas populares de este blog

Proyecto Estación Meteorológica 4: Visualización de Resultados Mediante HTML y D3.js

Diferentes técnicas de las redes neuronales