我使用带有Bosche BME680传感器屏蔽的WeMos D1 mini Pro。我从传感器获取数据并将它们频繁地放入Firebase数据库。一切正常,除了我的设备有随机崩溃。
传感器库的工作方式是,大约5分钟左右它没有显示IAQ数据(它返回iaq = 25,精度= 0)。 ESP8266在工作程序的第5分钟左右崩溃 - 当IAQ数据可用且已经进行了一些正确读数时。
我认为问题可能是bsec_iot_loop()
引起的,问题太长了。我试图在yield()
的随机位置使用bsec_iot_loop()
,但它不起作用。当我注释掉Firebase集合方法时,程序运行正常。
我的代码中有很大一部分是基于官方的博世文档。坦率地说,它是扩展的复制粘贴。这是文档: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
答案 0 :(得分:1)
我几乎可以肯定这是因为你的代码永远不会到达loop()
函数,在ESP8266 Arduino库上它会重置看门狗定时器每次循环函数的交互。
我认为你可以通过两种方式解决问题,一种是打开函数bsec_iot_loop()
并将调用放在while(1)
到loop()
函数中,另一种选择是调用{在ESP.wdtFeed()
内{1}}重置看门狗定时器。
以下链接对ESP Arduino库上的看门狗定时器有很好的解释。
https://techtutorialsx.com/2017/01/21/esp8266-watchdog-functions/
答案 1 :(得分:1)
好的,所以我解决了这个问题。我怀疑看门狗定时器有问题。具体来说,代码永远不会达到while(1)
函数,因为bsec_iot_loop()
位于while(1)
内。解决方案很简单。我将loop()
内的代码放入{{1}}。其余的代码bsec_iot_loop()是变量的声明,我把它们变成了全局。
答案 2 :(得分:0)
不释放系统资源是arduino库中的常见问题。特别是在具有单核CPU的系统(例如esp8266)上,该系统没有本机线程可用。底层RTOS需要处理时间来维护其TCP-Stack和其他内容。时不时地调用yield()并不是一个可靠的解决方案。
esp8266的arduino标准库主要由阻塞函数组成,这经常使程序员陷入WDT-Pitfalls。
我总是建议人们为esp8266使用异步库。有很多可用的。并且始终将控制权尽可能地返回给操作系统,因为即使是简单的delay()-Calls都能触发WDT-Reset。