XBeeを使った通信

[2016/3/24新規] [2016/3/26更新]
[戻る]

XBeeを使った通信に関するメモです。

XBeeについて

XBee は Digi International社(日本語サイト) が販売している無線モジュールです。ZigBee と名前は似ていますが、XBee は商品名で、ZigBee は規格の名前になります。
例えば、このページで紹介しているスケッチ及び回路では、XBee-PRO ZB S2B Wireアンテナ(XBP24BZ7WIT-004J) を使っていますが、これは ZigBeeプロトコルに従って通信するように設計されたものです。

XBee-PRO ZB S2B のマニュアル等は、以下のリンクの Digi International社のページからダウンロードできます。

ZigBeeのネットワーク構成

ZigBeeネットワークは以下の三種類の端末で構成されます。

Coordinator
ネットワークに一つ存在し、ネットワークの制御を行う端末。
Router
データの中継機能を持つ端末。
End Device
データの中継機能を持たない端末。

ZigBeeネットワークでは、データの中継機能によって、直接は通信できない端末同士でも他の端末を通じて通信を行うことができます。
この機能は、XBee端末においては、設定やコマンドなどで特に何かする必要なく利用可能です。

XBee端末は、ファームウェアを書き換えることで端末の種類を変更することができます。

Router と End Device の大きな違いは、データ中継機能の有無の他に、スリープ機能があります。End Device はスリープ機能を使った運用を目的とした端末と言え、スリープさせない運用であれば、中継機能のない End Device を使う必要はありません。

XCTUによるXBee端末の設定

XBee には、その設定用GUIツール「XCTU」がメーカーから提供されています。設定はシリアル端末からコマンドを送ることでも可能ですが、XCTU は設定値をわかりやすく表示してくれ、さらにシリアルコンソールやネットワークの状態を見るモードなどもあり、XBeeを使う上で非常に便利ですのでインストールしておきましょう。

ハードウェアの準備

まず、XBee を PC に接続するためには USBアダプタが必要になります。
このページでは、SparkFun XBee Explorer USB を使用して、動作等を確認しています。
別の同等品でも良いですが、リセットボタンが付いているものが望ましいです。XCTUを使っていると、XBee端末を接続した時や設定値の読み込みなどのタイミングで、たまにXBeeをリセットするように促されることがあります。

PC 側の準備

XCTU は以下にのリンクの Digi International社のページからダウンロードできます。

上記ページ内の「Diagnostics, Utilities and MIBs」の中にダウンロードページへのリンクがありますので、そこから入手して、インストールします。
なお、Mac OS X 用では、Java Development Kit が必要でした。
また、XBee を USBアダプタを使って PC に接続しても認識されない場合は、上記ページ内の「Drivers」の中にあるリンク先からデバイスドライバーをダウンロードしてインストールする必要があります。

XCTUでXBee端末の設定を確認

まず最初に、XBee は PC に接続せずに、XCTU を起動します。
次に、メニューの [Working modes] をクリックして、[Configuration working mode] が選択されていることを確認します。
そして、メニューの [XCTU]-[Add radio module] を選択してダイアログを表示させ、「Select the Serial/USB port」が選択されていることを確認します。
ここで、XBee を PC に接続し、ダイアログの [Refresh ports] ボタンをクリックします。
そうすると、ダイアログの「Select the Serial/USB port」欄に、新たにポートが追加されますので、それを選択して [Finish] ボタンをクリックします。

なお、予め XBee がどのポートに割り当てられているのか分かっている場合は、上記のような手順を踏まずに、最初から XBee を接続しておいて、そのポートを選択しても問題ありません。

また、XBee を接続しても、新しいポートが追加されない場合は、「PC 側の準備」に記した通り、ドライバーのインストールが必要かもしれません。

正常に XBee が追加されると、XCTU の画面左側の「Radio Modules」欄にアイコンが表示されます。
そのアイコンをクリックすると、当該 XBee の設定が XCTU の画面右側の「Radio Configuration」欄に表示されます。ここでは、設定の確認の他、設定の変更、設定を規定値に戻すこと、ファームウェアの更新ができます。

透過モードでの通信

XBee には二つの通信モードがあります。そのうちの一つが「透過モード(Transparent mode/Transparent operation)」です。
このモードは、単純なシリアル通信になりますので、既存のシリアル通信をXBeeによる無線に置き換えるようなときに便利です。

ここでは、定期的に Router から Coodinator へデータを送る例を作ってみます。

XBee端末の設定(1)

ZigBeeネットワークには一つの Coodinator が必要なので、一台を Coodinator 用のファームウェアに更新します。

まず、前述のように、PC に XBee を接続し、XCTU で設定を確認します。
「Function set」が現在のファームウェアでの機能になります。透過モードの Coodinator にするには、これを「ZigBee Coodinator AT」にします。

ファームウェアの更新は、XCTUの[Update]ボタンから行います。
[Update]ボタンをクリックすると「Update firmware」ダイアログが表示されます。そこで、
「Product family」欄はそのまま変更なし、
「Function set」欄は「ZigBee Coodinator AT」を選択、
「Firmware version」欄は「(Newest)」となっているものを選択します。
そして、ダイアログの[Update]ボタンをクリックすると、ファームウェアの更新が始まります。
しばらくして、アップデートが完了した旨書かれたダイアログが表示されたら完了です。

次に PAN ID を設定します。
PAN ID はネットワークを識別するもので、ネットワーク毎にユニークな値を付けます。
PAN ID は64bitの16進数ですので、その範囲で適当な値を決めます。
なお、規定値である 0 の場合、Coodinator は、周辺の Router、End Device に対して、それぞれに設定されている PAN ID に関係なくネットワークへの接続を許可します。

では、先ほど決めた PAN ID を、XCTU の「ID PAN ID」欄に書き込みます。
そうすると、欄の右下が緑色の三角に変わります。これは、XCTU 上での値は変更されていますが、まだ XBee には反映されていない状態であることを示しています。
ここで [Write] ボタンをクリックすると、XBeeに設定値が書き込まれます。
そうすると、欄の右下が青色の三角に変わります。これは、XBee に書き込まれた設定値は規定値とは異なることを示しています。

次はデータの送信先の設定です。
データの送信先は「DH Destination Address High」と「DL Destination Address Low」で設定しますが、ここに書き込む値は、送信先XBee端末の64bitデバイスアドレスの上位32bit、下位32bitになります。
自身の64bitデバイスアドレスは「SH Serial Number High」と「SL Serial Number Low」で確認できます。また、XBee の裏面にも印刷されています。
なお、Coodinator での規定値 DH=0、DL=0xFFFF はブロードキャストになります。
ここでは、Coodinator は受信のみをする例を作るので、DH、DL は変更せずブロードキャストのままにしてにおきます。

XBee端末の設定(2)

もう一台のファームウェアは、Router にします。

透過モードの Router の「Function set」は「ZigBee Router AT」ですので、これ以外のファームウェアとなっていたら、前述と同様にファームウェアを更新します。

ファームウェアが確認できたら、Coodinator に設定した値と同じ値を「ID PAN ID」に設定します。

次にデータの送信先を設定します。
DH Destination Address High」と「DL Destination Address Low」には、送信先のXBee端末の64bitデバイスアドレスを設定しますが、Router と End Device での規定値である DH=0、DL=0 は、送信先が Coodinator になります。
ここでは、Coodinator へ送信するサンプルを作るので、DH、DL は変更せず、0、0 のままにしておきます。
なお、Coodinator の64bitデバイスアドレスを DH、DL に設定しても同様の動作となります。

XBee端末を使ったArduinoからのデータ送信

Arduino で XBee を簡単に利用する方法の一つが、 ワイヤレスシールドSD付き)を使うことです。
Arduino UNO に XBee を取り付けたワイヤレスシールドを載せるだけで利用可能になります。

まず、Aruduino UNO に以下のスケッチを書き込んでおきます。
このスケッチは、シリアル通信でのテキストデータの送信を繰り返すという動作をするものです。

const double interval(1)/*[sec]*/;
unsigned int count(0);

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

void loop()
{
  Serial.print("From Arduino UNO ");
  Serial.println(count++);
  delay(1000*interval);
}

次に、Coodinator となっている XBee端末を PC に接続し、XCTU のメニューの [Working modes]-[Consoles working mode] を選択します。そして、画面左側の当該 XBee端末のアイコンをクリックし、画面右側の [Open] ボタンをクリックします。
これで、Coodinator に送信されてくるデータが XCTU の Console log に表示されるようになります。

次に、Arduino UNO に、ワイヤレスシールドを取り付け、さらにそこに Router となっている XBee端末を取り付けます。ワイヤレスシール上の USB-MICRO 切り替えスイッチは MICRO側にします。

これで準備が完了したので、USBコネクタなどから Arduino UNO に電源を供給します。

XCTU の Console log に概ね1秒周期でテキストデータが表示されていけば成功です。

透過モードでの通信における注意点

このように透過モードは、シリアル通信を簡単に無線に置き換えることができますが、運用に際しての注意点があります。

まず、データはマイコンから XBee へシリアル通信で送信したものがすぐに無線で送信されるわけではなく、パケット単位で送信されます。透過モードでは、「RO Packetization Timeout」に設定された値 (既定値は3) 分の文字の送信にかかる時間、マイコンから XBee へシリアル通信での送信がなかったら、そこまでにたまっていたデータが1パケットとして無線で送信されます。
このような動作であるため、複数の端末から一台の端末へ同時にデータを送信しても、別の端末からの送信データと混ざり合うことはありません。

実験してみたところ、正常に動作している時は良いのですが、例えば端末間の距離を離して電波を届かなくしたり、また戻したりなど、無線通信を不安定にさせた場合、データの一部が失われることがありました。
また、大きなデータを短い周期で送信した場合も、データの一部が失われることがありました。

ただし、上記は悪条件下での動作実験ですので、通信速度、通信量、端末の台数などを適切にすれば、透過モードを使っての複数台の端末での運用も可能だと考えられます。

APIモードでの通信

XBee のもう一つの通信モードが、「API(Application Programming Interface)モード(API mode/API operation)」です。
このモードは、「APIフレーム」と呼ばれる構造を持ったデータで通信が行われます。

透過モードでは、マイコンから XBee へ出力されたシリアル通信のデータがそのまま無線で送信されました。つまり、XBee はシリアル通信ケーブルの置き換えとして機能していました。
それに対して APIモードでは、XBee へのコマンドやリクエスト、それに対するレスポンスがマイコン-Xbee 間のシリアル通信を使って行われます。

例えば、他の端末へデータを送りたい場合、マイコンから XBee に対して送信リクエスト(ZigBee Transmit Request)フレームを送ります。
すると、XBee は送信先端末へ無線でデータを送信し、その送信結果(ZigBee Transmit Status)フレームをマイコンへシリアル通信で送ります。
送信先端末では、無線で送信元端末から送られてきたデータは、XBee からマイコンへ、受信パケット(ZigBee Receive Packet)フレームがシリアル通信で送られます。

また、APIモードでは自分の端末の XBee の設定の変更だけでなく、リモートで他の端末の設定を変更することもできます。

XBee端末の設定

XBee端末の設定(1) と同様の手順で、ファームウェアを更新します。APIモードの Coodinator の「Function set」は「ZigBee Coodinator API」、Routerの「Function set」は「ZigBee Router API」です。

そして、「AP API Enable」を 2 にします。これは、フレームの開始デリミタなどの特定の文字をエスケープする設定で、次に説明する「XBee-Arduino library」では、AP=2 で使うことを前提にしているためです。

XBee-Arduino library

XBee-Arduino library は Arduino で XBee の APIモードを利用するのに便利なライブラリです。
Arduino IDE 1.5 以降であれば、メニューの [スケッチ]-[Include Library]-[Manage Libraries] で、「Library Manager」を起動し、Filter欄に "XBee" と入力して検索すると見つかりますので、そこからインストールすることができます。
また、以下のリンクの XBee-Arduino library サイトからダウンロードして、手動でインストールすることもできます。

XBee-Arduino library を使ったAPIモードでの通信(1)

透過モードでの通信と同様に、ワイヤレスシールドを用いて、データの送信を繰り返すスケッチを以下に示します。

#include <XBee.h>

const double interval(1)/*[sec]*/;
unsigned char count(0);

struct xbee_manager {
  XBee manager;
  uint8_t payload[1];
  XBeeAddress64 destination_address64;
  ZBTxRequest tx;
  xbee_manager() : destination_address64(0, 0), tx(destination_address64, payload, sizeof(payload))
  {
    manager.setSerial(Serial);
  }
  void loop()
  {
    payload[0] = count;
    manager.send(tx);
    count++;
    delay(1000*interval);
  }
} xbee;

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

void loop()
{
  xbee.loop();
}

スケッチの中の「ZBTxRequest tx」は、データ送信リクエストである「ZigBee Transmit Request」フレームになります。
「XBeeAddress64 destination_address64」は、送信先 XBee端末の64bitデバイスアドレスです。DH、DLの設定値とは関係なく、ここに記述したアドレスにデータが送信されます。この例では、0、0 となっているので、Coodinator 宛てに送信されます。
「payload」が送信データになります。送信可能な最大のサイズは XBee の設定値「NP Maximum RF Payload Bytes」で確認できます。

準備ができたら、透過モードでの通信のときと同様に諸々接続し、XCTU の Frames log にデータが表示されるのを確認します。
APIモードのときの Frames log は、フレームが見やすい形式で表示されます。

XBee-Arduino library を使ったAPIモードでの通信(2)

XBee-Arduino library はソフトウェアシリアルを使って利用することも出来ます。
以下のスケッチでは、Arduino UNO の2番ピンを RX、3番ピンを TX に割り当てたソフトウェアシリアルで XBee と通信しています。
タイマー割り込みを使って1秒周期でデータを送信し、メインのループでは受信を行っています。
データを送信した結果は ZB_TX_STATUS_RESPONSE フレームの受信という形で得られるので、その送信結果を取り出して Arduino UNO の通常のシリアル通信で出力しています。そして、その送信結果が得られてから、次のデータを送信するようにしています。
また、データの受信(ZB_RX_RESPONSE)があった場合、送信元の XBee端末の64bitデバイスアドレスと受信データを出力します。
そしてもう一つ、XBee端末の異常などで送信結果が取得できない場合、そのままですとずっと送信ができなくなってしまいます。そこで、send_retry_count回タイマー割り込みが入っても送信処理ができなかったときは、強制的に送信を試みるようにしています。

#include <XBee.h>
#include <SoftwareSerial.h>
#include <MsTimer2.h>

const double interval(1)/*[sec]*/;
unsigned char count(0);

struct xbee_manager {
  XBee manager;
  uint8_t payload[1];
  ZBTxRequest tx;
  ZBTxStatusResponse tx_status;
  ZBRxResponse rx;
  const double read_packet_wait/*[sec]*/;
  bool tx_ready;
  const int send_retry_count;
  int send_retry;
  const uint8_t rx_pin;
  const uint8_t tx_pin;
  SoftwareSerial serial;
  xbee_manager() : tx(XBeeAddress64(0, 0), payload, sizeof(payload)),
    read_packet_wait(0.1), tx_ready(true), send_retry_count(5), send_retry(send_retry_count),
    rx_pin(2), tx_pin(3), serial(rx_pin, tx_pin)
  {
    pinMode(rx_pin, INPUT);
    pinMode(tx_pin, OUTPUT);
    serial.begin(9600);
    manager.setSerial(serial);
  }
  void periodic_transmission()
  { 
    if (tx_ready || --send_retry < 0) {
      payload[0] = count;
      manager.send(tx);
      Serial.println("XBee send.");
      tx_ready = false;
      send_retry = send_retry_count;
    }
    count++;
  }
  void loop() 
  {
    if (manager.readPacket(1000*read_packet_wait)) {
      switch (manager.getResponse().getApiId()) {
      case ZB_TX_STATUS_RESPONSE:
        manager.getResponse().getZBTxStatusResponse(tx_status);
        Serial.print("XBee DeliveryStatus: ");
        Serial.println(tx_status.getDeliveryStatus(), HEX);
        tx_ready = true;
        break;
      case ZB_RX_RESPONSE:
        manager.getResponse().getZBRxResponse(rx);
        Serial.print(rx.getRemoteAddress64().getMsb(), HEX);
        Serial.print(" ");
        Serial.print(rx.getRemoteAddress64().getLsb(), HEX);
        for (int i=0; i<rx.getDataLength(); i++) {
          Serial.print(" ");
          Serial.print(rx.getData(i), HEX);
        }
        Serial.println();
        break;
      default:
        Serial.print("Recieved API ID: ");
        Serial.println(manager.getResponse().getApiId(), HEX);
        break;
      }
    }
    else if (manager.getResponse().isError()) {
      Serial.print("readPacket() Error code: ");
      Serial.println(manager.getResponse().getErrorCode());
    }
  }
} xbee;

void periodic_transmission()
{ 
  xbee.periodic_transmission();
}

void setup()
{
  Serial.begin(9600);
  MsTimer2::set(1000*interval, periodic_transmission);
  MsTimer2::start();
}

void loop() 
{
  xbee.loop();
}

Arduino UNO と XBee は以下のように配線します。
XBee をブレッドボードに取り付けるときは、ピッチ変換基板が必要になります。
電圧レギュレータ付きのピッチ変換基板を利用する場合は、その仕様に合った電圧の電源を接続します。

XBeeのスリープモード

End Device となっている XBee端末は、スリープモードを設定することができます。スリープ機能を使って、不要なときは低電力状態に移行し、消費電力を抑えることができます。
Router でもスリープの設定は可能ですが、そうすると、データの中継ができなくなるため、役割としては End Device になります。

XBee には、以下の3種類のスリープモードがあります。

Pin sleep
Sleep RQ ピンが High のときスリープ、Low のときスリープ解除になるモード。
Cyclic sleep
設定した時間でスリープ→スリープ解除を繰り返すモード。
Cyclic sleep, pin wake
Pin sleep と Cyclic sleep の両方を動作を併せ持ったモード。

Pin sleep は、マイコンなど XBee端末外からの信号でスリープを解除できるため、使い勝手がよさそうです。しかし、実験したところ、スリープ状態ではネットワークから切り離されるため、スリープから復帰した後に通信が可能になるまで少し(5秒ほど)時間がかかりました。目的にもよりますが、すぐに通信できないのは不便です。

Cyclic sleep は、スリープ状態になってから XBeeの設定値「SN Number of Sleep Periods」×「SP Sleep Period」時間経過すると、スリープから復帰します。そして、そのとき送受信するデータがなければすぐにスリープ状態に戻ります。送受信するデータがあった場合、それらの通信を終え、送受信データがなくなってから「ST Time Before Sleep」時間経過するとスリープ状態に戻ります。
この他に、「SP Sleep Period」時間毎に親機(Coodinator または Router)に対してポーリングを行います。これにより、ネットワークから切り離されることなく稼働することができます。なお、親機は自身の SP の設定値を、任意の子機(End Device)の設定値以上にしておきます。これは子機がネットワークからいなくなったと判断するために使われます。
Cyclic sleep モードの XBee端末を利用する場合、マイコン側のタイミングでデータ送信をすることはできません。XBee端末の On/Sleep ピンを外部信号として利用し、XBee端末のスリープからの復帰を割り込みとするようにして、その割り込みが入ったタイミングでデータ送信を行うという方法になります。

Cyclic sleep, pin wake は、Cyclic sleep のような周期的な動作に加えて、Pin sleep のようにマイコンなどからスリープ状態を制御することができます。
そのため、マイコンなどからスリープ解除をして、すぐにデータ送信を行うことができます。ただし、すぐにとは言っても、わずかな時間が必要なので、Cyclic sleep と同様に、On/Sleep ピンを利用して、XBee端末のスリープからの復帰割り込みが入ったところでデータ送信を行うという方法になります。
マイコン側のタイミングでデータ送信をすることができるので、最も使い勝手のよいモードだと思います。

Cyclic sleep モード

Cyclic sleep を使って、約1秒周期でデータ送信を行うスケッチと回路を作ります。
XBee端末の On/Sleep(13番)ピンは、スリープ時は Low、スリープ解除時は High になりますので、これを Arduino UNO の 2番ピンに接続して、外部割り込みとしています。
また、回路のLEDは XBee端末のスリープ状態確認用です。スリープ時は消灯、スリープ解除時は点灯します。

XBee端末は次のように設定しますが、End Device とする端末の「Function set」は「ZigBee End Device API」にしておきます。その他設定値は、APIモードでの通信と同様にします。

End Device
SM Sleep Mode : 4 (Cyclic sleep)
ST Time Before Sleep : FA (250[mSec])
SP Sleep Period : 4B (750[mSec])
SN Number of Sleep Periods : 1
Coodinator
SP Sleep Period : 3E8 (10[Sec])
SN Number of Sleep Periods : 1

以下のスケッチでは XBee端末がスリープから復帰して、その割り込みが入る度にデータ送信が行われるので、スリープ状態になってから SP×SN 時間経過すると復帰し、そこでデータ送信が行われ、ST時間経過してからスリープに入るという動作になります。そこで、End Deviceを上記のように SP×SN+ST が1秒になるようが設定値にしています。

#include <XBee.h>

unsigned char count(0);

struct xbee_manager {
  XBee manager;
  uint8_t payload[1];
  ZBTxRequest tx;
  const double read_packet_wait/*[sec]*/;
  bool transmit_complete;
  xbee_manager() : tx(XBeeAddress64(0, 0), payload, sizeof(payload)),
    read_packet_wait(0.1), transmit_complete(true)
  {
    manager.setSerial(Serial);
  }
  void interrupt_process()
  {
    if (transmit_complete) {
      payload[0] = count;
      manager.send(tx);
      transmit_complete = false;
      count++;
    }
  }
  void loop()
  {
    if (manager.readPacket(1000*read_packet_wait))
      if (manager.getResponse().getApiId() == ZB_TX_STATUS_RESPONSE)
        transmit_complete = true;
  }
} xbee;

void interrupt_process()
{
  xbee.interrupt_process();
}

void setup()
{
  Serial.begin(9600);
  attachInterrupt(0, interrupt_process, RISING);
}

void loop()
{
  xbee.loop();
}

Cyclic sleep, pin wake モード

Cyclic sleep, pin wake を使って、1秒周期でデータ送信を行うスケッチと回路を作ります。

Cyclic sleep モードでの回路に加えて、Xbee端末の Sleep RQ(9番)ピンに、Arduino UNO の7番ピンを接続して、スリープ状態の制御を行います。
既定の設定(「PR Pull-up Resistor」)では、Sleep RQ ピンにはプルアップ抵抗が接続されているため、このピンに何も接続しなければ High 状態、つまりスリープ状態となります。そして、Low にすればスリープが解除されます。
これを実現するため、XBee端末をスリープさせたい場合は、Arduino UNO の7番ピンを入力モードに、スリープを解除したい場合は、Arduino UNO の7番ピンを出力モードにして、Low を出力するようにします。以下のスケッチでは sleep() および wakeup() がそれに当たります。

また、以下のスケッチでは、前述の通り、タイマーによる周期処理(periodic_process())では XBee端末のスリープ解除を行いますが、そこではデータ送信を行わず、XBee端末のスリープ解除信号による割り込み処理(interrupt_process())でデータ送信を行っており、Arduino からのリクエストによるスリープ解除と定期的なスリープ解除の区別をするために変数(bool transmit_request)を用いています。

XBee端末の設定はCyclic sleep モードと同様になりますが、以下の通り「SM Sleep Mode」を「5 (Cyclic sleep Pin-Wake)」にします。
また、このスケッチでは Arduino のタイミングでデータ送信を行うため、「SP Sleep Period」は適当な値で大丈夫です。

ただし、「ST Time Before Sleep」の値は重要で、XBee端末は Sleep RQ ピンの状態にかかわらず、データ送信後 ST 時間後にスリープ状態になります。つまり、ST の値を Arduino のタイマーの値以上にすると、望んだ周期でのデータ送信ができなくなります。
また、Sleep RQ ピンを使ってスリープ状態にせずとも、データ送信後 ST 時間でスリープ状態になるのですが、Sleep RQ ピンを Low(スリープ解除)のままにしておくと Sleep RQ ピンによるスリープ解除ができません。そのため loop() 内で、送信結果を受けたら Sleep RQ ピンを High(スリープ)にしています。

End Device
SM Sleep Mode : 5 (Cyclic sleep Pin-Wake)
ST Time Before Sleep : FA (250[mSec])
SP Sleep Period : 3E8 (10[Sec])
SN Number of Sleep Periods : 1
Coodinator
SP Sleep Period : 3E8 (10[Sec])
SN Number of Sleep Periods : 1
#include <XBee.h>
#include <MsTimer2.h>

const double interval(1)/*[sec]*/;
unsigned char count(0);

struct xbee_manager {
  XBee manager;
  uint8_t payload[1];
  ZBTxRequest tx;
  const double read_packet_wait/*[sec]*/;
  bool transmit_request;
  const int sleep_rq_pin;
  xbee_manager() : tx(XBeeAddress64(0, 0), payload, sizeof(payload)),
    read_packet_wait(0.1), transmit_request(false), sleep_rq_pin(7)
  {
    manager.setSerial(Serial);
  }
  void sleep()
  {
    pinMode(sleep_rq_pin, INPUT);
  }
  void wakeup()
  {
    pinMode(sleep_rq_pin, OUTPUT);
    digitalWrite(sleep_rq_pin, LOW);
  }
  void periodic_process()
  {
    wakeup();
    transmit_request = true;
    count++;
  }
  void interrupt_process()
  {
    if (transmit_request) {
      payload[0] = count;
      manager.send(tx);
      transmit_request = false;
    }
  }
  void loop()
  {
    if (manager.readPacket(1000*read_packet_wait))
      if (manager.getResponse().getApiId() == ZB_TX_STATUS_RESPONSE)
        sleep();
  }
} xbee;

void periodic_process()
{
  xbee.periodic_process();
}

void interrupt_process()
{
  xbee.interrupt_process();
}

void setup()
{
  Serial.begin(9600);
  attachInterrupt(0, interrupt_process, RISING);
  MsTimer2::set(1000*interval, periodic_process);
  MsTimer2::start();
  xbee.sleep();
}

void loop()
{
  xbee.loop();
}

Written by Jun Takahashi <junt@aihara.co.jp>