基于millis()的DHT22时序数据采集:初始循环异常问题
DHT22采集连续触发问题修复
问题描述
我正在用millis()函数从DHT22温湿度传感器采集数据,目的是避免缓冲区过载,同时适配传感器的响应速度。这个项目需要连续运行72小时来控制气阀,所以绝对不能用delay()函数。现在遇到的问题是:setup()完成缓冲区预填充后,loop()一开始会连续执行12次读取操作,之后才会按照设定的2秒间隔正常采集。我试过加额外标志位,但问题还是没解决。
原代码
#include <DHT.h> #include <Adafruit_Sensor.h> #include <CircularBuffer.h> #include <time.h> #define DHTPIN 2 #define DHTTYPE DHT22 using namespace std; const int sample_rate = 4; // 用于计算平均值的样本数量 const int temper_low = -3; // 温度下限 const int temper_high = 20;// 温度上限 float sum_temper, sum_humid, avg_temp, avg_humid = 0; // 移动平均值计算变量 int pre_fill_count = 0; // 预填充缓冲区的迭代器,到9后清零 float test = -255.0; int mins = 0; // 阀门控制计时器变量 int hours = 0; int days = 0; DHT dht(DHTPIN, DHTTYPE); // DHT对象定义 CircularBuffer<float, sample_rate> temper_buffer; // 温度数据循环缓冲区 CircularBuffer<float, sample_rate> humid_buffer; // 湿度数据循环缓冲区 const unsigned long DHT_reads_interval = 2000; // 传感器读取间隔(适配DHT响应速度) unsigned long currentMillis = 0; // 存储每次loop迭代的millis()值 unsigned long previous_sens_readout = 0; // 上次读取传感器的时间 // 传感器状态机 enum state{ preFill, measure, fail }; state sensorState = preFill; bool measurmentFlag = false; // 函数声明 void calculateAverageTemperature( const int& sample_rate, float& sum_temper, float& sum_humid, float& avg_temp, float& avg_humid ); void getSensorReadings( DHT &sensor, CircularBuffer<float, sample_rate> &temper_buffer, CircularBuffer<float, sample_rate> &humid_buffer ); float readTemperature ( state &sensorState, DHT &dht ); float readHumidity ( state &sensorState, DHT &dht ); void setup() { // 初始化,仅在单片机启动后运行一次 Serial.begin(9600); Serial.println(F("DHTxx test!")); dht.begin(); Serial.println("test value "); Serial.println(test); currentMillis = millis(); /////////////////////////////////////////////////////////////////////////// ////////////////////////////缓冲区预填充部分////////////////////////////// ///////////////////////////////////////////////////////////////////////// Serial.println("INITIALISING THE BUFFER, PLEASE WAIT... "); while(sensorState == preFill && (pre_fill_count <=9)) { // 向缓冲区推送数据,预填充约需20秒 if(currentMillis - previous_sens_readout >= DHT_reads_interval){ temper_buffer.push(readTemperature(sensorState, dht)); Serial.print(pre_fill_count); Serial.print(" it Temperature measurement from temper buffer: "); Serial.println(temper_buffer[pre_fill_count]); humid_buffer.push(readHumidity(sensorState, dht)); Serial.print(pre_fill_count); Serial.print(" it Humidity measurement from temper buffer: "); Serial.println(humid_buffer[pre_fill_count]); previous_sens_readout += DHT_reads_interval; pre_fill_count++; } currentMillis = millis(); } sensorState = measure; previous_sens_readout = 0; // 这里是错误根源之一 currentMillis = millis(); Serial.print("Program time on the exit of preFill: "); Serial.print(int(currentMillis/1000)); Serial.println(" seconds"); measurmentFlag = true; } void loop() { /////////////////////////////////////////////////////////////////////////////////// ////////////////////////////程序主状态机/////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////// Serial.println("--------------------HELLO THERE------------------------"); currentMillis = millis(); if(sensorState == measure && measurmentFlag == true){ calculateAverageTemperature( sample_rate, sum_temper, sum_humid, avg_temp, avg_humid ); if(currentMillis - previous_sens_readout >= DHT_reads_interval){ getSensorReadings(dht, temper_buffer, humid_buffer ); previous_sens_readout += DHT_reads_interval; // 时间更新方式错误 } if(sensorState == fail){ Serial.println("FAKAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP!"); delay(2000); // 这里不该用delay,后续可替换为millis实现 //TODO: 重试测量并触发故障LED提示 } } } void calculateAverageTemperature( const int& sample_rate, float& sum_temper, float& sum_humid, float& avg_temp, float& avg_humid ){ // 累加样本值 for (int k = 0; k < sample_rate; k++) { Serial.println("-----------------HERE I AM!---------------------"); sum_temper += temper_buffer[k]; sum_humid += humid_buffer[k]; } // 计算平均值 avg_temp = sum_temper / sample_rate; avg_humid = sum_humid / sample_rate; sum_temper = sum_humid = 0; measurmentFlag = false; } void getSensorReadings( DHT &sensor, CircularBuffer<float, sample_rate> &temper_buffer, CircularBuffer<float, sample_rate> &humid_buffer ){ // 获取新测量值 temper_buffer.push(readTemperature(sensorState, dht)); humid_buffer.push(readHumidity(sensorState, dht)); Serial.print(F("Avg Humidity: ")); Serial.print(avg_humid); Serial.print(F("% Temperature: ")); Serial.print(avg_temp); Serial.print(F("°C \n")); measurmentFlag = true; } // 读取温度 float readTemperature ( state &sensorState, DHT &dht ){ float t = 0; bool readed = false; for (int r = 0; r < 4; r++) { if ((!readed) && (!isnan(t = dht.readTemperature()))) { Serial.print("Temperature readed on iteration: "); Serial.println(r); readed = true; break; } else if ((r == 3) && (!readed)) { Serial.println("Something went wrong with temperature"); sensorState = fail; return -255; } } return t; } // 读取湿度 float readHumidity ( state &sensorState, DHT &dht ){ float h = 0; bool readed = false; for (int o = 0; o < 4; o++) { if (!readed && !isnan(h = dht.readHumidity())) { Serial.print("Humidity readed on iteration: "); Serial.println(o); readed = true; break; } else if ((o == 3) && (!readed)) { // 触发错误、闪烁LED、程序处理 Serial.println("Something went wrong with Humidity"); sensorState = fail; return -255; } } return h; }
问题分析与修复方案
核心问题
- 初始时间戳错误:
setup()结束时将previous_sens_readout设为0,而此时currentMillis已经是预填充后的时间(比如20秒),导致进入loop()时时间差远大于2秒,直接触发多次读取。 - 时间更新方式错误:使用
previous_sens_readout += DHT_reads_interval会累积误差,且在初始时间差过大时,会连续触发多次读取。正确做法是直接将previous_sens_readout赋值为当前的currentMillis。 - 标志位逻辑冲突:
measurmentFlag的切换导致calculateAverageTemperature被反复调用,同时时间判断逻辑在同一loop周期内被多次执行。
修复后的代码
#include <DHT.h> #include <Adafruit_Sensor.h> #include <CircularBuffer.h> #include <time.h> #define DHTPIN 2 #define DHTTYPE DHT22 using namespace std; const int sample_rate = 4; const int temper_low = -3; const int temper_high = 20; float sum_temper, sum_humid, avg_temp, avg_humid = 0; int pre_fill_count = 0; float test = -255.0; int mins = 0; int hours = 0; int days = 0; DHT dht(DHTPIN, DHTTYPE); CircularBuffer<float, sample_rate> temper_buffer; CircularBuffer<float, sample_rate> humid_buffer; const unsigned long DHT_reads_interval = 2000; unsigned long currentMillis = 0; unsigned long previous_sens_readout = 0; enum state{ preFill, measure, fail }; state sensorState = preFill; bool measurmentFlag = false; // 函数声明 void calculateAverageTemperature( const int& sample_rate, float& sum_temper, float& sum_humid, float& avg_temp, float& avg_humid ); void getSensorReadings( DHT &sensor, CircularBuffer<float, sample_rate> &temper_buffer, CircularBuffer<float, sample_rate> &humid_buffer ); float readTemperature(state &sensorState, DHT &dht); float readHumidity(state &sensorState, DHT &dht); void setup() { Serial.begin(9600); Serial.println(F("DHTxx test!")); dht.begin(); Serial.println("test value "); Serial.println(test); currentMillis = millis(); Serial.println("INITIALISING THE BUFFER, PLEASE WAIT... "); while(sensorState == preFill && pre_fill_count <=9) { currentMillis = millis(); // 每次循环更新当前时间 if(currentMillis - previous_sens_readout >= DHT_reads_interval){ temper_buffer.push(readTemperature(sensorState, dht)); Serial.print(pre_fill_count); Serial.print(" it Temperature measurement: "); Serial.println(temper_buffer[pre_fill_count]); humid_buffer.push(readHumidity(sensorState, dht)); Serial.print(pre_fill_count); Serial.print(" it Humidity measurement: "); Serial.println(humid_buffer[pre_fill_count]); previous_sens_readout = currentMillis; // 直接赋值当前时间,避免误差 pre_fill_count++; } } sensorState = measure; previous_sens_readout = millis(); // 初始化测量阶段的时间戳为当前时间 Serial.print("Program time on exit of preFill: "); Serial.print(int(millis()/1000)); Serial.println(" seconds"); measurmentFlag = true; } void loop() { currentMillis = millis(); if(sensorState == measure){ // 先处理时间间隔判断,确保每2秒仅执行一次读取 if(currentMillis - previous_sens_readout >= DHT_reads_interval){ getSensorReadings(dht, temper_buffer, humid_buffer); previous_sens_readout = currentMillis; // 更新为当前时间 } // 仅在有新数据时计算平均值 if(measurmentFlag){ calculateAverageTemperature(sample_rate, sum_temper, sum_humid, avg_temp, avg_humid); } if(sensorState == fail){ Serial.println("Sensor read failure!"); // 替换delay为millis实现,避免阻塞 static unsigned long failMillis = 0; if(currentMillis - failMillis >= 2000){ failMillis = currentMillis; // 这里可以添加重试逻辑或LED提示 } } } } void calculateAverageTemperature( const int& sample_rate, float& sum_temper, float& sum_humid, float& avg_temp, float& avg_humid ){ sum_temper = 0; sum_humid = 0; for (int k = 0; k < sample_rate; k++) { sum_temper += temper_buffer[k]; sum_humid += humid_buffer[k]; } avg_temp = sum_temper / sample_rate; avg_humid = sum_humid / sample_rate; measurmentFlag = false; } void getSensorReadings( DHT &sensor, CircularBuffer<float, sample_rate> &temper_buffer, CircularBuffer<float, sample_rate> &humid_buffer ){ temper_buffer.push(readTemperature(sensorState, dht)); humid_buffer.push(readHumidity(sensorState, dht)); Serial.print(F("Avg Humidity: ")); Serial.print(avg_humid); Serial.print(F("% Temperature: ")); Serial.print(avg_temp); Serial.println(F("°C")); measurmentFlag = true; } float readTemperature(state &sensorState, DHT &dht){ float t = 0; bool readed = false; for (int r = 0; r < 4; r++) { t = dht.readTemperature(); if (!isnan(t)) { Serial.print("Temperature read on iteration: "); Serial.println(r); readed = true; break; } } if(!readed){ Serial.println("Temperature read failed!"); sensorState = fail; return -255; } return t; } float readHumidity(state &sensorState, DHT &dht){ float h = 0; bool readed = false; for (int o = 0; o < 4; o++) { h = dht.readHumidity(); if (!isnan(h)) { Serial.print("Humidity read on iteration: "); Serial.println(o); readed = true; break; } } if(!readed){ Serial.println("Humidity read failed!"); sensorState = fail; return -255; } return h; }
关键修改点
- 初始化时间戳:
setup()结束时,将previous_sens_readout设为当前的millis()值,避免初始时间差过大。 - 时间更新方式:所有更新
previous_sens_readout的地方都直接赋值为currentMillis,消除累积误差,同时避免连续触发。 - 调整loop逻辑顺序:先判断时间间隔执行读取,再处理平均值计算,避免标志位导致的重复调用。
- 替换fail状态的delay:用
millis()实现2秒间隔,避免阻塞程序。
内容的提问来源于stack exchange,提问作者Binzky
相关产品推荐
相关产品推荐

