Graphique de pression atmosphérique

[Domotique] récupérer la température et la pression atmosphérique avec un Raspberry

Objectif

Récupérer la température, le taux d’humidité ainsi que la pression atmosphérique d’un BME/BP280 et l’envoyer sur le serveur domoticz avec un Raspberry Zero W

Matériel

Montage

La bestiole en question…
  • GPIO2 = alim
  • GPIO39 = grnd
  • GPIO3 = SDA
  • GPIO5 = SCL
montage Raspberry 0 W et BMP280

Voici un lien l’organisation des GPIO : https://pinout.xyz/pinout/pin1_3v3_power#

Code

Pré-requis

Au préalable installer i2c-tools :
sudo apt-get update
sudo apt-get install i2c-tools

Puis, si ce n’est pas déjà fait, activer l’usage du i2c sur raspbian, faire sudo raspi-config puis se rendre dans le menu 5 puis P5.

premier menu de Raspi-config
second menu, mettre enable sur menu P5

enfin, sudo i2cdetect -y 1, ce qui devrait vous mettre quelque chose qui ressemble à l’image suivante :

rendu de la commande sudo i2cdetect -y 1

A partir de là votre BMP communique avec le raspberry, reste à comprendre.

Lire le BMP280

J’ai récupéré ce script à cette adresse : https://bitbucket.org/MattHawkinsUK/rpispy-misc/raw/master/python/bme280.py

#!/usr/bin/python
# --------------------------------------
#    ___    ___    _ ____
#     / _ \/ _ \(_) __/__    __ __
#    / , _/ ___/ /\ \/ _ \/ // /
# /_/|_/_/    /_/___/ .__/\_, /
#    /_/     /___/
#
#    bme280.py
#    Read data from a digital pressure sensor.
#
#    Official datasheet available from :
#    https://www.bosch-sensortec.com/bst/products/all_products/bme280
#
# Author : Matt Hawkins
# Date     : 25/07/2016
#
# http://www.raspberrypi-spy.co.uk/
# https://bitbucket.org/MattHawkinsUK/rpispy-misc/raw/master/python/bme280.py
# --------------------------------------

import smbus
import time
from ctypes import c_short

# Default device I2C address
DEVICE = 0x76


# Rev 2 Pi, Pi 0, Pi 2 & Pi 3 uses bus 1
# Rev 1 Pi uses bus 0
bus = smbus.SMBus(1)


def getShort(data, index):
    # return two bytes from data as a signed 16-bit value
    return c_short((data[index + 1] << 8) + data[index]).value


def getUShort(data, index):
    # return two bytes from data as an unsigned 16-bit value
    return (data[index + 1] << 8) + data[index]


def getChar(data, index):
    # return one byte from data as a signed char
    result = data[index]
    if result > 127:
        result -= 256
    return result


def getUChar(data, index):
    # return one byte from data as an unsigned char
    result = data[index] & 0xFF
    return result


def readBME280ID(addr=DEVICE):
    # Chip ID Register Address
    REG_ID = 0xD0
    (chip_id, chip_version) = bus.read_i2c_block_data(addr, REG_ID, 2)
    return (chip_id, chip_version)


def readBME280All(addr=DEVICE):
    # Register Addresses
    REG_DATA = 0xF7
    REG_CONTROL = 0xF4
    REG_CONTROL_HUM = 0xF2

    # Oversample setting - page 27
    OVERSAMPLE_TEMP = 2
    OVERSAMPLE_PRES = 2
    MODE = 1

    # Oversample setting for humidity register - page 26
    OVERSAMPLE_HUM = 2
    bus.write_byte_data(addr, REG_CONTROL_HUM, OVERSAMPLE_HUM)

    control = OVERSAMPLE_TEMP << 5 | OVERSAMPLE_PRES << 2 | MODE
    bus.write_byte_data(addr, REG_CONTROL, control)

    # Read blocks of calibration data from EEPROM
    # See Page 22 data sheet
    cal1 = bus.read_i2c_block_data(addr, 0x88, 24)
    cal2 = bus.read_i2c_block_data(addr, 0xA1, 1)
    cal3 = bus.read_i2c_block_data(addr, 0xE1, 7)

    # Convert byte data to word values
    dig_T1 = getUShort(cal1, 0)
    dig_T2 = getShort(cal1, 2)
    dig_T3 = getShort(cal1, 4)

    dig_P1 = getUShort(cal1, 6)
    dig_P2 = getShort(cal1, 8)
    dig_P3 = getShort(cal1, 10)
    dig_P4 = getShort(cal1, 12)
    dig_P5 = getShort(cal1, 14)
    dig_P6 = getShort(cal1, 16)
    dig_P7 = getShort(cal1, 18)
    dig_P8 = getShort(cal1, 20)
    dig_P9 = getShort(cal1, 22)

    dig_H1 = getUChar(cal2, 0)
    dig_H2 = getShort(cal3, 0)
    dig_H3 = getUChar(cal3, 2)

    dig_H4 = getChar(cal3, 3)
    dig_H4 = (dig_H4 << 24) >> 20
    dig_H4 = dig_H4 | (getChar(cal3, 4) & 0x0F)

    dig_H5 = getChar(cal3, 5)
    dig_H5 = (dig_H5 << 24) >> 20
    dig_H5 = dig_H5 | (getUChar(cal3, 4) >> 4 & 0x0F)

    dig_H6 = getChar(cal3, 6)

    # Wait in ms (Datasheet Appendix B:
    # Measurement time and current calculation)
    wait_time = 1.25 + (2.3 * OVERSAMPLE_TEMP) +\
        ((2.3 * OVERSAMPLE_PRES) + 0.575) + ((2.3 * OVERSAMPLE_HUM) + 0.575)
    # Wait the required time
    time.sleep(wait_time / 1000)

    # Read temperature/pressure/humidity
    data = bus.read_i2c_block_data(addr, REG_DATA, 8)
    pres_raw = (data[0] << 12) | (data[1] << 4) | (data[2] >> 4)
    temp_raw = (data[3] << 12) | (data[4] << 4) | (data[5] >> 4)
    hum_raw = (data[6] << 8) | data[7]

    # Refine temperature
    var1 = ((((temp_raw >> 3) - (dig_T1 << 1))) * (dig_T2)) >> 11
    var2 = (((((temp_raw >> 4) - (dig_T1)) *
              ((temp_raw >> 4) - (dig_T1))) >> 12) * (dig_T3)) >> 14
    t_fine = var1 + var2
    temperature = float(((t_fine * 5) + 128) >> 8)

    # Refine pressure and adjust for temperature
    var1 = t_fine / 2.0 - 64000.0
    var2 = var1 * var1 * dig_P6 / 32768.0
    var2 = var2 + var1 * dig_P5 * 2.0
    var2 = var2 / 4.0 + dig_P4 * 65536.0
    var1 = (dig_P3 * var1 * var1 / 524288.0 + dig_P2 * var1) / 524288.0
    var1 = (1.0 + var1 / 32768.0) * dig_P1
    if var1 == 0:
        pressure = 0
    else:
        pressure = 1048576.0 - pres_raw
        pressure = ((pressure - var2 / 4096.0) * 6250.0) / var1
        var1 = dig_P9 * pressure * pressure / 2147483648.0
        var2 = pressure * dig_P8 / 32768.0
        pressure = pressure + (var1 + var2 + dig_P7) / 16.0

    # Refine humidity
    humidity = t_fine - 76800.0
    humidity = (hum_raw - (dig_H4 * 64.0 + dig_H5 / 16384.0 * humidity))\
        * (dig_H2 / 65536.0 * (1.0 + dig_H6 / 67108864.0 * humidity *
                               (1.0 + dig_H3 / 67108864.0 * humidity)))
    humidity = humidity * (1.0 - dig_H1 * humidity / 524288.0)
    if humidity > 100:
        humidity = 100
    elif humidity < 0:
        humidity = 0

    return temperature / 100.0, pressure / 100.0, humidity


def main():
    temperature, pressure, humidity = readBME280All()
    weather = [temperature, pressure, humidity]
    return weather


if __name__ == "__main__":
    (chip_id, chip_version) = readBME280ID()
    print("Chip ID :", chip_id)
    print("Version :", chip_version)

    temperature, pressure, humidity = readBME280All()
    print("Temperature : ", temperature, "C")
    print("Pressure : ", pressure, "hPa")
    print("Humidity : ", humidity, "%")

Vous noterez le DEVICE = 0x76 ligne 27 qui correspond à ce qu’il y avait d’affiché avec la commande sudo i2cdetect -y 1.

Avant de le faire fonctionner, n’oubliez pas d’installer smbus à coup de pip install smbus ; une fois que cela est fait vous pouvez faire python bme.py là où vous avez mis le fichier sur le Raspberry , ce qui vous donnera :

~/bme $ python bme.py 
('Chip ID :', 96)
('Version :', 0)
('Temperature : ', 20.34, 'C')
('Pressure : ', 1002.2085705444156, 'hPa')
('Humidity : ', 60.38054404621201, '%')

Donc, une température de 20.34° C, une pression atmosphérique de 1002.20 hPa, et une humidité de 60%. Maintenant, envoyons cela à Domoticz.

Communication avec Domoticz

Toujours le même principe que pour le poulailler ou encore le relai : un fichier send_domoticz avec une tache dans le cron.

#!/usr/bin/python

from bme import main
from requests.auth import HTTPBasicAuth
import requests
import sys


# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# fonctions
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

def maj_widget(val_url):
    requete = 'http://' + domoticz_ip + ':' + domoticz_port + val_url
    # print requete
    r = requests.get(requete, auth=HTTPBasicAuth(user, password))
    if r.status_code != 200:
        print("Erreur API Domoticz")


# 0=Normal
# 1=Comfortable
# 2=Dry
# 3=Wet
def humidity_status(val):
    if 0 <= val <= 75:
        humidity_status = 0
    elif 76 <= val <= 79:
        humidity_status = 1
    elif 80 <= val <= 94:
        humidity_status = 2
    elif 95 <= val <= 100:
        humidity_status = 3
    return humidity_status


# 0 = No info
# 1 = Sunny
# 2 = Partly cloudy
# 3 = Cloudy
# 4 = Rain
def barometer_forecast(val):
    if val < 1006:
        weather = 4
    elif 1007 <= val <= 1013:
        weather = 3
    elif 1014 <= val <= 1020:
        weather = 2
    elif val > 1020:
        weather = 1
    else:
        weather = 0
    return weather


# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# les parametres de Domoticz
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

domoticz_ip = ''
domoticz_port = '8080'
user = ''
password = ''
domoticz_idx = ''

# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# recuperation des informations des capteurs
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

value = main()

temperature = int(value[0])
pressure = int(value[1])
humidity = int(value[2])

hum_stat = humidity_status(humidity)
bar_for = barometer_forecast(int(pressure))

if humidity is not None and temperature is not None and pressure is not None:

    # https://www.domoticz.com/wiki/Domoticz_API/JSON_URL%27s#Temperature.2Fhumidity.2Fbarometer
    # modele url : /json.htm?type=command&param=udevice&idx=IDX&nvalue=0&
    # svalue=TEMP;HUM;HUM_STAT;BAR;BAR_FOR
    # l URL Domoticz pour le widget virtuel

    url = '/json.htm?type=command&param=udevice&idx=' + str(domoticz_idx)
    url += '&nvalue=0&svalue='
    url += str('{0:0.1f};{1:0.1f};{2:0};{3:0.1f};{4:0}').format(temperature, humidity, hum_stat, pressure, bar_for)
    print(url)
    maj_widget(url)

else:
    print('Probleme avec la lecture du BME280')
    sys.exit(1)

Pour l’automatisation avec le cron voir : https://journaldunarchiviste.fr/2019/01/15/domotique-poulailler-connecte-partie-2/#Temperature_et_humidite

Il faut également ajuster la fonction barometer_forecast ; en effet la pression atmosphérique dépend de votre localisation, en particulier de l’altitude, pour plus de renseignement lire : http://leguidemeteo.com/prevoir-le-temps-a-partir-de-son-barometre/

Note : la partie identification nécessite  d'avoir configuré son accès à domoticz par un identifiant et un mot de passe.

Configuration domoticz

Il faut créer un capteur virtuel : les explications en capture d’écran

Dans Configuration / Matériel puis remplir le champ Nom et sélectionner Dummy

Une fois cela fait, il faut créer le dispositif pour le capteur

on sélectionne le type de capteur, c’est à dire : Température, Humidité et Baromètre

Une fois que cela est fait, on alimente le script send_domoticz.py avec le bon idx

Se rendre dans Configuration/Dispositif et récuperer le numéro IDX (le 19 pour mon cas) et l’indiquer dans le fichier send_domoticz.py

Résultat

En lançant le script avec la commande python send_domo.py vous aurez un résultat comme celui-ci /json.htm?type=command&param=udevice&idx=19&nvalue=0&svalue=18.0;66.0;0;1003.0;4 ce qui mettra à jour Domotitcz. Sur l’interface Domoticz, un joli capteur, avec possibilité d’envoyer des notifications via Télégram.

Le capteur opérationnel sur domoticz

Voici des statistiques sur le mois précédent d’un autre BMP280 installé depuis plus d’un an dans le jardin.

Graphique de la pression atmosphérique sur le mois d’avril 2020

Le code est disponible sur ce dépôt github : https://github.com/fz3p/weatherstation ; cependant il sert également à envoyer les données d’un DHT11.