いよいよ、気温と湿度の測定を行います。
まずは、回路の構成です。購入したAM2302には、紙切れ一つついていませんでしたが、そもそも、前回「第5回 気温と湿度を測定するセンサ」で、どのセンサを使うか検討していた段階で、インターネット上のデータシートを入手していましたので、データシートを見ながら作業を進めます。
センサ本体の端子は4本。電源端子(VDD、GND)と、通信端子SDA、残りの1本は未接続(未使用)です。
データシートによれば、VDDには5V、SDAはプルアップしてMCUのI/Oポートに接続せよ、とありました。回路としては、以下の様になります。
フリーの回路図エディタBsch3Vを使用させていただきました。PowerPointに比べると、回路図を引くのがものすごく楽になりました。
プルアップ抵抗値ですが、困ったことに、データシートの中で複数の値が登場していて、混乱します。
ただのプルアップ抵抗だし、深く考えずとも良いのかもしれませんが、そもそも、自分(AM2302)が引ける電流駆動能力(Iol)が影響するパラメータだし、いい加減で良いのかしら?電気的特性の中には、Iolの規定なし。
ざっくり言うと、変な値の抵抗値を使った場合、以下の様な状態になります。
冷静に考えてみたら、どちらも極端なケースなので、常識的範囲で抵抗値を決めれば、動作するでしょう。でも、場所によって書くことの異なるデータシートなんか、信用できないので、自分の直感で10kΩを使うことにしました(データシートのどの値も使っていないが、なんとなく間を取った都合の良い値(1^^)b)。
回路は簡単なので、ブレッドボードとジャンパ線を使って、構成はあっという間に終了。
回路図のところでお解りのように、電源以外でArduinoとつながっている信号線は、1本(SDA)しかありません。どうやって通信するの?というのが、次の課題。
データシートに上図の様な通信波形が載っていました。赤で示したところがArduinoが駆動すべき時間帯、緑色のところがAM2302が駆動すべき時間帯、と、どちらが信号線SDAを使って良いか、時間帯によって切り分けています。
基本的に、オープンドレインドライバが前提になっていて、信号線を駆動するデバイスは、LOW出力はするけれど、HIGHは出力せずにハイインピーダンス(HIGH-Z)にします。HIGH-Zになると、プルアップ抵抗によって電位が上がり、一定時間後にはHIGHに到達します。
上で示した通信波形も、LOWレベルはArduino(赤部分)またはAM2302(緑部分)が出力していますが、HIGHレベルはすべて、プルアップによるHIGHだと思って眺めてください。
各8ビットデータの内容は以下の様になっています。
図中の記載 | 内容 | 備考 |
Humidity H | 16ビット湿度データの上位8ビット |
湿度の測定値が×10倍されている、 10進数の固定小数形式 |
Humidity L | 16ビット湿度データの下位8ビット | |
Temp. H | 16ビット気温データの上位8ビット |
気温の測定値が×10倍されている、 10進数の固定小数形式 |
Temp. L | 16ビット気温データの下位8ビット | |
Parity | Humidity H+Humidity L+Temp. H+Temp. L | 計算結果の下位8ビット、チェックサム |
ビットの順番はMSBファーストだそうです。正しく通信できたか検証する唯一の手段なので、Parity(チェックサム)もちゃんと検証しないとですね。
データシートには、上の図に示したように各8ビットデータの間にLOW期間があるように信号波形が描かれていましたが、たぶん無いですね。40ビットが分離されずに連続して一気に送られてくるものと思われます。ロジアナもオシロスコープも無いので、確かめられませんが……。
もう一つ、重要な情報が必要でした。各ビットの値(0または1)が、どのような形で送信されてくるのか?データシートによると、以下図の様に送信されてくることになっています。
各ビット、LOW幅は同じ50μsですが、ビットデータが0の時と1の時で、HIGH幅の時間が変わります。one-wireなので、一種のクロック/データマルチプレクスになっているわけですね(同期信号とデータが1本の信号線で表現されている)。
波形のスペックを確認しておきましょう。
Symbol | Parameter | min | typ | max | Unit |
Tlow | LOW幅(ビットデータ0,1共通) | 48 | 50 | 55 | μs |
Th0 | ビットデータ0の時のHIGH幅 | 22 | 26 | 30 | μs |
Th1 | ビットデータ1の時のHIGH幅 | 68 | 70 | 75 | μs |
Arduinoでどうやって受信するか?ですが、この際、LOW幅は無視します。LOW期間は通信の同期を取るためのタイミング信号、クロックに相当する物、と考えられるからです。LOW期間は検知できることが重要で、幅が50μsかどうかは、重要ではありません。
重要なのはHIGH幅ですので、I/Oポートを監視して、HIGH幅を計測しましょう。おっと、「第4回 シフトレジスタを使ってみる」で学習済みです、「IDEが持っている機能が使えるときは使いましょう」でしたね(1^^)。Arduino IDEには、pulseIn()という関数が用意されています。
pulseIn()関数は、指定した端子を監視して、指定したレベル(LOWまたはHIGH)のパルス幅を測定して返します。今回の用途にぴったりです。ちょっと「んっ?」と思ったのは、似たような名前のpulseInLong()関数でした。関数のインターフェイスは、引数や返り値の型に至るまで同じですし、Language Referenceを読んでも、違いが何なのか、理解することはできませんでした。
pulseIn()とpulseInLong()で微妙に表現が異なる個所はありましたが、若干、pulseInLong()の方に惹かれたので、またまた、自分の直感でpulseInLong()を使用することにしました。
仕事となれば、「直観に従って意思決定するなんてとんでもない」と怒られそうですが、DIYですから、良いですよね……。
計測したHIGHパルスの幅から、ビットが0なのか1なのか、判定しなければなりません。26μsと70μsの1/2(=48μs)で判定する手もありますが、これはどちらもtyp値です。Th0のmax値(30μs)と、Th1のmin値(68μs)の1/2(=49μs)で判定すべきでしょう。1μsしか変わらない微妙な差ですが、「考え方」として選択。
せっかくここまで来たので、気温と湿度を測定するプログラムを組んで、一回だけ、うまくAM2302と通信できるか?確かめてみます。
プログラム:test_am2302.ino
実行サンプル:
waiting am2302 initialization...
ready
Temp.: 28.2[degC]
Humid: 57.1[%RH]
良いようです。ちなみに、上の測定をした時、気温・湿度表示付き電波時計の指示値は、28.2℃と53%RHでした。たまたまかもしれませんが、気温の方は同じ値、湿度は少し高めに出ました。
どちらの値が正しいのかわかりませんし、校正しようと思ったら、恒気温湿度槽が必要になりますので、雰囲気オーダが合っていればOKとみなします。テストの測定結果は、十分OKな範囲内です。
まだまだ、プログラムは完成していませんので、次回もう少しブラッシュアップしていきます。