DHT11 介绍

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

Arduino实验——DHT11读取温湿度_Arduino

接口说明

管脚

信号

说明

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 的去耦电容。

Arduino实验——DHT11读取温湿度_温湿度_02

本实验将传感器的 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. Arduino实验——DHT11读取温湿度_DHT22_03

  6. 当一帧数据传输完成后,DHT11 释放总线,总线在上拉电阻的作用下再次恢复到高电平状态。
  7. Arduino实验——DHT11读取温湿度_温湿度_04

可以发现一帧为 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");
}

运行效果:

Arduino实验——DHT11读取温湿度_温湿度_05

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);
}