user330352
Published © CC BY

BME680 AirQuality sensor

Replacing a death EVE Room with Wifi connected ESP32 and BME680 AirQuality sensor

IntermediateFull instructions provided2 hours826
BME680 AirQuality sensor

Things used in this project

Hardware components

Adafruit Stemma BME680
×1
Adafruit HUZZAH32 – ESP32 Feather Board
Adafruit HUZZAH32 – ESP32 Feather Board
×1

Software apps and online services

PlatformIO IDE
PlatformIO IDE

Story

Read more

Schematics

Schema

Fritzing

Code

Sensor script

C/C++
C++, used with PlatformIO and Arduino Framework
#include <Arduino.h>
#include <WiFi.h>
#include <HTTPClient.h>
#include "bsec.h"

#define VBATPIN A13 // measuring battery
#define BMEPOWER A1 // power the BME Sensor
const char* server ="xxxxxxxxxxxxxxx";
const char* ssid  = "xxxxxxxxxxxxxxx";
const char* pass =  "xxxxxxxxxxxxxxx";

 
  * Configure the BSEC library with information about the sensor
    18v/33v = Voltage at Vdd. 1.8V or 3.3V
    3s/300s = BSEC operating mode, BSEC_SAMPLE_RATE_LP or BSEC_SAMPLE_RATE_ULP
    4d/28d = Operating age of the sensor in days
    generic_18v_3s_4d
    generic_18v_3s_28d
    generic_18v_300s_4d
    generic_18v_300s_28d
    generic_33v_3s_4d
    generic_33v_3s_28d
    generic_33v_300s_4d
    generic_33v_300s_28d
*/
const uint8_t bsec_config_iaq[] = {
#include "config/generic_33v_3s_4d/bsec_iaq.txt"
};


// Helper functions declarations
void checkIaqSensorStatus(void);
void errLeds(void);

Bsec iaqSensor;
byte i2C_address;
String postData;
String heaterStatus;

int Voltage;
float LowBattery = 3.5;
int StatusLowBattery = 0;
bool Test = false; // set here true get Serial output, false surpress all serial output
uint16_t ppm;
float gasResistance;
double temperature;
double humidity;

float m = -0.436; //Slope 
float b = 1.0240916; //Y-Intercept = 0.0000916 + 1.024
float ref_air = 5000; // measured

uint32_t uS_TO_S_FACTOR=1000000UL; //Conversion factor for micro seconds to seconds
uint32_t TIME_TO_SLEEP=600;    //Time ESP32 will go to sleep (in seconds) = 10 min

void ReadBattery() {
  float measuredvbat = analogRead(VBATPIN);
  StatusLowBattery = 0;
  measuredvbat *=2; // we divided by 2, so multiply back
  measuredvbat *=3.3; // Multiply by 3.3V, our reference voltage
  measuredvbat /= 1024; // convert to voltage
  if (measuredvbat>4.25f){measuredvbat=4.25f;}
      Voltage = roundf(100*measuredvbat/4.25f);
  if (Voltage <= LowBattery) {StatusLowBattery = 1;}
 };
int CalculateIAQ(double score) {
  int QualityIndex = 0;
  if (score >= 2101) QualityIndex = 5;
  else if (score >= 1601 && score <= 2100)  QualityIndex = 4;
  else if (score >= 1101 && score <= 1600 ) QualityIndex = 3;
  else if (score >=  701 && score <= 1100 ) QualityIndex = 2;
  else if (score >=  0.0 && score <=  700 ) QualityIndex = 1;
  return QualityIndex;
};

void connectToNetwork() {
   WiFi.begin(ssid, pass);
   int notConnectedCounter = 0;
   while (WiFi.status() != WL_CONNECTED)
   {
      notConnectedCounter++;
      if (Test == true) {Serial.print(".");}
      delay(500);
      if(notConnectedCounter > 30) { // Reset board if not connected after 15s
          if (Test==true) {Serial.println("Resetting due to Wifi not connecting...");}
          ESP.restart();
        }
    }
    if (Test ==true) {Serial.println("Connected to network");}
};

void setBME680() {
// sometimes the address drifted from 0x76 (118) to 0x77 (119) and vis versa
byte error;
Wire.beginTransmission(118);
error = Wire.endTransmission();
if (error == 0) {iaqSensor.begin(BME680_I2C_ADDR_PRIMARY, Wire);}
else  {iaqSensor.begin(BME680_I2C_ADDR_SECONDARY, Wire);}
iaqSensor.setConfig(bsec_config_iaq);
checkIaqSensorStatus();
};

void BME680_read(){
  if (iaqSensor.run()) 
  {
    int gasStabStatus = iaqSensor.stabStatus;
    if (gasStabStatus==1) {heaterStatus = "OK";} else {heaterStatus="ERROR";} 
    gasResistance = iaqSensor.gasResistance;
    humidity = iaqSensor.humidity;
    temperature = iaqSensor.temperature - 2.3;
    float ratio = gasResistance / ref_air;
    double ppm_log = (log10(ratio)-b) / m;
    ppm = round(pow(10, ppm_log)); 
  } else {
    checkIaqSensorStatus();
  }
  digitalWrite(BMEPOWER,LOW); // shut down before send and deep sleep
};

void setup() {
  pinMode(BMEPOWER, OUTPUT);
  digitalWrite(BMEPOWER,HIGH);delay(100);
  if (Test==true) {
  // Initialize serial and wait for port to open:
  Serial.begin(9600); 
  while (!Serial) {delay(10);}
  delay(1000);
  }
  Wire.begin();
  setBME680();
  bsec_virtual_sensor_t sensorList[6] = {
    BSEC_OUTPUT_RAW_TEMPERATURE,
    BSEC_OUTPUT_RAW_HUMIDITY,
    BSEC_OUTPUT_STABILIZATION_STATUS,
    BSEC_OUTPUT_RAW_GAS,
    BSEC_OUTPUT_SENSOR_HEAT_COMPENSATED_TEMPERATURE,
    BSEC_OUTPUT_SENSOR_HEAT_COMPENSATED_HUMIDITY,
  };
  iaqSensor.updateSubscription(sensorList, 6, BSEC_SAMPLE_RATE_LP);
  checkIaqSensorStatus();
  // esp32 hibernation
  esp_sleep_pd_config(ESP_PD_DOMAIN_MAX, ESP_PD_OPTION_OFF);
  esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_PERIPH, ESP_PD_OPTION_OFF);
  esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_SLOW_MEM, ESP_PD_OPTION_OFF);
  esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_FAST_MEM, ESP_PD_OPTION_OFF);
  // set timer
  esp_sleep_enable_timer_wakeup(TIME_TO_SLEEP * uS_TO_S_FACTOR);
};

void loop() {
  BME680_read();
  ReadBattery();
  postData = "{\"aid\":3,\"services\":{";
  postData +="\"TemperatureSensor\":{\"CurrentTemperature\":" + String(temperature) + "},";
  postData +="\"HumiditySensor\":{\"CurrentRelativeHumidity\":" + String(humidity) + "},";
  postData +="\"AirQualitySensor\":{\"AirQuality\":" + String(CalculateIAQ(ppm)) +  ", \"EveAirQuality\":" + String(ppm) +"},";
  postData +="\"Battery\":{\"ChargingState\": 2, \"StatusLowBattery\":" + String(StatusLowBattery) + ", \"BatteryLevel\":" + String(Voltage) + "}";
  postData += "}}";
  connectToNetwork();
  if (WiFi.status() == WL_CONNECTED) {
    if (Test == true) {
    Serial.println(WiFi.localIP());
    Serial.println(postData);
    Serial.println(" calculate Particle = " + String(ppm) + " ppm");
    Serial.println(" gasResistiance     = " + String(gasResistance)  + " Ohm");
    Serial.println(" gasStabStatus      = " + heaterStatus);
    Serial.println(" --------------------------------------");
    }
    HTTPClient http;
    int notConnectedCounter = 0;
    bool connect_state = true;
    while (!http.begin(server)) {
        if (Test==true) {Serial.println("Connection failed - Waiting 5 seconds before retrying...");}
        delay(500);
        notConnectedCounter ++;
        if(notConnectedCounter > 10) { 
          connect_state = false; 
          http.end();
        }
    }
    if (connect_state == true) {
    http.addHeader("Content-Type", "application/json");
    int Response = http.POST(postData);
    while (Response !=200) {
      if (Test==true) {Serial.println("HTTP failed");}
      delay(500);
      }
    http.end();
    if (Test==true) {Serial.println("HTTP OK");}
    }
  }
  esp_deep_sleep_start();
};
// Helper function definitions
void checkIaqSensorStatus(void)
{
  if (iaqSensor.status != BSEC_OK) {
    if (iaqSensor.status < BSEC_OK) {
      if (Test==true){Serial.println("BSEC error code : " + String(iaqSensor.status));}
      for (;;)
        errLeds(); /* Halt in case of failure */
    } else {
      if (Test==true) {Serial.println("BSEC warning code : " + String(iaqSensor.status));}
    }
  }
  iaqSensor.status = BSEC_OK;
};

void errLeds(void)
{
  pinMode(LED_BUILTIN, OUTPUT);
  digitalWrite(LED_BUILTIN, HIGH);
  delay(100);
  digitalWrite(LED_BUILTIN, LOW);
  delay(100);
};

Credits

user330352

user330352

5 projects • 1 follower
start programming on the first single chip 8086 in the '80

Comments