Suivi de température et humidité

Mon objectif était de pouvoir suivre et consulter la température des différentes pièces de l’appartement en temps quasi-réel à la fois sur une tablette fixée au mur et depuis un smartphone en dehors de l’appartement.

Ma solution repose sur plusieurs sondes disposées dans chaque pièce qui captent périodiquement la température et le taux d’humidité puis transmettent ces données à un serveur distant. Ce dernier conserve les données et héberge une application web permettant de les consulter via un smartphone ou un ordinateur. Au travers de webservices il met également ces données à disposition d’une tablette qui les affiche en continu.

Les températures et taux d'humidité sont affichées dans un encart en bas à droite
Les températures et taux d’humidité sont affichées dans un encart en bas à droite
Affichage des températures et taux d'humidité de chaque pièce
Affichage des températures et taux d’humidité
Suivi de l'évolution
Suivi de l’évolution

Les sondes de température et d’humidité

Matériel

Pour le choix du matériel des sondes 3 critères étaient importants :

  1. La capacité à définir la période de capture de la température et des taux d’humidité (toutes les 10 minutes) ;
  2. Pas d’émission radio (car elles sont actives toute la journée dans les chambres des enfants) ;
  3. Le coût.

Je me suis ainsi orienté vers le matériel suivant pour chacune des sondes :

  • Un Raspberry Pi Zero pour l’unité centrale (il dispose d’une connectique bien plus limitée que le Pi 3 mais il est également nettement moins cher (environ 5€ contre 35€) ;
  • Une sonde DHT22 qui permet de capter la température et le taux d’humidité. Cette sonde présente l’avantage d’être numérique et semblait la plus fiable dans cette gamme de prix (~3€) ;
  • Une résistance de 4,7KΩ (<1€) ;
  • Une carte réseau Ethernet en USB (car le Pi Zero ne dispose pas de connectique réseau) (~2,5€) ;
  • Un convertisseur micro-USB -> USB (~1€) ;
  • Le nécessaire pour le Pi Zero (carte SD, adaptateur secteur) ;
  • Un boitier pour le Pi Zero (non indispensable mais c’est plus propre) ;
  • Un adaptateur CPL car mes pièces ne disposent pas de prises ethernet et je ne souhaitais pas de Wi-Fi.

L’adaptateur CPL est de loin l’élément le plus coûteux, sans celui-ci l’ensemble tourne autour de 25€ par sonde.

Raspberry Pi Zero
Raspberry Pi Zero
sonde DHT22
sonde DHT22
carte réseau Ethernet en USB
carte réseau Ethernet en USB
convertisseur micro-USB - USB
convertisseur micro-USB – USB
Boitier du Pi Zero
Boitier du Pi Zero

Montage

Le montage est relativement simple, il consiste à souder la sonde sur le Raspberry.  Il faut prévoir une longueur de fil suffisante en fonction de l’emplacement de la sonde. Pour ma part, je pose le Raspberry au sol près de la prise électrique et je place la sonde à environ 1m du sol et non exposée aux courants d’air pour obtenir des valeurs de températures et de taux d’humidité les plus fiables possibles.

Voici le montage à réaliser :

Montage Raspberry Pi DHT22

Les branchements à effectuer sont :

  • La broche 1 de la sonde vers une broche de 3,3v du Pi Zero ;
  • La broche 2 de la sonde vers une broche d’entrée du Pi Zero (j’ai choisi la broche 4) ;
  • La broche 3 de la sonde n’est pas utilisée ;
  • La broche 4 de la sonde vers une broche terre du Pi Zero.
GPIO du Raspberry Pi Zero
GPIO du Raspberry Pi Zero

Logiciel

Il existe plusieurs distributions pour Raspberry, j’ai choisi l’officielle Raspbian basée sur Debian qui suffit largement à mes besoins.

Pour récupérer périodiquement la température et le taux d’humidité, j’ai choisi d’écrire une application en python reposant sur la bibliothèque d’Adafruit.

Première étape l’installation des pré-requis :

sudo apt-get update
sudo apt-get install build-essential python-dev python-openssl

Puis récupération de la bibliothèque :

git clone https://github.com/adafruit/Adafruit_Python_DHT.git
cd Adafruit_Python_DHT

Avant d’aller plus loin un hack est nécessaire :

A l’exécution la bibliothèque d’Adafruit détermine la version du Raspberry (Pi 1/Pi Zero ou Pi 2) sur la base de la version du processeur. Or il existe sur le marché plusieurs versions du Pi Zero (avec différentes versions du processeur). (Pour ma part j’ai deux versions différentes parmi mes 6 Pi Zero). Il est ainsi nécessaire de patcher la bibliothèque pour que le test fonctionne avec sa version du Pi Zero.

Il faut tout d’abord déterminer la version du processeur de son Pi Zero :

cat /proc/cpuinfo

Et chercher la ligne Hardware.

Sur l’un des miens, j’obtiens BCM2835.

pi@pi1:~ $ cat /proc/cpuinfo
processor       : 0
model name      : ARMv6-compatible processor rev 7 (v6l)
BogoMIPS        : 997.08
Features        : half thumb fastmult vfp edsp java tls
CPU implementer : 0x41
CPU architecture: 7
CPU variant     : 0x0
CPU part        : 0xb76
CPU revision    : 7

Hardware        : BCM2835
Revision        : 900093
Serial          : 000000004fc34801

Il faut ensuite éditer le fichier Adafruit_DHT/platform_detect.py pour indiquer cette version à la ligne 97 (if match.group(1) == ‘BCM2835’:) :

def pi_version():
     """Detect the version of the Raspberry Pi.  Returns either 1, 2 or
     None depending on if it's a Raspberry Pi 1 (model A, B, A+, B+),
     Raspberry Pi 2 (model B+), or not a Raspberry Pi.
     """
     # Check /proc/cpuinfo for the Hardware field value.
     # 2708 is pi 1
     # 2709 is pi 2
     # Anything else is not a pi.
     with open('/proc/cpuinfo', 'r') as infile:
         cpuinfo = infile.read()
     # Match a line like 'Hardware   : BCM2709'
     match = re.search('^Hardware\s+:\s+(\w+)

On peut maintenant compiler la bibliothèque :

sudo python setup.py install

Il reste ensuite a écrire un programme lancé périodiquement par un cron (pour ma part toutes les 10 minutes) qui interroge la sonde puis envoie les données au serveur. Afin de ne pas perdre de valeurs en cas de coupure réseau, si le programme ne parvient pas à envoyer les données au serveur, il les stocke temporairement dans un fichier pour les réémettre dès que la connexion fonctionne à nouveau.

#!/usr/bin/python
import sys
import os
from time import localtime, strftime
import Adafruit_DHT
import requests

# Reference de la sonde (DHT22), a ne pas modifier
sensor = 22

# Numero de la broche sur laquelle la sonde est soudee
pin = 4

# Identifiant de la piece dont les donnees sont capturees
room = ''

# Racine de l'url du webservice du serveur destinataires des donnees capturees
urlServeur = 'http://XXXXXXXX/'

# Fichier utilise pour stocker les donnees n'ayant pu etre transmise au serveur
error_sonde = '/home/pi/error_sonde'

# Nombre de tentative maximum de capture
nbTries = 4

current_date = strftime("%Y-%m-%d %H:%M:%S", localtime())
formatted_humidity = ''
formatted_temperature = ''

# Interroge la sonde pour obtenir la temperature et le taux d'humidite courante
# Si necessaire, reessaye 15 fois en attendant 2 secondes entre chaque tentatives
humidity, temperature = Adafruit_DHT.read_retry(sensor, pin)

# La capture des donnees peut ne pas fonctionner (cas rare), au besoin tente 'nbTries' fois
i = 1
while ((humidity is None or temperature is None) and i < nbTries):
    humidity, temperature = Adafruit_DHT.read_retry(sensor, pin)
    i = i + 1

# Si les donnees ont pu etre capturees
if humidity is not None and temperature is not None:
    # Formattage des donnees pour transmission au serveur
    formatted_humidity = '{0:0.0f}'.format(humidity)
    formatted_temperature = '{0:0.0f}'.format(temperature*100)
    url = urlServeur + 'Releve/'+room+'/'+formatted_temperature+'/'+formatted_humidity
    print(current_date+';'+room+';'+formatted_temperature+';'+formatted_humidity+';'+str(temperature)+';'+str(humidity)+';'+url)

    try:
        # Envoi des donnees collectees au serveur
        r = requests.get(url)

        # Si l'envoi a fonctionne, on envoie egalement les donnees qui n'avaient pu etre transmises lors de precedentes captures
        if os.path.exists(error_sonde):
            if os.path.getsize(error_sonde) > 0 :
                fil = [('file', ('file', open(error_sonde, 'rb'),'text'))]
                r4 = requests.post(url+ 'ReleveBulk', files=fil)

                # Vidage du contenu du fichier d'erreur
                f = open(error_sonde, 'w')
                f.close()
    except:
        print('erreur lors de l envoi des releves')

        # En cas d'erreur lors de l'envoi des donnees, celles-ci sont conservees dans le fichier des erreurs afin d'etre transmises des que la connexion est a nouveau fonctionnelle
        f = open(error_sonde, 'a')
        f.write(current_date+';'+room+';'+formatted_temperature+';'+formatted_humidity+'\n')
        f.close()

Pour la maintenance logicielle des Raspberry, j’utilise ansible qui permet d’effectuer des opérations groupées via SSH sur l’ensemble des 6 Raspberry. Je l’utilise essentiellement pour la mise à jour du système et le déploiement d’une nouvelle version du programme python.

Le serveur

Le serveur doit réceptionner les données brutes transmises par les sondes (température, humidité) pour :

  • Stocker ces données ;
  • Notifier la tablette que de nouvelles valeurs sont disponibles ;
  • Mettre à disposition de la tablette la dernière valeur de température et de taux d’humidité pour chacune des pièces ;
  • Héberger l’application web pour smartphone.

Les flux ressemblent à cela :

Ces webservices reposent sur la bibliothèque python flask pour réceptionner les valeurs brutes transmises par les sondes et pour répondre aux requêtes de la tablette 10′′. La couche de stockage repose sur une base de données relationnelle MySQL. Un webservice est dédié au superviseur nagios me permettant d’être notifié si une sonde ne transmet plus ses données. Cela m’évite de superviser directement les sondes (qui générerait du trafic réseau pour chaque sonde), seule la disponibilité de l’information depuis le serveur m’intéresse.

Les interfaces utilisateurs

Tablette 10′′

Je souhaitais disposer d’un écran fixé au mur affichant en permanence les températures dans les différentes pièces. J’ai choisi un Raspberry PI 3 avec un écran 10′’ tactile. Un affichage plus petit de type LCD aurait pu convenir mais je souhaitais profiter de cet écran pour afficher d’autres informations. Aujourd’hui il me permet également d’apporter d’autres services :

  • Afficher un tableau blanc (le contenu est modifiable depuis une web app sur nos smartphones) ;
  • Afficher les différentes listes de courses et permettre d’ajouter des articles parmi les 30 articles les plus ajoutés dans les deux derniers mois (l’application sur smartphone est plus riche et permet de saisir de nouveaux articles, de se faire envoyer la liste par SMS (et notifier lors d’ajouts pendant qu’on fait les courses) et de définir les règles d’ordonnancement des listes, afin que la liste des courses soit automatiquement triée selon les rayons du magasin) ;
  • Déclencher un minuteur de cuisson qui envoi un SMS à l’expiration et 30 secondes avant l’expiration ;
  • Afficher les temps de cuisson du cuit-vapeur ;
  • Réactiver le wifi quand il est coupé par la programmation horaire ;
  • Afficher le matin en semaine les deux prochains passages du bus pour la crèche.
Les températures et taux d'humidité sont affichées dans un encart en bas à droite
Les températures et taux d’humidité sont affichées dans un encart en bas à droite

Tout comme les sondes, le Raspberry Pi 3 derrière cette tablette tourne sur Raspbian. Ce dernier est configuré pour lancer au démarrage un programme (toujours en python) apportant les différents services listés ci-dessus. Pour l’IHM je n’ai pas trouvé de bibliothèque python suffisamment souple pour le rendu que j’avais en tête. J’ai donc utilisé la bibliothèque « bas niveau » Tkinter sur laquelle repose ma version « maison » de widget et de widget manager.

Je me suis posé la question de la consommation électrique induite par cette tablette qui reste allumée. La consommation du Raspberry est très limitée et reste raisonnable. N’ayant pas l’information concernant l’écran, j’ai configuré le X window manager pour que l’écran se mette en veille au bout de 10 minutes d’inactivité et se rallume au toucher. Via une tâche cron cette veille est désactivée (l’écran reste allumé) le matin du réveil au départ et le soir du retour au coucher.

Application mobile

Je souhaitais pouvoir également consulter les températures en dehors de l’appartement.  Plutôt que de développer une application native j’ai choisi de le faire au travers d’une application web ce qui me permet d’y accéder simplement depuis n’importe quel navigateur et même au travers d’une application iPhone en l’installant sous forme de web app.

Un minimum d’HTML et de CSS permet d’afficher les températures et taux d’humidité courants de chaque pièce.

Je souhaitais pouvoir également suivre l’évolution des données, j’ai utilisé pour cela la bibliothèque Highstock suffisamment paramétrable pour obtenir le rendu que je souhaitais.

Application smartphone de suivi de température

Références des Sources utilisées

https://learn.adafruit.com/dht-humidity-sensing-on-raspberry-pi-with-gdocs-logging/software-install-updated

https://www.highcharts.com/products/highstock/

http://flask.pocoo.org

https://www.ansible.com/

Je suis preneur de vos retours, suggestions ou questions via le formulaire de commentaire. Et n’hésitez pas à partager si vous avez également réalisé un système de suivi de température.

1 réaction sur “ Suivi de température et humidité ”

  1. Symon Réponse

    Hello,

    Super projet, je suis fan et esthétique en plus.
    quels retours ou améliorations faites depuis 2 ans ?

    Perso je vais me lancer pour monitorer l’hygrométrie de mes pièces avant changement de fenêtres.

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *