温湿度を計ってみよう ~DHT11編 by C~
はじめに
安価な測定素子であるDHT11を使用して温湿度を測定していきます。
測定値の取得方法One-Wireライクのみです。
C言語でプログラムを作成し温湿度を測定していきます。
python版も記事に起こしてますので、pythonで実施したい方はどうぞご参考に。
用意するもの
Raspberry Pi | Raspberry Pi 4 |
DHT11 | DHT11 |
抵抗 | カーボン抵抗 1/2W 5.1kΩ(100本入) |
ジャンパーケーブル | ジャンパーケーブル |
ブレッドボード | ブレッドボード |
読み取り方法の確認
DHT11を使って温湿度を計測していきます。
まず、何をやるにしても計測器がどういった接続をするのか、データの受信方法はどうやるのかをデータシートで確認します。
まぁ、使いたい素子とかその辺って基本英語なんですよねー・・・・。
私も全部読めるわけではないですが、ざっくりとまとめます。
ちゃんと読めてるわけではないので説明が間違っている箇所もあるかと思いますが、とりあえず動けばOKってことで・・・。
データを受信する流れは図の通りです。
スタートシグナルを受信するとレスポンスを返してデータを送信します。
各シグナルには送出時間が決まっています。
各シグナルの時間は表のとおりです。
読み取りの流れ
①スタートシグナル
Lowシグナルを20ms(Tbe) DHT11に送出し、13μs(Tgo)Highシグナルを送出するとスタートします。
②待機状態
スタートシグナルを送出した後、DHT11から待機シグナルを受信します。
Lowシグナルを83μs(Trel)を受信後、Highシグナルを87μs(Treh)を受信します。
③データの受信
- Lowシグナルを54μs(Tlow)を受信します。
- Highシグナルをデータビットが「0」の場合は24μs、データビットが「1」の場合は71μs受信します。
- 1にもどります。
- 2~3を40ビット分(40回)繰り返します。
①~③の動作をプログラムに書き起こせば温湿度を測定することができます。
測定プログラム
データ受信処理をプログラム化していきます。
まず、定数部分。
#include <stdio.h>
#include <time.h>
#include <wiringPi.h>
#include <math.h>
//DHT parameter
#define DHT11_DATAPIN 18 //データ受信PIN番号
#define DHT11_START_SIG_LOW 20 //スタートLOWシグナル
#define DHT11_START_PullUp 10 //スタートHIGHシグナル
#define DHT11_WAITRES_LOW 88 //待機LOWシグナル
#define DHT11_WAITRES_HIGH 92 //待機HIGHシグナル
#define DHT11_DATAWAIT_LOW 58 //データ待機
#define DHT11_BITZERO 27 //受信データが「0」の時のシグナル長
#define DHT11_BITONE 68 //受信データが「1」の時のシグナル長
#define DHT11_END 56 //終わりシグナル
各パラメータ1回しか使わないのでわざわざ宣言する必要もないかもしれませんが、1μs増減させて試すとかあるとめんどくさいので。
続いてGPIOとかのセットアップ。
int DHT11_DATA[40]= {0}; //受信データの格納
wiringPiSetupGpio(); //受信ピンをGPIOで指定
pinMode(DHT11_DATAPIN , OUTPUT); //ピンをアウトプットモードに変更
digitalWrite(DHT11_DATAPIN , HIGH); //アウトプットをHIGHに
delay(2000); //2000ms(2s)待つ
LOWシグナルがデータ受信の契機となっているため、まずHIGHシグナルを送出して待機させておきます。
時間は2秒としてますがもっと短くてもいいと思います。ここはお好みです。
続いて上記①の部分
//START OUTPUT LOW >18ms
digitalWrite(DHT11_DATAPIN , LOW);
delay(DHT11_START_SIG_LOW);
//START PUll up
digitalWrite(DHT11_DATAPIN , HIGH);
delayMicroseconds(DHT11_START_PullUp);
ここでぼーっとしてると嵌ります(やらかした)。μsとmsを間違います。msは初めのLOWシグナルのみで、あとはμsです。
②の部分
pinMode(DHT11_DATAPIN , INPUT);
//Response LOW SIG
pinMode(DHT11_DATAPIN , INPUT);
for ( int i = 0 ; i < DHT11_WAITRES_LOW ; i++ ){
if (digitalRead(DHT11_DATAPIN) == HIGH){
break;
}
delayMicroseconds(1);
}
//Response HIGH SIG
delayMicroseconds(DHT11_WAITRES_HIGH);
for ( int i = 0 ; i < DHT11_WAITRES_HIGH ; i++ ){
if (digitalRead(DHT11_DATAPIN) == LOW){
break;
}
delayMicroseconds(1);
}
for分で1μs毎にピンの状態を確認していきます。
状態が変わったらfor分を抜けて次の処理にいきます。
③の部分
//DATA Recieve
for ( int i = 0 ; i < 40 ; i++){ //受信するデータは40ビット
//start bit wait
delayMicroseconds(DHT11_DATAWAIT_LOW);
for ( int j = 0 ; j < DHT11_DATAWAIT_LOW ; j++){
if(digitalRead(DHT11_DATAPIN) == HIGH){
break;
}
delayMicroseconds(1);
}
for ( int j = 0 ; j < 100 ; j++){
if (digitalRead(DHT11_DATAPIN) == LOW){
if ( j < DHT11_BITONE ){ //0 1 の判定
DHT11_DATA[i] = 0;
} else {
DHT11_DATA[i] = 1;
}
break;
}
delayMicroseconds(1);
}
}
受信するデータは40ビットなので40回繰り返します。
受信データは配列に格納していきます。(DHT11_DATA[40])
Highシグナルの長さで「0」「1」がきまるので、if分で長さに応じてデータを格納していきます。
//End process
delayMicroseconds(DHT11_END);
digitalWrite(DHT11_DATAPIN , HIGH);
//2-10
int dec[8] = {1,2,4,8,16,32,64,128};
int dt[5] = {0};
for ( int i = 0 , j = 7 ,k = 0 ; i < 40 ; i++ , j--){
if (DHT11_DATA[i] == 1 ){
dt[k] += dec[j];
}
if ( j < 0 ){
j = 7;
k += 1;
}
}
printf( "temperature %d.%d humdity %d.%d \n" , dt[2],dt[3] , dt[0],dt[1] );
終了プロセスはなくても大丈夫ですが念のため。
受信したデータは2進数ですので10進数に変換します。
もっと簡単に変換できそうですが知らんので。こんな感じです。
プログラムの実行
コンパイルして実行します。
gcc -o dht11.c dht11 -lwiringPi
./dht11
temperature 26.00 humdity 49.00
それっぽい気温と湿度が表示されれば成功です。
受信データのビットずれなどで値がおかしな時がありますが、何回か実行すれば大丈夫です。
おわりに
安価なモジュールで(300円くらい)簡単に温湿度を計測することができました。
高性能モジュールもあるので、そちらでも計測していきたいと思ってます。もっと正確になるのではないでしょうか。
pythonと違いかなり時間がかかって大変でしたがなんとかなりました。
※追記 高性能モジュール版の記事を作成しました。
余談
③のループ部分をwhile分で書いたらなぜか途中で止まるという現象に陥りました。
for (i = 0 ; i < 40 ; i++){
//WAIT Recieve DATA
j=0;
delayMicroseconds(DHT11_DATAWAIT_LOW);
while (digitalRead(DHT11_DATAPIN) == LOW || j < DHT11_DATAWAIT_LOW ){
delayMicroseconds(1);
j++;
}
//Recive DATA
k=0;
while (digitalRead(DHT11_DATAPIN) == HIGH || k < DHT11_BITONE ){
delayMicroseconds(1);
k++;
}
if (k <= DHT11_BITZERO ){
DHT11_DATA[i] = 0;
} else {
DHT11_DATA[i] = 1;
}
}
C言語に関して詳しくないのでなぜ止まったかはわかりませんが、これで3日くらい悩みました。
結果全部for分で書いたら動いたっていう・・・・。