Termostato Raspberry
Con la reciente muerte de termostato inteligente de NetAtmo a causa de una simple pila sulfatada me he decidido a crear el mio propio con la raspberry y un poco de Python. Iré escribiendo aquí los procesos con el mismo.
Archivo de configuración
Puesto que la idea es construir un script que se ejecute cada determinado tiempo, necesito un archivo que dote al sistema de persistencia de algunas variables. Después de barajar varias alternativas, he optado por guardar los datos de configuración en un archivo “config.json” ubicado en la carpeta raiz del usario que ejecuta el script.
El manejo de este tipo de archivos en python es muy sencillo y queda sentenciado con sólo seis líneas de código.
import json
with open('/home/pi/config.json', 'r') as archivo_json:
data=archivo_json.read()
datos_json = json.loads(data)
...
...
...
...
...
with open("config.json", "w") as archivo_json:
json.dump(datos_json, archivo_json, indent = 4)
Al principio del script hay que importar la librería “json”, abrir el archivo en modo de lectura, leer el contenido y asignar este al dicionario “json_data”. Una vez realizadas las modificaciones necesarias en el diccionario a lo largo del script principal hay que abrir nuevamente el archivo “config.json”, aunque esta vez en modo de escritura, y volcar sobre el el contenido del diccionario “datos_json” modificado.
La variables utilizadas en el almacenamiento externo de variables son:
- data - Lectura del archivo json
- datos_json - Diccionario de datos con variables
Inicialización de variables
La primera vez que abramos este archivo de configuración estará vacío por lo que nos tendremos que asegurar de inicializar unas “variables por defecto” para no tenga lugar el correspondiente error en caso de uqe alguna no exista o tenga un valor nulo. Esto se realiza mediante el siguiente código una vez que la variable “datos_json” ha sido creada a partir del contenido del archivo.
def inic_var(var,valor):
if var in datos_json:
if datos_json[var] == "":
datos_json[var] = valor
else:
datos_json[var] = valor
variables = {
"rele_estado": "off",
"rele_hora_cambio": datetime.now().strftime('%Y/%m/%d %H:%M:%S'),
"histeresis": 0.2,
"inercia": 750,
"inercia_rango": 1.013,
"consigna": 21.0,
"rele_total_on": 0,
"cons_manual": 21.0,
"hora_manual": "2020/12/30 16:45:32",
"min_manual": 60,
"modo_fuera": False,
"cons_fuera": 16,
"dec_casa_vacia": 1,
"horas": ["7:45","9:00","12:30","18:00","22:30"],
"temperaturas": ["22","20.5","21.5","22.5","20.5"]
}
for x in variables:
inic_var(x,variables[x])
Este código lo podemos dividir en tres partes, la función que inicializa las variables, el almacenamiento de las variables y la ejecución de la función para cada una de las variables.
La función inic_var
La función “inic_var” coge dos argumentos, por un lado el nombre de la variable a inicializar y por otro su valor por defecto. En un primer momento se comprueba si la variable existe dentro de “datos_json”, si lo está y su valor es nulo le asignará el valor por defecto. En caso de que la variable no esté definida, se creará asignándole su valor por defecto.
El diccionario de variables
Después de barajar varias alternativas, he considerado que la forma más clara de representar todo el conjunto de variables dentro de un elemento es haciendo uso de un diccionario que he denominado “variables” en el que cada clave será el nombre de la variable a inicializar y el valor de la misma el valor por defecto de esta variable que está siendo inicializada.
For sobre el diccionario
Con la lista de variables, su valor por defecto y la función para inicializar cada una de ellas preparada solo resta realizar una combinación de ambas mediante un lazo “For”. De esta forma cada variable existente dentro del diccionario “Variables” sera inicializada mediante la funcion “inic_var”
La variables utilizadas en la inicialización de variables son:
- variables - Diccionario con el nombre de variable y valor por defecto
Captura de la temperatura exterior
De momento no he sido capaz de utilizarla para nada más allá de la mera representación de la misma. Extraigo la información de la web de la AEMET para obtener el valor de la temperatura exterior. Todo mediante la clase Aemet que puedes ver en mi repositorio de GitHub.
Esta clase nos devolverá las siguientes variables:
- hora - Momento de la última toma
- temp_actual - Valor instantáneo de Tª obtenida
- temp_media - Valor medio diaro de la Tª
Captura de la temperatura interior
Para tomar la temperatura que tengo en casa voy a utilizar un DHT22 (sensor de humedad y temperatura) que, aunque no es lo más preciso del mundo, es suficientemente barato como para empezar a hacer pruebas. En el artículo de atareao se explica perfectamente como llevar a cabo esta medición. El propio sensor DHT22, una resistencia de 10k y tres cables para conectar al puerto GPIO de la Raspberry es odo lo que he necesitado.
Necesitamos una librería para capturar los datos del sensor. Esta se instalará en la raspberry medianta el comando sudo pip3 install Adafruit_DHT
siempre y cuando tengamos instalado Python3 y el gestos de paquetes pip.
import Adafruit_DHT as dht
datos_dht = dht.read_retry(dht.DHT22,4)
while datos_dht[0] > 100:
time.sleep(5)
datos_dht = dht.read_retry(dht.DHT22,4)
real_hume = round(datos_dht[0],2)
real_temp = round(datos_dht[1],2)
Sobre estas líneas aparece la parte de código en Python involucrada en la medición de la temperatura. Importación de la libreria ADAfruit-DHT, captura de los datos del sensor y un redondeo a dos decimales que es más que suficiente.
Alguna vez me ha dado un dato inválido la medición por lo que le he incrustado una condición de que siempre que la medición de humedad sea mayor del 100% (dato no válido) que espere 5 segundos y vuelva a medir.
Las variables utilizadas en la adquisición de temperatura son las siguientes:
- datos_dht - Tupla con los valores de la medida del sensor
- real_hume - Humedad medida y redondeada
- real_temp - Temperatura medida y redondeada
El relé de la caldera
Para poder enceder la caldera es necesario un relé. Lo más sencilo hubiera sido colocar uno que, a través del puerto GPIO, fuera directamente gobernado por la Raspberry pero tengo una serie de limitaciones en casa que no me han permitido hacerlo. Por eso necesito un relé que cumpla con lo siguiente:
- Alimentación de 220V
- Conexión wifi
- Relé seco (Sin tensión en lo contactos)
- Disponibilidad de API
Aunque no son muchas las limitaciones que le he impuesto, no me ha resultado fácil enontrar un relé que se adapte a ellas y el resultado es este.
Una combinación casera de un relé wifi sonoff, un transformador de 220V a 5V y un relé de 5V (todo perfectamente unido con cinta adhesiva) que si no quieres esperar y los compras en Amazon te constarán en total poco más de 20€ pero que si no tienes prisa se pueden encontar en Aliexpress por menos de la mitad.
Según lo he montado, la Raspberry menda a través de wifi el comando de activación al relé Sonnoff y, cuando este se activa alimenta al mini transformador que a su vez hace lo mismo con el relé seco de 5V. A través del contacto de este segundo relé activamos los contactos del termostato de la caldera. una combinación efectiva, fiable y económica.