【初心者向け】Arduinoを使った電子工作を紹介!BGMルーレットプログラムを作ってみた!ソフトウェア編

スポンサーリンク

 

こんにちは!HELLO!您好!привет там ! 안녕하세요 !Hola !

この記事は 「BGMルーレットプログラムを作ってみた!」のソフトウェア編です。

ハードウェア編をまだご覧になっていない方は下のリンクからどうぞ。

【初心者向け】Arduinoを使った電子工作を紹介!BGMルーレットプログラムを作ってみた!ハードウェア編

2018.03.14

上の記事では私の作品のハードウェアの仕様について取り上げたので、

今回はそのハードウェアを動かすプログラムについて簡単にお話ししようと思います。

 

me

解説するスケッチのダウンロードはここからお願いします。

 

それでは早速スケッチの説明に移ります。

まず、当スケッチは主な役割ごとに以下の4つの大きなセクションから構成されています。

  1.  ルーレットの制御部
  2.  BGM を出力する関数群
  3.  景品セットの制御部
  4.  サーボモータの制御部

それぞれを制御するスケッチについて順に説明していきます。

説明にあたり前回と同じ以下の実体配線図を使います。

 

ルーレットを制御する

 

ルーレットの回転部は赤色 LEDで構成されています。

各 LEDはマイコンボードの2番から7番ピンに順に接続されており、スケッチ上のグローバル変数 hit に当選したピンの番号を格納します。

変数 hit の値はスタートボタンを押した後に loop関数内の random関数で取得した乱数で決定されます。

乱数の種の初期化

random関数を利用する際は randomSeed関数を用いて乱数の種を初期化する必要があります。

一般的に randomSeed( analogRead( 0 ) ) として未接続のアナログ入力ピンのノイズを利用して乱数を初期化します。

ルーレットの点滅と点滅に伴う効果音は当選ピンに接続された LEDでストップしますが、そうした制御は2つの関数を用いて行います。

roulette 関数とannounceHit 関数です。

前者の関数は単純にルーレットを3回転させる関数であり演出用です。

後者の関数は 回転をスタートする2番ピンに接続されたLEDから hit番号に接続されたLEDまでゆっくりルーレットを回転させ、ヒットした LEDを示す関数です。

me
roulette関数とannounceHit関数は後述する play関数の中で呼び出します。

roulette関数

 

ルーレット音は tone関数を利用し圧電スピーカーから出力し、LEDの点滅は digitalWrite関数を利用します。

tone関数の第1引数には出力するピン番号を指定するのですが、当スケッチでは #define speaker 12 と記述して12番ピンに割り当てています。

me

音と点滅を人間が同時に知覚するために必ず音を出力させてからLEDを点灯させてください。

私の実験の結果から、音と光をラグなく同時に人間が感知するためにはこの順序が最適です。

また、音を出力させた後1度 noTone関数を用いて消音すると音の粒が際立ちます。

ルーレットの点滅する間隔は delay関数を用いて各自お好きなように調整してください。

同様にルーレット音の高さも tone関数の第2引数を調整することで変更することができます。

 

announceHit 関数

 

announceHit 関数に hit番号の数値を渡して呼び出すことで、

2番ピンに接続されたLEDから hit番号に接続されたLEDまで順に点滅させながら回転音を出力します。

最後はヒットしたLEDで止まり、LEDを2回点滅させながら効果音を出力します。

roulette関数と同様に tone関数および delay関数を調整することで自由にルーレットの演出を変更することができますので適宜調整してください。

waveFlag 関数に関しては後述します。

 

ルーレットを制御するスケッチの説明は以上です。

 

BGMを作る

 

ルーレットがヒットした際の「景品」として用意する BGM を作成します。

各 BGM はそれぞれ関数に定義し適宜呼び出して利用します。

今回作成した BGMは全部で8種類ありますが、それぞれについて簡単に説明します。

 

ハズレ

 

ルーレットがハズレた場合の効果音です。

3つの引数をもつ tone関数とdelay関数を微調整して作成しました。

 

コインゲット

 

tone関数を使用せず圧電スピーカーを原始的に振動させることで音の高さを微調整する方法です。

[email protected]東北芸工大 さんのサイトを参考にさせていただきました。

コインをトータルで6枚獲得するとコインを連続ゲットする際の効果音とレベルアップの効果音が出力されますが、

その効果音を出力する getCoinRush関数と marioLifeUp関数も同様の手法で定義されています。



ポテト揚げたて

 

マクドナルドでよく聞くポテトが揚がった直後のあの MUSIC を出力する関数です。

単純な単音ですので tone関数を用いて作成しました。

また、単音と同時に LEDを点滅させる装飾を施しました。

その LEDの点滅を演出する関数は以下の flash関数です。

 

引数に渡された値に基づき偶数番号のLEDと奇数番号のLEDを点滅させる仕様で、

後述する他の効果音出力関数内でも利用しています。

引数には楽曲の音符の周波数などを渡すと音楽のメロディに応じて曲ごとに異なる挙動をするするので面白いです。

 

スイープ

 

パチンコ屋さん風効果音です。※ 行ったことがないので想像上での実装です。

名前の由来はスイープ奏法という主にヘビーメタルのギターで使用される奏法です。

YouTube などで聞いていただければ納得されるはずです。

一定の間隔で音程が上昇し下降するメロディを3回繰り返します。

音程の上昇と同時に LEDも順に点灯し、下降と同時に順に消灯する仕様です。

 

パイレーツ

 

パイレーツオブカリビアンの劇中でよく流れる有名な曲を短縮してアレンジした BGM です。

これまで紹介したBGMとは異なり少し長めの曲となっています。

こうした本格的な曲の作り方は Arduino 公式ページ を参考に作成することができます。

me

それでは、曲の作り方を簡単に説明します。

まず、実装する曲のメロディを単音毎に「音の高さ」と「音の長さ」に分けてそれぞれ配列で管理します。

上記のスケッチにおいて前者は note[ ] に格納され、後者は duration[ ] に格納されています。

一般的に実装する曲中で現れる音符の音階には #define で名前を付けておくと note[ ] で扱いやすくなります。

また、duration[ ] に格納される「音の長さ」は全音符を 2000ms とすると、

4分音符は 500ms、8分音符は 250ms、16分音符は125ms となります。

note[ ] と duration[ ] の要素が正しく対応するように注意してください !

 

音符の扱いを理解できたら後はネット上で公開されている楽譜などを参考に楽曲を2つの配列に分解すればよいです。

me

私は楽譜がいまいち読めないため音楽が得意な友人に手伝ってもらいました。

最後に完成した配列の音符を単音ごと tone関数に渡して出力すればよいです。

このとき LEDの点滅パターンを同時に定義すると綺麗な光の演出を装飾することができます。

私の場合、3パターンの点滅が順に繰り返されるような flashPirates関数を定義しました。



アンダーグラウンド

 

マリオさんが地下に潜りなさった時に流れるあの BGMを出力する関数です。

基本的な音楽の実装法はアンダーグラウンドと同じですが、この関数では tone 関数を使用せず自作の buzz関数で音を出力しています。

GitHubのMichael Dennyさんを参考にさせていただきました。

音声の出力方法自体はコインゲットと同じ手法で実装されています。

また、音楽に合わせた LEDの点滅の演出はこれまでに説明した flash関数を利用します。

引数には note[ ] の要素である周波数を渡します。

 

以上で BGMを作成するスケッチの説明は終わりです。

 

景品セットを制御する

 

作成した BGM関数はランク付けをされた景品セットとしてまとめます。

同時に、景品セットである関数群を呼び出すための関数も作成して扱いやすくします。

景品セットのランク付けは5段階とし以下の5つの関数を作成しました。

  1. hitSet_ultra() ★★★★★
  2. hitSet_super() ★★★★
  3. hitSet_middle() ★★★
  4. hitSet_standard() ★★
  5. hitSet_bad()

上の関数ほど豪華度は高くなっています。

ただし、ここで言う豪華度とはBGM関数の完成度を基準に任意に選択して作った景品セットの内容を指します。

私の場合は以下のような割り当てとなっています。

 

以上の景品セット関数は後述する play関数内で呼び出します。

また、変数 hit には ramdom関数で取得した乱数が格納されており、2~7のLEDが接続されたマイコンボードの端子番号を指します。

なぜか全て if文で書いていますが switch文に変更した方が綺麗ですので適宜変更してください。

 

サーボモータを制御する

 

ルーレットの結果発表を行う前に旗を振るためだけに利用しました。

正直な所無駄な実装であり本来のサーボモータの使い方から大きく逸脱していますので、

この実装は無視していただいても結構です。

ですが、一応サーボモータを動作させるスケッチについて簡単に説明しておきます。

以下は主要コードを抜き出したスケッチです。

 

サーボモータを制御するためには専用のServoライブラリをインクルードします。

次に関数呼び出しに利用するレシーバを用意する必要がありますが、3行目のようにオブジェクトを生成すればよいです。

また、setup関数内ではサーボモータの羽の初期角度を0度に設定しておきます。

waveFlag 関数は引数に渡す数値により回転スピードを指定することができます。

モータの動作は右左に数回ずつ小刻みに回転する仕様です。

me

ここで重要なポイントなのですが、

サーボモータを動作させる際はその直前に attach関数を利用してピンに割り当て動作終了直後は detach関数を利用してピンをサーボモータから解放させると良いです。

というのも使用後にサーボモータをピンから解放しておかなければ他のプログラムの実行中にサーボモータが動作待機状態となりモータが微かに動くなど不安定になるためです。



loop関数の処理

 

最後にメイン関数となる loop関数内での処理について説明しておきます。

以下が loop関数のスケッチです。

 

まず 4行目で乱数の種を初期化しておきます。

次にスイッチが押されるまで処理を待機する入力受付の処理を書きます。

プルアップ回路によりスイッチが押され Lレベルの出力が行われるまで while文内の処理を行わせることで実現しています。

while文内では可変抵抗のノブを回すことで調整可能な値を読み込み変数 luckに格納します。

また、luckを imply関数に渡しその値に基づき LEDの色を変更します。

LEDの色は景品セットのレベルを示唆するもので imply関数は luckの値を基準にした条件分岐によりその色を決定します。

スイッチが押され while文を抜け出した後は乱数を取得し、最後に play関数に luckと hit を渡します。

play関数が核となる関数で、これまでに作成した関数をここで呼び出します。

 

 

以上で全てのスケッチの説明は終わりです。

拙いコードでいくつか修正点があるかと思いますのでご自由に書き直してお使いください。

バグや改良点がある場合はぜひお問い合わせなどを通して連絡くだされば幸いです!

長くなりましたが最後までお付き合いありがとうございました。

それではまた!

スポンサーリンク


ABOUT ME!

yuk!

国立大学情報学科に通う大学生です。天然パーマと戦いながらすーぱーエンジニアを目指し技術とセンスを磨いています。 室内に引きこもりがちでヘビメタと猫と甘いものが救いのキーボードカチャカチャ生活ですが、最近はブログで文章を書くことが楽しいです。モットーは「 Who dares wins. = 人生是一箇,活殺全在我。」好きな言葉は「マジ卍」。