Raspberry Pi

Capteur de qualité de l'air Raspberry Pi avec BME680 et MQTT

Câblez un BME680 sur un Raspberry Pi, calibrez une référence de gaz, calculez un score simple de qualité de l'air intérieur et envoyez le tout vers un broker MQTT consultable de n'importe où.

Capteur de qualité de l'air Raspberry Pi avec une carte BME680 envoyant gaz, humidité et température vers un tableau de bord MQTT
Essayez-le sur votre propre broker MQTTObtenez un broker MQTT privé et managé avec l’offre Free et partez d’exemples de code prêts à l’emploi.
Essayer gratuitement Exemples de code Aucune carte bancaire requise

Un capteur de qualité de l'air Raspberry Pi construit autour du Bosch BME680 est l'un des petits projets de capteurs les plus utiles à installer dans un bureau, un atelier ou une chambre. Dans ce tutoriel, vous câblerez le capteur, lirez en Python la température, l'humidité, la pression et la résistance du gaz, transformerez ces valeurs brutes en une estimation simple de la qualité de l'air intérieur (IAQ) et publierez le tout en MQTT pour que n'importe quel tableau de bord ou téléphone puisse le suivre en direct.

Ce que le BME680 mesure réellement

Le BME680 combine quatre mesures dans un minuscule boîtier : la température, l'humidité relative, la pression barométrique et la résistance d'une couche de gaz à oxyde métallique chauffée. Les trois premières sont des grandeurs physiques simples. La quatrième est la plus intéressante, et aussi la plus souvent mal comprise.

Le capteur de gaz n'indique ni un gaz précis ni une concentration en ppm. Les composés organiques volatils (COV) issus des produits ménagers, de la cuisine, de la peinture, des occupants et du mobilier réagissent avec la couche sensible chauffée et font baisser sa résistance. Un air propre donne une résistance élevée, un air pollué une résistance plus faible. La valeur brute est donc un indicateur relatif : excellent pour repérer des tendances et des événements (« quelqu'un vient de commencer à cuisiner », « la pièce a besoin d'être aérée »), mais pas une mesure de laboratoire.

Bosch fournit une bibliothèque propriétaire appelée BSEC qui convertit ces mesures en un indice IAQ calibré de 0 à 500 et en équivalents CO2 estimés. Elle fonctionne bien, mais il s'agit d'un binaire fermé avec sa propre licence et un effort d'intégration non négligeable. Pour un capteur de loisir ou de laboratoire, un score transparent basé sur une référence suffit souvent, et c'est ce que nous allons construire ici. Si vous avez besoin de BSEC plus tard, la partie MQTT de ce projet reste strictement identique.

Mesure Unité Plage intérieure typique Ce qu'elle indique
Température °C 18 à 26 Confort, et facteur de correction pour la couche de gaz
Humidité relative % 30 à 60 Confort, risque de moisissures, composante du score IAQ
Pression hPa 950 à 1050 Tendance météo, pas qualité de l'air
Résistance du gaz Ohm Dizaines à centaines de kOhm Niveau relatif de COV ; plus c'est élevé, plus l'air est propre

Matériel et câblage

Il vous faut :

  1. N'importe quel Raspberry Pi avec un connecteur 40 broches (Zero 2 W, 3, 4 ou 5) sous Raspberry Pi OS.
  2. Une carte d'extension BME680. Les cartes Adafruit, Pimoroni et de nombreuses cartes génériques conviennent ; vérifiez si la vôtre utilise l'adresse I2C 0x76 ou 0x77.
  3. Quatre fils de liaison.

Reliez la carte au Pi en I2C :

Broche BME680 Broche Raspberry Pi
VIN / 3V3 Broche 1 (3,3 V)
GND Broche 6 (GND)
SDA / SDI Broche 3 (GPIO 2, SDA)
SCL / SCK Broche 5 (GPIO 3, SCL)

Montez le capteur à l'écart du Pi. Le processeur et le régulateur d'alimentation chauffent la carte, et un capteur posé directement au-dessus du SoC affichera plusieurs degrés de trop et une humidité trop basse. Un câble de 10 à 15 cm ou une petite entretoise font une différence notable.

Préparer le Raspberry Pi

Activez l'I2C, vérifiez que le capteur est visible et créez un environnement virtuel Python (Raspberry Pi OS Bookworm et les versions ultérieures l'exigent pour les installations pip) :

sudo raspi-config nonint do_i2c 0
sudo apt update && sudo apt install -y i2c-tools python3-venv
i2cdetect -y 1          # 76 ou 77 doit apparaître dans la grille

python3 -m venv ~/aq-env
source ~/aq-env/bin/activate
pip install adafruit-circuitpython-bme680 paho-mqtt

Si i2cdetect affiche une grille vide, revérifiez SDA et SCL (il est facile de les inverser) et assurez-vous que la carte est alimentée en 3,3 V. La documentation officielle de configuration du Raspberry Pi détaille la mise en place de l'I2C si vous utilisez une image non standard.

Écrire le script du capteur de qualité de l'air Raspberry Pi

Le script ci-dessous fait quatre choses : il lit le capteur toutes les dix secondes, effectue une phase de chauffe de cinq minutes pour établir une référence de gaz, calcule une estimation IAQ à partir du gaz et de l'humidité, et publie un message JSON sur le serveur MQTT public gratuit broker.connectmq.com. Il définit aussi un Last Will pour que les abonnés sachent quand le capteur passe hors ligne.

#!/usr/bin/env python3
"""Raspberry Pi air quality monitor: BME680 -> MQTT."""
import json
import time
import uuid

import board
import adafruit_bme680
import paho.mqtt.client as mqtt

BROKER = "broker.connectmq.com"
PORT = 1883
DEVICE_ID = "pi-aq-" + uuid.uuid4().hex[:6]
BASE_TOPIC = f"connectmq/demo/{DEVICE_ID}/air"
I2C_ADDRESS = 0x77          # passer à 0x76 pour les cartes de type Pimoroni
INTERVAL = 10               # secondes entre deux mesures
BURN_IN = 300               # secondes pour apprendre la référence d'air propre
HUM_BASELINE = 40.0         # humidité intérieure idéale en pourcentage
HUM_WEIGHT = 0.25           # part du score provenant de l'humidité


def on_connect(client, userdata, flags, reason_code, properties):
    print("Connected:", reason_code)
    client.publish(f"{BASE_TOPIC}/status", "online", qos=1, retain=True)


def iaq_score(gas, hum, gas_baseline):
    """0-100, higher is better. Simple, transparent estimate, not Bosch BSEC."""
    hum_offset = hum - HUM_BASELINE
    if hum_offset > 0:
        hum_score = (100 - HUM_BASELINE - hum_offset) / (100 - HUM_BASELINE)
    else:
        hum_score = (HUM_BASELINE + hum_offset) / HUM_BASELINE
    hum_score *= HUM_WEIGHT * 100

    gas_offset = gas_baseline - gas
    if gas_offset > 0:
        gas_score = (gas / gas_baseline) * (100 - HUM_WEIGHT * 100)
    else:
        gas_score = 100 - HUM_WEIGHT * 100
    return round(max(0.0, min(100.0, hum_score + gas_score)), 1)


def main():
    i2c = board.I2C()
    sensor = adafruit_bme680.Adafruit_BME680_I2C(i2c, address=I2C_ADDRESS)
    sensor.sea_level_pressure = 1013.25

    client = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2, client_id=DEVICE_ID)
    client.will_set(f"{BASE_TOPIC}/status", "offline", qos=1, retain=True)
    client.on_connect = on_connect
    client.connect(BROKER, PORT, keepalive=60)
    client.loop_start()

    print(f"Burn-in for {BURN_IN} s, publishing to {BASE_TOPIC}/#")
    samples = []
    start = time.time()
    while time.time() - start < BURN_IN:
        samples.append(sensor.gas)
        time.sleep(1)
    gas_baseline = sum(samples[-50:]) / len(samples[-50:])
    print(f"Gas baseline: {gas_baseline:.0f} Ohm")

    while True:
        gas = sensor.gas
        hum = sensor.relative_humidity
        payload = {
            "ts": int(time.time()),
            "temperature_c": round(sensor.temperature, 2),
            "humidity_pct": round(hum, 1),
            "pressure_hpa": round(sensor.pressure, 1),
            "gas_ohm": int(gas),
            "iaq_score": iaq_score(gas, hum, gas_baseline),
        }
        client.publish(f"{BASE_TOPIC}/telemetry", json.dumps(payload), qos=1)
        print(payload)
        time.sleep(INTERVAL)


if __name__ == "__main__":
    main()

Lancez-le avec python aq_monitor.py. Pendant la phase de chauffe, l'élément chauffant se stabilise ; un capteur neuf dérive pendant son premier ou ses deux premiers jours, ne vous fiez donc pas à la référence de la toute première exécution. Une bonne habitude consiste à démarrer le script à l'air frais (ouvrez une fenêtre pendant les cinq premières minutes), car la référence définit ce que « propre » signifie pour le reste de la session.

Interpréter l'estimation IAQ

Le score est inversé par rapport à l'indice de Bosch : 100 signifie un air aussi propre que la référence avec une humidité idéale, une valeur plus basse signifie un air moins bon. Un guide de lecture approximatif :

Score Signification Action typique
80 à 100 Proche de la référence Rien à faire
60 à 80 COV perceptibles ou humidité hors cible À surveiller
40 à 60 Nettement moins bon que la référence Aérer la pièce
Moins de 40 Source importante à proximité Trouver la source, aérer immédiatement

La référence étant relative, comparez les scores dans le temps pour une même pièce plutôt qu'entre différents appareils.

Suivre les données en direct

Ouvrez un second terminal (sur le Pi ou votre ordinateur portable) et abonnez-vous avec le client Mosquitto. Remplacez l'identifiant d'appareil par celui affiché par votre script :

mosquitto_sub -h broker.connectmq.com -p 1883 -i aq-viewer-$RANDOM \
  -t "connectmq/demo/pi-aq-1a2b3c/air/#" -v

Vous devriez voir d'abord le statut retenu online, puis un message de télémétrie toutes les dix secondes. Arrêtez le script Python avec Ctrl+C : après expiration du keepalive, le broker distribue le Last Will offline. Gardez à l'esprit que le serveur MQTT public gratuit est lisible par toute personne connaissant le topic : des données d'air à l'échelle d'une pièce ne posent pas de problème, mais rien de personnel ne doit y transiter.

Faire tourner le capteur sans surveillance

Un capteur qui s'arrête quand vous fermez votre session SSH n'est pas vraiment un outil de surveillance. Créez une unité systemd pour que le script démarre au boot et redémarre après une défaillance :

# /etc/systemd/system/aq-monitor.service
[Unit]
Description=BME680 air quality monitor
After=network-online.target
Wants=network-online.target

[Service]
User=pi
ExecStart=/home/pi/aq-env/bin/python /home/pi/aq_monitor.py
Restart=always
RestartSec=10

[Install]
WantedBy=multi-user.target
sudo systemctl daemon-reload
sudo systemctl enable --now aq-monitor
journalctl -u aq-monitor -f

Adaptez le nom d'utilisateur et les chemins à votre installation. Le même principe est expliqué plus en détail pour d'autres capteurs dans le projet de température DS18B20 et le tutoriel d'humidité DHT22, que vous pouvez combiner avec celui-ci sur un seul Pi.

Passer à un broker privé

Au-delà des expériences, publiez vers votre propre broker avec identifiants et TLS. Sur ConnectMQ, vous vous inscrivez, créez un broker et obtenez un nom d'utilisateur, un mot de passe et un client ID générés. Modifiez ensuite le bloc de connexion :

client = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2, client_id="YOUR_CLIENT_ID")
client.username_pw_set("YOUR_USERNAME", "YOUR_PASSWORD")
client.tls_set()                       # le magasin de CA système valide le certificat Let's Encrypt
client.will_set(f"{BASE_TOPIC}/status", "offline", qos=1, retain=True)
client.connect("broker.connectmq.com", 8883, keepalive=60)

Avec un broker MQTT managé, vous n'avez pas à maintenir vous-même une instance Mosquitto à jour et joignable, et l'explorateur de topics en direct du portail montre la télémétrie qui arrive sans outil supplémentaire. Vous pouvez aussi ajouter une règle d'alerte de seuil sur iaq_score ou humidity_pct afin que le journal des alertes du portail enregistre chaque dépassement de limite dans la pièce.

Dépannage

  1. ValueError: No I2C device at address : l'adresse est incorrecte. Utilisez celle affichée par i2cdetect (0x76 ou 0x77).
  2. Résistance du gaz bloquée très bas et qui ne remonte jamais : le capteur est encore en phase de conditionnement. Laissez-le alimenté plusieurs heures ; la dérive à la première utilisation est normale.
  3. Température trop élevée de plusieurs degrés : chaleur du Pi ou de l'élément chauffant du capteur lui-même. Éloignez le capteur de la carte ou appliquez un décalage fixe après comparaison avec un thermomètre de référence.
  4. Connexion refusée sur le broker privé : vérifiez que le client ID correspond exactement aux identifiants délivrés ; ConnectMQ lie les client IDs aux identifiants.
  5. Les messages s'arrêtent après une coupure réseau : paho se reconnecte automatiquement tant que loop_start() tourne, mais les messages QoS 1 publiés hors ligne ne sont mis en file qu'en mémoire. Pour les longues coupures, ajoutez un tampon local.

Idées pour aller plus loin

Une fois les données en circulation, le reste relève du MQTT pur. Node-RED peut s'abonner et piloter un ventilateur via un relais, une idée classique dans la maison connectée ; Jeedom et Domoticz savent eux aussi communiquer en MQTT. Telegraf peut écrire le JSON dans InfluxDB pour des graphiques à long terme. Un second Pi dans une autre pièce peut publier sous son propre identifiant d'appareil afin de comparer les espaces côte à côte. Si vous découvrez la plateforme, le tutoriel MQTT sur Raspberry Pi présente les bases des clients, des topics et de la QoS sur lesquelles repose ce projet.

Démarrer avec ConnectMQ

Faites passer votre capteur de qualité de l'air Raspberry Pi du serveur public de test à un broker privé sécurisé par TLS en quelques minutes. ConnectMQ vous offre un broker MQTT managé avec un client MQTT dans le navigateur, un explorateur de topics en direct et des alertes de seuil. Créez un broker Serverless gratuit, sans carte bancaire, et pointez-y votre script.

Questions fréquentes

La valeur IAQ du BME680 est-elle identique à l'indice Bosch BSEC ?

Non. Le score de ce tutoriel est une estimation transparente basée sur une référence de gaz et sur l'humidité. La bibliothèque BSEC de Bosch produit un indice calibré de 0 à 500 avec un suivi automatique de la référence, et elle peut être intégrée plus tard sans changer les topics MQTT.

Combien de temps le BME680 doit-il chauffer ?

L'élément chauffant se stabilise en quelques minutes, c'est pourquoi le script utilise une phase de chauffe de cinq minutes. Un capteur neuf continue de dériver pendant ses un ou deux premiers jours de fonctionnement ; les mesures deviennent ensuite plus cohérentes.

Peut-on mesurer le CO2 avec un BME680 ?

Pas directement. Le BME680 réagit aux COV, et toute valeur de CO2 qui en est dérivée reste une estimation. Pour une vraie mesure du CO2, utilisez un capteur NDIR comme le SCD40 ou le SCD41 et publiez-le à côté des données du BME680.

Quel broker MQTT utiliser pour un capteur de qualité de l'air à la maison ?

Le serveur MQTT public gratuit convient pour faire des essais, mais tout le monde peut le lire. Pour un usage permanent, un broker privé avec identifiants et TLS, comme un broker ConnectMQ Serverless gratuit, garde vos données privées.

Articles liés

Journal d'accès RFID MQTT sur Raspberry Pi avec le lecteur RC522

Câblez un lecteur RFID RC522 sur un Raspberry Pi, lisez les badges en Python, vérifiez-les dans une liste blanche locale et publiez chaque tentative d'accès sous forme d'événement MQTT exploitable par vos outils de journalisation, tableaux de bord et alertes.

8 min de lecture

Installer Mosquitto sur Raspberry Pi comme broker MQTT local sécurisé

Installez Mosquitto sur un Raspberry Pi, sécurisez-le avec des mots de passe et une configuration de listener raisonnable, testez-le avec mosquitto_pub et mosquitto_sub, et découvrez quand un broker local doit faire un pont vers un broker cloud.

9 min de lecture

Tous les articles →

Votre broker MQTT privé, à quelques clics

Créez un compte, récupérez les identifiants de votre broker et connectez votre premier appareil — aucun serveur à installer, mettre à jour ou dimensionner.