Skip to Content

Mesurer la qualité de l’air avec le BME680

Mesurer la qualité de l’air avec le BME680

Apprenez à mesurer la qualité de l’air avec le BME680 Capteur environnemental et un ESP32. Le BME680 est un petit capteur qui peut non seulement mesurer la température, l’humidité, la pression, mais aussi la concentration de composés organiques volatils (COV), nocifs pour la santé.

La BSEC-Arduino-library bibliothèque pour le BME680 facilite particulièrement l’évaluation de la qualité de l’air, car elle renvoie un indice de qualité de l’air (IAQ) avec des valeurs allant de 0 à 400. Le tableau suivant montre comment interpréter ces valeurs d’IAQ :

Meaning of IAQ values (source)
Signification des valeurs IAQ (source)

Nous utiliserons cette fonctionnalité pour construire un mesureur de qualité de l’air.

Pièces requises

Vous trouverez ci-dessous les pièces nécessaires pour ce projet. En plus du capteur BME680, vous aurez besoin d’un microcontrôleur. J’ai choisi un ESP32 lite plus ancien, mais tout autre ESP32 fonctionnera très bien aussi. Notez que la BSEC-Arduino-library bibliothèque que nous allons utiliser supporte également d’autres microcontrôleurs. Consultez sa readme file documentation.

Capteur BME680

ESP32 lite Lolin32

ESP32 lite

USB data cable

Câble USB de données

Dupont wire set

Jeu de fils Dupont

Half_breadboard56a

Plaque d’essai (breadboard)

Makerguides is a participant in affiliate advertising programs designed to provide a means for sites to earn advertising fees by linking to Amazon, AliExpress, Elecrow, and other sites. As an Affiliate we may earn from qualifying purchases.

Présentation du capteur de gaz BME680

Le BME680 est un petit capteur environnemental 4-en-1 puissant qui peut mesurer la température, l’humidité, la pression barométrique et, surtout pour ce projet, la résistance aux gaz, utilisée pour estimer la qualité de l’air intérieur (IAQ). La photo ci-dessous montre le capteur, qui ne mesure que 3x3x1 mm :

BME680 Sensor
Capteur BME680

Le BME680 utilise un capteur de gaz basé sur la technologie à oxyde métallique (MOX). Il ne détecte pas directement les gaz individuels, mais réagit à une large gamme de composés organiques volatils (COV) et de gaz comme le monoxyde de carbone (CO) présents dans l’air. Ces composés sont couramment émis par :

  • Produits de nettoyage et aérosols
  • Peintures et vernis
  • Meubles et tapis (surtout neufs)
  • Cuisine et tabagisme
  • Souffle humain et odeurs corporelles
  • Appareils de combustion

Bien que le capteur ne mesure pas les concentrations de gaz en parties par million (ppm), la bibliothèque BSEC utilise des modèles d’apprentissage automatique pour traduire les mesures de résistance aux gaz en un score IAQ significatif, ce qui le rend adapté à la surveillance de la qualité de l’air intérieur.

Qu’est-ce que l’IAQ et pourquoi c’est important

La valeur IAQ renvoyée par la bibliothèque logicielle du BME680 donne une estimation numérique de la pollution de l’air intérieur. Elle combine plusieurs mesures environnementales en un seul score, où :

  • 0–50 = Qualité d’air excellente
  • 51–100 = Bonne
  • 101–150 = Légèrement polluée
  • 151–200 = Modérément polluée
  • 201–300 = Fortement polluée
  • 301–500 = Très fortement polluée

Cette valeur est particulièrement utile car elle vous donne un chiffre clair et facile à comprendre, plutôt que des mesures brutes de résistance aux gaz, difficiles à interpréter seules.

Carte breakout pour BME680

Comme le capteur BME680 est très petit, nous utilisons une carte breakout pour le connecter à l’ESP32. Cela présente aussi l’avantage de pouvoir alimenter le capteur en 3,3 V ou 5 V, car la carte intègre un régulateur de tension. La photo ci-dessous montre la carte breakout avec le régulateur et le capteur BME680 :

Breakout board for BME680
Carte breakout pour BME680

Le BME680 est la petite boîte métallique carrée, à droite. À gauche se trouvent les broches pour l’interface I2C ou SPI, ainsi que les connexions d’alimentation (VCC, GND).

Nous allons utiliser l’interface I2C, car elle ne nécessite que de connecter SDA et SCL. Notez que la broche SDO détermine l’adresse I2C du capteur. Laisser SDO non connecté fixe l’adresse à 0x77, tandis que la connecter à la masse change l’adresse à 0x76.

Pour plus d’informations sur le capteur BME680, consultez notre BME680 Environmental Sensor with Arduinotutoriel, le BME680 Datasheetet peut-être le Bosch Website for the BME680.

Connexion du BME680 à l’ESP32

Pour connecter le BME680 à un ESP32 lite via I2C, il faut connecter SCL à la broche 23 et SDA à la broche 19. Les broches pour l’I2C matériel dépendent de votre carte et peuvent varier. Consultez le Find I2C and SPI default pins tutoriel pour identifier les broches de votre carte.

Sinon, il suffit de connecter GND à la masse (G) et VCC à la broche 3V (3,3 V) de l’ESP32. La photo ci-dessous montre le câblage complet :

Connecting BME680 to ESP32
Connexion du BME680 à l’ESP32

Code pour mesurer les données environnementales avec le BME680

Comme mentionné précédemment, nous allons utiliser la BSEC-Arduino-library pour lire les données du BME680. Vous pouvez l’installer via le LIBRARY MANAGER. Cherchez « bsec » et installez la « BSEC Software Library » comme montré ci-dessous :

BSEC Software Library installed in LIBRARY MANAGER
BSEC Software Library installée dans LIBRARY MANAGER

Notez qu’il existe aussi la plus récente Bosch-BSEC2-Library, que je n’ai pas testée. J’ai cependant utilisé la plus simple Adafruit BME680 library dans un autre tutoriel (BME680 Environmental Sensor with Arduino). Mais cette bibliothèque ne renvoie pas de mesures facilement interprétables de la qualité de l’air (IAQ), donc nous ne pouvons pas l’utiliser ici.

Le code suivant montre comment capturer des données de qualité de l’air telles que l’IAQ, le pourcentage de gaz, ainsi que des données environnementales simples comme la température, l’humidité et la pression avec le BME680. Je vais décomposer le code et expliquer chaque partie :

#include "bsec.h"

Bsec sensor;

void checkSensor() {
  if (sensor.bsecStatus != BSEC_OK) {
    Serial.println("BSEC status: " + String(sensor.bsecStatus));
  }
  if (sensor.bme68xStatus != BME68X_OK) {
    Serial.println("BME68X status: " + String(sensor.bme68xStatus));
  }
}

void setup(void) {
  Serial.begin(115200);
  delay(1000);

  sensor.begin(BME68X_I2C_ADDR_HIGH, Wire);
  checkSensor();

  bsec_virtual_sensor_t sensorList[8] = {
    BSEC_OUTPUT_IAQ,
    BSEC_OUTPUT_STATIC_IAQ,
    BSEC_OUTPUT_CO2_EQUIVALENT,
    BSEC_OUTPUT_BREATH_VOC_EQUIVALENT,
    BSEC_OUTPUT_RAW_PRESSURE,
    BSEC_OUTPUT_SENSOR_HEAT_COMPENSATED_TEMPERATURE,
    BSEC_OUTPUT_SENSOR_HEAT_COMPENSATED_HUMIDITY,
    BSEC_OUTPUT_GAS_PERCENTAGE
  };

  sensor.updateSubscription(sensorList, 8, BSEC_SAMPLE_RATE_LP);
  checkSensor();
}

void loop(void) {
  if (sensor.run()) {
    Serial.printf("Acc:  %d\n", sensor.iaqAccuracy);
    Serial.printf("IAQ:  %.0f\n", sensor.iaq);
    Serial.printf("sIAQ: %.0f\n", sensor.staticIaq);
    Serial.printf("gas:  %.0f %%\n", sensor.gasPercentage);
    Serial.printf("temp: %.1f C\n", sensor.temperature);
    Serial.printf("hum:  %.0f %%\n", sensor.humidity);
    Serial.printf("pres: %.0f hPa\n", sensor.pressure / 100.0);
    Serial.println();
  }
}

bibliothèques

En haut, le code inclut la BSEC library, abréviation de Bosch Sensortec Environmental Cluster. Cette bibliothèque combine les données brutes du capteur BME680 et applique des algorithmes pour fournir des mesures significatives de la qualité de l’air, comme l’IAQ et le pourcentage de gaz.

#include "bsec.h"

objets

Ensuite, nous créons une instance de la classe Bsecnommée sensor. Cet objet vous donne accès à toutes les fonctions BSEC.

Bsec sensor;

checkSensor

La fonction checkSensor()est une routine simple de vérification d’erreur. Elle contrôle à la fois le statut de l’algorithme BSEC et celui du matériel BME688. En cas de problème, elle affiche les codes d’erreur correspondants dans le moniteur série pour faciliter le débogage.

void checkSensor() {
  if (sensor.bsecStatus != BSEC_OK) {
    Serial.println("BSEC status: " + String(sensor.bsecStatus));
  }
  if (sensor.bme68xStatus != BME68X_OK) {
    Serial.println("BME68X status: " + String(sensor.bme68xStatus));
  }
}

Notez que les codes de statut positifs indiquent un avertissement, tandis que les codes négatifs indiquent une erreur !

setup

Dans la fonction setup(), le code démarre la communication série à 115200 bauds pour pouvoir afficher les résultats dans le moniteur série. Il inclut aussi un court délai pour s’assurer que le port série est prêt avant de continuer.

void setup(void) {
  Serial.begin(115200);
  delay(1000);

Ensuite, le capteur BME688 est initialisé via I2C avec la fonction sensor.begin(). L’adresse BME68X_I2C_ADDR_HIGH est utilisée, ce qui correspond à 0x77. Après l’initialisation, le code vérifie le statut du capteur.

sensor.begin(BME68X_I2C_ADDR_HIGH, Wire);
checkSensor();

La constante s’appelle « HIGH », car si la broche SDO n’est pas connectée, une résistance interne la tire vers le haut. Si vous souhaitez utiliser l’autre adresse I2C (0x76) en connectant SDO à la masse, modifiez cela en conséquence.

Ensuite, le sketch définit à quels capteurs virtuels s’abonner. Cela vous permet de choisir quels capteurs (température, humidité, …) et quelles valeurs dérivées (par ex. température brute ou compensée) utiliser. C’est important pour réduire la consommation d’énergie en désactivant les capteurs inutilisés.

La fonction sensor.updateSubscription() indique à la bibliothèque quelles données de quels capteurs nous voulons et à quelle fréquence :

bsec_virtual_sensor_t sensorList[8] = {
    BSEC_OUTPUT_IAQ,
    BSEC_OUTPUT_STATIC_IAQ,
    BSEC_OUTPUT_CO2_EQUIVALENT,
    BSEC_OUTPUT_BREATH_VOC_EQUIVALENT,
    BSEC_OUTPUT_RAW_PRESSURE,
    BSEC_OUTPUT_SENSOR_HEAT_COMPENSATED_TEMPERATURE,
    BSEC_OUTPUT_SENSOR_HEAT_COMPENSATED_HUMIDITY,
    BSEC_OUTPUT_GAS_PERCENTAGE
  };

  sensor.updateSubscription(sensorList, 8, BSEC_SAMPLE_RATE_LP);
  checkSensor();
}

La constante BSEC_SAMPLE_RATE_LP signifie un échantillonnage basse consommation (LP). C’est une autre façon de réduire la consommation du capteur. Il existe par exemple un mode BSEC_SAMPLE_RATE_ULP pour une consommation ultra basse (ULP). Vous pouvez même définir des taux d’échantillonnage différents pour différents ensembles de capteurs virtuels. Voir l’exemple basic_config_state_ULP_LP.

loop

La fonction loop()effectue le travail en temps réel. La fonction sensor.run()vérifie si de nouvelles données sont disponibles depuis la bibliothèque BSEC. Si oui, le code affiche diverses mesures dans le moniteur série :

void loop(void) {
  if (sensor.run()) {
    Serial.printf("Acc:  %d\n", sensor.iaqAccuracy);
    Serial.printf("IAQ:  %.0f\n", sensor.iaq);
    Serial.printf("sIAQ: %.0f\n", sensor.staticIaq);
    Serial.printf("gas:  %.0f %%\n", sensor.gasPercentage);
    Serial.printf("temp: %.1f C\n", sensor.temperature);
    Serial.printf("hum:  %.0f %%\n", sensor.humidity);
    Serial.printf("pres: %.0f hPa\n", sensor.pressure / 100.0);
    Serial.println();
  }
}

Notez que ceci n’est qu’un petit sous-ensemble des mesures et valeurs que la bibliothèque BSEC peut renvoyer. Le tableau suivant liste toutes les valeurs, leur signification et leur plage :

ValeurDescriptionUnité/PlageTypeCatégorie
iaqIndice de qualité de l’air intérieur (temps réel, dynamique)Indice (0–500)Traitéefloat
iaqAccuracyConfiance dans la valeur iaq0–3Statutuint8_t
staticIaqValeur IAQ lissée (moins sensible aux fluctuations)Indice (0–500)Traitéefloat
staticIaqAccuracyConfiance dans la valeur staticIaq0–3Statutuint8_t
co2EquivalentConcentration estimée de CO₂ à partir des COVppmTraitéefloat
co2AccuracyConfiance dans la valeur co2Equivalent0–3Statutuint8_t
breathVocEquivalentEstimation équivalente de COV dans l’air expiréppmTraitéefloat
breathVocAccuracyConfiance dans la valeur breathVocEquivalent0–3Statutuint8_t
compGasValueValeur compensée de gaz utilisée en interneOhms (normalisé)Internefloat
compGasAccuracyConfiance dans la valeur compGasValue0–3Statutuint8_t
gasPercentagePourcentage estimé d’air propre%Traitéefloat
gasPercentageAccuracyConfiance dans la valeur gasPercentage0–3Statutuint8_t
rawTemperatureTempérature brute du capteur°CBrutefloat
temperatureTempérature compensée (ajustée pour l’auto-chauffage du capteur)°CCompenséefloat
rawHumidityHumidité relative brute%Brutefloat
humidityHumidité relative compensée%Compenséefloat
pressurePression barométriquehPaBrutefloat
gasResistanceRésistance brute du capteur de gazOhmsBrutefloat
stabStatusIndique si l’échantillon est stabilisé0 ou 1 (type booléen)Statutfloat
runInStatusIndique si le capteur est encore en phase de rodage0 ou 1Statutfloat

Dans le code, nous affichons les valeurs iaqAccuracy (Acc), iaq (IAQ), staticIaq (sIAQ), gasPercentage (gas), temperature (temp), humidity (hum) et pressure (pres). La température et l’humidité sont les valeurs compensées (tenant compte du chauffage du capteur de gaz) et la pression est divisée par 100 pour afficher la pression en hectopascal (hPa).

Les valeurs les plus intéressantes sont iaq (IAQ) et staticIaq (sIAQ), cette dernière étant une version plus stable de la première. La valeur IAQ est directement liée à une interprétation exploitable de la qualité de l’air, comme montré dans le tableau suivant :

Meaning of IAQ values (source)
Signification des valeurs IAQ (source)

Sortie

Si vous téléversez et exécutez le code, vous devriez voir les mesures apparaître dans le moniteur série :

Output in Serial Monitor
Sortie dans le moniteur série

Cependant, le capteur de gaz du BME680 nécessite un temps de rodage initial important, allant d’environ 5 à 30 minutes selon les conditions ambiantes et la fréquence d’échantillonnage. Pendant ce temps, la valeur IAQ semble généralement fixée à 25 ou 50.

La valeur iaqAccuracy, affichée comme Acc dans le moniteur série, est un indicateur de confiance pour les mesures IAQ (et autres mesures liées aux gaz).

Au début, vous verrez une valeur de 0, ce qui signifie que les mesures IAQ ne sont pas encore valides (voir l’exemple de sortie ci-dessus). Avec le temps, la valeur iaqAccuracymontera de 0 → 1 → 2 → 3 et une fois qu’elle atteint 3, les mesures IAQ sont considérées comme valides ! Voici un exemple de sortie après le rodage complet du capteur avec un iaqAccuracy (Acc) de 3 :

Output in Serial Monitor after burn-in
Sortie dans le moniteur série après rodage

Évidemment, il est très pénible d’attendre jusqu’à 20 minutes pour obtenir des mesures valides à chaque redémarrage ou coupure de courant du BME680. Heureusement, le rodage peut être raccourci en sauvegardant et restaurant l’état du capteur BME680. La manière de procéder est décrite dans la section suivante.

Code pour sauvegarder et restaurer l’état du BME680

Le code ci-dessous étend notre code précédent avec deux fonctions, saveState et loadState, qui permettent de sauvegarder l’état du capteur BME680 et de réduire le temps avant que les mesures liées aux gaz soient disponibles :

#include <Preferences.h>
#include "bsec.h"

const unsigned long savePeriod = 3600 * 1000;  // 1 hour

Bsec sensor;
Preferences prefs;

void checkSensor() {
  if (sensor.bsecStatus != BSEC_OK) {
    Serial.println("BSEC status: " + String(sensor.bsecStatus));
  }
  if (sensor.bme68xStatus != BME68X_OK) {
    Serial.println("BME68X status: " + String(sensor.bme68xStatus));
  }
}

void saveState() {
  uint8_t state[BSEC_MAX_STATE_BLOB_SIZE];
  sensor.getState(state);
  prefs.begin("bsec", false);
  prefs.putBytes("state", state, BSEC_MAX_STATE_BLOB_SIZE);
  prefs.end();
  Serial.println("Sensor state saved");
}

void loadState() {
  prefs.begin("bsec", true); 
  if (prefs.getBytesLength("state") > 0) {
    uint8_t state[BSEC_MAX_STATE_BLOB_SIZE];
    prefs.getBytes("state", state, BSEC_MAX_STATE_BLOB_SIZE);
    sensor.setState(state);
    Serial.println("Sensor state loaded");
  }
  prefs.end();
}

void setup(void) {
  Serial.begin(115200);
  delay(1000);

  sensor.begin(BME68X_I2C_ADDR_HIGH, Wire);
  checkSensor();
  loadState();

  bsec_virtual_sensor_t sensorList[8] = {
    BSEC_OUTPUT_IAQ,
    BSEC_OUTPUT_STATIC_IAQ,
    BSEC_OUTPUT_CO2_EQUIVALENT,
    BSEC_OUTPUT_BREATH_VOC_EQUIVALENT,
    BSEC_OUTPUT_RAW_PRESSURE,
    BSEC_OUTPUT_SENSOR_HEAT_COMPENSATED_TEMPERATURE,
    BSEC_OUTPUT_SENSOR_HEAT_COMPENSATED_HUMIDITY,
    BSEC_OUTPUT_GAS_PERCENTAGE
  };

  sensor.updateSubscription(sensorList, 8, BSEC_SAMPLE_RATE_LP);
  checkSensor();
}

void loop(void) {
  static unsigned long lastSave = millis();

  if (sensor.run()) {
    Serial.printf("Acc:  %d\n", sensor.iaqAccuracy);
    Serial.printf("IAQ:  %.0f\n", sensor.iaq);
    Serial.printf("sIAQ: %.0f\n", sensor.staticIaq);
    Serial.printf("gas:  %.0f %%\n", sensor.gasPercentage);
    Serial.printf("temp: %.1f C\n", sensor.temperature);
    Serial.printf("hum:  %.0f %%\n", sensor.humidity);
    Serial.printf("pres: %.0f hPa\n", sensor.pressure / 100.0);
    Serial.println();

    if (millis() - lastSave > savePeriod && sensor.iaqAccuracy >=3) {
      saveState();
      lastSave = millis();
    }
  }
}

saveState

La fonction saveState utilise la bibliothèque Preferences de l’ESP32 pour stocker l’état du BME680 dans la mémoire non volatile (NVS) embarquée de l’ESP32. Ces données sont conservées lors des redémarrages et coupures de courant.

void saveState() {
  uint8_t state[BSEC_MAX_STATE_BLOB_SIZE];
  sensor.getState(state);
  prefs.begin("bsec", false);
  prefs.putBytes("state", state, BSEC_MAX_STATE_BLOB_SIZE);
  prefs.end();
  Serial.println("Sensor state saved");
}

L’état inclut les bases apprises pour la résistance aux gaz, la température et l’humidité, que le capteur utilise pour calculer des valeurs IAQ précises et autres sorties.

La fonction réserve un bloc mémoire de taille BSEC_MAX_STATE_BLOB_SIZE pour l’état, puis copie l’état du capteur dans ce bloc en appelant getState(state).

Nous commençons ensuite un espace de noms appelé "bsec" dans la NVS de l’ESP32 en appelant prefs.begin("bsec", false), où false indique le mode lecture & écriture.

Ensuite, nous stockons le tableau binaire d’état dans la mémoire flash sous la clé "state" et fermons la session NVS (prefs.end) pour garantir que les données sont bien enregistrées.

loadState

La fonction loadState récupère l’état précédemment sauvegardé, s’il existe :

void loadState() {
  prefs.begin("bsec", true); 
  if (prefs.getBytesLength("state") > 0) {
    uint8_t state[BSEC_MAX_STATE_BLOB_SIZE];
    prefs.getBytes("state", state, BSEC_MAX_STATE_BLOB_SIZE);
    sensor.setState(state);
    Serial.println("Sensor state loaded");
  }
  prefs.end();
}

Si prefs.getBytesLength("state") renvoie 0, cela signifie que l’état du capteur n’a pas encore été stocké et on saute la lecture de l’état.

Sinon, on appelle prefs.getBytes("state", state, BSEC_MAX_STATE_BLOB_SIZE) pour récupérer l’état et l’écrire dans le capteur via sensor.setState(state).

setup

Dans la fonction setup, nous appelons loadState après avoir initialisé le capteur. Ainsi, si l’ESP32 et donc le capteur BME680 perdent l’alimentation ou sont réinitialisés, setup est appelé et nous restaurons l’état du capteur – à condition qu’il ait été sauvegardé.

void setup(void) {
  ...

  sensor.begin(BME68X_I2C_ADDR_HIGH, Wire);

  loadState();

  ...
}

loop

Dans la fonction loop, nous sauvegardons régulièrement l’état du capteur, par exemple toutes les heures, à condition que iaqAccuracy soit égal ou supérieur à 3 :

void loop(void) {
  static unsigned long lastSave = millis();

  if (sensor.run()) {
    Serial.printf("Acc:  %d\n", sensor.iaqAccuracy);
    ...

    if (millis() - lastSave > savePeriod && sensor.iaqAccuracy >=3) {
      saveState();
      lastSave = millis();
    }
  }
}

La période de sauvegarde est contrôlée par la constante savePeriod. J’ai choisi une heure, mais vous n’êtes pas obligé de sauvegarder aussi souvent. Même toutes les trois ou quatre heures, c’est suffisant.

const unsigned long savePeriod = 3600 * 1000;  // 1 hour

Notez que malgré la sauvegarde et la restauration de l’état du capteur, la valeur iaqAccuracy sera toujours à 0 après un redémarrage du capteur, mais le temps pour atteindre 3 sera plus court.

Conclusion

Dans ce tutoriel, vous avez appris à mesurer la qualité de l’air avec le capteur environnemental BME680, un ESP32 et la bibliothèque BSEC-Arduino-library.

La BSEC-Arduino-library propose plusieurs exemples de code qui valent le coup d’œil. Le code de ce tutoriel est dérivé de l’exemple basic.ino. L’exemple basic_config_state.ino montre comment sauvegarder et restaurer l’état du capteur. Et avec la basic_config_state_ulp_plus.ino, vous pouvez faire fonctionner le capteur en mode ultra-basse consommation (ulp). Enfin, pour le deep-sleep, consultez l’exemple esp32DeepSleep.ino.

Le BME680 mesure aussi la température, l’humidité et la pression, mais si c’est votre principal intérêt et que vous n’avez pas besoin des valeurs IAQ, utilisez le Adafruit BME680 library plutôt que le plus complexe BSEC-Arduino-library. Voir le tutoriel BME680 Environmental Sensor with Arduino pour plus de détails.

Si vous n’avez pas besoin de mesures de gaz du tout, je vous conseille le capteur BME280, qui est plus petit et moins cher. Voir le tutoriel How To Use BME280 Pressure Sensor With Arduino pour plus d’informations.

Si vous avez des questions, n’hésitez pas à les poser dans la section commentaires.

Bon bricolage ; )