0
votes

I use WeMos D1 mini Pro with Bosche BME680 sensor shield. I get the data from sensor and put them frequently into Firebase database. Everything works, except that my device has random crashes.

The sensor library works in a way that for around 5 first minutes it is not showing IAQ data (it's returning iaq = 25 and accuracy = 0). ESP8266 crashes around 5th minute of working program - when IAQ data is available and some of correct reading had been made.

I think problem might be caused by bsec_iot_loop() which is working too long. I was trying to use yield() in random places of bsec_iot_loop(), but it didn't work. Program works perfectly, when I comment out Firebase set methods.

Huge part of my code is based on official Bosch documentation. Frankly speaking, it's extended copy-paste. Here's the docs: https://www.bosch-sensortec.com/bst/products/all_products/bsec

Here's the code:

/**********************************************************************************************************************/
/* header files */
/**********************************************************************************************************************/

#include "bsec_integration.h"
#include <Wire.h>
#include <Arduino.h>
#include <ESP8266WiFi.h>
#include <ESP8266HTTPClient.h>
#include <FirebaseArduino.h>
#include <time.h>

#define DEVICE_NAME "device1"

#define SSID "ssid"
#define PWD "pass"

#define FIREBASE_HOST "host"
#define FIREBASE_AUTH "auth"

#define UPDATE_INTERVAL 20

int startupTime;

/**********************************************************************************************************************/
/* functions */
/**********************************************************************************************************************/

/*!
 * @brief           Write operation in either Wire or SPI
 *
 * param[in]        dev_addr        Wire or SPI device address
 * param[in]        reg_addr        register address
 * param[in]        reg_data_ptr    pointer to the data to be written
 * param[in]        data_len        number of bytes to be written
 *
 * @return          result of the bus communication function
 */
int8_t bus_write(uint8_t dev_addr, uint8_t reg_addr, uint8_t *reg_data_ptr, uint16_t data_len)
{
    Wire.beginTransmission((uint8_t) 0x77);
    Wire.write(reg_addr);    /* Set register address to start writing to */

    /* Write the data */
    for (int index = 0; index < data_len; index++) {
        Wire.write(reg_data_ptr[index]);
    }

    return (int8_t)Wire.endTransmission();
}

/*!
 * @brief           Read operation in either Wire or SPI
 *
 * param[in]        dev_addr        Wire or SPI device address
 * param[in]        reg_addr        register address
 * param[out]       reg_data_ptr    pointer to the memory to be used to store the read data
 * param[in]        data_len        number of bytes to be read
 *
 * @return          result of the bus communication function
 */
int8_t bus_read(uint8_t dev_addr, uint8_t reg_addr, uint8_t *reg_data_ptr, uint16_t data_len)
{
    int8_t comResult = 0;
    Wire.beginTransmission((uint8_t) 0x77);
    Wire.write(reg_addr);                    /* Set register address to start reading from */
    comResult = Wire.endTransmission();

    delayMicroseconds(150);                 /* Precautionary response delay */
    Wire.requestFrom((uint8_t) 0x77, (uint8_t)data_len);    /* Request data */

    int index = 0;
    while (Wire.available())  /* The slave device may send less than requested (burst read) */
    {
        reg_data_ptr[index] = Wire.read();
        index++;
    }

    return comResult;
}

/*!
 * @brief           System specific implementation of sleep function
 *
 * @param[in]       t_ms    time in milliseconds
 *
 * @return          none
 */
void sleep(uint32_t t_ms)
{
    delay(t_ms);
}

/*!
 * @brief           Capture the system time in microseconds
 *
 * @return          system_current_time    current system timestamp in microseconds
 */
int64_t get_timestamp_us()
{
    return (int64_t) millis() * 1000;
}

/*!
 * @brief           Load previous library state from non-volatile memory
 *
 * @param[in,out]   state_buffer    buffer to hold the loaded state string
 * @param[in]       n_buffer        size of the allocated state buffer
 *
 * @return          number of bytes copied to state_buffer
 */
uint32_t state_load(uint8_t *state_buffer, uint32_t n_buffer)
{
    // ...
    // Load a previous library state from non-volatile memory, if available.
    //
    // Return zero if loading was unsuccessful or no state was available,
    // otherwise return length of loaded state string.
    // ...
    return 0;
}

/*!
 * @brief           Save library state to non-volatile memory
 *
 * @param[in]       state_buffer    buffer holding the state to be stored
 * @param[in]       length          length of the state string to be stored
 *
 * @return          none
 */
void state_save(const uint8_t *state_buffer, uint32_t length)
{
    // ...
    // Save the string some form of non-volatile memory, if possible.
    // ...
}

/*!
 * @brief           Load library config from non-volatile memory
 *
 * @param[in,out]   config_buffer    buffer to hold the loaded state string
 * @param[in]       n_buffer        size of the allocated state buffer
 *
 * @return          number of bytes copied to config_buffer
 */
uint32_t config_load(uint8_t *config_buffer, uint32_t n_buffer)
{
    // ...
    // Load a library config from non-volatile memory, if available.
    //
    // Return zero if loading was unsuccessful or no config was available,
    // otherwise return length of loaded config string.
    // ...
    return 0;
}

void connectToWiFi() {
  Serial.print("Connecting to ");
  Serial.println(SSID);

  WiFi.begin(SSID, PWD);

  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }

  Serial.println("");
  Serial.println("WiFi connected");
  Serial.println("IP address: ");
  Serial.println(WiFi.localIP());
}

void configureFirebase() {
  Serial.print("Connecting to ");
  Serial.println(FIREBASE_HOST);
  Serial.println("");

  Firebase.begin(FIREBASE_HOST, FIREBASE_AUTH);

  delay(500);
}

void configureTime() {
  configTime(0, 0, "pool.ntp.org", "time.nist.gov");
  Serial.println("\nWaiting for time");
  while (!time(nullptr)) {
    Serial.print(".");
    delay(1000);
  }
  Serial.println("");
}

void configureSensor() {
  return_values_init ret;

  /* Init I2C and serial communication */
  Wire.begin();

  /* Call to the function which initializes the BSEC library
   * Switch on low-power mode and provide no temperature offset */
  ret = bsec_iot_init(BSEC_SAMPLE_RATE_LP, 5.0f, bus_write, bus_read, sleep, state_load, config_load);
  if (ret.bme680_status)
  {
      /* Could not intialize BME680 */
      Serial.println("Error while initializing BME680");
      return;
  }
  else if (ret.bsec_status)
  {
      /* Could not intialize BSEC library */
      Serial.println("Error while initializing BSEC library");
      return;
  }

  Serial.println("Sensor success");
}

/*!
 * @brief           Handling of the ready outputs
 *
 * @param[in]       timestamp       time in nanoseconds
 * @param[in]       iaq             IAQ signal
 * @param[in]       iaq_accuracy    accuracy of IAQ signal
 * @param[in]       temperature     temperature signal
 * @param[in]       humidity        humidity signal
 * @param[in]       pressure        pressure signal
 * @param[in]       raw_temperature raw temperature signal
 * @param[in]       raw_humidity    raw humidity signal
 * @param[in]       gas             raw gas sensor signal
 * @param[in]       bsec_status     value returned by the bsec_do_steps() call
 *
 * @return          none
 */
void output_ready(int64_t timestamp, float iaq, uint8_t iaq_accuracy, float temperature, float humidity,
     float pressure, float raw_temperature, float raw_humidity, float gas, bsec_library_return_t bsec_status)
{
    yield();
    char startupTimeStr[32];
    itoa(startupTime, startupTimeStr, 10);
    //Get current time
    time_t now = time(nullptr);

    //Get last update time
    int lastUpdate = Firebase.getInt("device1/lastUpdate");
    if (Firebase.failed()) {
      Serial.print("getting device1/lastUpdate failed:");
      Serial.println(Firebase.error());
      return;
    }

    if (lastUpdate + UPDATE_INTERVAL <= (int) now) {
      //Set last update
      Firebase.setInt("device1/lastUpdate", (int) now);

      //Set the reading
      char nowStr[32];
      itoa(now, nowStr, 10);
      String path = "device1/readings/" + String(nowStr);
      // Firebase.setInt(path + "/iaq", iaq);
      // Firebase.setFloat(path + "/temp", temperature);
      // Firebase.setFloat(path + "/humid", humidity);
      // Firebase.setFloat(path + "/press", pressure);

      //Set uptime
      int uptime = (int) now - startupTime;
      //Firebase.setInt("device1/uptimes/" + String(startupTimeStr), uptime);

      //Verbose data
      Serial.print("Updated: ");
      Serial.print((int) now);
      Serial.print(" | Uptime: ");
      Serial.print(uptime);
      Serial.print(" | IAQ: ");
      Serial.print(iaq);
      Serial.print(" | Acc: ");
      Serial.println(iaq_accuracy);
    }
}

void setup()
{
    Serial.begin(9600);
    while (!Serial);

    connectToWiFi();
    configureFirebase();
    configureTime();
    configureSensor();

    startupTime = (int) time(nullptr);
    Serial.print("Startup time:");
    Serial.println(startupTime);

    /* Call to endless loop function which reads and processes data based on sensor settings */
    /* State is saved every 10.000 samples, which means every 10.000 * 3 secs = 500 minutes  */
    bsec_iot_loop(sleep, get_timestamp_us, output_ready, state_save, 10000);
}

void loop()
{
}

And here's beginning of typical Serial monitor dump:

Soft WDT reset

ctx: cont
sp: 3fff0df0 end: 40101b51 offset: 01b0


>>>stack>>>
3fff0fa0:  3fff31f4 3fff70ec 3fff662c 3fff372c
3fff0fb0:  0002d5a7 3fff70ec 3fff662c 40208866
3fff0fc0:  3fff662c 00000000 3fff703c 40201952
3fff0fd0:  3fff703c 00001388 3fff3b04 3fff0680
3fff0fe0:  000001bb 3fff662c 3fff31f4 3fff0680
3fff0ff0:  000001bb 3fff662c 3fff31f4 402089fd
3fff1000:  3ffe9770 5561c923 3ffe9770 5561c923
3fff1010:  3fff367c 00000000 3fff3684 4020717c
3fff1020:  00000000 00000206 00000206 4020526c
3fff1030:  fffffff4 00000000 3fff3684 40207980
3fff1040:  3ffe9584 00000046 3ffe96a9 40201ff3
3fff1050:  3fff36fc 3fff10c0 3fff367c 4020204c
3fff1060:  3fff3708 00000000 00000000 3ffe96a6
3fff1070:  3fff367c 3fff10a0 3ffe96a6 3ffe96a6
3fff1080:  3fff367c 3fff0680 3fff1188 402030fa
3fff1090:  3fff0660 3fff0680 3fff1188 40203b71
3fff10a0:  3fff3708 3e9b1316 3e9b1316 3d003b33
3fff10b0:  41ad99fb bf64685f 00000000 40212b8c
3fff10c0:  3fff39d0 af3cd700 0000d700 00000012
3fff10d0:  00000000 3fff11ac 3fff1198 3fff065c
3fff10e0:  3fff1128 3fff1128 402030e4 40202032
3fff10f0:  3fff112c 40590000 3ed1ca3e 3fff0660
3fff1100:  3fff11ac 3fff1120 3fff1188 40203e0a
3fff1110:  3fff1120 40fb6e3e 3fff2180 40219384
3fff1120:  3fff367c 3fff3944 3fff0680 41ad99fb
3
Why is the code duplicated? - gre_gor
Just double copied snippet, corrected - emil
Have you tired something simple to rule out a hardware problem, one of the first thing I always try is to run the i2c scanner. playground.arduino.cc/Main/I2cScanner - nPn
Hardware works perfect 😞 IMO it's a watchdog issue. I have no idea how to solve it though. - emil

3 Answers

1
votes

I am almost sure that this is happening because your code never reaches the loop() function, on ESP8266 Arduino library it resets the watchdog timer each interaction of loop function.

I think you can solve your problem by two ways, one is opening the function bsec_iot_loop() and put the calls inside the while(1) to loop() function, the other option is to call ESP.wdtFeed() inside the while(1) to reset watchdog Timer.

The link bellow has a good explanation about watchdog timer on ESP Arduino library.

https://techtutorialsx.com/2017/01/21/esp8266-watchdog-functions/

0
votes

Okay, so I solved the problem. As I suspected there was a problem with watchdog timer. Specifically, the code never reach loop() function, because while(1) inside bsec_iot_loop(). Solution is simple. I put code inside while(1) into loop(). The rest of the code bsec_iot_loop() was declaration of variables and I made them global.

0
votes

Not releasing system resources is a common problem in the arduino library. Especially on systems with a single core CPU like esp8266 where no native threading is available to the system. The underlying RTOS needs processing time to maintain its TCP-Stack and other stuff. Calling yield() every now and then is not a reliable solution.

The arduino standard library for the esp8266 mostly consists of blocking functions, which often leads the programmer into WDT-Pitfalls.

I always recommend people to use async libraries for the esp8266. There are plenty of them available. And always return control back to the operating system as much as possible because even simple delay()-Calls are capable of triggering WDT-Reset.