Raspberry PiとArduino 間 nRF24L01+ 通信実験

一月の末に RS Compornets社に注文予約して待つこと8ヶ月、やっと Raspberry Piが先日我が家に届きました。私はケースも一緒に頼んだので送料込みで合計金額が$49.49 USD 、日本円で¥4,082でした。

早速 Debian派生のLinux、Raspbianをインストールしてみました。イントールにあたってはJunさんのホームページがとても参考になります。raspbianのバージョンですが、私は2012-09-18-wheezy-raspbianをインストールしました。蛇足になりますが、「ラズビアン」っては、日本語では少々危ない響きですね。わたしは滑舌(かつぜつ)が良くないのでとても心配です。

GUIで操作することもできて、ネットワークにも繋がる。それでいてこの価格は、すごいの一言ですね。色々と応用ができそうです。

その後、Raspbery Piの GPIO で nRF24L01 を動かす記事を発見しました。

Raspberry Pi View topic – NRF24L01 RF Transceiver

Beaglebone 用に書かれたnRF24L01 Libraryを Raspberry Pi で動かせるように書き直したと書かれています。( Thanks Mr. Purinda Gunasekara & Mr./Ms. direk! ) この記事を参考に、Raspbery Pi と Arduinoとの通信実験を行いました。

※ 2.4GHz帯 ISMバンドを使用する小電力機器でも日本国内で使用する為には、電波法により技術基準に適合していることを、特定の認証機関で証明してもらう必要があります。違反しますと、1年以下の懲役または100万円以下の罰金という刑事罰の対象になり得ますので、くれぐれもご注意ください。

こちらより nrf.tar.gz をダウンロードします。(Click here to start download from sendspace をクリックするとダウンロードできます)

以下、私はRaspberry PiにSSHで接続して作業を行いました。

$ tar -xzvf nrf.tar.gz
$ cd nrf3

解凍してできた nrf3ディレクトリにある main.cpp がRaspberry Pi側のサンプルコードです。pingを打ってArduino側から戻ってくるreplayを待ちます。ArduinoのRF24ライブラリのサンプルプログラムを元に作られているようですね。roleピンの機能は省かれているみたいです。

また、pingpair_dyn_arduino_pongbackディレクトリにあるコードはArduino側のものになります。Raspberry Pi からのping を待ち、受け取ったら応答を返す動作をします。ちなみにArduinoにはRF24ライブラリが必要です。

$ make

コンパイルすると dist/Debug/GNU_Arm-Linux-x86/ ディレクトリに rf24bb が作成されます。これが実行するプログラムになります。

次に上記プログラムを実行するために、Raspberry PiでSPIを使えるようにします。「竹本 浩氏のページ」を参考にさせていただきました。

/etc/modules に spidev の1行を追加して、/etc/modprobe.d/raspi-blacklist.confのblacklist spi-bcm2708を#でコメントアウトします。

</etc/modules>

# /etc/modules: kernel modules to load at boot time.
#
# This file contains the names of kernel modules that should be loaded
# at boot time, one per line. Lines beginning with "#" are ignored.
# Parameters can be specified after the module name.

snd-bcm2835
spidev

</etc/modprobe.d/raspi-blacklist.conf>

# blacklist spi and i2c by default (many users don't need them)

#blacklist spi-bcm2708
blacklist i2c-bcm2708

リブート後、

# ls -l /dev/spidev* を実行して /dev/spidev0.0 and 0.1 が表示されることを確認します。これでソフトウエアの準備は完了です。


ハードウエアの結線ですが、raspberry pi 側のmain.cppの中に RF24 radio(8, 25); の記述があるのでnRF24L01のCEをGPIO 8,CSNをGPIO 25に繋ぎます。

同様に Arduino側 pingpair_dyn_arduino_pongback.ino 中に RF24 radio(4,3); の記述があるのでnRF24L01のCEをdigital 4,CSNをdigital 3に結線しました。必要であれば数値を変更してピン・アサインを変えることができます。

$ sudo ./nrf3/dist/Debug/GNU_Arm-Linux-x86/rf24bb

全てをセットした後に、Raspberry Pi 側ではルート権限で rf24bbを走らせます。同じくArduino側もスタートさせておきます。

Raspberry Pi のターミナルに以下のように表示され、Raspberry Pi と Arduino 間の通信を確認することができました。

STATUS = 0x0e RX_DR=0 TX_DS=0 MAX_RT=0 RX_P_NO=7 TX_FULL=0
RX_ADDR_P0-1 = 0xf0f0f0f0e1 0xf0f0f0f0d2
RX_ADDR_P2-5 = 0xc3 0xc4 0xc5 0xc6
TX_ADDR = 0xf0f0f0f0e1
RX_PW_P0-6 = 0x20 0x20 0x00 0x00 0x00 0x00
EN_AA = 0x3f
EN_RXADDR = 0x03
RF_CH = 0x78
RF_SETUP = 0x25
CONFIG = 0x0f
DYNPD/FEATURE = 0x3f 0x04
Data Rate = Model = CRC Length = PA Power = Now sending length 4...Got response size=4 value=ABCD
Now sending length 6...Got response size=6 value=ABCDEF
Now sending length 8...Got response size=8 value=ABCDEFGH
Now sending length 10...Got response size=10 value=ABCDEFGHIJ
Now sending length 12...Got response size=12 value=ABCDEFGHIJKL
Now sending length 14...Got response size=14 value=ABCDEFGHIJKLMN

・・・

Xbox 360 有線コントローラ ジョイスティック(アナログスティック)修理

Xbox360有線コントローラ


久々の投稿になってしまいました。今回はXbox360のコントローラの修理にチャレンジしました。

私はあまりコンピュータゲームはしないのですが、唯一の例外がXbox360上のCALL OF DUTY というタイトル。FPS(一人称シューティングゲーム)というカテゴリに属する、戦争もののテレビゲームです。

各シーンの描写がとてもリアルにできていて、オンライン対戦ではいつも顔が真っ赤になる程熱中しています。残念なことに歳とともに反射神経もにぶくなってしまい。成績がぜんぜんよくなりません。【 くやしぃ~~ 】

ハード、ソフトとも子供から借りて使っているのですが、ついつい力が入ってコントローラがすぐだめになってしまいます。ここ何年かの間にコントローラを何個か買いなおさせられてしまいました。私の場合壊れる箇所が決まっていて、左アナログスティックの押し込みボタンが壊れてしまいます。このジョイスティックの部品交換をします。

googleで調べて

【ebay (seller waxdvd)】「XBOX 360 REPLACEMENT CONTROLLER JOYSTICK POTENTIOMETERS」 2個で $8.25
+ $4.5 shipping

【http://www.7daysget.com/】「Replacement Handle Joystick 3D for Xbox 360」 1個 $3.69
+ Registerd Air Mail $1.99 (送料無料も選べます)

左はhttp://www.7daysget.com/ 右はebay (seller waxdvd)から購入。

上記2箇所からジョイステック部品を購入しました。後でわかったのですが、写真左側の品物とよく似た部品が楽天でも売りに出ていました。

ネジをはずし分解したところ

無線コントローラのトルクスネジと違い、有線コントローラは普通のプラスネジ7個で止められています。1箇所はシールの下になっていて見つけるのに手間取りました。また、このシールをはがすと保証が無効になるようです。

開けてみてびっくりしたのですが、長年使っていたコントローラは思いのほか手垢やほこりで汚れています。ケースやボタン等、プラスチックやゴムでできている部材は水洗いをして、きれいに乾かしてあげました。一つ一つの部品は簡単にはずすことができますが、位置や取り付け方向などを覚えておかないと、後で組み立てるときにパズルの様になってしまいます。できるだけ詳細に覚えておきましょう。デジカメで写真に撮っておく事も良い方法だと思います。私はこれを怠って、かなり苦労してしまいました。

左トリガとジョイスティック部品

振動用のモータ2個のコネクタをはずします。おもりの大きさが違うところに注意します。基盤から伸びるUSBケーブルのコネクタは、はずすのが難しそうだったのでテープを貼り付けて養生をすることにしました。

左のジョイスティック部品はハンダ付けされているので、そのハンダを除去しなければならないのですが、そのためには、ちょうど干渉している左トリガをはずさなくてはなりません。写真親指のところに見えている3箇所のハンダをハンダコテで温めながら、吸い取り器で取り除きます。このトリガは再利用しますので注意深く取りはずさなくてはなりません。

取り外し後の基盤とハンダ吸取り器

ジョイスティク部分は14箇所のハンダを吸取ります。何かコツが必要なのか、私には完全には吸取ることが出来なかったようです。作業後もかすかに付着しているみたいで、素手では簡単に基盤から外すことができません。気を取り直して、ペンチでつまんで引き抜くとあっけなく外れました。トリガと違い代替部品があるので気が楽です。

ここまでくれば終わったも同然です。新規購入のジョイスティック部品やトリガをハンダ付けした後、元の通りにケースに組み立て直します。(注… 私の場合、終わったも同然の後が長かった)

修理したコントローラの操作感ですが、残念ながら純正品と同じという訳ではありませんでした。7daysget.comから購入した部品は押し込みボタンの遊びが少なく、前後左右の動きも少し柔らかいような感触です。まぁ、ぎりぎり許容範囲内と言ったところでしょうか。ebayから購入した部品は、ほぼ純正品に近い感触でした。(それでも少し違和感があります)

修理した2ヶのコントローラは子供から無償でもらうことに成功したので、マイコントローラとして活躍しています。これで当分新しいコントローラを買わずにすみそうです。

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

さて、シリアルモニタに温度を表示させるだけではつまらないので、現在動いている自宅サーバにログを取って、その結果をweb上からグラフにして表示させてみることにします。

Kenji.Yさんが構築された素晴らしいシステムには遠く及びませんが、とにかく頑張りましょう。Ethernetシールドを使用してHTTP Getメソッドでサーバにデータをcsv形式で記録し、それを基にグラフ作成です。グラフの描写はhighstockで行うことにしました。

サーバ側のコードはphpで書きました。これは、Mapo堂さんの記事を参考にさせていただきました、ありがとうございます。現在私のところのPHPは Version 5.3です。

tempera_log.php
[PHP]

[/PHP]
動作確認のためにブラウザから

http://サーバアドレス/パス/tempera_log.php?tempera=1234.5

などとアドレス欄に入力してtempera_log.csvが作成され、数値が記録されるか確かめます。私の場合データの投稿はLAN内からのアクセスですので、外部からこのファイルにアクセスできないようにアクセス制限を掛けておきました。もし将来仮に、インターネット側からデータを送らなければいけないときは、安全性を考慮して別の手立てをしなければならないでしょう。

ログファイルの置かれているディレクトリはwebサーバを動かしているユーザーから書き込む事ができるようにアクセス権を設定します。私はいつもこのことで失敗してしまいます。(学習能力がまるで無い)

続いて、先日作成したRx_data.inoにwebサーバアクセス用のコードを付け加えます。 Arduino 1.0 の Examplesにあるwebclientスケッチを元に作成しました。

Rx_data_log.ino ( Rx_log_data.zip )

Rx_data.ino スケッチの冒頭に追加します

[C]
#include

// Ethernet configration

byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED }; //ほとんどの場合macアドレスはこのままで大丈夫です
byte ip[] = { 192, 168, 1, 201 }; //イーサネットシールドのIPアドレス:LAN環境に合わせて設定変更が必要です
byte gateway[] = { 192,168,1,1 }; //デフォルトゲートウエイアドレス: 同上
byte subnet[] = { 255, 255, 255, 0 }; //サブネットマスク: 同上

IPAddress Logging_server(192,168,1,100); //logを記録するwebサーバのIPアドレス

EthernetClient client;   // イーサーネットクライアントライブラリ をイニシャライズ
[/C]

void loop(void)内の最後に追加します

[C]
// start the Ethernet connection:
Ethernet.begin(mac, ip ,subnet,gateway ); //subnet,gatewayの順番はArduino1.0のためのものです(バグ?)
delay(1000); // イニシャライズの為のディレイです

if (client.connect(Logging_server, 80)) {
Serial.println(“connected to Logging server”);

client.print(“GET /cgi-bin/tempera_log.php?tempera=”); // /cgi-bin/tempera_log.phpへ HTTP GETリクエストです:
client.print( got_data);
client.println(” HTTP/1.0″);
client.println();
}
else {
Serial.println(“Logging server connection failed”);
}
client.stop();
[/C]

さて最後に一番手こずったのがグラフ描写です。今回試したhighstockは以前使ったRRDtoolsとは違いJavaScriptで書かれていて、クライアント側でグラフを描くものです。

Highsoft社が開発元で、個人利用であればフリーで使うことができます。また、姉妹版のhighchartsというライブラリもあって、豊富な種類かつ表現豊かなグラフを描くことができます。highstockは時系列のデータに対して相性が良いようです。

こちらからライブラリをダウンロードして適切なディレクトリに解凍して設置します。

ドキュメントを参考にしてプログラムを組んだのですが、CSVファイルの取り込みがどうしてもうまくいかなくて苦労しました。サポートフォーラムの中を必死に探してようやく動かすことができました。でも、ひょっとすると、もっと根本的な恥ずかしい間違いを犯しているかもしれません。

このソフトに関しては日本語の資料があまりなく、へっぽこメモさんのwebに助けていただきました。ありがとうございます。

highstock.jsとtempera_log.csvのファイルのパスに注意します。文字コードセットをUTF8で保存します。

temperature.html





Highstocks Example






こちらにグラフサンプル


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を使うようになっています。

nRF24L01+ 無線実験

Atmega328P-PUと無線ユニット

前回は失敗していまいましたが、こりずに挑戦です。今回もまたebayから仕入れた品物のお話です。

マイコン+無線といいますとxbeeが有名どころのようですが、今回、怪しいもの好きの私が見つけた品物はNordic Semiconductor社のnRF24L01+というチップを使った無線ユニットです。

「NRF24L01+ 2.4GHz Wireless Transceiver Module FOR Arduino」というタイトルで売っていました。最大の特徴はその安さです。私が購入した時は2個セットで約$6(送料込)でした。とても小型で、アンテナもユニットの基盤にプリントされています。

この製品の特徴は

  • 無線LAN等に使われている2.4GHzのISMバンドで通信を行う
  • 通信速度を250Kbs,1Mbps,2Mbpsから選ぶ事ができる
  • 1.9V-3.6Vで動作する。(※5Vをかけると壊れてしまう。注意が必要です)入力ピンは5Vトレントラント
  • 出力1mW (0dBm)
  • 低消費電力 送信1mw時11.3mA、受信2MBps時13.5mA

他にも超低電力消費モードがある事や、最大1台対5台の通信が出来ること(MultiCiever機能)がデータシートに記載されていました。周波数は2.400GHz~2.525GHz間で1MHz単位で変えることができます。

一定出力以下のISMバンドの通信機器は免許不要ということですが、日本国内で使用するためには認可が必要みたいですね。この製品はそのような認可はとってないでしょう。まぁ、個人的にちょっと実験するだけならば問題にはならないでしょう。残念ですが日本国の法律に抵触する可能性がありますね。善良な市民の皆さんは許可された国に行き実験しましょう。

ということで、まず初めにYour Duinoさんのwebを参考にさせていただきました。ぱっと見て気付きましたが、このページには私が購入したタイプとピン配置が違う製品が記載されています。よくわかりませんが、こちらの図のジグザグアンテナの方が良く電波が飛びそうに見えます。これは早くも失敗だったでしょうか?

気を取り直して、本文を良く読むと、Arduinoでこの無線ユニットを使うために2つのライブラリが作られている事がわかりました。1つはRF24ライブラリ、もう1つはMirfライブラリだそうです。

こちらのページにRF24ライブラリのダウンロード先が書いてありました。もうひとつのMirfライブラリはこちらのページに情報があります。残念なことにMirfライブラリはArduino 1.0環境では現時点で動作しませんでした。(0023環境では動きました)

ここから後の作業は、このブログタイトル通り試行錯誤でした。いかんせん初心者ですので誤りがあるかもしれません。もし記述に誤りがあった時には御指摘いただければ有難いです。NRF24L01+をより活用することができると書いてあったRF24ライブラリを使って通信を試みます。

  • こちらからRF24ライブラリをダウンロードします。 Download RF24 Library here (ZIP or GZ)
  • ダウンロードしたファイルを展開してできるフォルダ名(現時点ではmaiacbug-RF24-94635e8)をRF24にリネームする。
  • そのRF24フォルダごと Arduino-x.x\libraries\ 以下にコピーする

上記の手順でライブラリの登録ができました。

さてnRF24L01+ユニットとArduinoとの結線です。RF24ライブラリ作者であるmaniacbugさんのwebに載っていました。

【nFR24L01+とArduinoの結線】
nFR2401+ Arduino
GND GND
VCC 3.3V
CE 9
CSN 10
SCK 13
MOSI(MO) 11
MISO(MI) 12
IRQ 通常は未使用、チャレンジャーは2

Your Duinoさんのwebに書かれている結線とはCE,CSNの配線が異なっています。デフォルトでRF24ライブラリを使用するならば、上記が正解みたいです。)
--->1/19追記 CE,CSNのピンアサインは変えることができます。


最初ブレッドボードで実験しようとしたのですが、ピンの間隔は一般的な2.54mmなのですが列の間隔が足りなくてブレッドボードではうまくいきませんでした。(ちょっと考えれば分かりそうなものです。まだまだですね。)

というわけで今回は片側がオス反対側がメスのブレッドボード用のジャンパワイヤーで配線しました。また、実験には同じセットが2台必要になります。

さて準備ができました。Arduino 1.0の環境で実験開始です。Arduino IDEのメニューからサンプルスケッチを試します。 [File]-[Examples]-[RF24]-[GettingStarted]を、無線ユニットをつないだ2台のArduino双方にアップロードします。

シリアルモニタを表示させてボーレートを 57,600baud に合わせます。するとシリアルモニタには

RF24/examples/GettingStarted/

ROLE: Pong back

*** PRESS 'T' to begin transmitting to the other node

STATUS        = 0x0e RX_DR=0 TX_DS=0 MAX_RT=0 RX_P_NO=7 TX_FULL=0

RX_ADDR_P0-1  = 0xf0f0f0f0d2  0xf0f0f0f0e1

・・・ 以下略

このようなステータスが表示されます。現在双方のArduinoはPong backモードですので、’T’をタイプしてSendボタンを押します。

*** CHANGING TO TRANSMIT ROLE -- PRESS 'R' TO SWITCH BACK

Now sending 428654...ok...Got response 428654, round-trip delay: 23

Now sending 429678...ok...Got response 429678, round-trip delay: 24

Now sending 430703...ok...Got response 430703, round-trip delay: 24

・・・ 以下略

このように表示されれば成功です。ミリセコンド単位でpingのディレイが表示されました。

出力が1mWということで、どのくらいの飛距離がでるか実験してみました。デフォルトの1MBpsで室内障害物無しの条件で約10mは全く問題無しでした。さらに、外に出て木造建物の壁1枚を隔て約15m程度はなれた所で限界が来ました。いろいろな環境条件によって変わると思いますので、これはごくおおざっぱな参考値です。データシートには256Kbpsにすると消費電力は増えるが受信感度が増すという内容の記述がありました。距離的にちょっと心もとない気もしますが、使う用途によっては十分実用になると思います。

以下の事柄は私の環境に限ったことかもしれません。ジャンパー線を外しプロトシールドにピンソケットを付けて実験してみました。φ0.26mmのジュンフロン線で配線をしたのですが、最初全てのパケットがタイムアウトになってしまいました。一瞬言葉を失いましたが、冷静に戻った後、モジュールにつながる電源の3.3VとGNDの配線を太いものにしてようやく動作しました。電源周りに気を付けた方がよさそうです。現在はさらにバイパスコンデンサを取り付け、他の信号線も太い配線にしました。

ebayでの買い物 2点

(1点目) 【Prototype Shield ProtoShield & MINI Bread Board FOR Arduino Duemilanove】
というタイトルで売り出されていたユニバーサル基盤のシールド+小さなブレッドボードです。ブレッドボードの裏には両面テープがついていて、基盤の上に張り付けることができます。キットかと思っていたのですが、完成品でした。(送料込 約$8.0)

両面スルーホール。リセット用のタクトスイッチとICSP用ピンヘッダ用のスルーホールがあって、それぞれ配線されています。基盤の短辺方向に延びるピンソケットは、それぞれ5V、GNDにつながっています。14PINの表面実装用のSOICを載せるスペースが1か所ありあます。

LED1、2は1KΩの抵抗を経由してカソード側がGNDにつながっていて、アノード側はランドで止まっています。LED2は1kΩ、タクトスイッチと直列につながり、両端はランドで止まっています。 タクトスイッチS1は片側がGND、反対側がランドで止まってます。

(2点目)【Arduino UNO (ATMEGA328P / ATMEGA8U2) + USB Cable WL
というタイトルで売り出されていた品物。(送料込 $18.5)

ずいぶん安いな、と思って買ったのですが、結局のところ非常に似ている非純正品でした。まぁ、良く考えればこの値段では当然です。でも、ebayで表示されていた写真は純正品みたいだったのですが….

もう一度webに戻り、その業者の商品説明を読み直しました。すると、最後の方の目立たない所に「写真は図解の意味だけのものである」などと書かれていました。また同時に「気に入らないことがあったら、フィードバックをする前に必ずメールをよこしてくれ」との注意書きがあります。少々グレーゾーンの業者ですね。

さて、この商品には続きの話があります。内容は互換品なので動きさえすれば良いと思い、動作確認をすることにしました。WindowPCにUNO用のデバイスドライバをインストールした後に、Arduino IDE からスケッチをアップロードしましたが、エラーが出てアップロードできません。リセットスイッチを押して読み込ませようとしてもだめでした。IDE内のTools-BoardでもちゃんとUNOを指定してあります。IDEのバージョンを変えたりもしてみました。

業者に連絡しようか否か迷いましたが、言葉の壁や手間を考えると非常におっくうです。金額は大したことは無いですが、輸入品の悪い面をまともに食らってしまいました。国内の業者であればこのような事はまずないでしょう。あったとしても簡単に連絡して話ができます。

幸いICSPピンから書き込みができましたので、ブートローダ、fuseビット等を上書きして通常動作できるようになりました。スケッチがアップロードできない原因はいろいろあると思いますが、もし私が最初に購入したArduinoがこれでしたらお手上げだったと思います。今回は非常に良い勉強になりました。次回からは注意します。

Ethernet shield メール受信…か?

キャンプに行く予定はないですが....

前回Ardunoからメールを打つことができましたので、今回はメールの受信です。通常のメールソフトみたいな物はとても作る技術がないので、いま何通メールが届いているかシリアルモニタに表示させてみましょう。

前回、Telnetを使いSMTPサーバにアクセスしました。今回も前回と同様にArduino君にやってもらう前に、手動でメール(POP3)サーバにアクセスして試してみます。なおPOP3コマンドはwww.atmarkit.co.jpさんのWebに詳しく書いてあります。

[POP3 サーバにtelnetで接続してメールを読み込む]

telnet POP3サーバアドレス 110  

Connected to mail.hogehoge.co.jp.
Escape character is '^]'.
+OK Hello there. <24680.1326193375@mail.hogehoge.co.jp>

USER ユーザー名 

+OK Password required.

PASS メールパスワード 

+OK logged in.

STAT                   ;メールの数とサイズを尋ねる。 

+OK 2 2632            ;2通あることがわかります。 

RETR n                 ;n番目のメールを表示する。 
 
+OK message follows 
 Return-Path: baz@foobar.co.jp
Received: from deredere006.hogehoge.co.jp (LHLO deredere006.hogehoge.co.jp)
 (118.23.178.10) by daradara001.hogehoge.co.jp with LMTP; Tue, 10 Jan 2012
 19:23:01 +0900 (JST)

        ・・・ 以下内容表示 ・・・
        
QUIT                   ;通信終了 

+OK Bye-bye.
Connection closed by foreign host.

私のプロバイダのPOP3サーバでは、このようなやり取りになりました。黒字はPOP3サーバが表示した文字ですが、POP3サーバの種類によって異なる場合があります。さてさて手動では問題なくつながりました。でも、これからどうしたものでしょう。今回も諸先輩の知恵をお借りしました。

そこで、ようやくみつけたのがこのMy Open Source Projects さんのArduino POP3 Email Checker  です。(Thx Mr. Torchris!!)

こちらの作品は、LEDを使ってメールの数を知らせていますね。さすがです。でも、私は当初の予定通りシリアルモニタに表示させるだけにして置きましょう。実を言うと、ここにたどり着くまでに精魂使い果たしてしまいました。もう、余力がありません。ほんとです。

こちらに書かれていたコードを見て、おぼろげながら分かってきました。POP3サーバから表示されるデータは全部POP3コマンドを送信してから取り込めばよいみたいです。この参考スケッチではQUIT(終了)まで指示を行った後にデータを取り込んでいます。

だいたい理解できました、この作者が参考にしたお手本を元にしてスケッチを作ります。ほぼそのまんまです。

/* 
  Simple POP3 Email Checker
  Arduino 1.0 version
 */
  
#include <Ethernet.h>
#include <SPI.h>
 

 byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };
 byte ip[] = { 192, 168, xxx, xxx };     // Arduino EtherShield IP address
 byte gateway[] = { 192,168, xxx, xxx };  // Default Gateway IP address
 byte subnet[] = { 255, 255, 255, 0 };  // Subnet Mask
 
 byte server[] = { xxx,xxx,xxx,xxx };    // POP3 server address
 
 EthernetClient client;
 long updateTimer;
 boolean clientConnected = false;

void setup()
{
   Serial.begin(9600);
}
 
void loop()
{
  if ((millis() - updateTimer) > 10000)
  {
    Ethernet.begin(mac, ip ,subnet,gateway ); //Arduino 1.0 see http://arduino.cc/forum/index.php?topic=83800.0
    
    delay(1000);
 
    Serial.println("connecting...");
 
    if (client.connect(server, 110))        //POP3 port 110
    {
       Serial.println("connected");
       client.println("user  ユーザー名");
       client.println("pass  パスワード");
       client.println("stat");
       client.println("retr 1");
       client.println("QUIT"); 
       clientConnected = true;       
                                                   // **** (2) **** 
     } else {
       Serial.println("connection failed");
     }
     updateTimer = millis();
  }

  if (clientConnected)
  {
     if (client.available()) 
     {                    
       char c = client.read();                    // ***** (1) *****
       Serial.print(c);                           // ***** (1) *****
      }

      if (!client.connected())
      {
      Serial.println("disconnecting.");
      client.stop();
      clientConnected = false;
     }  
   } 
}
 

まず試しに、上記のスケッチでメールの数と、1番目のメールを表示させることができました。

これで、サーバから受け取ったすべての文字の表示をすることができました。さて、STATで表示されるメール数を取り出すにはどうすればよいでしょうか?

こちらもいろいろ検索してわかりました。find()関数parseInt()関数を使って取り出します。先程の例で、サーバから送られてくる文字列は以下の通りです


Connected to mail.hogehoge.co.jp.
Escape character is '^]'.
+OK Hello there. <24680.1326193375@mail.hogehoge.co.jp>
+OK Password required.
+OK logged in.
+OK 2 2632               ;この2をゲットしたい

・・・

ストリームクラスで使うfind()関数ですが、この場合ちょっとだけ工夫が必要です。4番目の”+OK”にヒットさせてparseInt()関数でメールの数を取り込みますが、今回は同じ文字列が複数あるので2段階にしなければなりません。


       if ( client.find("+OK logged in."))
       {
         client.find("+OK ");
         int count = client.parseInt(); 
         Serial.print(" Your mail = ");
         Serial.println(count);       
       }

(****** (1) ***** の場所を書き換えます)

これで良いはずだと思っていたのですが実際動かしてみると、うまくいきませんでした。結局のところ、理由不明ですがfind()関数を使う前にディレイを入れないと動かない事が分かりました。 苦労をしたので、この事を発見した時は少し涙が出てきてしまいました。この事象はArduino 1.0 に限った、特有の事かもしれません。

    delay(1000);       //この数値が適切か否かはわかりませんが、必要でした

(****** (2) ***** の場所に書き加えます)

ようやく完成しました。(・・・・と、喜んでいたのですが、しばらく動かしていると止まってしまいます。残念。原因が分かりません。前途多難です。)

Ethernet Shield メール送信

メール...です

思わぬ伏兵にやられそうになってしまいましたが、気を取り直して再び前進です。TCPを使ってwebサーバと通信ができましたので、今回はArduinoからメールを送信する実験です。

私たちがメールを送る時、その作業はメールソフトがやってくれるので、普段は気に留めることもありません。しかし、今回はArduino君にやってもらうので、その前に手動で試してみます。

プロバイダによっては流儀が違って、この方法ではメールを送れないかもしれません。私は自宅でメールサーバを運用しているので、そちらで試してみました。

[SMTP サーバにtelnetで接続してメールを送る]

telnet  SMTPサーバアドレス  25


HELO クライアントドメイン 
MAIL FROM:送信元メールアドレス 
RCPT TO: 送信先メールアドレス 

DATA
Subject:題名 
From: 送信元メールアドレス 
To:   送信先メールドレス 
本文 
.
QUIT

このような順番で通信です。さっそく自宅にあるDebian LinuxPCのターミナルから試してみましょう。

$ telnet mail.hogehoge.co.jp 25

Trying 123.xxx.789.101...
Connected to 123.xxx.789.101.
Escape character is '^]'.
220   mail.foobar.com ESMTP Postfix (Debian/GNU)
HELO foobar.co.jp
250 mail.foobar.co.jp
MAIL FROM:baz@foobar.co.jp
250 2.1.0 Ok
RCPT TO: hoge@hogehoge.co.jp
250 2.1.5 Ok
DATA
354 End data with <CR><LF>.<CR><LF>
Subject:test
From: baz@foobar.co.jp
To:   hoge@hogehoge.co.jp
Hello world.
.
250 2.0.0 Ok: queued as CE7AE57054D
QUIT
221 2.0.0 Bye
Connection closed by foreign host.

やりました、ちゃんと送信できました。さて、先日のEthernetClient()を元にしてスケッチを作ってみましょう。Arduino 1.0 の環境で動かします。

/* 
  Simple SMTP Email Sender
  Arduino 1.0 version
 */
#include <Ethernet.h>
#include <SPI.h>
 

 byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };
 byte ip[] = { 192, 168, xxx, xxx };     // Arduino EtherShield IP address
 byte gateway[] = { 192,168, xxx, xxx };  // Default Gateway IP address
 byte subnet[] = { 255, 255, 255, 0 };  // Subnet Mask
 
 
 byte server[] = { xxx,xxx,xxx,xxx };    // SMTP server address
 
EthernetClient client;
 
void setup()
 {
   Ethernet.begin(mac, ip ,subnet,gateway  ); //Arduino 1.0 http://arduino.cc/forum/index.php?topic=83800.0
   Serial.begin(9600);
 
  delay(1000);
 
  Serial.println("connecting...");
 
  if (client.connect(server, 25)) {       //port 25
     Serial.println("connected");
     client.println("HELO foobar.co.jp");
     client.println("MAIL FROM: baz@foobar.co.jp");
     client.println("RCPT TO:  hoge@hogehoge.co.jp");
     client.println("DATA");
     client.println("Subject:test");
     client.println("From:  baz@foobar.co.jp");
     client.println("To:    hoge@hogehoge.co.jp");     
     client.println("Hello world.");
     client.println(".");          
     client.println("QUIT");          
     client.println();
   } else {
     Serial.println("connection failed");
   }
 }
 
void loop()
 {
   if (client.available()) {
     char c = client.read();
     Serial.print(c);
   }
 
  if (!client.connected()) {
     Serial.println();
     Serial.println("disconnecting.");
     client.stop();
     for(;;)
       ;
   }
 }
 

例によってMACアドレスがDE:AD:BE:EF:FE:ED (Dead Beef Feed 「死んだ牛のえさ?」)になっていますが家庭内LANに接続する分には問題ないでしょう。このまま続行です。IPアドレス、デフォルトゲートウエイ、サブネットマスクは適切な値にセットします。SMTPサーバのIPアドレスも調べてセットです。果たして実行結果は?

やりました。ちゃんと送信することができました。