代码之家  ›  专栏  ›  技术社区  ›  emil

ESP8266随机软WDT重置

  •  0
  • emil  · 技术社区  · 7 年前

    我使用WeMos D1 mini Pro和Bosche BME680传感器屏蔽。我从传感器获取数据,并经常将其放入Firebase数据库。一切正常,除了我的设备随机崩溃。

    传感器库的工作方式是,在前5分钟左右,它不会显示室内空气质量数据(返回的是室内空气质量=25,精度=0)。ESP8266在工作程序的第5分钟左右崩溃,此时室内空气质量数据可用,并且一些读数已经正确。

    我认为问题可能是由 bsec_iot_loop() 工作时间太长了。我想用 yield() 在以下任意位置 bsec\U iot\U loop() ,但它不起作用。当我注释掉Firebase set方法时,这个程序运行得很好。

    我的大部分代码都基于博世的官方文档。坦率地说,这是扩展的复制粘贴。以下是文档: https://www.bosch-sensortec.com/bst/products/all_products/bsec

    代码如下:

    /**********************************************************************************************************************/
    /* 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()
    {
    }
    

    下面是典型串行监视器转储的开始:

    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 回复  |  直到 7 年前
        1
  •  1
  •   gre_gor    7 年前

    我几乎可以肯定,这是因为您的代码从未达到 loop() 函数,在ESP8266 Arduino库上,它在循环函数的每次交互中重置看门狗计时器。

    我认为您可以通过两种方式解决问题,一种是打开函数 bsec_iot_loop() 把电话放在 while(1) 循环() 函数,另一个选项是调用 ESP.wdtFeed() 内部 while(1) 重置看门狗计时器。

    下面的链接很好地解释了ESP Arduino库上的看门狗计时器。

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

        2
  •  0
  •   emil    7 年前

    好吧,我解决了这个问题。我怀疑看门狗计时器有问题。具体来说,代码永远达不到 loop() 函数,因为 while(1) 在…内 bsec_iot_loop() . 解决方案很简单。我把代码放进去了 进入 循环() . 代码bsec\u iot\u loop()的其余部分是变量声明,我将它们设置为全局变量。

        3
  •  0
  •   Niels Hüsken    5 年前

    不释放系统资源是arduino库中的常见问题。尤其是在具有单核CPU(如esp8266)的系统上,系统没有可用的本机线程。底层RTO需要处理时间来维护其TCP堆栈和其他内容。偶尔调用yield()并不是一个可靠的解决方案。

    esp8266的arduino标准库主要由阻塞函数组成,这通常会导致程序员陷入WDT陷阱。

    我总是建议人们为esp8266使用异步库。它们有很多。并且始终尽可能将控制权返回操作系统,因为即使是简单的delay()-调用也能够触发WDT重置。