收到信号:使用DHT11感知温度
简介
DHT11数字温湿度传感器是一款能够检测温湿度的复合传感器,其内置一个测温元件、一个电阻式感湿元件和一个单片机。其有效测量范围见表。
测量值 | 有效测量范围 |
---|---|
温度 | 0~50℃ |
湿度 | 20~95% |
通信协议
1.配对(握手)
在发送温湿度数据前,DHT11需要先和树莓派配对,配对的协议如下:
(1)DATA引脚在初始的默认状态时处于高电平(3.3v)。
(2)树莓派拉低DATA引脚的电平18ms毫秒以上,接着拉高电平20~40us,DHT11就会被激活。
(3)DHT11会主动拉低DATA引脚的电平,并且持续80us,表示已经收到了树莓派的指令并且配对成功。
(4)接着DHT11会再次拉高电平,80us后就开始发送温湿度数据给树莓派了。
这个配对过程如图所示。
2.接收数据
DHT11的温湿度数据是以二进制数据表示的,这些二进制数据是按照一个比特位接着一个比特位这样顺序发送到树莓派的,具体的原理如下:
(1)在发送每个比特位之前,DHT11都会把DATA引脚的电平拉低50us,以此告诉树莓派:“我接着要发送一个比特位了”。
(2)接着,DHT11把DATA引脚的电平拉高,如果持续拉高26~28us,表示发送的是数据0;如果持续拉高70us,表示发送的是数据1,如下图所示。
通过这个方式,温湿度数据就发送给树莓派了。每当配对成功后,DHT11就会默认发送40个比特位,即一共5个字节,其中包含两个字节的当前温度值、两个字节是当前湿度值和一个校验值。DHT11的通信协议大致上介绍完毕。
3.接收数据
- DHT11数字湿温度传感器采用单总线数据格式。即单个数据引脚端口完成输入输出双向传输。其数据包由5Byte(40Bit)组成。数据分小数部分和整数部分,一次完整的数据传输为40bit,高位先出。
- 数据格式:8bit湿度整数数据+8bit湿度小数数据+8bit温度整数数据+8bit温度小数数据+8bit校验和。校验和数据为前四个字节相加。
传感器数据输出的是未编码的二进制数据。数据(湿度、温度、整数、小数)之间应该分开处理。如果,某次从传感器中读取如下5Byte数据:
| byte4 | byte3 | byte2 | byte1 | byte0 | | —- | —- | —- | —- | —- | | 00101101 | 00000000 | 00011100 | 00000000 | 01001001 | | 整数 | 小数 | 整数 | 小数 | 校验和 | | 湿度 | 湿度 | 温度 | 温度 | 校验和 |- 由以上数据就可得到湿度和温度的值,计算方法:
humi (湿度)= byte4 . byte3=45.0 (%RH)
temp (温度)= byte2 . byte1=28.0 ( ℃)
jiaoyan(校验)= byte4+ byte3+ byte2+ byte1=73(=humi+temp)(校验正确)
- 由以上数据就可得到湿度和温度的值,计算方法:
硬件连接
DHT11:
- VCC:电源正极(接5V电源)
- GND:电源负极(接地)
- OUT:传感器数据输出,接树莓派 11脚
*_DHT11传感器_
DHT11驱动API设计
#导入函数
import RPi.GPIO as GPIO
import time
#定义数据口
channel = 18
data = []
j = 0
#初始化模式
GPIO.setmode(GPIO.BOARD)
#一秒后开始工作
time.sleep(1)
#设置GPIO接口为写入数据模式
GPIO.setup(channel, GPIO.OUT)
#输出一个低电平信号
GPIO.output(channel, GPIO.LOW)
time.sleep(0.02)
#0.02秒后输出一个高电平信号,启动模块测量
GPIO.output(channel, GPIO.HIGH)
#设置GPIO接口为读取读取数据模式
GPIO.setup(channel, http://GPIO.IN)
#等待,获取到高电平信号
while GPIO.input(channel) == GPIO.LOW:
continue
#等待,获取到低电平信号
while GPIO.input(channel) == GPIO.HIGH:
continue
#获取到高底电平信号后,开始读取模块获取数据
while j < 40:
k = 0
while GPIO.input(channel) == GPIO.LOW:
continue
while GPIO.input(channel) == GPIO.HIGH:
k +=1
if k > 100:
Break
#把获取数据放到list中
if k < 20:
data.append(0)
else:
data.append(1)
j +=1
#模块数据读取完毕,打印显示
print ("sensor is working.")
print (data)
#根据获取数据定义解析数据(5组二进制数据)
humidity_bit = data[0:8]
humidity_point_bit = data[8:16]
temperature_bit = data[16:24]
temperature_point_bit = data[24:32]
check_bit = data[32:40]
humidity = 0
humidity_point = 0
temperature = 0
temperature_point = 0
check = 0
#根据公式定义,解析温湿度数据
for i in range(8):
humidity +=humidity_bit[i] * 2 ** (7-i)
humidity_point +=humidity_point_bit[i] * 2 ** (7-i)
temperature +=temperature_bit[i] * 2 ** (7-i)
temperature_point +=temperature_point_bit[i] * 2 ** (7-i)
check +=check_bit[i] * 2 **(7-i)
tmp = humidity + humidity_point + temperature + temperature_point
#打印显示由模块数据解释的温湿度数据
if check == tmp:
print ("temperature :", temperature, "*C, humidity :", humidity, "%")
else:
print ("wrong")
print ("temperature :", temperature, "*C, humidity :", humidity, "% check :", check, ",tmp :", tmp)
#结束进程,释放GPIO引脚
GPIO.cleanup()
运行后获得结果:
安装 CircuitPython-DHT库
您还需要安装一个库来与 DHT 传感器进行通信。由于我们使用的是Adafruit Blinka(CircuitPython),我们可以将CircuitPython库直接安装到我们的小型linux板上。在本例中,我们将安装CircuitPython_DHT库。该库可与 DHT22 和 DHT11 传感器配合使用。
sudo apt-get Install pip3 #安装pip3
pip3 install adafruit-circuitpython-dht #通过pip3安装circuitpython_dht库
sudo apt-get install libgpiod2 #安装libgpiod2
测试 CircuitPython-DHT库
为了确保您正确安装了所有内容,我们将测试是否可以从连接到设备的DHT传感器读取值。
使用vim文本编辑器创建一个名为dht_simpletest.py的新文件
vim dht_simpletest.py
并将以下内容放入:
# SPDX-FileCopyrightText: 2021 ladyada for Adafruit Industries
# SPDX-License-Identifier: MIT
import time
import board
import adafruit_dht
# Initial the dht device, with data pin connected to:
dhtDevice = adafruit_dht.DHT11(board.D17)
#在这里需要说明,这里的board.D17,使用的是BCM编码的17号脚,对应到BOARD上,实际上第11个脚
#BCM17其实也就是之前的GPIO.0
# you can pass DHT22 use_pulseio=False if you wouldn't like to use pulseio.
# dhtDevice = adafruit_dht.DHT22(board.D18, use_pulseio=False)
while True:
try:
# Print the values to the serial port
temperature_c = dhtDevice.temperature
temperature_f = temperature_c * (9 / 5) + 32
humidity = dhtDevice.humidity
print(
"Temp: {:.1f} F / {:.1f} C Humidity: {}% ".format(
temperature_f, temperature_c, humidity
)
#上述打印的方式需要牢记,是连续打印了三个浮点型的变量,并进行了格式化
)
except RuntimeError as error:
# Errors happen fairly often, DHT's are hard to read, just keep going
#这里“happen fairly often”指的是常常发生的意思
print(error.args[0])
time.sleep(2.0)
continue
except Exception as error:
dhtDevice.exit()
raise error
time.sleep(2.0)
接下来,您将需要修改此文件中的一行代码,其中包含有关 DHT 传感器连接到的引脚以及您正在使用的 DHT 传感器类型的信息。
如果您使用的树莓派与连接到引脚 4 的 DHT22(或 AM2302)传感器,请将以下行
dhtDevice = adafruit_dht.DHT11(board.D17)
更改为:
dhtDevice = adafruit_dht.DHT22(board.D4)
如果您使用的是 DHT11 传感器,则可以通过将 DHT22 类重命名为 DHT11 来更改传感器类型:
dhtDevice = adafruit_dht.DHT11(board.D18)
然后,保存示例。接下来,通过在终端中键入以下命令来运行示例:
python3 dht_simpletest.py
按 Enter 运行示例。您应该会看到终端中显示的温度(以华氏度和摄氏度为单位)和湿度值:
Temp: 73.4 F / 23.0 C Humidity: 40.3%
Temp: 73.4 F / 23.0 C Humidity: 40.3%
结束进程
新建一个终端
ps a #展示全部进程
kill **** #结束相关的两个进程
新程序
import RPi.GPIO as GPIO
import time
#from functools import reduce
class DHT11:
def __init__(self, pin):
self.pin = pin
self.temp = 0.0
self.hum = 0.0
self.last_upd = 0.0
GPIO.setmode(GPIO.BCM)
time.sleep(1)
def dht11_read_noretry(self):
hum, temp = self._dht_read_data()
print("Temp is {}, Hum is {}%".format(temp, hum))
return hum, temp
def _dht_read_data(self):
# start transmit
bits = []
bit_times = []
GPIO.setup(self.pin, GPIO.OUT)
GPIO.output(self.pin, GPIO.HIGH)
self._delay_us(50 * 1000)
# start transmit
GPIO.output(self.pin, GPIO.LOW)
self._delay_us(25 * 1000) # 25ms > 18ms
GPIO.setup(self.pin, GPIO.IN, pull_up_down=GPIO.PUD_UP)#拉高电压
#GPIO.output(self.pin, GPIO.HIGH)
# check dht respond
start_t1 = time.time()
end_t1 = time.time()
while GPIO.input(self.pin) == 0:
end_t1 = time.time()
if end_t1 - start_t1 > 0.1:
raise Exception('DHT11 read failed at WAIT_LOW_RESPOND')
start_t2 = time.time()
while GPIO.input(self.pin) == 1:
end_t2 = time.time()
if end_t2 - start_t2 > 0.1:
raise Exception('DHT11 read failed at WAIT_HIGH_RESPOND, '
'PHASE1 used {} us'.format((end_t1 - start_t1) * 1000000))
# GPIO.setup(self.pin, GPIO.IN)
# while GPIO.input(self.pin) == GPIO.LOW:
# continue
# while GPIO.input(self.pin) == GPIO.HIGH:
# continue
# read data
# fist bit (still) is DHT11 respond, so we read 41 bits
for i in range(41):
low_start_t = time.time()
while GPIO.input(self.pin) == 0:
t = time.time()
if t - low_start_t > 0.1:
raise Exception("DHT11 read failed at READING_BITS")
high_start_t = time.time()
while GPIO.input(self.pin) == 1:
high_end_t = time.time()
bit_times.append((high_end_t - high_start_t) * 1000000)
if (high_end_t - high_start_t) * 1000000 < 30:
bits.append(0)
else:
bits.append(1)
return self._process_bits(bits)
def _process_bits(self, bits):
bits = bits[1:41]
hum_h = self._bin_to_dec(bits[0:8])
hum_l = self._bin_to_dec(bits[8:16])
temp_h = self._bin_to_dec(bits[16:24])
temp_l = self._bin_to_dec(bits[24:32])
checksum = self._bin_to_dec(bits[32:40])
if hum_h + hum_l + temp_h + temp_l != checksum:
raise Exception("DHT11 read failed at CHECKSUM"
"hum_h {} hum_l {} temp_h {} temp_l {} checksum {}".format(
hum_h, hum_l, temp_h, temp_l, checksum
))
hum = hum_h + hum_l * 0.1
temp = temp_h + temp_l * 0.1
return hum, temp
def _bin_to_dec(self, bin):
return int("".join(str(i) for i in bin), 2)
def _delay_us(self, t):
start = time.time()
end = 0.0
t = (t - 3) / 1000000 # us to sec
while end - start < t:
end = time.time()
if __name__ == '__main__':
dht = DHT11(pin=4)
dht.dht11_read_noretry()