====== 6-2 DFPlayerで音を鳴らす(UART) ======
「ライブラリ」を使いこなせば、「シリアル通信」で部品とArduino間でデータのやり取りを行う、高機能な電子部品を動かすことができるようになります。
本項より順に、「UART」・「I2C」・「SPI」の3種類のシリアル通信について説明します。
===== 本項で使用する電子部品 =====
==== DFPlayerモジュール ====
**「モジュール」とは、"ある機能を実現するために、必要な電子部品を組み合わせて作られた電子部品"**を指します。
DFPlayer(正確にはDFPlayer mini。以下DFPlayer)モジュールは、DFRobotが開発・販売している、音楽や音声などを再生することのできるモジュールです。
{{:gimmickkouza:electronic_basic:6:6-8.png?300|}}
DFPlayerはほぼ同じ見た目のコピー品が市場に出回っています。
秋月電子通商及びSwitchScienceでは正規品のDFPlayerを取り扱っていますが、Amazonで売っている格安の商品は確実にコピー品と思った方がいいです。
一見印刷されているロゴ以外に違いはわかりませんが、コピー品は音質や音量が劣るとの検証もあります(下記リンク参考)
また本資料の配線及びサンプルコードでのコピー品の動作は保証致しません。
正規品、海外品、DFPlayer miniを比べてみた(ど素人電子工作):
https://toccho.net/2023/01/03/dfplayer-mini/
==== スピーカー ====
{{:gimmickkouza:electronic_basic:6:6-9.png?300|}}
DFPlayerにはスピーカーが搭載されていません。
なので音楽を再生することはできますが、人間の耳に聞こえる形の"音波"を発することはできません。
そのため、今回は上図のスピーカーを買っていただきました。
こちらのスピーカーはブレッドボードに挿せるワイヤが既にはんだ付けされているため、手軽に扱うことができます。
===== 6-2-1_DFPlayer_UART =====
==== DFPlayerで音楽を再生するための下準備 ====
=== その1:音楽ファイル編 ===
DFPlayerは、挿し込まれたSDカードスロット内の音楽ファイルを読み込んで再生します。
音楽のファイルにはいろいろな形式のものがありますが、DFPlayerでは拡張子(ファイル名の末尾の「.○○○」の部分)が「.mp3」のMP3ファイルを扱うことができます。
サンプルコード6-2-1のフォルダ内mp3のフォルダに、MP3ファイルを一つご用意しました。
今回のサンプルコードではこちらのファイルをご利用ください。
(本資料のサンプル用音源はすべてOtoLogic様よりDLさせていただきました。感謝!)
OtoLogic:https://otologic.jp/ (音源素材はCC BY 4.0に則り提供されています)
== 手順1:SDカードをPC(パソコン。以下PC)に接続する ==
PCにmicroSDカードを接続してください。挿し口がない場合はカードリーダが必要です。
(SDカードををPCに接続した際に、「フォーマットしますか?」というメッセージが出た場合は、「はい」を選択 → 表示されたウィンドウのうち、「ファイルシステム」の欄が「FAT」もしくは「FAT32」になっていることを確認して「開始」を押してください)
SDカードが正常に読み込まれたら、SDカードのフォルダを開いてください。
出し方がわからない人はPC画面左下の検索バーに「PC」と打ち込み出てきた「PC」をクリック → 出てきたフォルダのうち「デバイスとドライブ」の欄にあります(下図参照)
{{:gimmickkouza:electronic_basic:6:6-10.png?600|}}
== 手順2:SDカードにフォルダを作り、MP3ファイルを転送する ==
サンプルコード6-2-1のフォルダ内にある「001_sample1.mp3」ファイルをコピーし、SDカードのフォルダ内直下に貼り付けてください。
__今回のサンプルコードではSDカード内部にフォルダを作る必要はありません。MP3ファイルを直で貼り付けてください。__
=== その2:ライブラリのインストール編 ===
またしても下準備です!今度はDFPlayerを動かすためのライブラリをインストールします!
前項で使用したServoライブラリは、Arduinoをインストールした際にデフォルトでインストールされていましたが、世の中の大半のライブラリは自分で探してインストール必要があります。
ArduinoIDEにライブラリをインストールする方法は2つあります。
一つはArduinoIDEの機能である「ライブラリマネージャ」を使う方法、もう一つはzipファイルを使ってインストールする方法です。
今回は前者の方法でインストールします。
== 手順1:ライブラリマネージャを開く ==
{{:gimmickkouza:electronic_basic:6:6-11.png?400|}}
ライブラリマネージャを開きます。
ArduinoIDEの左側に並んでいるアイコンのうち、上から三つめがライブラリマネージャです。
== 手順2:ライブラリを探す ==
{{:gimmickkouza:electronic_basic:6:6-12.png?400|}}
アイコンをクリックするとライブラリマネージャが出現します。
今回はDFPlayer用のライブラリを探したいので、検索欄に「DFPlayer」と打ち込みます。
すると検索ワードに該当するライブラリがズラーっと下に現れます。
DFPlayerはかなり人気のモジュールで、多くの人が独自のライブラリを作ってアップロードしているため、検索結果も非常に多いです。
== 手順3:公式のライブラリを見つけてインストールする ==
{{:gimmickkouza:electronic_basic:6:6-14.png?400|}}
たくさんのライブラリがありますが、今回はDFPlayerの開発元であるDFRobotがリリースしている、いわば"公式の"ライブラリを使用します。
検索結果をスクロールし、__ライブラリ名が「DFRobotDFPlayerMini」開発者が「by DFRobot」のもの__をインストールしてください。
__このライブラリがインストールされていないと本資料のサンプルコードは動きませんのでご注意ください。__
ライブラリのバージョンは最新版で大丈夫です。
(万が一、最新版のライブラリを使ってエラーが出る場合は、講師が使った1.0.6にダウングレードしてみてください)
/*バージョンは最新版で大丈夫とか一言あってもよさそう*/
== 手順4:公式のライブラリを見つけてインストールする ==
{{:gimmickkouza:electronic_basic:6:6-15.png?400|}}
「Installed DFRobotDFPlayerMini @(バージョン番号)」のメッセージが、画面下部のメッセージエリアに出ていればライブラリのインストールは完了です。
== おまけ:ついでなのでライブラリ付属のサンプルコードの出し方も覚えちゃってください!! ==
{{:gimmickkouza:electronic_basic:6:6-16.png?600|}}
ライブラリをインストールすると、そのライブラリの使い方見本としてサンプルコードがセットでインストールされます。
ArduinoIDEのツールバーから、ファイル → スケッチ例 → ライブラリの名前 と進んでいくと、そのライブラリを使ったサンプルコードの一覧が表示されます。
例えばDFRobotDFPlayerMiniライブラリだと、
* AdvancedSetting
* FullFunction
* GetStarted
* ReadValues
の4つのコードが入っています。
==== UARTの仕組み ====
=== その前に、「シリアル通信」についてもうちょっと詳しく知ろう ===
みなさんはHelloWorldの項からずっと「Arduinoから送られてきた数値や文字列を、パソコン上のシリアルモニタに表示する」という使い方でシリアル通信を使いこなし続けています。
この「シリアル通信」について、もう少し詳しく説明したいと思います。
(RPGで例えるなら最序盤に加入した謎多きキャラの素性が終盤明らかになり同時に強化が入る激アツパートです)
----
Arduinoでデータ通信を行う際に、主に使われるのは「シリアル通信」と「パラレル通信」です。
== シリアル通信 ==
「シリアル通信」は、"データを送信するための信号線を1本or2本使用して、HIGH(=1)とLOW(=0)のデータを連続的にやり取りする通信方式"です。
シリアル(Serial)は「連続した」の意です(連続殺人犯をシリアルキラーって呼びますよね!物騒!)
{{:gimmickkouza:electronic_basic:6:6-17.png?400|}}
シリアル通信のざっくり概念図です。
1本の信号線を通して、送信側が「00011100111…」と連続的にデータを送り、受信側も「00011100111…」と受信します。
シリアル通信は身近なところでも非常によく使われています。
例えばどこにでもある"USB"や"有線LAN"はシリアル通信方式で通信をしています。
== パラレル通信 ==
一方、「パラレル通信」は"複数の信号線を使って、同時並列でデータを送信する通信方式"です。
{{:gimmickkouza:electronic_basic:6:6-18.png?400|}}
パラレル通信のざっくり概念図です。
Arduinoでパラレル通信をする場合は、8本の線を接続します。この8本の線がそれぞれ並行してデータを送ります。
パラレル通信のデメリットは、なんと言っても使用するソケット/ピンの数が多すぎることです。
Arduinoのソケット/ピンの数は有限(特に小型のNano等はシビア)なので、できれば使いたくないですし、その点信号線が少なくて済むシリアル通信に軍配が上がります。
通信速度の面でも、一般的にはパラレル通信の方が速いと言われてはいますが、技術の進歩によりシリアル通信も十分高速となり、速度以外の優位性も込みでシリアル通信が主流になっています。
Arduinoと電子部品の間でパラレル通信をすることももちろん可能ではあるのですが、「わざわざ電子部品を追加購入してまでやってもらうメリットは感じないなあ…」という講師の思想により、本資料ではカットさせていただきます。
興味のある方はぜひ調べてチャレンジしてみてください。
例えば下記のページでは非常にわかりやすくレクチャーしてくださっています。
【Arduino入門編⑰】LCDディスプレイに文字を表示させてみる![前編](ぶらり@web走り書き):
https://burariweb.info/electronic-work/arduino-learning/arduino-lcd-module-display.html
----
**「シリアル通信」にはたくさんの種類があります。Arduinoと電子部品の間の通信では「UART」・「I2C」・「SPI」の3つの方式が使えます。**
どの通信方式で通信できるかは、個々の電子部品の仕様により決まっています。
本項で使用する「DFPlayerモジュール」では、「UART」によりArduinoと通信します。
=== UARTとは ===
「UART」はUniversal Asynchronous Receiver/Transmitterの略で、「ユーアート」と呼びます。
UARTはArduinoでは一番よく使うシリアル通信です。
"Arduinoからパソコンのシリアルモニタに数値や文字列を表示させる"為に使っているシリアル通信はUARTですし、パソコンからArduinoにプログラムを書き込む時に使う通信方式もUARTです。
Arduino対パソコンでUARTを使うときは、USBケーブルを信号線として通信しています。
一方、Arduino対電子部品のUARTでは、USBの代わりに2本のジャンパ線を信号線として使います。
UARTはとにかくシンプルなシリアル通信です。
他のシリアル通信と比較してみたときに、UARTに固有な特徴はざっくり以下の2つです。
* 「RX」と「TX」の2本の信号線を使う
* Arduino対部品間で、同期を取らない通信をする(非同期通信)
==== 配線図 ====
DFPlayerはちょっと複雑な電子部品です。
Arduinoに接続するにあたって、まずはDFPlayerのピンアサイン(ピンの配置。どのピンがどんな役割を持っていて、Arduinoのどこに繋ぐべきか)を確認します。
下の図は秋月電子通商のDFPlayer販売ページ掲載のデータシートから引用しています。
{{:gimmickkouza:electronic_basic:6:6-19.png?600|}}
今回必要となるピンについてのみ説明します。
== ①VCC ==
VCCは電源を取るピンです。Arduino側は電源を供給するピン、すなわち5Vか3.3Vのどちらかを接続します。
どちらを接続すべきかは、このモジュールの動作電圧をデータシートで確認する必要があります。
DFPlayerのInputVoltageは3.2V~5Vとの記載がありますが、ベストな電圧は4.2Vのようです。今回は5Vに接続します。
== ②RX ==
UART形式のシリアル通信で出てくるピンです。RXはReceiverつまり"受信側"のピンです。
データを受信する役割のピンなので、ArduinoのTX(Transmitter=送信側)と繋ぎます。
== ③TX ==
こちらもUARTで出てくるピンで、RXと対になる、送信側のピンです。
DFPlayerからデータを送信するので、Arduino側はそれを受信するRXに繋ぎます。
このように、UARTでは部品側とArduino側のRX/TXが互い違いになるように繋ぎます。
== ④SPK_1 ==
SPK_1ってなんのこっちゃ?って感じですが、データシートを見ると説明が書いてあります。
Speaker+とあるので、このピンにはスピーカーの+極を繋ぎます。
(データシートの表の順はピンの順番通りではないのでご注意ください)
ちなみに「Drive speaker less than 3W」とあるので、3W未満のスピーカーしか使えません。
(今回購入リストで指示したスピーカーは、もちろん要件を満たしたものになっています)
== ⑤GND ==
おなじみGNDです。ArduinoのGNDに繋ぎます。
== ⑥SPK_2 ==
こちらもデータシートを見ると丸わかりです。スピーカーの-極を繋ぎます。
== ⑦Busy ==
再生状態を示すピンです。ファイルが再生されている間はLOW、終わるとHIGHを出力します。
=== DFPlayer配線の注意点 ===
さて、秋月電子通商のDFPlayer販売ページには、データシートとは別に「参考資料(IOの電圧許容値について)」というPDFが掲載されていました。
開くと出てくるのが下の図です。
{{:gimmickkouza:electronic_basic:6:6-20.png?600|}}
これどういうことかと言いますと、**DFPlayerの動作電圧はもともと3.3V**なのです。
ただ「幅広い電圧の電源を供給できるように降圧回路を設計」されているため、5Vの電圧を入力されても、モジュール上で電圧を降圧できるため、動作に支障はないということなのです。
ただし、あくまでVCCから入力される電圧を降圧しているだけなので、**VCCとGND以外のすべてのピン(これらの"電源に関係ないピン"のことをI/Oピンと呼びます)の電圧は3.3Vしか扱えません。**
今回問題になるのはDFPlayerのRXピンです。
このRXを含むすべてのI/Oピンは3.3Vしか扱えないので、__このピンにArduinoのTX(Arduinoの動作電圧は5Vなので、ArduinoのI/Oピンからは5Vの電圧で電気が出力されます)を直通させてしまうのはNGです!__
この対策として、DFPlayerのRXとArduinoのTX間には1kΩの抵抗を挟むことにします。
== 講師の入れ知恵 ==
{{ :gimmickkouza:electronic_basic:6:6-21.png?200|}}
この手の落とし穴に自発的に気づくのは(特に初心者のうちは)非常に難しいです。
っていうか無理です。
なので、講師は初めて使う部品は他の方の作例をググって複数比較し参考にしています。
ご丁寧に落とし穴を解説してくれていることもあれば、
「この抵抗は何のために入ってるんだろう?🤔」
というような形で自発的に落とし穴に気づけることもあります。
もちろん他の方の作例が間違っていることもあるので、常に"何が正しいか"を考え続けるのが大切です。
----
{{:gimmickkouza:electronic_basic:6:6-2-1.png?600|}}
そんなわけでようやく配線図です。
DFPlayerのRXとArduinoのTXの間には、先述の通り1kΩの抵抗を入れてください。
__この図ではパッと見DFPlayerの向きがわかりにくいのですが、SDカードの挿入口が右側にある状態です。__
**また先述の通り、RXとTXは互い違いになるように(つまりDFPlayerのRXはArduinoのTX(D1)に、DFPlayerのTXはArduinoのRX(D0)に)繋いでください。**
今回ArduinoののD0とD1はデジタル入出力ではなく、UART(RX/TX)として使います。
[[gimmickkouza:electronic_basic:5:4_stepup#5-4-2_CdS_with_LED|]]で「D0とD1はデジタル入出力には使わない方が無難!」と書いていますが、今回はUARTとして使うので大丈夫です。
===使う部品リスト===
^ 部品 ^ 個数 ^
| DFPlayer | 1個 |
| 抵抗器(1kΩ) | 1個 |
| ブレッドボード用スピーカー | 1個 |
/*使う部品の表が欲しい
下の注意、の部分でもいいんですが、
注意ポイントとして、DFplayerの向きと、TXRXの配線の確認をお願いしたいです。
ほぼ絶対逆にする。私も逆にしました。*/
== ⚠注意⚠ ==
こちらの配線図及びサンプルコード6-2-1には、Arduino Uno R4シリーズでしか動作しない固有の仕様が含まれています。
なので、Arduino Uno R3(及び、R4シリーズではない他のArduino)で上の図の配線及び6-2-1を実行しようとすると、エラーが出て動作しません!
__Uno R4シリーズ以外のArduinoをお使いの方は、6-2-1の説明は目を通すだけにして、実際のコードはR4以外対応版となるサンプルコード6-2-2をご利用ください。__
ちなみに6-2-2では「R4シリーズ以外のArduinoで2つ以上のUARTを同時に使いたい時」のやり方を履修できます。
またそれだけでなく「R4シリーズでUARTを同時に3つ以上使いたい時」にも役立つ内容となっております。
R4をお持ちの方も、よければチャレンジしてみてください。
==== サンプルコード解説 ====
サンプルコード6-2-1_DFPlayer_UART.inoを開いてください。
#include "Arduino.h"//ライブラリをインクルードします
#include "DFRobotDFPlayerMini.h"
int busy_PIN = 9;//busyピンはD9に接続
DFRobotDFPlayerMini myDFP; //インスタンスmyDFPを生成
void setup() {
Serial.begin(9600); //シリアルモニタ用のシリアルを初期化
Serial1.begin(9600); //DFPlayerとの通信用のシリアルを初期化
pinMode(busy_PIN, INPUT); //D9を入力モードに設定
if (!myDFP.begin(Serial1)) {
//DFPlayerを初期化。2秒以内に初期化できなければエラーメッセージを表示
Serial.println(F("Unable to begin:"));
while (true);
}
myDFP.volume(25);//ボリュームを25に設定(指定できる値は0~30)
}
void loop() {
myDFP.play(1);//SDカード内の先頭にあるmp3ファイルを再生
while(digitalRead(busy_PIN) == LOW){
//BusyピンがLOW(ファイル再生中)ならdelayを無限ループして再生を続ける
delay(100);
}
}
== 1行目 ==
#include "Arduino.h"//ライブラリをインクルードします
このスケッチで使うライブラリをインクルードします。
"Arduino.h"は、Arduinoを動かすための基本的なことが書かれているライブラリです。
実は#include "Arduino.h"の一文を書かなくても、すべてのスケッチにはこのArduino.hが(見えないところで)インクルードされるようになっています。
今回「DFRobotDFPlayerMini」ライブラリ用のサンプルコード(スケッチ例)を召喚したところ、この#include "Arduino.h"が明示されていたため、これはそのまま生かしておくことにしました。
多分ESP32等向けのインクルードだと思います。今回はなくてもOKです
== 2行目 ==
#include "DFRobotDFPlayerMini.h"
今回のキモのライブラリ、"DFRobotDFPlayerMini.h"をインクルードします。
これが抜けていると必ずエラーが出て動作しません。
== 3行目 ==
int busy_PIN = 9;//busyピンはD9に接続
今回DFPlayerのBusyピンはD9に繋ぎましたので、これを変数に入れておきます。
== 5行目 ==
DFRobotDFPlayerMini myDFP; //インスタンスmyDFPを生成
インスタンスの生成です。今回はmyDFPというシンプルなインスタンス名にしました。
== 8行目 ==
Serial.begin(9600); //シリアルモニタ用のシリアルを初期化
シリアルモニタを使用するために、いつも通りSerial.begin(9600); で初期化します。
== 9行目 ==
Serial1.begin(9600); //DFPlayerとの通信用のシリアルを初期化
シリアルの初期化がもう一回出てきました。よく見るとSerialではなくSerial1とあります。
今回、このスケッチでは2つのUARTを使用します。
一つはArduinoのPC間で通信を行い、シリアルモニタにメッセージを表示するため。
もう一つは、DFPlayerとArduinoの間でMP3ファイル再生に必要な通信を行うためです。
**UARTを複数同時に使用する場合は、各UARTごとに初期化や命令をする必要があります。**
Arduino R4シリーズでは、シリアルモニタなどArduinoとPC間で行うシリアル通信には
Serialを、D0(RX)とD1(TX)ソケットを使ったシリアル通信にはSerial1を使うように定められていますので、こちらのSerial1の方も初期化を行います。
R4シリーズでは~と書いてある通り、この部分はR4独自の仕様となっています。
**R4以外のArduinoを使う場合は、このシリアルの使い方が異なります**(詳細は6-2-2で!)
/*気になる人は、前回繋いじゃいけないピンって言ってなかった?ってなるかもですね...UART用途で使うからOKなのですが*/
== 10行目 ==
pinMode(busy_PIN, INPUT); //D9を入力モードに設定
Busyからの信号を受け取るD9も、忘れずに初期化してあげてください。
== 12~16行目 ==
if (!myDFP.begin(Serial1)) {
//DFPlayerを初期化。2秒以内に初期化できなければエラーメッセージを表示
Serial.println(F("Unable to begin:"));
while (true);
}
このif文は、DFPlayerの初期化ができなかった場合に、シリアルモニタにエラーメッセージを表示してくれるちょっと親切な処理です。
"初期化できない場合"は、例えばSDカードがうまく挿さっていなかったり、そもそもDFPlayerが接続されていなかったりした場合などが想定されます。
条件文の(!myDFP.begin(Serial1))ですが、myDFP.beginの引数にはDFPlayerの接続されているシリアル名(今回ならSerial1)が入ります。
そして文の冒頭に否定を表すブール演算子の「!」がついています。「!」はtrueであればfalse、falseであればtrueを返すのでmyDFP.begin(Serial1)の結果がfalse(=beginしてない)であればtrueを返し、if文の内部に突入しエラーメッセージを表示します。
== 17行目 ==
myDFP.volume(25);//ボリュームを25に設定(指定できる値は0~30)
先ほどのif文に突入しなければ(=DFPlayerの初期化ができていれば)、ここでDFPlayerの音量を調整します。音量は0から30までの数値で指定できます。
音量はここでのボリューム調整以外にも、スピーカーの性能などによっても変わります。
今回教材として指定したワイヤ付きスピーカーは、お世辞にもつよつよな性能ではないので、プログラム側である程度大きめの音量「25」を指定しています。
== 21行目 ==
myDFP.play(1);//SDカード内の先頭にあるmp3ファイルを再生
MP3ファイルを再生します。
myDFP.play(1);は、「SDカードに直入れしてあるMP3ファイルのうち、一番最初に入れたものを1回再生する」命令です。
引数の(1)は、「一番最初に入れたファイル」を指します。もしファイルが複数入れてある場合、ここを(2)にすると「二番目に入れたファイル」が再生されます。
== 22~25行目 ==
while(digitalRead(busy_PIN) == LOW){
//BusyピンがLOW(ファイル再生中)ならdelayを無限ループして再生を続ける
delay(100);
}
while文です。条件が成立している間はdelay(100);を繰り返すようになっています。
"DFRobotDFPlayerMini.h"ライブラリには**仕様上の罠**がありまして、それは「ファイルの再生が終わっていようがなかろうが次の処理に移ってしまう」というものです。
例えば今回再生したい0001_sample1.mp3ファイルは再生時間が約3分07秒あります。
21行目にこのファイルを再生する処理が始まりますが、そのファイルがまだ再生中かどうかはお構いなしにプログラムは進行し、loop文の先頭に戻りもう一度音楽を再生する処理が始まるもまたloop文の先頭に戻り…という処理が超高速で繰り替えされ、結果的に音が全く出力されないという状態に陥ります。
なので、__myDFP.play(1);の命令をした後は、再生時間の間delay等を使ってプログラムの進行を止めておく必要があります。__
しかし、delay(187000);とするのはまったく現実的ではありません。(そもそも無理です。下記参照)
また、どれだけの時間delayで止めるべきかはファイルの再生時間によって変わります。
もしファイルを入れ替える場合や、再生したいファイルが複数ある場合に、ファイルごとに個別にdelayを設定するのはあまり賢い方法とは言えません。
ここでようやくBusyピンの登場です。**Busyピンはファイルが再生している間はLOW、再生していない間はHIGHを出力します。**
while(digitalRead(busy_PIN) == LOW)とすることで、BusyピンがLOWである間(=再生が続いている)は、delay(100);を繰り返す処理を行います。
これならトンチキな長さのdelayを書く必要もないですし、異なる再生時間のファイルが複数あったとしても、そのすべてに柔軟に対応できます。
MP3ファイルの再生が終わると、loop文の先頭に戻りもう一度ファイルの再生が始まります。
ちなみにdelayには限界があって、32767msが最大値です
===== 6-2-2_DFPlayer_UART_SoftwareSerial =====
なぜ6-2-1のコードは、R3系などのArduinoでは使えないのでしょうか。
R3系等のArduinoで同じ処理をするにあたっては、Arduinoが持っているシリアル通信の"種類"の違いについて知る必要があります。
==== 「ハードウェアシリアル」と「ソフトウェアシリアル」について ====
ArduinoのRX・TX、それからUSBは、Arduinoの脳みそともいえるマイコンチップに繋がっており、チップに内蔵されたUARTの機能を使って通信を行っています。
このハードウェア的な機能によるシリアル通信を、「ハードウェアシリアル(Hardware Serial)」と呼びます。
おなじみのSerialから始まる命令文が、「ハードウェアシリアル」を使っている時の命令文です。
この**ハードウェアシリアルは、Arduinoの種類によって搭載している数が異なります。**
Uno__ R4では、USBの通信を担うSerialとRX・TXピンのSerial1の2つ__を搭載しています。
しかし__Uno R3では、USBとRX・TXのシリアルはなんと兼用__になっており、つまりハードウェアシリアルは1つしか搭載しておらず、そのためSerialしか使えません。
サンプルコード6-2-1をR3等のArduinoで使えないのは、ずばりこれが原因です。
(逆にハードウェアシリアルを4つ搭載したArduino Mega 2560なんてのもあります)
こちらのページも参考に!:[[gimmickkouza:electronic_basic:7:3-r3-r4|]]
で、このようなハードウェアシリアルの数以上にUARTを繋ぎたいシーンで役に立つのが「ソフトウェアシリアル(Software Serial)」です。
**「ソフトウェアシリアル」は、ハードウェアシリアルの機能をプログラムで"模倣"することで、機能的には同じように使えるようにしたもの**です。
ソフトウェアシリアル、複数使えるしピンの制限もないしめっちゃ便利じゃん!と思いそうですが、なんとハードウェアシリアルよりも通信速度が遅いのです。
最大115200 bps。ソフトウェア内での処理が重いと9600でもキツい…。
/*ソフトウェアシリアル、複数使えるしピンの制限もないしめっちゃ便利じゃん!と思いそうですが、なんとハードウェアシリアルよりも通信速度が遅いのです。最大115200 bps。ソフトウェア内での処理が重いと9600でもキツい…。*/
==== 配線図 ====
{{:gimmickkouza:electronic_basic:6:6-2-2.png?600|}}
6-2-1との違いは、ArduinoのD0(RX)に繋いでいたワイヤはD3に、D1(TX)に繋いでいたワイヤはD4に配線されていることです。
**ソフトウェアシリアルを使う場合、RX・TXとして使うソケットはスケッチ内で指定します。**
Uno R4では、TXはほとんどのソケットが指定できます(とはいえ、DかAのどれかにするのが無難です)が、RXはD0, D1, D2, D3, D8, D14, D15, A1, A2, A3, A4, A5のどれかにする必要があります。
なので今回はD3をRX、D4をTXとして使用することにします。
===使う部品リスト===
^ 部品 ^ 個数 ^
| DFPlayer | 1個 |
| 抵抗器(1kΩ) | 1個 |
| ブレッドボード用スピーカー | 1個 |
/*使う部品の表が欲しい*/
==== サンプルコード解説 ====
サンプルコード6-2-2_DFPlayer_UART_SoftwareSerial.inoを開いてください。
#include "Arduino.h"//ライブラリをインクルードします
#include "SoftwareSerial.h"
#include "DFRobotDFPlayerMini.h"
int busy_PIN = 9;//busyピンはD9に接続
SoftwareSerial mySoftwareSerial(3, 4); //インスタンス作成とRX、TXピンの指定
DFRobotDFPlayerMini myDFP; //インスタンスmyDFPを生成
void setup() {
Serial.begin(9600); //シリアルモニタ用のハードウェアシリアルを初期化
mySoftwareSerial.begin(9600); //DFPlayer通信用のソフトウェアシリアルを初期化
pinMode(busy_PIN, INPUT); //D9を入力モードに設定
if (!myDFP.begin(mySoftwareSerial)) {
//DFPlayerを初期化。2秒以内に初期化できなければエラーメッセージを表示
Serial.println(F("Unable to begin:"));
while (true);
}
myDFP.volume(25);//ボリュームを25に設定(指定できる値は0~30)
}
void loop() {
myDFP.play(1);//SDカード内の先頭にあるmp3ファイルを再生
while(digitalRead(busy_PIN) == LOW);{
//BusyピンがLOW(ファイル再生中)ならdelayを無限ループして再生を続ける
delay(100);
}
}
6-2-1との相違点(つまりソフトウェアシリアルに関わる部分)の解説のみ書いていきます。
DFPlayerの操作に係るコードの解説は6-2-1の解説をご参照ください
== 2行目 ==
#include "SoftwareSerial.h"
ソフトウェアシリアルを使うには、"SoftwareSerial.h"ライブラリのインクルードが必要です。
世の中便利な機能は大体ライブラリ頼みです。
ちなみにSoftwareSerialライブラリはServoライブラリと同じように、ArduinoIDEにデフォルトで入っているライブラリなので、インストールは不要です。
== 6行目 ==
SoftwareSerial mySoftwareSerial(3, 4); //インスタンス作成とRX、TXピンの指定
ソフトウェアシリアルのインスタンスを生成します。
インスタンスの生成と同時に、どのソケットをRX・TXピンとして使うかを指定します。
今回RXはD3、TXはD4にしたいのでSoftwareSerial mySoftwareSerial(3, 4);と書いています。
== 11行目 ==
mySoftwareSerial.begin(9600); //DFPlayer通信用のソフトウェアシリアルを初期化
6-2-1では2つ目のハードウェアシリアルを初期化するために、Serial1を指定していました。
今回はソフトウェアシリアルを初期化したいたので、指定するのはmySoftwareSerialです。
== 14行目 ==
if (!myDFP.begin(mySoftwareSerial)) {
ここでも指定するシリアルをSerial1からmySoftwareSerialに変えるだけです。
===== 6-2-3_DFPlayer_SoundPlayer_for_fursuit =====
{{youtube>XUJsuB6To_c?large}}
仮にも"きぐるみギミック"制作講座を標榜しているので、きぐるみのギミックとして実際に役立ちそうな、或いはそのヒントになりそうな作例もお届けしていこうかなと思います。
とりあえず、DFPlayerで音を鳴らせるようになったので、タクトスイッチと組み合わせて好きなタイミングで任意のサウンドを鳴らせる、簡易サウンドプレイヤーを作ってみました。
なおこのコーナーはオマケ的な存在ですので飛ばしてもらってもOKです**(でもたのしいよ…)**
==== 実際に制作物に搭載する時のことを考えて部品を選び回路を作るということ ====
今回サンプルサウンドは4種類ご用意しました。
この4種類のサウンドを、タクトスイッチで切り替えて、好きなタイミングで再生します。
ふつーーーに考えると、一つのサウンドにスイッチを一つ割り当てて、計4つのスイッチでサウンドを再生する…方式でももちろんOKなのですが!
この方式だと再生したいサウンドが例えば10種類になると、スイッチを10個配置しなければなりません。
サウンドが増えるほどスイッチが増えて、巨大なコントローラが出来上がります。
講師が実際にきぐるみギミックを作る際は、少ないスイッチで複数のファンクションを切り替える「トリガー方式」をベースにしてコントローラを作ることが多いです。
== トリガー方式について ==
今回のサンプルコードで使うタクトスイッチは2つです!
2つ並んだスイッチのうち、左側がトリガーボタン、右側が選択ボタンです。
{{:gimmickkouza:electronic_basic:6:6-23.png?600|}}
使い方です。まず選択ボタンをポチポチ押して再生したいサウンドを選びます。
例えばsample3を再生したい場合は、選択ボタンを2回押します。
その状態でトリガーボタンを押すと、選択ボタンで選択されたsample3が再生されます。
こんな感じの設計にすることで、講師は制作物に実装するボタンの数を削減したりしています。
(勿論どんな仕様にしたいかは好き好きなので、参考程度に見て頂ければOKです)
==== 下準備(MP3ファイルをSDに入れる) ====
まず**SDカード内に半角数字の「1」という名前の空のフォルダを作ってください。**
作ったフォルダ「1」を開き、中にsample1~4の4つのファイルを入れるのですが、この時**sample1から4のファイルを、"順番に一つずつコピペ"**します。
理由は、DFRobotDFPlayerMiniライブラリでは、スケッチでMP3ファイルの再生を命令するときに、__"ファイル名の順番"でなく、"SDカードに書き込んだ順番"で再生を行うという仕様__になっている為です。
(例えばsample4→3→2→1の順番にコピペをした場合「フォルダ内の1つめのファイルを再生」という命令をするとsample4が再生されます)
==== 配線図 ====
{{:gimmickkouza:electronic_basic:6:6-2-3.png?600|}}
何のひねりもない配線図です。
===使う部品リスト===
^ 部品 ^ 個数 ^
| DFPlayer | 1個 |
| 抵抗器(1kΩ) | 1個 |
| ブレッドボード用スピーカー | 1個 |
| タクトスイッチ | 2個 |
==== サンプルコード解説 ====
サンプルコード6-2-3_DFPlayer_SoundPlayer_for_fursuitを開いてください。
/*使う部品の表が欲しい
コピーしてみたら全角スペースいたので貼り変えてもいいかも
{{:gimmickkouza:electronic_basic:6:pasted:20240616-193823.png}}
*/
#include "Arduino.h"//ライブラリをインクルードします
#include "SoftwareSerial.h"
#include "DFRobotDFPlayerMini.h"
int busy_PIN = 9;//busyピンはD9に接続
int TRIGGER_PIN = 10;//トリガーボタンはD10に接続
int SELECT_PIN = 11;//選択ボタンはD11に接続
int CURRENT_FILE = 1;//現在選択されているファイルを記憶する変数
SoftwareSerial mySoftwareSerial(3, 4); //インスタンス作成とRX、TXピンの指定
DFRobotDFPlayerMini myDFP; //インスタンスmyDFPを生成
void setup() {
Serial.begin(9600); //シリアルモニタ用のハードウェアシリアルを初期化
mySoftwareSerial.begin(9600); //DFPlayer通信用のソフトウェアシリアルを初期化
pinMode(busy_PIN, INPUT); //D9を入力モードに設定
pinMode(TRIGGER_PIN, INPUT_PULLUP);//D10の内部プルアップ抵抗を有効にし入力モードに
pinMode(SELECT_PIN, INPUT_PULLUP);//D11の内部プルアップ抵抗を有効にし入力モードに
if (!myDFP.begin(mySoftwareSerial)){
//DFPlayerを初期化。2秒以内に初期化できなければエラーメッセージを表示
Serial.println(F("Unable to begin:"));
while (true);
}
myDFP.volume(25);//ボリュームを25に設定(指定できる値は0~30)
}
void loop() {
if((digitalRead(SELECT_PIN)== LOW) && CURRENT_FILE < 4){
//選択ボタンが押され、且つCURRENT_FILEが4未満の場合
CURRENT_FILE = CURRENT_FILE + 1;//次の曲を選択する
while (digitalRead(SELECT_PIN) == LOW) {
delay(100);
}
}
if(digitalRead(TRIGGER_PIN) == LOW){
myDFP.playFolder(1, CURRENT_FILE);
//「1」フォルダ内にあるCURRENT_FILE番目のファイルを再生
CURRENT_FILE = 1;//トリガーが押されたので曲選択は初期値に戻す
while(digitalRead(busy_PIN) == LOW){
//BusyピンがLOW(ファイル再生中)ならdelayを無限ループして再生を続ける
delay(100);
if(digitalRead(TRIGGER_PIN) == LOW){
break;
}
}
while (digitalRead(TRIGGER_PIN) == LOW) {
delay(100);
}
}
}
めまいがしそうな長さのコードですが、大部分は今までの復習です。
がんばっていきましょう。
== 8行目 ==
int CURRENT_FILE = 1;//現在選択されているファイルを記憶する変数
今回の仕掛けでは、選択ボタンを押した回数によって再生するファイルを切り替えます。
なので、選択ボタンを押した回数を記憶しておく変数CURRENT_FILEを用意します。
初期値は「1」です。「0」にしない理由は、今回このCURRENT_FILE変数は、ボタンが押された回数を記憶しておくと同時に、トリガーボタンが押された時に再生するファイルの指定にも使う(例えば選択ボタンを1回も押していない状態ではCURRENT_FILEは「1」で、これをそのまま"「1」番目の曲を再生する"命令に使います)ためです。
== 17~18行目 ==
pinMode(TRIGGER_PIN, INPUT_PULLUP);//D10の内部プルアップ抵抗を有効にし入力モードに
pinMode(SELECT_PIN, INPUT_PULLUP);//D11の内部プルアップ抵抗を有効にし入力モードに
ボタン二種、D10とD11を入力モードにします。
今回は内部プルアップ抵抗を使うので、どちらもINPUT_PULLUPにしておきます。
== 29行目 ==
if((digitalRead(SELECT_PIN)== LOW) && CURRENT_FILE < 4){
選択ボタンが押された場合の処理です。条件式を見ると「&&」を挟み2つの条件があります。
左側は「選択ボタンがLOWになったら=押されたらtrue」です。
右側は「CURRENT_FILEが4未満ならtrue」です。
今回SDに入れた曲は4曲です。なので、例えば選択ボタンを10連打してトリガーを押されてしまうと、SD上に存在しない11曲目を再生しようとしてしまいます。
これを防ぐため、選択ボタンを既に3回連続で押している場合は、if文の内部に入れさせない(=それ以上CURRENT_FILEを+1させない)ようにしています。
== 37行目 ==
if(digitalRead(TRIGGER_PIN) == LOW){
今度はトリガーボタンが押された場合の処理です。
== 38行目 ==
myDFP.playFolder(1, CURRENT_FILE);
myDFP.playFolderはフォルダを指定しその内部の曲を再生する命令です。
引数は(1, CURRENT_FILE);となっています。
まず第一引数は「1」です。これは「1」フォルダを意味します。
SD内部に複数のフォルダを作成しておけば、違うフォルダを指定することもできます。
フォルダ名は(命名規則がかなりめんどくさいようなので)半角数字にしておくのが無難です。
(参考:http://twinklesmile.blog42.fc2.com/blog-entry-546.html)
第二引数では再生したいファイルの順番を数値で指定しますが、先述の通り、この"順番"は先述の通り、"ファイル名の順番"でなく、"SDカードに書き込んだ順番"となるので注意が必要です。
今回はCURRENT_FILEの数値がそのまま曲順となりますので、変数ごとブチ込みます。
== 40行目 ==
CURRENT_FILE = 1;//トリガーが押されたので曲選択は初期値に戻す
トリガーボタンが押されたので、CURRENT_FILEを初期値の「1」にリセットしておきます。
== 44~46行目 ==
if(digitalRead(TRIGGER_PIN) == LOW){
break;
}
不思議なif文です。条件は、「トリガーボタンが押された場合true」です。
これは、曲を再生している最中にトリガーが押された場合に、再生中の曲を中断して新しい曲の再生を始めるための処理です。
**__45行目のbreak;は、while文やfor文などのループ処理の中にいるときに使える命令で、そのループ処理を問答無用で終了させることができます。__**
今回は、曲の再生を続けるためにdelay(100);を繰り返しているwhile文が強制終了するので、曲の演奏が止まります。
{{:gimmickkouza:electronic_basic:6:6-22.png?600|}}