You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于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;
}

问题分析与修复方案

核心问题

  1. 初始时间戳错误:setup()结束时将previous_sens_readout设为0,而此时currentMillis已经是预填充后的时间(比如20秒),导致进入loop()时时间差远大于2秒,直接触发多次读取。
  2. 时间更新方式错误:使用previous_sens_readout += DHT_reads_interval会累积误差,且在初始时间差过大时,会连续触发多次读取。正确做法是直接将previous_sens_readout赋值为当前的currentMillis。
  3. 标志位逻辑冲突: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;
}

关键修改点

  1. 初始化时间戳:setup()结束时,将previous_sens_readout设为当前的millis()值,避免初始时间差过大。
  2. 时间更新方式:所有更新previous_sens_readout的地方都直接赋值为currentMillis,消除累积误差,同时避免连续触发。
  3. 调整loop逻辑顺序:先判断时间间隔执行读取,再处理平均值计算,避免标志位导致的重复调用。
  4. 替换fail状态的delay:用millis()实现2秒间隔,避免阻塞程序。

内容的提问来源于stack exchange,提问作者Binzky

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.15 13:20:28