树莓派基础实验27:温湿度传感器DHT11 实验

时间:2022-07-25
本文章向大家介绍树莓派基础实验27:温湿度传感器DHT11 实验,主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

一、介绍

   数字温湿度传感器DHT11是一种复合传感器,包含温度和湿度的校准数字信号输出。采用专用数字模块采集技术和温湿度传感技术,确保产品具有高可靠性和优异的长期稳定性。    该传感器包含一个电阻湿感元件和一个NTC温度测量设备,并与一个高性能8位微控制器连接。其精度:湿度+-5%RH, 温度+-2℃。量程:湿度20-90%RH, 温度0~50℃。采样周期:大于等于1秒/次。    在我们刚开始练习写传感器的时序时,DHT11非常适合新手入门练习如何写时序。


二、组件

★Raspberry Pi主板*1

★树莓派电源*1

★40P软排线*1

★湿度传感器DHT11模块*1

★面包板*1

★跳线若干

三、实验原理

温湿度传感器

温湿度传感器模块原理图

   DHT11是一款价格便宜,易于使用的温度湿度测量二合一传感器。它具有超小体积、极低功耗的特点。它使用单根总线与单片机进行双向的串行数据传输,信号传输距离可达20米以上。非常适用于对精度和实时性要求不高的温湿度测量场合。

DHT11硬件原理图

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

1. DHT11的数据格式:

  DATA 用于树莓派与DHT11之间的通讯和同步,采用单总线数据格式,一次通讯时间4ms左右,数据分小数部分和整数部分,具体格式在下面说明,操作流程如下:

   DHT11用的是单总线协议,一次传送40位的数据。 注意了,看到这一句话,也就是说我们每次读取DHT11的数据时,都要一次性读取40次,也就是读取40位。并且数据前16位是与湿度相关的,中间16位是与温度相关的,最后八位是用来校验的,当我们校验成功后,证明这一次的温湿度结果正确的,我们的树莓派就可以使用这个温湿度值;如果校验不通过,那么就代表我们这次读取出来的温湿度值,是错误的(也许是我们的时序错误了,也许是传感器的问题),我们不进行采样。

DHT11数据格式示例

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应答信号

第四步:由 DHT11 的 DATA 引脚输出 40 位数据,树莓派根据 I/O 电平的变化接收 40 位数据,位数据“0”的格式为: 50 微秒的低电平和 26-28 微秒的高电平;位数据“1”的格式为: 50 微秒的低电平加 70微秒的高电平。位数据“0”、“1”格式信号如图所示:

位数据“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

温湿度传感器DHT11 实验电路图

温湿度传感器DHT11 实验实物接线图

第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 programn"
    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,skipn"
            time.sleep(1)
            
def destroy():
    GPIO.cleanup()

if __name__ == '__main__':
    try:
        main()
    except KeyboardInterrupt:
        destroy()