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
Publicar un comentario