产品概述

DHT11数字温湿度传感器是一款含有已校准数字信号输出的温湿度 复合传感器。应用专用的数字模块采集技术和温湿度传感技术,可以将获取到的环境温湿度信号通过单总线传输;信号中携带有校验码,一定程度上保证了数据的准确性。

性能指标

参数最小最大
温度范围-20℃60℃
湿度范围5%RH95%RH
电压3.3V5.5V
电流0.06mA1.0mA
参数精确度
温度±2℃
湿度±5%RH

引脚说明:

  1. VDD 供电3.3~5.5V DC
  2. DATA 串行数据,单总线
  3. NC 空脚
  4. GND 接地,电源负极

工作原理

数据时序图

image-20220318152411531

  • 起始信号:微处理器把数据总线(SDA)拉低一段时间至少 18ms(最大不得超过 30ms),在拉高20us-40us,通知传感器准备数据。
  • 响应信号:传感器把数据总线(SDA)拉低 83µs,再拉高 87µs 以响应主机的起始信号。
  • 数据格式:收到主机起始信号后,传感器一次性从数据总线(SDA)串出 40 位数据,高位先出

    image-20221126205849766

    image-20221126205921333

    当检测到这一位数据的高电平后,我们要看这段高电平究竟会持续多长时间,0信号的高电平持续26-28us,1信号则持续70us。所以在上一步检测到高电平之后我们不妨等一等,等个30us看看高电平是不是还是存在,若还是高电平,那这一位信号就是1信号,若已经变低了,就是0信号,将这个1或0赋值给一个变量。

  • 温度:温度高位为温度整数部分数据,温度低位为温度小数部分数据,且温度低位 Bit8 为 1 则表示负温度,否则 为正温度
  • 湿度:湿度高位为湿度整数部分数据,湿度低位为湿度小数部分数
  • 校验位:校验位=湿度高位+湿度低位+温度高位+温度低位

以上内容来自奥松电子DHT11官方文档,详细内容请查看文档

代码实例

树莓派Python3代码实例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
#!/usr/bin/python3  coding=utf-8
import RPi.GPIO as GPIO
import time

data = 18 # DHT11的data引脚连接到的树莓派的GPIO引脚,使用BCM编号
tmp=[] # 用来存放读取到的数据
a,b=0,0

def delayMicrosecond(t): # 微秒级延时函数
start,end=0,0 # 声明变量
start=time.time() # 记录开始时间
t=(t-3)/1000000 # 将输入t的单位转换为秒,-3是时间补偿
while end-start<t: # 循环至时间差值大于或等于设定值时
end=time.time() # 记录结束时间

def DHT11():
GPIO.setmode(GPIO.BCM) # 设置为BCM编号模式
GPIO.setwarnings(False)
del tmp[0:] # 删除列表
time.sleep(1) # 延时1秒
GPIO.setup(data, GPIO.OUT) # 设置GPIO口为输出模式
#GPIO.output(data,GPIO.HIGH) # 设置GPIO输出高电平
#delayMicrosecond(10*1000) # 延时10毫秒
GPIO.output(data,GPIO.LOW) # 设置GPIO输出低电平
delayMicrosecond(25*1000) # 延时25毫秒
GPIO.output(data,GPIO.HIGH) # 设置GPIO输出高电平
GPIO.setup(data, GPIO.IN) # 设置GPIO口为输入模式
a=time.time() # 记录循环开始时间
while GPIO.input(data): # 一直循环至输入为低电平
b=time.time() # 记录结束时间
if (b-a)>0.1: # 判断循环时间是否超过0.1秒,避免程序进入死循环卡死
break # 跳出循环

a=time.time()
while GPIO.input(data)==0: # 一直循环至输入为高电平
b=time.time()
if (b-a)>0.1:
break

a=time.time()
while GPIO.input(data): # 一直循环至输入为低电平
b=time.time()
if (b-a)>=0.1:
break

for i in range(40): # 循环40次,接收温湿度数据
a=time.time()
while GPIO.input(data)==0: #一直循环至输入为高电平
b=time.time()
if (b-a)>0.1:
break

delayMicrosecond(28) # 延时28微秒

if GPIO.input(data): # 超过28微秒后判断是否还处于高电平
tmp.append(1) # 记录接收到的bit为1

a=time.time()
while GPIO.input(data): # 一直循环至输入为低电平
b=time.time()
if (b-a)>0.1:
break
else:
tmp.append(0) # 记录接收到的bit为0

humidity_bit=tmp[0:8] # 分隔列表,第0到7位是湿度整数数据
humidity_point_bit=tmp[8:16]# 湿度小数
temperature_bit=tmp[16:24] # 温度整数
temperature_point_bit=tmp[24:32] # 温度小数
check_bit=tmp[32:40] # 校验数据

humidity_int=0
humidity_point=0
temperature_int=0
temperature_point=0
check=0

for i in range(8): # 二进制转换为十进制
humidity_int+=humidity_bit[i]*2**(7-i)
humidity_point+=humidity_point_bit[i]*2**(7-i)
temperature_int+=temperature_bit[i]*2**(7-i)
temperature_point+=temperature_point_bit[i]*2**(7-i)
check+=check_bit[i]*2**(7-i)

humidity=humidity_int+humidity_point/10
temperature=temperature_int+temperature_point/10

check_tmp=humidity_int+humidity_point+temperature_int+temperature_point

if check==check_tmp and temperature!=0 and temperature!=0: # 判断数据是否正常
print("Temperature is ", temperature,"℃\nHumidity is ",humidity,"%")# 打印温湿度数据
else:
print("error")
while True:
DHT11()
GPIO.cleanup()
time.sleep(5)

树莓派C语言代码实例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
//
//mydht11.c
//gcc dht11.c -o dht11 -lwiringPi
//代码非原创,已经找不到原作者了,感谢大佬
//
#include <wiringPi.h>
#include <stdio.h>
#include <stdlib.h>

typedef unsigned char uint8;
typedef unsigned int uint16;
typedef unsigned long uint32;

#define HIGH_TIME 32

int pinNumber = 1; //use gpio1 to read data
uint32 databuf;

uint8 readSensorData(void)
{
uint8 crc;
uint8 i;

pinMode(pinNumber,OUTPUT); // set mode to output
digitalWrite(pinNumber, 1); // output a low level
delayMicroseconds(4);
digitalWrite(pinNumber, 0); // output a high level
delay(25);
digitalWrite(pinNumber, 1); // output a low level
delayMicroseconds(60);
pinMode(pinNumber, INPUT); // set mode to input
pullUpDnControl(pinNumber,PUD_UP);

if(digitalRead(pinNumber)==0) //SENSOR ANS
{
while(!digitalRead(pinNumber)); //wait to high
delayMicroseconds(80);
for(i=0;i<32;i++)
{
while(digitalRead(pinNumber)); //data clock start
while(!digitalRead(pinNumber)); //data start
delayMicroseconds(HIGH_TIME);
databuf*=2;
if(digitalRead(pinNumber)==1) //1
{
databuf++;
}
}

for(i=0;i<8;i++)
{
while(digitalRead(pinNumber)); //data clock start
while(!digitalRead(pinNumber)); //data start
delayMicroseconds(HIGH_TIME);
crc*=2;
if(digitalRead(pinNumber)==1) //1
{
crc++;
}
}
return 1;
}
else
{
return 0;
}
}

int main (void)
{
if (-1 == wiringPiSetup()) {
//printf("Setup wiringPi failed!");
return 1;
}

pinMode(pinNumber, OUTPUT); // set mode to output
digitalWrite(pinNumber, 1); // output a high level

//while(1)
//{
pinMode(pinNumber,OUTPUT); // set mode to output
digitalWrite(pinNumber, 1); // output a high level
//delay(3000);
if(readSensorData())
{
printf("OK!\n");
printf("RH:%d.%d\n",(databuf>>24)&0xff,(databuf>>16)&0xff);
printf("TMP:%d.%d\n",(databuf>>8)&0xff,databuf&0xff);
databuf=0;
}
else
{
printf("Error!\n");
databuf=0;
}
//}
return 0;
}

ESP32C语言代码实例(ESP-IDF官方编译烧写工具)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/gpio.h"
#include "sdkconfig.h"
#define DHT11_PIN (15) //可通过宏定义,修改引脚
#define DHT11_CLR gpio_set_level(DHT11_PIN, 0) //设为低电平
#define DHT11_SET gpio_set_level(DHT11_PIN, 1) //设为高电平
#define DHT11_IN gpio_set_direction(DHT11_PIN, GPIO_MODE_INPUT) //设为输入模式
#define DHT11_OUT gpio_set_direction(DHT11_PIN, GPIO_MODE_OUTPUT) //设为输出模式
uint8_t DHT11Data[4] = {0};
uint8_t Temp, Humi;
// us延时函数,误差不能太大
void DelayUs(uint32_t nCount)
{
ets_delay_us(nCount);
}
//DHT11初始化
void DHT11_Start(void)
{
/***************************
*
* 主机发送给DHT11复位信号
*
* 首先主机拉低总线至少 18ms,然后再拉高总线,延时 20~40us,取中间值 30us,此时复位信号发送完毕。
*
* **********************************/
DHT11_OUT; //设置端口方向
DHT11_CLR; //拉低端口
DelayUs(19 * 1000);
// vTaskDelay(19 * portTICK_RATE_MS); //持续最低18ms;
DHT11_SET; //主机拉高端口
DelayUs(30); //总线由上拉电阻拉高,主机延时30uS;
/********************************
* DHT11发送响应信号
*
* DHT11 检测到复位信号后,触发一次采样,
* 并拉低总线 80us 表示响应信号,告诉主机数据已经准备好了;
* 然后 DHT11 拉高总线 80us,
* 之后开始传输数据。如果检测到响应信号为高电平,
* 则 DHT11 初始化失败,
* 请检查线路是否连接正常。
*
*******************************/
DHT11_IN; //设置端口方向
while (!gpio_get_level(DHT11_PIN))
; // DHT11等待80us低电平响应信号结束
while (gpio_get_level(DHT11_PIN))
; // DHT11 将总线拉高80us
}
uint8_t DHT11_ReadValue(void)
{
uint8_t i, sbuf = 0;
for (i = 8; i > 0; i--)
{
sbuf <<= 1;
while (!gpio_get_level(DHT11_PIN))
;
DelayUs(40); // 延时 30us 后检测数据线是否还是高电平
if (gpio_get_level(DHT11_PIN))
{
sbuf |= 1;
}
else
{
sbuf |= 0;
}
while (gpio_get_level(DHT11_PIN))
;
}
return sbuf;
}
uint8_t DHT11_ReadTemHum(uint8_t *buf)
{
uint8_t check;
buf[0] = DHT11_ReadValue();
buf[1] = DHT11_ReadValue();
buf[2] = DHT11_ReadValue();
buf[3] = DHT11_ReadValue();
check = DHT11_ReadValue();
if (check == buf[0] + buf[1] + buf[2] + buf[3])
return 1;
else
return 0;
}
void app_main(void)
{
// printf("ESP32 DHT11 TEST:%s,%s!\r\n", __DATE__, __TIME__);
gpio_pad_select_gpio(DHT11_PIN);
while (1)
{
DHT11_Start();
if (DHT11_ReadTemHum(DHT11Data))
{
Temp = DHT11Data[2];
Humi = DHT11Data[0];
printf("ESP32 DHT11 TEST:%s,%s!\r\n", __DATE__, __TIME__);
printf("Temp=%d.%d, Humi=%d.%d\r\n", Temp,DHT11Data[3],Humi,DHT11Data[1]);
}
else
{
printf("DHT11 Error!\r\n");
}
vTaskDelay(5000 / portTICK_PERIOD_MS);
}
}
  • 附录:

    树莓派GPIO

    树莓派GPIO