INDEX(各項目ごとの目次)

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

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

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端子)を入れ替えてください。

5/23/2008

Arduino 5/24授業

今週5/24も先週に引き続きArduinoを行います。パソコン、Arduino基盤、USBケーブル、ブレッドボード、ジャンプワイヤ、7セグLED(先週使った部品)を持って来て下さい。Processingも同時に行います。

5/18/2008

Arduino 7セグLEDの点灯

7セグLEDは、数字の形を表示するための細長い7個のセグメントと数字右下にある「.(ドット)」の合計8個のLEDで構成されています。つまり8個のLEDを制御するプログラムになるということです。授業で用いた7セグLEDは、秋月電子で購入したものでデータシートは付属していましたが、部品単体で販売されていることもあるので、その際にはメーカーや型番をインターネットで検索しデータシートを探し出すか、自分自身でどのセグメントがどの端子に対応しているのかなどをテストする必要があります。今回の7セグLEDは、アノードコモンタイプ、8個のLED、10本の端子です。アノードコモンタイプは、8個のLEDのプラス側の端子を共有するつくりになっています。
Arduino基盤につなぐには、Arduinoの電圧が5Vに対してLEDの使用電圧が2.5V前後なので、前回同様、抵抗(220〜470Ω程度)が必要になります。一つの抵抗を8個のLEDに対して共有することもできないわけでもないのですが、数字を表示する際には、「0」の場合は6個のLEDセグメント、「1」の場合は2個のLEDセグメントを発光させるというように、その数字によって、発光させるLEDセグメントの数が異なるので、セグメント数が少ないほど明るく発光してしまいます。それぞれの発光を均一にするためにも、抵抗はセグメントの数と同じだけ必要となります。今回はアノードコモン(プラス側の端子は共有)なので、各抵抗はカソード側(マイナス)に接続する回路にします。
1秒ごとに0〜9までカウントアップしていき、それをループするプログラムにしたいと思います。数字右下の「.(ドット)」は今回使わないで、合計7個のLEDセグメントを制御します。

まず、7セグLED裏面に10本端子があるので、どの端子がどのセグメントであるか、ひとつずつ点灯実験してみます(Arduino基盤の5V端子とGND端子に接続してテストしますが、この際にも抵抗を直列つなぎしてください)。ブレッドボードとジャンプワイヤがあると便利です。7セグLEDの場合、共有端子が真ん中にあることが多いので、データシートがない場合は、そのようなことを想定してテストしてみるといいかもしれません。



それぞれの端子は、上図のように各セグメントに対応していることが分かりました。cとhはアノード共有端子です。今回は、eのドットは使わず、a、b、d、f、g、i、jの7個を制御表示させることにします。例えば「1」を表示させるには、dとfをオンにし、残りはオフになるように設定します。アノードコモンであることから、cにはプラスの電圧がかかり、dとfを発光させるには、dとfがマイナスにならなければいけません。前回用いた図の「抵抗をカソード側につけた場合」を参考とし、a、b、d、f、g、i、jの7個の端子にそれぞれ別個の抵抗(220Ω)を取付け、Arduino基盤のデジタル出力の端子に接続します。
とりあえず、aの端子をArduino基盤の1番ピン、bを 2番ピンという順番で以下のようにつなげることにします。
a---1番ピン
b---2番ピン
d---3番ピン
f---4番ピン
g---5番ピン
i---6番ピン
j---7番ピン



「1」を表示するためには、dとfを0V(LOW)にすることで電位差が生じ発光します(cの端子から5V、dのLEDを通して2.5Vに降下し、抵抗によって2.5Vから0Vに降下する)。プログラム上では、
digitalWrite(3,LOW);
digitalWrite(4,LOW);

に設定すると3番ピン(dのLED)とが4番ピン(fのLED)が発光し、「1」の数字が表示されることになります。ただし、光らせたいピンだけではなく、残りのピンを光らせないプログラムも必要となります。残りのピンについては、すべてLOWのかわりにHIGHに設定します。よって、「1」を表示させるプログラムは以下のようになります。
void setup(){
//1〜7番ピンを出力に設定
pinMode(1,OUTPUT);
pinMode(2,OUTPUT);
pinMode(3,OUTPUT);
pinMode(4,OUTPUT);
pinMode(5,OUTPUT);
pinMode(6,OUTPUT);
pinMode(7,OUTPUT);
}

void loop(){
//「1」を表示
digitalWrite(1,HIGH);
digitalWrite(2,HIGH);
digitalWrite(3,LOW);
digitalWrite(4,LOW);
digitalWrite(5,HIGH);
digitalWrite(6,HIGH);
digitalWrite(7,HIGH);
delay(1000);
}

他の数字に関しても同様にプログラムしていきます(ここでは省略して、0〜3までの表示とします)。
void setup(){
//1〜7番ピンを出力に設定
pinMode(1,OUTPUT);
pinMode(2,OUTPUT);
pinMode(3,OUTPUT);
pinMode(4,OUTPUT);
pinMode(5,OUTPUT);
pinMode(6,OUTPUT);
pinMode(7,OUTPUT);
}

void loop(){
//「0」を表示
digitalWrite(1,LOW);
digitalWrite(2,LOW);
digitalWrite(3,LOW);
digitalWrite(4,LOW);
digitalWrite(5,LOW);
digitalWrite(6,LOW);
digitalWrite(7,HIGH);
delay(1000);
//「1」を表示
digitalWrite(1,HIGH);
digitalWrite(2,HIGH);
digitalWrite(3,LOW);
digitalWrite(4,LOW);
digitalWrite(5,HIGH);
digitalWrite(6,HIGH);
digitalWrite(7,HIGH);
delay(1000);
//「2」を表示
digitalWrite(1,LOW);
digitalWrite(2,LOW);
digitalWrite(3,HIGH);
digitalWrite(4,LOW);
digitalWrite(5,LOW);
digitalWrite(6,HIGH);
digitalWrite(7,LOW);
delay(1000);
//「3」を表示
digitalWrite(1,HIGH);
digitalWrite(2,LOW);
digitalWrite(3,LOW);
digitalWrite(4,LOW);
digitalWrite(5,LOW);
digitalWrite(6,HIGH);
digitalWrite(7,LOW);
delay(1000);
}

setup()の中のpinMode()for文という繰り返しの構文を用いた書き方にします。以下のfor()の中の「int i=1; i<=7; i++」は、1から7までを順番(+1ずつ)に7回分繰り返し処理するという意味です。もし「int i=3; i<=5; i++」と書いていれば、3から5までの3回分繰り返し処理することになります。この場合なら、3から5がpinMode()内のiに代入されるので、3〜5番ピンをOUTPUTに設定するということになります。「int i=1; i<=7; i+=2」と書けば、「i++」の部分を「i+=2」にしたので、1〜7までを+2ずつということになり、「1、3、5、7」だけを処理させるということになります。上記プログラムでは、1〜7番ピンを一気にOUTPUTに設定したいので、以下のように「int i=1; i<=7; i++」とします。
それから、loop()の中が長くなりすぎて見づらいので、それぞれの数字の表示に対してファンクション名(任意の名前)をつけて、以下のようにloop()の外に定義しておくことができます。
void setup(){
for(int i=1; i<=7;i++){ //iを1から7までの数とし
pinMode(i,OUTPUT); //1から7までのピンを出力に設定
}
}

void loop(){
zero();
delay(1000);
one();
delay(1000);
two();
delay(1000);
three();
delay(1000);
}

void zero(){
digitalWrite(1,LOW);
digitalWrite(2,LOW);
digitalWrite(3,LOW);
digitalWrite(4,LOW);
digitalWrite(5,LOW);
digitalWrite(6,LOW);
digitalWrite(7,HIGH);
}

void one(){
digitalWrite(1,HIGH);
digitalWrite(2,HIGH);
digitalWrite(3,LOW);
digitalWrite(4,LOW);
digitalWrite(5,HIGH);
digitalWrite(6,HIGH);
digitalWrite(7,HIGH);
}

void two(){
digitalWrite(1,LOW);
digitalWrite(2,LOW);
digitalWrite(3,HIGH);
digitalWrite(4,LOW);
digitalWrite(5,LOW);
digitalWrite(6,HIGH);
digitalWrite(7,LOW);
}

void three(){
digitalWrite(1,HIGH);
digitalWrite(2,LOW);
digitalWrite(3,LOW);
digitalWrite(4,LOW);
digitalWrite(5,LOW);
digitalWrite(6,HIGH);
digitalWrite(7,LOW);
}

このように各数字に対するファンクションを定義することで、必要に応じてloop()内でファンクション名を呼び出す使い方が可能になります。loop()内では、全体の流れが上から順に書かれており、loop()外には、それぞれの数字の内訳のプログラムが書いてあり、全体と部分という関係でプログラムしていくことができます。部分(数字の内訳のプログラム)を修正すれば、loop()内で処理される全体の流れにもすぐ反映され、全体の流れだけを修正したい時には、いちいち部分のプログラム内容に触れなくても済むという利点があります。

また、数字がカウントアップしていくプログラムであれば、更に以下のように配列(Array)を利用して、反復する内容をできるかぎり省略して書くこともできます。先ほどまでは、pinMode()、digitalWrite()、delay()が繰り返し書かれてきましたが、以下のようにすれば、それぞれを一回書くだけで済みます。
boolean table[4][7]={ {0,0,0,0,0,0,1},
{1,1,0,0,1,1,1},
{0,0,1,0,0,1,0},
{1,0,0,0,0,1,0} };

void setup(){
for(int i=1; i<=7;i++){
pinMode(i,OUTPUT);
}
}

void loop(){
for(int i=0;i<=3;i++){
for(int j=0;j<=6;j++){
digitalWrite(j+1,table[i][j]);
}
delay(1000);
}
}

配列によって複数の値をグループとして用いることが可能となります。配列は[]の括弧をもちいてあらわします。もし、数字の「0」についてのグループをつくるなら、

boolean zero[]={0,0,0,0,0,0,1};

となります。zeroというグループには7つの値が含まれており、それぞれが順番にLEDセグメントのLOWかHIGH(0か1)の設定に対応しています。0か1しかないので、boolean型を用いています。zero[6]という表記は、配列zeroの6番目の値ということになります。配列では、{}内に含まれる最初の値を0番目として扱います。よって、zero[0]からzero[5]までは0で、配列zeroに含まれる最後の値であるzero[6]は1となります。
zero[]と同様にして数字「1」、「2」、「3」に関しては
boolean one[]={1,1,0,0,1,1,1};
boolean two[]={0,0,1,0,0,1,0};
boolean three[]={1,0,0,0,0,1,0}

となります。
さらに、これらの各数字グループをまとめる全体グループをもう一つの配列を用いてつくります(その配列名をtableとします)。
一行で書くと横に長くなるので、改行して少し見やすくします。

boolean table[4][7]={ {0,0,0,0,0,0,1},
{1,1,0,0,1,1,1},
{0,0,1,0,0,1,0},
{1,0,0,0,0,1,0} };

配列tableは、4つ要素を持ち、それぞれの要素がさらに7つの値を含んでいるということになります。例えば、table[2][2]は、数字「2」の2番目の値で1(HIGH)であり、table[2][5]も1(HIGH)となります。
この全体の配列tableは、loop()内で処理される全体の流れと、それぞれの内訳を0と1だけを用いて書き直した表のようなものです。loop()内では、この表に従って順にカウントアップされる処理が行われます。
void loop(){
for(int i=0;i<=3;i++){
for(int j=0;j<=6;j++){
digitalWrite(j+1,table[i][j]);
}
delay(1000);
}
}

このloop()内のfor文では、iをint型の変数にして0〜3までの数字を順番に処理していき、その処理のさらに内部では、jをint型の変数にして各配列の0〜6番目までの値を順番に処理させ、digitalWrite()のピン番号とLOWかHIGHかを指定する部分に0か1が入るようになっています(digitalWrite()の中のj+1という部分は、配列の0番目が今回使用したArduino基盤のピン番号の1番目に対応するため+1してあります。本来、基盤の0番ピンから使えば、配列の0番目がピン番号の0番目に対応するので、このようなことをしなくても済んだはずです)。
配列(Array)for文に関しては、プログラミングではよく出てきますので、詳細については再び授業あるいはブログで説明していきたいと思います。

関連:
Arduino:7 セグ+照度センサNJL7502L」--照度センサで得た値を7セグを用いて表示する




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