Arduinoセンサステーションを作る XBee設定編

 th_rapiro_xbee_arduino

今回は、前回作成したArduinoセンサステーションのセンシング情報を、XBeeを使って別のArduino(以前ラピロにシールドとして取り付けたもの)に飛ばします。ラピロ側のArduinoはさらにラピロの頭のRaspberry Piに繋がっているので、ラピロの頭のRaspberry Piでは、自身に直接繋がっているArduinoがセンシングした情報と、遠隔地のArduinoがセンシングした情報の両方を取り扱うことができます。

XBeeを取り扱うにあたって、とりあえず以下の本で簡単に勉強しました。

各XBeeにはコーディネータ、ルータ、エンドデバイスの3つのどれかの役割を与える必要があります。基本的にコーディネータとルータは常時電源ONが求められて、スリープ動作が可能なのはエンドデバイスのみ。

ラピロは電池でも動かせるロボットなので、ラピロ側のXBeeはできるかぎり省電力で動かしたいところです。ただ、今の所、センサの値を取得してどうこう(ログのクラウド蓄積とか)できる頭脳を持っているのがラピロの頭のRaspberry Piだけなので、とりあえずラピロは常に有線で電源供給し、ラピロのXBeeはコーディネータとして動作させることにします。

ラピロを完全無線にしつつ、かつ、XBeeも併用してできる限り長時間駆動させるなら、今回作成したセンサステーション側に別のRaspberry Piを繋ぐなりして母艦(コーディネータ)の機能を持たせて、ラピロ側のXBeeをエンドデバイスとして動作させた方がよいかもしれません。

センサステーションの方は、エンドデバイスにしてもいいのですが、今後これを中継器にして他のXBeeを繋いでいく可能性があるので、とりあえずルータとして動作させることにします。常時通電が必要になってしまいますが、そもそもArduino + センサ多数の構成で、ちゃんとした省電力対策をしないとバッテリ駆動にしてもあっという間に電池がなくなってしまいそうなものなので、これで良いことにします。

エンドデバイスに設定するのは、もっとシンプルな、ドアの開閉状態だけを通知するようなセンサノードを作るときに試そうと思います。とりあえず、コーディネータ+ルータの構成で先に進みます。

 

では、XBeeの設定に入ります。今回使用するXBeeは、XBee S2 ワイヤアンテナ型×2です。

まずはコーディネータ側から。XBee USBアダプタを使って、XBeeとPCを接続します。

X-CTUアプリで設定していきます。ここではMac版を使用。

 xbee_1

左上の”Add(+)”アイコンを押して、XBeeが繋がっていると思われるポートを選択してFinish。

xbee_2

XBeeが追加されます。

xbee_3

追加されたXBeeを選択すると、現在の設定情報が読み込まれて右側に表示されます。

xbee_4

とりあえずコーディネータにしたいので、右側の”Update firmware(マイコンに下矢印)”のアイコンを押します。

xbee_5

“ZigBee Coordinator API”を選んで、最新のFirmware Versionを選択。ちなみに、”ZigBee Coordinator AT”というのもありますが、ATモードとAPIモードの違いについては、恥ずかしながらまだよくわかっていません。ただ、今回はコーディネータの対向側にもArduinoがいますが、これがうまくいったら次はXBee+開閉センサのみ(=Arduinoなし)のノードも新たに追加したいと思っています。このような用途の場合はAPIモードが必須のようなので、初めからAPIモードで諸々設定していこうと思います。

さて、書き込みが成功したら、右側の”Function set”の欄が”ZigBee Coordinator API”になっていることが確認できます。

xbee_6

続いて、以下のように設定します。

  • PAN ID … 適当に16進数で設定
  • AP … 2に設定
  • Baud Rate … 57600。以後、PCがこのデバイスを探すときはボーレートを57600にしないと見つからないので注意。

それぞれ設定したら、個別に右側の”Write(鉛筆マーク)”をクリックして書き込み。とりあえずこの3つだけ。DHとDL(通信相手のアドレス)は、送信するメッセージの中に入れるので、ここでは設定しなくてよいです。設定が済んだら、ウィンドウ左側の”remove(Xマーク)”で接続解除をして、XBeeをUSBアダプタから外します。

 

続いて、ルータ側を設定します。XBeeとPCを接続して、コーディネータのときと同じように、”ZigBee Router API”の最新のFirmware versionとなるように、ファームウェアをアップデート。

アップデートに成功したら、以下のように設定します。

  • PAN ID … コーディネータで設定した値
  • AP … 2に設定
  • Baud Rate … 57600

それぞれ個別に書き込んだら、接続解除の後にXBeeを取り外します。

 

以上で、XBee本体の設定は終了です。次に、ArduinoソースをXBeeに対応させていきます。ATモードだとシリアル通信路が無線に置き換わるだけのようなので、ほぼ変更なしでいけると思うのですが、今回はAPIモードを使うので、ライブラリを使う方がよさそうです。

ライブラリはこちらにあるものを使います。ダウンロードしてきたものを、ディレクトリの名前を”XBee”に変更して、Arduinoのlibraries以下に保存します。これでArduino IDEを再起動すると、XBeeのライブラリとサンプルプログラムを参照できるようになります。

まず、サンプル”Series2_Tx”を参考にしながら、センサステーション側(送信側)のソースを修正します。

#include <XBee.h>
#include "DHT.h"
#include "pitches.h"

#define DHTTYPE DHT22   // DHT 22  (AM2302)

int TEMP_HUMID_PIN = 10;
int ILL_PIN        = A7;
int DOOR_PIN       =  4;
int MOTION_PIN     = 12;
int SPEAKER_PIN    = 13; 

int XBEE_SLEEP_PIN =  3; // 0 ... Enable XBEE / 1 ... Disable XBEE

DHT dht;

float temp_avg   = 0.0;
float humid_avg  = 0.0;
float ill_avg    = 0.0;
int   motion_avg = 0;
int   door       = 0;

// ドア(開閉)センサ以外のセンサは、問い合わせ時点から過去1分間の出力値の平均値を返す。
// ただし、モーションセンサについては、平均値が0.2以上なら「人がいた」と判定して1、
// 0.2未満なら「人がいない」と判定して0を返す

const int SENSOR_LOG_NUM = 30; // 2秒で1ループなので
float temp_log[SENSOR_LOG_NUM];
float humid_log[SENSOR_LOG_NUM];
float ill_log[SENSOR_LOG_NUM];
float motion_log[SENSOR_LOG_NUM];

int loop_count = 0;

//-----------------------------------------------------------------
#define XBEE_SEND_INTERVAL 60000 // 1分ごとに送出
XBee xbee = XBee();
// {温度下位バイト, 温度上位バイト, 湿度下位バイト, 湿度上位バイト, 照度下位バイト, 照度上位バイト, 
//  人感下位バイト, 人感上位バイト, 開閉下位バイト, 開閉上位バイト}
uint8_t payload[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
// SH + SL Address of receiving XBee
XBeeAddress64 addr64 = XBeeAddress64(0x0013a200, 0x40c1a65a); // コーディネータのアドレス
ZBTxRequest zbTx = ZBTxRequest(addr64, payload, sizeof(payload));
ZBTxStatusResponse txStatus = ZBTxStatusResponse();
unsigned long before = 0;
unsigned long now    = 0;
//-----------------------------------------------------------------

void updateTemperatureAvg(){
  float sum = 0.0;
  for(int i=0;i<SENSOR_LOG_NUM;i++){
    sum += temp_log[i];
  }
  temp_avg = sum/SENSOR_LOG_NUM;
}

void updateHumidityAvg(){
  float sum = 0.0;
  for(int i=0;i<SENSOR_LOG_NUM;i++){
    sum += humid_log[i];
  }
  humid_avg = sum/SENSOR_LOG_NUM;
}

float getIlluminance(){
  int AMS302_Value = analogRead(ILL_PIN);
  float lx = AMS302_Value*(3300.0/1024.0)/1000.0/(0.26/100);
  return lx;
}

void updateIlluminanceAvg(){
  float sum = 0.0;
  for(int i=0;i<SENSOR_LOG_NUM;i++){
    sum += ill_log[i];
  }
  ill_avg = sum/SENSOR_LOG_NUM;
}

void updateMotionAvg(){
  float sum = 0.0;
  int flag = 0;
  for(int i=0;i<SENSOR_LOG_NUM;i++){
    sum += motion_log[i];
  }
  float avg = sum/SENSOR_LOG_NUM;
  if(avg < 0.2){
    flag = 0;
  }else{
    flag = 1;
  }
  motion_avg = flag;
}

void get_coin(){
  tone(SPEAKER_PIN,NOTE_B5,100);
  delay(100);
  tone(SPEAKER_PIN,NOTE_E6,850);
  //delay(800);
  delay(500);
  noTone(SPEAKER_PIN);
}

void mario_1up(){
  tone(SPEAKER_PIN,NOTE_E6,125);
  delay(130);
  tone(SPEAKER_PIN,NOTE_G6,125);
  delay(130);
  tone(SPEAKER_PIN,NOTE_E7,125);
  delay(130);
  tone(SPEAKER_PIN,NOTE_C7,125);
  delay(130);
  tone(SPEAKER_PIN,NOTE_D7,125);
  delay(130);
  tone(SPEAKER_PIN,NOTE_G7,125);
  delay(125);
  noTone(SPEAKER_PIN);
}

void fire_ball(){
  tone(SPEAKER_PIN,NOTE_G4,35);
  delay(35);
  tone(SPEAKER_PIN,NOTE_G5,35);
  delay(35);
  tone(SPEAKER_PIN,NOTE_G6,35);
  delay(35);
  noTone(SPEAKER_PIN);
}

void setup() {
  Serial.begin(57600);  // シリアル通信速度
  xbee.setSerial(Serial);

  pinMode(ILL_PIN, INPUT);
  pinMode(MOTION_PIN, INPUT);
  pinMode(DOOR_PIN, INPUT);
  pinMode(SPEAKER_PIN, OUTPUT);
  pinMode(XBEE_SLEEP_PIN, OUTPUT);

  for(int i=0;i<SENSOR_LOG_NUM;i++){
    temp_log[i]   = 0.0;
    humid_log[i]  = 0.0;
    ill_log[i]    = 0.0;
    motion_log[i] = 0.0;
  }
  door = 0;

  digitalWrite(SPEAKER_PIN, LOW);
  digitalWrite(XBEE_SLEEP_PIN, LOW); // XBee Enable

  dht.setup(TEMP_HUMID_PIN);

  //Serial.println("Ready.");
}

void loop() {

  delay(2000);

  if(loop_count == SENSOR_LOG_NUM){
    loop_count = 0;
  }
  temp_log[loop_count]   = dht.getTemperature();
  humid_log[loop_count]  = dht.getHumidity();
  ill_log[loop_count]    = getIlluminance();
  motion_log[loop_count] = (float)digitalRead(MOTION_PIN);
  door                   = digitalRead(DOOR_PIN);

  // Check if any reads failed and exit early (to try again).
  if (isnan(temp_log[loop_count]) || isnan(humid_log[loop_count])) {
    //Serial.println("Failed to read from DHT sensor! Complement pre-measured value");
    fire_ball();
    if(loop_count > 0){
      temp_log[loop_count]  = temp_log[loop_count-1];
      humid_log[loop_count] = humid_log[loop_count-1];
    }else{
      temp_log[loop_count]  = temp_log[SENSOR_LOG_NUM-1];
      humid_log[loop_count] = humid_log[SENSOR_LOG_NUM-1];
    }
  }

  //Serial.print(temp_log[loop_count]); Serial.print(" / "); 
  //Serial.print(humid_log[loop_count]); Serial.print(" / ");
  //Serial.print(ill_log[loop_count]); Serial.print(" / ");
  //Serial.print(motion_log[loop_count]); Serial.print(" / ");
  //Serial.println(door);

  // あまり無線通信を発生させたくないので、送信するのは1分ごととする。 
  now = millis();
  if(now - before > XBEE_SEND_INTERVAL){
    updateTemperatureAvg();
    payload[0] = (uint8_t)(int(temp_avg*100));
    payload[1] = (uint8_t)(int(temp_avg*100) >> 8);
    updateHumidityAvg();
    payload[2] = (uint8_t)(int(humid_avg*100));
    payload[3] = (uint8_t)(int(humid_avg*100) >> 8);
    updateIlluminanceAvg();
    payload[4] = (uint8_t)(int(ill_avg));
    payload[5] = (uint8_t)(int(ill_avg) >> 8);
    updateMotionAvg();
    payload[6] = (uint8_t)motion_avg;
    payload[7] = (uint8_t)(motion_avg >> 8);     
    payload[8] = (uint8_t)door;
    payload[9] = (uint8_t)(door >> 8);
    xbee.send(zbTx);
    fire_ball();
    before = now;

    // after sending a tx request, we expect a status response
    // wait up to half second for the status response
    if (xbee.readPacket(500)) {
      // got a response!
      get_coin();
      // should be a znet tx status            	
      if (xbee.getResponse().getApiId() == ZB_TX_STATUS_RESPONSE) {
        xbee.getResponse().getZBTxStatusResponse(txStatus);

        // get the delivery status, the fifth byte
        if (txStatus.getDeliveryStatus() == SUCCESS) {
          // success.  time to celebrate

        } else {
          // the remote XBee did not receive our packet. is it powered on?       
        }
      }
    } else if (xbee.getResponse().isError()) {
      //nss.print("Error reading packet.  Error code: ");  
      //nss.println(xbee.getResponse().getErrorCode());
    } else {
      // local XBee did not provide a timely TX Status Response -- should not happen

    } 
  }

  loop_count++;
}

データを送信したときと、送信に対するレスポンスが返ってきたときに、圧電スピーカーで適当な音を鳴らすようにしています。get_coin(), mario_1up(), fire_ball()はその適当な音を鳴らすためだけのコードなので、適当に省いていただければと思います。

 

次に、ラピロに取り付けたArduino側(受信側)のソースを、サンプル”Series2_Rx”を参考に修正します。

#include <XBee.h>
#include <Wire.h>
#define SLAVE_ADDRESS 0x21

const int HIGH_STATE = 1;
const int LOW_STATE  = 0;
const float SUPPLY_VOLT = 3.3;

int TEMP_PIN   = A2;
int HUMID_PIN  = A0;
int ILL_PIN    = A3;
int MIC_PIN    = A1;
int MOTION_PIN = 12;

float temp_avg   = 0.0;
float humid_avg  = 0.0;
float ill_avg    = 0.0;
float mic_avg    = 0.0;
int   motion_avg = 0;

// すべてのセンサは、問い合わせ時点から過去1分間の出力値の平均値を返す。
// ただし、モーションセンサについては、平均値が0.2以上なら「人がいた」と判定して1、
// 0.2未満なら「人がいない」と判定して0を返す

const int SENSOR_LOG_NUM = 60;
float temp_log[SENSOR_LOG_NUM];
float humid_log[SENSOR_LOG_NUM];
float ill_log[SENSOR_LOG_NUM];
float mic_log[SENSOR_LOG_NUM];
float motion_log[SENSOR_LOG_NUM];

int loop_count = 0;
byte sendByte;

//-----------------------------------------------------------------
XBee xbee = XBee();
XBeeResponse response = XBeeResponse();
// create reusable response objects for responses we expect to handle 
ZBRxResponse rx = ZBRxResponse();
ModemStatusResponse msr = ModemStatusResponse();
// {温度下位バイト, 温度上位バイト, 湿度下位バイト, 湿度上位バイト, 照度下位バイト, 照度上位バイト, 
//  人感下位バイト, 人感上位バイト, 開閉下位バイト, 開閉上位バイト}
const uint32_t XBEE_ADDRESS_LIVING = 0x40CA2F26; // ルータの下位アドレス
uint8_t receiveXBeeData_living[] ={0,0,0,0,0,0,0,0,0,0};
//-----------------------------------------------------------------

float getTemperature(){
  int LM35DZ_Value = analogRead(TEMP_PIN);
  return ((SUPPLY_VOLT * LM35DZ_Value) / 1024) * 100;
}

void updateTemperatureAvg(){
  float sum = 0.0;
  for(int i=0;i<SENSOR_LOG_NUM;i++){
    sum += temp_log[i];
  }
  temp_avg = sum/SENSOR_LOG_NUM;
}

float getHumidity(float temp){
  int HIH4030_Value = analogRead(HUMID_PIN);
  float voltage  = HIH4030_Value/1024.0 * SUPPLY_VOLT;
  float sensorRH = 161.0 * voltage / SUPPLY_VOLT - 35;
  float trueRH   = sensorRH / (1.0546 - 0.00216 * temp);
  return trueRH;
}

void updateHumidityAvg(){
  float sum = 0.0;
  for(int i=0;i<SENSOR_LOG_NUM;i++){
    sum += humid_log[i];
  }
  humid_avg = sum/SENSOR_LOG_NUM;
}

float getIlluminance(){
  int AMS302_Value = analogRead(ILL_PIN);
  float lx = AMS302_Value*(3300.0/1024.0)/1000.0/(0.26/100);
  return lx;
}

void updateIlluminanceAvg(){
  float sum = 0.0;
  for(int i=0;i<SENSOR_LOG_NUM;i++){
    sum += ill_log[i];
  }
  ill_avg = sum/SENSOR_LOG_NUM;
}

float getMic(){
  float ADMP401_Value = analogRead(MIC_PIN);
  return 3.3*ADMP401_Value/1024.0;
}

void updateMicAvg(){
  float sum = 0.0;
  for(int i=0;i<SENSOR_LOG_NUM;i++){
    sum += mic_log[i];
  }
  mic_avg = sum/SENSOR_LOG_NUM;
}

void updateMotionAvg(){
  float sum = 0.0;
  int flag = 0;
  for(int i=0;i<SENSOR_LOG_NUM;i++){
    sum += motion_log[i];
  }
  float avg = sum/SENSOR_LOG_NUM;
  if(avg < 0.2){
    flag = 0;
  }else{
    flag = 1;
  }
  motion_avg = flag;
}

void receiveData(int byteCount){
  int i2c_command = -1;
  while(Wire.available()){
    i2c_command = Wire.read();
    //Serial.print("i2c data received: ");
    //Serial.println(i2c_command);
    switch(i2c_command){
      case 0xF0: updateTemperatureAvg();
                 sendByte = (uint8_t)(int(temp_avg*100)); break;
      case 0xF1: sendByte = (uint8_t)(int(temp_avg*100) >> 8); break;
      case 0xF2: updateHumidityAvg();
                 sendByte = (uint8_t)(int(humid_avg*100)); break;
      case 0xF3: sendByte = (uint8_t)(int(humid_avg*100) >> 8); break;
      case 0xF4: updateIlluminanceAvg();
                 sendByte = (uint8_t)(int(ill_avg)); break;
      case 0xF5: sendByte = (uint8_t)(int(ill_avg) >> 8); break;
      case 0xF6: updateMicAvg();
                 sendByte = (uint8_t)(int(mic_avg*100)); break;
      case 0xF7: sendByte = (uint8_t)(int(mic_avg*100) >> 8); break;
      case 0xF8: updateMotionAvg();
                 sendByte = (uint8_t)motion_avg; break;
      case 0xF9: sendByte = (uint8_t)(motion_avg >> 8); break;
      // ------------------------------------------------------------------
      case 0xE0: sendByte = receiveXBeeData_living[0]; break;
      case 0xE1: sendByte = receiveXBeeData_living[1]; break;
      case 0xE2: sendByte = receiveXBeeData_living[2]; break;
      case 0xE3: sendByte = receiveXBeeData_living[3]; break;
      case 0xE4: sendByte = receiveXBeeData_living[4]; break;
      case 0xE5: sendByte = receiveXBeeData_living[5]; break;
      case 0xE6: sendByte = receiveXBeeData_living[6]; break;
      case 0xE7: sendByte = receiveXBeeData_living[7]; break;
      case 0xE8: sendByte = receiveXBeeData_living[8]; break;
      case 0xE9: sendByte = receiveXBeeData_living[9]; break;
    }
  }
}

void sendData(){
  Wire.write(sendByte);
}

void setup() {
  // put your setup code here, to run once:
  Serial.begin(57600);  // シリアル通信速度
  xbee.begin(Serial);

  pinMode(TEMP_PIN, INPUT);
  pinMode(HUMID_PIN, INPUT);
  pinMode(ILL_PIN, INPUT);
  pinMode(MIC_PIN, INPUT);
  pinMode(MOTION_PIN, INPUT);

  Wire.begin(SLAVE_ADDRESS);
  Wire.onReceive(receiveData);
  Wire.onRequest(sendData);

  for(int i=0;i<SENSOR_LOG_NUM;i++){
    temp_log[i]   = 0.0;
    humid_log[i]  = 0.0;
    ill_log[i]    = 0.0;
    mic_log[i]    = 0.0;
    motion_log[i] = 0.0;
  }

  //Serial.println("Ready.");
}

void loop() {

  xbee.readPacket();

  if (xbee.getResponse().isAvailable()) {
    // got something

    if (xbee.getResponse().getApiId() == ZB_RX_RESPONSE) { // APIモードフレームタイプ 0x90: RX受信
      // got a zb rx packet

      // now fill our zb rx class
      xbee.getResponse().getZBRxResponse(rx);

      // どこのXBeeからデータから送られてきたデータか確認
      XBeeAddress64 address64 = rx.getRemoteAddress64();
      uint32_t address64_lsb = address64.getLsb();// 上位バイトはXBeeで共通なので、下位バイトでアドレスを判断する
      if(address64_lsb == XBEE_ADDRESS_LIVING){
        // リビングのXBeeから送られてきたデータの場合
        uint8_t payload_length = rx.getDataLength();
        //uint8_t offset = rx.getDataOffset();
        uint8_t* payload = rx.getData();
        for(int i=0;i<payload_length-1;i++){
          receiveXBeeData_living[i] = payload[i];
        }
      }

      if (rx.getOption() == ZB_PACKET_ACKNOWLEDGED) { // 受信オプション 0x01: パケットには確認応答が返された
          // the sender got an ACK
          //flashLed(statusLed, 10, 10);
      } else {
          // we got it (obviously) but sender didn't get an ACK
          //flashLed(errorLed, 2, 20);
      }
      // set dataLed PWM to value of the first byte in the data
      //analogWrite(dataLed, rx.getData(0));
    } else if (xbee.getResponse().getApiId() == MODEM_STATUS_RESPONSE) { // APIモードフレームタイプ 0x8a: モデムステータス
      xbee.getResponse().getModemStatusResponse(msr);
      // the local XBee sends this response on certain events, like association/dissociation

      if (msr.getStatus() == ASSOCIATED) {
        // yay this is great.  flash led
        //flashLed(statusLed, 10, 10);
      } else if (msr.getStatus() == DISASSOCIATED) {
        // this is awful.. flash led to show our discontent
        //flashLed(errorLed, 10, 10);
      } else {
        // another status
        //flashLed(statusLed, 5, 10);
      }
    } else {
    	// not something we were expecting
      //flashLed(errorLed, 1, 25);    
    }
  } else if (xbee.getResponse().isError()) {
    //nss.print("Error reading packet.  Error code: ");  
    //nss.println(xbee.getResponse().getErrorCode());
  }

  if(loop_count == SENSOR_LOG_NUM){
    loop_count = 0;
  }
  temp_log[loop_count]   = getTemperature();
  humid_log[loop_count]  = getHumidity(temp_log[loop_count]);
  ill_log[loop_count]    = getIlluminance();
  mic_log[loop_count]    = getMic();
  motion_log[loop_count] = (float)digitalRead(MOTION_PIN);

  //Serial.print(temp_log[loop_count]); Serial.print(" / "); 
  //Serial.print(humid_log[loop_count]); Serial.print(" / ");
  //Serial.print(ill_log[loop_count]); Serial.print(" / ");
  //Serial.print(motion_log[loop_count]); Serial.print(" / ");
  //Serial.println(mic_log[loop_count]);

  loop_count++; 

  delay(1000);
}

このArduino自身に繋がっている各種センサを処理するためのコードが書かれている上に、さらにI2Cの処理まで書かれているので、ごちゃごちゃしてしまって申し訳ありません。XBeeに関係するのは、36〜44行目と、setup()、loop()の中ぐらいです。

サンプルでは実際にデータのペイロードを処理するところまで書いてくれていなかったので、必要最小限のところを、適当にこんな感じかなーというものを書いています。

 

最後に、これは必要な人だけですが、I2CでArduinoとRaspberry Piが通信する場合の、Raspberry Pi側のPythonソースの修正です。自分はbottleフレームワークを使って、HTTPアクセスの形でRaspberry PiとArduinoの通信を開始しているので、以下のような感じになります。抜粋なので、不要なimport文があったり、逆に欠けている部分があるかもしれませんのでご了承ください。

from bottle import route, request, response, run, hook, static_file
import requests
import time
import smbus

bus = smbus.SMBus(1)
ARDUINO_ADDRESS = 0x21

registerMap = {
  "temperature_t":[0xF0,0xF1],
  "humidity_t":   [0xF2,0xF3],
  "illuminance_t":[0xF4,0xF5],
  "mic_t":        [0xF6,0xF7],
  "motion_t":     [0xF8,0xF9],
  "temperature_l":[0xE0,0xE1],
  "humidity_l":   [0xE2,0xE3],
  "illuminance_l":[0xE4,0xE5],
  "motion_l":     [0xE6,0xE7],
  "door_l":       [0xE8,0xE9]
}

def read_value(sensor):
  time.sleep(0.01)
  bus.write_byte(ARDUINO_ADDRESS,registerMap[sensor][0])
  time.sleep(0.01)
  data_1 = bus.read_byte(ARDUINO_ADDRESS)
  bus.write_byte(ARDUINO_ADDRESS,registerMap[sensor][1])
  time.sleep(0.01)
  data_2 = bus.read_byte(ARDUINO_ADDRESS)
  #print data_1
  #print data_2
  data = ((data_2 << 8) | data_1)
  if sensor in ['temperature_t','humidity_t','mic_t','temperature_l','humidity_l']:
    data = data*1.0/100
  return data 

@route('/v1/robots/rapiro/sensors')
def control_arduino_read():
  print read_value('temperature_t')
  print read_value('humidity_t')
  print read_value('illuminance_t')
  print read_value('mic_t')
  print read_value('motion_t')
  print read_value('temperature_l')
  print read_value('humidity_l')
  print read_value('illuminance_l')
  print read_value('motion_l')
  print read_value('door_l')
  return "OK, Arduino READ."

これで動作させてみたところ、センサステーションからラピロのArduinoに対してデータが送信されていることを確認することができました。ただ、割と頻繁に通信失敗(Ackが返ってこない)が起こっているようなので、本当はXBee本体の設定のときに、もう少し色々ちゃんと設定しないといけないのかもしれません。

 

というわけで、省電力設定とか応答処理はまったく考慮していませんが、一応XBeeでの通信のワンパス成功しました。あとはXBeeを単体でエンドデバイスとして動作させることができれば、とりあえずXBeeを使った簡単なネットワークは実現できるかな、と思います。

自分の場合は元々ラピロの装備の拡張、という目的からスタートしたので、Arduino – (XBee) – Arduino – (I2C) – Raspberry Piというあまり一般的ではない構成でのお話になってしまっていますが、単純にArduino – (XBee) – Arduino間の通信に使えるお話だと思うので、何かしらの参考になれば幸いです。