INDEX(各項目ごとの目次)

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

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

6/12/2008

Arduino-Processing シリアル通信5


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


以前行ったシリアル通信では、ArduinoのanalogRead()で読み取った値(0〜1023までの値)を0〜255(8ビット)にスケールダウン(4で割る)して送信するか、256以上の大きな値を、二つの0〜255(8ビット)の数値に分解して送っていました。今回は、たとえば「1023」という255より大きい値を、そのままの「1023」という値で送信したいと思います。そのためには、読み取った整数値(int型)を文字列として送信します。
今回は3つの可変抵抗器を読み取って(接続方法は「Arduino-Processing シリアル通信2」を参照)、Arduinoから3つの値をまとめて送信します。複数の値を送る際には数値と数値の間にデリミターという区切りの記号(今回の場合「,」コンマ)を挿入して送信します。そうすることによって、Processingでデータを受け取る際に、データ内容を混同せず読み取ることができます。最初の二つの読み取り値は、Serial.print()を使ってDEC(十進数文字列)のフォーマットで送信し、区切り記号のコンマもSerial.print()で文字列として送信します。最後の読み取り値を送る時に、DECフォーマットでSerial.println()を用い「改行」して送信します。改行することで、Processing側でデータを受け取る際に、送られて来たデータのどの部分が最後であるのかを確認することが可能になります。それでは、Arduinoのプログラムから始めます。

Arduinoのプログラム:
void setup(){
  //シリアル通信開始
  Serial.begin(9600);
}

void loop(){
  //3つのセンサの値を読み取り、変数に代入
  int x=analogRead(0);
  int y=analogRead(1);
  int z=analogRead(2);

  //合図用データが一個きたら
  if(Serial.available()>0){
    //xの値を十進数文字列で送信
    Serial.print(x,DEC);
    //区切り記号コンマを送信
    Serial.print(",");
    //yの値を十進数文字列で送信
    Serial.print(y,DEC);
    //区切り記号コンマを送信
    Serial.print(",");
    //zの値を十進数文字列かつ改行して送信
    Serial.println(z,DEC);
    //合図用データを読み込みバッファを空にする
    Serial.read();
  }
}


「xの値 コンマ yの値 コンマ zの値 改行」というデータが一度に送られることになります。Serial.print(value,DEC)の「DECフォーマット」の数値は文字列であり、以前使ったSerial.print(value,BYTE)の「BYTEフォーマット」の数値と異なる値になります。文字列の「1」は、BYTEフォーマットでは、「49」に相当します。十進数文字列とバイトの数値の対応は以下のようになります(ちなみに、BYTEフォーマットの「65」は文字列の「A」になります)。

DEC: BYTE:
 0  48
 1  49
 2  50
 3  51
 4  52
 5  53
 6  54
 7  55
 8  56
 9  57

アスキーコード表」にこれらの対応関係が掲載されています。
たとえば、「120」という値の場合、BYTEフォーマットならそのまま「120」となりますが、DECフォーマットでは「49 50 48」というように「1」「2」「0」という3つの文字を送ることになります。DECフォーマットでは、1桁の数値なら1バイト分のデータであり、2桁なら文字二つを送るので2バイト、3桁なら3バイト必要になります。BYTEフォーマットは、255までの数値であれば1バイトで済みますが、それ以上の数値は「Arduino-Processing シリアル通信3」で行ったように、分解して送るなどの工夫が必要となります。
DECフォーマットで、そのままの値を文字列として送信した方が分かりやすいのですが、その分バイト数が増えてしまうことにもつながります。BYTEフォーマットであれば少ないバイト数で送ることができますが、大きな数値を分解して計算し直さなければいけないので、十進数の数値として扱いづらくなります。状況に応じて使い分けるのがいいと思います。

次に、Processing側のプログラムに入る前に、どのようなかたちでデータを受け取るかということについて説明します。

例えば、3つの可変抵抗器から読み取られる値が、

x=120
y=284
z=1015

の場合、Arduinoからは、

「120 コンマ 284 コンマ 1015 改行」

というデータが送られてきます。
「コンマ」は「アスキーコード表」では「44」であり、「改行」記号は「アスキーコード表」の「13」と「10」がデータの最後に付け加えられることになります。
「13」は「キャリッジリターン(行頭に戻る)」ということであり、文字列では「\r」になります。
「10」は 「ラインフィード(次の行へ移る)」ということであり、文字列では「\n」になります。
Windowsでは、キャリッジリターンとラインフィードで改行となり、Macintoshでは、キャリッジリターンのみで改行されるので、この二つがあることで、いずれにせよ改行されることになります。

先ほどの、

「120 コンマ 284 コンマ 1015 改行」

というデータは、

"120" + "," + "284" + "," + "1015" + "\r" + "\n" 

という文字列データになります。
コンマや改行記号を手掛かりにすれば、データの順番や終わりの部分をProcessing側で判別して読み込むことができます。それでは、このようなことを踏まえてProcessingのプログラムをしてみたいと思います。PFontを用いて、数値を文字で画面に表示することにします。マウスを押したら通信開始することにします(プログラムが開始して数秒たってからマウスを押さないと反応しないときがあります)。

Processingのプログラム:
//シリアルライブラリを取り込む
import processing.serial.*;
//シリアル通信用変数portを用意
Serial port;

//フォント用変数fontを用意
PFont font;

//読み込み値の変数を用意
int x,y,z;

void setup(){
  //画面サイズ設定
  size(400,200);

  //フォントをロードする
  font = loadFont("Monaco-10.vlw");
  //フォント使用開始:サイズ10
  textFont(font, 10);
  //文字を右寄りに配置する
  textAlign(RIGHT);

  //シリアルポート設定
  port = new Serial(this,"/dev/tty.usbserial-A50019vD",9600);
  //念のためバッファを空にする
  port.clear();
  //「10」(ラインフィード)が来る度に
  //serialEvent()を発動させる
  port.bufferUntil(10);  
}

void draw(){
  //背景を白で塗りつぶす
  background(255);
  //3つの値を文字で表示する
  text(x,100,50);
  text(y,200,50);
  text(z,300,50);
}

//シリアル通信
void serialEvent(Serial p){
  //文字列の変数stringDataを用意し、
  //「10」(ラインフィード)が来るまで読み込む
  String stringData=port.readStringUntil(10);

  //文字列データが空ではないとき
  if(stringData!=null){
    //文字列データに含まれる改行記号を取り除く
    stringData=trim(stringData);

    //整数型の配列data[]を用意し、
    //コンマ記号をもとに文字列データを区切って
    //配列data[]に整数化して入れておく
    int data[]=int(split(stringData,','));

    //配列data[]内のデータが3つなら、
    if(data.length==3){
      //最初のデータをxに代入
      x=data[0];
      //次のデータをyに代入
      y=data[1];
      //その次のデータをzに代入
      z=data[2];

      //合図用データ送信
      port.write(65);
    }
  }  
}

//マウスが押されたら通信開始
void mousePressed(){
  //開始用データ送信
  port.write(65);
}

Processing上のシリアル通信では、まず初期設定setup(){...}内で、bufferUntil()を使って、指定した文字がArduinoから送られて来るたびにserialEvent()が作動するように設定しておきます。Arduinoから最後に送られてくるSerial.println()によって、データの末尾が改行記号の「\n」であることから、今回はbufferUntil()の括弧内には「10」を入れておきます。整数値「10」は文字列の改行記号の「\n」(ラインフィード)に相当します。
そして、serialEvent(){...}内では、readStringUntil()を用いて、同様に「10」つまり「\n」が来るまでデータを読み込む設定にします。読み込まれたデータは、3つの値以外にも「コンマ」や「改行」記号が含まれた連続した文字列なので、その文字列の内容を整理し直す必要があります。
「stringData!=null」は、読み込まれたデータが空(null)ではないとき、つまり何かしらのデータがあるときという条件です。データがあれば、その文字列データに含まれている余分な空白記号や改行記号をtrim()によって取り除きます。
その後、それぞれの値の区切り記号(デリミター)として用いた「,」コンマをもとに、連続したひとつのデータをsplit()で分解します。split()によって分解されたデータは、複数のデータを内包する配列に変換されます。さらに、分解されたデータは、まだ文字列なので、int()を用いて整数値へ変換します。そのためにdata[]という配列を用意し、「int data[]=int(split(stringData,','))」の中で、この一連の変換作業を行っています。配列については、「Arduino 7セグLEDの点灯」の後半でも触れていますので、参照してください。
if(data.length==3){...}は、配列data[]内のデータ数が3つあるときにという条件です。length配列の大きさ(データを何個含んでいるか)を数えます。データ数が3つあることを確認してから、配列data[]に含まれる一つ目の値「data[0]」をxに代入します(配列では、最初のデータは0番目となります)。同様にyとzについても代入します。最後に合図用データを一つ送信します(65以外の数値でも大丈夫です)。合図用データをArduinoへ送信すれば、Arduinoは再び新たなデータを送り返してきます。

連続した文字列データを個別の数値に変換する手続きを以下にもう一度書きます。
Arduinoで読み取った3つの値を、

x=120
y=284
z=1015

とすれば、
Arduinoからは、

"120" + "," + "284" + "," + "1015" + "\r" + "\n" 

という順番で文字列として送信されます。
Processingでは、port.readStringUntil(10)で括弧内の「10」つまり「\n」までを、

"120,284,1015\r\n" 

という連続したデータとして読み込みます(「\r\n」は改行記号)。合計14個の文字があるので14バイトになります。「\r」と「\n」はそれぞれ1バイトずつとなります。 
trim()で「改行」記号を削除すると、

"120,284,1015"

になります。
split()で「,」をもとに分解すると、

{"120","284","1015"}

という、3つの文字列を含んだ配列のデータに変換されます。
さらに、これら3つの文字列をint()で整数の数値に変換すると、

{120,284,1015}

になり、予め用意しておいた整数型の配列data[]に入れます。

int data[]={120,284,1015}

そして、「data.length」によって配列data[]のデータ数が3個であるかを確認し、これらの値(整数値)を順にx、y、zへ入れます。

x=data[0]
y=data[1]
z=data[2]

この手順を踏んで、連続した文字データを個別の数値として扱うことができます。


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

0 件のコメント:



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