Funcionamiento conjunto de los sensores
Funcionamiento
En este apartado trataremos el emsamblaje conjunto de todos los sensores vistos previamente y su funcionamiento conjunto mediante Python.
El funcionamiento del programa sin entrar en mucho detalle es el siguiente:
- Arranque automático del programa cada vez que se enciende la Raspberry.
- Un scrip que siempre está en funcionamiento y que mediante la librería
apscheduler
que instalamos en la configuración de la Raspberry Pi leerá los datos de todos los sensores a cada hora en punto soportando fallos de lectura y dando un valor predeterminado en el caso de que no se pueda leer el dato. - Una vez tiene todos los datos se envían los datos a una URL configurada en el archivo
acceso_web.py
.
Cableado
Aquí lo único que se está haciendo es mostrar todos los cableados que se han estado haciendo en los anteriores post y representarlos de forma conjunta:

Te puedes ayudar del esquema para ver mejor las conexiones realizadas.
Código
Todo el proyecto está en github y te invito a ver todo el código ahí ya que estará actualizado y podrás ver más a fondo el funcionamiento del programa
Inicio automático
Lo primero que teneremos que hacer es decirle al sistema que inicie el programa al encenderse la Raspberry Pi. Esto lo conseguiremos yendo a un terminar y escribiendo crontab -e
. Se nos abrirá el crontrab y veremos muchas líneas comentadas, al final del documento escribiremos @reboot /usr/bin/python3 /home/pi/Meteo/__init__.py >> ~/cron.log 2>&1
.
Expliquemos que hace cada parte del comando que hemos puesto en crontab:
@reboot
: Esto le está diciendo el sistema que cada vez que el sistema se encienda tiene que ejecutar el siguiente comando./urs/bin/python3
: Esto indica la ruta del ejecutable que va a usar, en este caso el ejecutable es el interprete de python3./home/pi/Meteo/__init__.py
: es la ruta del programa que tiene que ejecutar siendo __init__.py el archivo por el que empieza la ejecución.>> ~/cron.log 2>&1
: Le estamos diciendo que guarde registro de todo lo que debería ir saliendo en el terminal por si ocurriese algún problema poder verlo con facilidad.
Con este código escrito en el crontab nos aseguramos que aunque la estación meteorológica pierda corriente un momento esta se pondrá opertativa automaticamente sin la necesidad de iniciar el programa de forma manual.
Lógica del programa
El archivo __init__.py lleva la lógica del programa y es quien se encarga de la recogida de datos, su tratamiento y envío al servidor de estos. Se puede dividir en distintos bloques.
Constructor
Al final del programa se crea un constructor de la clase principal haciendo así que se ejecute su construcor y con ello todo el proceso necesario para obtener los datos. Justo despues de la creación del objeto podemos ver un bucle infinito while True
que evita que el programa se cierre cuando finaliza la ejecución ya que no tiene encuenta al scheduler de Python como código en ejecución.
Entrando en el funcionamiento del constructor se puede ver que se realizan varias acciones:
- Nada más empezar nos encontramos con el objeto logging el cual nos permite guardar en un archivo todo el funcionamiento del programa por si ocurriese algún problema poder seguir la traza del problema. Se le pueden poner varios parámetros de entrada, en mi caso han sido
filename='registro.log'
el cual pone el nombre del documento,level=logging.INFO
que especifica el nivel de información que queremos, yo lo tengo en info porque aun está en desarrollo el programa, y por último le he puesto el parámetroformat='%(asctime)s %(message)s'
que hace cuando escriba cualquier mensaje de logging lo haga con la fecha por delante para saber el momento exacto del error. - Inicilo de las clases VelocidadViento(), Pluviometro() y sensores MQ. Esto hace que tanto el viento como la lluvia se vayan leyendo durante toda la hora para luego poder luego saber las rachas de viento, velocidad media y cantidad de agua caida. Esto se tiene que hacer así porque si no se inician estas clases no sabriamos cuantos pulsos han enviado estos sensores. Se pone un intervalo de dos segundo en el inicio de cada clase ya que al hacerlos todos juntos sin tiempo entre medias en mi caso no leía los valores de los sensores adecuadamente.
- Inicio del ciclo se crea una rutina en el programa gracias al scheduler el cual nos permite iniciar todo el proceso de recogida, procesamiento y envío de datos a cada hora en punto.
Con lo comentado anteriormente el constructor queda de la siguiente forma:
def __init__(self):
# nos guarda toda la información de lo ocurridoe en el programa
logging.basicConfig(filename='registro.log',
level=logging.ERROR,
format='%(asctime)s %(message)s')
self.llamar_constructores()
# Inicia la recogida de datos y envío cada hora
scheda = BackgroundScheduler()
scheda.add_job(self.iniciar_ciclo, 'cron', minute='00')
scheda.start()
def llamar_constructores(self):
'''Llama a los constructores de los sensores que tienen que hacer
lectura continua durante todo el ciclo.'''
tiempo = 2
self.objeto_sensor_viento = VelocidadViento()
time.sleep(tiempo)
self.objeto_sensor_lluvia = Pluviometro()
time.sleep(tiempo)
self.objeto_sensor_mq2 = MQ2()
time.sleep(tiempo)
self.objeto_sensor_mq3 = MQ3()
time.sleep(tiempo)
self.objeto_sensor_mq4 = MQ4()
time.sleep(tiempo)
self.objeto_sensor_mq5 = MQ5()
time.sleep(tiempo)
self.objeto_sensor_mq7 = MQ7()
time.sleep(tiempo)
self.objeto_sensor_mq8 = MQ8()
time.sleep(tiempo)
self.objeto_sensor_mq9 = MQ9()
time.sleep(tiempo)
self.objeto_sensor_mq135 = MQ135()
Iniciar ciclo
En este método se ve de forma sencilla todo el funcionamiento del programa. Llama a la clase que tiene los datos de acceso al servidor y monta la URL principal junto con la fecha de los datos tomados. Se llama al método de lectura de los sensores y con los valores de los sensores y la URL se pasan a enviar datos que envía los datos al servidor.
def iniciar_ciclo(self):
'''Recoge los datos de los sensores y envía los datos al servidor'''
acceso_web = AccesoWeb()
link = acceso_web.get_dominio() + acceso_web.get_clave_servidor()
link += self.leer_fecha()
datos = self.recoger_datos()
self.enviar_datos(link, datos)
Recoge datos
Este apartado se hacen llamadas a todos los sensores para que devuelvan los valores en formato JSON para que así no haya problemas de compatibilidad en programa. Todos los valores se han registrado en la variable datos y es lo que devolvemos en el método.
def recoger_datos(self):
'''Recoge los datos de todos los sensores y los devuelve en un json.'''
datos = {}
datos = {**datos, **self.leer_sht31(0x44)} # Temperatura ambiental
datos = {**datos, **self.leer_bmp180()}
datos = {**datos, **self.leer_tsl2561()}
datos = {**datos, **self.leer_direccion_viento()}
datos = {**datos, **self.leer_viento()}
datos = {**datos, **self.leer_lluvia()}
datos = {**datos, **self.leer_sht31(0x45)} # Temperatura caja de gases
datos = {**datos, **self.leer_mq2()}
datos = {**datos, **self.leer_mq3()}
datos = {**datos, **self.leer_mq4()}
datos = {**datos, **self.leer_mq5()}
datos = {**datos, **self.leer_mq7()}
datos = {**datos, **self.leer_mq8()}
datos = {**datos, **self.leer_mq9()}
datos = {**datos, **self.leer_mq135()}
return datos
Recoge datos de cada sensor
Todos los sensore siguen el mismo patrón para recoger los datos por lo que viendo se han visto practicamente todos.
Se tienen que hacer los siguientes pasos para poder leer los datos del sensor:
- Importar clase del sensor: Si recordamos cada sensor tiene su propia clase y en un archivo por separado para mejorar la legibilidad del programa. Para importar por ejemplo el sensor BMP180 tenemos que escribir
from sensor_bmp180 import Bmp180
al principio del archivo principal. El sensor_bmp180 se refiere al nombre del archivo y Bmp180 es el nombre de la clase que tiene en su interior el archivo. - Llamar al constructor: Tenemos que crear un objeto del sensor que queremos leer. Llamando al constructor podemos crear el objeto que de forma automática ya ha recogido los valores del sensor. Si hacemos en ejemplo con el sensor BMP180 quedaria
objeto_bmp180 = Bmp180()
. - Creamos la variable
datos
y la iniciamos como un JSON vacío. - Guardar los datos: el objeto que hemos creado contiene los valores del sensor por lo que simplemente tenemos que acceder a los métodos get que creamos en su momento para cada parámetro del sensor y guardar esos valores. Un ejemplo sería
datos["presionBMP180"] = objeto_bmp180.get_presion()
. - Devolver datos: a la hora de devolver los datos simplemente hacemos return de la variable datos que contiene todos los valores.
Si juntamos todo lo comentado en un bloque de código lo veremos de la siguiente forma:
@classmethod
def leer_bmp180(cls):
'''Crea el objeto del sensor Bmp180 y recoge la presión, temperatura
y altura'''
datos = {}
objeto_bmp180 = Bmp180()
datos["presionBMP180"] = objeto_bmp180.get_presion()
datos["temperaturaBMP180"] = objeto_bmp180.get_temperatura()
datos["alturaBMP180"] = objeto_bmp180.get_altura()
return datos
Enviar datos
El método enviar datos recibe las variables link y datos. Usamos la librería requests de Python para enviar los datos al servidor. En mi caso le he puesto un time out de 300 segundos. En el caso de que el servidor falle a la hora de recibir los datos la librería requests volverá intentarlos de nuevo pasado un tiempo.
def enviar_datos(self, link, datos):
'''Envia los datos al servidor'''
logging.debug(datos)
requests.get(link, params=datos, timeout=300)
Con todo esto se termina la parte de la estación meteorológica. En un futuro mostraré como hacer la parte del backend, base de datos y mostrar los datos con gráficas y demás. Espero que la guía te haya sido útil y puedes contactar conmigo si tienes alguna duda con algo.