目次
前
次
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の双方のスケッチに
コマンドインタプリタを用意して対応するのが簡単。
コマンドインタプリタは、受信内容を解析後、対応した
処理を実行しますが、受信割込みを使うと効率よく動作
を記述できます。
コマンドインタプリタを使うには、プロトコルを規定して
齟齬がないようにします。
自分が実践しているプロトコル規定は、以下。
- コマンドは英大文字の1文字で表現
- なるべくコマンド数は減らす
- パラメータはコマンド1文字に続ける
- パラメータは判読できる数字にする
- コマンド、コマンド+パラメータは'\r'で終端する
- 受信バッファサイズは4文字の倍数にする
- 受信バッファは配列sbufとする
- なるべくデフォルト転送速度9600bpsを利用
- コマンドインタプリタを、イベントフラグで起動する
上の規定から、コマンドインタプリタの基本形態は以下。
/* 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);
}
}
やっていることは単純です。
受信バッファのデータを取出し、加工後、帯状に表示。
不可視情報を可視化するため、図形描画しています。
必要な処理は、以下に限定しました。
- Arduinoに情報を要求
- Arduinoの出力情報を、割込みで受取る
- Arduinoの出力情報は、受信バッファに格納
- 情報が揃ったことをフラグで通知
- 情報を加工後、帯状に表示
ProcessingとArduino間の情報交換は、この程度に
とどめておきます。
クラス定義による汎用の情報交換メカニズムを
用意できますが、基本はここまでの内容で充分
だと思います。
目次
前
次