Embedded System/ATmega128

[ATmega128] DHT11(온습도 센서) 제어

전두선 2019. 12. 2. 12:48

DHT11 Humidity & Temperature Sensor


  • single-wire serial interface(양방향)
  • temperature & humidity sensor
  • 3~5.5V DC

DHT11은 single-wire interface로 구성되어 있는 온습도 센서이다. 정전식 습도 센서와 써미스터(thermistor)를 사용하여 대기온도를 측정하고 측정값을 디지털 센서 신호로 출력한다. 정전식 습도 센서는 습도에 따라 저항 값이 변하며, 써미스터는 온도에 따라 저항값이 변하는 소자이다.

그리고 이 온습도 센서 모듈은 부가적인 회로나 ADC 기능이 필요하지 않다. 원래는 아날로그 온도 및 습도 센서를 사용하여 MCU에 응용하려면 부가적인 회로ADC가 요구되지만 DHT11에는 센서 내부에서 신호증폭, ADC, 보정, 통신 등의 회로를 내장하기때문에 사용자는 센서 내부에서 계산된 온습도 데이터를 시리얼 통신으로 받아오기만 하면 된다.

 

DHT11 module

DHT11 센서로 측정가능한 온습도 범위 및 오차는 다음과 같다.

DHT11 Technical Specifications

참고로 습도의 단위 RH는 Relative Humidity로 상대 습도를 말한다. 즉 대기 중에 포함되어있는 수증기량과 측정 당시의 온도에서 포함할 수 있는 최대의 수증기량과의 비를 백분율로 표시한 것이다.

온도의 단위 섭씨 ℃는 얼음의 녹는점 0℃도와 물이 끓는점 100℃로하여 100등분한 단위이며, 화씨 ℉는 얼음이 녹는점을 32℉, 물이 끓는점을 212℉로하여 그 사이를 등분한 단위이다.


MCU - DHT11

만약 데이터 선의 길이가 20 미터보다 짧은경우, 5k의 pull-up 저항이 권장되고, 데이터 선의 길이가 20 미터보다 긴 경우에는 적절한 pull-up 저항이 필요하다. (DHT11 모듈의 경우에는 pull-up 저항이 달려있다.)

 

Communication Process: Serial Interface (Single-Wire Two-Way)


위의 MCU와 DHT11의 연결그림을 보면 하나의 데이터선으로 데이터를 주고받는 모습을 볼 수가 있다. 즉, 하나의 선으로 MCU와 DHT11 센서간의 통신 및 동기화를 진행하며, 하나의 통신 과정은 약 4ms가 소모된다고 데이터 시트에 나와있다.

DHT11 Data Format

데이터는 integral 부분과 decimal 부분으로 나뉘어진다. 위 사진에 나타낸 data format을 보면 RHIntegral부터 Checksum까지 각각 1byte씩 5단계로 나뉘어져있는 모습을 볼 수있고, 총 5byte = 50bit로 구성된다.

(Data Format : 8bit integral RH data + 8bit decimal RH data + 8bit integral T data + 8bit decimal T data + 8bit check sum.)

 

DHT11에서 보낸 데이터 전송이 정상적으로 MCU에 도착한다면 오류를 체크하는 8bit 체크썸(Checksum)은 수신받은 "8bit integral RH data + 8bit decimal RH data + 8bit integral T data + 8bit decimal T data"와 같아야한다.

 

체크썸 예제 :

 위와 같은 데이터 40bit를 받았을 때, 체크썸은 아래와 같이 계산된다.

Checksum = "8bit integral RH data + 8bit decimal RH data + 8bit integral T data + 8bit decimal T data" = 0011 0101 + 0000 0000 + 0001 1000 + 0000 0000 = 0100 1101.

 

계산된 Checksum(0100 1101) = 전송 받은 Parity bit(0100 1101)이기때문에 데이터 전송이 정상적으로 이루어졌다고 판단하게 된다. 

-> 습도 : 0011 0101 . 0000 0000 = 53.0 %

-> 온도 : 0001 1000 . 0000 0000 = 24 'C

 

만약 이렇게 계산된 Checksum이랑 전송받은 마지막 8bit인 Parity bit(= checksum)이 다르다면 해당 전송은 error가 있다고 판단하고 해당 데이터를 버리고 재전송을 요청해야한다.

 

이러한 통신 모듈을 사용할때 가장 중요한 부분은 역시나 타이밍 다이어그램이다. 

 

DHT11 Communication Timing Diagram

1. MCU에서 start 신호를 DHT11에 보냈을 때, DHT11은 low-power-consumption mode(저전력 소비 모드)에서 running mode(동작 모드)로 변경되고, MCU가 start 신호 송신을 완료할 때 까지 기다린다.

 

2. DHT11은 MCU로 위에서 언급한 포맷의 40 bit 데이터의 응답신호를 전송한다. 

(1번 과정에서 MCU가 start 신호를 보내지 않았다면, DHT11은 응답신호 데이터를 전송하지 않는다.)

 

3. MCU는 DHT11이 전송한 40 bit의 데이터를 수신하고, 이를 완료하면 DHT11은 다시 MCU로부터 start 신호를 수신할 때까지 저전력 소비모드로 변경된다.

위 1-3번까지의 과정이 MCU와 DHT11간의 전체적인 통신과정을 간략하게 나타낸 것이다.

 

이를 소스코드로 구현하기 위해서는 각각의 신호가 어떤 구성을 이루고 있는지 다뤄야한다. 아래부터는 위 전체 과정에서 각각의 신호들이 어떤 구성을 이루고 있는지 확대해서 나타낸 그림들이다.

 

MCU Sends out Start Signal & DHT11 Responses

  • 데이터 선이 사용되지 않을때(자유로운 상태, free status)는 high-level voltage를 유지한다. MCU와 DHT11 사이의 통신이 시작되면 MCU는 데이터 선의 신호 레벨을 high에서 low으로 설정하며, 이 과정은 최소 18ms가 소요되어야만 MCU의 신호를 DHT11가 감지할 수 있으며, 그 후 MCU는 전압을 끌어올려(high) DHT11의 응답을 20-40us 기다려야한다.
  • DHT11이 start 신호를 감지하면, low-level voltage의 응답신호를 80us동안 전송한다.(즉, 데이터 선이 low-level voltage일때는 DHT11이 응답 신호를 보내고 있다는 것을 의미한다) 그런다음 DHT11은 데이터 선의 신호 레벨을 low에서 high로 설정하고, 데이터 전송 준비를 위해 80us 동안 해당 상태를 유지한다.
  • DHT11이 MCU로 데이터를 전송할 때, 모든 데이터의 비트는 50us의 low-level voltage에서 시작하고 다음 high-level voltage의 길이에 따라 데이터 비트가 "0"인지 "1"인지 결정한다.(아래 그림 참고)

데이터의 비트가 "0" 인지 "1" 인지 결정

 

  • high-level voltage가 26~28us이면 "0", "70us"이면 "1"을 의미한다.
  • 마지막 데이터의 비트가 전송되면 DHT11은 전압 레벨을 low로 하여 50us 동안 유지합니다. 그런다음 저항에 의해 데이터 선이 풀업되어 다시 자유로운 상태(free status)로 설정됩니다.

ATmega128를 이용하여 DHT11 제어(using Atmel Studio)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
 
#include <stdio.h>
#include <string.h>
#include <avr/io.h>
#include <util/delay.h>
 
#define DHT11_ERROR 255
 
#define DHT11_DDR DDRF
#define DHT11_PORT PORTF
#define DHT11_PIN PINF
#define DHT11_INPUTPIN PF1
 
void dht11_getdata(uint8_t num, uint8_t *data);
uint8_t getdata(uint8_t select);
 
/* get data from dht11 */
uint8_t getdata(uint8_t select) {
    uint8_t bits[5];
    uint8_t i,j = 0;
 
    memset(bits, 0sizeof(bits));
 
    //reset port
    DHT11_DDR |= (1<<DHT11_INPUTPIN); //output
    DHT11_PORT |= (1<<DHT11_INPUTPIN); //high
    _delay_ms(100);
 
    //send request
    DHT11_PORT &= ~(1<<DHT11_INPUTPIN); //low
    _delay_ms(18);
    DHT11_PORT |= (1<<DHT11_INPUTPIN); //high
    _delay_us(1);
    DHT11_DDR &= ~(1<<DHT11_INPUTPIN); //input
    _delay_us(39);
 
    //check start condition 1
    if((DHT11_PIN & (1<<DHT11_INPUTPIN))) {
        return DHT11_ERROR;
    }
    _delay_us(80);
    //check start condition 2
    if(!(DHT11_PIN & (1<<DHT11_INPUTPIN))) {
        return DHT11_ERROR;
    }
    _delay_us(80);
 
    //read the data
    for (j=0; j<5; j++) { //read 5 byte
        uint8_t result=0;
        for(i=0; i<8; i++) {//read every bit
            while(!(DHT11_PIN & (1<<DHT11_INPUTPIN))); //wait for an high input
            _delay_us(30);
            if(DHT11_PIN & (1<<DHT11_INPUTPIN)) //if input is high after 30 us, get result
                result |= (1<<(7-i));
            while(DHT11_PIN & (1<<DHT11_INPUTPIN)); //wait until input get low
        }
        bits[j] = result;
    }
 
    //reset port
    DHT11_DDR |= (1<<DHT11_INPUTPIN); //output
    DHT11_PORT |= (1<<DHT11_INPUTPIN); //low
    _delay_ms(100);
 
    //check checksum
    if (bits[0+ bits[1+ bits[2+ bits[3== bits[4]) {
        if (select == 0) { //return temperature
            return(bits[2]);
        } else if(select == 1){ //return humidity
            return(bits[0]);
        }
    }
 
    return DHT11_ERROR;
}
 
void dht11_getdata(uint8_t num, uint8_t *data){
    uint8_t buf = getdata(num);
    if(buf == DHT11_ERROR)
       ;
    else
        *data = buf;
}
 
 

위 코드를 활용해서 자신의 디스플레이 환경(ex> CLCD 또는 시리얼 모니터 등)에 띄우시면 됩니다. 코드는 보시면 dht11_getdata 함수를 통해 입력한 num에 값에 따라 온도 또는 습도 값을 받아오도록 구성되어 있습니다. 그러나 이는 온도 값을 한번 받아오고 습도는 버리는 형태 또는 그 반대의 경우가 되기 때문에 필요하신분은 그 부분을 수정하셔서 사용하시면 됩니다.