全体的にひどい文章です。「は?」と言いたくなる題ですが、落ち着いてください。桜先輩にランダムに喋っていただくデバイスをArduinoとDFPlayer Miniで作ったというだけです(はぇ?)。
桜先輩とは
そこからかよ…面倒な人間だなコイツ。
桜先輩こと桜井美景はきらら系『恋する小惑星』(小惑星はアステロイドと読む)の登場人物です。いわゆるツンデレ成分あり。ちょっと不器用で悪い意味で現実的な娘でしたが、ほぐれましたねぇ。そういう成長は大好きです。アニメ放送当時に高校生だったこともあり、進路、将来の話は共感の嵐!大変美味でした。
で、なんじゃそりゃ
ボタンが押されると全mp3ファイルの中から1つをたぶん同様に確からしく(ランダムに)再生します。機能はこれだけです。
でけた。桜先輩ボタン。 pic.twitter.com/koWEnwFOJq
— Ramb (@haya_astronomy) 2022年7月8日
こういうものです。ここで作るものは台詞のmp3ファイルさえ用意できれば他のキャラクターにもそのまま流用可能です。だからといってしまりんverよこせ!とか怒らないでください。自分でやってね。
着想自体はこちらの記事から得ました。素敵なボタンです。
nixeneko.hatenablog.com
DFPlayer Mini とは
Arduinoでmp3ファイルを再生するのに便利なモジュールです。秋月電子でも取り扱いがあります。
akizukidenshi.com
ただAmazonでもっと安く売っているので、慣れている人はそちらで買えばいいと思います*1。
DFRobot製品だけあって、Arduino用ライブラリが用意されています。ここではこのライブラリを使います。
github.com
その他、練習にはこちらのブログ記事が参考になります。ただライブラリのスケッチ例にFullFunctionというのがあり、それをじっくり見るのがライブラリの機能を活用する近道です。
blog.hrendoh.com
部品
部品は以下の表にまとめておきます。
部品名 | 個数 |
---|---|
ブレッドボード | 1 |
Arduino Nano | 1 |
DFPlayerMini | 1 |
スピーカー | 1 |
タクトスイッチ | 1 |
1kΩ抵抗 | 1 |
470Ω抵抗 | 1 |
配線 | たくさん |
スピーカーは秋月のこちらが手軽に扱えて良いです。今回はこれを使います。
akizukidenshi.com
基板に実装するならこれもあり。使ったことがありますが何も考えなくてもちゃんと動きました。
akizukidenshi.com
とりあえずダイナミックスピーカーと書いてあって、DFPlayerのアンプ出力3Wに近ければ何でもいいはずです。
配線
以下の図のようにつなぎます。ここはある程度自由にやってもらって構いません。ここの配線を変えたらスケッチ(プログラム)も修正してください。とはいえ注意すべき制限があるので、それを説明します。
DFPlayerMiniとArduino間の配線
ここはシリアル通信関連に注意が必要です。シリアル通信は送信ピン(TX)と受信ピン(RX)を繋ぐので、下の表のようにつなぎます。
① | Arduino TX ↔ DFPlayerMini RX |
② | Arduino RX ↔ DFPlayerMini TX |
しかもArduinoとDFPlayerMiniの間の通信は(ここで紹介するプログラムでは)Software Serialですから、Arduino RX, TXは0ピンや1ピンではなく、自分で指定したピン(4,5番ピン)になります。ここで出すプログラムではArduinoの純粋なシリアル通信用の0ピン1ピンはパソコンとのシリアル通信で使われているので、他のことには使えません。何かを繋ぐことはできません。
もう一つシリアル通信で注意する点があります。それはDFPlayer Miniの電圧が3.3Vだという点です。Arduino Nanoの電圧は5Vなので当然シリアル通信の信号は5Vです。しかしDFPlayerは3.3Vで設計されていますから、①を直に繋いでいいのかは怪しいです。*2。ということで①の間に分圧回路を挟みます。こちらが参考ページ。高校物理をやった方なら原理はわかると思います。
www.kairo-nyumon.com
我が家には470Ωと1kΩが転がっていたので、これでだいたい5Vを1:2に内分しました。
ランダム関数
Arduinoでランダムに整数値を出す関数も本当はランダムに出しているのではなくて、予め準備された数を出しているに過ぎないのです(この理解でいいのかは自信ない)。予め準備された数のセットの番号はシード値といって、こいつを変えない限り毎回同じ順番で数が出てくることになります。従ってこいつを何も繋いでいないアナログ入力から読み取った値にすればランダムになるというわけ。ここではアナログ入力ピンにA0を使っているので、A0には何も繋いではいけません。
参考:Arduino 日本語リファレンス
SDカードの準備
microSDに各話の桜先輩の台詞を切り出したmp3ファイルを話別フォルダーに入れておきます*4。
フォルダー名は01, 02, ..., 12のようにしないと認識してくれませんでした。ファイル名は001.mp3, 002.mp3,...のようにします。
スケッチ(プログラム)
上でも書きましたが、ピンのつなぎ方を変えたらスケッチもピン番号を書き換えてください。ピンを変えたり音量を変えたりする部分は上部の//constants----にまとめてあります。
ちなみに私のArduino Nanoは3年以上前の古い互換品なので、最新のArduino IDEではoldBootloaderを選ばなければなりませんでした。oldとは何だ失礼な。このせいで30分くらい溶かしたのだ。
#include <SoftwareSerial.h> #include <Arduino.h> #include <DFRobotDFPlayerMini.h> //https://github.com/DFRobot/DFRobotDFPlayerMini //Play Sakurai Mikage line at random //refer to https://jumbleat.com/2016/08/19/switch_without_chatter/ (chattering) //------ constants -------------- const int PlayerRX = 5; //Arduino側RXピン DFPlayerのTXと結線 const int PlayerTX = 4; //Arduino側TXピン DFPlayerのRXと結線 int volume = 20; //音量設定(1~30) const int CharaLines[] = {32, 56, 10, 49, 53, 42, 17}; //各話の台詞数(まだ7話までしか追えていない) const int StoryNumber = sizeof(CharaLines) / sizeof(int); int AllLines = 0; const int Button = 2; const int PushThreshould = 1000; const int BusyPin = 7; //-------------------------------- SoftwareSerial PlayerSerial(PlayerRX, PlayerTX); DFRobotDFPlayerMini Player; void setup() { //全台詞数をカウント for (int i = 0; i < StoryNumber; i++) { AllLines += CharaLines[i]; } pinMode(Button, INPUT_PULLUP); pinMode(BusyPin, INPUT_PULLUP); PlayerSerial.begin(9600); Serial.begin(115200); Serial.println(); Serial.println(AllLines); Serial.println(StoryNumber); Serial.println(F("Initializing DFPlayer ... (May take 3~5 seconds)")); if (!Player.begin(PlayerSerial)) { //Use softwareSerial to communicate with mp3. Serial.println(F("Unable to begin:")); Serial.println(F("1.Please recheck the connection!")); Serial.println(F("2.Please insert the SD card!")); while (true) { delay(200); }; //loop, Restart to break this loop. } Serial.println(F("DFPlayer Mini online.")); Player.setTimeOut(500); //Set serial communictaion time out 500ms //----Set volume---- Player.volume(volume); //----Set different EQ---- Player.EQ(DFPLAYER_EQ_NORMAL); randomSeed(analogRead(0)); } void loop() { unsigned long gauge = 0; //チャタリング防止 while (true) { if (!(digitalRead(Button))) { gauge++; } if (gauge > PushThreshould) { break; } } int LineNumber = random(1, AllLines + 1); //無作為に台詞を一つ選ぶ Serial.println(LineNumber); int Folder = 1; int NumInFolder = 0; //どのフォルダのどのファイルかを求める for (int i = 0; i < StoryNumber; i++) { if (LineNumber <= CharaLines[i]) { NumInFolder = LineNumber; break; } else { LineNumber -= CharaLines[i]; Folder++; } } //play specific mp3 in SD:/Folder/NumInFolder.mp3; Folder Name(01~99); File Name(001~255) Player.playFolder(Folder, NumInFolder); while (true) { delay(300); if (digitalRead(BusyPin) == HIGH) { break; //再生終了まで待つ } } //Print the detail message from DFPlayer to handle different errors and states. if (Player.available()) { printDetail(Player.readType(), Player.read()); } } void printDetail(uint8_t type, int value) { //copied from FullFunction Sketch switch (type) { case TimeOut: Serial.println(F("Time Out!")); break; case WrongStack: Serial.println(F("Stack Wrong!")); break; case DFPlayerCardInserted: Serial.println(F("Card Inserted!")); break; case DFPlayerCardRemoved: Serial.println(F("Card Removed!")); break; case DFPlayerCardOnline: Serial.println(F("Card Online!")); break; case DFPlayerUSBInserted: Serial.println("USB Inserted!"); break; case DFPlayerUSBRemoved: Serial.println("USB Removed!"); break; case DFPlayerPlayFinished: Serial.print(F("Number:")); Serial.print(value); Serial.println(F(" Play Finished!")); break; case DFPlayerError: Serial.print(F("DFPlayerError:")); switch (value) { case Busy: Serial.println(F("Card not found")); break; case Sleeping: Serial.println(F("Sleeping")); break; case SerialWrongStack: Serial.println(F("Get Wrong Stack")); break; case CheckSumNotMatch: Serial.println(F("Check Sum Not Match")); break; case FileIndexOut: Serial.println(F("File Index Out of Bound")); break; case FileMismatch: Serial.println(F("Cannot Find File")); break; case Advertise: Serial.println(F("In Advertise")); break; default: break; } break; default: break; } }
長くて読みにくい。まずいな、これはGitを使い始めなければいけない予感。
基本的にconstantsのところをいじれば動きます。解説しようと思ったけど長くなりすぎるし疲れたからいいや。後で編集します。