超详细!基于树莓派Python编程使用dht11温湿度模块

前言:

本人树莓派小白(电路知识也很有限),在树莓派上使用dht11模块时没有在网络上找到 很详细的教程,因此经历两天的摸索才得以正确使用该模块,所以我萌生了写一篇较为详细的教程的想法。如果同为小白,仔细研究dht11的使用会为其他的模块使用打下坚实的基础(例如:超声波模块、寻路模块等),如果你没有耐心看完本文可去文章末尾拿代码。

一、材料

1.树莓派
2.dht11温度模块
3.杜邦线(买模块一般都会送)

二、连线

dht11模块包含三个引脚:VCC(正极3.3v)、DATA(数据传输口)、GND(负极或0电位)树莓派GPIO参考图(本文使用BOARD编码即下图的编码模式)

python 对比 C语音 树莓派 python开发树莓派_python 对比 C语音 树莓派


其中VCC接树莓派任意3.3v接口

DATA接树莓派任意GPIO(本文接12)

GND接树莓派任意GND

VCC与GND勿接反(据说接反必烧)

三、dht11通信原理及对应Python编程

1.首先,对进行准备工作,引入一些库(官网系统自带,无需安装)和定义一下引脚。

import RPi.GPIO as GPIO   #引入RPi.GPIO模块,并实例化为GPIO,简化后面的模块调用
import time                #引入time模块
GPIO.setmode(GPIO.BOARD)    #定义GPIO编码方式
time.sleep(2)               #由于dht11的采样周期间隔为2s,此处防止dht无方响应
Pin=12                      #定义引脚,使用哪个就定义哪个

2.DATA 用于树莓派与 DHT11之间的通讯和同步,采用单总线数据格式,一次通讯时间4ms左右,数据分整数部分和小数部分,一次完整的数据传输为40bit(40个二进制数),高位先出(代码在第5点处一并说明)(dht11与树莓派的通信过程见下图)。

python 对比 C语音 树莓派 python开发树莓派_树莓派_02

3.dht11默认处于高电平状态 ,当树莓派发送开始信号(1段大于18μs的低电平),dht11接收到树莓派的开始信号后会转为工作模式,此时应将树莓派转的GPIO转为高电平状态然后将GPIO转为输入模式用于接收dht11的信号。

GPIO.setup(Pin,GPIO.OUT)    #将GPIO设置为输出模式
GPIO.output(Pin,0)          #向dht11发送低电平(开始信号)(其中0可以用Flase、GPIO.low代替,下文的1同理)
time.sleep(0.02)            #低电平持续时间0.02s大于dht11要求的0.018s
GPIO.output(Pin,1)          #向dht11发送高电平(理论上可以取消,但是实际使用中不发送高概率出错)
GPIO.setup(Pin,GPIO.IN)     #将GPIO转为输入模式,用以接收dht11的信号

4.接收到开始信号后dht11首先会发送一段持续80μs的低电平信号和80μs的高电平信号以回复树莓派的开始信号,然后将会发送40bit(40个二进制数)的湿度温度数据,每个bit都将以一段持续50μs的低电平信号开始(信号的开头是一样的,因此无法判断是0还是1),然后续以一段时间不等的高电平信号,其中如果发送二进制"0"将持续26-28μs,二进制"1"将持70μs。因此后续编程将根据高电平信号持续的时间来判断dht11发送的信号是0还是1。(前三行赋值代码放到这里是便于理解,实际运行过程高概率因为数据接收不全卡死,在后面会介绍一下)

Bit=[]                     #定义列表,用以存储接收到的数据
Time=[]                    #存储数字0和数字1的高电位持续时间
K=0                         #定义循环初始值
while GPIO.input(Pin)==0:   #采用轮训检测dht的响应信号,如果输入高电平将跳出此循环(不懂可以查阅python的while continue用法)
    continue
while GPIO.input(Pin)==1:   #采用轮训检测dht的响应信号
    continue
while K<40:                 #循环40次用以接收dht11的40bit数据
    while GPIO.input(Pin)==0:#因为每bit数据以低电平开始,故此。
        continue
    begin=time.time()       #低电平循环结束故此时是高电平信号,因此开始计时
    while GPIO.input(Pin)==1:#轮训高电平
        continue
    end=time.time()         #获取高电平信号的结束时间
    #Time.append(end-begin)#此处为了确定0和1的高电平持续时间
    if (end-begin)<0.00003: #检测高电平的持续时间判断输入是0还是1(0.00003可以自测一下,取一个适合你的时间)
        Bit.append(0)       #高电平小于0.00003s证明是0
    else:
        Bit.append(1)       #高电平大于0.00003s证明是1
    K=K+1                   #记录循环次数
#print(Time)				#观察时间特点以确定上文0.00003的取值

5.将得到40bit数据进行拆分、校验并转换为10进制数输出。

humidity1bit=Bit[0:8]       #根据dht11的信号原理获取所需的值(下同理)
humidity2bit=Bit[8:16]
temperature1bit=Bit[16:24]
temperature2bit=Bit[24:32]
checkbit = Bit[32:40]
humidity1=0
humidity2=0
temperature1=0
temperature2=0
check=0
for i in range(0,8):         #循环8次,将二进制数转换为十进制数
    humidity1+=humidity1bit[i]*(2**(7-i))
    humidity2+=humidity2bit[i]*(2**(7-1))
    temperature1+=temperature1bit[i]*(2**(7-i))
    temperature2+=temperature2bit[i]*(2**(7-i))
    check+=checkbit[i]*(2**(7-i))
temperature=temperature1+temperature2*0.1 #获取温度值(注意dht11的使用说明中明文写到整数位后是小数位故应乘0.1)
humidity=humidity1+humidity2*0.1        #(网上大部分都没乘0.1,他们的运行结果是整数,足可见网上的帖子都是复制粘着的水文)
checknum=temperature1+humidity1+temperature2+humidity2 #计算前32位数的值
if checknum==check:                         #检查前32位的值是否与校验位相等
    print("temp:%s,hum:%s"%(temperature,humidity))   #相等输出温度和湿度
else:
    print("dht11 check was wrong.  checknum:%s check:%s"%(

四、本人实际运行时遇到的一些错误

1.在准备阶段,由于dht11使用说明显示发送低电平开始信号后,可直接将GPIO调到输入模式。但是在实际操作过程中,将GPIO直接调到输入状态,后续接受到的数据高概率出现校验码不符的情况,如果在发送开始信号后加上发送高电平信号代码会减少此类错误。
2.本人第一次采用模块化编程,但是这种编程手段运行速度缓慢,会发生接收数据不全的情况,然后会卡到while循环的高电平状态,无法退出。
3.切记采用我这种检测高电平信号持续时间的方法,不要在whlie k <40:的循环内加入print等函数,不然会导致类似问题二循环不能退出的问题。
4.赋值语句要放到开头不然影响代码运行进度,而造成数据接收不全卡死在while循环内.(具体原因是dht11输出信号后会转入高电平状态,而赋值语句的加入影响代码运行进度使接收到的数据小于40bit而无法跳出while循环)

``

while K<40:
    while GPIO.input(Pin)==0:
        continue
    begin=time.time()
    while GPIO.input(Pin)==1:      #我断点测试卡到这里,符合推测
        continue
    end=time.time()
    if (end-begin)<0.00003:
        Bit.append(0)
    else:
        Bit.append(1)
    K=K+1

五、完整代码及注释

import RPi.GPIO as GPIO   #引入RPi.GPIO模块,并实例化为GPIO,简化后面的模块调用
import time                #引入time模块
Bit=[]                     #定义列表,用以存储接收到的数据
Time=[]                    #存储数字0和数字1的高电位持续时间
K=0                         #定义循环初始值
GPIO.setmode(GPIO.BOARD)    #定义GPIO编码方式
time.sleep(2)               #由于dht11的采样周期间隔为2s,此处防止dht无方响应
Pin=12                      #定义引脚,使用哪个就定义哪个
GPIO.setup(Pin,GPIO.OUT)    #将GPIO设置为输出模式
GPIO.output(Pin,0)          #向dht11发送低电平(开始信号)(其中0可以用Flase、GPIO.low代替,下文的1同理)
time.sleep(0.02)            #低电平持续时间0.02s大于dht11要求的0.018s
GPIO.output(Pin,1)          #向dht11发送高电平(理论上可以取消,但是实际使用中不发送高概率出错)
GPIO.setup(Pin,GPIO.IN)     #将GPIO转为输入模式,用以接收dht11的信号
while GPIO.input(Pin)==0:   #采用轮训检测dht的响应信号,如果输入高电平将跳出此循环(不懂可以查阅python的while continue用法)
    continue
while GPIO.input(Pin)==1:   #采用轮训检测dht的响应信号
    continue
while K<40:                 #循环40次用以接收dht11的40bit数据
    while GPIO.input(Pin)==0:#因为每bit数据以低电平开始,故此。
        continue
    begin=time.time()       #低电平循环结束故此时是高电平信号,因此开始计时
    while GPIO.input(Pin)==1:#轮训高电平
        continue
    end=time.time()         #获取高电平信号的结束时间
    #Time.append(end-begin)#此处为了确定0和1的高电平持续时间
    if (end-begin)<0.00003: #检测高电平的持续时间判断输入是0还是1(0.00003可以自测一下,取一个适合你的时间)
        Bit.append(0)       #高电平小于0.00003s证明是0
    else:
        Bit.append(1)       #高电平大于0.00003s证明是1
    K=K+1                   #记录循环次数
#print(Time)				#观察时间特点以确定上文0.00003的取值
humidity1bit=Bit[0:8]       #根据dht11的信号原理获取所需的值(下同理)
humidity2bit=Bit[8:16]
temperature1bit=Bit[16:24]
temperature2bit=Bit[24:32]
checkbit = Bit[32:40]
humidity1=0
humidity2=0
temperature1=0
temperature2=0
check=0
for i in range(0,8):         #循环8次,将二进制数转换为十进制数
    humidity1+=humidity1bit[i]*(2**(7-i))
    humidity2+=humidity2bit[i]*(2**(7-1))
    temperature1+=temperature1bit[i]*(2**(7-i))
    temperature2+=temperature2bit[i]*(2**(7-i))
    check+=checkbit[i]*(2**(7-i))
temperature=temperature1+temperature2*0.1 #获取温度值(注意dht11的使用说明中明文写到整数位后是小数位故应乘0.1)
humidity=humidity1+humidity2*0.1        #(网上大部分都没乘,他们的运行结果是整数,足可见网上的帖子都是复制粘着的水文)
checknum=temperature1+humidity1+temperature2+humidity2 #计算前32位数的值
if checknum==check:                         #检查前32位的值是否与校验位相等
    print("temp:%s,hum:%s"%(temperature,humidity))   #相等输出温度和湿度
else:
    print("dht11 check was wrong.  checknum:%s check:%s"%(checknum,check))#不等输出前32位值和校验位值

六、附录

1.dht11原理:
链接:https://pan.baidu.com/s/1bdQ0y9RWzJmOeZz986PJDg
提取码:tqzh
2.Python代码下载:
链接:https://pan.baidu.com/s/1F4qF0RY_xuFe5OJhgowPUg
提取码:qgxz
3.本人亲测使用树莓派IDE运行出错概率远高于使用命令行:sudo python dht11.py
4.对与0和1的判断网上有与我不同的想法,他的想法很巧妙(通过检测在高电平持续时间内能进行多少次赋值运算,进而通过检测k值来判断高电平持续时间),但有点难以理解(不如我的来的直接)可作为参考(我的代码和他的代码均有一定概率出错)

while j < 40:
  k = 0
  while GPIO.input(channel) == GPIO.LOW:
    continue
  while GPIO.input(channel) == GPIO.HIGH:
    k += 1		
    if k > 100:
      break
  if k < 8:
    data.append(0)
  else:
    data.append(1)
 
  j += 1