From fc6020e703d1b2f07221b3e811b356227846082c Mon Sep 17 00:00:00 2001 From: Mathieu Maret Date: Tue, 28 Jul 2020 16:57:03 +0200 Subject: [PATCH 1/3] Add instruction to install Bosch Sensortec Environmental Cluster library --- .gitlab-ci.yml | 1 + Readme.md | 1 + 2 files changed, 2 insertions(+) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 8164377..73ad879 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -7,6 +7,7 @@ before_script: - arduino --install-library "Adafruit BME680 Library" || true - arduino --install-library "Adafruit Unified Sensor" || true - arduino --install-library "DHT sensor library" || true + - arduino --install-library "BSEC Software Library" || true - arduino --board esp8266:esp8266:generic --save-prefs - arduino --pref "compiler.warning_level=all" --save-prefs diff --git a/Readme.md b/Readme.md index 678f0ff..7eab824 100644 --- a/Readme.md +++ b/Readme.md @@ -44,3 +44,4 @@ Quick cmdline to install deps: arduino --install-library "Adafruit BME680 Library" arduino --install-library "Adafruit Unified Sensor" arduino --install-library "DHT sensor library" + arduino --install-library "BSEC Software Library" # Then you have to follow the instruction in the README.md in the BSEC install folder From 976a9a80539bb9f63712be5785e5f816de605f5b Mon Sep 17 00:00:00 2001 From: Mathieu Maret Date: Tue, 28 Jul 2020 19:37:19 +0200 Subject: [PATCH 2/3] Add bme680 bsec --- WifiControlSensor/BME680.h | 2 +- WifiControlSensor/BME680_BSEC.h | 15 +++ WifiControlSensor/BME680_BSEC.ino | 153 ++++++++++++++++++++++++ WifiControlSensor/EEPROM.h | 2 + WifiControlSensor/EEPROM.ino | 42 ++++++- WifiControlSensor/MQTT.h | 2 + WifiControlSensor/WebServer.ino | 3 + WifiControlSensor/WifiControlSensor.ino | 28 ++++- WifiControlSensor/config.h | 8 ++ WifiControlSensor/config_device.h | 24 ++-- 10 files changed, 266 insertions(+), 13 deletions(-) create mode 100644 WifiControlSensor/BME680_BSEC.h create mode 100644 WifiControlSensor/BME680_BSEC.ino diff --git a/WifiControlSensor/BME680.h b/WifiControlSensor/BME680.h index 9d1451f..833121e 100644 --- a/WifiControlSensor/BME680.h +++ b/WifiControlSensor/BME680.h @@ -21,7 +21,7 @@ int BME680GetMeasure(float &t, float &p, float &h, float &g, float &a); int BME680Setup(); bool BME680IsConnected(); -#else //CONFIG_ENABLE_BMP80 +#else //CONFIG_ENABLE_BME680 int BME680GetMeasure(float &, float &, float&, float &, float &){return -1;}; int BME680Setup(){SKETCH_DEBUG_PRINTLN("BME680 is disabled at build time"); return -1;}; bool BME680IsConnected(){return false;}; diff --git a/WifiControlSensor/BME680_BSEC.h b/WifiControlSensor/BME680_BSEC.h new file mode 100644 index 0000000..ab868dc --- /dev/null +++ b/WifiControlSensor/BME680_BSEC.h @@ -0,0 +1,15 @@ +#pragma once +#ifdef CONFIG_BME680_BSEC_ENABLE +int BME680BSECGetMeasure(float &t, float &p, float &h, float &iaq, float &iaqAcc); +int BME680BSECSetup(); +bool BME680BSECIsConnected(); +#else // CONFIG_ENABLE_BME680_BSEC +int BME680BSECGetMeasure(float &, float &, float &, float &, float &) { + return -1; +}; +int BME680BSECSetup() { + SKETCH_DEBUG_PRINTLN("BME680 BSEC is disabled at build time"); + return -1; +}; +bool BME680BSECIsConnected() { return false; }; +#endif diff --git a/WifiControlSensor/BME680_BSEC.ino b/WifiControlSensor/BME680_BSEC.ino new file mode 100644 index 0000000..d8fc405 --- /dev/null +++ b/WifiControlSensor/BME680_BSEC.ino @@ -0,0 +1,153 @@ +#ifdef CONFIG_BME680_BSEC_ENABLE +#include "BME680_BSEC.h" + +#include "bsec.h" +/* 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[] = { +#if CONFIG_SAMPLING_PERIODE_MS == 3000 +#include "config/generic_33v_3s_4d/bsec_iaq.txt" +#elif CONFIG_SAMPLING_PERIODE_MS == 300000 +#include "config/generic_33v_300s_4d/bsec_iaq.txt" +#else +#error "Unsupport CONFIG_SAMPLING_PERIODE_MS (3000 and 300000 are supported)" +#endif +}; + +#define STATE_SAVE_PERIOD \ + UINT32_C(360 * 60 * 1000) // 360 minutes - 4 times a day +uint16_t stateUpdateCounter = 0; +Bsec iaqSensor; +uint8_t bsecState[BSEC_MAX_STATE_BLOB_SIZE] = {0}; + +// Helper function definitions +int checkIaqSensorStatus(void) { + if (iaqSensor.status != BSEC_OK) { + if (iaqSensor.status < BSEC_OK) { + SKETCH_DEBUG_PRINTLN("BSEC error code : " + String(iaqSensor.status)); + + return -2; + } else { + SKETCH_DEBUG_PRINTLN("BSEC warning code : " + String(iaqSensor.status)); + + return -1; + } + } + + if (iaqSensor.bme680Status != BME680_OK) { + if (iaqSensor.bme680Status < BME680_OK) { + SKETCH_DEBUG_PRINTLN("BME680 error code : " + String(iaqSensor.bme680Status)); + + return -2; + } else { + SKETCH_DEBUG_PRINTLN("BME680 warning code : " + String(iaqSensor.bme680Status)); + + return -1; + } + } + iaqSensor.status = BSEC_OK; + + return 0; +} + +void updateState(void) +{ + bool update = false; + /* Set a trigger to save the state. Here, the state is saved every STATE_SAVE_PERIOD with the + * first state being saved once the algorithm achieves full calibration, i.e. iaqAccuracy = 3 + */ + if (stateUpdateCounter == 0) { + if (iaqSensor.iaqAccuracy >= 3) { + update = true; + stateUpdateCounter++; + } + } else { + /* Update every STATE_SAVE_PERIOD milliseconds */ + if ((stateUpdateCounter * STATE_SAVE_PERIOD) < millis()) { + update = true; + stateUpdateCounter++; + } + } + + if (update) { + iaqSensor.getState(bsecState); + checkIaqSensorStatus(); + + SKETCH_DEBUG_PRINTLN("Writing state to EEPROM"); + + EepromSaveBME680State(bsecState); + } +} + +int BME680BSECGetMeasure(float &t, float &p, float &h, float &iaq, float &iaqAcc) +{ + if (iaqSensor.run()) { // If new data is available + t = iaqSensor.temperature; + p = iaqSensor.pressure; + h = iaqSensor.humidity; + iaq = iaqSensor.iaq; + iaqAcc = iaqSensor.iaqAccuracy; + updateState(); + } else { + return -1; + } + return 0; +} + +int BME680BSECSetup() +{ + Wire.begin(); + iaqSensor.begin(CONFIG_BME680_BSEC_I2C_ADDR, Wire); + + SKETCH_DEBUG_PRINTLN("\nBSEC library version " + String(iaqSensor.version.major) + "." + + String(iaqSensor.version.minor) + "." + String(iaqSensor.version.major_bugfix) + + "." + String(iaqSensor.version.minor_bugfix)); + + if (checkIaqSensorStatus()) + return -1; + + iaqSensor.setConfig(bsec_config_iaq); + + if (checkIaqSensorStatus()) + return -1; + + if (!EepromLoadBME680State(bsecState)) + iaqSensor.setState(bsecState); + + if (checkIaqSensorStatus()) + return -1; + + bsec_virtual_sensor_t sensorList[7] = { + BSEC_OUTPUT_RAW_TEMPERATURE, + BSEC_OUTPUT_RAW_PRESSURE, + BSEC_OUTPUT_RAW_HUMIDITY, + BSEC_OUTPUT_RAW_GAS, + BSEC_OUTPUT_IAQ, + BSEC_OUTPUT_SENSOR_HEAT_COMPENSATED_TEMPERATURE, + BSEC_OUTPUT_SENSOR_HEAT_COMPENSATED_HUMIDITY, + }; + + iaqSensor.updateSubscription(sensorList, 7, BSEC_SAMPLE_RATE_LP); + if (checkIaqSensorStatus()) + return -1; + return 0; +} + +bool BME680BSECIsConnected() +{ + if (checkIaqSensorStatus()) + return false; + return true; +} +#endif diff --git a/WifiControlSensor/EEPROM.h b/WifiControlSensor/EEPROM.h index e17e6fd..ed397c4 100644 --- a/WifiControlSensor/EEPROM.h +++ b/WifiControlSensor/EEPROM.h @@ -23,3 +23,5 @@ typedef struct productConfig_t { int EepromSaveConfig(productConfig &config); int EepromSaveBootMode(uint8_t bootMode); void EepromReadConfig(productConfig &config); +int EepromSaveBME680State(uint8_t *bsecState); +int EepromLoadBME680State(uint8_t *bsecState); diff --git a/WifiControlSensor/EEPROM.ino b/WifiControlSensor/EEPROM.ino index cd5fe25..422fd52 100644 --- a/WifiControlSensor/EEPROM.ino +++ b/WifiControlSensor/EEPROM.ino @@ -6,6 +6,8 @@ */ #include "EEPROM.h" +#define BME680_BSEC_EEPROM_ORIG (CONFIG_EEPROM_SIZE) + char eeprom[CONFIG_EEPROM_SIZE]; int EepromSaveConfig(productConfig &config) { @@ -54,7 +56,6 @@ void readConfElement(char** element, int &i) { SKETCH_DEBUG_PRINTLN("Looks like there is a configuration issue (too long)"); **element = '\0'; } - } void EepromReadConfig(productConfig &config) { @@ -101,3 +102,42 @@ void EepromReadConfig(productConfig &config) { readConfElement(&config.bssid, i); } + +int EepromSaveBME680State(uint8_t *bsecState) +{ + for (uint16_t i = BME680_BSEC_EEPROM_ORIG; + i < BME680_BSEC_EEPROM_ORIG + BSEC_MAX_STATE_BLOB_SIZE; i++) { + EEPROM.write(i + 1, bsecState[i]); + } + + EEPROM.write(BME680_BSEC_EEPROM_ORIG, BSEC_MAX_STATE_BLOB_SIZE); + EEPROM.commit(); + + return 0; +} + +int EepromLoadBME680State(uint8_t *bsecState) +{ + if (EEPROM.read(BME680_BSEC_EEPROM_ORIG) == BSEC_MAX_STATE_BLOB_SIZE) { + // Existing state in EEPROM + SKETCH_DEBUG_PRINTLN("Reading BME680 state from EEPROM"); + + for (uint16_t i = BME680_BSEC_EEPROM_ORIG; + i < BME680_BSEC_EEPROM_ORIG + BSEC_MAX_STATE_BLOB_SIZE; i++) { + bsecState[i] = EEPROM.read(i + 1); + } + + return 0; + } else { + // Erase the EEPROM with zeroes + SKETCH_DEBUG_PRINTLN("Erasing EEPROM for BME680 state"); + + for (uint16_t i = BME680_BSEC_EEPROM_ORIG; + i < BME680_BSEC_EEPROM_ORIG + BSEC_MAX_STATE_BLOB_SIZE + 1; i++) + EEPROM.write(i, 0); + + EEPROM.commit(); + + return -1; + } +} diff --git a/WifiControlSensor/MQTT.h b/WifiControlSensor/MQTT.h index 92d673e..d2ff014 100644 --- a/WifiControlSensor/MQTT.h +++ b/WifiControlSensor/MQTT.h @@ -16,6 +16,8 @@ #define BME680_HUMIDITY_FEED_FORMAT "/feeds/%s/%s/bme680/humidity" #define BME680_GAZ_FEED_FORMAT "/feeds/%s/%s/bme680/gaz" #define BME680_ALT_FEED_FORMAT "/feeds/%s/%s/bme680/alt" +#define BME680_IAQ_FEED_FORMAT "/feeds/%s/%s/bme680/iaq" +#define BME680_IAQ_ACC_FEED_FORMAT "/feeds/%s/%s/bme680/iaqAcc" #ifndef CONFIG_DISABLE_MQTT #include "Adafruit_MQTT.h" diff --git a/WifiControlSensor/WebServer.ino b/WifiControlSensor/WebServer.ino index e00e8a7..ca3e2df 100644 --- a/WifiControlSensor/WebServer.ino +++ b/WifiControlSensor/WebServer.ino @@ -40,6 +40,9 @@ void WebHandleRoot() { #ifdef CONFIG_ENABLE_BME680 "" + (BME680IsConnected() ? "
BME680
Temperature " + String(bme680T, 2) + "C
Pressure " + String(bme680P, 2) + "hPa
Humidity " + String(bme680H, 2) + "%
Gaz " + String(bme680G, 2) + "kOhm
" : "BME680 Disconnected" ) + "" #endif +#ifdef CONFIG_BME680_BSEC_ENABLE + "" + (BME680BSECIsConnected() ? "
BME680 BSEC
Temperature " + String(bme680BSECT, 2) + "C
Pressure " + String(bme680BSECP, 2) + "hPa
Humidity " + String(bme680BSECH, 2) + "%
Indoor Air Quality " + String(bme680BSECIaq, 2) + "/500
IAQ Accuraccy" + String(bme680BSECIaqAcc) + "/3
": "BME680 Disconnected" ) + "" +#endif #ifdef CONFIG_ENABLE_DRY_SENSOR "Dryness " + String((dryness * 100) / 1024) + "%
" #endif diff --git a/WifiControlSensor/WifiControlSensor.ino b/WifiControlSensor/WifiControlSensor.ino index 3aeedd4..52496bd 100644 --- a/WifiControlSensor/WifiControlSensor.ino +++ b/WifiControlSensor/WifiControlSensor.ino @@ -44,6 +44,7 @@ #include "debug_sketch.h" #include "BMP180.h" #include "BME680.h" +#include "BME680_BSEC.h" #include "sensor_DHT.h" #include "dry_sensor.h" #include "MQTT.h" @@ -63,6 +64,7 @@ extern "C" { double temp, pressure; float dhtTemp, dhtHumidity; float bme680T, bme680P, bme680H, bme680G, bme680A; +float bme680BSECT, bme680BSECP, bme680BSECH, bme680BSECIaq, bme680BSECIaqAcc; int dryness; uint8_t mode; int reconfig = 0; @@ -142,7 +144,7 @@ void WifiSetup(productConfig conf) { reconfig = 0; return; } - if (connectionTry == 20) { + if (connectionTry == 60) { SKETCH_DEBUG_PRINTLN("Cannot connect to wifi. Try without BSSID and channel"); WiFi.begin(conf.ssid, conf.password); } @@ -278,6 +280,9 @@ void setup() { if(!BME680Setup()){ SKETCH_DEBUG_PRINTLN("BME680 init success"); } + if(!BME680BSECSetup()){ + SKETCH_DEBUG_PRINTLN("BME680 with BSEC init success"); + } WebSetupServer(mode); } #ifdef CONFIG_ENABLE_POWER_SAVE @@ -312,7 +317,7 @@ void loop() { } if (!DryGetMeasure(dryness)) { SKETCH_DEBUG_PRINTF("Current dryness %d %%\n", (dryness * 100) / 1024); - batchInfo.push_back({dryness, DRY_FEED_FORMAT, 0, 0}); + batchInfo.push_back({(float)dryness, DRY_FEED_FORMAT, 0, 0}); } if (!BME680GetMeasure(bme680T, bme680P, bme680H, bme680G, bme680A)) { SKETCH_DEBUG_PRINT("Current T°C: "); @@ -332,6 +337,25 @@ void loop() { batchInfo.push_back({bme680A, BME680_ALT_FEED_FORMAT, 0, 0}); } + if (!BME680BSECGetMeasure(bme680BSECT, bme680BSECP, bme680BSECH, bme680BSECIaq, + bme680BSECIaqAcc)) { + SKETCH_DEBUG_PRINT("Current T°C: "); + SKETCH_DEBUG_PRINT(bme680BSECT); + SKETCH_DEBUG_PRINT(" Pressure hPa: "); + SKETCH_DEBUG_PRINT(bme680BSECP); + SKETCH_DEBUG_PRINT(" Humidity %: "); + SKETCH_DEBUG_PRINT(bme680BSECH); + SKETCH_DEBUG_PRINT(" Indoor Air Quality: "); + SKETCH_DEBUG_PRINT(bme680BSECIaq); + SKETCH_DEBUG_PRINT(" IAQ Accuracy: "); + SKETCH_DEBUG_PRINTLN(bme680BSECIaqAcc); + batchInfo.push_back({bme680BSECT, BME680_TEMPERATURE_FEED_FORMAT, 0, 0}); + batchInfo.push_back({bme680BSECP, BME680_PRESSURE_FEED_FORMAT, 0, 0}); + batchInfo.push_back({bme680BSECH, BME680_HUMIDITY_FEED_FORMAT, 0, 0}); + batchInfo.push_back({bme680BSECIaq, BME680_IAQ_FEED_FORMAT, 0, 0}); + batchInfo.push_back({bme680BSECIaqAcc, BME680_IAQ_ACC_FEED_FORMAT, 0, 0}); + } + if (mode == BOOTMODE_NORMAL) MqttBatchPublish(batchInfo, conf.mqttUser, conf.host); nbCycle = 0; diff --git a/WifiControlSensor/config.h b/WifiControlSensor/config.h index de3f4e4..a2515bc 100644 --- a/WifiControlSensor/config.h +++ b/WifiControlSensor/config.h @@ -13,6 +13,14 @@ #define CONFIG_SAMPLING_PERIODE_MS 60000 #endif +#if defined(CONFIG_ENABLE_BME680) && defined(CONFIG_BME680_BSEC_ENABLE) +#error "BME680 and BME680 BSEC cannot be enabled together" +#endif + +#ifndef CONFIG_BME680_BSEC_I2C_ADDR +#define CONFIG_BME680_BSEC_I2C_ADDR 0x76 +#endif + #if defined(CONFIG_ENABLE_BMP180) && !defined(CONFIG_BMP180_SDA) #error "When enabling BMP180, you should configure SDA pin" #elif !defined(CONFIG_ENABLE_BMP180) diff --git a/WifiControlSensor/config_device.h b/WifiControlSensor/config_device.h index f00827c..2651a36 100644 --- a/WifiControlSensor/config_device.h +++ b/WifiControlSensor/config_device.h @@ -14,16 +14,22 @@ // Enable the temperature, pressure, humidity and gaz Sensor BME680 on standard i2c esp8266 pins // It use default i2c pin GPIO4(D2): SDA, GPIO5(D1):SCL -#define CONFIG_ENABLE_BME680 +// Should be powered by 3.3v and sampled every 3sec or 300s (Or you should adapt bsec_config_iaq in BME680_BSEC.ino ) +#define CONFIG_BME680_BSEC_ENABLE +#define CONFIG_BME680_BSEC_I2C_ADDR 0x77 + +// Enable the temperature, pressure, humidity and gaz Sensor BME680 on standard i2c esp8266 pins +// It use default i2c pin GPIO4(D2): SDA, GPIO5(D1):SCL +//#define CONFIG_ENABLE_BME680 // Enable the temperatue and pressure Sensor BMP180 // (CONFIG_BMP180_SDA and CONFIG_BMP180_SDA should be defined as well) -#define CONFIG_ENABLE_BMP180 -#define CONFIG_BMP180_SDA SDA //D2 -#define CONFIG_BMP180_SCL SCL //D1 - -#define CONFIG_ENABLE_DHT -#define CONFIG_DHT_PIN 2 -#define CONFIG_DHT_TYPE DHT22 +//#define CONFIG_ENABLE_BMP180 +//#define CONFIG_BMP180_SDA SDA //D2 +//#define CONFIG_BMP180_SCL SCL //D1 +// +//#define CONFIG_ENABLE_DHT +//#define CONFIG_DHT_PIN 2 +//#define CONFIG_DHT_TYPE DHT22 //#define CONFIG_ENABLE_DRY_SENSOR //If the dry sensor is powered by a GPIO, this GPIO could be defined here @@ -47,7 +53,7 @@ //#define CONFIG_WEB_DELAY_MS 100 // Get sensors value every X ms -#define CONFIG_SAMPLING_PERIODE_MS 300000 +#define CONFIG_SAMPLING_PERIODE_MS 3000 // Name of the SSID when in AP mode for configuration #define CONFIG_SSID_NAME "ESPConfiguratorBureau" From 0e3afa391b46811bb14f3e53dc8994ab240bde4c Mon Sep 17 00:00:00 2001 From: Mathieu Maret Date: Tue, 28 Jul 2020 19:45:23 +0200 Subject: [PATCH 3/3] Add check on EEPROM size --- WifiControlSensor/BME680_BSEC.h | 1 + WifiControlSensor/EEPROM.ino | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/WifiControlSensor/BME680_BSEC.h b/WifiControlSensor/BME680_BSEC.h index ab868dc..d6a6450 100644 --- a/WifiControlSensor/BME680_BSEC.h +++ b/WifiControlSensor/BME680_BSEC.h @@ -4,6 +4,7 @@ int BME680BSECGetMeasure(float &t, float &p, float &h, float &iaq, float &iaqAcc int BME680BSECSetup(); bool BME680BSECIsConnected(); #else // CONFIG_ENABLE_BME680_BSEC +#define BSEC_MAX_STATE_BLOB_SIZE 0 int BME680BSECGetMeasure(float &, float &, float &, float &, float &) { return -1; }; diff --git a/WifiControlSensor/EEPROM.ino b/WifiControlSensor/EEPROM.ino index 422fd52..8c37df3 100644 --- a/WifiControlSensor/EEPROM.ino +++ b/WifiControlSensor/EEPROM.ino @@ -8,6 +8,10 @@ #define BME680_BSEC_EEPROM_ORIG (CONFIG_EEPROM_SIZE) +#if (CONFIG_EEPROM_SIZE + BSEC_MAX_STATE_BLOB_SIZE) >= SPI_FLASH_SEC_SIZE +#error "CONFIG_EEPROM_SIZE too big" +#endif + char eeprom[CONFIG_EEPROM_SIZE]; int EepromSaveConfig(productConfig &config) {