こんにちは、「メカのりまき」です。

この記事では、ELEGOO Uno R3(Arduino Uno R3の互換機)、タクトスイッチ、パッシブブザーを使って、シンプルな楽器を作りたいと思います。

本記事で使用するもの

本記事で私が使用した物は以下の通りです。

  • 「Arduino IDE2.3.6」をインストールしたPC(Windows11)
  • ELEGOO UNO R3(Arduino Uno R3でも可)
  • USBケーブル(A-B)
  • パッシブブザー
  • タクトスイッチ
  • ブレッドボード
  • ジャンパーワイヤー

今回使用した物は、記事の最後にある広告のところで紹介しています。

記事を見て、気になるものがあれば、是非チェックをしてみてください。

押されているタクトスイッチに応じてブザーの音を鳴らす

今回の記事では、以下の動画のように、タクトスイッチを押すと、それに応じてブザーから音が鳴るというシンプルな楽器を作成したいと思います。

空のスケッチを書き込む

回路を作成する前に、空のスケッチ(「ファイル」タブの「New Sketch」をクリックしたときに出てくる何の処理も書かれていないスケッチ)を書き込みます。

空のスケッチを書き込む

そうすることにより、前に書き込んだスケッチが、新しく作成しようとしている回路に影響を及ぼすことを防げます。

新しい回路を作成するときは、いつもこの作業をやっておくと、安全に回路を作成することができます。

回路を作成する

以下のような回路を作成します。

ブレッドボードを使った回路

スケッチを書き込む

回路の作成が終わりましたら、以下のようなスケッチを書き込みます。

スケッチの解説

定数の宣言

このスケッチでは、まず、「buzzerPin」、「switchPin~」を宣言してそこにブザーとスイッチに繋がっているピンの番号を格納しています。

これにより、ブザーやスイッチを別のピンに変更したいといったときに、宣言している部分の数値を変更するだけで済みます。

今回は変数ではなく定数の宣言なので、頭に「const」という型修飾子を付けて宣言しています。

変数と定数の違いは、動作中に値が変わるか変わらないかの違いであり、定数を宣言しているのに、動作中にその値を変えようとするスケッチを記述するとコンパイル時にエラーが起きます。

定数と変数の違い

よって、動作中に値が変わってほしくないときは、定数で宣言しておくと、安全性が高まりますし、他の人や未来の自分が、頭の「const」を見れば、これは動作中に値の変更を行わないものだと即座に理解することが出来ます。

また、型は「uint8_t」と言う0から255までの整数を扱う型を使っています。

「uint8_t」型は「int」型よりも扱える数値の範囲が狭い代わりに、使用するメモリを少なくすることができます。

今回の場合は、別にメモリを節約する必要はなく、「int」型などで宣言しても全然問題ないのですが、「uint8_t」という型を紹介するために宣言しました。

ピン番号のように、マイナスの値も大きな数値も扱う必要がないような変数、定数は「uint8_t」型の宣言を検討してみてください。

ピンの設定

ブザーに繋げているピンは出力ピンに、スイッチに繋がっているピンはプルアップ付きの入力ピンとして設定しています。

スイッチに繋がっているピンをプルアップ付きの入力ピンにすることによって、抵抗器を用意しなくてもスイッチを利用することができます。

Arduinoでのスイッチの使い方については、以下のリンク先の記事で説明していますので、良く分からないという方は、是非、そちらをご覧ください。

押されているスイッチに応じて音を鳴らす部分

スイッチに繋がっているピンの状態を確認し、そのピンが「LOW」の状態の場合、それに応じた音を鳴らすといったことを行っています。

ピンの状態は、digitalRead()によって確認しており、今回はプルアップ付きの入力ピンを利用しているので、スイッチが押されている時「LOW」、スイッチが押されていない時「HIGH」が返されます。

tone()関数は指定したピンの出力の「HIGH」と「LOW」を、指定した振動数になるように、切り替え続けることが出来る関数で、パッシブブザーで音を鳴らすときによく利用されます。

tone()関数の第1引数はピン番号、第2引数は振動数です。

参考までに、ピアノの音階と振動数の関係(小数点以下を四捨五入した場合)を以下の表に示します。

音階名   1 2 3 4 5 6 7
33 65 131 262 523 1047 2093
ド# 35 69 139 277 554 1109 2217
37 73 147 294 587 1175 2349
レ# 39 78 156 311 622 1245 2489
41 82 165 330 659 1319 2637
ファ 44 87 175 349 698 1397 2794
ファ# 46 93 185 370 740 1480 2960
49 98 196 392 784 1568 3136
ソ# 52 104 208 415 831 1661 3322
55 110 220 440 880 1760 3520
ラ# 58 117 233 466 932 1865 3729
62 123 247 494 988 1976 3951

押されたスイッチと鳴る音の関係は、if文によって管理しており、ドレミファソラシドの音(Cメジャースケール)を各スイッチに割り当てています。

また、第2引数までのtone()関数は、一度実行するとそのまま出力を切り替え続けるので、ブザーが鳴り続けるのですが、notone()関数によって出力を止め、ブザーを止めることができます。

notone()の引数はピン番号です。

今回は、全てのスイッチが押されていない状態のときに、「notone(buzzerPin)」を実行し、音が鳴らないようにしています。

Arduinoでのif文やブザーの使い方については、以下のリンク先の記事で説明していますので、良く分からないという方は、是非、そちらをご覧ください。

配列を利用してカスタマイズしやすいスケッチにする

上記のスケッチでも、楽器として十分な機能を果たしていますが、スイッチの数を変更したい場合、定数の宣言以外にvoid setup()、void loop()内を変更しなければなりません。

また、鳴らす音の変更を行いたい場合は、void loop()内を変更しなければなりません。

ここでは、配列を利用することによって、スイッチの数や音階の変更を行う際に、宣言部分の変更だけで済ませられるようなスケッチを作成したいと思います。

スケッチを書き込む

回路はそのままで以下のようなスケッチを書き込みます。

スケッチの解説

定数と配列と変数の宣言

先程と違いスイッチに繋がっているピンの番号を配列にまとめています。

「配列」というのは同じ型の変数、定数をまとめたようなものです。

今回は、「uint8_t型」の定数を8個分まとめています。

配列の説明

スイッチを押したときに鳴る音の振動数は配列によってまとめています。

配列の要素数は、スイッチの数と同じにしたいため、スイッチの数を格納する定数「numberOfSwitches」を先に用意して、その値を要素数としています。

配列の宣言時、[ ]内に変数を使用するとコンパイル時にエラーが起きますが、定数の場合は、先にその定数が宣言されていれば使用することができます。

また、配列の宣言時、[ ]内で指定した要素数より{ }内で指定した初期値の数が多い場合は、コンパイル時にエラーが起き、[ ]内で指定した要素数より{ }内で指定した初期値の数が少ない場合は、要素の追加が行われます。

配列の要素数指定について

配列の型はint型で、型修飾子は「const」以外に「unsigned」を付けています。

int型に入れられる値は通常-32768から32767の整数ですが、「unsigned」を付けると負の値(マイナスの付いた値)を使用することができなくなり、0から65535の整数を扱うことができるようになります。

今回のように、正の数だけ扱いたいという場合は、「unsigned」という型修飾子を付けることも検討してみてください。

最後にスイッチが押されているか記録するため、bool型の変数「switchIsPressed」を宣言しています。

ピンの設定

void setup()では、初めにブザーに繋がっているピンを出力ピンとして設定した後、配列「switchPins[]」の要素数と変数「numberOfSwitches」を比べ条件分岐を行っています。

配列の要素数は、「sizeof()」という関数を使って計算しています。

「sizeof()」関数は、( )内に記述したデータのサイズをバイト単位で返します。

例えば今回の場合、「sizeof(keySwitches[0])」は、uint8_t型の変数1個分のサイズである「1」という数値を返し、「sizeof(keySwitches)」は型の変数8個分のサイズである「8」という値を返します。

sizeof()関数の説明

sizeof(switchPins) / sizeof(switchPins[0])は、配列全体のサイズを配列のデータ1個分のサイズで割ることになるので、配列にいくつの要素が入っているか計算することができます。

これは、配列の型がuint8_t型で無くても利用できるので、配列の要素数を調べたいときは、是非利用してみてください。

sizeof()関数で配列の要素数を計算

上記の計算によって求めた配列「switchPins[]」の要素数と定数「numberOfSwitches」の値が一致していない場合、配列「cMajorScale[numberOfSwitches]」と配列「switchPins[]」の要素数が異なってしまい、おかしな動作になる可能性があるため、tone()関数によってエラーを知らせる音(ブブーという音)を鳴らした後、while文によって以降の処理が実行されないようにしています。

ここで、tone()関数の第3引数は、出力の切り替えを行う時間([ms]単位)、つまり、音を鳴らしている時間の長さです。

また、while()文というのは、( )内がtrueの場合、{ }内の処理を繰り返し行う文です。

今回は、( )内にtrueを入れているため、ずっと繰り返しの処理が行われます。

そして、今回繰り返しの処理は、空になっており、何も行わないという処理を繰り返すことになります。

配列「switchPins[]」の要素数と変数「numberOfSwitches」の値が一致している場合は、特に問題は無いためfor文を用いて、配列switchPins[]にまとめた番号のピンをすべてプルアップ付きの入力ピンとして設定しています。

ピンを設定するfor文

今回は、定数「numberOfSwitches」を使って、for文を実行していますが、以下のようなfor文でも同じように配列に入っている要素をすべて、プルアップ付き入力ピンに設定することができます。

配列の要素数を他の部分で使うことが無い場合は、上記のような記述でピンの設定などをすると楽です。

押されているスイッチに応じて音を鳴らす部分

void loop()では、初めに、変数「switchIsPressed」をfalseにしています。

その後for文を用いて、配列の要素「switchPins[i]」に割り当てたスイッチが押されているかを確認し、押されていれば、配列の要素「cMajorScale[i]」の振動数でtone()関数を実行し音を鳴らします。

ここで、音を鳴らした後は、「switchIsPressed」をtrueにし、「break」によってfor文から抜け出します。

もし、スイッチが1つも押されていない場合は、「switchIsPressed」はfalseのままになるので、「if(!switchIsPressed)」の条件が満たされ、「noTone(buzzerPin)」により、音を消します。

「!switchIsPressed」の ! (エクスクラメーションマーク)は、trueとfalseをひっくり返す役割があり、bool型を使う際は、便利でよく使われます。

鳴らす音の優先順位を考える

今回の楽器は、tone関数を利用して、1つのブザーを鳴らしており、複数の音を同時に鳴らすことはできません。

では、スイッチが同時に押された場合、どの音が鳴るのか考えてみようと思います。

先程までのスケッチでは、スイッチが同時に押された時、左側のスイッチに割り当てられた音が優先して鳴ります。

以下は、上記のスケッチで同時押しをしたときの動画です。

これは、if文の条件が満たされた場合、その中で対応している音を鳴らし、その後のif文はelseやbreakなどによって無視するといったスケッチを書いているからです。

上記の仕様では同時押しをしたときに、少し違和感があるので、以下の動画のように、後から押された音を優先して鳴らすように変更してみたいと思います。

スケッチを書き込む

回路はそのままで以下のようなスケッチを書き込みます。

スケッチの解説

定数と配列と変数の宣言

今回のスケッチでは、スイッチが押されているか記録する変数「switchIsPressed」を無くし、新たに、同時に押されているスイッチの数をカウントするための変数「pressedSwitches」と、各スイッチが何番目に押されているかを記録する配列「statusOfSwitches[numberOfSwitches]」を用意しています。

変数と配列の要素の値はデフォルトですべて0です。

ピンの設定

ピンの設定は先程と同じで、ブザーに繋がっているピンを出力ピンに設定した後、配列「switchPins[]」の要素数と定数「numberOfSwitches」の値を比べ、一致していなかった場合、エラーを知らせる音を鳴らした後、空のループで止め、一致していた場合、スイッチに繋がっているピンをすべてプルアップ付きの入力ピンとして設定しています。

押されているスイッチに応じて音を鳴らす部分

今回のスケッチでは、スイッチが押されているかを確認して、すぐに音を鳴らすのではなく、まず、スイッチがどの順番で押されているのかを確認して、その後、最後に押されているスイッチに対応した音を鳴らすといったスケッチを記述しています。

まず、スイッチがどの順番で押されているのかを確認する部分は、for文によって、全てのスイッチの状態を確認し、それらの状態を変数「pressedSwitches」、配列「statusOfSwitches[]」にまとめています。

もし、配列の要素「switchPins[i]」に割り当てたスイッチが押されていて、配列の要素「statusOfSwitches[i]」の値が0の状態だった場合は、変数「pressedSwitches」の値を1増やして、配列の要素「statusOfSwitches[i]」に変数「pressedSwitches」の値を格納します。

スイッチが押されたときの処理

配列の要素「statusOfSwitches[i]」には、変数「pressedSwitches」の値が入り、0ではなくなるため、スイッチが押されている間、ループによって何度もif文の中身が実行されることはありません。

配列の要素「switchPins[i]」に割り当てたスイッチが離されていて、配列の要素「statusOfSwitches[i]」の値が0以外の場合は、変数「pressedSwitches」の値を1減らしたあと、for文により、配列「statusOfSwitches[]」に格納されている値の中で、配列の要素「statusOfSwitches[i]」よりも大きい数値の値を1減らし、その後、配列の要素「statusOfSwitches[i]」の値を0にします。

スイッチが離されたときの処理

ここで、配列「statusOfSwitches[]」に格納されている値の中で、配列の要素「statusOfSwitches[i]」よりも大きい数値の値を1減らしているのは、変数「pressedSwitches」の値と配列「statusOfSwitches[]」の中で最も大きい数を一致させるためです。

変数pressedSwitchesの値と配列statusOfSwitches[]の中で最も大きい数を一致させる工夫

そうすることによって、配列「statusOfSwitches[]」の中で、変数「pressedSwitches」の値と一致する要素を探せば、最後に押されたスイッチに繋がっているピンが分かります。

最後に押されたスイッチを探す方法

音を鳴らす部分については、まず、変数「pressedSwitches」が0の場合は、スイッチが1つも押されていない状態なので、「notone(buzzerPin)」により音を消しています。

それ以外の場合は、for文で変数「pressedSwitches」の値と一致する配列の要素「statusOfSwitches[i]」を探し、配列の要素「cMajorScale[i]」に割り当てた音を鳴らしています。

最後に押されたスイッチに割り当てた音を鳴らす

終わりに

皆さん、上手く音を鳴らすことができたでしょうか。

最初は比較的簡単なスケッチでしたが、後半は少し難しかったかもしれません。

今回の記事では、ELEGOO Uno R3(Arduino Uno R3の互換機)、タクトスイッチ、パッシブブザーを使ってシンプルな楽器を作成しました。

とてもシンプルなので、最初は楽しくてもすぐに飽きてしまうかもしれません。

そのようなときは、今回作成したものを改造してみるのも良いですし、Arduinoはタクトスイッチ、ブザー以外にも、様々な入力、出力が可能なので、色々な方法で音を鳴らす手段を考えてみると面白いかもしれません。

是非、色々試してみてください。

本記事はここまでです。

ご清覧ありがとうございました。

広告

Arduinoの互換機と電子部品が入ったスターターキットです。

これからArduinoを始めるという方におすすめのキットで、ELEGOO UNO R3、ブザー、ブレッドボード、長いジャンパーワイヤー、USBケーブル(A-B)はこの中に入っていたものを使用しました。

他にも、モータや超音波センサなど、様々な部品が入っていてお得なので、是非、購入を検討してみてください。

タクトスイッチは以下の製品を使用しましたが、投稿時(2025/06/20)は在庫切れの状態になっていました。

私が購入した際は、1,399円(スイッチ一つあたり8.74円)と安くておすすめだったのですが、しばらく在庫切れの状態が続いているみたいなので、購入できない場合や価格が大きく上がった場合は他のタクトスイッチの購入を検討してみてください。

カラフルなタクトスイッチは、秋月電子通商などでも、安く売られているので、そちらもおすすめです。

秋月電子通商ではたくさんの電子部品が売られているので、色々と見てからまとめ買いすることをおすすめします。

秋月電子通商トップページへのリンク

コンパクトなブレッドボードとジャンパーワイヤーのセットです。

今回はジャンパーワイヤーのみ使用しました。

純正のArduino Uno R3です。

価格の安さを求めるのであれば、互換機(ある程度情報が得られるもの)がおすすめですが、安心を求めるのであれば純正品の購入がおすすめです。

楽天モーションウィジェットとGoogleアドセンス広告です。

気になる商品がございましたら、チェックをしてみてください。