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

[公開:any]

[電子工作/AVR]
[AVR,ATmega88,SHT11,湿度計,CHS-UGR]

origin 2009-11-13


 以前、「PICを使った温度計・湿度計の製作」で作成したものは、パソコンのUSB端子から給電するようにしました。パソコンを使わない所でも、ACアダプタ等から給電して使用できるようにDCコネクタと3端子レギュレータを接続して改造したところ、電源の逆接続をやらかしました。3端子レギュレータや、PICは問題なく動作しますが、どうも湿度が低めに測定されます。
 CHS-UGRを基板から取り外して単体でテストすると実際の湿度の1/3程度の測定値しか得られません。(このときの実際の湿度は60%前後ある。)息を吹きかけたりすると即座に反応して湿度が上がるので完全に故障というわけではありませんが、このままでは正確な湿度がわかりません。

故障した湿度センサーCHS-UGR

 CHS-UGRは、1個2000円程度と高価なので同じような値段で、温度センサーと湿度センサーが一体化されたSensirion社製のSHT11をストロベリー・リナックスさんから通販で購入しました。

湿度センサーSHT11

 特殊なパッケージなので、変換基板もサービスで付属しています。思ってたより小さく、センサーそのものは、比較においたTO-92パッケージのトランジスタよりも小型です。精度は、温度で±0.4℃、湿度は±3.0%と高精度です。また分解能も温度が0.01℃、湿度が0.03%と普通に使うのは十分すぎる性能を持っています。

 センサーは、I2Cという規格化されたシリアル通信で温度や湿度のデータを読み出すことができると一部Webサイトに書いてありますが、データシートを読むと"not compatible with I2C interfaces・・・・"とあり、実際I2Cとは異なっています。
 ということで、シリアル通信部分はスクラッチで作る必要があります。でもWebを検索するとエレキジャックさんでかなり前に特集されたようでプロトコルから実際のコーディングまで詳しく解説されています。ただ、PIC用のソースプログラムなので、参考にしながらAVRで使えるように作成しなおしました。

湿度センサーSHT11のプログラム開発

 湿度データの測定コマンドをオシロスコープでキャプチャしました。CH1がDATAでCH2がSCKです。(下半分は、上半分のズーム画面となっています。)
 "Transmission Start"シーケンスに続いてアドレス"000"(固定)と温度測定コードの"00101"を連続して送出します。

SHT11制御シーケンス

 SHT11から帰ってくる湿度データです。最後の12bitの湿度データと8bitのCRCチェックサムが帰ってきます。

SHT11からのシリアル信号

 シリアル通信のプログラミングは、LCDを表示に使ったのですが、実際の製作は、遠距離からの視認性を重視して7セグメントLEDを表示に使用します。この部分のソースプログラムは、「AVRを使った温度計・湿度計の製作」からそのまま持ってきました。

SHT11温度計・湿度計開発

 回路図です。これも以前作ったものを使いまわしています。

SHT11温度計・湿度計回路図

 基板の実装図です。これも使いまわせる部分は、そのままにしています。

SHT11温度計・湿度計部品実装図

 製作して、ブレッドボードで作成した時のプログラムで走らせるとLEDの表示が滅茶苦茶になります。ブレッドボードでは、接続の自由度があるため、AVRのポートを順番に使用していたのですが、実装では配線の取り回しの都合上、ポートの順番が異なってしまうためです。AVRライターを接続して正しい表示になるようにデバッグします。

SHT11温度計・湿度計基板裏面SHT11温度計・湿度計基板表面

 ケースも以前のものを使いまわします。100円ショップで購入した写真立てです。適当なスペーサーで2枚のプラフレームと基板を固定します。上下左右はオープンなので通気性も十分です。

SHT11温度計・湿度計1SHT11温度計・湿度計2

 無調整で正確な温度と湿度が測定できるので便利です。Webサイトには、測定間隔が早すぎると自身の発熱で温度が高めに測定されるとあります。とりあえず1秒間隔としていますが、500ms間隔でも値は大きく異なりませんでした。SHT11内部でのAD変換等の処理時間があるので温度、湿度を連続して読み出す場合、間隔が短いと正常な通信ができなくなることもあるようです。

 参考までにソースプログラムを示します。7セグメントLEDは、Timer0により約2ms間隔でダイナミック点灯させています。最初は、SHT11との通信タイミングをTimer1の1秒間隔でやってみたのですが、ダイナミック点灯のため多重割り込みを許可するとSHT11との通信が固まってしまう現象が低頻度ながら発生しました。しかたなくメインループの中で1秒のディレイをつかってSHT11と通信するようにしていますが、ブレッドボードの環境では、ごく稀にリセットのタイミングで測定ができなくなることがありました。基板化してから何度もリセットしてみたのですが症状は発生していません。

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
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
/**
* SHT-11 temperature and humidity meter
*  2009-11-22 気温と湿度の四捨五入計算を修正
*  2011-07-23 sht-11からの温度、湿度データ取得分割+相互1秒間隔に変更
*  2011-07-24 湿度の計算修正(thanks yukki)
*/
#include <avr/io.h>
#include <util/delay.h>
#include <avr/pgmspace.h>
#include <avr/interrupt.h>
#include <stdio.h>
#include <stdlib.h>
#include <math.h>

unsigned int seg[6];
unsigned int mx;
double temp, humd;

#define cbi(addr, bit)     addr &= ~(1<<bit)
#define sbi(addr, bit)     addr |=  (1<<bit)
#define nop() asm volatile("nop\n\t"::)
#define dataGet()    bit_is_set(PINC, PINC5)

void dataLo(void)    { sbi(DDRC, DDC5); nop(); }
void dataHi(void)     { cbi(DDRC, DDC5); nop(); }
void sckLo(void)     { cbi(PORTC, PORTC4); nop(); }
void sckHi(void)     { sbi(PORTC, PORTC4); nop(); }

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

// SHT Transmission Start
void sht_TS()
{
    dataHi();
    sckHi();
    dataLo();
    sckLo();
    sckHi();
    dataHi();
    sckLo();
}

// SHT Data Send
unsigned char sht_PUT(unsigned int data)
{
    unsigned int i;
    unsigned char rs;

    for(i = 0; i < 8; i++) {
        sckLo();
        if(data << i & 0x80)
            dataHi();
        else
            dataLo();
        sckHi();
    }
    sckLo();

    dataHi();
    sckHi();

    if(dataGet())
        rs = 1;
    else
        rs = 0;

    sckLo();

    return rs;
}

// SHT Data receive
unsigned char sht_GET(unsigned char ack)
{
    unsigned int i;
    unsigned char rs;

    rs = 0;
    dataHi();

    for(i = 0; i < 8; i++) {
        rs <<= 1;
        sckHi();
        if(dataGet())
            rs |= 1;
        sckLo();
    }

    if(!ack)
        dataLo();
    else
        dataHi();

    sckHi();
    sckLo();

    dataHi();

    return rs;
}

// SHT-11 temparature data get
void sht_temp(double *tmp)
{
    unsigned char datL, datH;
    unsigned int datW;

    sht_TS();
    sht_PUT(0x03);
    while(dataGet());
    datH = sht_GET(0);
    datL = sht_GET(0);
    sht_GET(1);

    datW = (unsigned int)datH << 8 | datL; 

    *tmp = -40.0 + 0.01 * datW;
}

// SHT-11 humidity data get
void sht_humd(double *hmd)
{
    unsigned char datL, datH;
    unsigned int datW;
    double hm;

    sht_TS();
    sht_PUT(0x05);
    while(dataGet());
    datH = sht_GET(0);
    datL = sht_GET(0);
    sht_GET(1);

    datW = (unsigned int)datH << 8 | datL; 

    hm = -4.0 + 0.0405 * datW - (2.8 * 0.000001 * datW * datW);
    // calc temperature coefficient
    *hmd = (temp - 25.0) * (0.01 + 0.00008 * datW) + hm;
}


// SHT initialized
void sht_init()
{
//    sht_PUT(0x1e);    // SHT Soft reset
    delay_ms(15);
    dataHi();
}

// Returns mask 7-seg. display
unsigned int mask(unsigned int num) {
      switch (num) {
        case 0 : return 0xfa;
        case 1 : return 0x42;
        case 2 : return 0x79;
        case 3 : return 0x6b;
        case 4 : return 0xc3;
        case 5 : return 0xab;
        case 6 : return 0xbb;
        case 7 : return 0x62;
        case 8 : return 0xfb;
        case 9 : return 0xeb;
        case 10 : return 0x01;  // -
        case 11 : return 0xb9;    // E
        case 12 : return 0x11;    // r
        case 99 : return 0x00;     // space
        default: return 0x10;
    }
}

// Timer0 Overflow interrupt
ISR(TIMER0_OVF_vect)
{
    PORTB = seg[mx];
    switch(mx) {
        case 0: PORTD = 0x01; break;
        case 1: PORTD = 0x04; PORTB |= _BV(PORTB2); break;    // with DP.
        case 2: PORTD = 0x02; break;
        case 3: PORTD = 0x20; break;
        case 4: PORTD = 0x08; PORTB |= _BV(PORTB2); break;    // with DP.
        case 5: PORTD = 0x10; break;
    }
    mx++;
    if(mx > 5)
        mx = 0;
}

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

     DDRB = 0b11111111;        // 7SegLED Segment Control
    DDRC = 0b11111111;        // PC5 SHT-data PC4 SHT-sck Control
    DDRD = 0b11111111;        // 7SegLED Multiplex Control
    PORTC = 0b00000000;

    TCCR0B = 0b00000011;    // Timer0 prescaler 1/64 
    TIMSK0 = _BV(TOIE0);     // Timer0 Overflow Interrupt Enable

    sht_init();
    sht_temp(&temp);
    delay_ms(1000);
    sht_humd(&humd);
    rt = 0;

    sei();

    while(1) {
        tem = (int)(temp * 10.0 + 0.5);
        hum = (int)(humd * 10.0 + 0.5);
        if(tem > 999 || tem < -99) {
            seg[3] = mask(11);
            seg[4] = mask(12);
            seg[5] = mask(12);
        } else {
            if(tem < 0) {
                seg[3] = mask(10);
            } else {
                i = (tem / 100) % 10;
                if(i == 0)
                    i = 99;
                seg[3] = mask(i);
            }
            i = abs((tem / 10) % 10);
            seg[4] = mask(i);
            i = abs(tem % 10);
            seg[5] = mask(i);
        }
        if(hum > 999 || hum < 0) {
            seg[0] = mask(11);
            seg[1] = mask(12);
            seg[2] = mask(12);
        } else {
            i = (hum / 100) % 10;
            if(i == 0)
                i = 99;
            seg[0] = mask(i);
            i = (hum / 10) % 10;
            seg[1] = mask(i);
            i = hum % 10;
            seg[2] = mask(i);
        }
        if(rt == 0) {
            sht_temp(&temp);
            rt = 1;
        } else {
            sht_humd(&humd);
            rt = 0;
        }
        delay_ms(1000);
    }
}

▲ページ Top へ...