こんにちは、「メカのりまき」です。
この記事では、ELEGOO Uno R3(Arduino Uno R3の互換機)、パッシブブザー、ジョイスティック(5ポジションスイッチ)DIP化キットを使って、楽器を作りたいと思います。
目次
本記事で使用するもの
記事を見て、気になるものがあれば、是非チェックをしてみてください。
ジョイスティック(5ポジションスイッチ)DIP化キット
今回の記事では、秋月電子通商で購入したジョイスティック(5ポジションスイッチ)DIP化キットを使って、楽器の製作をしたいと思います。
ジョイスティック(5ポジションスイッチ)DIP化キットは、「SKRHAAE010」という4方向+センタープッシュのスイッチをブレッドボードやユニバーサル基板で使いやすいようDIP化したモジュールです。

スティックを倒したとき、押し込んだときの出力は以下のような感じになります。

モジュールはプルアップ回路になっているため、それぞれ、スイッチがOFFになっているとき対応するピンが「HIGH」になり、スイッチがONになっているとき対応するピンが「LOW」になります。
スティックを押し込みながら、スティックを倒した場合は、SWと倒した方向に対応するピンの2つが「LOW」になります。
斜めには倒すことができないため、方向に対応するピンを同時に「LOW」にすることはできません。
スティックの部分は何も付けずに操作することも可能ですが、スティックが小さく、長時間操作していると指が痛くなるため、今回は以下のようなキャップを3Dプリンタで製作して取り付けています。

楽器の製作
では、先に挙げた動画のように、スティックの操作により、ブザーから音を鳴らす楽器を作りたいと思います。
ここで、スティックの操作とブザーで鳴らす音の関係は、以下のようにしたいと思います。

なお、上記のような音の配置にした理由は、コードを構成する音ごとに配置をまとめることで、アルペジオ(コードを構成する音を順番に弾くこと)がやり易くなると思ったからです。

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

そうすることにより、前に書き込んだスケッチが、新しく作成しようとしている回路に影響を及ぼすことを防げます。
新しい回路を作成するときは、いつもこの作業をやっておくと、安全に回路を作成することができます。
回路を作成する
以下のような回路を作成します。

スケッチを書き込む
回路の作成が終わりましたら、以下のようなスケッチを書き込みます。
|
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 |
/* Arduinoを使って楽器を作る(2,ブザー+5ポジションスイッチ) 2025/11/04 instrument_02 //スケッチの詳細はこちら↓ https://mecha-norimaki.com/arduino_instrument_02/ */ //ブザーに繋がっているピンを指定 const uint8_t BUZZER_PIN = 12; //ブザーで鳴らす音の振動数を配列でまとめている const unsigned int BUZZER_TONES[] = { 262, 294, 330, 349, 392, 440, 494, 523, 587, 659 }; //ジョイスティック(5ポジションスイッチ)DIP化キットに繋がっているピンを指定 const uint8_t SWITCH_PINS[] = { 4, 9, 3, 8, 5, 10, 6, 11, 2, 7 }; //スイッチの数を配列SWITCH_PINSより計算して格納 const uint8_t NUMBER_OF_SWITCHES = sizeof(SWITCH_PINS) / sizeof(SWITCH_PINS[0]); void setup() { //ブザーに繋がっているピンを出力ピンに設定 pinMode(BUZZER_PIN, OUTPUT); //配列BUZZER_TONESの要素数と定数NUMBER_OF_SWITCHESを比べ //一致していなかった場合、エラーを知らせる音を鳴らし空のループを //一致していた場合、スイッチに繋がっているピンをすべて入力ピンとして設定する if (sizeof(BUZZER_TONES) / sizeof(BUZZER_TONES[0]) != NUMBER_OF_SWITCHES) { tone(BUZZER_PIN, 165, 50); delay(100); tone(BUZZER_PIN, 165, 150); while (true) {} } else { for (int i = 0; i < NUMBER_OF_SWITCHES; i++) { pinMode(SWITCH_PINS[i], INPUT); } } } void loop() { //スイッチが何番目にONになっているかを記録する配列を用意 static uint8_t switchStates[NUMBER_OF_SWITCHES]; //同時にONになっているスイッチの数をカウントするための変数 static uint8_t pressedSwitchCount; //スイッチのONになっている数、スイッチがONになった順番を記録 for (int i = 0; i < NUMBER_OF_SWITCHES; i++) { if (digitalRead(SWITCH_PINS[i]) == LOW && switchStates[i] == 0) { pressedSwitchCount++; switchStates[i] = pressedSwitchCount; } else if (digitalRead(SWITCH_PINS[i]) == HIGH && switchStates[i] != 0) { pressedSwitchCount--; for (int j = 0; j < NUMBER_OF_SWITCHES; j++) { if (switchStates[j] > switchStates[i]) { switchStates[j]--; } } switchStates[i] = 0; } } //スイッチがONになっていない時は音を消し、それ以外の場合は最後にONになっているスイッチに応じた音を鳴らす if (pressedSwitchCount == 0) { noTone(BUZZER_PIN); } else { for (int i = 0; i < NUMBER_OF_SWITCHES; i++) { if (switchStates[i] == pressedSwitchCount) { tone(BUZZER_PIN, BUZZER_TONES[i]); break; } } } } |
スケッチの解説
定数と変数の宣言
まず、ブザーに繋がっているピンを、「BUZZER_PIN」という変数に格納しています。
ブザーで鳴らす音の振動数は、「BUZZER_TONES」という配列でまとめています。
配列には、音の振動数を、低い音から順番に初期値として格納しています。
![BUZZER_TONES[]](https://mecha-norimaki.com/wp-content/uploads/2025/11/arduino_instrument_02_07.webp)
参考までに、ピアノの音階と振動数の関係(小数点以下を四捨五入した場合)を以下の表に示します。
| 音階名 | 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 |
ジョイスティック(5ポジションスイッチ)DIP化キットに繋がっているピンの番号は「SWITCH_PINS」という配列でまとめています。
配列は、ジョイスティック(5ポジションスイッチ)DIP化キットに繋がっているピンの番号を、音の配置に対応するように、初期値として格納しています。
![SWITCH_PINS[]](https://mecha-norimaki.com/wp-content/uploads/2025/11/arduino_instrument_02_08-457x1024.webp)
「SWITCH_PINS」を宣言した後、スイッチの数を入れるための定数「NUMBER_OF_SWITCHES」を宣言しています。
配列「SWITCH_PINS」の要素数とスイッチの数は同じであるため、配列の要素数を「sizeof()」という関数を使って計算し「NUMBER_OF_SWITCHES」に格納しています。
「sizeof()」関数は、( )内に記述したデータのサイズをバイト単位で返します。
例えば今回の場合、「sizeof(SWITCH_PINS[0])」は、uint8_t型の変数1個分のサイズである「1」という数値を返し、「sizeof(SWITCH_PINS)」は型の変数10個分のサイズである「10」という値を返します。
![sizeof(SWITCH_PINS)/siseof(SWITCH_PINS[0]](https://mecha-norimaki.com/wp-content/uploads/2025/11/arduino_instrument_02_09.webp)
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]](https://mecha-norimaki.com/wp-content/uploads/2025/11/arduino_instrument_02_10.webp)
上記の計算によって求めた配列「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」にまとめた番号のピンをすべて入力ピンとして設定しています。

スティックの操作に応じて音を鳴らす部分
スイッチが同時にONになっている状態のとき、最後にONになっているスイッチに対応した音が鳴るように工夫をしています。

今回のスケッチでは、スイッチがONになるのを確認して、すぐに音を鳴らすのではなく、まず、スイッチがどの順番でONになっているのかを記録して、その後、最後にONになっているスイッチに対応した音を鳴らすといったスケッチを記述しています。
まず、スイッチがどの順番でONになっているのかを記録する部分は、for文によって、全てのスイッチの状態を確認し、それらの状態を、配列「switchStates」、変数「pressedSwitchCount」にまとめています。
配列「switchStates」、変数「pressedSwitchCount」を宣言する際、「static」という型修飾子を付けています。
通常void loop内で変数を宣言した場合、void loopの最後に変数は消されるため、ループのたびに、変数を宣言し直します。
しかし、「static」を付けて宣言している場合、void loopの最後に変数は消されず、ループのたびに、変数を宣言し直すことはありません。
配列の要素「SWITCH_PINS[i]」に割り当てたスイッチがONになっていて、配列の要素「switchStates[i]」の値が0の状態だった場合は、変数「pressedSwitchCount」の値を1増やして、配列の要素「switchStates[i]」に変数「pressedSwitchCount」の値を格納します。

配列の要素「switchStates[i]」には、変数「pressedSwitchCount」の値が入り、0ではなくなるため、スイッチがONになっている間、ループによって何度もif文の中身が実行されることはありません。
配列の要素「SWITCH_PINS[i]」に割り当てたスイッチがOFFになっていて、配列の要素「switchStates[i]」の値が0以外の場合は、変数「pressedSwitchCount」の値を1減らしたあと、for文により、配列「switchStates」に格納されている値の中で、配列の要素「switchStates[i]」よりも大きい数値の値を1減らし、その後、配列の要素「switchStates[i]」の値を0にします。

上記の処理中、変数「pressedSwitchCount」の値と配列「switchStates」の中で最も大きい数は一致します。
よって、配列「switchStates」の中で、変数「pressedSwitchCount」の値と一致する要素を探せば、最後にONになったスイッチに繋がっているピンが分かります。

音を鳴らす部分については、まず、変数「pressedSwitchCount」が0の場合は、スイッチが1つもONになっていない状態なので、「notone(BUZZER_PIN)」により音を消しています。
それ以外の場合は、for文で変数「pressedSwitchCount」の値と一致する配列の要素「switchStates[i]」を探し、配列の要素「BUZZER_TONES[i]」に割り当てた音を鳴らしています。

終わりに
今回は、ELEGOO Uno R3(Arduino Uno R3の互換機)、パッシブブザー、ジョイスティック(5ポジションスイッチ)DIP化キットを使って楽器を製作しました。
もともと、別の用途で、「SKRHAAE010」を使ってみたいと思い、お試し用に、ジョイスティック(5ポジションスイッチ)DIP化キットを購入しましたが、これを使えば、スティックをグリグリ動かすだけで、良い感じに演奏できる楽器が作れるのではと、ふと思ったので実験をしてみました。
今回は、ブレッドボードで楽器を製作しましたが、ゲームコントローラのような形にすれば、持ちやすそうですし、他の指を使った操作も可能そうなので良いのではないかと思いました。
3Dプリンタで作ったスティックのキャップ部分は、まだまだ、改善の余地がありそうです。
大分先になりそうですが、そのうち気が向いたら、今回の実験を活かして、ゲームコントローラのような形をした楽器を作ってみたいなと思います。
本記事はここまでです。
ご清覧ありがとうございました。
参考
ジョイスティック(5ポジションスイッチ)DIP化キットは、秋月電子通商にて購入しました。
「ジョイスティック(5ポジションスイッチ)DIP化キット」の販売コードは115233です。
- 秋月電子通商のトップページ (最終閲覧2025/11/04)
ピアノの音階と振動数の関係は、以下のサイトを参考にさせていただきました。
- 音階の周波数 [TOM’s Web Site] (最終閲覧2025/11/04)
広告
Arduinoの互換機と電子部品が入ったスターターキットです。
これからArduinoを始めるという方におすすめのキットで、ELEGOO UNO R3、ブザー、ブレッドボード、長いジャンパーワイヤー、USBケーブル(A-B)はこの中に入っていたものを使用しました。
他にも、モータや超音波センサなど、様々な部品が入っていてお得なので、是非、購入を検討してみてください。
私が購入したのは、ブレッドボードとジャンパーワイヤーのみのセットだったのですが、投稿時(2025/11/04)に、それは売られていないようだったので、同じメーカーのブレッドボードとジャンパーワイヤーと電源モジュールがセットになったものを広告させていただきます。
今回はジャンパーワイヤーのみ使用しました。
楽天モーションウィジェットとGoogleアドセンス広告です。
気になる商品がございましたら、チェックをしてみてください。