勉強会用

processingやopenframeworksで自分が作っていったものの過程など

xBee (APIモード) ×processing×arduino シリアル通信で。

 

xBeeで1対複数の通信をするためにまず1対1のAPIでの無線通信を試みた。
ATモードについては以前挙げた記事を見れば分かると思う。

APIモードについては以下の記事がよい。
http://airshipnewsjapan.blogspot.jp/2014/07/xbeearduinoxbeemac.html

ただ、この記事にある通りやってもprocessing側のコードが動かなかった(ライブラリーがなぜかうまく動かなかった)
のと
自分が行いたいのはコーディネーター側からルーター側への通信、つまり1対複数でいう親から複数の子機への通信。
上のURLを含めよく記事になっているのはセンサーの値をコーディネーター側へ送るものばかりであったので、自分で作ってみた。

忘備録ということでその方法を書いていく。

■作るもの
processingのスケッチで4分割された部分のどこかをクリックすると対応されたLEDが光るというものを作る。

■流れ
processing -> xBee(coordinator) -> xBee(router) -> arduino
serial通信を用いる。

xBeeの設定
http://airshipnewsjapan.blogspot.jp/2014/07/xbeearduinoxbeemac.html
の記事と同じ。
xBee(Coordinator API) AP-API Enable 2に設定。
xBee(Router API) AP-API Enable 2に設定。
同じPAN IDを設定

■processing側のコード
後々自分はこれをラジコンのように扱うつもりであったので
送るデータが
・data1
・data2
・data3
になっているが気にしない。この場合使うのは一つのデータだけ。
dataを増やしたいor減らしたい場合はchecksumの計算のときの合計値の部分とFrame lengthを変える。あとこのコードの場合は関数の引数とかも変えないといけない。

またxBeeルーター側の64ビットアドレスを記す部分があるのでtoSerialNumの部分を対応するものに変える。
portNameも変える。
/**/でくくっているところを消すと複数台の制御が出来る。その場合もそれぞれ対応するxBeeの64ビットアドレスを書き換える。

import processing.serial.*;

Serial myPort;

//ここを変える。
int toSerialNum1 = {0x00, 0x13, 0xA2, 0x00, 0x??, 0x??, 0x??, 0x??};
int SerNum1Sum = 0;
/*int
toSerialNum2 = {0x00, 0x13, 0xA2, 0x00, 0x??, 0x??, 0x??, 0x??};
int SerNum2Sum = 0;
int toSerialNum3 = {0x00, 0x13, 0xA2, 0x00, 0x??, 0x??, 0x??, 0x??};
int SerNum3Sum = 0;
int
toSerialNum4 = {0x00, 0x13, 0xA2, 0x00, 0x??, 0x??, 0x??, 0x??};
int SerNum4Sum = 0;*/

void setup()
{
size(500, 500);
String portName = Serial.list()[0];
println(portName);
//ここも変える。
myPort = new Serial(this, "/dev/tty.usbserial-????????", 9600);

for(int i = 0; i < toSerialNum1.length; i++){
SerNum1Sum += toSerialNum1[i];
}
/*for(int i = 0; i < toSerialNum2.length; i++){
SerNum2Sum += toSerialNum2[i];
}
for(int i = 0; i < toSerialNum3.length; i++){
SerNum3Sum += toSerialNum3[i];
}
for(int i = 0; i < toSerialNum4.length; i++){
SerNum4Sum += toSerialNum4[i];
}*/

}

void draw()
{
background(255);
fill(255,255,0,100);
rect(0,0,width/4,height);
fill(255,0,255,100);
rect(width/4,0,width/4,height);
fill(0,0,255,100);
rect(width/2,0,width/4,height);
fill(0,255,255,100);
rect(width-width/4,0,width/4,height);
line(width/4,0,width/4,height);
line(width/2,0,width/2,height);
line(width-width/4,0,width-width/4,height);
}

void mousePressed(){
if(mouseX<width/4){
setRemoteState(toSerialNum1, SerNum1Sum, 1, 255, 255);
/*setRemoteState(toSerialNum2, SerNum2Sum, 1, 255, 255);
setRemoteState(toSerialNum3, SerNum3Sum, 1, 255, 255);
setRemoteState(toSerialNum4, SerNum4Sum, 1, 255, 255);*/
fill(255,255,0);
rect(0,0,width/4,height);
println("mode=1");
}else if(mouseX<width/2){
setRemoteState(toSerialNum1, SerNum1Sum, 2, 255, 255);
/*setRemoteState(toSerialNum2, SerNum2Sum, 2, 255, 255);
setRemoteState(toSerialNum3, SerNum3Sum, 2, 255, 255);
setRemoteState(toSerialNum4, SerNum4Sum, 2, 255, 255);*/
fill(255,0,255);
rect(width/4,0,width/4,height);
println("mode=2");
}else if(mouseX<width-width/4){
setRemoteState(toSerialNum1, SerNum1Sum, 3, 255, 255);
/*setRemoteState(toSerialNum2, SerNum2Sum, 3, 255, 255);
setRemoteState(toSerialNum3, SerNum3Sum, 3, 255, 255);
setRemoteState(toSerialNum4, SerNum4Sum, 3, 255, 255);*/
fill(0,0,255);
rect(width/2,0,width/4,height);
println("mode=3");
}else{
setRemoteState(toSerialNum1, SerNum1Sum, 4, 255, 255);
/*setRemoteState(toSerialNum2, SerNum2Sum, 4, 255, 255);
setRemoteState(toSerialNum3, SerNum3Sum, 4, 255, 255);
setRemoteState(toSerialNum4, SerNum4Sum, 4, 255, 255);*/
fill(0,255,255);
rect(width-width/4,0,width/4,height);
println("mode=4");
}
}


void setRemoteState(int[] toSer,int serSum, int data1, int data2,int data3){
//start byte
myPort.write(0x7E);
//frame length
myPort.write(0x00);
myPort.write(0x11);
//frame type
myPort.write(0x10);
//frame ID
myPort.write(0x01);


//64bit address
//Bload cast 0x000000000000FFFF
myPort.write(toSer[0]);
// esc mode(API=2): は0x13 -> 0x7D, 0x33に変わるらしい
myPort.write(toSer[1]);
myPort.write(toSer[2]);
myPort.write(toSer[3]);
//router ID
myPort.write(toSer[4]);
myPort.write(toSer[5]);
myPort.write(toSer[6]);
myPort.write(toSer[7]);

//16bit address
//bload cast 0xFFFE
myPort.write(0xFF);
myPort.write(0xFE);

//set maximum hop num
myPort.write(0x00);

//option
myPort.write(0x00);

// RFdata1
if(data1 == 1){
myPort.write(0x01);
}else if(data1 == 2){
myPort.write(0x02);
}else if(data1 == 3){
myPort.write(0x03);
}else if(data1 == 4){
myPort.write(0x04);
}else{
myPort.write(0x00);
}

// RFdata2
myPort.write(byte(data2));

// RFdata3
myPort.write(byte(data3));


long sum = 0x10 + 0x01 + serSum + 0xFF + 0xFE + data1 + data2 + data3;
long sum2 = 0xFF - (sum & 0xFF);
myPort.write(byte(sum2));

delay(10);
}

■arduino側
arduinoの2,3,4,5ピンのLEDが光るようにする。(1ピンはTXに使っていた。)
そのためそれぞれLEDを繋いでおく。
また通信されているかの確認のため13番ピンを使う。
arduinoに書き込むときはxBeeシールドを外すか、xBeeシールドのSERIAL SELECTをMICROではなくUSB側にスイッチを切り替えてから書き込む。
xBEEで通信するときはMICRO側に戻す。

int debugLED = 13;

void setup(){
 pinMode(debugLED, OUTPUT);
 pinMode(2, OUTPUT);
 pinMode(3, OUTPUT);
 pinMode(4, OUTPUT);
 pinMode(5, OUTPUT);
 Serial.begin(9600);
}

void loop() {
if(Serial.available() > 19){  if(Serial.read() == 0x7E){
   digitalWrite(debugLED, HIGH);
   delay(10);
   digitalWrite(debugLED, LOW);
   for(int i = 0; i < 15; i++){
   byte discard = Serial.read();
   }
    int mode = Serial.read();
   if(mode<5 && mode>0){
     digitalWrite(mode+1, HIGH);
     delay(10);
   digitalWrite(mode+1, LOW);
   }
   int spd = Serial.read();
   int fan = Serial.read();
   Serial.print(mode);
 }}}

■やり方
arduinoのコードをarduinoへ書き込んだらxBeeシールド,xBeeを取り付ける。
その後シールドはMICRO側へスイッチを切っておく。
またarduinoに電源を取り付ける。USB供給でも良い。

その後processingで先ほどのコードをRUNする。
もしポートがbusyだよ、と言われたらx-ctuのポート検索をしてみる。それでもしxBeeが見つかったらもう一度processingをRUNしてみるとよい。
x-ctuで見つからなかったら一度USBを抜いてみる。

スケッチが開けたら、どこか押してみる。それに対応してLEDも光るはず。

■説明
APIモードだと2byteづつ送って通信するみたい。またそのプロトコルもちゃんとあるから確認しておくと良い。
今回はフレームタイプ0x10を用いた。
送るデータは
7E 00 11 10 01 00 13 A2 00 40 C1 C0 DD FF FE 00 00 Mode speed FanStr checksum
という感じ。Modeは(0x01~0x04)で今回は意味ないデータだがspeed(0x00~0xFF)、FanStr(0x00~0xFF) である。
(checksum)=FF-(はじめの3つを除いた数値の合計の下2桁(16進数))である。
ちゃんとデータが壊れていないか確かめるためのもの。

7E -> 開始デリミタ (開始の合図)
00 11->フレーム長 フレーム長からチェックサムに挟まれたbyte数
10 -> フレームタイプ
01 -> フレームID ACK(確認応答)と関連づけるためのID。いつ送ったモノかが分かる
00 13 A2 40 C1 C0 DD -> 宛先64bitアドレス
FF FE-> 16bit宛先アドレス。これはブロードキャスト
00 -> ブロードキャスト半径 00だと最大に設定
00 -> option 特に設定しない。
Mode  speed FanStr->
Modeが01だと2pin, 02だと3pin 03だと4pin 04だと5pinがHIGHになりLEDが光る。後の二つは冗長。
checksum -> チェックサム フレームタイプ10からこの一つ前までの8ビット和をFFからひいたもの。

大体この感じ。ちゃんと意味を知りたい場合は、「xbeeで作るワイヤレスセンサーネットワーク」の5章を読むと良い。arudinoのコードの意味もココに書いてあるものがほとんど。

補足
もし送るデータ(7E 00 …とかそういうもの)が具体的にどういうものか分からない場合はX-ctuのモニターマークを選択し下の部分の+マークで作る事が出来る。+を押した後、create frame using “Frame generator” tool…を選択。そして上のframe typeを希望の項目に変える。今回は0x10を選択。そして値を入れていけば簡単に送りたいものを作る事が出来る。ただEnable APIを2にしているためescモードになっている。そのため0x13が別の意味をもっているため0x7D 0x33に変わったりする。 0x7Dがescのもの。まぁ気にしなくてもprocessingからは0x13で送れる。0x13を0x7D 0x33に変えても送れる。ただチェックサムは0x13の方で計算する。
okをおしてaddFrameをおして作り終えたら、左上にあるプラグ?みたいなのをおして繋がる状態にする。その前に左上(プラグよりも)cordinatorの×の下の青い○を選択してルータを探しておいた方が良いかも。そしてスタートボタン見たいなsend select frameをおすとルーターにデータを送れる。これでもLEDを光らせる事は出来ると思うのでprocessingがうまくいかなかったらこっちで作っても良いかもしれない

以上!