0 com

iPhoneから照明のスイッチをオン/オフ

こんにちは、Mr.ZigBeeです。

前回APIモードを利用してZigBee間で通信を行う事ができたので、iPhoneから照明のオン/オフしてみます。正確には、PCに立ちあげたWEBサーバーを経由して、arduinoに接続したLEDのオン/オフを制御する単純なデモです。Arduino言語のdigitalWrite関数を使って簡単に点灯する事ができます。今回のテストで作った環境は、PCにWEBサーバーを立ちあげて、PCに接続されたZigBeeを経由して、Arduino側のZigBeeと通信します。PCで立ちあげたWEBサーバーをiPhoneのSafariから閲覧します。On/Offのリンクが押された際に、AJAXを利用してサーバーにリクエストを送り、あらかじめサーバーの起動時にディスカバーしておいたZigBee端末に対してOn/Offの命令(1byte)を送ります。



構成は、次の通りです。
サーバー: Jetty + Scala + xbee-api
arduino/xbee shield: xbee-arduino



40分ほどで完成した簡単なデモですが、LEDの点灯をiPhoneから制御できました。
video

このプログラムに登録すると、正式なiPhoneアクセサリを開発できるようです。ハックでいいので、iPhoneのコネクタとXBeeチップをつなげてみても面白いかも。
Read more »
0 com

ZigBee的Hello World、APIモードでのユニキャストに挑戦!

こんにちは、Mr. ZigBeeです。

前回のエントリで開発環境を構築し仕様書を集め終えたので、いよいよZigBee的Hello Worldプログラムの作成に取りかかります。今回、作成するZigBee的Hello Worldは、Coordinator側のXBeeから、End Device側のXBeeへのユニキャスト、ZigBeeの64ビットアドレスを指定してパケットを送信するシンプルなプログラムです。ZigBeeのトポロジやプロトコルの詳細は、ひとまず忘れて、ZigBeeの無線を動作させてみます。

ZigBee的Hello World!のゴール


Coordinator側からEnd Deviceに、APIモードでHello World!のバイト列を送信し、正しく受信できている事を確認する。

XBeeチップの設定

ZigBeeの通信には、シリアルポートへRead/Writeする事で透過的にPAN(Personal Area Network)内のZigBeeと通信できる、Serial Transparentモードと、より柔軟な制御が可能なAPIモードがあります。今回は、後者のAPIモードを利用して2つのXBeeチップ間で通信を行います。(実際には、APIモードでは、データをエスケープするかどうかにより、2つのモードにわかれています。)

工場出荷時の状態では、Serial Transparentモードで通信を行うように設定されているので、APIモードへとXBeeの設定を切り替える必要があります。XBeeの設定は、シリアルからコマンドモードと呼ばれる設定モードに入り、コマンドを発行する事でXBeeの設定を更新する事ができます。Windowsなら、X-CTUTera Term、MacならZ-Term等のターミナルソフトウェアが利用できます。

デフォルトでは、XBeeのシリアル接続のボーレートは9600bpsに設定されてます。ここでは、19200bpsに変更します。はエンターキーです。OKは、XBeeからのレスポンスで、入力する必要はありません。ここでの設定項目は、以下の4つです。最後にATWRコマンドで設定を永続化する必要があります。このコマンドを発行しないと、電源を落とした時に設定が消えてしまいます。
  • ボーレートを19200bpsに変更
  • APIモードへ切り替え
  • ノードIDの設定
  • PAN(Personal Area Network) IDの設定
  • コーディネーターとして設定 (コーディネーターのみ)

設定例
+++OK // +++と入力後、しばし待つとOKと表示される
ATBD 4<cr> // ボーレートを19200bpsに変更
OK
ATNI COORDINATOR<cr>// ノードID(20文字以内の任意のASCII文字列)
OK
ATAP 2<cr>// APIモード(バイト列が必要に応じてエスケープされる)に設定
OKCR
ATID 0x1a 0xaa<cr>// PAN IDの設定 (0-0xFFFFの間で設定する。デフォルトは、0x3332)
ATWR<cr>// 設定の書き込み
OK
ATCE 1<cr>
ATFR // 再起動


2台のXBeeの設定は、次のようになります。XBeeは、同一のPAN ID間で通信を行います。
Coordinator
  • ボーレート: 19200bps
  • ノードID: COORDINATOR
  • モード: API
  • PAN ID: 0x1a 0xaa

End Device
  • ボーレート: 19200bps
  • END_DEVICE_1
  • モード: API
  • ノードID: 0x1a 0xaa

PCとの接続


今回、XBee単体での制御とArduino/XBee Shield経由での制御を学ぶために、一台をSparkfunより販売されているXBee USB Explolerに、もう一台をXBee Shield上にセットアップします。XBee USB Exploler側に、Coordinator、ArduinoにEnd Deviceをそれぞれ配置します。状態とエラーの確認のために、ブレッドボード上に緑色と赤色のLEDを設置しています。緑色のLEDをArduinoの13番ピンと、赤色のLEDを12番ピンと結線しています。



ZigBeeスタック


XBeeの仕様は、前回のエントリより無料で入手できるので、ZBeeスタックのライブラリを自作する事ができますが、今回は、オープンソースで作成されている以下の2つのプロダクトを利用します。xbee-apiが、シリアルからXBeeを制御するためのライブラリで、xbee-arduinoがArduino上でXBeeを制御するためのライブラリです。



Coordinator側の実装


Coordinator側は、先程紹介したxbee-apiライブラリを用いてScalaで実装しています。始めにPAN内に存在するノードリストを取得するために、ATNDコマンドを発行します。プログラム中では、nodeDiscoverメソッド内でATNDコマンドを発行し、送信先End DeviceのシリアルID(64bit)を取得します。この64bitのシリアルIDは、全世界でユニークな不変値です。PAN内でユニークなID(16bit)で通信する事もできます。この16bit IDは、ノードがPANに参加した時や抜けた時などに動的に変更されます。

取得した送信先End Deviceの64bitアドレス(シリアルID)を宛先に指定して、Hello, World!のバイト列を送信します。XBeeのシリアルIDは、コマンドモードにてATSH, ATSLコマンドを使用して取得する事もできます。それぞれ、上位32ビット、下位32ビットを取得します。


import com.rapplogic.xbee.api.{ApiId, XBee, XBeeResponse, AtCommand, AtCommandResponse}
import com.rapplogic.xbee.api.{XBeeAddress64, XBeeAddress16, XBeeException, XBeeRequest, AtCommandResponse}
import com.rapplogic.xbee.api.zigbee.{ZNetTxRequest, ZNetTxStatusResponse, ZNetRemoteAtRequest, ZNetRxResponse}
import com.rapplogic.xbee.api.zigbee.{ZNetRemoteAtResponse, AssociationStatus}
import com.rapplogic.xbee.util.ByteUtils
import com.rapplogic.xbee.api.wpan.{RxResponse, TxRequest16, TxRequest64, TxRequestBase}
import org.apache.log4j.PropertyConfigurator
import scala.collection.jcl.Conversions.convertList

object CoordinatorTest {
def nodeDiscover(xbee: XBee) = {
// Set timeout for ATND command
var response: XBeeResponse = null
response = xbee.sendAtCommand(new AtCommand("NT", Array(NODE_DISCOVER_TIME)))
if(response.isError) {
System.err.println("[ERROR] ATNT: " + response + "\n")
System.exit(1)
}
// publish ATND command to discover nodes in PAN
response = xbee.sendAtCommand(new AtCommand("ND"))
if(response.isError) {
System.err.println("[ERROR] ATND: " + response + "\n")
System.exit(1)
}
// ATND response form
// 2 bytes for Source Address
// 4 bytes for Serial Number High value
// 4 bytes for Serial Number Low value
// 1 bytes for Received Signal Strength
// 12 bytes for Node Identifier
// 1 bytes for NULL
val value = response.asInstanceOf[AtCommandResponse].getValue
Array(value.slice(2, 10))
}
def main(args: Array[String]) = {
PropertyConfigurator.configure("log4j.properties")
val xbee = new XBee
xbee.open("/dev/tty.usbserial-A8008iws", 19200) // need to change for your system.
sendData(xbee)
}

def toHexString(bytes: Seq[Int]) = bytes.map(b => "0x" + b.toHexString).mkString(" ")

def sendData(xbee: XBee) = {
val nodeId = nodeDiscover(xbee).first
println("Found Node: " + toHexString(nodeId))
val addr64 = new XBeeAddress64(nodeId(0), nodeId(1), nodeId(2), nodeId(3),
nodeId(4), nodeId(5), nodeId(6), nodeId(7))
val payload = "Hello, World!".map(_.toInt).toArray
while(true) {
val request = new TxRequest64(addr64, xbee.getNextFrameId, payload)
xbee.sendSynchronous(request, 3000)
println("sent: " + request)
Thread.sleep(3000)
}
}
}

End Device側の実装 (on Arduino)


Arduino上のXBeeは、Coordinatorから送信されたデータを受信するだけのプログラムです。Coordinatorからデータを受信した際に、response.isAvailableがtrueになり、レスポンスを解析します。始めに、APIリクエストの種類を検出し、種類に応じた処理に振りわけます。ここでは、RX 16bitとRX 64bitのパケットを処理し、どちらでも無い場合は、エラー用のLEDを点灯させます。状態確認用のLEDを13番ピンに、エラー用のLEDを、12番ピンに接続しています。

ビルド方法について
ビルドには、Arduinoに付属しているMakefileを利用できます。Makefileは、/path/to/arduino-xxx/hardware/cores/arduino/Makefileです。XBee Shieldへの書き込みは、make uploadで実行できます。(※ Jumberピンを二つともUSB側に指しておく必要があります)


#include <XBee.h>

static const unsigned int BAUD_RATE = 19200;

XBee xbee = XBee();
XBeeResponse response = XBeeResponse();
Rx16Response rx16 = Rx16Response();
Rx64Response rx64 = Rx64Response();

// LED settings
static uint8_t stLed = 13;
static uint8_t errLed = 12;

// NOTE: For link error
extern "C" void __cxa_pure_virtual(){}

void setup() {
// LED
pinMode(stLed, OUTPUT);
pinMode(errLed, OUTPUT);
digitalWrite(13, HIGH);

Serial.begin(19200);
swserial.begin(9600);
swserial.println("SWSERIAL started");
xbee.begin(BAUD_RATE);
}

void loop() {
xbee.readPacket();
response = xbee.getResponse();
if(response.isAvailable()) {
uint8_t apiId = response.getApiId();
if(apiId == RX_16_RESPONSE) {
response.getRx16Response(rx16);
uint8_t dataLength = rx16.getDataLength();
Serial.print("rcv(16): addr16=");
Serial.print((int)rx16.getRemoteAddress16());
Serial.print(", data=");
for(int i = 0; i < dataLength; ++i) {
uint8_t data = rx16.getData(i);
Serial.print("0x");
Serial.print(data, HEX);
Serial.print(" ");
}
Serial.println("");
} else if(apiId == RX_64_RESPONSE) {
response.getRx64Response(rx64);
uint8_t dataLength = rx64.getDataLength();
Serial.print("rcv(64): data=");
for(int i = 0; i < dataLength; ++i) {
uint8_t data = rx64.getData(i);
Serial.print("0x");
Serial.print(data, HEX);
Serial.print(" ");
}
Serial.println("");
} else {
// not expected
digitalWrite(errLed, HIGH);
delay(1000);
digitalWrite(errLed, LOW);
}
}
}


動作確認


動作確認時には、プログラムの書き込み時に移動したJumperピンをXBEE側に戻します。
Coordinator側 (Sender)

% sbt run
[info] Building project xbee 1.0 using XBeeProject
[info]
[info] == compile ==
[info] Source analysis: 0 new/modified, 0 indirectly invalidated, 0 removed.
[info] Compiling main sources...
[info] Nothing to compile.
[info] Post-analysis: 5 classes.
[info] == compile ==
[info]
[info] == run ==
[info] Running Test ...
Experimental: JNI_OnLoad called.
Stable Library
=========================================
Native lib Version = RXTX-2.1-7
Java lib Version = RXTX-2.1-7
Found Node: 0x0 0x13 0xa2 0x0 0x40 0x31 0x9f 0x11
sent: apiId=TX_REQUEST_64 (0x00),frameId=1,option=DEFAULT_OPTION,payload=0x48 0x65 0x6c 0x6c 0x6f 0x2c 0x20 0x57 0x6f 0x72 0x6c 0x64 0x21,remoteAddress64=0x00 0x13 0xa2 0x00 0x40 0x31 0x9f 0x11
sent: apiId=TX_REQUEST_64 (0x00),frameId=2,option=DEFAULT_OPTION,payload=0x48 0x65 0x6c 0x6c 0x6f 0x2c 0x20 0x57 0x6f 0x72 0x6c 0x64 0x21,remoteAddress64=0x00 0x13 0xa2 0x00 0x40 0x31 0x9f 0x11
sent: apiId=TX_REQUEST_64 (0x00),frameId=3,option=DEFAULT_OPTION,payload=0x48 0x65 0x6c 0x6c 0x6f 0x2c 0x20 0x57 0x6f 0x72 0x6c 0x64 0x21,remoteAddress64=0x00 0x13 0xa2 0x00 0x40 0x31 0x9f 0x11


End Device側 (Receiver)

rcv(16): addr16=1, data=0x48 0x65 0x6C 0x6C 0x6F 0x2C 0x20 0x57 0x6F 0x72 0x6C 0
x64 0x21
rcv(16): addr16=1, data=0x48 0x65 0x6C 0x6C 0x6F 0x2C 0x20 0x57 0x6F 0x72 0x6C 0
x64 0x21
rcv(16): addr16=1, data=0x48 0x65 0x6C 0x6C 0x6F 0x2C 0x20 0x57 0x6F 0x72 0x6C 0
x64 0x21


Coordinatorから送信されたデータを、End Device側で受信できました。

次回は、CoordinatorとEnd Device間のアソシエーションの確立や、APIモード、ZigBeeのアドレッシングについてより深く見ていきます。
Read more »
0 com

ZigBee/XBeeの仕様書を集める

開発を進めるために仕様書を集めます。XBeeチップに関する仕様書は、Digiのサイトより入手できます。XBeeチップのミドルウェアの設定コマンド一覧や、XBeeのピン仕様、XBeeの通信プロトコルなど一通り記載されています。XBeeには、Series1とSeries2(XBee Pro)の2つのタイプがあります。
ZigBeeプロトコルに関する仕様は、ZigBee AlianceのTechnical Documentより入手できます。
Read more »
0 com

XBee + Arduinoの開発環境を構築

XBeeでの開発環境を整えます。今回、Maxscale社より販売されているZigBee規格に準拠したXBeeモジュールとオープンソースハードウェアのArduinoを用意しました。ArduinoにXBeeを接続するには、XBee Shieldと呼ばれる拡張ボードが必要です。XBee Shieldは、スイッチサイエンス等のショップで購入する事ができます。

一台のXBeeは、Arduino + XBee Shieldにつなぎ、もう一台のXBeeは、Sparkfun社より販売されているXBee USB Explolerに接続します。XBee USB Explolerも、スイッチサイエンス等のショップで販売されており、容易に入手する事ができます。XBee USB Explolerを利用すれば、USB/シリアルケーブルで、PCとXBeeを簡単に接続できます。

さて、今回用意した物をまとめると
  • XBee 2ユニット
  • Arduino
  • XBee Shield
  • USB/シリアル変換ケーブル × 2





次は、XBeeチップを設定してみます。
Read more »
0 com

Mr.ZigBeeブログ開設

こんにちは、Mr.ZigBeeです。

これまで、組み込み系プログラミングの経験の無い私が、ZigBeeやArduinoでプログラミングを始めたのをきっかけに、このブログを開設しました。いつか来るMr.Zigbeeと呼ばれるその日を目指しZigBeeやArduino、DIYなどハードウェアに関連した話題を書いていく予定です。

Let's DIY !
Read more »