玩转树莓派入门系列——4.传感器系列之温湿度模块DHT11

一、DHT11介绍

1、DHT11和DHT22

一般的温湿度传感器有DHT11和DHT22,两者的差别在与价格与精确度。DHT11便宜但精度范围大,DHT22贵但精度范围小。

树莓派温湿度监控 树莓派温度模块_数据

2、引脚说明

a) VDD 供电3.3-5.5V DC
b) DATA串行数据,单设备
c) NC 空脚
d) GND 接地,电源负极

3、参数

树莓派温湿度监控 树莓派温度模块_python_02


树莓派温湿度监控 树莓派温度模块_引脚_03


树莓派温湿度监控 树莓派温度模块_树莓派温湿度监控_04

4、通信原理

DHT11器件只有一根数据线,即单总线通信,所以对于时序要求比较大。由于DHT11是主从结构,所以只有当主机发起通信请求时,从机才会开始应答。
数据格式:
8bit湿度整数数据+8bit湿度小数数据+8bit温度整数数据+8bit温度小数数据+8bit校验位

a、起始信号:
主机把SDA(数据总线)拉低18ms<x<30ms
b、响应信号:
从机把SDA拉低83us,再拉高87us响应起始信号
c、数据信号:
“0”的格式为:54微秒的低电平+23到27微秒的高电平
“1”的格式为:54微秒的低电平+68到74微秒的高电平
d、结束信号:
从机的DATA引脚输出40位数据后,继续输出54微秒的低电平后转换为输入状态。
e、数据格式:
收到主机信号后,从机一次性从SDA串出40bit,高位先出
f、温度:
高位为温度整数部分,低位为温度小数部分
g、湿度:
高位为温度整数部分,低位为湿度小数部分
低位第8位1表示负温度,否则位正温度
h、校验位:
校验位=湿度高位+湿度低位+温度高位+温度低位

5、时序图

树莓派温湿度监控 树莓派温度模块_树莓派温湿度监控_05

a、步骤一:

DHT11上电后,要等待超过1s时间等待电平稳定,同时DHT11的SDA处于高电平,DATA引脚处于输入状态,时刻监测外部信号。

b、步骤二:

主机发送低电平,拉低SDA电平,至少超过18ms(最大不超过30ms),然后主机拉高电平,等待从机做出应答信号。

树莓派温湿度监控 树莓派温度模块_python_06

c、步骤三:

DHT11的DATA引脚监测到起始信号后,等待起始信号结束,再输出83微秒的低电平作为应答信号,然后输出87微秒的高电平通知外设准备接收数据,然后主机的IO口就处于输入状态等待数据接收。

树莓派温湿度监控 树莓派温度模块_引脚_07

d、步骤四:

主机开始接收40位的数据,

“0”的格式为:54微秒的低电平+23到27微秒的高电平

“1”的格式为:54微秒的低电平+68到74微秒的高电平

树莓派温湿度监控 树莓派温度模块_数据_08

e、步骤五:

结束信号,DHT11的DATA引脚输出40位数据后,继续输出54微秒的低电平后转换为输入状态。

二、DHT11接入树莓派4B

刚买回来的DHT11模块集成在一块板子上,提供三个引脚:VCC、DATA和GND。很明显,VCC接3.3V电源,GND接地,DATA引脚接入GPIO口,从树莓派4b的GPIO引脚图。我们把VCC接1脚,DATA接7脚,GND接9脚,其中DATA脚对应GPIO.7(BCM为4,WiringPi为7)。

树莓派温湿度监控 树莓派温度模块_引脚_09

树莓派温湿度监控 树莓派温度模块_python_10

三、DHT11读取数据

1、通过系统功能读取

树莓派有一个Device Tree(DT)功能,即在/boot/overlays发现外设,并加载到系统上去,其操作只要在/boot/config.txt进行对应的外设配置,重启后,就可以在/sys/devices下找对对应的外设。
修改/boot/config.txt

dtparam=i2c_arm=on
dtparam=spi=on
dtoverlay=dht11

重启

reboot

采集数据

DHT11Path="/sys/devices/platform/dht11@4/iio:device0"
HumidityRelative=$(awk '{print $1 / 1000 }' ${DHT11Path}/in_humidityrelative_input)
RoomTemp=$(awk '{print $1 / 1000 }' ${DHT11Path}/in_temp_input)

这个方法经常因为1-write协议本身的缘故,1bit数据丢失,就会导致数据无法读出。
解决方法只能通过多次读值,取正常值。

2、通过Adafruit库读取

更新源软件包

apt update
apt upgrade
apt install build-essential python-dev python3-dev python-pip python3-pip

获取Adafruit库

apt install git
sudo git clone https://github.com/adafruit/Adafruit_Python_DHT.git

修改Adafruit对树莓派4B的支持

vi Adafruit_Python_DHT/Adafruit_DHT/platform_detect.py
#在elif match.group(1) == 'BCM2837': > #Pi 3b+ >return 3后面加入新的分支
elif match.group(1) == 'BCM2711':
	#Pi 4B
	return 3

安装Adafurit库

python setup.py install
python3 setup.py install

执行demo获取温湿度

#11代表DHT11,4代表gpio4
python AdafruitDHT.py 11 4
#或
python3 AdafruitDHT.py 11 4

3、通过C编程读取

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>

#include <wiringPi.h>

typedef unsigned char uint8;
typedef unsigned int  uint16;
typedef unsigned long uint32;

uint32 databuf;

/*
//初始化引脚
//上电1s内不操作,维持电平稳定
*/
void GPIO_init(int gpio_pin)
{
	pinMode(gpio_pin, OUTPUT); // set mode to output
	digitalWrite(gpio_pin, 1); // output a high level
	
    sleep(1);
	
	return;
}

/*
//起始信号
	1.主机初状态是高电平,要超过1s稳定电平
	2.主机再拉低延时18ms-30ms
	3.主机末状态是高电平,等待
*/
void DHT11_start(int gpio_pin)
{	
	pinMode(gpio_pin, OUTPUT);
	digitalWrite(gpio_pin, 0);
	
	delay(25);
	
	digitalWrite(gpio_pin, 1);
	pinMode(gpio_pin, INPUT);
	pullUpDnControl(gpio_pin, PUD_UP);	//当引脚被配置为输入(INPUT)模式,使用函数pullUpDnControl来激活其内部的上拉电阻或下拉电阻
	
	delayMicroseconds(27);
	
	return;
}

/*
//主机接受数据
	1.主机接受到从机回复的响应信号
	2.格式0——54us的低电平+23到27us的高电平
	  格式1——54us的低电平+68到74us的高电平
	3.思路:从识别到低电平开始,然后去除掉掉前面54秒的低电平还有
*/
uint8 DHT11_read(int gpio_pin)
{
	uint8 crc, i;
	
	if (0 == digitalRead(gpio_pin))			//主机接收到从机发送的响应信号(低电平)
	{
		while(!digitalRead(gpio_pin));		//主机接收到从机发送的响应信号(高电平)
		
		for (i = 0; i < 32; i++)
		{
			while(digitalRead(gpio_pin));	//数据位开始的54us低电平
			while(!digitalRead(gpio_pin));	//数据位开始的高电平就开始
			
			delayMicroseconds(32);			//跳过位数据,32us已经是数据0和数据1的差距点
			
			databuf *= 2;
			
			if (digitalRead(gpio_pin) == 1)
			{
				databuf++;
			}
		}
		
		for (i = 0; i < 8; i++)
        {
            while (digitalRead(gpio_pin));
            while (!digitalRead(gpio_pin));
			
            delayMicroseconds(32);
			
            crc *= 2;  
            if (digitalRead(gpio_pin) == 1)
            {
                crc++;
            }
        }
		return 1;
	}
	else
	{
		return 0;
	}
}

int main(int argc, char *argv[])
{
	if (2 != argc)
	{
		printf("Usge: %s <gpio_pin>\n", argv[0]);
		return 0;
	}
	
	if (-1 == wiringPiSetup())
	{
		printf("Setup WiringPi failed!\n");
		
		return 1;
	}
	
	while(1)
	{
		GPIO_init(atoi(argv[1]));
		
		DHT11_start(atoi(argv[1]));
	 
		if (DHT11_read(atoi(argv[1])))
		{
			printf("RH:%d.%d\n", (databuf >> 24) & 0xff, (databuf >> 16) & 0xff); 
			printf("TMP:%d.%d\n", (databuf >> 8) & 0xff, databuf & 0xff);
		
			databuf = 0;
		}
		else
        {
            printf("Sensor dosent ans!\n");
            databuf = 0;
        }
		
		sleep(3);
	}
	
	return 0;
}

编译执行
这里要用sudo权限,并且引脚值要对应好,GPIO-7的Wiring值对应7

gcc -o DHT DHT11.c  -lwiringPi
sudo ./DHT 7

4、通过Python编程读取

对Python不熟,暂时无输出。