24.温度・湿度測定(DHT11) (2021.7.15-2021.7.22)

空気中の温度と湿度を測定することは、周囲の環境を知る上で重要です。応用する機会も多いと考えて、実用化も視野に入れた上で温度・湿度センサーを使用した実験をしたいと思います。今回使用するセンサーはDHT11と呼ばれるモデルです。小数点以下のデータは得られないので細かな測定には不向きですが、小型で安価なので広く普及しているようです。より細かな測定と精度が必要なら、上位モデルのDHT22と言う選択肢があります。DHT11はRaspberry Piで使用するセンサーとしてはかなりメジャーなもののようで、検索すればすぐにこのモデルが出てきます。ちょうど手元にあったOSOYOOの実験キットの中にもこのセンサーが入っていました。実はamazonでも安価な複数セットが売られていたので、今後の実験や実用に備えて購入してみました。3個セットで1080円でしたので、1個当たりでは360円と大変リーズナブルです。

DHT11は古いRaspberry Pi Model-Aに組み込みました。下図の右部分にあるUSBコネクタの下に開口部があるので、そこにスポンジ付きの両面テープで貼り付けてあります。ここなら内部の温度上昇の影響をさほど受けないと思います。最初にお断りしておきますが、この古いRasPiは現在のRaspberry OSでは正直実用になりません。遅過ぎて頻繁にフリーズしたり、マウスの動きすらカクカクと不安定になります。CPUの能力不足もさることながら、恐らくメモリー容量不足(搭載容量は256MBしか無い)でSWAPが発生するのだと思います。一度OSのリカバリを試みましたが、多少改善した程度で根本的には直りませんでした。これがModel-B+になると多少マシにはなりますが、やはり遅くてイライラする場面が多くなります。仕方無いので古いバージョンにOSを書き換えました。NOOBSのバージョン1.Xでは色々な障害が出て使い辛いため、最終的にバージョン2.1をインストールしています。満足できる動作とは言えませんが、どうにか使えるギリギリの状態だと思います。他に有効な使い道も見つからないので、せめて温度・湿度センサーとして役立って欲しいものです。

DHT11は独自の1-wire方式でRasPiと通信します。そのため必要な信号線は1本だけで、後はVCC(電源)とGNDのみです。これ以上無いシンプルな結線のため、あらかじめ規定された規格内で通信しなければなりません。その分エラーになる頻度も多いようで、特にタイミングにはシビアな面もあるようです。処理の遅いModel-B+(NOOBSは3.Xの最新)ではエラーで値が読み出せないケースが頻繁に発生しました。それがRasPi-3 Model-Bではかなり改善されます。では、このModel-Aはどうかと言うと、結論から言えばOSを古いバージョンにしたおかげで結構エラーになること無く正常にデータが得られました。ただし、プログラムの処理が間に合わないのか、時間待ちが正常に機能しません。大きく遅れて動作することから、用途としては限定されそうです。定期的にデータを更新するだけなら問題無さそうなので、単なる温度・湿度モニターとして稼動させるのが最適かと思います。

動作に必要なプログラムは、一般に公開されている良いものがあるので利用させてもらいます。
git clone https://github.com/szazo/DHT11_Python.git
上記を実行するとPiフォルダの中にDHT11_Pythonディレクトリが作られて、必要なファイルがダウンロードされます。その中のexample.pyがデータを取得するプログラムです。元は同じフォルダにDHT11.pyと言うファイルがあったのですが、今は同じ階層にあるDHT11フォルダの中の__init__.pyなるファイルが使われます(後述)。どうやらこの改良版では、これまで小数点以下が常に0になっていたのが、小数点以下1桁までは数値が得られるようです。DHT11の仕様では分解能が1度となっていましたし、実際に小数点以下は0にしかならなかったのでそんなものかと考えていたのですが、センサー自体は分解能0.1度の性能を持つものと考えられます。両者のプログラムを走らせて違いを確認しましたので、恐らく間違い無いと思われます。もし古いプログラムを使われているのなら、こちらを試してみると良いでしょう。湿度センサーは元々小数点以下は測定できず常に0です。湿度センサーの精度はさほど高く無いので、小数点以下を取得できたとしてもあまり意味は無いと思います。

次にハードウェアですが、結線は3本しか無いため至って簡単です。電源2本(VCC=3.3VとGND)に加えて、信号線はサンプルプログラムに合わせてGPIO14(8ピン)に接続します。では、早速サンプルプログラムを走らせてみます。実際にDHT11と通信して温度・湿度を取得するモジュールは複雑(プロトコルに沿ったもの)ですが、そこから値を読み込むサンプルは定期的に表示するだけの単純なものです。実行結果は次のようになります。うまく動作したなら、次はサンプルを様々にアレンジして利用するだけです。アイデア次第で色々面白い表示や使い方ができるかと思います。

一例ですが、実験に使用したRaspberry Pi Model-Aを温度・湿度計として有効活用できるよう、コンソール画面に専用Windowを開いて値を大きく表示するプログラムも考えてみました。Tkinterを利用するため、「14.GUIプログラムの基礎」を改めておさらいすると良いと思います。GUI表示ではmainloop関数を使うため、これまでのように時間待ちルーチンは使えません。DHT11のデータを取得するために、タイマーイベントを発生させて実現しています。なお、繰り返し表示を書き換えると、変化が無い場合でも書き換え時に一瞬文字がフラッシュする状態になります。そのため値に変化が無ければ書き換えしないように対策しました。また、温度表示では10度未満の時に青色に、30度以上では赤色に表示して警告を兼ねるようにしています。湿度表示でも色から受けるイメージを考慮して、30%未満の時に薄茶色(乾燥)に、60%以上では水色(多湿)にしています。

今回のプログラムでは時間待ちルーチンを使用しないせいか、処理が遅れることも無く順調に進んでいくようです。DHT11との通信時に値の取得を失敗することが多かったため、エラーが発生しても最大100回まで取得を繰り返すように改良しました。これでもエラーは皆無にはなりませんが、劇的に頻度を低下できています。元々処理能力の高いモデルなら、ここまでループを繰り返す必要はありません。実際バージョン3のモデルBを採用した自動ミニトマト栽培機では最大10回のループにしましたが、その範囲内でデータを取得できています。

DHT11のテストプログラム

動作確認の際は拡張子をtxt→pyにします。プログラムが動作を開始すると、表示ウインドウはこのようになります。右は温度が30度以上なので黒色から赤色表示に変わっています。

  

応用例として「20.IICインターフェース」で使用したLCDと組み合わせて、LCDにDHT11で取得した温度・湿度データを表示するプログラムも、キット元のOSOYOから公開されているので試してみました。今回はあらかじめハードを結線して準備を整えた上で進めたため、IICのポート確認では表示の「- -」の中に数値(27H)が入っています。これがLCDのアドレスになります。

メーカーサイトのガイドに従って実験用プログラムをダウンロードします。2つのモジュールを使うため、先にそれぞれダウンロードして準備を整えます。ダウンロードを終えたらいよいよプログラムの実行です。ガイドではIICのアドレスは3fHでしたが、私の環境では27Hになっているため、プログラムソースを一部変更しました(サイトではその点も説明があります)。ただし解説文の日本語が変です。恐らく中華系のメーカーで、きちんと日本人が監修したものでは無いのでしょう。

下図が実験時の結線です。温度・湿度センサーは3線の結線のみで、信号線は1本だけを使っています。LCDの結線は次章と同じです。なお、図ではDHT11の電源を5Vに接続していますが3.3Vが正しい結線です。GPIOの入力電圧は3.3Vまでなので、5Vを印加すると最悪の場合ポートが壊れる可能性があるからです。ネットでの情報が交錯していて5Vだったり3.3Vだったりと紛らわしく、センサー自体が3.3Vだと誤動作を起こしやすいとのコメントもあるくらいなので迷う人も多いかもしれません。DHT11のモジュールは信号ラインがプルアップ抵抗で電源につながっているので、普通に考えれば5Vには接続すべきでは無いと思います。もっとも電流値はごくわずかなので、実際にポートが壊れることは無さそうですが。LCDの方は3.3Vが併記されていますが、私が試した限りでは5Vに接続する必要がありました。想定と同じモデルでは無いのかもしれません。

   

LCDが表示されるのはIICインターフェースの項目で既に確認済みです。本章の実験では次のように温度と湿度が表示されました。

LCDの表示プログラムに関しては次章を参照して下さい。

プログラムは2つあります。例によって拡張子はpyにして使用する必要があります。
1.メインとなるLCD表示プログラム
2.温度・湿度センサーからデータを取得するモジュールで、メインプログラムにおいてimportされます。(旧コア・モジュールのため、温度の小数点以下は常に0)

2.の改良版のモジュール(__init__.py)