こんにちは、「メカのりまき」です。
この記事では、関数を自分で作成して使う方法について、説明したいと思います。
また、関数の使い方を学ぶために、LEDを使った簡単なゲームの作り方を今回と次回の記事の2回に分けて説明をしたいと思います。
この記事は「Arduino IDE」の基本的な使い方を、理解していることを前提とした記事となっています。
「Arduino IDE」の使い方は以下のリンク先の記事で説明していますので、「Arduino IDE」の使い方が分からない方は、以下のリンク先の記事を是非ご覧になってください。
目次
本記事で使用するもの
本記事で私が使用した物は以下の通りです。
- 「Arduino IDE2.0.4」をインストールしたPC(Windows11)
- USBケーブル
- Arduino Uno R3(ELEGOO UNO R3でも可)
- ブレッドボード
- LED
- 抵抗器
- パッシブブザー
- タクトスイッチ
- ジャンパーワイヤー
「Arduino Uno R3」の互換ボードであれば、「ELEGOO UNO R3」以外のボードでも、本記事と同様な手順で動作可能だと思われますが、動作検証はしていないので、その点はご了承ください。
以下のリンクのスターターキットを購入した場合、上記で述べた使用するものは、PC以外すべて入手することができます。
Arduinoをまだ購入していないという方やArduinoで動かすことができるたくさんのパーツが欲しいという方は、購入を検討してみてください。
関数の説明
プログラミングにおける関数とは、機能をまとめて1行で行えるようにしたものを表します。
例えば、「Arduino」でよく使われる「digitalWrite()」も指定したピンを「HIGH」または「LOW」にする関数です。
「digitalWrite()」は、あらかじめ用意された関数ですが、自分でオリジナルの関数を作成して、好きなときに呼び出すことも可能です。
今回は4つの例を紹介しながら、オリジナルの関数を作成する方法について説明したいと思います。
これから、紹介する例では、PCとシリアル通信を行います。
シリアル通信のやり方が分からない場合は、以下のリンク先の記事で説明していますので、是非、ご覧になってください。
複数の処理を呼び出す
では、早速ですが、以下のスケッチを書き込んで、シリアルモニタを確認してみてください。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
void setup() { //シリアル通信の初期化処理 Serial.begin(9600); } void loop() { //作成した関数を呼び出す Serial_Apple(); //1秒間待機 delay(1000); } //「...Apple」と表示させる関数を作成する void Serial_Apple(){ Serial.print("..."); Serial.println("Apple"); } |
スケッチが正しく書き込まれていた場合、シリアルモニタに、「...
Apple」と表示され続けるのが確認できます。
スケッチの解説
スケッチの17~21行目に以下のような文を記述しています。
17 18 19 20 21 |
void Serial_Apple(){ Serial.print("..."); Serial.println("Apple"); } |
この部分が、新しい関数を定義している部分となり、「void setup()」、「void loop()」の外側に記述します。
ここで、「void」というのは、何も値を返さないで、中の処理のみを行う関数であることを示しており、「Serial_Apple」が関数の名前です。
関数の名前はアルファベット、数字、アンダーバー( _ )を使って好きな名前を自分で付けることができます。
名前の後の( )は引数というものを定義する部分です。
今回のスケッチでは引数を使用しないため、中に何も記述しません。
その後の{ }内に記述したものが、関数を呼び出した際に、実行される処理です。
関数を呼び出す際は、スケッチの10行目のように、関数名と( )内に引数を記述します。
今回は引数を使わない関数であるため、( )内は空であり、以下のような文により関数を呼び出しています。
10 |
Serial_Apple(); |
つまり、今回のスケッチは「void loop()」内で、関数「Serial_Apple()」を1秒置きに呼び出して、「...
Apple」とシリアルモニタに表示させているということになります。
スケッチ全体を図にまとめると以下のようになります。
引数を渡して処理を呼び出す
先ほどの関数は、呼び出すことによって、決められた処理を行うというものでした。
次は関数を呼び出す際に、引数というものを渡し、それに応じた処理を行う関数の作り方を説明したいと思います。
以下のスケッチを書き込んで、シリアルモニタを確認してみてください。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
void setup() { //シリアル通信の初期化処理 Serial.begin(9600); } void loop() { //作成した関数を呼び出す Serial_Apples(1,2); //1秒間待機 delay(1000); } //関数を作成する void Serial_Apples(int a,int b){ Serial.print(a+b); Serial.println(" Apples"); } |
スケッチが正しく書き込まれていた場合、シリアルモニタに、「3 Apples」と表示され続けるのが確認できます。
スケッチの解説
まず、関数の定義を行っている17~21行目の以下の文について説明したいと思います。
17 18 19 20 21 |
void Serial_Apples(int a,int b){ Serial.print(a+b); Serial.println(" Apples"); } |
先程の関数では、関数名の後の( )内は空でしたが、今回の関数は( )内に、「int a,int b」と記述されています。
これは、「int型」の変数、「a」と「b」を引数として、関数内で利用するということを示しています。
ここで、記述した「a」と「b」は関数内で変数として扱うことができ、今回のスケッチでは、「a」と「b」を足し算したものを、シリアルモニタに出力し、その後「 Apples」と出力しています。
引数の値は、関数を呼び出す際に、渡すことができ、スケッチの10行目ように、関数名の後の( )内にそれぞれ対応した値を記述します。
10 |
Serial_Apples(1,2); |
つまり、今回のスケッチでは、「a=1」、「b=2」として、関数が実行されて、「3 Apples」と表示させているということになります。
スケッチ全体を図にまとめると以下のようになります。
値を返す
上記の2つのスケッチは、関数で定義した処理を実行するだけのものであり、関数を値として扱うようなことはしませんでした。
次は、関数を値として扱うような場合について、説明したいと思います。
以下のスケッチを書き込んで、シリアルモニタを確認してみてください。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
void setup() { //シリアル通信の初期化処理 Serial.begin(9600); } void loop() { //関数を呼び出し返してきた値を変数に入れる bool result=Yes_Man(); //返してきた値を出力 Serial.println(result); //1秒間待機 delay(1000); } //関数を作成する bool Yes_Man(){ return true; } |
スケッチが正しく書き込まれていた場合、シリアルモニタに、「1」と表示され続けるのが確認できます。
スケッチの解説
今回は先に関数を呼び出している10行目の以下の文について説明します。
1 |
bool result=Yes_Man(); |
今まで、関数を呼び出す際は、関数名と引数のみを記述していて、上記のように、変数に入れるような記述はしていませんでした。
なぜなら、関数に値は存在しないため、変数に入れるなんてことはできないからです。
しかし、今回は上記のように、変数の中に入れるような記述がされています。
つまり、今回の関数「Yes_Man()」は何等かの値を表しているということになります。
では、関数の定義を行っている20~23行目の以下の文について説明したいと思います。
20 21 22 23 |
bool Yes_Man(){ return true; } |
関数名の前を見ると、今までの関数とは異なり、「bool」と記述されています。
これは、この関数が「bool型」の値を返すことを意味しています。
さらに、関数の中身を確認すると、「return true」という文が、記述されています。
この文が、値を返すということを行っている文で、これにより、「Yes_Man()」は「true」という値として扱われます。
つまり、今回のスケッチでは、10行目の文で変数「result」に「Yes_Man()」の戻り値である「true」が入れられるということになります。
スケッチ全体を図にまとめると以下のようになります。
「Serial.print()」で、bool型の変数を出力すると、「true」のとき「1」、「false」のとき「0」が出力されるため、1秒ごとに「1」という値が出力されています。
上記のスケッチでは、値を返す文のみを記述しましたが、値を返す文の前に処理を行う文を書いた場合は、それらの処理は関数が呼び出された際に実行されます。
例えば以下のように、関数を変更すると、「Yes1」とシリアルモニタに出力されることが確認できます。
20 21 22 23 24 |
bool Yes_Man(){ Serial.print("Yes"); return true; } |
引数を渡して値を返す
値を返す関数についても、値を返さない関数と同様に引数を扱うことができます。
以下のスケッチを書き込んで、シリアルモニタを確認してみてください。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
void setup() { //シリアル通信の初期化処理 Serial.begin(9600); } void loop() { //関数を呼び出し返してきた値を変数に入れる bool result=Trickster(true); //返してきた値を出力 Serial.println(result); //1秒間待機 delay(1000); } //関数を作成する bool Trickster(bool a){ bool b=!a; return b; } |
スケッチが正しく書き込まれていた場合、シリアルモニタに、「0」と表示され続けるのが確認できます。
スケッチの解説
まず、関数の定義を行っている20~24行目の以下の文について説明したいと思います。
20 21 22 23 24 |
bool Trickster(bool a){ bool b=!a; return b; } |
今回の関数も戻り値はbool型としており、名前の前に「bool」と記述しています。
また、引数は1つで、こちらも「bool型」の変数で名前は「a」としています。
「bool b=!a」は、「bool型」の変数「b」に「a」の反対の値(「true」なら「false」、「false」なら「true」)を入れており、最後に「return b」により、「b」の値を戻り値として設定しています。
よって、10行目の文では、「a=true」であるため、「result」に反対の「false」が入れられて、出力結果は「0」となります。
スケッチ全体を図にまとめると以下のようになります。
関数の基本的な説明は以上となります。
今回のスケッチでは、戻り値の型を「bool型」としましたが、もちろん「int型」や「float型」なども扱うことができます。
自分で関数を作成して、色々と試してみてください。
LEDを使ったゲームを作る(前編)
上記で説明した関数の使い方を実践的に学ぶために、LEDを使った簡単なゲームの作り方を今回と次回の記事の2回に分けて説明したいと思います。
関数の知識以外にも、LED、ブザー、スイッチを動かすための知識などが必要になります。
分からない部分があった場合は、所々に必要な知識を説明した記事へのリンクを貼っておきますので、ゆっくりと確認しながら、制作してみてください。
ゲームの概要
まずは、今回制作していただこうと思っているゲームの概要を説明したいと思います。
ゲームの名前は「右往左往ゲーム」で、見た目は下の写真のような感じです。
ゲームの内容は、左もしくは右から、ブザーの音ともに決まったリズムでLEDを点灯させ、最後のLEDが点灯するタイミングでスイッチを押せた場合に成功とみなし、次の別のリズムに進めるようなリズムゲームです。
回路を作成する
では、早速ですが、回路を作成していただこうと思います。
以下のような回路を作成してください。
上記で説明してきた関数の動作を確認するスケッチを書き込まずに、ここまで読み進めたという方は、現在書き込まれているスケッチが回路に影響を及ぼさないように、空のスケッチを書き込んでおくことをお勧めします。
LEDの接続している抵抗器は、LEDの点灯が明るくなりすぎないように、大きめの電気抵抗のものにしており、私は黄色のLEDを1[kΩ]、緑色、青色のLEDを10[kΩ]の抵抗器に接続しました。
次の節でLEDを点灯させるので、LEDの明るさを見ながら、各々、抵抗器の調整をしてください。
LEDの抵抗器の決め方が分からない方は、以下のリンク先の記事を参考にしてみてください。
リズムに合わせてLEDを点灯させる
回路が作成できましたら、まず、リズムに合わせてLEDを点灯させる方法について説明したいと思います。
今回制作するゲームは、LEDが順番に点灯していき、最後のLEDが点灯するタイミングでスイッチを押すというものです。
よって、LEDの点灯に法則性を持たせて、最後のLEDが光るタイミングが予想できるようなものにしたいと思います。
今回、LEDは以下のような法則で点灯させます。
- 左の4つのLEDが点灯するリズムと右の4つのLEDが点灯するリズムは同じにする
- 1つのLEDが点灯する長さは変数で決定し、最後のLEDが点灯するまで統一する
- 左右それぞれのLED4つは点灯の長さ×16の間で順番に1回ずつ点灯する
- LEDが消灯してから、隣のLEDが点灯するまでの間隔は点灯する長さの倍数とする
上記の法則を図にまとめると以下のようになります。
次に上記の法則でLEDが点灯するようなスケッチについて、説明したいと思います。
以下のスケッチを書き込んでください。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 |
//点灯の長さを決める変数[ms] int Beat_time=125; void setup() { //LEDに繋いだピン(4~11)を出力ピンとして設定する for(int i=4;i<=11;i++){ pinMode(i,OUTPUT); } } void loop() { //11→8、7→4番ピンに繋いだLEDを同じリズムで順番に点灯させる(右向き) for(int j=11;j>=7;j=j-4){ //指定したピンのLEDをBeat_timeの時間点灯させる digitalWrite(j,HIGH); delay(Beat_time); digitalWrite(j,LOW); //点灯の長さ×3待機 delay(Beat_time*3); //指定したピンのLEDをBeat_timeの時間点灯させる digitalWrite(j-1,HIGH); delay(Beat_time); digitalWrite(j-1,LOW); //点灯の長さ×3待機 delay(Beat_time*3); //指定したピンのLEDをBeat_timeの時間点灯させる digitalWrite(j-2,HIGH); delay(Beat_time); digitalWrite(j-2,LOW); //点灯の長さ×1待機 delay(Beat_time*1); //指定したピンのLEDをBeat_timeの時間点灯させる digitalWrite(j-3,HIGH); delay(Beat_time); digitalWrite(j-3,LOW); //点灯の長さ×(12-(3+3+1))待機 delay(Beat_time*(12-(3+3+1))); } } |
上記のスケッチを書き込むと以下の動画のように、決まったリズムでLEDが点灯します。
ここで、LEDが明るすぎると感じた場合は、電気抵抗が高い抵抗器に、LEDが暗すぎると感じた場合は、電気抵抗が低い抵抗器に、変更してみてください。
ただし、電気抵抗が低すぎるもの(大体100[Ω]を下回るくらい)の場合、LEDの寿命を縮めてしまったり、破壊してしまったりすることがあるので注意してください。
また、LEDが点灯しない場合は、回路とスケッチが誤っていないか確認をしてみてください。
スケッチの解説
まず、「void loop()」までの処理の流れは以下のようになります。
ここでは、「for文」を利用して、4~11番のピンを出力ピンとして設定しています。
続いて、「void loop()」の処理の流れは以下のようになります。
こちらも「for文」を利用し、左の4つのLEDと右の4つのLEDを同じリズムで点灯させています。
ここで、ひとまず、LEDの点灯する時間は125[ms]とし、それぞれのLEDが点灯する間隔は、以下の図のような間隔になるように、23、31、39、47行目の「delay(Beat_time*~)」で調節しています。
少々、文が長く複雑に見えますが、LEDを制御する方法と「for文」が分かっていれば、順番に処理を追うことで、スケッチの内容を理解することができると思います。
ここで、LEDを制御する方法や「for文」が良く分からないという方は、以下のリンク先の記事を参考にしてみてください。
関数を利用して読みやすくする
先ほどのスケッチを良く見てみると、同じような処理が何回も繰り返し使われていることに気が付くと思います。
以下の、黄色でマークしている部分が、何回も同じような処理が繰り返し使われている部分になります。
12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 |
void loop() { //11→8、7→4番ピンに繋いだLEDを同じリズムで順番に点灯させる(右向き) for(int j=11;j>=7;j=j-4){ //指定したピンのLEDをBeat_timeの時間点灯させる digitalWrite(j,HIGH); delay(Beat_time); digitalWrite(j,LOW); //点灯の長さ×3待機 delay(Beat_time*3); //指定したピンのLEDをBeat_timeの時間点灯させる digitalWrite(j-1,HIGH); delay(Beat_time); digitalWrite(j-1,LOW); //点灯の長さ×3待機 delay(Beat_time*3); //指定したピンのLEDをBeat_timeの時間点灯させる digitalWrite(j-2,HIGH); delay(Beat_time); digitalWrite(j-2,LOW); //点灯の長さ×1待機 delay(Beat_time*1); //指定したピンのLEDをBeat_timeの時間点灯させる digitalWrite(j-3,HIGH); delay(Beat_time); digitalWrite(j-3,LOW); //点灯の長さ×(12-(3+3+1))待機 delay(Beat_time*(12-(3+3+1))); } } |
上記のように、スケッチ内で同じような処理が繰り返し使われている場合、その処理を関数としてまとめることで、スケッチをコンパクトにし読みやすくすることができます。
以下のスケッチを書き込んでください。
黄色でマークされている部分が変更または追加した部分です。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 |
//点灯の長さを決める変数[ms] int Beat_time=125; void setup() { //LEDに繋いだピン(4~11)を出力ピンとして設定する for(int i=4;i<=11;i++){ pinMode(i,OUTPUT); } } void loop() { //11→8、7→4番ピンに繋いだLEDを同じリズムで順番に点灯させる(右向き) for(int j=11;j>=7;j=j-4){ //指定したピンのLEDをBeat_timeの時間点灯させる関数を呼び出す LED_HIGH(j); //点灯の長さ×3待機 delay(Beat_time*3); //指定したピンのLEDをBeat_timeの時間点灯させる関数を呼び出す LED_HIGH(j-1); //点灯の長さ×3待機 delay(Beat_time*3); //指定したピンのLEDをBeat_timeの時間点灯させる関数を呼び出す LED_HIGH(j-2); //点灯の長さ×1待機 delay(Beat_time*1); //指定したピンのLEDをBeat_timeの時間点灯させる関数を呼び出す LED_HIGH(j-3); //点灯の長さ×(12-(3+3+1))待機 delay(Beat_time*(12-(3+3+1))); } } //**********指定したピンのLEDをBeat_timeの時間点灯させる関数********** void LED_HIGH(int a){ digitalWrite(a,HIGH); delay(Beat_time); digitalWrite(a,LOW); } |
スケッチの解説
「void loop()」の処理の流れは以下のようになります。
上記のスケッチでは、LEDの点灯から消灯までの処理を「LED_HIGH()」という関数としてまとめており、引数にピン番号を指定し関数を呼び出すと、指定したピンのLEDを「Beat_time」で指定した時間だけ点灯させ、その後、消灯させます。
処理の内容は、先程のスケッチと変わりませんが、関数を利用することで、1連の処理がブロック化され、スケッチが読みやすくなっています。
ここで、「Beat_time」は引数ではないのに、なぜ関数内で扱うことができるか疑問に思う人がいらっしゃるかもしれないので、少しだけ説明しておきます。
「Beat_time」という変数を宣言している部分は、スケッチの2行目で、「void setup()」、「void loop()」、「void LED_HIGH()」の外側に記述されています。
このような変数は、グローバル変数と呼ばれ、「void setup()」や「void loop()」、その他の関数内で同一なものとして扱うことが出来ます。
逆に、「void setup()」や「void loop()」、その他の関数内で宣言された変数を、ローカル変数と呼び、それらは、宣言した範囲でしか利用できません。
例えば、先程のスケッチの2行目の文「int Beat_time=125;」を「void setup()」の中で記述した場合、「void LED_HIGH()」内でその変数を利用できないため、コンパイルをする際に「Beat_time」という変数が宣言されていないというエラーが表示されます。
LEDの点灯に合わせてブザーを鳴らす
関数を利用すると、後から少し変更を加えたい場合に、役立つことがあります。
以下のスケッチを書き込んでください。
黄色でマークされている部分が変更または追加した部分です。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 |
//点灯の長さを決める変数[ms] int Beat_time=125; void setup() { //LEDに繋いだピン(4~11)を出力ピンとして設定する for(int i=4;i<=11;i++){ pinMode(i,OUTPUT); } //ブザーに繋いだピン(12)を出力ピンとして設定する pinMode(12,OUTPUT); } void loop() { //11→8、7→4番ピンに繋いだLEDを同じリズムで順番に点灯させる(右向き) for(int j=11;j>=7;j=j-4){ //指定したピンのLEDをBeat_timeの時間点灯させる関数を呼び出す LED_HIGH(j); //点灯の長さ×3待機 delay(Beat_time*3); //指定したピンのLEDをBeat_timeの時間点灯させる関数を呼び出す LED_HIGH(j-1); //点灯の長さ×3待機 delay(Beat_time*3); //指定したピンのLEDをBeat_timeの時間点灯させる関数を呼び出す LED_HIGH(j-2); //点灯の長さ×1待機 delay(Beat_time*1); //指定したピンのLEDをBeat_timeの時間点灯させる関数を呼び出す LED_HIGH(j-3); //点灯の長さ×(12-(3+3+1))待機 delay(Beat_time*(12-(3+3+1))); } } //**********指定したピンのLEDをBeat_timeの時間点灯させる関数(ブザーで音を鳴らす処理あり)********** void LED_HIGH(int a){ digitalWrite(a,HIGH); tone(12,392,Beat_time); //ブザーで音を鳴らす処理を追加 delay(Beat_time); digitalWrite(a,LOW); } |
上記のスケッチを書き込むと以下の動画のように、LEDが点灯するタイミングで音が鳴るのが確認できます。
ここで、ブザーが鳴らない場合は、回路とスケッチが誤っていないか確認をしてみてください。
スケッチの解説
「void loop()」の処理の流れは以下のようになります。
関数を利用していない場合は、ピンの出力設定の他、LEDを点灯させる文の後、全てに、「tone()関数」を追加する必要がありましたが、今回はLEDの点灯から消灯までを関数でまとめているため、関数内に「tone()関数」を1つ追加するだけで、全てのLEDの点灯に合わせてブザーを鳴らすことができます。
ここで、ブザーの鳴らし方、「tone()関数」が良く分からないという方は、以下のリンク先の記事を参考にしてみてください。
関数を利用して色々なリズムで点灯させる
現在のスケッチでは、LEDが点灯するタイミングは、1パターンしかありません。
ここでは、関数を利用して、様々なパターンのリズムでLEDが点灯できるようにする方法を説明したいと思います。
LEDの点灯するタイミングは以下の黄色でマークされた文の数値によって、決められています。
15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 |
void loop() { //11→8、7→4番ピンに繋いだLEDを同じリズムで順番に点灯させる(右向き) for(int j=11;j>=7;j=j-4){ //指定したピンのLEDをBeat_timeの時間点灯させる関数を呼び出す LED_HIGH(j); //点灯の長さ×3待機 delay(Beat_time*3); //指定したピンのLEDをBeat_timeの時間点灯させる関数を呼び出す LED_HIGH(j-1); //点灯の長さ×3待機 delay(Beat_time*3); //指定したピンのLEDをBeat_timeの時間点灯させる関数を呼び出す LED_HIGH(j-2); //点灯の長さ×1待機 delay(Beat_time*1); //指定したピンのLEDをBeat_timeの時間点灯させる関数を呼び出す LED_HIGH(j-3); //点灯の長さ×(12-(3+3+1))待機 delay(Beat_time*(12-(3+3+1))); } } |
ここで、LEDを右に向かって順番に点灯させる処理の部分を丸ごとコピーして増やし、黄色でマークされた文の数値を変更することでパターンを増やしても良いのですが、パターンを増やすたびに、文がどんどん長くなってしまうため、読みづらくなってしまいます。
このような場合は、LEDを右に向かって順番に点灯させる処理の部分を関数でまとめ、さらに、「delay()」の数値を引数にすれば、簡単に別のリズムでLEDを点灯させる処理を増やすことができます。
以下のスケッチを書き込んでください。
黄色でマークされている部分が変更または追加した部分です。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 |
//点灯の長さを決める変数[ms] int Beat_time=125; void setup() { //LEDに繋いだピン(4~11)を出力ピンとして設定する for(int i=4;i<=11;i++){ pinMode(i,OUTPUT); } //ブザーに繋いだピン(12)を出力ピンとして設定する pinMode(12,OUTPUT); } void loop() { //関数を呼び出してLEDを点灯させる LED_Beat_R(3,3,1); LED_Beat_R(1,1,3); LED_Beat_R(3,1,1); } //**********指定したピンのLEDをBeat_timeの時間点灯させる関数(ブザーで音を鳴らす処理あり)********** void LED_HIGH(int a){ digitalWrite(a,HIGH); tone(12,392,Beat_time); //ブザーで音を鳴らす処理を追加 delay(Beat_time); digitalWrite(a,LOW); } //**********11→8、7→4番ピンに繋いだLEDを同じリズムで順番に点灯させる関数(右向き)********** void LED_Beat_R(int delay_1,int delay_2,int delay_3){ for(int j=11;j>=7;j=j-4){ //指定したピンのLEDをBeat_timeの時間点灯させる関数を呼び出す LED_HIGH(j); //点灯の長さ×delay_1 待機 delay(Beat_time*delay_1); //指定したピンのLEDをBeat_timeの時間点灯させる関数を呼び出す LED_HIGH(j-1); //点灯の長さ×delay_2 待機 delay(Beat_time*delay_2); //指定したピンのLEDをBeat_timeの時間点灯させる関数を呼び出す LED_HIGH(j-2); //点灯の長さ×delay_3 待機 delay(Beat_time*delay_3); //指定したピンのLEDをBeat_timeの時間点灯させる関数を呼び出す LED_HIGH(j-3); //点灯の長さ×(12-(delay_1+delay_2+delay_3)) 待機 delay(Beat_time*(12-(delay_1+delay_2+delay_3))); } } |
上記のスケッチを書き込むと以下の動画のように、3つのパターンでLEDが点灯するのが、確認できます。
スケッチの解説
「void loop()」の処理の流れは以下のようになります。
上記のスケッチでは、LEDを右に向かって順番に点灯させる処理を「LED_Beat_R()」という関数にまとめて、「void loop()」内で、引数を渡して呼び出すことにより、3パターンのリズムでLEDを点灯させています。
上記のスケッチはコンパイル時にエラーが無く、動作も正常なため、何の問題もないように思われますが、実は大きな問題があります。
スケッチの問題点
上記のスケッチで問題となるのは、以下の文です。
45 |
delay(Beat_time*(12-(delay_1+delay_2+delay_3))); |
上記の文は、関数の引数の合計が12以上になった場合、「delay()」の( )内がマイナスになってしまい、こちらが意図した動作と異なる動作をしてしまいます。
関数の中身を把握している場合は、引数の合計が12以下になるようにして関数を呼び出すことができますが、時間が空いて関数の中身を忘れてしまった場合や、共同で作業している場合、このようなエラーを発見するのに時間が掛かってしまうことがあります。
よって、関数を呼び出す処理の前に、コメントを残しておき、さらに、引数の合計が12以上になってしまった場合は、右端のLEDを点滅させてエラーを知らせるように、変更を加えます。
以下のスケッチを書き込んでください。
黄色でマークされている部分が変更または追加した部分です。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 |
//点灯の長さを決める変数[ms] int Beat_time=125; void setup() { //LEDに繋いだピン(4~11)を出力ピンとして設定する for(int i=4;i<=11;i++){ pinMode(i,OUTPUT); } //ブザーに繋いだピン(12)を出力ピンとして設定する pinMode(12,OUTPUT); } void loop() { //関数を呼び出しLEDを点灯させる //※LED_Beat_Rの引数の合計は12以下にすること(引数の合計が12より大きい場合右端のLEDが点滅します) LED_Beat_R(3,3,1); LED_Beat_R(1,1,3); LED_Beat_R(3,1,1); LED_Beat_R(6,6,1); } //**********指定したピンのLEDをBeat_timeの時間点灯させる関数(ブザーで音を鳴らす処理あり)********** void LED_HIGH(int a){ digitalWrite(a,HIGH); tone(12,392,Beat_time); //ブザーで音を鳴らす処理を追加 delay(Beat_time); digitalWrite(a,LOW); } //**********11→8、7→4番ピンに繋いだLEDを同じリズムで順番に点灯させる関数(右向き)********** void LED_Beat_R(int delay_1,int delay_2,int delay_3){ //引数の合計が12以下の場合、リズムに合わせてLEDを点灯,それ以外は右端のLEDが点滅 if(delay_1+delay_2+delay_3<=12){ for(int j=11;j>=7;j=j-4){ //指定したピンのLEDをBeat_timeの時間点灯させる関数を呼び出す LED_HIGH(j); //点灯の長さ×delay_1 待機 delay(Beat_time*delay_1); //指定したピンのLEDをBeat_timeの時間点灯させる関数を呼び出す LED_HIGH(j-1); //点灯の長さ×delay_2 待機 delay(Beat_time*delay_2); //指定したピンのLEDをBeat_timeの時間点灯させる関数を呼び出す LED_HIGH(j-2); //点灯の長さ×delay_3 待機 delay(Beat_time*delay_3); //指定したピンのLEDをBeat_timeの時間点灯させる関数を呼び出す LED_HIGH(j-3); //点灯の長さ×(12-(delay_1+delay_2+delay_3)) 待機 delay(Beat_time*(12-(delay_1+delay_2+delay_3))); } }else{ //右端のLEDを点滅させる digitalWrite(4,HIGH); delay(500); digitalWrite(4,LOW); delay(500); } } |
上記のスケッチを書き込むと以下の動画のように、3つのパターンでLEDが点灯した後、右端のLEDが1回点滅するのが、確認できます。
変更後のスケッチの解説
「void loop()」の処理の流れは以下のようになります。
上記のスケッチでは、関数を呼び出す処理の前に、コメントを残して注意を促し、「if文」により、関数の引数が12以下の場合は、LEDを右に向かって順番に点灯させる処理を、それ以外の場合は、右端のLEDを1回点滅させる処理を行っています。
ここで、if文が良く分からないという方は、以下のリンク先の記事を参考にしてみてください。
上記のスケッチでは、3つのパターンでLEDを点灯させた後、わざと引数の合計が12より大きくなるようにして、関数を呼び出しています。
エラーを知らせる動作の確認できましたら、引数が12より大きくなるような関数の呼び出し部分を削除し、8つのパターンでLEDが点灯するように、変更を加えます。
以下のスケッチを書き込んでください。
黄色でマークされている部分が変更または追加した部分です。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 |
//点灯の長さを決める変数[ms] int Beat_time=125; void setup() { //LEDに繋いだピン(4~11)を出力ピンとして設定する for(int i=4;i<=11;i++){ pinMode(i,OUTPUT); } //ブザーに繋いだピン(12)を出力ピンとして設定する pinMode(12,OUTPUT); } void loop() { //関数を呼び出しLEDを点灯させる //※LED_Beat_Rの引数の合計は12以下にすること(引数の合計が12より大きい場合右端のLEDが点滅します) LED_Beat_R(3,3,1); LED_Beat_R(1,1,3); LED_Beat_R(3,1,1); LED_Beat_R(1,5,1); LED_Beat_R(5,3,3); LED_Beat_R(1,5,3); LED_Beat_R(0,5,3); LED_Beat_R(3,3,5); } //**********指定したピンのLEDをBeat_timeの時間点灯させる関数(ブザーで音を鳴らす処理あり)********** void LED_HIGH(int a){ digitalWrite(a,HIGH); tone(12,392,Beat_time); //ブザーで音を鳴らす処理を追加 delay(Beat_time); digitalWrite(a,LOW); } //**********11→8、7→4番ピンに繋いだLEDを同じリズムで順番に点灯させる関数(右向き)********** void LED_Beat_R(int delay_1,int delay_2,int delay_3){ //引数の合計が12以下の場合、リズムに合わせてLEDを点灯,それ以外は右端のLEDが点滅 if(delay_1+delay_2+delay_3<=12){ for(int j=11;j>=7;j=j-4){ //指定したピンのLEDをBeat_timeの時間点灯させる関数を呼び出す LED_HIGH(j); //点灯の長さ×delay_1 待機 delay(Beat_time*delay_1); //指定したピンのLEDをBeat_timeの時間点灯させる関数を呼び出す LED_HIGH(j-1); //点灯の長さ×delay_2 待機 delay(Beat_time*delay_2); //指定したピンのLEDをBeat_timeの時間点灯させる関数を呼び出す LED_HIGH(j-2); //点灯の長さ×delay_3 待機 delay(Beat_time*delay_3); //指定したピンのLEDをBeat_timeの時間点灯させる関数を呼び出す LED_HIGH(j-3); //点灯の長さ×(12-(delay_1+delay_2+delay_3)) 待機 delay(Beat_time*(12-(delay_1+delay_2+delay_3))); } }else{ //右端のLEDを点滅させる digitalWrite(4,HIGH); delay(500); digitalWrite(4,LOW); delay(500); } } |
上記のスケッチを書き込むと、以下の動画のように、8つのパターンでLEDが点灯するのが確認できます。
LEDを使った簡単なゲームの作り方の前編は以上となります。
次回の記事では、スイッチを押したときに関数を実行させる、外部割り込みについての説明を行った後、LEDを使ったゲームの説明の続きをしたいと思います。
本記事はここまでです。ご清覧ありがとうございました。
広告
楽天モーションウィジェットとGoogleアドセンス広告です。
気になる商品がございましたら、チェックをしてみてください。