sensor-de-calidad-ambiental-con-arduino-y-bme680

Sensor de calidad ambiental con Arduino y BME680

¿Qué es un BME680?

El BME680 es un sensor de calidad de aire interior del fabricante Bosch que incorpora medición de temperatura, humedad, presión barométrica y contenido en compuestos orgánicos volátiles (VOC) que podemos usar fácilmente junto con un procesador como Arduino.

En sensor puede detectar algunos gases como monóxido de carbono, etanol, o acetona. También es posible calcular parámetros de calidad de aire interior, así como estimar la cantidad de dióxido de carbono equivalente.

Sin embargo, el BEM680 no puede distinguir entre los gases o realizar la medición de forma individual para cada uno de ellos. En su lugar, totaliza el efecto total del VOC para proporcionar una idea cuantitativa de la calidad del aire resultante.

El BME680 tiene una tensión de funcionamiento de 1.2 V a 3.6 V. No obstante, algunos módulos incorporan un regulador de voltaje y un conversor de nivel, que permiten operarlos directamente a 5V.

EL consumo es de 0.15 µA en sleep, 3.7 µA para mediciones de humedad, presión y temperatura, y de 0.09 a 12 mA para medición del gas, en función del modo de operación elegido.

La comunicación se realiza a través de I²C hasta 3.4 MHz, o SPI de 3 o 4 cables hasta 10 MHz. Por tanto, es muy sencillo conectarlo a un procesador como Arduino.

Precio

Podemos encontrar módulo con sensor BME680 por 8-10€, en vendedores internacionales de eBay y AliExpress.

arduino-calidad-aire-bme680-componente

¿Cómo funciona un BME680?

El BME680 está compuesto por un sensor MOX (Metal-oxide). En su funcionamiento, el MOX es calentado y este absorbe moléculas de oxigeno. El Datasheet del componente proporciona los siguientes valores.

Molar fractionCompoundProduction toleranceCertified accuracy
5 ppmEthane20%5%
10 ppmIsoprene /2-methyl-1,3 Butadiene20%5%
10 ppmEthanol20%5%
50 ppmAcetone20%5%
15 ppmCarbon Monoxide10%2%

Al absorber los VOC el MOX varía su conductividad, por lo que la resistencia del mismo varía. Con el valor de esta resistencia es posible calcular un índice de calidad del aire.

ResistenciaCalidad aire
0 – 50Good
51 – 100Average
101 – 150Little bad
151 – 200Bad
201 – 300Worse
301 – 500Very bad

El sensor proporciona una precisión de 1.0 °C para medición de temperatura, ± 3% para humedad, y ± 1 hPa para presión barométrica, equivalente a una precisión de altura de ± 1 metro.

Al igual que la mayoría de sensores químicos, el BME680 necesita un pre-calentador. El fabricante recomienda que el sensor funcione durante 30 minutos para que las mediciones se estabilicen, y durante 48 horas si se cambia de ubicación.

Esquema de montaje

El control de módulo se realiza a través de I2C, por lo que el conexionado es muy sencillo. Simplemente alimentamos el módulo mediante Gnd y Vdd, y conectamos los pines del I2C SDA y SCL.

arduino-calidad-aire-bme680-esquema

La conexión vista desde Arduino sería la siguiente.

arduino-calidad-aire-bme680-conexion

Ejemplos de código

Librería Adafruit

Para controlar el BME680 podemos usar la librería desarrollada por Adafruit, cuyo código está disponible en https://github.com/adafruit/Adafruit_BME680.

La librería incorpora ejemplos de su uso, que resulta aconsejable consultar. El siguiente es un resumen extraído de los mismos.

#include <Wire.h>
#include <SPI.h>
#include <Adafruit_Sensor.h>
#include "Adafruit_BME680.h"

const int BME_SCK = 13;
const int BME_MISO = 12;
const int BME_MOSI = 11;
const int BME_CS = 10;

const int SEALEVELPRESSURE_HPA = 1013.25;

Adafruit_BME680 bme;

void setup() {
    Serial.begin(9600);
    while (!Serial);
    Serial.println(F("BME680 test"));

    if (!bme.begin()) {
    Serial.println("Could not find a valid BME680 sensor, check wiring!");
    while (1);
    }

    // Set up oversampling and filter initialization
    bme.setTemperatureOversampling(BME680_OS_8X);
    bme.setHumidityOversampling(BME680_OS_2X);
    bme.setPressureOversampling(BME680_OS_4X);
    bme.setIIRFilterSize(BME680_FILTER_SIZE_3);
    bme.setGasHeater(320, 150); // 320*C for 150 ms
}

void loop() {
    if (! bme.performReading()) {
    Serial.println("Failed to perform reading :(");
    return;
    }
    Serial.print("Temperature = ");
    Serial.print(bme.temperature);
    Serial.println(" *C");

    Serial.print("Pressure = ");
    Serial.print(bme.pressure / 100.0);
    Serial.println(" hPa");

    Serial.print("Humidity = ");
    Serial.print(bme.humidity);
    Serial.println(" %");

    Serial.print("Gas = ");
    Serial.print(bme.gas_resistance / 1000.0);
    Serial.println(" KOhms");

    Serial.print("Approx. Altitude = ");
    Serial.print(bme.readAltitude(SEALEVELPRESSURE_HPA));
    Serial.println(" m");

    Serial.println();
    delay(2000);
}

Librería Bosch

Otra alternativa es emplear la librería desarrollada por Bosch, disponible en este enlace. Esta librería es algo más compleja que la de Adafruit, pero proporciona más valores del sensor como, por ejemplo, la estimación de CO2 equivalente.

Sin embargo la librería de Bosch ocupa más memoria. Por tanto, no se puede emplear en un Arduino Uno, Nano o similar. Si es compatible con Arduino Mega, ESP8266, ESP32, y otros dispositivo. Consultad la documentación para una lista completa de dispositivos compatibles.

La librería incorpora ejemplos de su uso, que resulta aconsejable consultar. El siguiente es un resumen extraído de los mismos.


    #include "bsec.h"

    // Helper functions declarations
    void checkIaqSensorStatus(void);
    void errLeds(void);
    
    // Create an object of the class Bsec
    Bsec iaqSensor;
    
    String output;
    
    // Entry point for the example
    void setup(void)
    {
      Serial.begin(115200);
      Wire.begin();
    
      iaqSensor.begin(BME680_I2C_ADDR_PRIMARY, Wire);
      output = "\nBSEC library version " + String(iaqSensor.version.major) + "." + String(iaqSensor.version.minor) + "." + String(iaqSensor.version.major_bugfix) + "." + String(iaqSensor.version.minor_bugfix);
      Serial.println(output);
      checkIaqSensorStatus();
    
      bsec_virtual_sensor_t sensorList[10] = {
        BSEC_OUTPUT_RAW_TEMPERATURE,
        BSEC_OUTPUT_RAW_PRESSURE,
        BSEC_OUTPUT_RAW_HUMIDITY,
        BSEC_OUTPUT_RAW_GAS,
        BSEC_OUTPUT_IAQ,
        BSEC_OUTPUT_STATIC_IAQ,
        BSEC_OUTPUT_CO2_EQUIVALENT,
        BSEC_OUTPUT_BREATH_VOC_EQUIVALENT,
        BSEC_OUTPUT_SENSOR_HEAT_COMPENSATED_TEMPERATURE,
        BSEC_OUTPUT_SENSOR_HEAT_COMPENSATED_HUMIDITY,
      };
    
      iaqSensor.updateSubscription(sensorList, 10, BSEC_SAMPLE_RATE_LP);
      checkIaqSensorStatus();
    
      // Print the header
      output = "Timestamp [ms], raw temperature [°C], pressure [hPa], raw relative humidity [%], gas [Ohm], IAQ, IAQ accuracy, temperature [°C], relative humidity [%], Static IAQ, CO2 equivalent, breath VOC equivalent";
      Serial.println(output);
    }
    
    // Function that is looped forever
    void loop(void)
    {
      unsigned long time_trigger = millis();
      if (iaqSensor.run()) { // If new data is available
        output = String(time_trigger);
        output += ", " + String(iaqSensor.rawTemperature);
        output += ", " + String(iaqSensor.pressure);
        output += ", " + String(iaqSensor.rawHumidity);
        output += ", " + String(iaqSensor.gasResistance);
        output += ", " + String(iaqSensor.iaq);
        output += ", " + String(iaqSensor.iaqAccuracy);
        output += ", " + String(iaqSensor.temperature);
        output += ", " + String(iaqSensor.humidity);
        output += ", " + String(iaqSensor.staticIaq);
        output += ", " + String(iaqSensor.co2Equivalent);
        output += ", " + String(iaqSensor.breathVocEquivalent);
        Serial.println(output);
      } else {
        checkIaqSensorStatus();
      }
    }
    
    // Helper function definitions
    void checkIaqSensorStatus(void)
    {
      if (iaqSensor.status != BSEC_OK) {
        if (iaqSensor.status < BSEC_OK) {
          output = "BSEC error code : " + String(iaqSensor.status);
          Serial.println(output);
          for (;;)
            errLeds(); /* Halt in case of failure */
        } else {
          output = "BSEC warning code : " + String(iaqSensor.status);
          Serial.println(output);
        }
      }
    
      if (iaqSensor.bme680Status != BME680_OK) {
        if (iaqSensor.bme680Status < BME680_OK) {
          output = "BME680 error code : " + String(iaqSensor.bme680Status);
          Serial.println(output);
          for (;;)
            errLeds(); /* Halt in case of failure */
        } else {
          output = "BME680 warning code : " + String(iaqSensor.bme680Status);
          Serial.println(output);
        }
      }
    }
    
    void errLeds(void)
    {
      pinMode(LED_BUILTIN, OUTPUT);
      digitalWrite(LED_BUILTIN, HIGH);
      delay(100);
      digitalWrite(LED_BUILTIN, LOW);
      delay(100);
    }

Descarga el código

Todo el código de esta entrada está disponible para su descarga en Github. github-full