Table of contents
I. Introduction
I recently wrote DHT11 code to Orange Pi (Allwinner H616), and found that most of the online cases are Raspberry Pi + DHT11, and there are few Orange Pi. The few found codes are unstable or the data is relatively Not very accurate, so I wrote an article here for your reference and criticism
product description
DHT11 digital temperature and humidity sensor is a temperature and humidity composite sensor with calibrated digital signal output. Application fields: HVAC; automobile; consumer goods; weather station; humidity regulator; dehumidifier; home appliance; medical treatment; automatic control
features
- Relative Humidity and Temperature Measurement
- Fully calibrated, digital output
- long-term stability
- Ultra-long signal transmission distance: 20 meters
- Ultra-low power consumption: sleep
- 4-pin installation: can buy packaged
- Completely interchangeable: get the result directly without conversion
data transfer logic
There is only one data line, DATA, and the main control MCU sends sequence commands to the DHT11 module. The complete data transmission of the module is 40bit, high bit first out
Data Format
8bit humidity integer data+8bit humidity decimal data+8bi temperature integer data+8bit temperature decimal data+8bit checksum
About verification : Assume that the received 40-bit data is:
Calculation:
0011 0101 + 0000 0000 + 0001 1000 + 0000 0100 = 0101 0001 (check digit)
DHT11 communication sequence
(1) The host sends the start signal.
First, the microcontroller will output a low level to the GPIO port connected to the DHT11 DATA pin, and the low level should not be kept for less than 18ms (t1) can not exceed 30ms at most, and then pull the data line high for 20~40us (t2), waiting to read the response signal of DHT11.
(2) Detect
the DATA pin of the slave response signal DHT11 detects that the external signal has a low level (t1), and wait for the end of the external low level signal (t2). After the delay, the DATA pin of the DHT11 is in the output state, and then the DHT11 Start to output 80us (t3) low level as a response signal, and then output 80us (t4) high level to inform the host to prepare to receive data.
The I/O of the host is in the input state at this time. After detecting the low level of the I/O (DHT11 response signal), wait for the high level of 80us to receive the data.
(3) Data transmission
DHT11's DATA pin outputs 40-bit data, using MSB first, and the microprocessor receives 40-bit data according to the change of I/0 level.
The format of bit data "0" is: low level of 50 microseconds and high level of 26-28us.
The format of the bit data "1" is: 50 microseconds low level plus 70us high level.
Two, the code
The main code mainly uses multi-threading. Every time the user sends a data reading request, a thread is created for reading data; it is beneficial to improve the robustness and scalability of the code;
At the same time, a blockFlag flag is introduced to avoid the problem that the sub-thread code runs away and cannot exit;
And during the test, it is found that there are occasional data with obvious temperature errors; considering that the data may be inaccurate due to the environment, sensors, delay errors, etc., the program will regard data exceeding 50°C as Invalid data, retest by yourself, up to 5 times by yourself
GPIO initialization
Because the initial signal sent to DHT11 is to pull down the level first, so maintain a stable high level state before pulling down the level
void GPIO_init(int gpio_pin)
{
pinMode(gpio_pin, OUTPUT); // set mode to output
digitalWrite(gpio_pin, 1); // output a high level
delay(1000);
}
start signal
Because the trigger of DHT11 is single, that is, the temperature and humidity will be checked every time the initial signal is sent, so the code for sending the initial signal must be multiplexed, so it is also encapsulated into a function here
void DHT11_Start_Sig()
{
pinMode(pinNumber,OUTPUT); //让GPIO为输出模式
digitalWrite(pinNumber,HIGH);
digitalWrite(pinNumber,LOW);
delay(25); //维持25ms的低电平
digitalWrite(pinNumber,HIGH); //转化为高电平,等待DHT11的响应信号
pinMode(pinNumber,INPUT);
//响应信号为80us电平与80us的准备信号
pullUpDnControl(pinNumber,PUD_UP); //进行上拉,增加稳定性,非必选
delayMicroseconds(35); //维持35微秒
}
read data
void* readSensorData(void *arg)
{
uint8 crc;
uint8 i;
int attempt = 5; //调用一次最多尝试测5次
while(attempt)
{
databuf = 0; //清空数据存储buf
crc = 0; //清空校验位数据存储buf
DHT11_Start_Sig();
if(digitalRead(pinNumber)==0) //检测DHT11是否应答,应答则继续下一步
{
while(!digitalRead(pinNumber)); //wait to high
//读取4个数据,合计32位
for(i=0;i<32;i++)
{
while(digitalRead(pinNumber)); //data clock start
while(!digitalRead(pinNumber)); //data start
delayMicroseconds(HIGH_TIME); //如果32微秒后,仍然检测到是高电平,则该数据位为1
databuf*=2; //移位到buf的更高位
if(digitalRead(pinNumber)==1) //1
{
databuf++;
}
}
//读取校验位
for(i=0;i<8;i++)
{
while(digitalRead(pinNumber)); //data clock start
while(!digitalRead(pinNumber)); //data start
delayMicroseconds(HIGH_TIME);
crc*=2;
if(digitalRead(pinNumber)==1) //1
{
crc++;
}
}
//用于校验数据的准确性,当温度大于50时,视为无效数据
if(((databuf>>8)&0xff) > 50)
{
attempt--;
delay(500); //不加这段延迟,下一次传感器来不及响应
continue;
}
else
{
//打印数据
printf("Congratulations ! Sensor data read ok!\n");
printf("RH:%lu.%lu\n",(databuf>>24)&0xff,(databuf>>16)&0xff);
printf("TMP:%lu.%lu\n",(databuf>>8)&0xff,databuf&0xff);
blockFlag = 0; //用来避免程序有时候跑飞,卡在此函数中,无法跳出
return (void*)1;
}
}
else //dht not answer
{
blockFlag = 0;
printf("Sorry! Sensor dosent ans!\n");
return (void*)0;
}
}
//如果代码执行到这里,则证明尝试读取了5次数据,都是不准确的数据
blockFlag = 0;
printf("get data fail\n");
return (void*)2;
}
overall code
#include <wiringPi.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
#include "contrlDevices.h"
typedef unsigned char uint8;
typedef unsigned int uint16;
typedef unsigned long uint32;
#define HIGH_TIME 32
int pinNumber = 6; //use gpio1 to read data
uint32 databuf;
int blockFlag;
void GPIO_init(int gpio_pin)
{
pinMode(gpio_pin, OUTPUT); // set mode to output
digitalWrite(gpio_pin, 1); // output a high level
delay(1000);
//return;
}
void DHT11_Start_Sig()
{
pinMode(pinNumber,OUTPUT);
digitalWrite(pinNumber,HIGH);
digitalWrite(pinNumber,LOW);
delay(25);
digitalWrite(pinNumber,HIGH);
pinMode(pinNumber,INPUT);
//响应信号为80us电平与80us的准备信号
pullUpDnControl(pinNumber,PUD_UP);
delayMicroseconds(35);
}
void* readSensorData(void *arg)
{
uint8 crc;
uint8 i;
int attempt = 5; //调用一次最多尝试测5次
while(attempt)
{
databuf = 0; //清空数据存储buf
crc = 0; //清空校验位数据存储buf
DHT11_Start_Sig();
if(digitalRead(pinNumber)==0) //检测DHT11是否应答,应答则继续下一步
{
while(!digitalRead(pinNumber)); //wait to high
//读取4个数据,合计32位
for(i=0;i<32;i++)
{
while(digitalRead(pinNumber)); //data clock start
while(!digitalRead(pinNumber)); //data start
delayMicroseconds(HIGH_TIME); //如果32微秒后,仍然检测到是高电平,则该数据位为1
databuf*=2; //移位到buf的更高位
if(digitalRead(pinNumber)==1) //1
{
databuf++;
}
}
//读取校验位
for(i=0;i<8;i++)
{
while(digitalRead(pinNumber)); //data clock start
while(!digitalRead(pinNumber)); //data start
delayMicroseconds(HIGH_TIME);
crc*=2;
if(digitalRead(pinNumber)==1) //1
{
crc++;
}
}
//用于校验数据的准确性,当温度大于50时,视为无效数据
if(((databuf>>8)&0xff) > 50)
{
attempt--;
delay(500); //不加这段延迟,下一次传感器来不及响应
continue;
}
else
{
//打印数据
printf("Congratulations ! Sensor data read ok!\n");
printf("RH:%lu.%lu\n",(databuf>>24)&0xff,(databuf>>16)&0xff);
printf("TMP:%lu.%lu\n",(databuf>>8)&0xff,databuf&0xff);
blockFlag = 0; //用来避免程序有时候跑飞,卡在此函数中,无法跳出
return (void*)1;
}
}
else //dht not answer
{
blockFlag = 0;
printf("Sorry! Sensor dosent ans!\n");
return (void*)0;
}
}
//如果代码执行到这里,则证明尝试读取了5次数据,都是不准确的数据
blockFlag = 0;
printf("get data fail\n");
return (void*)2;
}
int main ()
{
pthread_t tid;
int waitTimes = 10;
char cmd[5] = {
'\0'};
if (wiringPiSetup() == -1)
{
printf("Setup wiringPi failed!");
return 1;
}
printf("Enter OS-------\n");
while(1)
{
waitTimes = 10;
blockFlag = 1;
delay(1000);
printf("input y\n");
scanf("%s",cmd);
getchar();
if(strcmp(cmd,"y") == 0)
{
//创建一个线程用于读取传感器数据;
//严谨来说此处的tid并发时有bug;读者可以自行优化,可以用互斥锁或者tid设为数组等都行
//就当作留给读者的一个小作业吧
if (pthread_create(&tid, NULL, readSensorData, NULL) != 0)
{
printf("thread create fail!\n");
return -1;
}
//等待数据读取线程10s钟,如果10后blockFlag未置0,则说明读数据时跑飞卡住了
while(waitTimes && blockFlag)
{
delay(1000);
waitTimes--;
}
//强行结束跑飞的线程
if(blockFlag == 1)
{
pthread_cancel(tid);
printf("线程超时退出.....\n");
}
}
else
{
printf("go on\n");
continue;
}
}
return 0;
}
Results of the
Reference article:
STM32 first-line protocol - DHT11 temperature and humidity sensor sampling implementation