温湿度センサDHT11をRaspberry Piに繋いでシリアル入力を学ぶ

温湿度センサDHT11をRaspberry Piに繋いでシリアル入力を学ぶ

秋月電子で見つけたDHT11という温湿度センサをRaspberry Piで動作させてみました。このチップはシリアル入力で動くので、今後のアナログ入力の練習台になると思って。

DHT11 Humidity & Temperature Sensor

AOSONGという中国?のメーカーが作っている温度・湿度・ADCをワンチップにした小型センサ。その下位モデルみたいであまり精度は高くないそうな。

20131224-02秋月電子のページに掲載のDatasheet(PDF)

センサー単体で300円。PINソケットにプルアップ抵抗、ハーネスがついた基板キットが500円だったので後者を購入。別のデータシートの情報と合わせると

  • 測定範囲:湿度20~90% 温度0-50℃
  • 測定誤差:湿度+-5% 温度+-2℃
  • データ精度:湿度8bit(分解能:1%)、温度8bit(分解能:1℃)
  • 動作電圧:3.3V~5.5V
  • 出力:単線双方向シリアル

湿度誤差+-5%はいいとして、温度誤差+-2℃って結構デカイな。1℃おき(TEMPerみたいにコンマ以下が出ない)だし、0℃以下は測れない=北海道の外では使えない。”Low cost, long-term stability”がコンセプトらしいので仕方ない所でしょうか。

あとはチラシの裏の電子工作 激安温湿度センサDHT11を使ってみたによると、3.3V付近ではまともに拾えなさそう。Raspberry Piから供給される4.75Vではなんとかという所でしょうか。

まずは動かしてみる

20131224-01

3本のリード(VCC,DATA,GND)をRaspberry Habuに接続。赤を5Vライン、緑をデジタル出力の近くにあるW05のパターン=GPIO5(注:WiringPi上の”5″なので、実際は18pinなど。gpio readallの結果から読み取る)へ繋いでいます。Habuの機能は何も使ってないので、PiのGPIOコネクタに直刺しでも可。

Noobsから起動したRaspbianを初期セットアップ

# dpkg-reconfigure tzdata

タイムゾーンを東京に

# aptitude install ruby ruby-dev

rubyをインストール

# git clone git://git.drogon.net/wiringPi
# cd wiringPi
# ./build
# gpio readall

wiringPiをインストール。動作確認。

/etc/rsyslog.conf をテキストエディタで開き、以下をコメントアウト

cron.* /var/log/cron.log

DHT11からの読み取りは、下記のサイトからCで書かれたプログラムを拝借しました。

これは3秒おきに数値を取り続けるプログラム。時々データを取りこぼすらしく”Invalid Data!!”と表示されます。これを少し改造し、

  1. TEMPerと同じ様に行頭に時刻をカンマ区切りで追加
  2. cron用に1ショット動作
  3. データ取りこぼしの場合は取れるまで3秒ごとにリトライする
  4. F(華氏)は削除

した”temp_rh_sensor.c”が以下のソース

#include <wiringPi.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <time.h>
#define MAX_TIME 85
#define DHT11PIN 5
int dht11_val[5]={0,0,0,0,0};
int res;

int dht11_read_val()
{
  uint8_t lststate=HIGH;
  uint8_t counter=0;
  uint8_t j=0,i;
  float farenheit;
  for(i=0;i<5;i++)
     dht11_val[i]=0;
  pinMode(DHT11PIN,OUTPUT);
  digitalWrite(DHT11PIN,LOW);
  delay(18);
  digitalWrite(DHT11PIN,HIGH);
  delayMicroseconds(40);
  pinMode(DHT11PIN,INPUT);
  for(i=0;i<MAX_TIME;i++)
  {
    counter=0;
    while(digitalRead(DHT11PIN)==lststate){
      counter++;
      delayMicroseconds(1);
      if(counter==255)
        break;
    }
    lststate=digitalRead(DHT11PIN);
    if(counter==255)
       break;
    // top 3 transistions are ignored
    if((i>=4)&&(i%2==0)){
      dht11_val[j/8]<<=1;
      if(counter>16)
        dht11_val[j/8]|=1;
      j++;
    }
  }
  // verify cheksum and print the verified data
  if((j>=40)&&(dht11_val[4]==((dht11_val[0]+dht11_val[1]+dht11_val[2]+dht11_val[3])& 0xFF)))
  {
    time_t timer;
    struct tm *date;
    char str[256];
    timer = time(NULL);
    date = localtime(&timer);
    strftime(str, 255, "%Y/%m/%d %H:%M:%S", date);
    printf("%s ,", str);
    printf("Humidity = %d.%d %% Temperature = %d.%d *C\n",dht11_val[0],dht11_val[1],dht11_val[2],dht11_val[3]);
    return 2;
  }
}

int main(void)
{
  if(wiringPiSetup()==-1)
    exit(1);
  while(1)
  {
     res = 1;
     res = dht11_read_val();
     if(res==2) {
      return 0;
      break;
     }
     delay(3000);
  }
  return 1;
}

同じように

# gcc -o XXXX temp_rh_sensor.c -L/usr/local/lib -lwiringPi

でコンパイルします。 これをcronで30分おきに回すと

2013/12/23 21:30:01 ,Humidity = 40.0 % Temperature = 19.0 *C
2013/12/23 22:00:07 ,Humidity = 40.0 % Temperature = 19.0 *C
2013/12/23 22:30:04 ,Humidity = 41.0 % Temperature = 20.0 *C
2013/12/23 23:00:04 ,Humidity = 39.0 % Temperature = 22.0 *C
2013/12/23 23:30:04 ,Humidity = 39.0 % Temperature = 22.0 *C

みたいなログが取れました。

DHT11の精度

冒頭に精度が悪そうな事書きましたが、分解能はともかくそれほど悪くなさそうです。

20131224-03我が家の温度計集合(放射温度計は欠席)。
この中で一番信頼できそうな棒状温度計が20.8℃くらい。この時の読み込みは

2013/12/27 10:18:42 ,Humidity = 40.0 % Temperature = 21.0 *C

なのでまあいいんじゃないでしょうか。後ろの白い四角はこれも個体差があると評価されている 無印良品のデジタル温湿度計。湿度に関してもこれらと大きな差は無い様です。あとは出来る限り寒い所と暑い所でも比較すべきなのでしょうけど。

ともかくこれは使える。

ソースから何が学べるのか

原文に英語で全部書いてる様なのですが、読解力に乏しい為、ソースとタイムチャートを照らし合わせながら読解に努めてみます。

データシートにも同じようなチャートが出ていますが、ソース元サイトのが具体的に時間軸が書かれているので借用します。

1~18行目

ライブラリの組み込みと行列の初期化

19~23行目

GPIOのピンをOUTPUT、LOWへ。delay(18);で18ms待った後再びHIGHへ。delayMicroseconds(40); で40us待機。
これでタイムチャートのMCU signalの赤い部分完了。

28~33行目

1bitを表すデータ(bit data “1” format)も0bitを表すデータも、最初は50usのLOWから始まる。その間はスルーして、HIGHになったらdelayMicroseconds(1)でカウントしていき、HIGHの長さを測定する。
28us以下ならbit data”0″、70usならbit data”1″。

38~43行目

キモの部分なのに、英語とCの記述への理解が及ばない。
40bit=5byteのデータをフェッチして配列に入れてる。データは

8bit湿度整数データ → 8bit湿度小数点データ(実際は出ない)
→ 8bit温度整数データ → 8bit温度小数点データ(実際は出ない)
→パリティビット

の順に出力されます。”top 3 transistions are ignored”として(i>=4)にしているのは最初の”DHT11 response signal”分を無視しているのかな・・・

Subsequently, the sensor responds to the Pi’s data transfer request by pulling the data bus LOW for 80 μs followed by 80 μs of HIGH

46行目

AOSOUNGのデータシートの5ページ目の通り、4byteのデータ和(下位8bit)とパリティビットを比較しています。OKなら結果の表示へ、ダメな場合はリターンコード0のまま戻るので72行目の3秒ディレイ→メインループへもどってもう一回やります

48~56行目

結果表示部。時刻を取得し最初に出力。配列は2進数なので%dで10進数表示としています。リターンコードに2を返す事でメインループで正常終了となります。

シリアルデータの扱いもCの表記も慣れていない為、未だに消化出来ないでいます。特に36~41行目あたり。でもソースを元にwiringPiを使ったGPIO入出力を少し勉強できそう。正月の飛行機の上ででもゆっくり考えてみようかなと思っています。

部屋に100℃以上の物体があるあなたに。

4 thoughts on “0

  1. お世話になります。
    記事の中で「cronを30分置きに回す」
    コンパイル後、どのようにするのですか

    1. OSが持っているcronという機能を使い、毎時00分と30分に今回の
      コマンドを実行する様にスケジュールを組んでいます。
      その設定の際に temp_rh_sensor >> roomtemp.log などとすることで
      年輪のように過去の温度が溜まっていく仕組みです

  2. 有難うございます。
    CRONはOKになりました。
    このデータをグラフ化はできますか

    1. データをグラフ化する事はできますが、それぞれ簡単にはいきません。

      手動でやる場合は、CSV→グラフのやり方を過去にやった事があります
      HWiNFOとLibraOfficeでマシンのモニターグラフを書こう | 徒労日記
      <http://dolls.tokyo/?p=5269>

      自動でやりたい場合は、こちらの方の記事が参考になりそうです。
      Raspberry Piを購入して、部屋の温度を淡々とグラフにしてみた / マスタカの ChangeLog メモ
      <http://masutaka.net/chalow/2013-12-05-1.html>