ArduinoとRaspberry Pi間をZigBeeで接続する

# 準備

+ Arduino Uno Rev3
+ Raspberry Pi 3 Model B
+ Arduino ワイヤレスSDシールド
+ XBee USBインターフェースボードキット
+ XBeeZB S2Cモジュール ワイヤーアンテナタイプ **2個**
+ Arduino-PC間接続用のUSBケーブル
    + Arduino側がTYPE-B
+ XBee USBインターフェースボードキットと、PCやRaspberry Piの間の接続用のUSBケーブル
    + インターフェースボードキット側がMicro-B
+ 作業用PC
    + MacでもWinでもLinuxでもできるはず
    + 私はMac Book(Sierra)で作業を行った

# XBee USBインターフェースボードキットの組み立て

## はんだ付け

いつ以来のハンダ付けかわからないため、汚いが勘弁してほしい。
(特にDC端子のとこはひどい・・・)

はんだ付けの方法は[ここ](https://www.murata.com/ja-jp/campaign/ads/japan/elekids/ele/craft/knack/soldering)を参考にした。

### 表

![handa1.jpg](https://qiita-image-store.s3.amazonaws.com/0/172314/8177bc9a-d6fd-13f3-b2d7-a9ca45baf8b4.jpeg)

### 裏

![handa2.jpg](https://qiita-image-store.s3.amazonaws.com/0/172314/1bba84e4-ee73-df70-6cb7-71698fafdc20.jpeg)

## PWE-SELにジャンパピンを取り付ける

今回はRaspberry PiとUSBで接続する想定なので、USB側に接続。

![handa3.jpg](https://qiita-image-store.s3.amazonaws.com/0/172314/befa0196-5fb5-dc99-792b-c4e74b1a5283.jpeg)

## XBeeZB S2Cモジュールを取り付ける

![handa4.jpg](https://qiita-image-store.s3.amazonaws.com/0/172314/74a40048-a5e7-8283-aa2d-00ee0eb8bff2.jpeg)

このモジュールには向きがあるので注意すること。

# Raspberry Piの準備

+ インターネットに[WiFiで接続する](https://qiita.com/h-sakano/items/aec2bd67a2d573534716)、もしくは有線で接続する
    + 作業用PCからssh接続するため。
    + RaspberryPiに直接ディスプレイやキーボードを接続する場合は不要
+ [Pythonの設定](https://qiita.com/h-sakano/items/cb8df027b81acd78bd77)
+ ベースのアップデートと必要なライブラリをインストール

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

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

手元のRaspberry Piだとpipに`sudo`付けないと`Permission Denied`が出るんだが、
これがデフォルトの挙動なのだろうか。

# Arduinoの準備

+ [Arduino IDEを作業PCにインストール](https://qiita.com/h-sakano/items/ae57617f06a107bee3f3)
+ ワイヤレスSDシールドの接続
    + この後、USBケーブルでPCと接続して設定を行うので、下図の赤枠で囲っている箇所にある`SERIAL SELECT`のDIPスイッチをUSB側へ倒しておく
+ XBeeモジュールの接続

![arduino3.jpg](https://qiita-image-store.s3.amazonaws.com/0/172314/ef4e1719-2224-df0d-4690-29910ea8b837.jpeg)

# XCTUのインストール

PCにインストールを行う。

[XCTU SOFTWARE](https://www.digi.com/support/productdetail?pid=3352)の`Diagnostics, Utilities and MIBs`から適切なOS用のインストーラを選びダウンロード。

ダウンロードの際、メンバー登録を促されるので、メンバー登録したくない人は`No thanks, register later`で回避可能。

インストールが完了したら、XCTUを起動する。

起動時に「"XCTU"を開くには、以前のJava SE 6ランタイムをインストールする必要があります。」とエラーが出た場合は、「詳しい情報」からインストールする。

# XBee USBの接続

作業PCに[VCP Drivers](http://www.ftdichip.com/Drivers/VCP.htm)ページからOSに合ったドライバをダウンロードし、インストール。
この工程が本当に必要かどうかは不明だが、私は一応実行しておいた。

その後、USBでPCに接続する。
ここでPWRのところが青く光らない場合は、はんだ付けやジャンパピンの取り付けが正しく行われていない可能性がある。

![handa5.jpg](https://qiita-image-store.s3.amazonaws.com/0/172314/4d87f9dc-1e5b-4b69-d2ee-0f8abf89643e.jpeg)

# XCTUでネットワーク設定

## XBeeZB S2CモジュールをPCから認識させる

USBポートが2つ以上あるPCの場合は親機(XBee USB)と子機(Arduino)を同時にPCに差して、同時に認識させることが可能。
USBポートが1つしかない場合は、1台ごとに認識させて、設定を行う必要がある。

### XBee USB側(親機)のXBeeZB S2Cモジュールの認識

<img width="1019" alt="スクリーンショット 2017-11-18 16.32.20.png" src="https://qiita-image-store.s3.amazonaws.com/0/172314/b2d0a1cf-20ad-fd01-1b48-736f98776e1b.png">

赤枠で囲ったアイコンからモジュールを検索できる。
XBeeモジュールを接続しているPortを選択し、その後の画面を適当にスキップして検索する。

<img width="446" alt="スクリーンショット 2017-11-18 16.34.58.png" src="https://qiita-image-store.s3.amazonaws.com/0/172314/ce534fb5-e557-53fe-e390-522025fe43c5.png">

モジュールが見つかるので、「Add selected devices」でモジュールを追加。

### Arduino側(子機)のXBeeZB S2Cモジュールの認識

ArduinoをPCに接続し、同じ手順で認識させる。
ただし、Arduinoの信号による干渉のせいか、接続がうまく行かなかったりする場合がある。
以下のトラブルシューティングを参考にどうぞ。

### 認識されない場合に試すこと

#### 突然XBee USB側のXBeeモジュールがXCTUに認識されなくなった

![20151107220137.png](https://qiita-image-store.s3.amazonaws.com/0/172314/7ca7b925-250e-37ba-d3fc-2d77a5dd2c68.png)

接続しようとしたところ上記の画面が出て認識されなくなった。
XBeeUSBのResetボタンを押したが、反応がなかったため、キャンセルボタンを押した。

その後、モジュール追加アイコンから再度試すが、同じ画面が。
Resetを押すと今回は反応し、Recoveryアイコンを含む画面が現れたので、Firmwareをリカバリー。

`Product family`と`Function set`を聞かれたので、それぞれ`XC24C`・`ZIGBEE TH Req`を選択した。

リカバリー終了後、再度認識するが試すが、同じ画面が出る。

どうしたものかと思いながら、
何度か抜いたり差したりを繰り返したり、リセットボタンを押してみたりと、
いろいろとやっているうちに認識するようになった。

#### XBee USBに差してるXBeeモジュールは認識されるが、同じモジュールをArduino側に差したら認識されない

XBee USBでとりあえず認識させて、スパナのアイコンのXBee Recoverから、XBee USBを差しているUSBポート、`Product family(XC24C)`と`Function set(ZIGBEE TH Req)`を選択してファームウェアのリカバリーを行う。

リカバリーが完了したら、Arduinoに差し替えて認識させてみると正常に認識された。

#### と思ったらまたArduino側のXBeeモジュールが認識しなくなった

[ArduinoのGNDとRSTを接続してショートさせると良いらしい。](http://blog.prhyzmica.com/?p=110)

![arduino2.jpg](https://qiita-image-store.s3.amazonaws.com/0/172314/fbb31932-2824-2226-81cb-8cec0b602ae9.jpeg)

ただし、この状態のまま、Arduinoにプログラムを書き込もうとするとエラーがでたので、書き込むときは外すようにした。
~~理由はわからん。~~(追記: RSTピンをGNDに落とすというのはArduinoボードに実装されているリセットボタンを押したままにするのと同義なので、その間はArduinoが全く停止してしまうためであるとのこと。)

## ファームウェアアップデート

<img width="1280" alt="スクリーンショット 2017-11-18 16.55.35.png" src="https://qiita-image-store.s3.amazonaws.com/0/172314/47d9cdc0-e970-336c-8d6d-9b58f7d55423.png">

Updateアイコンから`Product family/Function set/Version`を選択し、Updateをクリック。

## 設定

設定を変えたら画面上部の`Write`からまとめて書き込むことができる。
ただし、まとめて書き込むと書き込みがうまくいかない場合があったので、私は1つ1つ書き込みを行うようにした。

### 親機

パラメータ|略名|値
---|---|---
PAN ID|ID|任意の値
Cordinator Enable|CE|Enabled[1]
Destination Address High|DH|子機のXBeeモジュールのMacアドレス上位4オクテット
Destination Address Low|DL|子機のXBeeモジュールのMacアドレス下位4オクテット
API Enable|AP|API enabled[1]

### 子機

パラメータ|略名|値
---|---|---
PAN ID|ID|親機と同じPAN ID
Cordinator Enable|CE|Disabled[0]
Destination Address High|DH|親機のXBeeモジュールのMacアドレス上位4オクテット
Destination Address Low|DL|親機のXBeeモジュールのMacアドレス下位4オクテット
API Enable|AP|API enabled[1]

# Arduinoにプログラムを書き込む

## XBeeライブラリをインストール

Arduino IDEの「スケッチ=>ライブラリをインクルード=>ライブラリを管理」でxbeeと検索してインストール。

```.c
#include <XBee.h>

// Initialize XBee ZB client
XBee xbee = XBee();

// Payload byte array
uint8_t payload[16];

// Send to coordinator
XBeeAddress64 addr64 = XBeeAddress64(0, 0);

void set_int_to_payload(int 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);
}

void loop() {
  delay(3000);

  int tid = 13;  // 端末ID(テキトー)

  // 今回はA0から光抵抗センサの値を読み取った
  int cds = analogRead(0);

  set_int_to_payload(tid, 0);
  set_int_to_payload(cds, 8);
  
  // Create request for XBee ZB
  ZBTxRequest zbTx = ZBTxRequest(addr64, payload, sizeof(payload));

  xbee.send(zbTx);
}
```

## SERIAL SELECTの切り替え

書き込み終わったら、PCからUSBを外してワイヤレスシールドのスイッチをMicroの方に切り替える。
この時点でArduinoは作業用PCと切り離しても良い。

# Raspberry Piのコーディング

## 受信用コードを記述

[公式ドキュメント](http://python-xbee.readthedocs.io/en/latest/)を参考にして、受信用コードを書く。

```receive.py
# coding: utf-8
from xbee import ZigBee
import serial
import struct
import time


def parseData(data):
    data = struct.unpack('qq', data['rf_data'])
    return data

if __name__ == '__main__':
    # Setup ZigBee
    PORT = '/dev/ttyUSB0'
    BAUD_RATE = 9600
    myDevice = serial.Serial(PORT, BAUD_RATE)
    xbee = ZigBee(myDevice)

    # Continuously read and print packets
    print(">> Waiting for data...")
    while True:
        try:
            data = parseData(xbee.wait_read_frame())
            tid = data[0]
            cds = data[1]
            print(tid, cds)
        except KeyboardInterrupt:
            break

    myDevice.close()
```

# 実行

作業用PCに接続しているモジュールをRaspberryPiに差し替えて、先程のコードを実行する。

```.sh
$ python receive.py 
>> Waiting for data...
(13, 404)
(13, 404)
(13, 407)
(13, 403)
(13, 406)
(13, 404)
 .
 .
 .
```

上記のように、3秒ごとにデータが送られてくれば成功。

# まとめ

以下の手順を行うことで、端末間の通信を行うことができた。

+ 各端末(RaspberryPi, Arduino, XBee USBインターフェースボード)の準備、設定
+ XCTUでのXBeeモジュール(親機, 子機)の接続、設定
+ Arduino, RaspberryPiでのコーディング

今まで無線接続といえばWifiとBluetoothしか扱ったことがなかったが、
ZigBeeも1日足らずで通信の成功まで持っていけた。