DHT22を使った温度計・湿度計の製作

[公開:any]

[電子工作/AVR]
[DHT22,RHT03,AVR,ATtiny861A,湿度計,測定器]

origin 2011-07-25


再び、センサーのDHT22が故障しました。顛末を末尾に追記しました。(2013-08-04)

センサーのDHT22が故障しました。顛末を末尾に追記しました。(2012-07-08)


 無駄な冷暖房を防ぐために、部屋ごとに温度計・湿度計を置くことにしました。これまで使用した温度・湿度センサーのSHT11(Sensirion社)は、AVRやPICから使いやすく、測定精度も申し分ないのですが、2000円以上する単価がネックです。いつもSHT11を調達しているストロベリー・リナックスさんのサイトを見ていると別のセンサーがありました。

 温度・湿度センサー DHT22です。中国製激安と紹介があります。値段も1000円以下とSHT11よりはお手頃です。スペックも目的には十分です。・・・さっそく調達してみると外形は同じでRHT03とプリントされたセンサーが届きました。インターネットで調べるとRHT03はMaxdetect社製となっています。入手したデータシート見る限り同じ製品のようです。おそらく社名が変更になって品番も変わったと思われます。

温度・湿度センサーDHT22(RHT03)

 ブレッドボードで開発です。マイコンはAVRのATTiny861Aを使用しました。DHT22は、1-Wire(1線シリアル)インターフェースで通信しますが、一般的な1-Wireのようにアドレスを使って利用するものではなく、ごく単純な手順となっています。表示には室内での視認性を考慮して7セグメントLEDを使用しました。

ブレッドボードでの温度計・湿度計の開発風景

 AVRの1ポートで送信と受信を行います。送信といっても、一旦(1ms以上)ローレベルに落として入力に切り替えるだけでDHT22がレスポンスに続いて40ビットのシリアル列を送信してくるのでAVRでタイミングを計測しながらデコードするだけです。下の画像はオシロスコープでキャプチャしたものですが、DHT22が80usのLOW、続いて80usのHIGHの後に
  00000010 01111110 00000001 00101011 10101100
を送出しています。先頭から2バイトが湿度で638(63.8%)、次の2バイトが温度で299(29.9℃)を示します。最後の1バイトはCRCチェックサムです。

DHT22シリアル信号波形

 回路図です。7セグメントLEDは、ダイナミック点灯方式とします。20ピンのAVRでポート数もちょうどいい感じです。明るい表示になるようにLEDの電流制限抵抗を330Ωと比較的小さな値としたので、ダイナミック点灯とはいえ40~50mAの電流がながれます。電池駆動は厳しいので、ACアダプターで使用することにして3端子レギュレータの電源も搭載します。

DHT22温度計・湿度計回路図

 秋月電子のユニバーサル基板に実装図を作成しました。部品数が少ないので簡単です。なお、この実装図は、回路図と接続が異なっています。実装時には、ジャンパーなどができるだけ少なくしてソフトウェアの変更で対応する予定です。

DHT22温度計・湿度計の基板実装図

 7セグメントLEDは秋月電子で10個150円のGL9A040Gを6個使用します。そのまま基板に実装するとピン数が多く配線が大変なので、あらかじめ加工して共通となるピンの一部にジャンパーを飛ばします。(ショートしないように注意が必要ですが・・・)こうすると基板表と裏で配線できてすっきりとします。

7セグLED GL9A040G

 部品を実装しました。DHT22は、基板上に横向きでネジで固定しました。


 電源を入れると、ブレッドボードでの開発時と接続が違うので表示が不正となります。この状態からソフトウェアを手直しします。


 正常な表示になりました。しばらくエージングしていると、気温がちょっと高いようです。


 同時に購入したDHT22 2個を別のブレッドボード環境でテスト動作させ、センサーの特性のばらつきを見てみます。電源ON時は、3個ともほぼ同じ値を示しますが、しばらくすると製作したものだけ1℃程度、気温が上昇し、湿度が低下します。DHT22の周辺の部品を触っても、とくに熱を持った部品はありません。一番、発熱しそうな3端子レギュレータも低ドロップタイプを使用して、5.8VのACアダプタを使用しているのでほとんど発熱はありません。ただ、いずれにしても基板上の実装方法に問題がありそうです。


 実装方法を変更して他の部品からできるだけ離してみました。これで3個のセンサーともほぼ同じ値となりました。SHT11も敏感なセンサーでしたが、DHT22も侮れません。


 SHT11を使用したものと比較しても気温、湿度とも仕様誤差の範囲内で近似します。十分使えそうです。


 ケースはいつものように100円均一で購入したフォトスタンドです。だだのアクリルパネル2枚なので、スペーサーで固定して周囲はオープンです。これで十分な通気性が確保できるので、気温や湿度の測定には影響ありません。これを壁のフックなどにぶら下げればOKです。


 ソースプログラムです。AVRStudio4とWinAVR(gcc)で作成しました。DHT22の仕様ではデータを取り出すタイミングは最小でも2秒と規定されています。7セグメントLEDのダイナミック点灯にタイマー割り込みを使用していますが、DHT22との通信時に割り込みを停止するため2秒間隔で表示がチラつきます。(じっと見ていなければわかりませんが・・・)。DHT22との通信には5ms程度もかかるのでダイナミック点灯の間隔を広げて調整するしかなさそうです。(それか全部タイマーで処理するか・・・)
 あと、センサーに息を吹きかけたりして大きく値が変化するときに、ごく稀にDHT22との通信でCRCエラーが発生します。・・・が普段は問題ないので追及していません。

 センサーのDHT22は、あと2個あるので、時間を見つけてさらに2台製作する予定です。PICでも作ってみたいと思います。

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
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
#include <avr/io.h>
#include <util/delay.h>
#include <avr/interrupt.h>
#include <stdio.h>
#include <stdlib.h>
#include <math.h>

unsigned int seg[6];
unsigned int mx;

#define cbi(addr, bit)     addr &= ~(1<<bit)
#define sbi(addr, bit)     addr |=  (1<<bit)

// milisecond order delay
void delay_ms(unsigned int t)
{
    while(t--)
        _delay_ms(1);
}

// Returns mask 7-seg. display
unsigned int mask(unsigned int num) {
      switch (num) {
        case 0 : return 0xa0;
        case 1 : return 0xfc;
        case 2 : return 0xc1;
        case 3 : return 0xd0;
        case 4 : return 0x9c;
        case 5 : return 0x92;
        case 6 : return 0x82;
        case 7 : return 0xf8;
        case 8 : return 0x80;
        case 9 : return 0x90;
        case 10 : return 0xdf;  // -
        case 11 : return 0x83;    // E
        case 12 : return 0xcf;    // r
        case 99 : return 0xff;     // space
        default: return 0xfe;
    }
}

// Timer1 Overflow interrupt
ISR(TIMER1_OVF_vect)
{
    TCNT1 = 128;
    PORTA = seg[mx];
    switch(mx) {
        case 0: PORTB = ~_BV(PB6); break;
        case 1: PORTB = ~_BV(PB5); break; 
        case 2: PORTB = ~_BV(PB3); break;
        case 3: PORTB = ~_BV(PB2); break;
        case 4: PORTB = ~_BV(PB1); break; 
        case 5: PORTB = ~_BV(PB0); break;
    }
    mx++;
    if(mx > 5)
        mx = 0;
}

// read 1-wire sensor DHT22(RHT03)
void dht22_read(int *temp, int *humd)
{
    unsigned int i, j;
    unsigned char rs[5];
    unsigned int c;

    cli();

    // Host send start signal
    sbi(DDRA, DDA7);    // PA7 output
    cbi(PORTA, PA7);    // PA7 low
    delay_ms(1);
    // Host pull up and wait for sensor response
    cbi(DDRA, DDA7);    // PA7 input

    _delay_us(20);    // このディレイは最適化できていません。
    loop_until_bit_is_clear(PINA, PINA7);     // wait sensor send out response(20-40us)
    loop_until_bit_is_set(PINA, PINA7);        // wait sensor pull up get ready(80us)
    loop_until_bit_is_clear(PINA, PINA7);    // wait sensor send data(80us)

    // 40bit recive data 
    for(i = 0; i < 5; i++) {
           for(j = 0; j < 8; j++) {
            c = 0;
            loop_until_bit_is_set(PINA, PINA7);
            while(bit_is_set(PINA, PINA7)) {
                c++;
                _delay_us(1);
            }
            rs[i] <<= 1;
            if(c > 40)        // over 40count is "1"
                       rs[i] |= 1;
        }
    }

    sei();

    // check sum and value set
    if((rs[0] + rs[1] + rs[2] + rs[3] & 0xff) == rs[4]) {
        *humd = (rs[0] << 8 | rs[1]);
        *temp = (rs[2] << 8 | rs[3]);        // 室内での利用なので氷点下には対応しない
    } else {
        *humd = 1000;    // エラー表示のための適当な値
        *temp = 1000;
    }
}

int main(void)
{
    unsigned int i;
    int tem, hum;

     DDRA = 0b01111111;        // PA0-6 7seg cathode, PA7 DHT22 1-wire
    DDRB = 0b11111111;        // 7seg Anode control

    TCNT1 = 128;             // Timer1 counter set
    TCCR1A= 0;                // Timer1 standard timer mode                
    TCCR1B= 8;                // Timer1 prescaler 1/128
    TIMSK = _BV(TOIE1);        // Timer1 Overflow Interrupt Enable

    delay_ms(2000);
    sei();

    while(1) {

        dht22_read(&tem, &hum);

        if(tem > 500 || tem < -99) {        // 表示は氷点下9.9℃まで対応している
            seg[0] = mask(11);
            seg[1] = mask(12);
            seg[2] = mask(12);
        } else {
            if(tem < 0) {
                seg[0] = mask(10);
            } else {
                i = (tem / 100) % 10;
                if(i == 0)
                    i = 99;
                seg[0] = mask(i);
            }
            i = abs((tem / 10) % 10);
            seg[1] = mask(i);
            i = abs(tem % 10);
            seg[2] = mask(i);
        }
        if(hum > 999 || hum < 0) {    // 湿度100%はありうるのでこれはマズイな。
            seg[3] = mask(11);
            seg[4] = mask(12);
            seg[5] = mask(12);
        } else {
            i = (hum / 100) % 10;
            if(i == 0)
                i = 99;
            seg[3] = mask(i);
            i = (hum / 10) % 10;
            seg[4] = mask(i);
            i = hum % 10;
            seg[5] = mask(i);
        }
        delay_ms(5000);    // DHT22での測定は5秒間隔
    }
}

DHT22の故障(2012-07-08追記)


 約1年使用したのですが、最近、湿度が高いと99.9%を表示したままとなります。比較的乾燥した日に電源を入れなおすと一時的に正常になりますが、しばらくすると再び99.9%を表示します。なお、気温表示は正常です。


 DHT22とのシリアル通信の部分がテキトーなので、経年変化(・・といっても1年たっていませんが・・)でタイミングがおかしくなったのかと、オシロスコープをつないで見てみました。


 ・・が、DHT22からの湿度データが999(99.9%)となっておりシリアル通信自体に問題はありませんでした。どうやらDHT22が故障したようです。買い置きしてあった新品のDHT22に取り替えて正常となりました。


 たまたま寿命の短い個体だったのかもしれません。温度・湿度センサーとしては価格が安いし、PICやAVRで使いやすいのでお勧めですが、寿命が短いようなら問題です。しばらく使って様子を見たいと思います。

再びDHT22の故障(2013-08-04追記)


 2013年7月頃から湿度の値が常に99%近くを表示するようになりました。約1年前のDHT22の故障と同じ状況です。センサーにSHT11を使用したものと比較すると、気温は、ほぼ同じ値となりますが、湿度は明らかに異常と思われます。


 DHT22を予備のものと交換して正常になりました。


 前回のセンサー故障では、たまたまDHT22の個体が悪かった可能性がありましたが、2回続けて約1年で故障するということは、DHT22の使用方法が悪いのか、または製品の寿命が1年程度ということが考えられます。使用環境は、ごく普通の室内なので問題ないと思います。
 DHT22の電気的仕様は、問題ない範囲で使用しているので、シリアル通信によるデータアクセスの方法が悪いのか、またはデータアクセスの間隔が短すぎることが考えられます。シリアル通信で寿命に影響が出る可能性は少ないと思われますので、データアクセスの間隔を2秒から5秒に変更して様子を見ることにします。


▲ページ Top へ...