【変更】以下はArduino1.0まで対応したプログラム内容です。
特にシリアル通信においては、Arduino2.0使用の際、バイト送信する場合、
Serial.print(value,BYTE);
のかわりに、
Serial.write(value);
を使用してください。
前回までのサンプルでは、ArduinoのSerial.write()を用いて、0〜255までの256段階の値を扱っていました。analogRead()からは、0〜1023までの1024段階の値を読み取ることができるのに、わざわざ4で割って256段階にスケーリングして解像度を落としていました。
今回はanalogRead()から読み取った1024段階の値を解像度を落とさずProcessingへ送信したいと思います。1024段階の値を二つの値に分解して送信する方法を用いることにします。大きな画面上で図形などを滑らかに動かすときや、センサなどから読み取った値を精度高く計算したいときは、解像度を低くできない場合があります。そのような時に、今回の方法を用いるといいでしょう。
今回もまた3個の可変抵抗器を基盤につなぎ(配線については前回ブログ参照)、それぞれをanalogRead()で1024段階で読み取り、Processing上でも1024段階で表現したいと思います。
Arduinoのプログラム:
//3つの変数を用意
int x, y, z;
void setup(){
//シリアル通信開始
Serial.begin(9600);
}
void loop(){
//x,y,zの変数へ読み込んだ値を入れる
x = analogRead(0);
y = analogRead(1);
z = analogRead(2);
//Processingからの合図用データが
//バッファ領域内に一つ以上になったら
if(Serial.available() > 0){
//それぞれの値を2つの値に
//分解するための変数を用意し
//計算結果を入れる
int x_high = x / 256;
int x_low = x % 256;
int y_high = y / 256;
int y_low = y % 256;
int z_high = z / 256;
int z_low= z % 256;
//分解した値を送る
Serial.write(x_high);
Serial.write(x_low);
Serial.write(y_high);
Serial.write(y_low);
Serial.write(z_high);
Serial.write(z_low);
//合図用データを読み込んで、
//バッファ領域を空にする
Serial.read();
}
}
以上のプログラムで、x,y,zは整数型の変数です。整数型の計算では、例えば10/3=3、3/10=0であり、小数点以下の部分は無視されます(四捨五入もされません)。ちなみに、小数点まで求める場合は、float型の変数を用います。
それから「%」(Modulo)という割り算の余りを求める計算式があります。10%3=1、3/10=3、10%4=2、10%5=0となります。
今回はこの二つの計算方法を使って、0〜1023の1024段階の値をx_highとx_lowという二つの値に分解して、二つのデータとしてArduinoから送ります。送られたデータはProcessing上で再び合成されて、一つの値として扱われるようにします。
例えばArduinoのanalogRead()から950という読み取り値があったら、
x_high = 950 / 256; x_low = 950 % 256;
として、
x_high = 3; x_low = 182;
となります。
この二つの値3と182をそれぞれProcessingへ送信し、Processing上で、
x_high * 256 + x_low = 950
つまり、
3 * 256 + 182 = 950
と計算し直して、もとの値950を得ることになります。実は、分解された値は前回同様最大値が255(256段階)であり、大きな値に対して256が幾つ分あり、そしてその余りが幾つかということを計算して送信しています。
同様にyとzについても分解して送信し、Processing上で合成します。
今回は3つの値があり、それぞれを二つに分解して送信するので、合計6回送信します。
Processingの方では、円のXY座標と大きさをそれぞれ調節可能にするプログラムとします。解像度が1024段階あるので、画面サイズは大きめにしておきます。前回同様、マウスで画面をクリックしたら、通信開始するプログラムにします。
Processingのプログラム:
//シリアルライブラリを取り込む
import processing.serial.*;
Serial myPort;
int x,y,z;
void setup(){
//大きめの画面サイズに設定
size(1000,700);
//滑らかな描画にする
smooth();
//シリアルポート設定
myPort=new Serial(this,"/dev/tty.usbserial-A4001Kjl",9600);
//バッファ領域を空にしておく
myPort.clear();
//図形の塗りを200に設定
fill(200);
}
void draw(){
//背景を白に設定
background(255);
//円の描画
//XY座標と幅、高さに変数を入れる
ellipse(x,y,z,z);
}
void serialEvent(Serial p) {
//バッファ領域に6個のデータがあるとき
if(myPort.available()>5){
//分解された値を読み込む変数を用意し
//各変数に読み込み値を入れる
int x_high = myPort.read();
int x_low = myPort.read();
int y_high = myPort.read();
int y_low = myPort.read();
int z_high = myPort.read();
int z_low = myPort.read();
//読み込んだ値をそれぞれ合成し、
//1024段階の値としてx,y,zに代入
x = x_high * 256 + x_low;
y = y_high * 256 + y_low;
z = z_high * 256 + z_low;
//合図用のデータを送る
myPort.write(65);
}
}
//マウスが押されたら通信開始
void mousePressed(){
myPort.write(65);
}
0〜255というのは、1バイト分のデータであり、二進数の
00000000〜11111111
の値ということになります。00000000は0であり、11111111は255です。0か1が8桁あり8ビット(=1バイト)となります。
16ビットなら16桁あり
0000000000000000〜1111111111111111
となり、0〜65535となります。16ビットなので2バイトあります。
今回値を二つに分けて送信した方法というのは、16ビットを
00000000|00000000
というように上位8桁と下位8桁に分けて送信したことと同じです。
もし、256という値なら、
00000001|00000000
となり、上位8桁だけをみれば00000001なので、8ビットの1と等しいことになります。下位8桁は00000000なので、8ビットの0になります。つまり、256の場合は、1(上位)と0(下位)が送信されるということになります。
950を16ビットの二進数であらわすと、
0000001110110110
であり、同様に上位8ビット、下位8ビットで分けると、
00000011|10110110
になり、
上位8ビット00000011は10進数の3であり、下位10110110は182になり、3と182を送信することになります。
ArduinoやProcessingにも二進数/ビット演算の数式や関数があります。
「>>」という記号を用いて、
950 >> 8
と書けば、この値は3となります。つまり、950/256=3と同じ結果が得られます。
「>>」というのは「右にシフトする」という意味で、「950 >> 8」においては「950を8桁右にシフトする」ということになります。二進数で言えば、
0000001110110110
という16桁を8桁右にずらす(シフト)ので、
0000000000000011
となり、十進数の3になります。
950 >> 1
なら、1桁右にずらすので、
0000000111011011
となります。ずらされて右端からあふれてしまった0や1は消えてしまいます。
そのほか「&」というのもあります。
950 & 255
と書けば、
182となり、950 % 256と同じ結果が得られます。以下のようにそれぞれを上下に重ね合わせて、950を255でマスキングするような感じです(ビットマスク)。
00000011|10110110 950 00000000|11111111 255 00000000|10110110 182
255の0のある桁(上位8桁)の真上の950の桁00000011はすべて消されて0となり、255の1のある桁(下位8桁)の真上の950の桁10110110だけ残ります。255は16桁のうち上位8桁はすべて0が並んでおり、下位8桁はすべて1が並んでいるので、255というマスキングを950に施すということは、950の上位8桁を消して(0にする)、下位8桁だけを取り出すということになります。つまり、
950 >> 8 = 3 950 & 255 = 182
という計算でも、950/256=3、950%256=182と同じ値が得られるということになるので、Arduino上のxの値に関しては、
x = analogRead(0); byte mask = B11111111; Serial.write(x >> 8); Serial.write(x & mask);
と書くことができます。「byte mask = B11111111」というのは、maskというバイト型の変数を用意し、その値が11111111であるということで、xの値の下位8桁だけを取り出すために使っています。Arduinoにおいてバイトの場合は、8桁の二進数の頭の部分に「B」(二進数/バイナリーのフォーマット)を付けます。Processing上では、
(3 << 8) + 182 = 950
というように、逆に「3を左に8桁シフト」してから「182を足す」と「950」になるので、Processingでは、
x = (x_high << 8) + x_low;
と書けば同じ値を得ることができます。
二進数の表記やビット演算はサンプルプログラムやデータシートの中にも出てくることがあります。ビットやバイトを使わなくてもプログラムできますが、覚えておくと便利なときもあり、考え方や計算もよりシンプルになる場合があります。
関連:
「Arduino-Processing シリアル通信1」(一つの値を送る/非同期通信)
「Arduino-Processing シリアル通信2」(複数の値をバイトで送る/同期通信)
「Processing-Arduino シリアル通信4」(ProcessingからArduinoを制御する)
「Arduino-Processing シリアル通信5」(複数の値を文字列で送信する)
「Arduino-Processing シリアル通信6」(2台のArduinoとProcessingを通信させる)
0 件のコメント:
コメントを投稿