INDEX(各項目ごとの目次)

[HOME]  [Processing関係]  [Arduino関係]  [マテリアル関係]  [秋葉原工作マップ]

2008年用ですが、部分的に内容を更新しています(2010/06/14)。
また、[建築農業工作ゼミ2009-2010]とも連動していますので、そちらにも幾つかサンプルがあります。
:

10/18/2008

Arduino-Processing シリアル通信6


【変更】以下はArduino1.0まで対応したプログラム内容です。
特にシリアル通信においては、Arduino2.0使用の際、バイト送信する場合、
Serial.print(value,BYTE);
のかわりに、
Serial.write(value);
を使用してください。


これまでのシリアル通信では、ProcessingとArduinoの一対一の通信を行ってきましたが、今回はProcessingで二つのシリアルポートを使い、二つのArduinoとシリアル通信を行う実験をしてみます。それぞれのArduino基盤には可変抵抗器をとりつけて入力値をProcessingへ別々に送信することにします。Processingの画面では、二つの入力信号を個別に読み取ってそれぞれの状態を描画することにします。

具体的なサンプルとして、「Pong」(下画像)のように二つのコントローラによって対戦するプログラムにします。それぞれのArduino基盤がコントローラとしてコンピュータに接続され、画面上でそれぞれのラケットを動かすことになります(得点のプログラムは含まれていません)。


(上画像:Processingの画面「Pong」)

Arduino基盤と可変抵抗器の接続は以下のようになります(二つ必要です)。



Arduinoのプログラム:
void setup(){
  Serial.begin(9600);
}

void loop(){
  //可変抵抗器の読み取り
  int val=analogRead(0);
  //シリアル通信処理
  if(Serial.available()>0){//合図用データが一つ来たら
    //合図用データを読み込んでバッファを空にする
    Serial.read();
    //読取値を4で割り、バイトで送信
    Serial.print(val/4,BYTE);
  }
}

Arduino側のプログラムでは、可変抵抗器からの読取り値を4で割ってスケールダウンした値(0~255)をシリアル通信でProcessing側へ送信しています(同期通信させるために合図用データを用いるシリアル通信の方法については「Arduino-Processing シリアル通信2」を参照して下さい)。二つのArduino基盤とも同じ内容になります。

Processingのプログラム:
//シリアル通信ライブラリを取り込む
import processing.serial.*;
//二つのポートのインスタンス
Serial portA;
Serial portB;
//二つの読取値の変数
int valA=100,valB=100;
//ボ−ル座標用変数
int x=100,y=100;
//ボールの動きの向きの変数(1:正の向き、-1:負の向き)
int dirX=1,dirY=1;

void setup(){
  //画面サイズ設定
  size(300,256);
  //二つのシリアルポート設定
  portA = new Serial(this, "/dev/tty.usbserial-A50019vD", 9600);
  portB = new Serial(this, "/dev/tty.usbserial-A40014iU", 9600);
  //図形外形線なし
  noStroke();
  //塗り色(白)
  fill(255);
  //矩形描画位置を中央に設定
  rectMode(CENTER);
}

void draw(){
  //背景(黒)
  background(0);
  //左ラケット描画(valAをY座標に代入)
  rect(20,valA,10,30);
  //右ラケット描画(valBをY座標に代入)
  rect(280,valB,10,30);
  //ボール描画
  rect(x,y,10,10);

  //ボールX座標の動き
  x+=dirX;//X軸方向に+1または-1ずつ進める

  //ラケットAに当たった時のはね返り
  if(x==30 && y>valA-15 && y<valA+15){
    dirX*=-1;//向きを反転する
  }
  //ラケットBに当たった時のはね返り
  if(x==270 && y>valB-15 && y<valB+15){
    dirX*=-1;//向きを反転する
  }
  //画面左端からはみ出た場合
  if(x<0){
    x=270;//右側に戻る
  }
  //画面右端からはみ出た場合
  if(x>width){
    x=30;//画面左側に戻る
  }

  //ボールY座標の動き
  y+=dirY;//Y軸方向に+1または-1ずつ進める

  //画面上下位置でのはね返り
  if(y<5 || y>251){
    dirY*=-1;//向きを反転する
  }  
}

//キーを押した場合
void keyPressed(){
  //「s」キーでシリアル通信開始
  if(key=='s'){
    //二つのポートへ開始用データ送信
    portA.write(65);
    portB.write(65);
  }
}

//シリアル通信処理
void serialEvent(Serial p){
  //portAの場合
  if(p==portA){
    if(p.available()>0){
      //値を読み込みvalAに代入
      valA=p.read();
      //合図用データ送信
      portA.write(65);
    }
  }
  //portBの場合
  if(p==portB){
    if(p.available()>0){
      //値を読み込みvalBに代入
      valB=p.read();
      //合図用データ送信
      portB.write(65);
    }
  }
}

Processing側のプログラムでは、二つのシリアルポートを用意し、それぞれportA、portB(名前は任意)にしておきます。serialEvent(Serial p){...}の括弧内のpは、ポート名に対応しています(今回の場合は、pはportAまたはportBに対応します)。serialEvent()は、Processingがデータを受信した際に作動するので、if()文を使ってどちらのポートなのかを条件分けして判別し、ポートに応じてそれぞれの読込み値を変数に代入します。同期通信させるために、Arduinoから送信されたデータをp.available()で確認しデータを読み込んだ後に、合図用データ(0~255の数値あるいは'A'や'a'などの一つの文字/1バイト分のデータ)を送信しています。
Processingのプログラムを開始したら、「s」キーを押すことでシリアル通信を開始することにしました(プログラム開始から数秒経った後に「s」キーを押さないと、シリアル通信が開始されないことがあります)。

関連:
Arduino-Processing シリアル通信1」(一つの値を送る/非同期通信)
Arduino-Processing シリアル通信2」(複数の値をバイトで送る/同期通信)
Arduino-Processing シリアル通信3」(大きな値を複数送る)
Processing-Arduino シリアル通信4」(ProcessingからArduinoを制御する)
Arduino-Processing シリアル通信5」(複数の値を文字列で送信する)

4 件のコメント:

匿名 さんのコメント...

このゲームを人対COMでやってみたいのですが、どこを変更すればよいのですか?

kousaku さんのコメント...

左プレーヤーがAでコンピュータ、右プレーヤーがBで人間だとすれば:
void draw(){...};内4行目の

rect(20,valA,10,30);

を、

valA=y;
rect(20,valA,10,30);

に変更することで、プレーヤーA(コンピュータ)は常にボールのY座標値(縦の位置)を追いかけるプログラムになります。この部分は左プレーヤーのラケットの大きさと位置を描画するプログラムなので、そのY座標値にボールのY座標値の変数であるyを代入します。
ただし、このままだとコンピュータは決してミスしてくれないので、コンピュータがたまにミスするように、random()を使ってコンピュータとボールのY座標値に多少のズレをつくるなどします。random(30)と書けば、0~30までの数値をランダムに選んでくれます。
まずランダム用の変数rdmとズレの変数diffを用意しておきます。

int rdm=0;
int diff=0;

先ほどのコンピュータ用のラケットのプログラム部分を、

diff+=(rdm-diff)/4;
valA=y+diff;
rect(20,valA,10,30);

に書き換えて、
ボールが壁に反射したときにだけrdm変数が変わるようにしておきます。void draw(){...}内最後の方のボールの「画面上下位置でのはね返り」の以下のプログラム部分、

if(y<5 || y>251){
dirY*=-1;//向きを反転する
}

にランダム変数のプログラムを付け足して以下のようにします。

if(y<5 || y>251){
dirY*=-1;//向きを反転する
rdm=int(random(30));
}

そのほか、左プレーヤーのシリアル通信部分のプログラムは消去してください。

Kyomesuke3 さんのコメント...

両手に持ったArduino二つをProcessingで認識したかったので大変参考になりました!

ところで、シリアル通信の開始はできるのですが、アプリを正常に停止することができないのですが、どうしたらよいでしょうか?
基本的に今は強制終了しています。

kousaku さんのコメント...

void keyPressed(){...}内に、
if(key=='z'){
portA.stop();
portB.stop();
}
を入れてみてはどうでしょうか?
実際、動作確認はしていませんが、
zキーを押して通信終了にすれば、閉じることができるかもしれません。



[目次:Processing関係]  [HOMEへ戻る]  [目次:Arduino関係]