アフィリエイト広告
アフィリエイト広告

温湿度センサ DHT11 を使ってみる (2)

前回は、温湿度センサ DHT11 のデータシートを読んでみました。

温湿度センサ DHT11 を使ってみる (1)
ずいぶん前に、他の部品と一緒に買ってあった温湿度センサ DHT11 を使ってみました。 DHT11 は、現在は生産中止になっていて、バージョンアップ品として DHT20 があります。DHT20 は、通信インターフェースに I2C を使用して...

通信インターフェースは独自プロトコルのようですが、そんなに難しい手順ではありません。自前でスケッチを書いて、テストしてみることにします。

回路図

DHT11 を Arduino につなぐ

データシートにしたがって、Arduino につなぎます。

データはシリアルモニタで確認しますので、Arduino の電源も USB からとることにしましょう。センサの電源は、Arduino の 5V ピンから供給します。

DATA ピンは、4.7KΩ で 5V へプルアップします。I/O ピンは、どれでも良いのですが、D7 としました。

スケッチ

DHT11 からデータを読み出し、シリアルモニタに表示させるスケッチです。ライブラリは使っていません。

以下いつものように、備忘録的解説です。

  1. // test DHT11 2022.05.31 by meyon
  2. const byte pinDHT11 = 7;
  3. void setup() {
  4.   Serial.begin(9600);
  5. }

ピン定義とシリアル通信の指定です。

  1. void readDHT11(byte *temp, byte *hmd) {
  2.   const int timeout = 500; // us
  3.   byte startResponse = 0;
  4.   byte detectedValue[5] = {0};
  5.   byte checkDigit = 0;
  6.   pinMode(pinDHT11, OUTPUT);
  7.   delay(20);
  8.   pinMode(pinDHT11, INPUT);
  9.   startResponse = pulseIn(pinDHT11, HIGH, timeout);
  10.   if(startResponse) {
  11.     for(byte i=0; i<5; i++) {
  12.       for(byte j=0; j<8; j++) {
  13.         byte duration = pulseIn(pinDHT11, HIGH, timeout);
  14.         detectedValue[i] *= 2;
  15.         if(54 < duration) detectedValue[i] |= 1;
  16.       }
  17.     }
  18.     for(byte i = 0; i < 4; i++) {
  19.       checkDigit += detectedValue[i];
  20.     }
  21.     if((detectedValue[4] == checkDigit) && checkDigit) {
  22.       *hmd = detectedValue[0];
  23.       *temp = detectedValue[2] + (0x04 < detectedValue[3] ? 1 : 0);
  24.       if(0x7F < detectedValue[3]) *temp = 0;
  25.     }
  26.     for(byte i=0; i<5; i++) {
  27.       Serial.print(detectedValue[i]);
  28.       Serial.print(" ");
  29.     }
  30.   }
  31. }

DHT11 からデータを読み出し、温度と湿度の測定値を返す関数です。

15〜17 行で、起始信号を送ります。
I/O ピンのモードを OUTPUT にすることで、I/O ピンはローインピーダンス (Low) になります。20ms 待って、今度は INPUT に変更します。すると、I/O ピンはハイインピーダンスになり、プルアップされた DATA ラインは High に戻ります。

19 行で响应信号を受け取ります。
もし信号を受け取れなかったとき、pulseIn() はタイムアウトして 0 を返しますので、データ読み出しは行いません (21 行) 。

22〜29 行で、5 バイトのデータを受け取ります。受け取ったデータは 8 ビットごとに detectedValue[] に格納していきます。
ビットが 0 か 1 かの判断は、High の時間が 54μs 以下か超えているかで行なっています (27 行) 。
格納したデータを 2 倍することで、ビットが左へひとつシフトします (26 行) 。ちなみに、ビットを左へ n 個シフトするには、2n を掛けます。へぇ (^_^;) またひとつ賢くなったよ。

31〜33 行は、チェックデジットの計算です。単純な足し算です。

35〜39 行、チェックデジットが合格したとき、かつ、チェックデジットが 0 でないときには、読み出したデータを温度と湿度の値として更新します。
温度について、小数部の値は四捨五入しています (37 行) 。測定精度が ±2℃ ですから、小数点以下の値を表示する意味がありません。また、氷点下の温度は 0 表示することにします (38 行) 。室温を測定することが目的で、氷点下になることはまず、ないです。また、負記号を表示させるために、表示部を一桁増やす価値も、ないです。

41〜44 行で、読み出した各データをシリアルモニタへ送ります。

  1. void loop() {
  2.   static const int interval = 2500; // ms
  3.   static unsigned long previousMillis = 0;
  4.   static byte humidity = 0;
  5.   static byte temperature = 0;
  6.   if(interval < millis() - previousMillis) {
  7.     readDHT11(&temperature, &humidity);
  8.     Serial.print("/ ");
  9.     Serial.print(temperature);
  10.     Serial.print("C ");
  11.     Serial.print(humidity);
  12.     Serial.print("%\n");
  13.     previousMillis = millis();
  14.   }
  15. }

DHT11 からデータを読み出す間隔は 2.5 秒としました (49 行) 。

55 行で関数 readDHT11() を呼び出しています。温度と湿度の二つの値を返したいので、引数としてポインタを渡しています。関数側で、*temp と *hmd の仮変数が示すアドレスにそれぞれのデータを書き込むことで、二つの値を得ることができます。
うーん、エクセレントぉ (^_^;) まぁね、グローバル関数使っちゃえば簡単なんだけど、さ。

57〜61 行で、取得した温度と湿度の値を、シリアルモニタへ送ります。

シリアルモニタの表示

シリアルモニタの表示

ある日の温度、湿度データです。蒸し暑い日です。

数値は左から、湿度整数部、湿度小数部、温度整数部、温度小数部、チェックデジット、です。
チェックデジットは 54+0+26+5=85 で、合格です。

/ の右は、関数が返した温度と湿度の値です。温度は、小数部が四捨五入されています。

出力波形

DHT11 の出力を見てみましょう。

DHT11 起始信号

起始信号とデータ転送の全体です。

20ms Low の起始信号の後、データが送られてきていることがわかります。

DHT11 响应信号

响应信号の周辺です。

最初の Low は起始信号です。起始信号が High に戻った後、ホストを解放するための 13μs の High があります。
その後、响应信号として 83μs の Low と 87μs の High が届きます。
その次からがデータで、左図では 0011 ときていることがわかります。

ということで、DHT11 から温度と湿度の測定データを、ライブラリ使わずとも、わりと簡単に、取り出すことができました。

次回は、温度と湿度を 7 セグメント LED に表示させて、温湿度計を作ってみましょう。

タイトルとURLをコピーしました