2014年7月29日火曜日

ClassNotFoundException: processing.core.PAplet への対処

ネットへの恩返しをしておこう。
Processingを利用して、色々といじっている内に
 ClassNotFoundException: processing.core.PAplet
というエラーが出て、Serialライブラリが使えなくなった。
(Mac OS X 10.9.4, Processing 2.0.3)

対処方法は、
 /Library/Java/Extension
に入れたものを出す(空にする)、であった。

以下のサイトには、
http://blog.livedoor.jp/reona396/archives/54536269.html
core.jar
を入れろとある。私の場合は、ArduinoとのZigBee通信のために試行錯誤している途中で、RXTX.jar, Serial.jarなどを上記 Extension フォルダに入れていたのが悪さをしていた。

Javaがどのライブラリを見るか迷ったのが原因のようだ。
上記のサイトに書いてある内容を見てヒントを思いついたので、感謝をこめて、私のメモをアップする。

以上

2014年7月28日月曜日

ZigBee (API)通信用のProcessingプログラム(3台の子機)

ZigBee (API) + Arduino -- ZigBee (API) + Macの通信」でアップしたProcessingプログラムを、3台のZigBee+Arduinoから受信するように改良した。

ZigBeeのアドレステーブルをPCで保持し、アドレスを確認してそれぞれにデータを保持する。

// サンプル
import processing.serial.*;
import java.util.concurrent.*;
import java.util.Queue;
import org.apache.log4j.PropertyConfigurator;

XBee xbee;
Queue<XBeeResponse> queue = new ConcurrentLinkedQueue<XBeeResponse>();
boolean message;
XBeeResponse response;

String senderAddress = "00:13:A2:00:40:B9:3C:CB";
String rxStr = "********";
String txStatus = "";
int rssi = 0;
int rxVarC, rxErrC, tblC;

String[] addressTbl = new String[3];
int[] rxVar = new int[3];
int[] oldrxVar = new int[3];
int[] rxErr = new int [3];

void setup()
{
  size(640,200);
  colorMode(RGB,256);
  background(0,0,0);

  setUpDataTble(); // センサノードに対応した状態管理の初期化
  
  try {
    //optional.  set up logging
    //PropertyConfigurator.configure(dataPath("")+"/log4j.properties");
    xbee = new XBee();
    // replace with your COM port
    xbee.open("/dev/tty.usbserial-A5025X1E", 9600); // xbeeを繋いでいるシリアルポートを指定
    xbee.addPacketListener(new PacketListener() { // xbeeのレスポンスを待つパケット監視を設定
      public void processResponse(XBeeResponse response) {
        queue.offer(response);
      }
    }
    );
  } 
  catch (Exception e) {
    System.out.println("XBee failed to initialize");
    e.printStackTrace();
    System.exit(1);
  }
}

void setUpDataTble() {
  //int[] idTbl = new int[3];
  //int[] rxVar = new int[3];
  //int[] oldrxVar = new int[3];
  //int[] rxErr = new int [3];
  tblC = rxVarC = rxErrC = 0;
}

int setData(String nad, int rx) {
  int id = -1;
  for (int i = 0; i < tblC; i++) {
    if (addressTbl[i].equals(nad)) id = i;
  };
  if (id == -1) {
    addressTbl[tblC] = nad;
    id = tblC++;
  };
  
  rxVar[id] = rx;
  if (rxVar[id] != oldrxVar[id] + 1) rxErr[id]++;
  oldrxVar[id] = rxVar[id];
  
  return id;
};

void draw(){
  // パケット読み取り
  try {
    readPackets();
  }
  catch (Exception e) {
    e.printStackTrace();
  }
  
  background(255);
  textAlign(LEFT);
  textSize(20);
  
  // 受信強度の表示
  fill(0); text("RSSI", 10, 25); fill(0,0,255);
  text(String.format("%d", rssi) + "dB", 100, 25);
  
  // 受信元アドレスを表示
  fill(0); text("Address", 10, 50); fill(0,0,255);
  text(senderAddress, 100, 50);

  // 受信データの表示  
  fill(0);
  text("Rx Data", 10, 75);
  fill(0,0,255);
  //text(rxStr, 100, 75);
  text(rxVarC, 200, 75);
  text("ERR:"+rxErrC, 300, 75);
  
  for (int i = 0; i < tblC; i++) {
    text(addressTbl[i] +" = "+rxVar[i], 110, 100 + i * 25);
  };
  
  // 受信状態の表示
  fill(255,0,0);
  text(txStatus, 10, 100);
}

// パケットの読み込みとエコー、ATDBの送信
void readPackets() throws Exception {

  while ((response = queue.poll()) != null) {
    // we got something!
    try {
      if (response.getApiId() == ApiId.ZNET_RX_RESPONSE && !response.isError()) {
        ZNetRxResponse rxResponse = (ZNetRxResponse) response;
        
        // アドレスをintで取得
        int[] addressArray = rxResponse.getRemoteAddress64().getAddress();
        String[] hexAddress = new String[addressArray.length];
        // 16進数のStringへ
        for(int i=0; i<addressArray.length;i++){
          hexAddress[i] = String.format("%02x",addressArray[i]);
        }
        // 送信元アドレスを文字列で保存
        senderAddress = join(hexAddress,":");
        
        //受信データを取得
        int[] rxData = rxResponse.getData();
        rxStr = "";
        for(int i=0; i<rxData.length; i++){
          rxStr += String.format("%c", rxData[i]);
        }
        // Arduinoのプログラムと対応して、2バイトのintデータを前提にしている。
        rxVarC = rxData[0] << 8 | rxData[1];
        int id = setData(senderAddress, rxVarC);
        rxVarC = rxVar[id];
        rxErrC = rxErr[id];
        
        // エコー (送られてきたデータを、そのまま返信)
        int[] payload = rxData;
        XBeeAddress64 addr64 = new XBeeAddress64(addressArray); // データの送信元アドレスを設定
        ZNetTxRequest request = new ZNetTxRequest(addr64, payload); // 送られてきたデータをAPIパケットに変換
        try {
          // データ送信
          ZNetTxStatusResponse response = (ZNetTxStatusResponse) xbee.sendSynchronous(request);
          txStatus = "";
        } catch (Exception e) {
          txStatus = "tx request timed out";
        }
        
        // ATコマンドを送信し、RSSIを取得
        AtCommand  at = new AtCommand("DB"); // ATDBで、RSSIを取得し、電波強度を調査する。
        AtCommandResponse response = (AtCommandResponse) xbee.sendSynchronous(at, 5000);
        if (response.isOk()) {
          // 成功
          rssi = response.getValue()[0];
          rssi = rssi * (-1);
        }
      }
    }
    catch (ClassCastException e) {
      // not an IO Sample
    }
  }
}

2014年7月27日日曜日

Xbee+ArduinoとXbee+Macを繋ぐ方法(つまづきメモ)

2014年7月27日

XBee+ArduinoとXBee+Macを繋ぐ方法
http://airshipnewsjapan.blogspot.jp/2014/07/xbeearduinoxbeemac.html
をまとめるに至った過程で、苦労したことをメモとして記録しておく。

XBee+Arduino+バッテリーで、センサノードをばらまいて、PCでデータ収集をするシステムを作るのが目的だ。

XBee WIFIで作ろうかと思ったが、インターネットへの接続はPCで集約した方が便利なのと、センサノードの省電力化をはかりたいと思ったため、ZigBeeを扱えるXbee ZBで作ってみることにした。

最初はキーワードが全然わからず苦労した。
XBeeとZigBeeとArduino、さらに懐かしいATコマンドのそれぞれの用語がある。

XBeeをArduinoに搭載するには、XBeeとArduinoを接続する必要がある。簡単なのはシールドを用いることだ。ところが3つ購入したシールドの内の一つが不良であった。4個のXBeeを相互接続したところ、どうしても4つ目が認識せず、Arduinoのせいなのか、XBeeのせいなのか、シールドのせいなのか分からず切り分け試験に苦労した。

ArduinoやXBeeを交換してもなおらないので、シールドが妖しいとにらんだが、プログラムの方も自信がなかったので、確信が持てなかった。シールドの代わりに、XBeeをピッチ変換基板(細かなハンダつけが必要)に載せ変えたところ、問題なく動いた。

3カ所以上のハンダづけは久しぶりだったが、昔取った杵柄なんだね、身体が覚えていた。

なお、ここまでの試験では、ATモードで接続していた。
XBeeに関するATモードでの設定は、スイッチサイエンスのホームページが詳しい。
丁寧に記述されていて、この通りにすれば設定できる。

さてここでATモードというのが出てきたが、XBeeには、ATモードとAPIモードという2種類がある。ATモードは別名「透過モード」とも言う。ZigBeeは本来、規格にあったパケットをやりとりすることで通信する。それがAPIモードである。しかしAPIモードは数字列で普通のプログラマが直接やりとりするには低レベルすぎる。

そこでXBeeでは、APIモードのパケットを独自に変換して、直接シリアルモデムで接続されているかのように見せてくれるモードが用意されている。つまり、ATモードだと、
 PC--シリアル--モデム----モデム--シリアル--Arduino
というイメージで使える。

ただし簡易モードなので、1対1通信程度なら問題ないが、センサノードを複数台扱おうとすると無理がある。ただ、無理がある、といろんなホームページに書かれているが、どの程度無理があるのかと思って、1対3で接続してテストしてみた。PC1台に、センサノード3台をつなげてみた。

PCに繋いだXBeeはCoordinator ATで設定し、ブロードキャスト設定(送信先をDH=0, DL=FFFFにする)で、センサノードは全て、Router ATで設定して、送付先をPCに繋いだCoordinatorにした。


なお、ここでまた新しい用語が出てきた。Coordinatorと Routerである。あとEnd Nodeというのがある。ZigBeeにはこの3種類がある。Coordinatorというのは、一つのネットワークの監視役である。Routerは中継に使われる。End Nodeというのは末端のセンサノードである。End Nodeにはスリープする機能がある。

設定方法は、「Xbee+ArduinoとXbee+Macを繋ぐ方法」に書いた通りで、xctuというソフトを使う。xctuにはWindows版しかない、と多くのホームページで書かれているが、今はちゃんとMac版がある。けっこう使い勝手の良いソフトである。

Coordinatorとか、Routerとか、またはATモードとかAPIモードとかの変更は、ファームを変えることで行う。単に設定変更ではできない。

さて、元に戻って、ATモードの限界についてだが。テストしたのは、Coordinatorから1, 2, 3,...と番号を1秒おきにブロードキャストして、センサノードにそれぞれ番号を割り当てて、自分の番号が送られてきたら、値を返す、という単純なテストである。

最初はうまく行っていた。順調に値が返ってきていた。ところが何十回かやっていると、通信が一時停止することがわかった。細かく追っていないが、ブロードキャストが滞っているものと推察される。その理由はブロードキャストタイミングを早めて、1/10秒ごとに番号を送るようにすると、あっという間に滞るためだ。

滞るのも、1、2秒なら良いが酷い場合には、10秒も20秒も滞る。これでは使い物にならない。ブロードキャストの範囲などを細かく制限すれば、大丈夫になるのかもしれないが、あまり深入りしても時間の無駄なので、ここでATモードとはおさらばすることにした。

さてAPIモードを使おうと思ったが、直接APIパケットを作るのはいくらなんでもやりたくないので、便利なものが無いか探した。最初は小林さん@IAMASたちが作ったfunnelを使おうかと思ったが、Processing 2.0に対応していないというのと、カプセル化が強く、高レベルすぎるのが気になり、もう少し低レベルで扱えるライブラリを探した。

ただ、だいぶ自分の頭が整理できたので、funnelを使うことを再検討してみても良い。

funnelは、小林さんの「Prototyping Lab--「作りながら考える」ためのArduino実践レシピ」の前提とされているライブラリで、未踏で作られたかなりできの良いものだ。2009年に完成しているので、その後のアップデートが滞っているのだろうか?

funnelだと、Arduino側の設定も、PC側からできてしまう。ProcessingでArduinoの入出力まで制御できるというすごいライブラリだ。ただ、このアクロバットをこなすために、ちょっと凝ったことがしてある。私としては、Arduinoは、Arduinoでコーディングして、PCは受信したデータを扱ったり、Arduinoに指令を出すなどの役割分担をする方が、気持ち的に良い。オブジェクト指向的というか。

調べてみると、Arduinoには、XBee-Arduinoというライブラリが、ProcessingというかJavaには、xbee-apiというライブラリがあることがわかった。いずれも

 APIモード
 エスケープモード
を扱えるライブラリである。
今回、「XBee+ArduinoとXBee+Macを繋ぐ方法」で解説しているのは、この方法である。

さて、ここでまた新しい用語「エスケープモード」が出てきた。APIモードでは、すべてのパケットは0x7Eで始まる。よってパケットの中に0x7Eを含むデータがあると、パケットの先頭と間違える可能性がある。そこで、パケット内の0x7Eを別の値に退避(エスケープ)させることで、0x7Eを扱えるようにするモードである。

実はXBeeをこのモードに設定する方法がわからず、けっこう苦労した。結論としてはxctuで、AP-API Enableという設定項目を「2」に変更することで行う。

エスケーブモードにしていないとライブラリは全然動いてくれない。

また二つのライブラリを使うので、どっちのライブラリの使用方法にエラーがあるのか、の切り分け試験ができずに苦労した。

これは結論としては、PC側はシリアルモニタで監視できるので、最初にXBee-Arduino側から確認する、のが良いことに気づいた。

まずXBee-Arduinoをインストールして、データをZigBeeに送り出す簡単なサンプルプログラムをArduinoに転送することとした。XBeeを繋いだPCでシリアルモニタにより、シリアルを監視すると、値が一定時間毎に送られているのを確認できる。

ここでシリアルモニタとしては、Cool Termが良い。HEX表示もあり、使い勝手が良い。

ちゃんと値が送られてこない場合は、XBee-Arduino側に問題がある。私の場合は、エスケープモードになっていなかったためだった。

次に、Processing側のxbee-apiの設定をした。ちなみにxbee-apiに関しては、あまり日本語の情報が多くない。javaでxbeeを使おうという人がまだ多くないということだろうか?

ちなみにProcessingとは、MITで開発された簡易言語である。ここまで読むような人で知らない人はいないよね。javaベースで、javaネイティブで書くより簡単に可視化などのプログラミングができる。ProcessingのIDEは、ArduinoのIDEと見た目や使い勝手がそっくりなのも、Arduinoと一緒に使うときの敷居を下げている。

xbee-apiで苦労したのは、ドキュメントが少なく、未だに更新され続けているためだった。
例えばProcessing 2.0.3までは、私がアップしたコードで動く。しかし、2014年3月の最新のProcessingでは、RXTXというシリアルを扱うライブラリが使われなくなっており、xbee-apiが前提としているため、RXTXを別にインストールしないと使えない。

なおlog4jは使わなくても動く。log4jを使わないようにコメントアウトすると警告(Warning)が出るが、通信自体には影響がない。

Arduinoにモバイルバッテリーを繋いで、PCから離して、寝室に置いてみた。3DKの鉄筋コンクリートで、リビング-寝室間は-92dBとかなり電波状況が悪い。1秒一回の通信で5000回のテストの間に15回エラーが生じている。まぁ、そんなものか。

以上










XBee+ArduinoとXBee+Macを繋ぐ方法

Xbee APIモードの扱い方
なるべく簡単に、かつ間違いなく、安定してXBee+ArduinoとXBee+Macを繋ぐ方法

必要品は、「XBeeをはじめてみよう(ZB編)」
で用いている物に、Mac (MacBook Air)である。

1. xctuのダウンロードとインストール
「ArduinoUnoとXBeeとMac... Mac版のX-CTUを使って設定しちゃおう」
に記載のあるnew xctu for Mac
をダウンロードし、インストールする。

2. XBeeの設定
XBeeをXBee USB アダプターにセットし、それをMacに接続する。
xctuを起動する。
「Add Device」でusbserialを選んで、XBeeを設定できる状態にする。
最終的にMacに接続するXBeeは、
Coordinator API
に設定する。設定方法は、ファーム更新で選択する。

PAN-IDを好きな4桁の数字にする。これがZigBeeネットワークの識別番号、
相互接続する他のXBeeも同じ数字をPAN-IDに入れる。

AP-API Enable 2にする。これでEscモードになる。

一旦、USBを抜いて、XBeeをArduinoに搭載する側に交換する。
Arduino側のXbeeは、
Router API
に設定する。
PAN-IDは上記と同じ数字。AP-API Enableも同じく2とする。

3. XBeeとArduinoの結合
XBeeをXbeeシールドに載せ、XBeeシールドをArduinoに載せる。
XBeeシールドの小さなスイッチをUSB側にする。
ArduinoをUSBで、Macのもう一つのUSBに接続する。
Arduino-IDE(アプリケーション名はArduino)を起動する。

4. XBee-Arduino
XBee-Arduinoのサイトに行き、
xbee-arduino-0.5.zip(番号は更新されているかもしれない)をダウンロードする。
Arduino-IDEの「スケッチ->ライブラリを使用->Add Library...」でダウンロードしたファイルを選択する。

5. サンプルプログラムをArduinoに転送
以下のサンプルプログラムを、Arduino-IDEで「マイコンボードに書き込む」。
一旦ArduinoをUSBから外し、XBeeシールドの小さなスイッチをMICROに変更する。
再びArduinoをUSBでMacに接続する。

----
#include <XBee.h>

XBee xbee = XBee();

// 送付データは1バイトづつの配列に入れる
uint8_t sendData[] = { 'H', 'L' };

// 送付先のXbeeのアドレス (PCに繋いだCoordinatorのアドレスを入れる。アドレスはXbeeの裏に印刷されている。
XBeeAddress64 remoteAddress = XBeeAddress64(0x0013a200, 0x40b93ccb);

// 送付データをAPIパケットに変換する。詳細な説明は以下:
// http://xbee-arduino.googlecode.com/svn/trunk/docs/api/class_z_b_tx_request.html
ZBTxRequest zbTx = ZBTxRequest(remoteAddress, sendData, sizeof(sendData)); 

// counterは送付データの例delay毎に+1する。
int pin5, counter;

void setup() {
  Serial.begin(9600); // シリアルを9600bpsで使用開始
  xbee.setSerial(Serial); // xbeeが受け持つシリアルを設定
  counter=0;
};

void loop() {
  // pin5を使っているのは、Arduinoのpin入力データを送付するイメージだから。今はダミーのcounterを送っている。
  pin5 = counter++;
  
  // 送付データはHigh, Lowの2つに分けて送付する。この作業の役割に関しては以下の解説が詳しい。
  // http://yoppa.org/tau_bmaw13/4832.html
  sendData[0] = pin5 >> 8 & 0xff;
  sendData[1] = pin5 & 0xff;
  
  // データを含んだパケットを送付
  xbee.send(zbTx);
  
  delay(1000); // ちょっと待つ
};

----

6. Cool Termで確認
Macで、Cool Term
を起動する。(入れていないならダウンロードしてインストールする)
Cool Termで、Arduinoではなく、Xbee+USBアダプターが入っている方のシリアルをオープンする。名前はusbserial-...と言った名称である。
文字列が一定時間おきに、ばらばら出てくるようなら接続成功。
Arduino->XBee---->XBee->Mac
という接続がなされている。

7. Processingでサンプルソフトを起動
新しいスケッチ画面を開く。
xbee-apiをダウンロードする。
展開したフォルダの直下にある xbee-api-0.9.jarと、libフォルダに入っているlog4j.jar, RXTXcomm.jarを新しいスケッチ画面にドラッグアンドドロップする。
もしくは、スケッチのcodeフォルダにコピーする。
スケッチのdataフォルダに、xbee-api-0.9フォルダ直下の、log4j.propertiesをコピーする。
以下のサンプルプログラムをスケッチにコピーする。
Runすると、画面に数字などが表示される。数字が更新されていればProcessingでデータを取得できている。

----
// KYDrill氏のコードを元に作成(オリジナルは以下のURL)
import processing.serial.*;
import java.util.concurrent.*;
import java.util.Queue;
import org.apache.log4j.PropertyConfigurator;

XBee xbee;
Queue<XBeeResponse> queue = new ConcurrentLinkedQueue<XBeeResponse>();
boolean message;
XBeeResponse response;

String senderAddress = "00:13:A2:00:40:B9:3C:CB";
String rxStr = "********";
String txStatus = "";
int rssi = 0;
int rxVar, oldrxVar, rxErr;

void setup()
{
  size(640,150);
  colorMode(RGB,256);
  background(0,0,0);
  rxVar = oldrxVar = rxErr = -1;
  
  try {
    //optional.  set up logging
    //PropertyConfigurator.configure(dataPath("")+"/log4j.properties");
    xbee = new XBee();
    // replace with your COM port
    xbee.open("/dev/tty.usbserial-A5025X1E", 9600); // xbeeを繋いでいるシリアルポートを指定
    xbee.addPacketListener(new PacketListener() { // xbeeのレスポンスを待つパケット監視を設定
      public void processResponse(XBeeResponse response) {
        queue.offer(response);
      }
    }
    );
  } 
  catch (Exception e) {
    System.out.println("XBee failed to initialize");
    e.printStackTrace();
    System.exit(1);
  }
}

void draw(){
  // パケット読み取り
  try {
    readPackets();
  }
  catch (Exception e) {
    e.printStackTrace();
  }
  
  background(255);
  textAlign(LEFT);
  textSize(20);
  
  // 受信強度の表示
  fill(0); text("RSSI", 10, 25); fill(0,0,255);
  text(String.format("%d", rssi) + "dB", 100, 25);
  
  // 受信元アドレスを表示
  fill(0); text("Address", 10, 50); fill(0,0,255);
  text(senderAddress, 100, 50);

  // 受信データの表示  
  fill(0);
  text("Rx Data", 10, 75);
  fill(0,0,255);
  //text(rxStr, 100, 75);
  text(rxVar, 200, 75);
  text("ERR:"+rxErr, 300, 75);
  
  // 受信状態の表示
  fill(255,0,0);
  text(txStatus, 10, 100);
}

// パケットの読み込みとエコー、ATDBの送信
void readPackets() throws Exception {

  while ((response = queue.poll()) != null) {
    // we got something!
    try {
      if (response.getApiId() == ApiId.ZNET_RX_RESPONSE && !response.isError()) {
        ZNetRxResponse rxResponse = (ZNetRxResponse) response;
        
        // アドレスをintで取得
        int[] addressArray = rxResponse.getRemoteAddress64().getAddress();
        String[] hexAddress = new String[addressArray.length];
        // 16進数のStringへ
        for(int i=0; i<addressArray.length;i++){
          hexAddress[i] = String.format("%02x",addressArray[i]);
        }
        // 送信元アドレスを文字列で保存
        senderAddress = join(hexAddress,":");
        
        //受信データを取得
        int[] rxData = rxResponse.getData();
        rxStr = "";
        for(int i=0; i<rxData.length; i++){
          rxStr += String.format("%c", rxData[i]);
        }
        // Arduinoのプログラムと対応して、2バイトのintデータを前提にしている。
        rxVar = rxData[0] << 8 | rxData[1];
        if (rxVar != oldrxVar + 1) rxErr++;
        oldrxVar = rxVar;
        
        // エコー (送られてきたデータを、そのまま返信)
        int[] payload = rxData;
        XBeeAddress64 addr64 = new XBeeAddress64(addressArray); // データの送信元アドレスを設定
        ZNetTxRequest request = new ZNetTxRequest(addr64, payload); // 送られてきたデータをAPIパケットに変換
        try {
          // データ送信
          ZNetTxStatusResponse response = (ZNetTxStatusResponse) xbee.sendSynchronous(request);
          txStatus = "";
        } catch (Exception e) {
          txStatus = "tx request timed out";
        }
        
        // ATコマンドを送信し、RSSIを取得
        AtCommand  at = new AtCommand("DB"); // ATDBで、RSSIを取得し、電波強度を調査する。
        AtCommandResponse response = (AtCommandResponse) xbee.sendSynchronous(at, 5000);
        if (response.isOk()) {
          // 成功
          rssi = response.getValue()[0];
          rssi = rssi * (-1);
        }
      }
    }
    catch (ClassCastException e) {
      // not an IO Sample
    }
  }
}



----

以上