Bosch Sensortec Community

    cancel
    Showing results for 
    Search instead for 
    Did you mean: 

    Supporting BME680 with BSEC on ESP32 using deep-sleep

    Supporting BME680 with BSEC on ESP32 using deep-sleep

    mikkojaakkola
    Occasional Visitor

    I'm thinking of using BME680 and related software with ESP32 using deep-sleep. In deep-sleep, the system turns system RAM off but small part of RAM (RTC_RAM) is kept powered for preserve the state during the sleep. I was thinking of putting data structures from bme680.h, the integration files into the RTC_RAM and initialize everything once during the power-up but after returning from the deep-sleep, I would just trigger measurements (no initializations) as MBE680 is kept all the time powered in (U)LP so it retains its state during the deep-sleep boot-ups.

    This part should work fine but I'm not sure if any system data is kept at libalgosec. If there are some data at the algorithm section that needs to be preserved between measurements (or initialized in the library), then this approach would not work and the best I can do is light-sleep. Can you, please, confirm if you store any system data in the algorithm library that would prevent my approach?

    13 REPLIES 13

    kgoveas
    Community Moderator
    Community Moderator

    Hi mikkojaakkola,

    The state should be sufficient to resume using BSEC when the the ESP32 is powered back on. If you see a problem in the behavior of the algorithm, for example, the IAQ estimate never calibrates, get back to us.

    Regards,
    kgoveas

    I'm trying to use BME680 with ESP32 in deep sleep mode but it doesn't become calibrated even after more than 2 days running. There is a problem in Bsec::getTimeMs https://github.com/BoschSensortec/BSEC-Arduino-library/blob/master/src/bsec.cpp#L413-L424 it uses millis() that starts from zero after each deep sleep so compensated temperature and humidity get completely wrong after first deep sleep. I fixed it locally and compensated temperature and humidity are fine for me now. But IAQ still constant i.e. sensor is not calibrated but gas resistance looks fine. Could could you please help with getting BME680 works with deep sleep mode? I can share my minimal reproducer if it might help.

    handytech
    Community Moderator
    Community Moderator

    @Dmitry wrote:

    IThere is a problem in Bsec::getTimeMs https://github.com/BoschSensortec/BSEC-Arduino-library/blob/master/src/bsec.cpp#L413-L424 it uses millis() that starts from zero after each deep sleep so compensated temperature and humidity get completely wrong after first deep sleep.


    The BSEC library expects an 'absolute' timestamp while the Arduino reference code doesn't expect the system to go into deep-suspend mode. Therefore it is correct that appropriate changes are needed for you to provide relevant timestamps.


    @Dmitry wrote:

     Could could you please help with getting BME680 works with deep sleep mode? I can share my minimal reproducer if it might help.


    It would indeed help if you could share some code snippet and short log of the inputs fed to BSEC. As mentioned in the posts above, it is mandatory that you store/reload the state file every time the MCU enters/leaves deep-suspend mode, otherwise the library will never be able to calibrate.

    I attached my minimal reproducer and log for 30 minutes (log.cpp - .cpp because forum doesn't accept .txt files as attachment). But it looks like I found why calibration didn't happen. In the real code I synchronize time with the server and I had a race condition between sensor initialization and time sync. Therefore for there was a huge gap in time between the very first and all subsequent measurements. After fixing this race sensor increased accuracy from 0 to 1 but getting further takes significantly longer so I'm still waiting. I'll post if it doesn't get to accuracy 3.

     

    #include <Arduino.h>
    #include <bsec.h>
    #include <bsec_serialized_configurations_iaq.h>
    #include <sys/time.h>
    
    #define LOG(fmt, ...) (Serial.printf("%09llu: " fmt "\n", GetTimestamp(), ##__VA_ARGS__))
    
    Bsec sensor;
    
    RTC_DATA_ATTR uint8_t sensor_state[BSEC_MAX_STATE_BLOB_SIZE] = {0};
    RTC_DATA_ATTR int64_t sensor_state_time = 0;
    
    bsec_virtual_sensor_t sensor_list[] = {
      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,
    };
    
    int64_t GetTimestamp() {
    	struct timeval tv;
    	gettimeofday(&tv, NULL);
    	return (tv.tv_sec * 1000LL + (tv.tv_usec / 1000LL));
    }
    
    bool CheckSensor() {
      if (sensor.status < BSEC_OK) {
        LOG("BSEC error, status %d!", sensor.status);
        return false;;
      } else if (sensor.status > BSEC_OK) {
        LOG("BSEC warning, status %d!", sensor.status);
      }
    
      if (sensor.bme680Status < BME680_OK) {
        LOG("Sensor error, bme680_status %d!", sensor.bme680Status);
        return false;
      } else if (sensor.bme680Status > BME680_OK){
        LOG("Sensor warning, status %d!", sensor.bme680Status);
      }
    
      return true;
    }
    
    void DumpState(const char* name, const uint8_t* state) {
      LOG("%s:", name);
      for (int i = 0; i < BSEC_MAX_STATE_BLOB_SIZE; i++) {
        Serial.printf("%02x ", state[i]);
        if (i % 16 == 15) {
          Serial.print("\n");
        }
      }
      Serial.print("\n");
    }
    
    void setup() {
      Serial.begin(115200);
    
      sensor.begin(BME680_I2C_ADDR_SECONDARY, Wire);
      if (!CheckSensor()) {
        LOG("Failed to init BME680, check wiring!");
        return;
      }
    
      LOG("BSEC version %d.%d.%d.%d", sensor.version.major, sensor.version.minor, sensor.version.major_bugfix, sensor.version.minor_bugfix);
    
      sensor.setConfig(bsec_config_iaq);
      if (!CheckSensor()) {
        LOG("Failed to set config!");
        return;
      }
    
      if (sensor_state_time) {
        DumpState("setState", sensor_state);
        sensor.setState(sensor_state);
        if (!CheckSensor()) {
          LOG("Failed to set state!");
          return;
        } else {
          LOG("Successfully set state from %lld", sensor_state_time);
        }
      } else {
        LOG("Saved state missing");
      }
    
      sensor.updateSubscription(sensor_list, sizeof(sensor_list)/sizeof(sensor_list[0]), BSEC_SAMPLE_RATE_ULP);
      if (!CheckSensor()) {
        LOG("Failed to update subscription!");
        return;
      }
    
      LOG("Sensor init done");
    }
    
    void loop() {
      if (sensor.run()) {
        LOG("Temperature raw %.2f compensated %.2f", sensor.rawTemperature, sensor.temperature);
        LOG("Humidity raw %.2f compensated %.2f", sensor.rawHumidity, sensor.humidity);
        LOG("Pressure %.2f kPa", sensor.pressure/1000);
        LOG("IAQ %.0f accuracy %d", sensor.iaqEstimate, sensor.iaqAccuracy);
        LOG("Static IAQ %.0f accuracy %d", sensor.staticIaq, sensor.staticIaqAccuracy);
        LOG("Gas resistance %.2f kOhm", sensor.gasResistance/1000);
    
        sensor_state_time = GetTimestamp();
        sensor.getState(sensor_state);
        DumpState("getState", sensor_state);
        LOG("Saved state to RTC memroy at %lld", sensor_state_time);
        CheckSensor();
    
        uint64_t time_ms = 300 * 1000 * 1000ull - esp_timer_get_time();
        LOG("Deep sleep for %llu ms!", time_ms/1000);
        esp_sleep_enable_timer_wakeup(time_ms);
        esp_deep_sleep_start();
      }
    }

     

     

    Icon--AD-black-48x48Icon--address-consumer-data-black-48x48Icon--appointment-black-48x48Icon--back-left-black-48x48Icon--calendar-black-48x48Icon--center-alignedIcon--Checkbox-checkIcon--clock-black-48x48Icon--close-black-48x48Icon--compare-black-48x48Icon--confirmation-black-48x48Icon--dealer-details-black-48x48Icon--delete-black-48x48Icon--delivery-black-48x48Icon--down-black-48x48Icon--download-black-48x48Ic-OverlayAlertIcon--externallink-black-48x48Icon-Filledforward-right_adjustedIcon--grid-view-black-48x48IC_gd_Check-Circle170821_Icons_Community170823_Bosch_Icons170823_Bosch_Icons170821_Icons_CommunityIC-logout170821_Icons_Community170825_Bosch_Icons170821_Icons_CommunityIC-shopping-cart2170821_Icons_CommunityIC-upIC_UserIcon--imageIcon--info-i-black-48x48Icon--left-alignedIcon--Less-minimize-black-48x48Icon-FilledIcon--List-Check-grennIcon--List-Check-blackIcon--List-Cross-blackIcon--list-view-mobile-black-48x48Icon--list-view-black-48x48Icon--More-Maximize-black-48x48Icon--my-product-black-48x48Icon--newsletter-black-48x48Icon--payment-black-48x48Icon--print-black-48x48Icon--promotion-black-48x48Icon--registration-black-48x48Icon--Reset-black-48x48Icon--right-alignedshare-circle1Icon--share-black-48x48Icon--shopping-bag-black-48x48Icon-shopping-cartIcon--start-play-black-48x48Icon--store-locator-black-48x48Ic-OverlayAlertIcon--summary-black-48x48tumblrIcon-FilledvineIc-OverlayAlertwhishlist