一、介绍
数字温湿度传感器DHT11是一种复合传感器,包含温度和湿度的校准数字信号输出。采用专用数字模块采集技术和温湿度传感技术,确保产品具有高可靠性和优异的长期稳定性。
该传感器包含一个电阻湿感元件和一个NTC温度测量设备,并与一个高性能8位微控制器连接。其精度:湿度+-5%RH, 温度+-2℃。量程:湿度20-90%RH, 温度0~50℃。采样周期:大于等于1秒/次。
在我们刚开始练习写传感器的时序时,DHT11非常适合新手入门练习如何写时序。
二、组件
★Raspberry Pi主板*1
★树莓派电源*1
★40P软排线*1
★湿度传感器DHT11模块*1
★面包板*1
★跳线若干
三、实验原理
DHT11是一款价格便宜,易于使用的温度湿度测量二合一传感器。它具有超小体积、极低功耗的特点。它使用单根总线与单片机进行双向的串行数据传输,信号传输距离可达20米以上。非常适用于对精度和实时性要求不高的温湿度测量场合。
数据总线DATA使用上拉电阻拉高,因此总线空闲时为高电平。上拉电阻阻值推荐范围:4.7K~5.1K。必要时在VDD和GND之间并一个100nF的去耦电容。
1. DHT11的数据格式:
DATA 用于树莓派与DHT11之间的通讯和同步,采用单总线数据格式,一次通讯时间4ms左右,数据分小数部分和整数部分,具体格式在下面说明,操作流程如下:
DHT11用的是单总线协议,一次传送40位的数据。 注意了,看到这一句话,也就是说我们每次读取DHT11的数据时,都要一次性读取40次,也就是读取40位。并且数据前16位是与湿度相关的,中间16位是与温度相关的,最后八位是用来校验的,当我们校验成功后,证明这一次的温湿度结果正确的,我们的树莓派就可以使用这个温湿度值;如果校验不通过,那么就代表我们这次读取出来的温湿度值,是错误的(也许是我们的时序错误了,也许是传感器的问题),我们不进行采样。
2. DHT11的工作原理:
DHT11的总体通信流程:
第一步:主机(树莓派)先发送开始信号,从机(DHT11)会返回一个相应信号进行应答。
第二步:主机信号线拉高准备接收数据。
第三步:开始接收数据(一次接收40位)。
DHT11使用单一总线通信,即DATA引脚和单片机连接的线。总线总是处于空闲状态和通信状态这个2个状态之间。当树莓派没有与DHT11交互时,总线处于空闲状态,在上拉电阻的作用下,处于高电平状态。
当单片机和DHT11正在通信时,总线处于通信状态,一次完整的通信过程如下:
第一步:DHT11 上电后(DHT11 上电后要等待 1秒以越过不稳定状态在此期间不能发送任何指令),测试环境温湿度数据,幵记录数据,同时 DHT11 的 DATA 数据线由上拉电阻拉高一直保持高电;此时 DHT11 的DATA 引脚处于输入状态,时刻检测外部信号。
第二步:微处理器的 I/O 设置为输出,同时输出低电平,且低电平保持时间不能小于 18ms,然后输出高电平20~40us,再树莓派的 I/O设置为输入状态,等待 DHT11 作出回答信号,发送信号如图所示:
第三步:DHT11 的 DATA 引脚检测到外部信号有低电平时,等待外部信号低电平结束,延迟后 DHT11 的 DATA引脚处于输出状态,输出 80 微秒的低电平作为应答信号,紧接着输出 80 微秒的高电平通知外设准备接收数据,树莓派的 I/O 此时处于输入状态,检测到 I/O 有低电平(DHT11 回应信号)后,等待 80 微秒的高电平后的数据接收,发送信号如图所示:
第四步:由 DHT11 的 DATA 引脚输出 40 位数据,树莓派根据 I/O 电平的变化接收 40 位数据,位数据“0”的格式为: 50 微秒的低电平和 26-28 微秒的高电平;位数据“1”的格式为: 50 微秒的低电平加 70微秒的高电平。位数据“0”、“1”格式信号如图所示:
我们可以把这一段的时序理解为,DHT11先把数据线拉低50us,然后我们再去对比高电平持续的时间,如果持续时间较短,则为位“0”;如果持续时间较长,则为位“1”。
结束信号:DHT11 的 DATA 引脚输出 40 位数据后,继续输出低电平 50 微秒后转为输入状态,由于上拉电阻随之变为高电平。但 DHT11 内部重测环境温湿度数据,幵记录数据,等待外部信号的到来。
注意事项:
1、DHT11上电后,要等待 1秒 以越过不稳定状态,在此期间不能发送任何指令。
2、DHT11属于低速传感器,两次通信请求之间的间隔时间不能太短,一般来说要不能低于1秒。
对DHT11的时序做一个总结:
一. 主机(单片机)发送起始信号:
1.主机先拉高data。
2.拉低data延迟18ms。
3.拉高data(单片机引脚设置为输入)。
二. 从机(DHT11)收到起始信号后进行应答:
从机拉低data,主机读取到data线被拉低持续80us后从机拉高data线, 持续80us,直到高电平结束,意味着主机可以开始接受数据。
三. 主机开始接收数据:
1.主机先把data线拉高(io设置为输入)。
2.从机把data线拉低,主机读取data线电平,直到低电平结束(大约50us)从机拉高data线后,对比高电平持续的时间,如果持续时间较短,则为位“0”;如果持续时间较长时,则为位“1”。
3.继续重复上述1,2步骤累计40次。
四. data线拉低50us代表读取结束
五. 校验数据
更多资料请参考DHT11 官方手册:
https://www.dfrobot.com.cn/image/data/DFR0067/DFR0067_DS_10.pdf
四、实验步骤
第1步:连接电路。
树莓派 | T型转接板 | 温湿度传感器 |
---|---|---|
GPIO0 | G17 | OUT(DATA) |
5V | 5V | VCC |
GND | GND | GND |
第2步:编写控制程序。将提取的二进制数据转化为十进制数据,校验后打印出来。
本次编程中将用到NumPy(Numerical Python)扩展程序库,支持大量的维度数组与矩阵运算,此外也针对数组运算提供大量的数学函数库。详情参考NumPy 教程:
https://www.runoob.com/numpy/numpy-tutorial.html
为便于对比DHT11高电平持续的时间,我们设置了一个计数器参数k,如果持续时间较短,则k值较小;如果持续时间较长时,则k值较大。当然,也可以使用时间函数直接对比时间长短,但是程序相对要复杂一些。比如使用GPIO.add_event_detect()和time.time()函数。
从上面的截图中可以看出,高电平持续的时间较短,26-28 微秒时,参数k等于5或6;高电平持续的时间较长,70 微秒时,参数k等于17或18。
#!/usr/bin/env python
import RPi.GPIO as GPIO
import numpy as np
import time
DHTPIN = 17 #引脚号17
GPIO.setmode(GPIO.BCM) #以BCM编码格式
def read_dht11_dat():
GPIO.setup(DHTPIN, GPIO.OUT)
GPIO.output(DHTPIN, GPIO.LOW)
#给信号提示传感器开始工作,并保持低电平18ms以上
time.sleep(0.02) #这里保持20ms
GPIO.output(DHTPIN, GPIO.HIGH) #然后输出高电平
GPIO.setup(DHTPIN, GPIO.IN)
# 发送完开始信号后得把输出模式换成输入模式,不然信号线上电平始终被拉高
while GPIO.input(DHTPIN) == GPIO.LOW:
continue
#DHT11发出应答信号,输出 80 微秒的低电平
while GPIO.input(DHTPIN) == GPIO.HIGH:
continue
#紧接着输出 80 微秒的高电平通知外设准备接收数据
#开始接收数据
j = 0 #计数器
data = [] #收到的二进制数据
kk=[] #存放每次高电平结束后的k值的列表
while j < 40:
k = 0
while GPIO.input(DHTPIN) == GPIO.LOW: # 先是 50 微秒的低电平
continue
while GPIO.input(DHTPIN) == GPIO.HIGH: # 接着是26-28微秒的高电平,或者 70 微秒的高电平
k += 1
if k > 100:
break
kk.append(k)
if k < 8: #26-28 微秒时高电平时通常k等于5或6
data.append(0) #在数据列表后面添加一位新的二进制数据“0”
else: #70 微秒时高电平时通常k等于17或18
data.append(1) #在数据列表后面添加一位新的二进制数据“1”
j += 1
print("sensor is working.")
print '初始数据高低电平:\n',data #输出初始数据高低电平
print '参数k的列表内容:\n',kk #输出高电平结束后的k值
m = np.logspace(7,0,8,base=2,dtype=int) #logspace()函数用于创建一个于等比数列的数组
#即[128 64 32 16 8 4 2 1],8位二进制数各位的权值
data_array = np.array(data) #将data列表转换为数组
#dot()函数对于两个一维的数组,计算的是这两个数组对应下标元素的乘积和(数学上称之为内积)
humidity = m.dot(data_array[0:8]) #用前8位二进制数据计算湿度的十进制值
humidity_point = m.dot(data_array[8:16])
temperature = m.dot(data_array[16:24])
temperature_point = m.dot(data_array[24:32])
check = m.dot(data_array[32:40])
print humidity,humidity_point,temperature,temperature_point,check
tmp = humidity + humidity_point + temperature + temperature_point
#十进制的数据相加
if check == tmp: #数据校验,相等则输出
return humidity, temperature
else: #错误输出错误信息
return False
def main():
print "Raspberry Pi DHT11 Temperature test program\n"
time.sleep(1) #通电后前一秒状态不稳定,时延一秒
while True:
result = read_dht11_dat()
if result:
humidity, temperature = result
print "humidity: %s %%, Temperature: %s ℃" % \
(humidity, temperature)
print '\n'
time.sleep(1)
if result == False:
print "Data are wrong,skip\n"
time.sleep(1)
def destroy():
GPIO.cleanup()
if __name__ == '__main__':
try:
main()
except KeyboardInterrupt:
destroy()