NRF24L01+ と DS18B20+と Arduinoでリモート温度計2

無事DS18B20+センサを使って温度を計測することができましたので、NRF24L01+無線モジュールをつかって無線でデータを飛ばすことにします。 送り手側は電池駆動で試してみました。きっと、将来他の事例にも応用が利くと思います。

今回の実験のためにもう一台Arduino互換機を作る事にしました。秋月電子からATMEGA168/328用マイコンボード(1枚150円)、水晶振動子、抵抗、コンデンサ、ピンソケット等、必要最小限の部品を購入して組み立てました。USBインターフェースと電源レギュレーターは省略します。

ATmegaマイコンボードとAVRISP MkII

この基盤にはICSP端子がついていますので、電源とAVRプログラマAVRISPmkII を繋ぐとArduino1.0 IDEからブートローダとスケッチ双方が書き込めますので大変便利です。

さらに秋月電子からは 昇圧型DC-DCコンバーター(3.3Vタイプ)を仕入れました。入力電圧が0.7~3.3V出力電圧が3.3V最大出力電流が200mAという性能です。広告には電解コンデンサを1個追加するだけで使えると書いてありました。

センサー、無線モジュールの他にMCUの電源も DC-DCコンバータから供給するように配線します。

送信側ハードです

ハードは整いました。ソフトウエアはJ. Colizさんが書かれた RF24ライブラリにバンドルされている「pingpair_sleepy」スケッチを基にして作りました。この「pingpair_sleepy」は電池駆動のために消費電力を抑えるAtmegaのスリープモードとウオッチドッグタイマを利用しています。

ATmegaのスリープモードは

SLEEP_MODE_IDLE  アイドルモード
SLEEP_MODE_ADC   ADCノイズ低減モード
SLEEP_MODE_PW_SAVE パワーセーブモード
SLEEP_MODE_STANDBY スタンバイモード
SLEEP_MODE_PWR_DWN パワーダウンモード

5種類あって、パワーダウンモードが一番消費電力が少ないモードになっています。また、スリープモードから復帰するためにWatchdog(WDT)タイマーを使って割り込みを発生させます。

ウオッチドッグタイマーは、スリープモード中でも動いている内蔵128KHz発信器のクロックをカウントして指定のタイムアウト時に割り込み又はシステムリセットを行う機能を持ちます。

タイムアウト時間は16ms,32ms,64ms,128ms,250ms,500ms,1s,2s,4s,8sの中から選ぶことができまして、またそれをループさせることにより任意の間隔で一連の処理を行うことができます。

「pingpair_sleepy」スケッチでは setup_watchdog()のパラメータでタイムアウト時間を指定し、sleep_cycles_per_transmissionの値の倍数の期間だけスリープモードを繰り返すように組んであります。ただ、内蔵発信器のクロックなので時間の精度はあまり良くないようです。ATmega MCU内部動作の詳細はデータシートの訳 こちら を参考にさせていただきました。awawaさんありがとうございました。

スリープモードの時間はいろいろな値を設定することができますが、現在私は setup_watchdog(wdt_4s)そして sleep_cycles_per_transmission = 75として、5分に一回データを送るように設定しました。

以下にarduino1.0用に作成したスケッチを記述します。オリジナルから変更点は、温度センサーの値を読み込む処理、送受信役割分担(ロールピン)の部分を削除した所です。私のハードがNRF24L01+無線モジュールのCE,CSNがそれぞれデジタル8,9ピン、DS18B20+温度センサのDQがデジタル6ピンにつががっていますので、スケッチの該当する部分もそれに合わせてあります。温度センサの解像度は10bitに設定してあります。

送信側スケッチ Tx_data.zip
受信側スケッチ Rx_data.zip

このスケッチを動かすためには前述のOneWireライブラリRF24ライブラリをインストールする必要があります。

送信側と受信側双方をセットして動かすとシリアルモニターに温度が無事表示されました。シリアルモニタの通信速度はNRF24L01+無線モジュールの関係で57600bpsになっています。

実際に回路の電流を測ってみると、待機時に流れる電流が約0.2mAでした。どのくらい電池が持つか計算してみましょう。こちらの記事を参考にさせてもらいました。

スケッチが動いているときの消費電流は

 ATmega328P-PU         20mA
 NRF24L01+ モジュール   11mA
 DS18B20+  センサ        2mA
 DC-DCコンバータ         6mA (変換効率が85%と言うことで)
 
 計      おおむね 40mA
 

スケッチが動いている実働時間は2秒程度、待機している時間が約300秒(5分)なので平均の消費電流は

(2s/300s)*40mA+0.2mA ≒ 0.5mA

単三電池の容量が2000mAh程度なので

2000mAh÷0.5mA = 4000h(時間) ≒ 166日

計算では結構長く電池が持ちそうです。いずれこの期間についても検証してみたいと思っています。

※ 上記のハード・ソフトで 2012年3月3日 から 2012年8月17日まで、電池交換なしで5.5ヶ月間動き続けました。8月17日に落雷が原因で受け取り側のPC(サーバ)が不調にならなければ、もっと長い期間の記録になったはずでした。

NRF24L01+ と DS18B20+と Arduinoでリモート温度計 1

NRF24L01+を使って無線実験が成功したので、実用的な用途に応用です。本当は家の中の消費電力を計測したいのですが、他の方の製作記事を拝見すると結構大変そうです。今の私には少々荷が重すぎるようですね。なので、すかさず方針変更です。手始めに気温を測る温度計に挑戦です。これなら何とかいけそうです。

でも、一旦冷静になって考えると”気温を測ってどうするの?”という素朴な疑問がわいてきました。「実用的な用途」と言えるかどうかも論議が必要です….結局、結論がなかなか出てきそうにないので、先に進むために今そのことは考えないことにします。

温度を測るセンサーもいろいろあって迷いますが、秋月電子の広告を見て「1wireデジタル温度センサー」という文字が目にとまりました。値段も1つ300円とお手頃です。

Maximの1wireデジタル温度センサー DS18B20+
=特長=
・1wireインターフェースのシンプルなデジタル温度センサー
・すべてのデバイスに対してユニークな64ビットのシリアルコードを付与(内部ROMに書込済)
・データ線から電源を供給可能
・電源電圧:3.0~5.5V
・測定温度範囲:-55℃~+125℃
・精度:±0.5℃(-10℃~;85℃)
・ドリフト:±0.2℃

・・・

他の温度センサーと比べて精度が良いみたいですね。ネットで検索するとO-Famiry 電子工作の部屋さんのWeb中にDS18B20+の説明が書かれていました。またマニュアルの日本語訳もされています。すごいですね、頭がさがります。ありがとうございます。調べて行くとArduinoとの相性も良さそうな事がわかりました。これに決めます。

ArduinoでDS18B20+を使うためにPJRCさんのサイトから
OneWire Library:Download: OneWire.zip (Version 2.1)
このライブラリをダウンロードします。

zipファイルを解凍してできる「OneWire」フォルダをフォルダごとAduino1.0の「libraries」ディレクトリにコピーすると使えるようになります。

まずArduino単体で温度が読み取れるか試してみましょう。ブレッドボードにDS18B20+を配線します。このOnWireデバイスは、一つ一つのデバイスに64ビットのアドレスが割り振られているとのことです。ワンワイヤーと言っても現実には通信に2本の線が必要(パラサイトモード時)なのですが、カスケードにして複数つなぐことができるそうです。トランジスタ風のパッケージの中に、機能がぎっしり詰まっていますね。感心してしまいます。

今回私は、DS18B20+の2番ピン(DQ)はArduinoのデジタル6番ピンにつなぎました。ブレッドボードに配線をすませて、ライブラリ付属のサンプルスケッチ「DS18x20_Temperature」を実行します。

スケッチ中の
OneWire ds(10); // on pin 10
この行は、自分の環境に合わせ、6に変更しておきました。これでシリアルモニターに現在の気温が表示されるはずです。

スケッチを走らせると瞬時に気温が計測され、表示されます。センサーを指で触ると体温に反応して表示される温度が刻々と変化します。応答性能もよさそうです。

ちなみに配線を間違えると85℃の表示がされ、そこから微動だにしません。(なぜこのことを知っているかは秘密です)資料を見ると85℃はパワーオンリセット時にセットされるデフォルト値みたいです。配線には気をつけましょう。壊れなくて良かったです。

先ほどのPJRCさんのwebに、このスケッチには氷点下以下が測れないというBugがあると書かれていましたので、このスケッチを修正してみました。とりあえず接続するDS18B20+は1つなので、アドレス指定の部分は削除してしまいました。
[C]
#include
// OneWire DS18B20 Temperature Reading
// only slightly modified DS18x20_Temperature sketch
// from OneWire library examples bundle.
// http://www.pjrc.com/teensy/td_libs_OneWire.html
//

OneWire ds(6); // on digital pin 6 DS18B20+ (DQ)
const short Resolution = 12; //DS18B20 Resolution 9 – 12 Bit

void setup(void) {
Serial.begin(9600);

// Thermometer Resolution set (default 12 BITS)

short Sw_value ;
switch (Resolution){
case 9:
Sw_value = 0x1F;
break;
case 10:
Sw_value = 0x3F;
break;
case 11:
Sw_value = 0x5F;
break;
default:
Sw_value = 0x7F;
break;
}
ds.reset();
ds.skip();
ds.write(0x4E); // Write Scratchpad
ds.write(0x00); // User Byte 1 (not in use)
ds.write(0x00); // User Byte 2 (not in use)
ds.write(Sw_value); // set Thermometer Resolution

}

void loop(void) {
byte i;
byte data[12];
float celsius;

ds.reset();
ds.skip();
ds.write(0x44,1); // start conversion, with parasite power on at the end

int Conv_time = 1000;
switch (Resolution ){
case 9 :
Conv_time = 100;
break;;
case 10 :
Conv_time = 200;
break;
case 11 :
Conv_time = 400;
break;
}
delay(Conv_time); // Resolution to 9 BITS conversion time 93.75ms
// 200 : Resolution to 10 BITS conversion time 187.5 ms
// 400 : Resolution to 11 BITS conversion time 375 ms
// 1000 : Resolution to 12 BITS conversion time 750 ms
// we might do a ds.depower() here, but the reset will take care of it.

ds.reset();
ds.skip();
ds.write(0xBE); // Read Scratchpad

for ( i = 0; i < 9; i++) { // we need 9 bytes data[i] = ds.read(); } if (OneWire::crc8(data,8) != data[8]) { Serial.println("CRC is not valid!"); return; } // convert the data to actual temperature int raw = (data[1] <<8) | data[0]; switch ( Resolution){ case 9: raw = raw & 0xFFF8; // 9 bit resolution break; case 10: raw = raw & 0xFFFC; // 10 bit resolution break; case 11: raw = raw & 0xFFFE; // 11 bit resolution break; } celsius = (float)raw / 16.0 ; Serial.print(" Temperature = "); Serial.print(celsius); Serial.println(" C "); } [/C] ちゃんと動くか否か心配なのでテストをすることにしました。昨日まで冷え込んでいたので夜明け前なら外気温は氷点下なると見込んで測ってみました。が、残念ながらあとわずかの所で氷点下になりません。2~3日繰り返しましたが徒労に終わりました。このまま待っていると来冬になってしまいそうなので、DS18B20+を簡単なプローブにして氷と塩を使い測ってみました。まるで理科の実験です。小学校3年生以来ですね。 解像度( Resolution)は9,10,11,12ビットのいずれかで設定することができ、それぞれ0.5℃,0.25℃,0.125℃,0.0625℃単位で測ることができます。設定をしないと12ビットになります。解像度が大きくなるほど温度をデジタルデータに変換する時間がかかりますので、電池駆動で消費電力を少なくしたい時は解像度を低く抑えた方が良いかもしれません。

nRF24L01+無線モジュール SCANNER

nRF24L01L+には電波の強度を計測する機能(RPD)があって、RF24ライブラリ作者のMr. J.Colizさんが2.4GHz帯(ISMバンド)の電波利用状況を計測するスケッチを書いてくれました。RF24ライブラリをインストールした後、[Examples]-[RF24]-[scanner]でロードできます。

ArduinoにnRF24L01L+無線モジュールをセットし、スケッチを走らせるとシリアルモニタには下記のように表示されます。

RF24/examples/scanner/に続く2行はチャンネル(RF_CH)を表示しています。このチャンネルは縦に読むのがミソで、十六進数で00~7fの間、2.4GHz帯の1MHzごと、128CH分が表示されています。次の行からはスキャンした時点で使われている電波の強度を表示しています。

チャンネルと周波数の関係式 : F = 2400 + RF_CH [MHz] (中心周波数)

WindowsPC用のソフトウエアで無線LANの電波利用状況を調べるNetwork Stumblerというものがあり、同時に計測してみました。無線LANも802.11 b/g/n規格の物は2.4GHz帯を使用します。

私の所は郊外で、家も少ない訳なのですが、それでも結構電波が飛んでいますね。一番下の行が私の使っている無線ルータです。無線LANの11CHを使っていて、この中では一番シグナルが強いです。ややっこしいのですが、無線LANのチャンネルは上記のチャンネルとは異なります。

2.4GHz帯の無線LANの電波の割り当ては、第1CHの中心周波数が2412MHzで、それ以降は5MHzずつ中心周波数が高くなり13CHまで使われています。ただ、使う帯域は22Mhzですので隣どおしのchはかなり重なってしまいます。確か、全く周波数が重ならないように802.11 b/gの無線LANを使うには、3つの組み合わせしかなかったと思います。

上記無線LANの11chは中心周波数が2462MHzですので、2451MHz~2473Mhzを使っている事になります。これは、ちょうどシリアルモニタに表示されたピンクのアンダーラインの所に該当します。双方を比較してみると、Arduinoで計測した方が明らかに詳しく測ることができますね。

nRF24L01+のデータシートを見るとnRF24L01+の占有する周波数の帯域は通信速度によりますが、250kbps と 1Mbpsで1MHz、2Mbpsで2MHz 程度だそうです。設定できる範囲は1Mhz刻みで2.400GHz から 2.525GHzと書いてあります。

このスケッチを使って無線LANや他の電波と干渉しないようにnRF24L+をセットする事ができます。ちなみにこのRF24ライブラリはデフォルトで76CH(0x4C)、2476MHzを使うようになっています。