自由研究向け!初めての電子工作とプログラミング「Arduino で作る音楽ルーレット」

Arduino Uno を用いた電子工作の基礎知識や開発フローをまとめます。

また、初心者向けの音楽ルーレットを作ります。

小中学生の自由研究や制作課題の参考になればと思います。

小学生向けロボット・プログラミング教室
  • エジソンアカデミー
    国内最大級の子供向けロボットプログラミング教室であり、全国 45 都道府県に約 900 教室を展開しています。国際大会(URC)に出場することもできます。
  • ヒューマンアカデミー
    ロボット電話「RoBoHoN」や、世界初の対話できるロボット宇宙飛行士「KIROBO」(トヨタ自動車 & 電通と共同開発)の開発者である、話題のロボットクリエイター「高橋智隆」先生が監修した教材によるカリキュラムやイベントが用意されています。

Arduino の基本

Arduino とは

Arduino(アルデュイーノ)は、有名なマイコンボードの 1 つです。

マイコンボードとは、コンピュータシステムが集積された半導体チップであり、プリント基板上にマイクロコントローラチップと、そのチップの動作に必要な機能が搭載されています。

PC と比較すると、機能は最低限であり性能も低いですが、コンパクトで安価です。

マイクロコントローラは、家電や携帯電話、AV 機器、車など、多くの電子機器やマシンに組み込まれています。

マイコンボードの基本部品は、下記の通りです。

  • CPU
  • プログラムの格納用メモリ
  • A/D コンバータ
  • タイマー
  • 外部機器と通信する入出力部

多くの Arduino には、Atmega 328 というマイクロコントローラチップが使用されています。

また、Arduino には、数十種類の型やシリーズがあります。

ここでは、最も初心者向きで有名な入門機である Arduino Uno を利用します。

Arduino Uno

下記は、Arduino Uno の概要図です。

Arduino Uno 概要図

主に利用する接続端子やピン、ボタンの概要は下表の通りです。

接続端子 / ピン / ボタン 用途 番号
DIGITAL(PWM~) デジタル入出力 0 ~13 14
アナログ出力(PWM) 3 / 5 / 6 / 9 ~ 11 6
ANALOG IN アナログ入力 A0 ~ A5 5
GND 接地用端子 GND 出力用 : 1 電源用 : 2
RESET (赤色ボタン) プログラムの実行リセット RESET 1
USB (銀色ボックス) PC 接続用 USB 端子 (プログラム転送 / 電力供給) 1

開発を行う上で、ひとまず押さえておくべき仕様は下記の通りです。

DIGITAL(PWM~)

デジタル入出力ピン

デジタル値(H / L レベルの信号)の入出力に使用します。

入出力のどちらのピンとして使用するかは、プログラムにおいて指定することができます。

スイッチにおける ON/OFF の読み取りや、LED の点灯や消灯を行う際に使用します。

アナログ出力ピン(PWM)

14 本のデジタル入出力ピンのうち 6 本は、PWM(Pulse Width Modulation)による擬似アナログ出力ピンとしても使用することができます。

PWM とは H レベルと L レベルの信号を交互に高速で出力することにより、平均の中間値における電圧を作り出す仕組みです。

プログラムの中で指定することで利用可能です。

LED の明るさやモーターの回転速度を制御する際に使用します。

ANALOG IN

アナログ値を入力する際に使用します。

各種センサからの入力電圧は、基盤上の A/Dコンバータにより 0 ~ 1023 の数値に変換されます。

A/D コンバータとは、電圧などのアナログ信号をデジタル信号に変換する機器です。

ボードへの電力供給

Arduino Uno は、AC アダプタ以外にも、PC の USB ポートから電源を取ることができます。

AC アダプタ用の端子に電源が接続されている場合は、その電源を利用し、そうでない場合は、USB からの電源を使用します。


その他、Arduino Uno の仕様は、公式ページにて確認することができます。

必要に応じて参照します。

開発方法

専用の統合開発環境である「Arduino IDE」を用いて開発します。

Windows または Mac の場合も同様に開発可能です。

開発フロー

大まかな開発フローは、下記の通りです。

スケッチの作成

Arduino IDE のテキストエディタにて、プログラムを作成します。

Arduino ボード上で動作するプログラムは、スケッチと呼ばれます。

ファイルの拡張子は、*.inoです。

C/C++ に似たシンプルな言語を用いて記述します。

スケッチには、setup 関数と loop 関数と 2 つの関数があり、それぞれを必ず利用します。

関数 概要
setup ・ボードにおける端子への入出力モードの割り当てや、初期化などの設定を定義
・スケッチの動作開始時に一度だけ実行
loop 繰り返し実行されるメインの処理を定義

通常のプログラミングと同様に、独自に関数を定義し、loop 関数内で呼び出すことができます。

そのため、全ての処理を loop 関数に記述する必要はありません。

また、センサや電圧の入出力などの操作は、開発環境に用意されている関数群を利用し、組み合わせることで定義します。

関数の詳細は、公式リファレンスにて確認することができます。

検証(コンパイル)

スケッチの作成後、IDE 上で検証(コンパイル)を行います。

コンパイルエラーがある場合、メッセージに従いスケッチを修正します。

回路の作成

スケッチに対応する回路を作成します。

各種コンポーネント(センサ、LED、スイッチ、モータなど)を、ブレッドボードに設置し、ジャンプワイヤ抵抗を用いて配線します。

一般的には、回路図エディタを用いて実態配線図を事前に作成し、設計図に従い配線します。

代表的なエディタには、Fritzing があります。

ボードへのアップロード

Arduino IDE でコンパイルしたスケッチは、PC ではなく、Arduino ボード上で実行します。

そのため、USB で PC と接続し、ボードにアップロードする必要があります。

IDE 上のボタンをクリックして転送します。

スケッチの生存期間

アップロードされたスケッチは、別のスケッチがアップロードされるまでボード上に残ります。

リセットや電源のオフによって消えることはありません。

スケッチの実行

ボードの電源を入れると、アップロードされたスケッチの実行が開始されます。

処理を停止させる場合は、電源をオフにします。

ただし、スイッチやセンサ等を用いたトリガによる処理の ON/OFF 操作や条件分岐は可能です。

実行時の注意点

一般的なプログラムと異なり、同時に複数のスケッチを実行することはできません。

また、基本的には、自身で停止させることはできません。

以上が、Arduino IDE を持ちた開発フローです。

「スケッチの作成」と「回路の作成」と、どちらを先に完成させるか、あるいは平行して進めるかは任意です。

ここでは、説明の便宜上、前者を先に説明しました。

Arduino IDE のインストール

公式サイトからソフトウェアをダウンロードし、セットアップを行います。

投稿時点の最新バージョンは、1.8.19 です。

Windows 向けには、インストーラ版と、zip 版(実行可能ファイルと参照ファイルが同梱)とがありますが、ここでは前者を利用します。

インストーラのダウンロード
  1. 「公式サイト」へアクセス
  2. Windows Win 7 and newer を選択
  3. JUST DOWNLOAD をクリック(自動ダウンロード開始)
Windows Win 7 and newer を選択
JUST DOWNLOAD をクリック(自動ダウンロード開始)
インストーラの実行
  1. ダウンロードした arduino-1.8.19-windows.exe をダブルクリックして実行
  2. 利用規約の承諾(I Agree をクリック)
  3. Browse… からインストール先フォルダを設定(初期設定は C:\Program Files (x86)\Arduino
  4. Install をクリック
  5. インストールの完了後に Close をクリック
利用規約の承諾(I Agreeをクリック)
オプションに全てチェックのうえNEXTをクリック
オプションに全てチェックのうえNEXTをクリック
インストール先フォルダを設定のうえInstallをクリック
インストールの完了後にCloseをクリック
Arduino IDE の起動
  1. インストール先フォルダへ移動(初期設定の場合は C:\Program Files (x86)\Arduino
  2. arduino.exe をダブルクリックで起動
  3. アイコンのオプションからタスクバーにピン留め(任意)

Arduino IDE のインストールとセットアップは以上です。

Mac 向けには、アプリケーションファイルが同梱された zip が用意されています。

zip のダウンロード
  1. 「公式サイト」へアクセス
  2. Mac OS X 10.10 or newer を選択
  3. JUST DOWNLOAD をクリック(自動ダウンロード開始)
Mac OS X 10.10 or newer を選択
JUST DOWNLOAD をクリック(自動ダウンロード開始)
zip ファイルの解凍
  1. ダウンロードした arduino-1.8.19-macosx.zip をダブルクリックして解凍
  2. 展開された Arduino.app をアプリケーションフォルダへ移動
Arduino IDE の起動
  1. アプリケーションフォルダへ移動
  2. Arduino.app をダブルクリックで起動
  3. アイコンのオプションから Dock に追加(任意)

Arduino IDE のインストールとセットアップは以上です。

デモンストレーション

練習も兼ねて、簡単なスケッチと回路を作成します。

赤色 LDE を 1 秒間隔で点滅させる制御を実装します。

ここでは、Windows を用いますが、Mac の場合も同様です。

事前準備
Arduino IDE の起動とファイル名の変更

インストールした Arduino IDE を起動します。

起動すると、日付に基づく初期設定名が付いたスケッチが表示されます。

「ファイル」の「名前を付けて保存」から、編集するスケッチの名前を変更し、任意のフォルダに保存します。

Windows の場合、Ctrl + Shift + Sのキーバインドもあります。

名前を付けて保存

ここでは、flash と名前を付けました。

このとき、同じ名前のフォルダが自動作成され、その中に flash.ino というファイルが保存されていることを確認できます。

スケッチの作成

下記コードを Arduino IDE に記述します。

#define LED 13 // 定数を定義し、13 番ピンを設定

void setup( ) {
    pinMode( LED, OUTPUT ); // LED ピンの Pin Mode を出力に設定
}

void loop( ) {
    digitalWrite( LED, HIGH ); // LED ピンに H レベルの電圧を入力
    delay( 1000 ); // 処理を 1000 ミリ秒停止
    digitalWrite( LED, LOW ); // LED ピンに L レベルの電圧を入力
    delay( 1000 ); // 処理を 1000 ミリ秒停止
}

pinModedigitalWritedelay は、Arduino で事前に定義されている関数です。

引数や戻り値などの関数の仕様は、前述の「公式リファレンス」にて確認できます。

また、INPUT と OUTPUT も、Arduino で定義されている定数です。

C 言語と同様に、#define を用いることで、独自の定数を宣言できます。

任意のテキストエディタによる編集

Arduino IDE のテキストエディタでは、コーディングが不便に感じるシーンが多々あります。

そのため、使い慣れたエディタでスケッチを開き、開発を行うと快適です。

代表的なテキストエディタの 1 つに、Visual Studio Code があります。

検証(コンパイル)

Arduino IDE 上で検証(コンパイル)を行います。

画面左上のチェックマークをクリックすることで開始します。

検証開始
コンパイル中
コンパイル完了

検証結果は、画面下のウィンドウに表示されます。

エラーがある場合はスケッチの修正を行い、コンパイルが無事完了した場合は、ボードへアップロードすることができます。

回路の作成

下記の実態配線図に従い、スケッチに対応する回路を作成します。

赤色 LED のアノード(A: 正極)側を、Arduino Uno の 13 番ピンに、カソード(K: 負極)側を GND に接続します。

一般的に、端子の長い方がアノードで、短い方がカソードです。

LED の接続の際は、抵抗(330 Ω 程度)を使用します。

flash の実態配線図
ブレッドボードの内部接続

「A – E」ブロックおよび「F – J」ブロックのソケット(穴)は、縦に繋がっています。

一方、赤線と青線で囲まれたブロックのソケットは、横に繋がっています。

ボードへのアップロード

コンパイル済のスケッチを Arduino ボードにアップロードします。

PC とボードを USB で接続し、右矢印のボタンをクリックします。

ボードへのアップロード
PC 接続時の注意点
  • 「ツール」の「マイコンボード」から適切なボードの種類が選択されていることを確認
  • 「ツール」の「シリアルポート」から接続されているポート番号が正しく設定されていることを確認(Windows の場合は COM1 など)
スケッチの実行

ここでは、Arduino Uno の電源は、USB 接続している PC から供給されます。

そのため、スケッチのアップロード直後に実行が開始されます。

赤色 LED が 1 秒間隔で点滅することを確認できれば、スケッチおよび回路が正しく実装できています。

以上が、簡単な制御を例題とした Arduino Uno による開発のデモンストレーションになります。

音楽ルーレットの制作

制作する音楽ルーレットの仕様と、必要なスケッチおよび回路をまとめます。

仕様

タクトスイッチを押してスタートさせ、当選すると、効果音または BGM が流れます。

赤色 LED の点滅でルーレットを表現し、回転に合わせて音が出ます。

音は圧電スピーカーから出力させます。

ポテンショメータ(可変抵抗)を調整することにより、ルーレットの当選内容を 5 段階に変更可能で、グレードはフルカラー LED の色を見て確認することができます。

消灯、青色、緑色、赤色、白色 の順に当選確率が上昇し、当選内容もグレードアップさせ、異なる効果音または曲が流れるようになります。

効果音を 6 つと、BGM を 2 つ制作しました。

用途と関数名は下表の通りです。

効果音 / BGM 概要 関数名
ハズレ ハズレの効果音 miss
ポテト ポテトが揚がった時の効果音 mc
スウィープ ロックギターのような派手な効果音 sweep
コインゲット コインをゲットした時の効果音
coinGet
ライフアップ ライフアップをイメージした効果音
lifeUp
コインゲットラッシュ 「ライフアップ」の中で利用する効果音
coinGetRush
アンダーグラウンド マリオが地下に潜った時の BGM
underWorld
パイレーツ パイレーツオブカリビアン風の BGM
pirates

「ライフアップ」は「コインゲット」の当選を 6 回繰り返した際に流れる「隠し BGM」です。

効果音や BGM のリズムに合わせ、ルーレットを鮮やかに点滅させる点灯パターンも実装しました。

スケッチ

制御フローの概要は、下記の通りです。

  1. 乱数の種を変更
  2. ポテンショメータ(可変抵抗)の値を取得し、フルカラー LED の光を変更
  3. タクトスイッチが押されたか?(YES : 4 へ / NO : 1 へ)
  4. 乱数を取得し、当選ピンを決定
  5. ルーレットの演出実行と当選発表
  6. 当選結果に基づく演出実行

上記のプロセスを実装します。

下記ファイルに分割して作成しました。

ファイル 概要
musicRoulette.ino ・メインスケッチ
・setup 関数と loop 関数を定義した制御フローの骨格
mrlib.ino ・サブスケッチ
・制御に必要な各種関数を独自定義
mrlib.h ・ヘッダファイル(メインスケッチでインクルード)
・定数や変数の定義とプロトタイプ宣言

また、loop 関数にて制御フローを記述し、利用する関数はサブスケッチから呼び出します。

/*
	制御に必要な各種関数を独自定義したヘッダファイルをインクルード
*/

#include "mrlib.h"

/* 
	setup 関数
*/

void setup() {
	
	// 圧電スピーカーピンの Pin Mode を出力に設定
	pinMode( SPEAKER, OUTPUT );

	// フルカラー LED における各ピンの Pin Mode を出力に設定
	pinMode( LED_R, OUTPUT );
	pinMode( LED_G, OUTPUT );
	pinMode( LED_B, OUTPUT );

	// ルーレットを構成する赤色 LED における各ピン(2 ~ 7番)の Pin Mode を出力に設定
	for( int i = 2; i <= 7; i++ ) {
		pinMode( i, OUTPUT );
	}
}

/* 
	loop 関数
*/

void loop() {

	// 未接続のアナログ入力ピンのノイズを利用して乱数を初期化
	randomSeed( analogRead( 0 ) );
	
	int grade; // ポテンショメータの値
	
	// タクトスイッチが OFF の場合は待機(プルアップ回路)
	while ( digitalRead( 13 ) == HIGH ){
		grade = analogRead( 1 ); // ポテンショメータの値を取得
		showGrade( grade ); // フルカラー LED の発光色を調整
	}
	showGradeOff(); // 消灯
	
	digitalWrite( hit, LOW );  // 当選 LED を消灯
	grade = analogRead( 1 ); // ポテンショメータを取得
	
	int resultOfRand = random( 0, 5 + 1 ); // random( min, max ) の戻り値は min to max-1
	hit = resultOfRand + 2; // ピン番号に調整(赤色 LED のピン番号は 2 ~ 7)

	// ポテンショメータの値と当選ピンに基づく演出実行
	play( grade, hit );
}

ヘッダファイルである mrlib.h にて、各種ピンへ定数の割り当てや変数の定義、関数のプロトタイプ宣言を行います。

また、サブスケッチである mrlib.ino にて、各機能の処理を記述した関数群を定義します。

mrlib.h
/* 音符の定義 */

#define NOTE_D3 147
#define NOTE_DS3 156
#define NOTE_E3 165
#define NOTE_F3 175
#define NOTE_G3 196
#define NOTE_GS3 208
#define NOTE_A3 220
#define NOTE_AS3 233
#define NOTE_B3 247

#define NOTE_C4 262 // 鍵盤の真ん中のドを基準に C4 と表記
#define NOTE_CS4 277
#define NOTE_D4 294
#define NOTE_DS4 311
#define NOTE_E4 330
#define NOTE_F4 349
#define NOTE_FS4 370
#define NOTE_G4 392
#define NOTE_GS4 415
#define NOTE_A4 440
#define NOTE_AS4 466
#define NOTE_B4 494

#define NOTE_C5 523
#define NOTE_D5 587
#define NOTE_E5 659
#define NOTE_F5 698
#define NOTE_G5 784
#define NOTE_A5 880
#define NOTE_B5 988

/* フルカラー LED のピンへの割り当て */
#define LED_R 10
#define LED_G 9
#define LED_B 8

/* 圧電スピーカーのピンへの割り当て */
#define SPEAKER 12

/* 当選 LED ピンの番号 */
int hit;

/* コインの総獲得枚数 */
int totalCoin = 1;

/* ポテンショメータの値と当選ピンに基づく演出実行 */
void play( int grade, int hit );

/* ルーレットの演出(点滅と音の出力)*/
void roulette();

/* 当選発表 */
void announceHit( int hit );

/* 当選セット */
void hitSet_bad(); // 最低グレード
void hitSet_standard(); // 標準グレード
void hitSet_middle(); // 中級グレード
void hitSet_super(); // 上級グレード
void hitSet_ultra(); // 最上級グレード

/* 効果音 */
void miss(); // ハズレ
void potato(); // ポテト
void sweep(); // スウィープ
void coinGet(); // コインゲット
void lifeUp() ; // ライフアップ
void flash( int num ); // 赤色 LED の点滅(効果音に合わせて利用)

/* BGM */
void pirates(); // パイレーツ
void flashPirates( int &num ); // パイレーツの BGM に合わせた赤 LED の点滅
void underWorld(); // アンダーワールド
void buzz( long frequency, long length ); // 音符の出力(アンダーワールドにて利用)

/* フルカラー LED の操作 */
void showGrade( int grade ); // 色を調整
void showGradeOff(); // 消灯

/* 全赤色 LED を消灯 */
void flashOff();
mrlib.ino
/*
	ポテンショメータの値と当選ピンに基づく演出実行
*/

void play( int grade, int hit ) {
  
	roulette();
	announceHit( hit );
	
	if ( grade >= 1000 ) hitSet_ultra();
	else if ( grade >= 800 ) hitSet_super();
	else if ( grade >= 500 ) hitSet_middle();
	else if ( grade >= 200 ) hitSet_standard();
	else hitSet_bad();
}

/* 
	ルーレットの演出(点滅と音の出力)
*/

// void tone( pin, frequency ) : pin へ frequency(Hz)に指定した周波数で電圧を印加

void roulette() {
  
	for ( int i = 0; i < 3; i++ ) { // 3 周
		for ( int j = 2; j <= 7; j++ ) {
			tone( SPEAKER, 800 );
			digitalWrite( j , HIGH );
			delay( 40 );
			noTone( SPEAKER ); // 印加を停止
			digitalWrite( j , LOW );
			delay( 40 );
		}
	}
}

/*
	当選発表
*/

void announceHit( int hit ) {

	// 当選ピン番号
	unsigned int i;

	// 当選ピンまでスローで点滅しながら移動(音声付き)
	for ( i = 2; i < hit; i++ ) {
		tone( SPEAKER, 700 );
		digitalWrite( i , HIGH );
		delay( 350 );
		noTone( SPEAKER );
		digitalWrite( i , LOW );
		delay( 350 );
	}

	// 当選ピンで 2 回点滅(音声付き)
	tone( SPEAKER, 900 );
	digitalWrite( hit, HIGH );
	delay( 100 );
	noTone( SPEAKER );
	digitalWrite( hit, LOW );
	delay( 100 );
	
	tone( SPEAKER, 900 );
	digitalWrite( i, HIGH );
	delay( 100 );
	noTone( SPEAKER );
	digitalWrite( i, LOW );
	delay( 100 );
	
	digitalWrite( i, HIGH );
	delay( 100 );
}

/*
	最低グレードの当選セット
*/

void hitSet_bad() {
	
	if( hit == 2 || hit == 4 || hit == 5 || hit == 6 || hit == 7 ) miss();
	if( hit == 3 ) coinGet();
}

/*
	標準グレードの当選セット
*/

void hitSet_standard() {
	
	if( hit == 3 || hit == 5 ) miss();
	if( hit == 2 || hit == 4 ) coinGet();
	if( hit == 6 ) sweep();
	if( hit == 7 ) potato();
}

/*
	中級グレードの当選セット
*/

void hitSet_middle() {
	
	if( hit == 4 || hit == 6 ) miss();
	if( hit == 2 ) coinGet();
	if( hit == 5 ) sweep();
	if( hit == 3 || hit == 7 ) potato();
}

/*
	上級グレードの当選セット
*/

void hitSet_super() {
	
	if( hit == 5 ) miss();
	if( hit == 6 ) coinGet();
	if( hit == 7 ) sweep();
	if( hit == 2 ) lifeUp();
	if( hit == 3 ) underWorld();
	if( hit == 4 ) pirates();
}

/*
	最上級グレードの当選セット
*/

void hitSet_ultra() {
	
	if( hit == 2 || hit == 4 || hit == 6 ) underWorld();
	if( hit == 3 || hit == 5 || hit == 7 ) pirates();
}


/*
	ハズレの効果音
*/

void miss() {
	
	tone( SPEAKER, 300, 50 );
	delay( 200 );
	tone( SPEAKER, 300, 500 );
}

/*
	ポテトの効果音
*/

void potato() {
	
	int count = 0;
	for ( int i = 0; i < 5; i++ ) {
		
		tone( SPEAKER, 785 );
		flash( count++ );
		delay( 180 );
		noTone( SPEAKER );
		flashOff();
		delay( 60 );

		tone( SPEAKER, 698 );
		flash( count++ );
		delay( 180 );
		noTone( SPEAKER );
		delay( 60 );

		tone( SPEAKER, 784 );
		flash( count++ );
		delay( 180 );
		noTone( SPEAKER );
		flashOff();
		delay( 60 );

		delay( 190 );
	}
}

/*
	スウィープの効果音
*/

void sweep() {
	
	for ( int i = 0; i < 3; i++ ) {
		int count = 2;
		for ( int j = 300; j <= 800; j += 100 ) {
			tone( SPEAKER, j );
			digitalWrite( count++, HIGH );
			delay( 50 );
			noTone( SPEAKER );
		}
		flashOff();
		delay( 5 );
		for ( int k = 800; k >= 300; k -= 100 ) {
			tone( SPEAKER, k );
			digitalWrite( --count, HIGH );
			delay( 50 );
			noTone( SPEAKER );
			digitalWrite( count, LOW );
		}
	}
}

/*
	コインゲットの効果音
*/

void coinGet() {
	
	digitalWrite( hit, LOW );
	
	for ( int got = 2; got <= totalCoin + 1; got++ ) digitalWrite( got, HIGH );
	
	for ( int i = 0; i < 250; i++ ) {
		digitalWrite( SPEAKER, HIGH ) ;
		delayMicroseconds( 253 );
		digitalWrite( SPEAKER, LOW );
		delayMicroseconds( 253 );
	}
  
	for ( int j = 0; j < 800; j++ ) {
		digitalWrite( SPEAKER, HIGH );
		delayMicroseconds( 189 );
		digitalWrite( SPEAKER, LOW );
		delayMicroseconds( 189 );
	}
	digitalWrite( totalCoin + 1 , HIGH );
	totalCoin++;
	
	if ( totalCoin == 7 ) {
		coinGetRush( 6 );
		lifeUp();
		totalCoin = 1;
	}
	
	delay( 1000 );
	flashOff();
}

/*
	コインゲットラッシュの効果音
*/

void coinGetRush( int n ) {
	
	flashOff();
	int count = 2;
	for ( int i = 0; i < n - 1; i++ ) {
		for ( int j = 0; j < 250; j++ ) {
			digitalWrite( SPEAKER, HIGH );
			delayMicroseconds( 253 );
			digitalWrite( SPEAKER, LOW );
			delayMicroseconds( 253 );
		}
		digitalWrite( count++, HIGH );
		delay( 5 );
	}
	
	for ( int i = 0; i < 250; i++ ) {
		digitalWrite( SPEAKER, HIGH ) ;
		delayMicroseconds( 253 );
		digitalWrite( SPEAKER, LOW );
		delayMicroseconds( 253 );
	}
	digitalWrite( count, HIGH );
	for ( int j = 0; j < 800; j++ ) {
		digitalWrite( SPEAKER, HIGH );
		delayMicroseconds( 189 );
		digitalWrite( SPEAKER, LOW );
		delayMicroseconds( 189 );
	}
	
	delay( 800 );
	flashOff();
}

/*
	ライフアップの効果音
*/

void lifeUp() {
	
	digitalWrite( 6, HIGH );
	for ( int i = 0; i < 97; i++ ) {
		digitalWrite( SPEAKER, HIGH );
		delayMicroseconds( 379 );
		digitalWrite( SPEAKER, LOW );
		delayMicroseconds( 379 );
	}
	digitalWrite( 2, HIGH );
	for ( int j = 0; j < 235; j++ ) {
		digitalWrite( SPEAKER, HIGH );
		delayMicroseconds( 319 );
		digitalWrite( SPEAKER, LOW );
		delayMicroseconds( 319 );
	}
	
	digitalWrite( 4, HIGH );
	for ( int k = 0; k < 396; k++ ) {
		digitalWrite( SPEAKER, HIGH );
		delayMicroseconds( 189 );
		digitalWrite( SPEAKER, LOW );
		delayMicroseconds( 189 );
	}
	
	digitalWrite( 7, HIGH );
	for ( int l = 0; l < 315; l++ ) {
		digitalWrite( SPEAKER, HIGH );
		delayMicroseconds( 238 ) ;
		digitalWrite( SPEAKER, LOW );
		delayMicroseconds( 238 );
	}
	
	digitalWrite( 3, HIGH );
	for ( int m = 0; m < 353; m++ ) {
		digitalWrite( SPEAKER, HIGH );
		delayMicroseconds( 212 );
		digitalWrite( SPEAKER, LOW );
		delayMicroseconds( 212 );
	}
	
	digitalWrite( 5, HIGH );
	for ( int n = 0; n < 471; n++ ) {
		digitalWrite( SPEAKER, HIGH );
		delayMicroseconds( 159 );
		digitalWrite( SPEAKER, LOW );
		delayMicroseconds( 159 );
	}
	
	delay( 800 );
	flashOff();
}

/*
	赤色 LED の点滅(効果音に合わせて利用)
*/

void flash( int num ) { 
	
	if ( num % 2 == 0 ) {
		digitalWrite( 2, HIGH );
		digitalWrite( 4, HIGH );
		digitalWrite( 6, HIGH );
	} else {
		digitalWrite( 3, HIGH );
		digitalWrite( 5, HIGH );
		digitalWrite( 7, HIGH );
	}
}

/*
	パイレーツの BGM
*/

void pirates() {
	
	// 曲のスピード(仕様上、数値とスピードは反比例)
	// songspeed = 2.0 とすると遅くなるが同じ音が 2 回以上続くときノイズが入る
	const int songSpeed = 1.5;
	
	// 音符の配列を作成(0 は休符)
	const int notes[] = {
	
		NOTE_E4, NOTE_G4, NOTE_A4, NOTE_A4, 0, // ミ ソ ラ ラ 休符
    	NOTE_A4, NOTE_B4, NOTE_C5, NOTE_C5, 0, // ラ シ ド ド 休符
    	NOTE_C5, NOTE_D5, NOTE_B4, NOTE_B4, 0, // ド レ シ シ 休符
    	NOTE_A4, NOTE_G4, NOTE_A4, 0,          // ラ ソ ラ 休符

    	// 別パート
    	NOTE_A4, NOTE_A4,

    	NOTE_A4, NOTE_B4, NOTE_C5, NOTE_C5, 0,
    	NOTE_C5, NOTE_D5, NOTE_B4, NOTE_B4, 0,
    	NOTE_A4, NOTE_G4, NOTE_A4, 0,
    
    	NOTE_E4, NOTE_G4, NOTE_A4, NOTE_A4, 0,
    	NOTE_A4, NOTE_C5, NOTE_D5, NOTE_D5, 0,
    	NOTE_D5, NOTE_E5, NOTE_F5, NOTE_F5, 0,
    	NOTE_E5, NOTE_D5, NOTE_E5, NOTE_A4, 0,

    	NOTE_A4, NOTE_B4, NOTE_C5, NOTE_C5, 0,
    	NOTE_D5, NOTE_E5, NOTE_A4, 0,
    	NOTE_A4, NOTE_C5, NOTE_B4, NOTE_B4, 0,
    	NOTE_C5, NOTE_A4, NOTE_B4, 0,

    	// クライマックス
    	NOTE_E5, 0, 0, NOTE_F5, 0, 0,
    	NOTE_E5, NOTE_E5, 0, NOTE_G5, 0, NOTE_E5, NOTE_D5, 0, 0,
    	NOTE_D5, 0, 0, NOTE_C5, 0, 0,
    	NOTE_B4, NOTE_C5, 0, NOTE_B4, 0, NOTE_A4,
    
    	NOTE_E5, 0, 0, NOTE_F5, 0, 0,
    	NOTE_E5, NOTE_E5, 0, NOTE_G5, 0, NOTE_E5, NOTE_D5, 0, 0,
    	NOTE_D5, 0, 0, NOTE_C5, 0, 0,
    	NOTE_B4, NOTE_C5, 0, NOTE_B4, 0, NOTE_A4
	};
	
	// 音の長さを定義
	// 4分音符 = 500 ms | 8分音符 = 250 ms | 16分音符 = 125 ms

	int duration[] = {

    	125, 125, 250, 125, 125,
    	125, 125, 250, 125, 125,
    	125, 125, 250, 125, 125,
    	125, 125, 375, 125,
    
    	250, 125,
    
		// 別パート
    	125, 125, 250, 125, 125,
    	125, 125, 250, 125, 125,
    	125, 125, 375, 125,
    
    	125, 125, 250, 125, 125,
    	125, 125, 250, 125, 125,
    	125, 125, 250, 125, 125,
    	125, 125, 125, 250, 125,

    	125, 125, 250, 125, 125,
    	250, 125, 250, 125,
    	125, 125, 250, 125, 125,
    	125, 125, 375, 375,

    	// クライマックス
    	250, 125, 375, 250, 125, 375,
    	125, 125, 125, 125, 125, 125, 125, 125, 375,
    	250, 125, 375, 250, 125, 375,
    	125, 125, 125, 125, 125, 500,
   
    	250, 125, 375, 250, 125, 375,
    	125, 125, 125, 125, 125, 125, 125, 125, 375,
    	250, 125, 375, 250, 125, 375,
    	125, 125, 125, 125, 125, 125
	};
	
	int count = 0;
	int size = sizeof( notes ) / sizeof( int ); // size は音符の総数

	// それぞれの音符について処理
	for ( int i = 0; i < size; i++ ) {

		// 休符は無視
		if ( notes[ i ] != 0 ) flashPirates( count );
		
		int rest = duration[ i ] * songSpeed; // 楽曲スピードの調整可能
		tone( SPEAKER, notes[ i ], rest );  // tone( pin, frequency, duration );
		delay( rest );
	}
	
	delay( 100 );
	flashOff();
}

/*
	パイレーツの BGM に合わせた赤 LED の点滅
*/

void flashPirates( int &num ) {
	
	if ( num == 0 ) { 
		digitalWrite( 2, HIGH );
		digitalWrite( 3, LOW );
		digitalWrite( 4, HIGH );
		digitalWrite( 5, LOW );
		digitalWrite( 6, HIGH );
		digitalWrite( 7, LOW );
		num++;
	} else if ( num == 1 ) {
		digitalWrite( 2, LOW );
		digitalWrite( 3, HIGH );
		digitalWrite( 4, LOW );
		digitalWrite( 5, HIGH );
		digitalWrite( 6, LOW );
		digitalWrite( 7, HIGH );
		num++;
	} else {
		digitalWrite( 2, HIGH );
		digitalWrite( 3, HIGH );
		digitalWrite( 4, HIGH );
		digitalWrite( 5, HIGH );
		digitalWrite( 6, HIGH );
		digitalWrite( 7, HIGH );
		num = 0;
	}
}

/*
	アンダーワールドの BGM
*/

void underWorld() {
	
	// 音符の配列を作成(0 は休符)
	const int notes[] = {
		
		NOTE_C4, NOTE_C5, NOTE_A3, NOTE_A4,
		NOTE_AS3, NOTE_AS4, 0,
    	0,

    	NOTE_C4, NOTE_C5, NOTE_A3, NOTE_A4,
    	NOTE_AS3, NOTE_AS4, 0,
    	0,

    	NOTE_F3, NOTE_F4, NOTE_D3, NOTE_D4,
    	NOTE_DS3,  NOTE_DS4, 0,
    	0,

    	NOTE_F3, NOTE_F4, NOTE_D3, NOTE_D4,
    	NOTE_DS3,  NOTE_DS4, 0,
    	0,

    	NOTE_DS4, NOTE_CS4, NOTE_D4,
    	NOTE_CS4, NOTE_DS4,
    	NOTE_DS4, NOTE_GS3,
    	NOTE_G3, NOTE_CS4,
    	NOTE_C4, NOTE_FS4, NOTE_F4, NOTE_E3, NOTE_AS4, NOTE_A4,
    	NOTE_GS4, NOTE_DS4, NOTE_B3,
    	NOTE_AS3, NOTE_A3, NOTE_GS3,
    	0, 0, 0
	};
	
	// 音の長さを定義
	// 音符の全音符を 1000 ms として分音符を作成

	const int duration[] = {
		
		12, 12, 12, 12,
    	12, 12, 6,
    	3,
    	12, 12, 12, 12,
    	12, 12, 6,
		3,
		12, 12, 12, 12,
		12, 12, 6,
		3,
		12, 12, 12, 12,
		12, 12, 6,
		6, 18, 18, 18,
		6, 6,
		6, 6,
		6, 6,
		18, 18, 18, 18, 18, 18,
		10, 10, 10,
		10, 10, 10,
		3, 3, 3
	};
	
	const int noteLength = 56;
	for ( int i = 0; i < noteLength; i++ ) {
		
		int noteDuration = 1000 / duration[ i ]; // x 分音符を作成
		buzz( notes[ i ], noteDuration ); // 音符の出力
		int pause = noteDuration * 1.3; // 音符を明確に区別するために最小時間で 1.3 が最適
		delay( pause );
		buzz( 0, noteDuration );
	}
}

/*
	音符の出力(アンダーワールドにて利用)
*/

void buzz( long frequency, long length ) {
	
	// digitalWrite 間の delay を計算(実質的には音の高さを計算)
	// 1000000 s → μs に変換 (HIGH/LOW の 2 つのサイクルがあるため 2 で割る)
	long delayValue = 1000000 / frequency / 2; 
	
	// 波の数を計算(音の長さを計算)
	long cycles = frequency * length / 1000;   // length / 1000 : ms → s の変換
	
	flash( frequency ); // 音の高さが偶数、奇数の場合で点灯が場合分け
	for ( long i = 0; i < cycles; i++ ) {
		digitalWrite( SPEAKER, HIGH );
		delayMicroseconds( delayValue );
		digitalWrite( SPEAKER, LOW );
		delayMicroseconds( delayValue );
	}
	flashOff();
}

/*
	フルカラー LED の色を変更
*/

void showGrade( int grade ) {
	
	if ( grade >= 1000 ) {
		digitalWrite( LED_R, HIGH );
		digitalWrite( LED_G, HIGH );
		digitalWrite( LED_B, HIGH );
	}
	else if ( grade >= 800 ) {
		digitalWrite( LED_R, HIGH );
		digitalWrite( LED_G, LOW );
		digitalWrite( LED_B, LOW );
	}
	else if ( grade >= 500 ) {
		digitalWrite( LED_R, LOW );
		digitalWrite( LED_G, HIGH );
		digitalWrite( LED_B, LOW );
	}
	else if ( grade >= 200 ) {
		digitalWrite( LED_R, LOW );
		digitalWrite( LED_G, LOW );
		digitalWrite( LED_B, HIGH );
	}
	else {
		digitalWrite( LED_R, LOW );
		digitalWrite( LED_G, LOW );
		digitalWrite( LED_B, LOW );
	}
}

/*
	フルカラー LED の消灯
*/

void showGradeOff() {
	digitalWrite( LED_R, LOW );
	digitalWrite( LED_G, LOW );
	digitalWrite( LED_B, LOW );
}

/*
	全赤色 LED を消灯
*/

void flashOff() {
	digitalWrite( 2, LOW );
	digitalWrite( 3, LOW );
	digitalWrite( 4, LOW );
	digitalWrite( 5, LOW );
	digitalWrite( 6, LOW );
	digitalWrite( 7, LOW );
}

効果音や BGM の音楽は、圧電スピーカーを制御する tone 関数を利用し、音階を再現することで実装します。

特に、曲を制作する際は、音符を音の高さとリズムの 2 つに分け、それぞれの順序を対応付けて配列に格納し、両者を掛け合わせることで再現します。

Arduino を用いた音楽制作の詳細については、下記を参照ください。

検証(コンパイル)

Arduino IDE にて「検証」を行う際は、メインスケッチである musicRoulette.ino に対して実行します。

分割されたファイルが自動で連結され、コンパイルが行われます。

参考までに、メインスケッチ以外のファイルのタブで「検証」ボタンを押しても、同様にコンパイル可能です。

回路

実態配線図は、下記の通りです。

この図を参照しながら、回路を作成します。

音楽ルーレットの実態配線図

配線手順と各パーツの詳細は下記の通りです。

タクトスイッチの配線

タクトスイッチ13 番ピンに接続します。

このとき、10 KΩ 程度の大きな抵抗を挟みます。

これにより、スイッチが押されるまで、常に H レベルの値が出力され、ボタンが押された時、L レベルの出力が行われます。

こうしたプルアップ回路により、スケッチ上の条件分岐に利用します。

ボタンが押されるまでループよる待機状態を作り出し、ボタンが押されたことをトリガーに以降のプログラムを実行するように実装します。

タクトスイッチの配線

ポテンショメータは、回転角によって抵抗値が変わるパーツです。

右に回すことで抵抗値が上がり、この値をスケッチにおける閾値として用います。

3 つの端子の内、右端に +5V、左端に GND、真中に A1 のアナログ入力ピンを接続します。

フルカラー LED の配線

フルカラー LED は、「赤」「緑」「青」の 3 色の LED が 1 つにまとめられたものです。

各 LED の明るさを変化させ、光を混合することにより、様々な色の光を作り出すことができます。

色ごとに LED の特性が異なるため、それぞれに適した抵抗を選択する必要があります。

LED 目安 当回路 ピン番号
300 Ω 程度 330 Ω 10
270 Ω 程度 270 Ω 9
600 Ω 程度 560 Ω 8

左側の端子から、GND、の順になるように設置し、適当な抵抗を接続します。

コモンタイプ

フルカラー LED には、「コモンアノードタイプ」と「コモンカソードタイプ」と 2 つのタイプがあります。

前者は、色別に 3 本のカソード(K)と、共通のアノード(A)を持つタイプです。

後者は、色別に 3 本のアノード(A)と、共通のカソード(K)を持つタイプです。

ここでは、コモンカソードタイプを利用します。

赤色 LED の配線

赤色 LED は、正六角形に並べて設置し、それぞれに 330 Ω の抵抗を接続します。

右下の赤色 LED から時計回りに 2 ~ 7 番ピン へ接続します。

ルーレットは、2 番ピン からスタートします。

圧電スピーカーの配線

圧電スピーカーは、薄板の圧電振動板を金属板に貼り付けて作製される一種のブザーです。

H/L レベルの電圧を高速で繰り返し印加することで圧電振動板が伸縮し、内部の金属板が振動します。

これにより、音波を発生させることができ、電圧の間隔(周波数)を制御することで音階を作り出すことができます。

Arduino 上で制御する場合、H/L レベルの電圧のみを印加するため、デジタル出力ピンに接続します。

ここでは、スピーカの正極を 12 番ピン に接続します。

回路の配線は以上です。

おすすめの開発アイテム

電子工作の初心者に最適な開発キットや、参考書を紹介します。

小中学生向けの電子工作講座にて、利用しています。

ELEGOO Arduino UNO R3 スターターキット

初心者向けの入門用の開発セットです。

マイコンボードやブレッドボードをはじめ、開発に必要十分な電子部品が全て揃えられています。

単体購入すると高くなる付属パーツも豊富に同梱されており、コスパが良いです。

マイコンボードは、ELEGOO 社の互換機ではありますが、全く問題なく動作します。

Arduino UNO R3 スターターキット

ELEGOO

さらに電子部品を追加したバージョンの開発セットもあります。

初心者では使いきれない可能性もありますが、買い揃えるのにハードルが高いアイテムに出会うことができます。

Arduino UNO R3 最終版スターターキット

ELEGOO

ELEGOO 社

ELEGOO 社は、「中国のシリコンバレー」と呼ばれる深センにある会社です。

深センは、香港にも近く、IT 人材が豊富であり、スタートアップが多く集まるエリアです。

短期間で電子製品を設計製造できるエリアとしても有名です。

HUAWEI、Tencent、DJI など、世界的に有名な企業も多く輩出しています。

これ1冊でできる!Arduino ではじめる電子工作 超入門 改訂第4版

Arduino を用いた電子工作の書籍では、最も有名な 1 冊です。

マイコンボードや部品の扱い方、スケッチを用いた電子回路の制御方法などの基礎知識が丁寧に解説されており、初心者の方でも安心して学習することができます。

中級者向けにも、細かなノウハウや応用テクニックも豊富に紹介されています。

また、作品例を通じて、実際に手を動かしながら理解を深めることができます。

サンプルのスケッチや回路を改変しながら、独自の作品としてカスタマイズすることでも、十分に楽しむことができます。

手元に置いておくと、開発がスムーズに進みます。

学生時代の開発にて、旧版を活用していましたが、とても重宝しました。

これ1冊でできる!Arduino ではじめる電子工作 超入門

著者 : 福田 和宏 / 出版社 : ソーテック社

1週間で C 言語の基礎が学べる本

Arduino は、C言語とほぼ同じ文法で記述します。

スケッチを作成するうえで、プログラミングの知識不足を感じる場合は、C言語の習得も兼ねて体系的に学習しておくと、スムーズに開発を行うことができます。

この書籍は、「1週間シリーズ」の C言語編です。

必要なポイントを押さえ、短期間で基礎知識を学び、全体像を把握することができます。

プログラミングを始めたばかりの超初心者が対象です。

初歩的な文法をはじめ、基本的なアルゴリズムとデータ構造を学び、練習問題を通じて手を動かすことで理解を深めることができます。

1週間で C 言語の基礎が学べる本

著者 : 亀田 健司 / 出版社 : インプレス

小学生向けロボット・プログラミング教室
  • エジソンアカデミー
    国内最大級の子供向けロボットプログラミング教室であり、全国 45 都道府県に約 900 教室を展開しています。国際大会(URC)に出場することもできます。
  • ヒューマンアカデミー
    ロボット電話「RoBoHoN」や、世界初の対話できるロボット宇宙飛行士「KIROBO」(トヨタ自動車 & 電通と共同開発)の開発者である、話題のロボットクリエイター「高橋智隆」先生が監修した教材によるカリキュラムやイベントが用意されています。