Arduino实验——DHT11读取温湿度

DHT11 介绍

DHT11 数字温湿度传感器是一款含有已校准数字信号输出的温湿度复合传感器。它包括一个电阻式感湿元件和一个 NTC 测温元件。每个 DHT11 传 感器都在极为精确的湿度校验室中进行校准。校准系数以程序的形式储存在 OTP 内存中,传感器内部在检测信号的处理过程中要调用这些校准系数,用户 无需重新校准。单线制串行接口,使系统集成变得简易快捷。超小的体积、 极低的功耗,信号传输距离可达 20 米以上。

在这里插入图片描述

接口说明

管脚 信号 说明
1 VCC 外接 3.3 - 5V
2 GND 外接 GND
3 DO 数字量输出接口,接单片机 IO 口

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GemGbz1i-1575631984278)(./images/DHT11模块3.jpg)]

电气参数

  • 电源电压:3~5.5V(典型值:5V);
  • 温度量程:0~50℃,误差 ±2℃;
  • 湿度量程:20~95%RH,误差 ±5%RH;
  • 采样周期:大于等于1秒/次。

硬件连接

  • 数据总线 DATA 使用上拉电阻拉高,因此总线空闲时为高电平。上拉电阻阻值推荐范围:4.7K~5.1K。
  • 必要时在 VDD 和 GND 之间并一个 100nF 的去耦电容。

在这里插入图片描述

本实验将传感器的 DATA 引脚连接到 Arduino 的 D2 口。

工作原理

DHT11 使用单一总线通信,即 DATA 引脚和单片机连接的线。总线总是处于 空闲状态通信状态 这个2个状态之间。当单片机没有与 DHT11 交互时,总线处于空闲状态,在上拉电阻的作用下,处于高电平状态。当单片机和 DHT11 正在通信时,总线处于通信状态,一次完整的通信过程如下:

  1. 单片机将驱动总线的 IO 配置为输出模式。准备向 DHT11 发送数据。

  2. 单片机将总线拉低至少 18ms,以此来发送起始信号;再将总线拉高并延时 20~40us,以此来代表起始信号结束。

  3. 单片机将驱动总线的 IO 配置为输入模式,准备接收 DHT11 回传的数据。

  4. 当 DHT11 检测到单片机发送的起始信号后,就开始应答,回传采集到的传感器数据。DHT11 先将总线拉低 80us 作为对单片机的应答(ACK),然后接着将总线拉高 80us,准备回传采集到的温湿度数据。温湿度数据以固定的帧格式发送,具体格式如下图:

    在这里插入图片描述

  5. 当一帧数据传输完成后,DHT11 释放总线,总线在上拉电阻的作用下再次恢复到高电平状态。

    在这里插入图片描述

可以发现一帧为 40个bit,而每一个 bit 的传输时序逻辑为:每一个 bit 都以 50us 的低电平(DHT11 将总线拉低)为先导,然后紧接着 DHT11 拉高总线,如果这个高电平持续时间为 26~28us,则代表逻辑 0,如果持续 70us 则代表逻辑 1。

注意事项

  1. DHT11 上电后,要等待 1 秒以越过不稳定状态,在此期间不能发送任何指令。
  2. DHT11 属于低速传感器,两次通信请求之间的间隔时间不能太短,一般来说要不能低于1秒。
  3. 当前 DHT11 通信帧的小数部分默认都是 0,厂商预留给以后实现,所以一般只读取整数值部分即可。
  4. 校验和定义为:前 4 个 Byte 的总和的低 8 位。

Arduino Test

#include "dht11.h"

#define DHT_PIN 2

DHT11 dht11(DHT_PIN);

void setup()
{
    Serial.begin(9600);
    Serial.println("DHT11 TEST PROGRAM ");
    Serial.print("LIBRARY VERSION: ");
    Serial.println(DHT11LIB_VERSION);
    Serial.println();
}

void loop()
{
    // Wait a few seconds between measurements.
    delay(2000);

    int ret = dht11.read();
    if(ret == DHTLIB_ERROR_CHECKSUM) {
        Serial.println("(E) Checksum failed");
        return;
    }
    else if(ret == DHTLIB_ERROR_TIMEOUT) {
        Serial.println("(E) Read time out");
        return;
    }

    Serial.print("Humidity: ");
    Serial.print((float)dht11.getHumidity(), 2);
    Serial.print("%  Temperature: ");
    Serial.print((float)dht11.getTemperature(), 2);
    Serial.print("'C  ");
    Serial.print((float)dht11.getTemperature(true), 2);
    Serial.println("'F");
}

运行效果:

在这里插入图片描述

DHT11 类

这里使用了我自定义的类 DHT11,这个类在头文件 dht11.h 中,内容如下:

 #ifndef dht11_h
#define dht11_h

#if defined(ARDUINO) && (ARDUINO >= 100)
#include <Arduino.h>
#else
#include <WProgram.h>
#endif

#define DHT11LIB_VERSION "0.1.0"

#define DHTLIB_OK				 0
#define DHTLIB_ERROR_CHECKSUM	-1
#define DHTLIB_ERROR_TIMEOUT	-2

#define BEGIN_TIME       20  // ms
#define GO_TIME          30  // us
#define REPLY_LOW_TIME   83  // us
#define REPLY_HIGH_TIME  87  // us
#define DATA_LOW_TIME    54  // us
#define DATA_HIGH_0_TIME 24  // us
#define DATA_HIGH_1_TIME 71  // us
#define RELEASE_TIME     54  // us

#define THINK_TIME       40  // us

class DHT11
{
public:
	DHT11(uint8_t pin);

    uint8_t read(uint8_t usec=55);
	float   getHumidity();
	float   getTemperature(bool isFahrenheit=false);
	float   convertCtoK(float c);
	float   convertCtoF(float c);
	float   convertFtoC(float f);
	double  dewPoint(double celsius, double humidity);
	double  dewPointFast(double celsius, double humidity);
	float   computeHeatIndex(float temperature, float percentHumidity, bool isFahrenheit=true);

private:
  uint8_t data[5];
  uint8_t _pin;

  void resetBuffer(uint8_t *buf, uint8_t size);
  uint8_t checksum();
};
#endif

函数实现在 dht11.c 中,在构造函数 DHT11() 中初始化引脚,在 read() 函数中实现与 dht11 传感器的通信并获取原始温湿度值,通过 getHumidity()getTemperature() 函数获取当前温湿度值。DHT11 类中同时提供了一些温湿度转换的功能函数,有需要的小伙伴可以测试一下。整个 dht11.c 文件的内容如下。

#include "dht11.h"

/*!
 *  @brief  Instantiates a new DHT class
 *  @param  pin
 *          pin number that sensor is connected
 */
DHT11::DHT11(uint8_t pin) 
{
  _pin = pin;
  pinMode(_pin, INPUT_PULLUP);

}

/*!
 *  @brief  Reset the specific buffer
 *  @param  buf
 *          buffer that you want to reset
 *  @param  size
 *          how many bytes you want to reset
 */
void DHT11::resetBuffer(uint8_t *buf, uint8_t size)
{
	for (int i=0; i<size; i++) {
		buf[i] = 0;
	}
}

/*!
 *  @brief  Read value from sensor and update data array.
 *  @param  usec
 *          Optionally pass pull-up time (in microseconds) before DHT reading
 *          starts. Default is 55 (see function declaration in dht.h).
 *	@return If read data successfully return DHTLIB_OK,
 *          if the data are not complete return DHTLIB_ERROR_CHECKSUM,
 *          if read timeout return DHTLIB_ERROR_TIMEOUT.
 */
uint8_t DHT11::read(uint8_t usec)
{
	uint8_t bit = 7, index = 0;
	uint32_t t1 = 0, t2 = 0, delta = 0;

	// Reset data buffer
	resetBuffer(data, sizeof(data)/sizeof(data[0]));

	// MCU request sampling
	pinMode(_pin, OUTPUT);
	digitalWrite(_pin, LOW);
	delay(BEGIN_TIME);

	digitalWrite(_pin, HIGH);
	delayMicroseconds(GO_TIME);
	pinMode(_pin, INPUT);

	// Waiting for DHT11 sensor reply
	// ACKNOWLEDGE or TIMEOUT
	unsigned int loopCnt = 10000;
	while(digitalRead(_pin) == LOW)
		if (loopCnt-- == 0) return DHTLIB_ERROR_TIMEOUT;

	loopCnt = 10000;
	while(digitalRead(_pin) == HIGH)
		if (loopCnt-- == 0) return DHTLIB_ERROR_TIMEOUT;

	// READ OUTPUT - 40 BITS => 5 BYTES or TIMEOUT
	for (int i=0; i<40; i++)
	{
		loopCnt = 10000;
		while(digitalRead(_pin) == LOW)
			if (loopCnt-- == 0) return DHTLIB_ERROR_TIMEOUT;

		t1 = micros();

		loopCnt = 10000;
		while(digitalRead(_pin) == HIGH)
			if (loopCnt-- == 0) return DHTLIB_ERROR_TIMEOUT;

		t2 = micros();
		if(t2 < t1) delta = UINT32_MAX - t1 + t2;
		else delta = t2 - t1;

		if(delta > THINK_TIME) {
			data[index] |= (1 << bit);
		}

		// Next byte or not
		if (bit == 0) {
			bit = 7;
			index++;
		}
		else bit--;
	}

	if (data[4] != checksum()) {
		return DHTLIB_ERROR_CHECKSUM;
	}

	return DHTLIB_OK;
}

/*!
 *  @brief  Calculate checksum
 *	@return The last 8 bit of checksum value
 */
uint8_t DHT11::checksum()
{
	uint16_t sum = 0;

	for(int i=0; i<sizeof(data)/sizeof(data[0]); i++) {
		sum += data[i];
	}
	return (uint8_t)sum;
}

/*!
 *  @brief  Read Humidity
 *	@return float value - humidity in percent
 */
float DHT11::getHumidity()
{
	float f = data[0] + data[1] * 0.1;
	return f;
}

/*!
 *  @brief  Get temperature
 *  @param  isFahrenheit
 *          If or not scale in Fahrenheit (default in Celcius)
 *	@return Temperature value in selected scale
 */
float DHT11::getTemperature(bool isFahrenheit)
{
	float f = data[2];
    if (data[3] & 0x80) {
      f = -1 - f;
    }
    f += (data[3] & 0x0f) * 0.1;

    if (isFahrenheit) {
      f = convertCtoF(f);
    }
    return f;
}

/*!
 *  @brief  Converts Celcius to Fahrenheit
 *  @param  c
 *			value in Celcius
 *	@return float value in Fahrenheit
 */
float DHT11::convertCtoF(float c) 
{ 
	return c * 1.8 + 32; 
}

/*!
 *  @brief  Converts Fahrenheit to Celcius
 *  @param  f
 *			value in Fahrenheit
 *	@return float value in Celcius
 */
float DHT11::convertFtoC(float f) 
{ 
	return (f - 32) * 0.55555; 
}

/*!
 *  @brief  Converts Celcius to Kelvin
 *  @param  c
 *			value in Celcius
 *	@return float value in Kelvin
 */
float DHT11::convertCtoK(float c)
{
        return c + 273.15;
}

/*!
 *  @brief  Calculate the dew point
 *  @param  celsius
 *          temperature value in Celcius
 *  @param  humidity
 *          humidity value in percent
 */
double DHT11::dewPoint(double celsius, double humidity)
{
        double A0= 373.15/(273.15 + celsius);
        double SUM = -7.90298 * (A0-1);
        SUM += 5.02808 * log10(A0);
        SUM += -1.3816e-7 * (pow(10, (11.344*(1-1/A0)))-1);
        SUM += 8.1328e-3 * (pow(10,(-3.49149*(A0-1)))-1);
        SUM += log10(1013.246);
        double VP = pow(10, SUM-3) * humidity;
        double T = log(VP/0.61078);   // temp var
        return (241.88 * T) / (17.558-T);
}

/*!
 *  @brief  Calculate the dew point fast
 *  @param  celsius
 *          temperature value in Celcius
 *  @param  humidity
 *          humidity value in percent
 */
double DHT11::dewPointFast(double celsius, double humidity)
{
        double a = 17.271;
        double b = 237.7;
        double temp = (a * celsius) / (b + celsius) + log(humidity/100);
        double Td = (b * temp) / (a - temp);
        return Td;
}

/*!
 *  @brief  Compute Heat Index
 *  				Using both Rothfusz and Steadman's equations
 *					(http://www.wpc.ncep.noaa.gov/html/heatindex_equation.shtml)
 *  @param  temperature
 *          temperature in selected scale
 *  @param  percentHumidity
 *          humidity in percent
 *  @param  isFahrenheit
 * 					true if fahrenheit, false if celcius
 *	@return float heat index
 */
float DHT11::computeHeatIndex(float temperature, float percentHumidity,
                              bool isFahrenheit) {
  float hi;

  if (!isFahrenheit)
    temperature = convertCtoF(temperature);

  hi = 0.5 * (temperature + 61.0 + ((temperature - 68.0) * 1.2) +
              (percentHumidity * 0.094));

  if (hi > 79) {
    hi = -42.379 + 2.04901523 * temperature + 10.14333127 * percentHumidity +
         -0.22475541 * temperature * percentHumidity +
         -0.00683783 * pow(temperature, 2) +
         -0.05481717 * pow(percentHumidity, 2) +
         0.00122874 * pow(temperature, 2) * percentHumidity +
         0.00085282 * temperature * pow(percentHumidity, 2) +
         -0.00000199 * pow(temperature, 2) * pow(percentHumidity, 2);

    if ((percentHumidity < 13) && (temperature >= 80.0) &&
        (temperature <= 112.0))
      hi -= ((13.0 - percentHumidity) * 0.25) *
            sqrt((17.0 - abs(temperature - 95.0)) * 0.05882);

    else if ((percentHumidity > 85.0) && (temperature >= 80.0) &&
             (temperature <= 87.0))
      hi += ((percentHumidity - 85.0) * 0.1) * ((87.0 - temperature) * 0.2);
  }

  return isFahrenheit ? hi : convertFtoC(hi);
}
发布了299 篇原创文章 · 获赞 1219 · 访问量 159万+

猜你喜欢

转载自blog.csdn.net/luckydarcy/article/details/103396014