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

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

本記事で使用するもの

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

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

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

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

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

まずは、シンプルにタクトスイッチが押されたら、それに応じてブザーから音を鳴らすといったことを行いたいと思います。

空のスケッチを書き込む

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

空のスケッチを書き込む

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

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

回路を作成する

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

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

スケッチを書き込む

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

スケッチの解説

定数の宣言

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

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

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

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

定数と変数の違い

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

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

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

ピンの設定

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

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

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 92 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文によって管理しており、ドレミファソラシドの音を各スイッチに割り当てています。

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

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

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

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

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

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

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

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

スケッチを書き込む

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

スケッチの解説

定数と変数の宣言

先程と違いブザーで鳴らす音の振動数、スイッチに繋がっているピンの番号を配列にまとめています。

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

ブザーで鳴らす音の振動数は、「BUZZER_TONES」という配列でまとめています。

配列には、ドレミファソラシドの振動数を、低い音から順番に初期値として格納しています。

BUZZER_TONES[]

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

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

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

スイッチに繋がっているピンの番号は「SWITCH_PINS」という配列でまとめています。

配列は、スイッチに繋がっているピンの番号を、左のスイッチから順番に初期値として格納しています。

SWITCH_PINSj[]

「SWITCH_PINS」を宣言した後、スイッチの数を入れるための定数「NUMBER_OF_SWITCHES」を宣言しています。

配列「SWITCH_PINS」の要素数とスイッチの数は同じであるため、配列の要素数を「sizeof()」という関数を使って計算し「NUMBER_OF_SWITCHES」に格納しています。

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

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

sizeof(SWITCH_PINS)/siseof(SWITCH_PINS[0]

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

ピンの設定

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

「BUZZER_TONES」の要素数は、「NUMBER_OF_SWITCHES」のときと同様にsizeofを利用して計算しています。

sizeof(BUZZER_TONES)/siseof(BUZZER_TONES[0]

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

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

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

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

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

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

ピンを設定するfor文

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

void loop()では、初めに、変数「isSwitchPressed」を宣言し、falseの初期値を格納しています。

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

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

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

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

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

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

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

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

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

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

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

スケッチを書き込む

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

スケッチの解説

定数と変数の宣言

最初に行っている定数と変数の宣言は、先程と同じで、ブザーに繋がっているピンを定数に、ブザーで鳴らす音の振動数、スイッチに繋がっているピンの番号を配列にまとめています。

ピンの設定

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

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

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

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

配列「switchStates」、変数「pressedSwitchCount」を宣言する際、「static」という型修飾子を付けています。

通常void loop内で変数を宣言した場合、void loopの最後に変数は消されるため、ループのたびに、変数を宣言し直します。

しかし、「static」を付けて宣言している場合、void loopの最後に変数は消されず、ループのたびに、変数を宣言し直すことはありません。

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

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

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

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

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

上記の処理中、変数「pressedSwitchCount」の値と配列「switchStates」の中で最も大きい数は一致します。

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

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

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

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

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

終わりに

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

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

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

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

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

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

本記事はここまでです。

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

参考

回路とスケッチを作成する際、以下のサイトを参考にさせていただきました。

ピアノの音階と振動数の関係は、以下のサイトを参考にさせていただきました。

以下のサイトは、オクターブ切り替え用のスイッチを設けた簡易電子ピアノを紹介しており、本記事の楽器を改造する際に参考になると思います。

使用しているスイッチが大きく指への圧力が小さそうなのも良いなと思いました。

広告

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

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

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

<

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

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

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

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

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

私が購入したのは、ブレッドボードとジャンパーワイヤーのみのセットだったのですが、投稿更新時(2025/11/04)に、それは売られていないようだったので、同じメーカーのブレッドボードとジャンパーワイヤーと電源モジュールがセットになったものを広告させていただきます。

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

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

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