INDEX(各項目ごとの目次)

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

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

5/28/2008

Arduino-Processing シリアル通信3


【変更】以下は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を通信させる)

5/27/2008

Arduino-Processing シリアル通信2


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


前回のシリアル通信では、ひとつの値をArduinoからProcessingへ一方的に送り続ける内容でした。今回は、Arduino側から三つの可変抵抗器を使って、三つの異なる値をProcessingへ送りたいと思います。

例えば、Processing上の3DモデルのX座標、Y座標、Z座標の値を送り、それぞれのツマミで三次元的に立体を動かすことができるプログラムになるということです。
Arduinoから3つの値を送るには、
Serial.write(x);
Serial.write(y);
Serial.write(z);

となります。
3つの値がXYZの順番で送られる場合、XYZ XYZ XYZ・・・と繰り返されますが、Processing側の読み取りを開始するタイミングがずれると、最初の幾つかの値をスキップしてしまい、YZX YZX YZX・・・、あるいはZXY ZXY ZXY・・・となってしまいます。このように、順番がずれないようにするためには通信上の工夫が必要となります。今回の方法では、3つの値を一方的に送るのではなく、受取確認をしながらお互いに通信します。以下にプログラムを書きます。

Arduino側のプログラム:
//x,y,zの3つの変数を用意し、初期値を0とする
int x=0;
int y=0;
int z=0;

void setup(){
  //シリアル通信開始
  Serial.begin(9600);
}

void loop(){
  //アナログ入力ピン0,1,2を
  //それぞれx,y,zに対応させる
  //値を4で割って最大値255にする
  x=analogRead(0)/4;
  y=analogRead(1)/4;
  z=analogRead(2)/4;

  //Processingから合図のデータが
  //一つ送られてきたらという条件
  if(Serial.available()>0){
    //x,y,zの順番で値を送る
    Serial.write(x);
    Serial.write(y);
    Serial.write(z);
    //先ほどのProcessingからの
    //合図のデータを読み込む
    Serial.read();
  }
}

Arduinoプログラム上のSerial.available()は、外部(この場合Processing)から1バイト分のデータが何個送られてきているかを数えてくれます。送られてくるデータは、Arduinoであれ、Processingであれ、それぞれのメモリ上(バッファ領域)に一旦貯められます。その後、Serial.read()によって、貯められているデータから順番にひとつずつ読み込む処理をします。もし、読み込む処理をしなければ、送られたデータは次々とバッファ領域に溜まり続けます(設定した限界をこえるとそれ以上貯めることはできなくなります)。if(Serial.available()>0){...}という条件は、データが1個以上貯まったら(0個より多い場合)、{...}内の処理をするという意味です。

このプログラムの場合の手順は以下のようになります。

(1)まず、Processingから合図用のデータが送られてくる。
(2)合図用のデータが、一旦Arduinoのバッファ領域に貯められる。
(3)Serial.available()で、現在何個データが貯まっているか数える。
(4)合図用のデータが1個以上あれば、(5)以下を実行する。
(5)Serial.write(x)で、新たなxの値を送信する。
(6)Serial.write(y)で、新たなyの値を送信する。
(7)Serial.write(z)で、新たなzの値を送信する。
(8)Serial.read()で、バッファ領域内の合図用のデータを読み込む。
(9)結果、バッファ領域が空になる(データの個数が0個になる)。
(10)Processingからの合図用のデータを待つ。

その後は(1)に戻り、同様の処理を繰り返します。要するに、Processingからの合図のデータが毎回1個送られて、それを確認後、Arduinoはx、y、zの値を送り返すという手順を踏みます。

次にProcessing側のプログラムを書きます。Arduinoのプログラムで書いたように、Processingからは、合図用のデータを1個送る必要があります。そうすれば、Arduinoから3個のデータが送られてくるので、Processingのバッファ領域にデータが3個貯まったときに、読み込む処理をさせればいいということになります。今回はx、y、zの3個の値なので3Dの図形を動かすプログラムにします。

Processing側のプログラム:
//シリアルライブラリを取り込む
import processing.serial.*;
//シリアルのオブジェクトmyPortを用意
Serial myPort;

//x,y,zの3個の変数を用意
int x=0;
int y=0;
int z=0;

void setup(){
  //3D用の画面サイズとして設定
  size(255,255,P3D);
  //シリアルポートの設定(「A4001Kjl」は基盤により異なる)
  //Windowsの場合は、"COM5"などとなる(前回ブログを参照)
  myPort=new Serial(this,"/dev/tty.usbserial-A4001Kjl",9600);
  //3D図形の塗りは無し(ワイヤーフレーム描画)
  noFill();
}

void draw(){
  //背景色を白に設定
  background(255);
  //3Dの位置座標にx,y,z入れる
  translate(x,y,z);
  //一辺50のボックス(立方体)を描画
  box(50);
}

//シリアル通信処理
void serialEvent(Serial p){
  //Arduinoから送られてきたデータが
  //3個(2より多い)の場合
  if(myPort.available()>2){
    //x,y,zの順番でデータを読み込む
    x=myPort.read();
    y=myPort.read();
    z=myPort.read();
    //読み込み後、合図データ送信
    myPort.write(65);
  }
}

//マウスが押されたら通信開始とする
void mousePressed(){
  //念のためバッファ領域を空にする
  myPort.clear();
  //とりあえず65という合図用データを送る
  myPort.write(65);
}

以上が、Processing側のプログラムです。3Dの場合は、size()の括弧内に画面幅、高さ以外に「P3D」を書き足します。ワイヤーフレームで描画した方が、今回の場合分かりやすいと思うので、noFill()をつかって塗り面を無しにしました。
void draw(){...}内のtranslate()は、その後に描画される3D図形の座標値をいれます。translate()に含まれる値が変化することで、3D図形は移動します。box()は、3Dの直方体を描画します。box(50,100,80)というように3つ値を入れれば、幅、高さ、奥行きをそれぞれ定義できます。box(50)の場合は、各辺が50の立方体になります。
シリアル通信の部分は、まずmousePressed()でマウスを押したときに、myPort.clear()でProcessingのバッファ領域内に貯まっているデータをとりあえず空にします(初期化)。そして通信を開始するきっかけとなる合図のデータをmyPort.write(65)で送ります。65という値を送っていますが、0〜255の値であれば何でも構いません。Arduino側は送られてくるデータの個数を数えるのであって、データの中身の値については、何でもいいことになります。つまり、合図用に何らかのデータを1個送ればいいということです。一旦合図用のデータがProcessingから送られれば、次にArduinoがそのデータを受取り、そして3個のデータを送り返してきます。void serialEvent(Serial p){...}内のif(myPort.available()>2){...}内では、Arduinoから送られて来たデータがProcessingのバッファ領域に3個貯まったら(2個より多くなったら)myPort.read()で3回読み込み、x,y,zの3個の変数にそれぞれ値を入れていき、それらのデータは、box()の3D座標になるtranslate()に代入され、box()が動きます。



もう一度、手順をはじめから書くと、

(1)Arduinoの電源がオンになり、Arduinoのプログラムが開始。
(2)Processingのプログラムを立ち上げる。
(3)Arduinoは、Processingからの合図用データを待つ。
(4)Processing側でマウスを押す(通信開始)。
(5)Processingのバッファ領域を一旦空にする(初期化)。
(6)Processingから合図用データが1個送られる。
 *以下、Arduino上での処理
(7)Arduinoのバッファ領域に合図用データが一旦貯められる。
(8)Arduinoのバッファ領域内のデータの個数を数える。
(9)データの個数が1個以上のとき、以下の処理を実行。
(10)Arduinoから、新たなxの値を送信する。
(11)Arduinoから、新たなyの値を送信する。
(12)Arduinoから、新たなzの値を送信する。
(13)バッファ領域内の合図用のデータを読み込む。
(14)読み込んだ結果、Arduinoのバッファ領域が空になる。
(15)次の合図用データがバッファ領域に貯まるまで待機。
 *以下、Processing上での処理
(16)Processingのバッファ領域にデータが貯められる。
(17)Processingのバッファ領域内のデータの個数を数える。
(18)データの個数が3個になったら、以下の処理を実行。
(19)xの値として1番目のデータを読み込む。
(20)yの値として2番目のデータを読み込む。
(21)zの値として3番目のデータを読み込む。
(22)その結果、Processingのバッファ領域が空になる。
(23)Processingから合図用データが1個送られる。
(24)次の3個のデータがバッファ領域に貯まるまで待機。
 *その後は(7)へ戻り処理を繰り返します。

Serial.available()
myPort.available()を使うことで、バッファ領域内に貯められているデータの個数を数え、その個数をもとにデータを送受信するタイミングを制御することができます。これ以外にも、送られる複数のデータの先頭部分や最後の部分に「.」(ピリオド)などの特定のデータを付け加えて送ることで、読み込み開始地点や読み込み終了地点を知らせる方法もあります。
シリアル通信を使うことで、コンピュータの内側と外側の世界をつなぐことができます。今後も様々な表現に応じて「シリアル通信」の技術は、繰り返し登場してきます。今回一気に理解できなくても、徐々に使いこなしていくことで、身についていくと思います。


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

5/26/2008

Arduino-Processing シリアル通信1

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

今回はシリアル通信を用いて、Arduino基盤に接続した入力装置(可変抵抗器)で、Processingで描かれた図形を動かしてみます。
Arduino側のシリアル通信は、前回のモニタリングで使用したときのような感じです。Processingにおいても、シリアル通信の設定が必要になります。

通信の流れとして:
Arduino基盤に接続した入力装置からの値をanalogRead()で読み取る。
その値をSerial.print()でProcessing側に送る。
Processing側で、その値をSerial.read()で読み取る。
Processing上の図形の座標値に入れる。
という感じです。

まずは、Arduino側からプログラムしていきます。今回入力装置となる可変抵抗器を基盤に接続します。


Arduino側のプログラム:
int val; //読み取り値の変数を用意

void setup(){
  Serial.begin(9600);
}
void loop(){
  //アナログ入力0番ピンの値を読み取り(0~1023)
  //4で割った値を変数valに入れる(0~255)
  val=analogRead(0)/4;
  //シリアルでvalを送信
  Serial.write(val);
  //1秒間に20回ループ(0.05sec)とする
  delay(50);
}

上記プログラムの説明:
Processingと通信する場合、Serial.write()で一度に送ることができる値は、0〜255までの数値になるので、analogRead()で読み取った値(0〜1023)を4で割って、最大値が255になるようにスケーリングした値をvalに代入し送信します。delay(50)程度にし、一秒間に20回送信することにします。Arduino側から一方的にデータを送り続けるので、あまりにも多くのデータを送りすぎると、Processing側での受取処理が追いつかなくなり、反応に時差がでることがあります。

Processingのプログラムについて:
Processing側のプログラムでは、シリアル通信(Serial)はライブラリに含まれており、必要に応じてその機能を取り込む(import)必要があります。
Processingのsketchを開いたら、メニューバーのSketch>Import Library>serialを選択します。そうすると、プログラムを書く欄に自動的に「import processing.serial.*;」という一文が追加されます。これによって、シリアル機能が導入されます。そして以下のようにmyPort(名前は任意)というシリアルのためのインスタンスを用意し、シリアルポート、通信速度を設定します。Processingのシリアル通信についての説明は、Processing画面のメニューバーでHelp>Referenceへ行き、そのページ内の上部のLibrariesをクリックし、さらにページ内のCore Librariesの欄にSerialという項目があるので、そこをクリックします(Processingのサイトにも同じSerialのページがあります)。

Processing側のプログラム:
//シリアルライブラリを取り入れる
import processing.serial.*;
//myPort(任意名)というインスタンスを用意
Serial myPort;

int x; //図形のX座標の変数を用意

void setup(){
  //画面サイズ
  size(256,256);
  //シリアルポートの設定
  myPort=new Serial(this,"/dev/tty.usbserial-A4001Kjl",9600);
}

void draw(){
  //背景色を白に設定
  background(255);
  //XY座標を(x,100)に設定し、
  //幅50、高さ50の円を描画
  ellipse(x,100,50,50);
}

void serialEvent(Serial p){
  //変数xにシリアル通信で読み込んだ値を代入
  x=myPort.read();
}

上記プログラムの説明:
初期設定のsetup()内の「myPort=new Serial(this,"/dev/tty.usbserial-A4001Kjl",9600);」の設定において、MacOSXの場合は「/dev/tty.usbserial-********」の箇所の********の部分は使用しているArduino基盤によって異なります。Arduino画面のメニューバーのTools>Serial Portのなかから使用している基盤のシリアルポートと同じものを書いて、両端を「"」マークで括ってください。Windowsの場合は、「COM*」(*は番号)のようにポートが表示されるので、同様に「COM*」を「"」マークで括ってください。
通信速度の「9600」は、通常この設定で構いません。

シリアル通信が行われるたびに、serialEvent()内のx=myPort.read()によってArduino基盤から送られて来た値を読み込み、変数xに代入され、最終的にellipse()のX座標に代入されます。Arduinoから送られてくる値は0〜255であるため、Processing上のellipse()のX座標の移動範囲も0〜255(256段階)となります。それに合わせて、Processingの画面幅を256に設定しました。つまり、ellipse()を1ピクセルずつ左右に動かすことができます。


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

Arduino 圧電スピーカ

圧電スピーカは、ブザー(音が鳴る)として機能する一種のスピーカです。圧電スピーカについている2本の線に、そのまま電気を流しても音は鳴りません。音を出すためには、高速でオンとオフを繰り返し(パルス)、内部の金属板を振動させます。プログラム上では、digitalWrite()を用いてHIGHとLOWの切替を行い、delay()によってオン/オフの時間の間隔(周波数)をつくりだすことで制御することができます。
以下では、前回使用した可変抵抗器を用いて、可変的に周波数をつくりだし、圧電スピーカを鳴らす実験をしてみます。

Arduinoのプログラム:
int val=0;

void setup(){
pinMode(13,OUTPUT);
}

void loop(){
val=analogRead(0);
digitalWrite(13,HIGH);
delay(val);
digitalWrite(13,LOW);
delay(val);
}

音はでますが、あまりいい音ではないので、周波数を細かくするために、delay()のかわりにdelayMicroseconds()を用いて同様にテストしてみます。delayMicroseconds()は、delay()の1/1000の時間、つまり1マイクロ秒(1/1000000秒)が単位となります。高音領域が高周波になりすぎないように、valに予め+500のオフセットを設け、500〜1523までの値がdelayMicroseconds()に入ることにします。ちなみに、Arduinoサイトの説明によると、delayMicroseconds()の()内に入れられる数値は、最大で「16383」であり、delayMicroseconds(0)というように()内に「0」を入れると0秒ではなく、それよりも長い時間(~1020マイクロ秒)ディレイしてしまうと書いてあります。設定するときは注意して下さい。

int val=0;

void setup(){
pinMode(13,OUTPUT);
}

void loop(){
val=analogRead(0)+500;
digitalWrite(13,HIGH);
delayMicroseconds(val);
digitalWrite(13,LOW);
delayMicroseconds(val);
}

圧電スピーカは、音を鳴らす以外にセンサとして使うこともできます。圧電スピーカとLEDを直結し、圧電スピーカを指先でたたいて衝撃を与えると電源がなくてもLEDが一瞬発光します。

この発電原理を利用して、圧電スピーカをマイクのような衝撃センサとして用いることが可能となります。圧電スピーカからの電圧をanalogRead()で読み取って、どの程度の値が得られるかテストしてみます。
読み取り値などを画面に出力するには、シリアル通信機能を用いて以下のようなプログラムを付け足し、プログラムが開始したら、Arduinoの画面上のSerial Monitorボタンを押します。

初期設定のSerial.begin(9600)は、通信速度を9600に設定し、シリアル通信を開始するという意味です。Serial.println(val)は、モニタリングするためにvalの値をシリアル通信を用いて出力します。Serial.println()は、データを毎回改行しながら出力します。もうひとつSerial.print()という、改行せずにそのままデータを送り出すものもあります。今回はモニタリングするために、改行して出力したほうが見やすいので、Serial.println()の方を使います。

圧電スピーカに衝撃を与えると値が変化することが確認できます。出力される値が10以上であれば、衝撃を加えたことに反応しているとみなすこととします。以下に、圧電スピーカからの衝撃によってオン/オフするLEDのプログラムを書きます。boolean型の変数を用いて、以前Processingで用いたトグルスイッチのプログラムを付け足します。boolean型の変数checkがfalseの時はLEDがオフの状態、checkがtrueの時はオンの状態とします。
int val=0;
boolean check=false;

void setup(){
//13番ピンをLEDの出力に設定
pinMode(13,OUTPUT);
}

void loop(){
//圧電スピーカ0番ピンの読み取り値
val=analogRead(0);
//読み取り値が10以上の場合
if(val>10){
if(check==false){ //LEDオフ状態の場合
digitalWrite(13,HIGH); //オンに切替
check=true; //オンの状態として記憶
}else{ //LEDオン状態の場合
digitalWrite(13,LOW); //オフに切替
check=false; //オフの状態として記憶
}
}
delay(100);
}


Arduinoのサイト内のLearning/Examples/SoundページにもPlay Melodies with a Piezo Speakerという名前で、圧電スピーカのサンプルが掲載されています。

Arduino アナログ入出力

前回までは、LEDをオン/オフ(5V/0V)するプログラムでした。今回は、オン/オフの制御ではなく、外部からの入力によってLEDの明暗を変化させるプログラムをします。
analogWrite()を用いれば、0〜255の256段階でLEDの明るさが調節できます。モータの出力に使えば、スピード調節が可能となります。PWM(パルス幅変調)という方法で256段階を調整しますが、パルスについては後で説明したいと思います。まずはanalogWrite()の使い方からマスターしていくこととします。
analogWrite(ピン番号,出力値)というように二つの値を設定する必要があります。「ピン番号」は出力したいピンの番号を入れますが、Arduino基盤の0〜13番ピンのうちの3、5、6、9、10、11の6つのピン(基盤上のピン番号下にPWMと書かれている番号)のどれかになります。「出力値」は、0〜255(0V〜5Vに対応)の値をいれます。
analogWrite(3,0);

と書けば、3番ピンを0(0V)で出力となるので、LEDであれば消灯します。
analogWrite(3,255);

であれば、一番明るい状態となり、
analogWrite(3,127);

であれば、約半分の明るさとなります。

次に、外部からの入力(センサ入力)の際に使用するanalogRead()について説明します。analogRead()は、0〜1023の1024段階で値を読み取ることができ、0〜1023が0V〜5Vに対応しています。Arduino基盤の右下に「ANALOG IN」と書かれた0〜5番ピンを使用します。
analogRead(0);

と書けば、「ANALOG IN」の0番ピンに接続したワイヤからの電圧を読み取って0〜1023の値が得られることになります。
入力用に使われるセンサは様々なものがありますが、今回は「可変抵抗器(ボリューム)」を使用することにします。一般的な可変抵抗器には3つの端子があり、両端の二つの端子を0V(GND)と5Vにつなぎ、ツマミを回すと中央の端子から任意の電圧(0V〜5V)が出力されます。つまり、中央の端子からの可変電圧をanalogRead()で読み取って、その入力値をanalogWrite()の出力値に入れれば、LEDの明るさをツマミをひねることで調整できるようになります。
ひとつ注意しなければいけないことは、analogRead()によって得られる値は0〜1023に対して、analogWrite()の出力値が0〜255までなので、入力値(読み取り値)を4で割った値を出力値に入れないといけません。
尚、analogWrite()analogRead()の場合は、初期設定でpinMode()の入出力を設定せずに直接使うことが出来ます。

以下に、analogWrite()analogRead()を用いて、外部からの入力(可変抵抗器)によってLEDの明るさを変えるプログラムを書きます。
int val=0; //入力値の変数を用意し、0に設定

void setup(){
//pinMode()の設定は不要
}

void loop(){
//ANALOG INの0番ピンを読み取りvalに代入
val=analogRead(0);
//アナログ出力(PWM)の3番ピンを出力とし
//valを4で割った値を入れる
analogWrite(3,val/4);
//0.1秒ループにする
delay(100);
}


可変抵抗器のツマミの回し方(時計回り/半時計回り)と出力値の増減の向きを変えたい場合は、可変抵抗器の両端の端子(0V端子/5V端子)を入れ替えてください。


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