我们需要实现一个联网温湿度显示系统,它测量室内的温湿度,将数据传输给服务器;手机(iOS)端通过向服务器发起请求来获取温湿度数据。本文先实现获取DHT11的温湿度数据
什么温湿度传感器好用?DHT11呗,又便宜,性能也没那么不堪。用什么联网?ESP8266呀,教程又多,AT指令又容易上手。最后,我手里有一个Arduino uno的板子,那何不就地取材呢?
DHT11有三个引脚 (图片来自网络,没有任何宣传意图):
不懂的童鞋们,百度一下就知道了,很简单。这个家伙是一个单总线的器件(两电源脚,一个数据脚),这意味这电路很简单,但时序会比较复杂。此处,数据引脚接arduino UNO的8号引脚。不多说,上时序图(图片来自于网络):
那么,DHT11的使用就如上图所示:
1. 单片机(arduino)发送一个低电平(至少持续18ms)
2. 单片机发送一个高电平(持续20-40us)
3. 前两步相当于一个唤醒信号,DHT11如果没坏,会发回一个响应信号。这一步接受响应信号的低电平部分(约80us)
4. 这一步接受响应信号的高电平部分(大约80us)。
5. 好,DHT11发送完响应信号之后,进入数据传输阶段,这一阶段由两部分组成------一个50us的等待部分,一个真正数据传输部分。这一步就是50us的低电平等待部分。
6. 这一步是数据传输部分。DHT11发送的数据是二进制,0和1都由高电平表示,但是持续时间不同。如果单片机引脚收到了26-28us的高电平,则收到了“0”数据;如果接受到70us的高电平,则收到了“1”数据。DHT11发完一位数据,又回到第5步,重复40次,一共发送40位数据。
头文件:
#ifndef _Dht11_H_
#define _Dht11_H_
#include
#define OK 0
#define HIGH_TIMEOUT 1
#define LOW_TIMEOUT 2
#define READ_TIMEOUT 3
#define FAILED_CHECK 4
class dht11
{
public:
int dht11_ReadData(int pin); //读取温湿度原始数据
int getHumidity(); //外部获取湿度
int getTemperature();//外部获取温度
private:
uint8_t humidity; //湿度
uint8_t temperature;// 温度
};
#endif
cpp源文件
#include "dht11.h"
int dht11::dht11_ReadData(int pin)
{
uint8_t res[5];//这里用来放置数据,5个,每一个8位,一共40位
uint8_t count= 7;//每一个数据是8位,一位一位读,所以它用来计数
uint8_t index= 0;//读完一个数据读下一个,所以它是res数组的计数
unsigned int timeCount= 10000; //这个是用来看是否超时的
for (int i=0; i< 5; i++)
res[i] = 0;
//为了防止出现随机数。全部初始化为0.
pinMode(pin, OUTPUT);
//将引脚定义为输出,因为第1、2步是单片机发送低电平和高电平,所以是输出状态
digitalWrite(pin, LOW);
delay(18);//低电平持续18秒(第一步)
digitalWrite(pin, HIGH);
delayMicroseconds(40);//高电平持续40us(第二步)
pinMode(pin, INPUT);
//引脚定义为输入,因为第3,4步是接收DHT11的应答信号,是输入状态
while(digitalRead(pin) == LOW) //(第三步)
if (timeCount-- == 0)
return LOW_TIMEOUT;
//如果timeCount走完了,而引脚还是低电平,说明DHT11的返回信号有问题,因为此时应该输出高电平
timeCount= 10000;
while(digitalRead(pin) == HIGH) //(第四步)
if (timeCount-- == 0)
return HIGH_TIMEOUT;
//和上面的道理一样。
//应答信号结束之后,就是进入读取阶段了
for (int i=0; i<40; i++)
{
timeCount= 10000;
while(digitalRead(pin) == LOW) //第5步,50us的低电平
if (timeCount-- == 0)
return READ_TIMEOUT; //超时,50us之后不管数据位是0是1,都应该是高电平了
unsigned long t = micros();//获取当前时间
timeCount= 10000;
while(digitalRead(pin) == HIGH) //开始读取高电平
if (loopCnt-- == 0)
return READ_TIMEOUT;
if ((micros() - t) > 40) //跳出while循环说明高电平结束,再次获取当前时间,将其与
//之前获取的时间相减,就是高电平持续时间。大于40us,是“1”
res[index] |= (1 << count);//将数据1放置到对应的数据位上。
//数据0不用管,因为已经在程序开始的时候初始化为0了。
if (count== 0) //一个完整的8位数据获取完毕,重新装填计数器
{
count= 7;
index++;
}
else count--;
}
humidity = res[0];
temperature = res[2];
uint8_t sum = res[0] + res[2]; //因为第2位和第4位总是0,不用管他们
if (res[4] != sum)
return FAILED_CHECK;
}
int dht11::getHumidity()
{
return humidity;
}
int dht11::getTemperature()
{
return temperature;
}
#include
#define DHT 8 //引脚8接DHT11的数据引脚
dht11 dht = dht11();
void setup(){
Serial.begin(115200);
}
void loop{
int res = dht.dht11_ReadData(DHT);
int temp = 0, hum = 0;
switch(res){
case 0:
temp = dht.getTemperature();
hum = dht.getHumidity();
Serial.print(temp);
Serial.write(',');
Serial.print(hum);
break;
case 1:
Serial.write("HIGH_TIMEOUT");
break;
case 2:
Serial.write("LOW_TIMEOUT");
break;
case 3:
Serial.write("READ_TIMEOUT");
break;
case 4:
Serial.write("FAILED_CHECK");
}
}
28, 57
温度28,相对湿度57%