目次
本記事で使用するもの
以下のリンクのスターターキットを購入した場合、上記で述べた使用するものは、PC以外すべて入手することができます。
Arduinoをまだ購入していないという方やArduinoで動かすことができるたくさんのパーツが欲しいという方は、購入を検討してみてください。
温湿度センサ(DHT11)の説明
温湿度センサ(DHT11)は温度と湿度を測定できる部品で、以下の写真のような見た目をしています。
DHT11は、内部で温度や湿度の計算などを行ってくれるため、こちら側で複雑な計算を行う必要はなく、データの送受信を行うことが出来れば、温度と湿度の情報を得ることができます。
上記の写真のようなDHT11の場合、Arduinoでデータの送受信を行う際に、抵抗器を用いて、以下のような回路を作成する必要があります。
しかし、以下の写真のようなDHT11と抵抗器をひとまとめにしたセンサモジュールの場合は、抵抗器を用意する必要はなく以下のような回路でデータの送受信を行うことができます。
DHT11側は、データの開始するための合図をマイコン側から受け取った後、マイコンに対して温度、湿度の2進数データを送信しますが、通信は一本の線で行うため、マイコン側は合図を送る際は出力ピンにして、データを受け取る際は、入力ピンにするといった切り替えを行う必要があります。
具体的な通信方法は以下のようになります。
ここで、受け取った40ビットの2進数のデータは、以下のような、割り当てとなっており、2進数のデータを10進数に直すことにより、温度と湿度が分かります。
この通信をArduinoで行う方法について、一番手っ取り早いのはライブラリを使う方法で、有名なのが「DHT sensor library」というライブラリを使う方法ですが、マイナスの温度を測定するときの処理がイマイチで誤差が発生するという情報を発見し、ライブラリの中を確認したところ実際にイマイチだと思ったので(2023/09/22)、この記事では、マイナスの温度もしっかり測れるように、ライブラリなしで通信を行う方法を紹介したいと思います。
以下に、私が参考にしたDHT11のデータシートのリンクを貼らせていただきます。
日本語で書かれていないので、少し分かりづらいかもしれませんが、DHT11について、もう少し詳しく知りたいという方は、上記のデータシートを参考にしてみてください。
また、以下に「DHT sensor library」によるマイナスの温度測定が、どのようにイマイチなのかを説明してくださっている記事のリンクを貼らせていただきます。
“湿度センサー「DHT11」がやってきた・・・けど!?” [居酒屋ガレージ日記 ]
DHT11を試す
DHT11で温度と湿度を測定するには、DHT11としっかり通信が行える必要があります。
そこで、まずは以下の画像のように、DHT11からデータを受け取り、エラーがあるかをチェックするといったことを行いたいと思います。
空のスケッチを書き込む
回路を作成する前に、空のスケッチ(「ファイル」タブの「New Sketch」をクリックしたときに出てくる何の処理も書かれていないスケッチ)を書き込みます。
そうすることにより、前に書き込んだスケッチが、新しく作成しようとしている回路に影響を及ぼすことを防げます。
新しい回路を作成するときは、いつもこの作業をやっておくと、安全に回路を作成することができます。
回路を作成する
DHT11がセンサモジュールでない場合は、抵抗器を用いて以下のような回路を作成してください。
ここで、使用する抵抗器は、データシートで推奨されている電気抵抗4.7[kΩ]付近のものを使用します。
DHT11がセンサモジュールの場合は以下のような回路を作成してください。
スケッチを書き込む
回路の作成が終わりましたら、以下のようなスケッチを書き込んでください。
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 |
/* Arduinoで遊ぼう(23,温湿度センサ); 2023/09/22 DHT11_test //スケッチの詳細はこちら↓ https://mecha-norimaki.com/arduino_start_23/ */ //受けとったデータを入れる配列を宣言 byte response_data[5]; void setup() { //シリアル通信の初期処理 Serial.begin(9600); //2番ピンを入力ピンに設定(デフォルト設定なので省略しても良い) pinMode(2,INPUT); } void loop() { //2番ピンを出力ピンに設定、出力ピンはデフォルトがLOWのため、DHT11への信号がLOWになる pinMode(2,OUTPUT); //開始信号作成のため20ms待機 delay(20); //2番ピンを入力ピンに設定、プルアップの回路より、DHT11への信号がHIGHになり通信が開始される pinMode(2,INPUT); //データが送られる前に発せられる返答の信号を検出する int response_signal=pulseIn(2,HIGH); //返答の信号が送られたのを確認したら、続いて送られてくるデータ(8bit×5)を検出しまとめていく if(response_signal){ for(int i=0;i<5;i++){ for(int j=0;j<8;j++){ //送られてきた信号のHIGHの長さを計測する int duration=pulseIn(2,HIGH); //データを左に1シフトする response_data[i]<<=1; //送られてきた信号のHIGHの長さが50μs以上なら一番右のbitデータを1にする if(duration>50){ response_data[i]|=1; } } } //まとめたデータを10進数で表示させていく for(int i=0;i<5;i++){ Serial.print(response_data[i]); Serial.print(" "); } //データのエラーをチェックし結果を表示する if(response_data[0]+response_data[1]+response_data[2]+response_data[3]==response_data[4]){ Serial.println("OK"); }else{ Serial.println("ERROR"); } } //エラーを防ぐため、2s待機して計測の間隔を空ける delay(2000); } |
スケッチの書き込みが終わりましたら、シリアルモニタを開き、入力部分の右に表示されている設定が「9600baud」、自動スクロールが「ON」になっているのを確認して、しばらく放置してみてください。
ここで、表示が以下のような状態の場合、ひとまず、通信は成功していると考えられます。
表示される数値が明らかにおかしいものだったり、「OK」ではなく、「ERROR」ばかり表示されたりする場合は、通信が上手くいっていないと考えられますので、回路とスケッチをもう一度確認してみてください。
スケッチの解説
では、スケッチの解説をしていきたいと思います。
まず、初めにこのスケッチでは、11行目の変数宣言で、「byte型」の配列を用意しており、この中に、送信されたデータを格納できるような状態にしています。
次に、「void setup()」内では、シリアル通信の初期化を行い、その後、DHT11の「DATAピン」に繋いだ2番ピンを入力ピンに設定しています。
ここで、入力ピンはハイインピーダンス状態といって、とても高い電気抵抗を持っているような状態となり、DHT11の「DATAピン」は「HIGH」の状態となります。
次に、「void loop()」内について、順番に説明したいと思います。
まずは、DHT11にデータを送ってもらうため、以下のような、合図の信号を作成する必要があり、それを行っているが、25行目~31行目の文となります。
25行目では、DHT11の「DATAピン」に繋いだ2番ピンを出力ピンに設定しています。
出力ピンに設定されたピンは、デフォルトの状態だと「LOW」になるため、DHT11の「DATAピン」は「HIGH」から「LOW」の状態に変化します。
その後、「delay(20)」により、20[ms]待機したら、DHT11の「DATAピン」に繋いだ2番ピンを入力ピンに設定します。
入力ピンに設定すると、「void setup()」のときと同様に、DHT11の「DATAピン」は「HIGH」の状態になるため、上記のような合図の信号が作成されるということになります。
また、入力ピンとなっているため、データを受け取る準備はこれで整います。
次に、合図の信号に対しての返答の信号を受け取ります。
34行目では、「response_signal」という変数を用意し、その中に「pulseIn関数」の結果を格納しています。
「pulseIn関数」は、以下の画像のような、パルス波の「HIGH」や「LOW」となっている時間を測定するものです。
今回は、以下のような返答の信号が送られたかどうかを確認するために、「HIGH」のパルス信号の長さを検出して、「response_signal」に格納しています。
ここで、返答の信号が送られていない場合は、「response_signal」の値は0になります。
if文の( )内に数字を入れる場合、0の時は「false」、それ以外の場合は「true」として扱われるため、37行目の「if(response_signal)」で、パルス信号の有無による分岐を行うことができます。
返答の信号が送られているのを確認したら、次は送られてくる8bit×5のデータを格納しデータをまとめていきます。
それを行っているのは、38行目~52行目の部分です。
ここでは、パルス信号の長さから0か1のデータを判別し、データとしてまとめていくということを、8bit×5のデータ分行うため、for文による繰り返しを行っています。
また、データの0、1の判別は、先ほども登場した「pulseIn関数」を利用して、パルス信号の「HIGH」の長さが、50[μs]以上の場合1として扱い、それ以外の場合は0として扱うように分岐させています。
データのまとめ方については、ビット演算というものを利用しています。
ビット演算は私たちが普段行う計算とは少し異なるもののため、少し理解するのが難しいかもしれませんが、今回のように、2進数のデータを扱う際はとても便利でよく扱われます。
今回のスケッチでビット演算を行っている部分は2か所あるので、それぞれについて、どのようなことを行っているのか詳しく説明したいと思います。
まず、45行目では、シフト演算というものを行い、データを左に1シフトさせています。
これを図で説明すると以下のような感じです。
シフト演算でデータを左にシフトする際は、「<<」という記号を使い、その後の数値でいくつ左にシフトするかを決めます。
スケッチで使用しているのは「<<=」ですが、実はこれは文を省略したものであり、「a<<=1」と「a=a<<1」は同じ意味になります。
「=」の左側に記号を書いて、変数名を省略する形は、他の演算でも使用されることがあるので、覚えておくと良いと思います。
続いて、49行目ではOR演算というものを行い、一番右のデータを1に変更しています。
これを図で説明すると以下のような感じです。
このOR演算( | )は2つのデータのビットの内、どちらかが1なら1、どちらも0の場合は0にするといった処理が行われます。
スケッチで使用しているのは「|=」は先程のシフト演算と同様に、文を省略したものであり、「a|=1」と「a=a|1」は同じ意味になります。
よって、今回のスケッチでは、データを左に移動し、パルス信号のHIGHの部分が50[μs]以上の場合、一番右のデータを1にするということを、データの数だけ繰り返すことによって、送られてくるデータを各配列にまとめるといったことを行っています。
少し長かったですが、データをまとめるための文についての説明は以上です。
まとめたデータは、55行目~58行目で表示を行っています。
この時「Serial.print」では、2進数が10進数に直された形で表示されます。
続いて、61行目~65行目では、最初の4つの8ビットデータの和と5つ目の8ビットデータが一致しているかどうかで、分岐をおこない「OK」または、「ERROR」の表示を行っています。
そして、最後の70行目ではエラーを防ぐために、測定間隔を2[s]空けています。
DHT11と通信を行う方法についての説明は以上です。
温湿度センサで温度と湿度を測定する
上記の通信が行えれば、後は受けとったデータを、湿度、温度に直してあげる仕組みを作るだけです。
ここでは、以下の画像のように、湿度、温度をシリアルモニタに表示させるといったことを行いたいと思います。
スケッチを書き込む
回路はそのままで、以下のようなスケッチを書き込んでください。
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 |
/* Arduinoで遊ぼう(23,温湿度センサ); 2023/09/22 DHT11_measurement //スケッチの詳細はこちら↓ https://mecha-norimaki.com/arduino_start_23/ */ //受けとったデータを入れる配列を宣言 byte response_data[5]; //湿度と温度を入れる変数 float humidity,temperature; void setup() { //シリアル通信の初期処理 Serial.begin(9600); //2番ピンを入力ピンに設定(デフォルト設定なので省略しても良い) pinMode(2,INPUT); } void loop() { //2番ピンを出力ピンに設定、出力ピンはデフォルトがLOWのため、DHT11への信号がLOWになる pinMode(2,OUTPUT); //20ms待機 delay(20); //2番ピンを入力ピンに設定、プルアップの回路より、DHT11への信号がHIGHになり通信が開始される pinMode(2,INPUT); //データが送られる前に発せられる返答の信号を検出する int response_signal=pulseIn(2,HIGH); //返答の信号が送られたのを確認したら、続いて送られてくるデータ(8bit×5)を検出しまとめていく if(response_signal){ for(int i=0;i<5;i++){ for(int j=0;j<8;j++){ //送られてきた信号のHIGHの長さを計測する int duration=pulseIn(2,HIGH); //データを左に1シフトする response_data[i]<<=1; //送られてきた信号のHIGHの長さが50μs以上なら一番右のbitデータを1にする if(duration>50){ response_data[i]|=1; } } } //データのエラーをチェックし問題なければ、データを湿度と温度に変換して表示 if(response_data[0]+response_data[1]+response_data[2]+response_data[3]==response_data[4]){ //1番目と2番目のデータを湿度に変換する humidity=response_data[0]+response_data[1]*0.1; //4番目の一番左のbitが1のとき温度を負の値として変換、0のときは温度を正の値として変換する if(response_data[3]>>7){ //4番目の一番左のbitを0にする response_data[3]&=~(1<<7); //3番目と4番目のデータを温度に変換する(負の値) temperature=-1*(response_data[2]+response_data[3]*0.1); }else{ //3番目と4番目のデータを温度に変換する(正の値) temperature=response_data[2]+response_data[3]*0.1; } //湿度と温度を表示 Serial.print("Humidity: "); Serial.print(humidity); Serial.print("[%] "); Serial.print("Temperature: "); Serial.print(temperature); Serial.println("[℃]"); } } //エラーを防ぐため、2s待機して計測の間隔を空ける delay(2000); } |
スケッチが正しければ、先程の画像のように、シリアルモニタで、湿度と温度が表示されていくのが、確認できます。
プラスの温度とマイナスの温度の環境で、手持ちの温湿度計とデータの比較をしましたが、大きな誤差はないため、それっぽい値が測定できていると思われます。
低い温度の場所にあったものを、急に暖かい場所に持っていくと、結露が発生してしまい、機械などを壊してしまう恐れがあります。
低い温度の場所にあったものを、暖かい場所に持っていく場合は、暖かい空気の当たらない密閉された空間(できれば乾燥した空間)でゆっくりと温度を上げて、周りの空気と同じ程度の温度になってから取り出すなど、結露の対策を行うなうことが必要になります。
私は上記のマイナスの温度を測定した際に、ジップロックとクーラーボックスを使い結露対策をしました。
スケッチの解説
先程のデータを受け取る処理に加え、今回のスケッチでは、受け取ったデータを湿度、温度のデータに変換し、表示をさせています。
61行目では、最初の2つの8ビットデータを利用し、湿度のデータを取得しています。
2つ目の8ビットデータは、常に0となっていると思われますが、一応、湿度の小数部を表しているとのことなので、0.1倍にして、足し算を行っています。
次に、64行目から75行目では、3番目と4番目の8ビットデータを利用し、温度のデータを取得しています。
温度の場合は、プラスの温度の場合とマイナスの温度の場合が存在し、4番目の8ビットデータの1番左のビットが0であるか、1であるかで判別を行うことができます。
よって、64行目では、左シフト(<<)の反対である右シフト(>>)というものを利用し、一番左のビットが0の場合と、1の場合で分岐をさせています。
1番左のビットが1で、マイナスの温度であると判定した後、この1番左の1は邪魔になるため、67行目でビット演算を利用し、0に変更しています。
この67行目の処理では新しい演算を2つ使用しているため、それらについて、順番に説明したいと思います。
まず、NOT演算( ~ )では、ビットデータの0と1を全て逆にすることが出来ます。
これを図で説明すると以下のような感じです。
次に、AND演算( & )では、2つのデータのビットの内、両方とも1なら1、それ以外の場合は0にするといった処理が行われます。
これを図で説明すると以下のような感じです。
スケッチで使用しているのは「&=」はこれまでの演算と同様に、文を省略したものであり、「a&=1」と「a=a&1」は同じ意味になります。
よって、67行目の処理をまとめると以下のようになっています。
70行目と74行目はどちらも3番目と4番目の8ビットデータを温度データに変えるという処理を行っています。
70行目の場合は、マイナスの温度であると、判定しているため、-1を掛け算して符号を変更しており、74行目は、プラスの温度であるため、符号の変更はしていません。
参考にしたデータシートの例では、温度の小数部のデータが10進数で2桁の数字となっていますが、実際に測定したデータでは2桁の数字は出ず、1桁の数字のみであったため、温度の小数部のデータは0.1倍にして、足し算を行っています。
最後に、変換した湿度と温度データは、78行目~83行目で、表示を行っています。
DHT11からデータを受け取り、温度と湿度を表示させる方法についての説明は以上です。
終わりに
皆さん温湿度を測定することが出来たでしょうか。
今回の記事ではビット演算というものを、多く利用しました。
少し難しいですが、2進数データを扱う際は便利で良く扱われるので、是非、覚えてみてください。
次回の記事では、赤外線リモコンから信号を受け取り、その結果をシリアルモニタに表示させるといったことを行いたいと思います。
本記事はここまでです。ご清覧ありがとうございました。
広告
楽天モーションウィジェットとGoogleアドセンス広告です。
気になる商品がございましたら、チェックをしてみてください。