一起玩转树莓派(23)——DHT11温湿度传感器实践

本文涉及的产品
云解析 DNS,旗舰版 1个月
全局流量管理 GTM,标准版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
简介: DHT11是一款强大的复合传感器,支持环境温度和湿度的测量。其本身比较简单,但是由于其采用串行时序的方式进行数据读写,非常适合我们练习时序编程。

一起玩转树莓派(23)——DHT11温湿度传感器实践

一. 引言

DHT11是一款强大的复合传感器,支持环境温度和湿度的测量。其本身比较简单,但是由于其采用串行时序的方式进行数据读写,非常适合我们练习时序编程。本次实验我们使用的传感器模块如下图所示。

可以看到,此传感器模块有3个引脚,除了电源和接地引脚外,只有一个out引脚用来输出数据和传输控制指令。下面我们来介绍下如何使用此传感器模块。

二. 关于DHT11传感器模块

由于DHT11传感器元件只有一个通信引脚,因此其输入和输出都需要使用同一个引脚。即此引脚是一个串行的单线双向引脚。所谓单线双向是指其只有一条信号传输线,但是可以双向通信。这有些像我们使用的对讲机,一方说话时另一方只能听。DHT11的完整使用手册地址如下:

https://www.dfrobot.com.cn/image/data/DFR0067/DFR0067\_DS\_10.pdf

首先我们先来看DHT11所传输的信息数据的格式。根据文档介绍,DHT11一次完整的通信将传递40位数据,这40位数据包含了温度,湿度和用于校验正确性的数据。因此,我们在读取DHT11的数据时,要完整的读出40位数据后再进行计算。这40位数据的具体格式为:

[8bit的湿度整数部分数据]+[8bit的湿度小数部分数据]+[8bit的温度整数部分数据]+[8bit的温度小数部分数据]+8bit校验数据

其中[8bit的湿度整数部分数据]与[8bit的湿度小数部分数据]与[8bit的温度整数部分数据]与[8bit的温度小数部分数据]的和结果应为8bit的校验数据,如果结果不等则表明此次获取的数据出现异常,应该抛弃掉重新获取。

从传感器拿到的数据格式本身比较简单,比较复杂的点在于其通信过程。整体来说,树莓派与DHT11传感器的通信过程分为3个阶段:

1. 树莓派发出开始信号,之后开始等待传感器模块的应答。

2.传感器模块收到树莓派发出的开始信号后,返回应答信号。

3.树莓派接收到应答信号后,开始进行40位数据的接收。

整体的通信过程手册中有提供一张示意图,如下:

通电后,传感器模块的总线将始终处于空闲状态或通信状态中的一种状态下。定义当空闲状态时,总线输入高电平。对于上面通信过程中的第1个阶段,树莓派先将总线电平拉低,且必须大于18毫秒以让传感器模块检测到此拉低的信号。之后树莓派再将总线拉高,表示树莓派已经发出了一次开始通信信号,1阶段结束。

传感器模块检测到树莓派发起的开始信号后,此时总线电平为被树莓派拉高状态。传感器模块通过总线发送80微秒的低电平信号,表示响应了树莓派的开始信号,之后传感器模块会将总线电平再拉高80微妙,提示树莓派准备开始接收数据,2阶段结束。

阶段1和阶段2的更详细示意图如下:

3阶段为传感器模块发送数据,树莓派接收数据的阶段。每一位数据的发送有0和1两种状态,传感器每发送50微妙的低电平信号即表示要进行1bit数据的传输,之后如果传感器发送了26微秒到28微秒的高电平,则表示发送数据位0,如果发送了70微秒的高电平则表示发送数据位1。之后再进行下一位数据的发送。在实际编程操作时,我们可能不太好精准的测量高电平的时间,但是由于数据位0和数据位1的高电平时间相差很多,我们可以通过测试循环变量的计数来大致得到一个传输数据0时的大致循环次数和传输数据1时的大致循环次数,通过循环次数来判断数据具体是0还是1。

数据位0的信号示意图如下:

数据位1的信号示意图如下:

需要注意,每次测量的时间间隔最好大于1秒,且上电后等待1秒稳定再进行测量。

如果没有接触过元件单总线时序编程,上面的文字描述,总的来说还是有些抽象,下面我们会通过代码来实践。

三. 连线编码

DHT11传感器模块只有3个引脚,中间的out引脚我们可以选择任意一个树莓派额GPIO引脚来连接,这里我们选择物理编码为11的GPIO引脚。按照上面我们介绍的DHT11模块的使用方法,编写代码如下:

#coding:utf-8
import RPi.GPIO as GPIO
import time
import math

# 使用物理编码为11的引脚做总线
P = 11

GPIO.setmode(GPIO.BOARD)

print("开始进行DHT11测量数据获取\n")
# 等待1s后再进行逻辑
time.sleep(1)


def readData():
    print("readData")
    # 先将总线引脚设置为输出模式
    GPIO.setup(P, GPIO.OUT)
    # 将总线电平拉低,发出开始信号
    GPIO.output(P, GPIO.LOW)
    # 手册要求至少保持18ms的低电平,我们这里保持20ms
    time.sleep(0.02)
    # 拉高电平
    GPIO.output(P, GPIO.HIGH)
    # 之后将引脚设为输入模式,等待传感器响应
    GPIO.setup(P, GPIO.IN)

    # 先等待80us的低电平信号
    while GPIO.input(P) == GPIO.LOW:
        continue
    # 再等待80us的高电平信号 
    while GPIO.input(P) == GPIO.HIGH:
        continue

    # 开始接收数据

    # 循环计数
    i = 0
     # 存放二进制位数据
    data = []
    # 存放中间数据
    kdata = []
    # 每次读取40位数据
    while i < 40:
        j = 0
        # 50us的低电平表示准备传输一位数据
        while GPIO.input(P) == GPIO.LOW:
            continue
        # 开始检测高电平的时间
        while GPIO.input(P) == GPIO.HIGH:
            j+=1
            if j > 100:
                    return [False, 0, 0]
        kdata.append(j)
        i+=1

    
    print("--------临时数据----------\n")
    print(kdata)
    print("--------临时数据----------\n")
    
    # 开始整理数据
    i = 0
    while i < 40:
        tmp = kdata[i]
        if tmp > 7:
            data.append(1)
        else:
            data.append(0)
        i+=1
    print("--------临时数据2----------\n")
    print(data)
    print("--------临时数据2----------\n")
    # 解析湿度整数部分
    t1 = data[0:8]
    c1 = 0
    i = 7
    for n in t1:
        c1 += n * math.pow(2, i)
        i-=1

    # 解析湿度整数部分
    t2 = data[8:16]
    c2 = 0
    i = 7
    for n in t2:
        c2 += n * math.pow(2, i)
        i-=1

    # 解析温度整数部分
    t3 = data[16:24]
    c3 = 0
    i = 7
    for n in t3:
        c3 += n * math.pow(2, i)
        i-=1

    # 解析温度整数部分
    t4 = data[24:32]
    c4 = 0
    i = 7
    for n in t4:
        c4 += n * math.pow(2, i)
        i-=1

    # 解析校验
    t5 = data[32:40]
    c5 = 0
    i = 7
    for n in t5:
        c5 += n * math.pow(2, i)
        i-=1
    
    # 进行校验
    va = True
    print("c1:%d\n2c:%d\n3c:%d\n4c:%d\n5:%d\n"%(c1,c2,c3,c4,c5))
    if c1 + c2 + c3 +c4 == c5:
        va = True
    else:
        va = False

    return [va, "%d.%d"%(c1, c2), "%d.%d"%(c3, c4)]


while True:
    time.sleep(1)
    result = readData()
    if result[0]:
        hum = result[1]
        temp = result[2]
        print("当前环境湿度: %s %%, 当前环境温度:%s℃\n" % (hum, temp))
    else:
        print("此次数据无效,已被丢弃\n")
    time.sleep(1)

上面代码中有比较详细的注释,有些地方我们还是可以再解释一下。首先,树莓派发出开始指令的过程比较简单,无需过多解释。麻烦之处在于循环40次来获取40位的二进制数据。在获取数据时,我们采用循环变量来记录高电平的时间比例,从而可以分析出传输的数据是0还是1。需要注意,不同的设备可能循环一次的时间并不完全相同,我们可以先测试下上面代码中kdata变量存储的数据,运行代码如下图:

可以看到,如果是传输数据0,循环计数基本是在3或者4。而如果传输的是数据1,则循环计数在11左右。代码中我们以7为分割,分析时,循环计数大于7时就认为当前传输数据1,否则为0。

获取到了完整的40位二进制数据后,将其转换为数值进行校验即可。最终代码运行效果如下图所示。

专注技术,懂的热爱,愿意分享,做个朋友
目录
相关文章
|
6月前
|
传感器 物联网 芯片
毕业设计 基于STM32单片机无线ZIGBEE智能大棚土壤湿度光照检测
毕业设计 基于STM32单片机无线ZIGBEE智能大棚土壤湿度光照检测
130 0
|
传感器 存储 定位技术
一起玩转树莓派(18)——MPU6050陀螺仪加速度传感器模块应用
现在智能手机的功能已经非常强大,除了基础的通信功能外,测位测速,空间角度等数据的测量也非常方便,这在线路导航,地图,体感游戏等应用中十分重要。不知你是否想过,智能设备是如何获取到其所在的空间状态与加速度等数据的呢?MPU6050就是提供这类数据测量的一种传感器模块。
2305 0
|
5月前
|
传感器 数据采集 移动开发
基于STM32设计的炉温温度检测仪
本文档描述了一个基于STM32F103C8T6微控制器的炉温检测系统设计。系统采用铂电阻PT100作为温度传感器,提供精确的温度测量,并通过0.96寸IIC接口的OLED显示屏显示结果。STM32F103C8T6因其丰富的外设和计算能力被选为主控芯片,PT100的电阻变化通过ADC转换为数字信号。软件设计包括数据采集、处理和显示三个部分,其中OLED显示屏的初始化函数`oled_init()`设置各种屏幕参数,`OLED_Show_Temperature()`函数负责在指定位置显示温度值。
84 3
|
传感器 编解码 物联网
STC89C52+DHT20设计的环境温湿度检测仪
本项目基于STC89C52单片机和DHT20温湿度传感器,实现了一款环境温湿度检测仪。通过传感器采集环境的温度和湿度数据,利用IIC接口的OLED显示屏显示出来,便于用户实时监测环境温湿度状态。
188 1
|
6月前
|
传感器 存储 芯片
毕业设计 基于51单片机环境监测设计 光照 PM2.5粉尘 温湿度 2.4G无线通信
毕业设计 基于51单片机环境监测设计 光照 PM2.5粉尘 温湿度 2.4G无线通信
127 0
|
6月前
|
传感器 数据采集 监控
毕业设计 基于STM32单片机生理监控心率脉搏TFT彩屏波形曲线设计
毕业设计 基于STM32单片机生理监控心率脉搏TFT彩屏波形曲线设计
|
传感器
STM32 AHT10温湿度传感器数据
STM32 AHT10温湿度传感器数据 机智云传输温湿度
215 0
|
传感器 前端开发 机器人
基于单片机的智能循迹避障小车STC89C52红外对管L298N驱动PWM波控制速度
利用红外对管检测黑线与障碍物,并以STC89C52单片机为控制芯片控制电动小汽车的速度及转向,从而实现自动循迹避障的功能。其中小车驱动由L298N驱动电路完成,速度由单片机输出的PWM波控制。
343 0
|
传感器 存储 IDE
HTU21D温湿度传感器与Arduino连接电路图说明
在本用户指南中,我们将学习如何将 HTU21D 温湿度传感器模块与 Arduino 连接。首先,我们将向您介绍 HTU21D 传感器,包括其引出线、特性以及与 Arduino 的接口。其次,我们将在 Arduino IDE 中安装 Adafruit HTU21D 库以访问传感器数据。最后,我们将看到两个在串行监视器和 SSD1306 OLED 显示器上显示温度和湿度值的示例。
345 0
HTU21D温湿度传感器与Arduino连接电路图说明