ArduinoとRaspberry PiをXBee ZBでZigBee通信させる

センサ類を接続したArduinoとRaspberry Piを、XBee ZBを使用してZigBeeで通信させるための作業メモです。Arduino側をIoTデバイス、Raspberry Pi側をインターネットに繋がるIoTゲートウェイとして動かすことを見据えています。


# 用意するもの
今回は、Arduinoに取り付けるセンサとして、温湿度センサのDHT11を例として扱います。DHT11を基板に取り付けモジュール化したDFR0067を使用して、直接Arduinoに接続します。

まずは以下の部品を準備します。これらは、特に配線等を行わず簡単にDHT11とXBeeをArduinoに繋ぐための部品構成ですので、ご自分で回路を組まれたり、他のセンサを接続したい場合には、適宜読み替えて下さい。また、ArduinoやRaspberry Piは下記バージョンで動作確認をしていますが、他のバージョンでも同じような手順で事は成せると思います。

* Arduino Uno
[Arduino Uno Rev3 （秋月電子）](http://akizukidenshi.com/catalog/g/gM-07385)

* Arduino ワイヤレスSDシールド
[Arduino ワイヤレスSDシールド （秋月電子）](http://akizukidenshi.com/catalog/g/gM-08288)

* Raspberry Pi
[Raspberry Pi 3 Model B (Element14) （秋月電子）](http://akizukidenshi.com/catalog/g/gM-11425/)

* Raspberry Pi用ケース
[Raspberry Pi 3 Model B用公式ケース (秋月電子)](http://akizukidenshi.com/catalog/g/gP-10362)

* XBee ZB モジュール (2つ)
[XBee ZB S2C モジュール ワイヤーアンテナタイプ (秋月電子)](http://akizukidenshi.com/catalog/g/gM-10072)

* XBee USBインターフェースボード (1つでも足りますが、2つあると便利です)
[XBee USBインターフェースボード (秋月電子)](http://akizukidenshi.com/catalog/g/gK-06188)

* 温湿度センサDHT11 (DFR0067)
[DHT11温湿度モジュール （秋月電子）](http://akizukidenshi.com/catalog/g/gM-07040)

* ブレッドボード・ジャンパーコード（オス－オス）
[ブレッドボード・ジャンパーコード（オス－オス）（１０ｃｍ）２０本セット （秋月電子）](http://akizukidenshi.com/catalog/g/gC-05371)

* マイクロUSBケーブル
[USBケーブル Aオス-マイクロBオス 0.5m A （秋月電子）](http://akizukidenshi.com/catalog/g/gC-09314/)

Raspberry Piのセットアップ用に、必要に応じて以下の周辺機器も用意します。

* ディスプレイ
* USBマウス
* USBキーボード
* HDMIケーブル


# Arduino側のセットアップ

以下の手順でDHT11をArduinoに取り付けます。

1. Arduino ワイヤレスSDシールドをArduino Unoに取り付けます。
2. DFR0067の黒色のピンをブレッドボード・ジャンパーコードを介してシールドのGNDピンに接続します。
3. DFR0067の赤色のピンをブレッドボード・ジャンパーコードを介してシールドの5Vピンに接続します。
4. DFR0067の緑色のピンをブレッドボード・ジャンパーコードを介してシールドの2番ピンに接続します。
  
![IMG_7912.JPG](https://qiita-image-store.s3.amazonaws.com/0/61098/10ce50fd-6a90-2c7e-94e8-f64a1c716b81.jpeg)

# Raspberry Pi側のセットアップ
以下の手順でRaspberry Piをセットアップします。マウス、キーボード、ディスプレイなどの周辺機器を必要に応じて準備します。具体的なステップについては、Web上の先人たちの知恵を参考にしましょう。

1. OSイメージ(Raspbian Jessie)のダウンロード
2. SDカードのフォーマット、イメージの書き込み
3. OSの起動
4. ネットワークの設定

作業端末と同じローカルネットワークに接続して、プライベートIPアドレスでSSHログイン出来るように設定しておくと以降の作業がスムーズです。


# XBee ZBのセットアップ

1. X-CTUのインストール  
作業端末に、X-CTUをインストールします。以下のURLから、お使いマシンに合わせたインストーラをダウンロードして、インストールを行います。  
[X-CTU Software](https://www.digi.com/support/productdetail?pid=3352)  
　
2. デバイスの接続
XBee USBインターフェースボードを組み立て、XBee通信デバイスとマイクロUSBケーブルを接続します。
![IMG_7917.JPG](https://qiita-image-store.s3.amazonaws.com/0/61098/7782ad5d-c799-fac2-1fc9-72c1410fabc6.jpeg)　

3. XBee通信デバイスの設定
XBee USBインターフェースボードから出ているマイクロUSBケーブルを作業端末に接続します。X-CTUで接続したXBee ZBデバイスを検索し、見つかったデバイスに対して以下の設定を行います。2台のXBee ZBデバイスに対して、それぞれ設定を行います。XBee ZBデバイスの世代によって設定方法が異なりますので、X-CTUのオンラインマニュアル等を参照して下さい。設定項目が非常に多くありますが、以下の設定以外はデフォルトのままでも通信は可能です。
 * ファームウェアのインストール（アップデート）  
 2台のデバイスで共通です。最新のファームウェアを入れましょう。
 * PAN ID（とScan Channel）の設定  
 2台に同一の値を設定します。これが異なっていると通信出来ません。
 * Coordinator/Routerの設定  
 1台はCoordinator、もう1台はRouterに設定します。
 * APIモードの設定  
 2台ともAPIモードに設定します。  
　
4. 接続確認
XBee USBインターフェースボードが2つある場合は、双方を作業端末に接続して給電すると、X-CTUから接続確認を行えます。ここでZigBeeの通信が問題なく出来ることを確認しておくと良いです。

# Arduino側からZigBeeを送信

1. Arduino IDEのインストール  
作業端末に、Arduino IDEをインストールします。以下のURLから、お使いのマシンに合わせたインストーラをダウンロードして、インストールを行います。  
[Arduino Software](https://www.arduino.cc/en/Main/Software)  
　
2. Arduinoの接続  
ワイヤレスSDシールドを取り付けたArduino Unoを、USBケーブルで作業端末に接続します。  
　
3. ライブラリのインポート  
今回は、温湿度センサDHT11からデータを取得するadafruitのライブラリ(DHT sensor library)と、XBee通信のためのライブラリを使用します。Arduino IDEのスケッチに、これらのライブラリをインポートします。
 * DHT11ライブラリ  
リポジトリをCloneして、Arduino IDEにワーキングディレクトリを指定してインポートします。  
```
git clone https://github.com/adafruit/DHT-sensor-library.git
```
 * XBeeライブラリ  
リポジトリをCloneして、Arduino IDEにワーキングディレクトリを指定してインポートします。もしくは、Arduino IDEの「ライブラリを管理」から「XBee」と検索してインポートします。  
```
git clone https://github.com/andrewrapp/xbee-arduino.git
```  
　
4. スケッチの編集
Arduino IDEでスケッチを編集します。スケッチでは、ループ処理の中でDHT11から温湿度のデータを読み込み、XBeeでCoordinatorに送信します。以下に動作確認をしたプログラム例を書いておきます。

    ```c:DHT11XBeeZBSender.ino
    #include <DHT.h>
    #include <DHT_U.h>

    #include <XBee.h>
    #include "DHT.h"
    #define DHTPIN 2
    #define DHTTYPE DHT11

    // Initialize XBee ZB client
    XBee xbee = XBee();
    // Payload byte array
    uint8_t payload[8];
    // Send to coordinator
    XBeeAddress64 addr64 = XBeeAddress64(0, 0);
    // Create request for XBee ZB
    ZBTxRequest zbTx = ZBTxRequest(addr64, payload, sizeof(payload));

    // Initialize DHT sensor module
    DHT dht(DHTPIN, DHTTYPE);

    void set_float_to_payload(float value, int index) {
      uint8_t *value_array;
      value_array = reinterpret_cast<uint8_t*>(&value);
      for(int i=0; i<sizeof(value); i++){
        payload[i+index] = value_array[i];
      }
    }

    void setup() {
      Serial.begin(9600); 
      xbee.setSerial(Serial);
      dht.begin();
    }

    void loop() {
      delay(3000);

      float temperature = dht.readTemperature();
      float humidity = dht.readHumidity();

      set_float_to_payload(temperature, 0);
      set_float_to_payload(humidity, 4);

      xbee.send(zbTx);
    }
```  

5. スケッチの書き込み
上記のスケッチを接続しているArduino UNOに書き込みます。このとき、XBeeデバイスをシールドに接続していると書き込みが失敗するので、取り外した状態で書き込みを行って下さい。
　
6. 通信の確認
ここまで来れば、既にArduino側では温湿度のデータがCoordinator側のXBeeデバイスに送信されているはずです。作業端末上で、ZigBee受信モジュールを作成し、データを受信してみます。XBee USBインターフェースボードにCoordinatorに設定したXBeeデバイスを、ArduinoのシールドにRouterに設定したXBeeデバイスを接続し、両者を作業端末に接続します。  
![IMG_7921.JPG](https://qiita-image-store.s3.amazonaws.com/0/61098/a560ef8c-0a31-1916-431c-be74b44ca412.jpeg)    
X-CTUでCoordinator側のXBeeのシリアルモニターを開くと、3秒おきにデータが送られてきていることを確認出来ます。


# Raspberry Pi側でZigBeeを受信
Coordinatorに設定したXBeeデバイスをRaspberry Piに接続します。

![IMG_7922.JPG](https://qiita-image-store.s3.amazonaws.com/0/61098/ec02d14c-9a2d-c95b-529e-2209c76751ca.jpeg)

Raspberry PiのOSを起動し、受信モジュールをRaspberry Pi上に配置すれば、ArduinoからRaspberry PiにZigBee通信することが出来ます。

![IMG_7929.JPG](https://qiita-image-store.s3.amazonaws.com/0/61098/f1abbcab-6f4f-90cf-3658-625f63f626ab.jpeg)

今回は、PythonとJavaでそれぞれ受信モジュールを組む方法をご紹介します。

## Pythonで受信する
以下では、Raspberry Pi上にvirtualenvでPython環境を作っています。virtualenvを使わず、Raspbian Jessie上に事前構成されているPython3の環境を使用しても構いません。その場合は、`python3`コマンドでプログラムの実行を行います。

まず、ベースのアップデートをしてから、Pythonのビルドに必要なものをインストールし、Pyenvとvirtualenvをインストールします。その後、必要なライブラリをインストールします。XBeeをPythonで受信するために、[python-xbee](https://github.com/nioinnovation/python-xbee)を使います。

```bash
# ベースのアップデート
$ sudo apt-get update  
$ sudo apt-get upgrade -y  
$ sudo apt-get dist-upgrade  

# Pythonのビルドに必要なものをインストール
$ sudo apt-get install -y build-essential libssl-dev openssl libbz2-dev libreadline-dev libsqlite3-dev

# Pyenvとvirtualenvのインストール
$ git clone https://github.com/yyuu/pyenv.git ~/.pyenv
$ git clone https://github.com/yyuu/pyenv-virtualenv.git ~/.pyenv/plugins/pyenv-virtualenv
$ echo 'export PYENV_ROOT=$HOME/.pyenv' >> ~/.profile
$ echo 'export PATH=$PYENV_ROOT/bin:$PATH' >> ~/.profile
$ echo 'eval "$(pyenv init -)"' >> ~/.profile
$ echo 'eval "$(pyenv virtualenv-init -)"' >> ~/.profile
$ source ~/.profile

# Python3.5以上の環境を準備
$ pyenv install 3.6.0

# 今回のZigBee通信に使うPython作業環境の準備
$ cd <working directory>
$ pyenv virtualenv 3.6.0 3.6.0_xbee
$ pydev local 3.6.0_xbee
$ python -V
Python 3.6.0

# 必要なライブラリのインストール
$ pip install xbee
$ pip install pyserial
```

次に、Pythonで受信プログラムを書きます。受信プログラムでは、python-xbeeライブラリを使って対象のUSBポートからシリアル通信でデータを受信して、受信した結果からバイト配列のPayloadを取り出して解析し、得られたデータを出力します。以下に動作確認をしたプログラム例を書いておきます。注意点として、中で使われている`hex()`関数はPython3.5以上でないと動きません。Raspbian Jessie上に事前構成されているPython3の環境を使う場合など、3.5未満のバージョンで動かしたい場合には、`hex()`に該当する関数を自分で定義して置き換えるか、該当する標準出力部分をコメントアウトして適宜書き換えて下さい。

```bash
$ vi XBeeZBReceiver.py
```

```py3:XBeeZBReceiver.py
#!/usr/bin/python

"""
Application to receive data from XBee ZB using XBee Python Library.
"""

from xbee import ZigBee
import serial
import struct

# TODO Replace with the serial port where your receiver module is connected.
PORT = '/dev/ttyUSB0'
# TODO Replace with the baud rate of you receiver module.
BAUD_RATE = 9600

# Open serial port
myDevice = serial.Serial(PORT, BAUD_RATE)

# Create API object
xbee = ZigBee(myDevice)

def prettyHexString(str):
    "split string by 2 length"
    return ' '.join([str[i:i+2] for i in range(0, len(str), 2)])

# Continuously read and print packets
print(">> Waiting for data...")
while True:
    try:
        response = xbee.wait_read_frame()
        source_addr = response['source_addr_long'].hex().upper()
        payload = prettyHexString(response['rf_data'].hex().upper())
        data = struct.unpack('ff', response['rf_data'])
        print('From %s >> [%s] | { temperature: %.1f degrees, humidity: %.1f%% }' % (source_addr, payload, data[0], data[1]))

    except KeyboardInterrupt:
        break

myDevice.close()
```

上記のPythonコードを実行すると、3秒置きに受信したデータが表示されます。

```bash
$ python XBeeZBReceiver.py
>> Waiting for data...
From 0013A20040E7446F >> [00 00 D0 41 00 00 14 42] | { temperature: 26.0 degrees, humidity: 37.0% }
From 0013A20040E7446F >> [00 00 D0 41 00 00 14 42] | { temperature: 26.0 degrees, humidity: 37.0% }
From 0013A20040E7446F >> [00 00 D0 41 00 00 14 42] | { temperature: 26.0 degrees, humidity: 37.0% }
...
```

## Javaで受信する
Raspbian Jessieには、事前構成としてJava SE 8の環境がインストールされていますので、それを使います。

```bash
$ echo 'export JAVA_HOME=/usr/lib/jvm/jdk-8-oracle-arm32-vfp-hflt' >> ~/.profile
$ echo 'export PATH=$JAVA_HOME/bin:$PATH' >> ~/.profile
$ source ~/.profile
$ java -version
java version "1.8.0_65"
Java(TM) SE Runtime Environment (build 1.8.0_65-b17)
Java HotSpot(TM) Client VM (build 25.65-b01, mixed mode)
```

XBeeをJavaで受信するために、[XBeeJavaLibrary](https://github.com/digidotcom/XBeeJavaLibrary)を使います。

```bash
$ cd <working directory>
$ wget https://github.com/digidotcom/XBeeJavaLibrary/releases/download/v1.2.0/XBJL-1.2.0.zip
$ unzip XBJL-1.2.0.zip
```

次に、Javaで受信プログラムを書きます。受信プログラムではPythonと同じく、XBeeJavaLibraryを使って対象のUSBポートからシリアル通信でデータを受信して、受信した結果からバイト配列のPayloadを取り出して解析し、得られたデータを出力します。以下に動作確認をしたプログラム例を書いておきます。

```bash
$ vi XBeeZBReceiver.java
```

```java:XBeeZBReceiver.java
import java.nio.ByteBuffer;
import java.nio.ByteOrder;

import com.digi.xbee.api.XBeeDevice;
import com.digi.xbee.api.exceptions.XBeeException;
import com.digi.xbee.api.listeners.IDataReceiveListener;
import com.digi.xbee.api.models.XBeeMessage;
import com.digi.xbee.api.utils.HexUtils;

/**
 * Application to receive data from XBee ZB using XBee Java Library
 */
public class XBeeZBReceiver {

	// TODO Replace with the serial port where your receiver module is connected.
	private static final String PORT = "/dev/ttyUSB0";
	// TODO Replace with the baud rate of you receiver module.
	private static final int BAUD_RATE = 9600;

	/**
	 * Class to manage the XBee received data that was sent by other modules in the same network.
	 * 
	 * @see IDataReceiveListener
	 */
	static class XBeeZBReceiveListener implements IDataReceiveListener {
		@Override
		public void dataReceived(XBeeMessage xbeeMessage) {
			byte[] bytes = xbeeMessage.getData();
			ByteBuffer buffer = ByteBuffer.wrap(bytes);
			buffer.order(ByteOrder.LITTLE_ENDIAN);
			System.out.format("From %s >> [%s] | { temperature: %.1f degrees, humidity: %.1f%% }%n",
					xbeeMessage.getDevice().get64BitAddress(),
					HexUtils.prettyHexString(HexUtils.byteArrayToHexString(xbeeMessage.getData())), buffer.getFloat(),
					buffer.getFloat());
		}
	}

	/**
	 * Application main method.
	 * 
	 * @param args Command line arguments.
	 */
	public static void main(String[] args) {
		XBeeDevice myDevice = new XBeeDevice(PORT, BAUD_RATE);

		try {
			myDevice.open();
			myDevice.addDataListener(new XBeeZBReceiveListener());
			System.out.println(">> Waiting for data...");

		} catch (XBeeException e) {
			e.printStackTrace();
			System.exit(1);
		}
	}
}
```

コンパイルします。XBeeJavaLibraryのxbee-java-library-x.x.x.jarをクラスパスに加えます。また、`android.content.Context`が見えないとコンパイルエラーになるので、extra-libsディレクトリ以下のandroid-sdk-x.x.x.jarもクラスパスに加えます。

```bash
$ javac -cp XBJL-1.2.0/xbee-java-library-1.2.0.jar:XBJL-1.2.0/extra-libs/android-sdk-5.1.1.jar XBeeZBReceiver.java
```

最後に、XBeeJavaLibraryにはARMのCPUで動くRXTXのNative Libraryが入っていないため、自分でダウンロードしてコンパイルします。

```bash
$ wget http://rxtx.qbang.org/pub/rxtx/rxtx-2.2pre2.zip
$ unzip rxtx-2.2pre2.zip
$ cd rxtx-2.2pre2
$ ./configure
$ make
$ cd ..
```

コンパイルしたJavaコードを実行すると、3秒置きに受信したデータが表示されます。実行時には、Native Libraryをjava.library.pathに指定するなどしてリンクする必要があります。

```bash
$ java -cp .:XBJL-1.2.0/xbee-java-library-1.2.0.jar:rxtx-2.2pre2/RXTXcomm.jar:XBJL-1.2.0/extra-libs/slf4j-api-1.7.12.jar:XBJL-1.2.0/extra-libs/slf4j-simple-1.7.12.jar -Djava.library.path=rxtx-2.2pre2/armv7l-unknown-linux-gnu/.libs XBeeZBReceiver
>> Waiting for data...
From 0013A20040E7446F >> [00 00 D0 41 00 00 14 42] | { temperature: 26.0 degrees, humidity: 37.0% }
From 0013A20040E7446F >> [00 00 D0 41 00 00 14 42] | { temperature: 26.0 degrees, humidity: 37.0% }
From 0013A20040E7446F >> [00 00 D0 41 00 00 14 42] | { temperature: 26.0 degrees, humidity: 37.0% }
...
```

これでめでたくArduinoからの通信をRaspberry Pi上で受信することができました！
![IMG_7941.JPG](https://qiita-image-store.s3.amazonaws.com/0/61098/a25102a4-662f-45f1-432e-acded2aa3ba3.jpeg)


# 最後に
以上で、センサ類（温湿度センサDHT11）を接続したArduinoとRaspberry Piを、XBee ZBを使用してZigBeeで通信させることが出来ました。Arduino側を2台、3台と増やしても、同じようにRaspberry Pi上で受信することが出来ます（受信側ではXBeeの64ビットアドレスで送信元を区別出来ます）。
Raspberry Pi上にIoTプラットフォームを提供するクラウドサービスのSDKやClient Libraryを入れて、ゲートウェイとしてクラウド上へデータを転送するようにすれば、Arduino側をIoTデバイス、Raspberry Pi側をIoTゲートウェイとして、エッジ側のネットワークをZigBee通信で作ることが出来ますね。