目次

ProcessingとArduino間情報交換

 ProcessingとArduinoは、シリアルインタフェースで接続します。



 シリアルインタフェースでのデータ交換ができるように
 送信、受信のインタフェースを用意します。

 Processingスケッチが動作しているマシンがWindowsであれば
 通信インタフェースは、デバイス名COM1からはじまる送受信の
 どれかのインタフェースになります。

 COMのどこにArduinoが接続されているのかを知るため
  Serial.list()
 を利用します。

 Serialは、Processingではビルトインのクラスなので
 定義しないで使えます。実際にどのCOMポートが使える
 のかを表示するには、printlnでリストを出しておき
 ます。

  println(Serial.list());

 上の処理では、使えるCOMポートの一覧が出てくるので
 どこにArduinoが接続されているのかは、ArduinoIDEで
 確認します。
 自分の環境では、次のリストが表示されました。

   COM1 COM3 COM5 COM15 COM17

 COM15、COM17はUSB/シリアルのアダプタで接続した
 機器になっています。
 COM17が、Arduinoへと接続されているので、Serial.list()
 を配列としてみれば、添字は4。

 Processingスケッチでの認識には、クラスインタンスに
 してからと考えます。

  // include serial class library
  import processing.serial.*;

  // declare class library
  Serial cport;

  // assign class instance
  String arduinoPort = Serial.list()[4];

  // initialize serial port
  cport = new Serial(this,arduinoPort,9600);
  cport.clear();
  cport.bufferUntil('\n');

 COMx(x=1,2,..)はWindowsの場合で、Unix系のOSでは
 「/dev/...」のようにデバイスファイルを利用。

 情報交換には、ProcessingとArduinoの双方のスケッチに
 コマンドインタプリタを用意して対応するのが簡単。



 コマンドインタプリタは、受信内容を解析後、対応した
 処理を実行しますが、受信割込みを使うと効率よく動作
 を記述できます。

 コマンドインタプリタを使うには、プロトコルを規定して
 齟齬がないようにします。

 自分が実践しているプロトコル規定は、以下。

 上の規定から、コマンドインタプリタの基本形態は以下。

  /* command interpreter */
  if ( uflag == ON ) {
    /* clear flag */
    uflag = OFF ;
    /* new line */
    crlf() ;
    /* command */
    cmd = sbuf[0] ;
    /* help */
    if ( cmd == '?' ) { show_help() ; }
    /* command A */
    if ( cmd == 'A' ) {
      /* ??? */
    }
    /* command B */
    if ( cmd == 'B' ) {
      /* ??? */
    }
    /* command C */
    if ( cmd == 'C' ) {
      /* ??? */
    }
  }

 コマンドインタプリタを入れるブロックは、Processingでは
 draw()、Arduinoではloop()とします。

 Processing側は、コマンドを送信し、受信情報から
 図形を描画するコマンドインタプリタを書くことに。

 コマンド送信には、マウスを利用します。

 マウスカーソルを矩形領域に持っていき、左クリックで
 コマンドを送信します。

 下のような画面をイメージするとわかりやすいでしょう。



 ボタン操作を担当する矩形領域表示は、アイコンを作成し
 イメージとして入力後、貼り付ける操作で対応。

 アイコンとなる画像フォーマットは、JPEG、GIF、PNGの
 いずれかの形式で作成しておけばよいでしょう。

 静的画像入力は関数setup、貼付けは関数drawで。

PImage imgGet;
PImage imgExit;

void setup()
{
  /* main window */
  size(320, 240);
  /* button get color image */
  imgGet = loadImage("gbtn.png");
  /* button Exit image */
  imgExit = loadImage("ebtn.png");
}

void draw() {
  image(imgGet,20,20);
  image(imgExit,200,200);
}


 画像ファイルは、スケッチを入れたディレクトリの
 サブディレクトリ「data」に格納しておきます。

 コマンド送信は、マウスの左ボタンクリックで担当する
 ので、カーソル位置での判定を入れて対応します。

    int BX = 20 ;
    int BY = 20 ;
    int BW = 60 ;
    int BH = 20 ;

    boolean isRangeOk(int x,int bx,int ex)
    {
      boolean result ;
      /* default */
      result = false ;
      /* judge */
      if ( bx <= x && x <= ex ) { result = true ; }

      return result ;
    }

  void mouseClicked()
    {
      /* LEFT button */
      if ( mouseButton == LEFT ) {
        /* get color button */
        if ( isRangeOk(mouseX,BX,BX+BW) &&
             isRangeOk(mouseY,BY,BY+BH)     ) {
          cport.write('G');
          cport.write('\r');
        }
        /* Exit button */
        if ( isRangeOk(mouseX,BX+180,BX+BW+180) &&
             isRangeOk(mouseY,BY+180,BY+BH+180)    ) {
          exit();
        }
      }
    }

 コマンドの送信ができれば、シリアルインタフェースを
 利用して情報を受信します。

 1レコード分のデータが揃うまで受信バッファに
 情報を保存していきます。1レコードが揃った時
 関数drawへ、フラグで通知します。

       byte[]  sbuf ;
       byte    sindex ;
       boolean uflag ;

       void serialEvent(Serial p)
       {
         byte ch ;

         if ( cport.available() > 0 ) {
           ch = (byte)cport.read();
           /* judge */
           if ( ch == '\r' ) {
             uflag = true ;
             sindex = 0 ;
           }
           /* store */
           sbuf[sindex] = ch ;
           /* update */
           sindex++ ;
         }
       }

 受信情報をどう扱うのかは、関数drawで決めます。

    int XDIV = 40 ;

    int xr ;
    int xg ;
    int xb ;

    int xwr ;
    int xwg ;
    int xwb ;

    boolean dflag ;

    byte get_hex(byte x)
    {
      byte result ;
      /* default */
      result = 0 ;
      /* convert */
      if ( '0' <= x && x <= '9' ) { result = x - '0' ; }
      if ( 'A' <= x && x <= 'F' ) { result = x - 'A' + 10 ; }
      if ( 'a' <= x && x <= 'f' ) { result = x - 'a' + 10 ; }

      return result ;
    }

    void draw()
    {
      /* draw button */
      image(imgGet,20,20);
      image(imgExit,200,200);
      /* data handling */
      if ( uflag == true ) {
        /* clear flag */
        uflag = false ;
        /* get color values */
        xr = 0 ;
        xg = 0 ;
        xb = 0 ;
        for ( int i = 1 ; i < 5 ; i++ ) {
          /* multiply */
          xr *= 10 ;
          xg *= 10 ;
          xb *= 10 ;
          /* digit -> value */
          xr += get_hex( sbuf[i]   ) ;
          xg += get_hex( sbuf[i+4] ) ;
          xb += get_hex( sbuf[i+8] ) ;
        }
        /* send draw */
        dflag = true ;
      }
      /* update color bar */
      if ( dflag == true ) {
        /* clear flag */
        dflag = false ;
        /* resize */
        xwr = xr / XDIV ;
        xwg = xg / XDIV ;
        xwb = xb / XDIV ;
        /* red bar */
        fill(255,  0,  0); rect(80, 80,xwr,20);
        /* green bar */
        fill(  0,255,  0); rect(80,100,xwg,20);
        /* blue bar */
        fill(  0,  0,255); rect(80,120,xwb,20);
      }
    }

 やっていることは単純です。

 受信バッファのデータを取出し、加工後、帯状に表示。

 不可視情報を可視化するため、図形描画しています。
 必要な処理は、以下に限定しました。

 ProcessingとArduino間の情報交換は、この程度に
 とどめておきます。

 クラス定義による汎用の情報交換メカニズムを
 用意できますが、基本はここまでの内容で充分
 だと思います。


目次

inserted by FC2 system