最終更新日:2020年3月18日
【履歴】
MP3Playerモジュールはとても安価に提供されています。TFカードリーダとして使える様なモノも有りますが、基本インタフェースがシリアル通信となっているモノを此処では対象としています。
オリジナルはDFRobot社のMini MP3 Player For Arduinoかと思われます。$8.9の安価なモジュールですがいろいろな使い道がありそうですので取り上げます。※$1以下のモジュールもあります。
DFROBOT社からライブラリが提供されていますが、残念ながらそのまま使えるライブラリはないはずです。
https://github.com/DFRobot/DFRobotDFPlayerMini
『SoftwareSerial.h』をCallするライブラリはSTM32duinoではそのまま使えません。Serial1に置き換えてスケッチをレタッチする必要があります。
今回の構想は、LabVIEW側にクライアントソフトを用意し、コマンドを駆使してMP3の再生をリモート制御します。LabVIEWからBluePill間はTCPsocketで、それをシリアルでDFPlayerMiniに送りスピーカから音を出そうというモノです。
DFPlayerMiniで音楽を聴こうとする方は少ないと考えます。音はそれなりに出るのですが、ノイズが酷いです。この対策ができる方なら音楽も十分に楽しめるとは思います。
一方、メッセージの自動再生と考えると使い道が有ると思います。予め用意した音声リストをTFカードに認めて、リモートで『発声』させるわけです。
DFPlayer制御用のコマンドは『YX5200-24SS』を理解する必要がありますが、この資料は日本語版が無いようです。セカンドソースの『KT403A』には日本語訳版がありましたので、こちらを参照して、LabVIEW側のコードを書きます。
基本的な動作は、まずは単体で動く様に仕立てます。電源供給とデータ供給さえすれば数個のボタン操作で何らかの結果が出るはずです。
※オリジナルchipを『YX5200-24SS』と解釈したのは以下のフォーラム内容を見て判断しました。
https://discourse.voss.earth/t/dfplayer-verschiedene-versionen/681
『KT403A』には日本語訳版の資料から以下の条件があることが判りました。
DFPlayerMiniは単体動作確認が出来ます。スイッチを4つ接続し、mp3ファイルを搭載したTFカードを挿入。スピーカを接続して通電するします。いずれかのボタンを押すと、本体のLEDが点灯し再生が始まります。これで単体のモジュール動作確認が出来たことになります。
スピーカインピーダンスは8Ω 出力は最大3Wとのことです。
ただ機械的なボタンはチャタリング対策をちゃんとしないといけません。外部コマンドコントロールの方が楽とも云えます。
※同じPin位置に接続したSWでもモジュールに搭載されているChipによって機能が異なります。注意してください |
|
S1: | 短く押すと次曲のを再生 長押しすると音量を下げます。 |
S2: | 短く押すと前の曲を再生 長押しすると音量を上げげます。 |
S3: | 抵抗値を噛ませてグランドに落とすことで10通りの機能を実現します |
S4: | 抵抗値を噛ませてグランドに落とすことで10通りの機能を実現します |
音声ノイズ対策として 先人のアドバイスをリストアップしておきます。
シリアル通信をして制御出来ます。設定は、
です。
コマンドはたくさん用意されています。
チップによってコマンド体系が微妙に変わるようです。ここでは『YX5200-24SS』をターゲットとしています。日本語ドキュメントのある『KT403A』と比較したところ仕様に相違があることを確認しました。手元にあるモジュールでの動作確認のため、『YX5200-24SS』をターゲットとせざるを得ません。
※DFRobot社のMini MP3 Playerはchip表面の型番がDFRobot****とあり、怪しいです。巷にはまともにシリアルコマンドを受け付けない物もたくさん有るみたいです。まともなメーカ品を直販店から購入するのが安全かと思います。基本的なコマンド体系は、10バイト固定長で、
[開始コード][バージョン][データ長][コマンド][返答有無][Para_MSB][Para_LSB][Check_MSB][Check_LSB][終了コード]
となっています。一部データ長が6バイトで無い物もあります。また、チェックサムが省略された物も有るようです。いろいろ実行して判ったこととして、Serial1にコマンドを書き込む場合、文字列をSerial1.print();で書き込むとDFPlayerは認識してもらえないです。一文字ずつSerial1.write();で書き込みます。そうしないとフレーミングエラーとなってしまいます。これはロジックアナライザで確認する必要があり面倒でした。
開始コード | 0x7E | 固定 |
バージョン | 0xFF | |
データ長 | 0x06 | データの個数、[バージョン]〜[Para_LSB]まで 6バイトよりも長いコマンドもある |
コマンド | 0x01-0x4A | 操作コマンド=0x01-0x1A 問い合わせコマンド=0x3A-x4F |
返答有無 | 0x00/0x01 |
0x00=返答不要 0x01=返答必要、確認をMCUに送り返す 返答必要で送信した場合は[0x41]コマンドが返される (7E FF 06 41 00 00 00 FE BA EF) |
Para_MSB | パラメータの最上位バイト | |
Para_LSB | パラメータの最下位バイト | |
Check_MSB | チェックサの最上位バイト | |
Check_LSB | チェックサの最下位バイト | |
終了コード | 0xEF |
固定 可変長でもこのコードまで読み取ることで対応出来る その意味で、チェックサムが無くてもモジュールは対応可能 |
制御コマンドであっても『返答有無』のフラグをONにするとモジュールから応答が掛かることを確認しました。
chipによるコマンドの比較をして置きます。chipは、DFROBOT/KT403A/YX5200-24SSをチェックしています。最終的にはYX5200-24SSで動作確認をしています。
コマンド | DFROBOT | LISP3 | KT403A | YX5200-24SS | コメント |
0x01 |
|
|
|
次のトラックを再生する。 |
0x02 |
|
|
|
前のトラックを再生する。 |
0x03 |
|
|
|
指定したファイルを再生する。(ファイル順 1〜約3000) |
0x04 |
|
|
|
音量を上げる。 (+) |
0x05 |
|
|
|
音量を下げる。 (−) |
0x06 |
|
|
|
音量を指定する。 (0〜30) 0:消音 , 30:最大(初期値) |
0x07 |
|
|
|
イコライザーを選択する。 (0〜4) |
0x08 |
|
|
|
指定した1つのファイルを繰り返し再生する。 (ファイル順 1〜約3000) |
0x09 |
|
|
|
再生するデバイスを選択する。 (デバイスのファイル・システムを初期化します) |
0x0A |
|
|
|
待機モード(低消費電力)に入る。 |
0x0B |
|
|
|
通常モードにする。 (デバイスの再選択が必要) |
0x0C |
|
|
|
チップをリセットする。 |
0x0D |
|
|
|
再生を開始する。 |
0x0E |
|
|
|
一時停止する。 |
0x0F |
|
|
|
フォルダーとトラック番号を指定して再生する。 (フォルダー:01〜99 , トラック:01〜255) |
0x10 |
|
|
音の定位を広げる。 (ステレオ・エンハンサー) (00:初期値OFF) (効果量 0〜31) 0:小 , 31:大 |
|
0x11 |
|
|
デバイス内の全てのファイルを繰り返し再生する。 | |
0x12 |
|
[MP3]フォルダー内のトラックを、番号を指定して 再生する。 (トラック番号 0001〜約3000) | ||
0x13 |
|
|
[ADVERT](広告)フォルダー内のトラックを、 割り込みで再生する。(トラック番号 0001〜約3000) |
|
0x14 |
|
指定したフォルダー内の255を超えるトラックを再生 する。(フォルダー:01〜15 , トラック:01〜約3000) | ||
0x15 |
|
|
割り込みしたトラックの再生を停止して、中断した トラックの再生を再開する。 | |
0x16 |
|
|
再生を停止する。 | |
0x17 |
|
|
指定したフォルダー内のファイルを繰り返し再生する。 | |
0x18 |
|
|
デバイス内の全てのファイルをシャッフルして再生する。 | |
0x19 |
|
現在、再生中のトラックを繰り返し再生する。 | ||
0x1A |
|
|
DAC(D/Aコンバーター)の出力をON/OFFする。 | |
0x1B | ||||
0x1C | ||||
0x1D | ||||
0x1E | ||||
0x1F | ||||
0x20 | ||||
0x21 |
|
Combination playback | ||
0x22 |
|
Play with volume | ||
0x23 | ||||
0x24 | ||||
0x25 |
|
Spot ads. -multiple folders | ||
0x26 | ||||
0x27 | ||||
0x28 |
|
指定したフォルダー内のファイルをシャッフル再生する。 | ||
0x29 |
|
指定したフォルダー内のファイルを一通り再生する。 | ||
0x2A | ||||
0x2B | ||||
0x2C | ||||
0x2D | ||||
0x2E | ||||
0x2F | ||||
0x30 | ||||
0x31 | ||||
0x32 | ||||
0x33 | ||||
0x34 | ||||
0x35 | ||||
0x36 | ||||
0x37 | ||||
0x38 | ||||
0x39 | ||||
0x3A |
|
|
(通知) デバイスが挿入された。 −このコマンドの送信は動作しない− |
|
0x3B |
|
|
(通知) デバイスが取り出された。 −このコマンドの送信は動作しない− |
|
0x3C |
|
|
|
(通知) USBメモリーのファイルの再生が終了。 −このコマンドの送信は動作しない− |
0x3D |
|
|
|
(通知) SDカードのファイルの再生が終了。 −このコマンドの送信は動作しない− |
0x3E |
|
|
|
(通知)フラッシュメモリーのファイルの再生が終了。 −このコマンドの送信は動作しない− |
0x3F |
|
|
|
現在選択されているデバイスの種類を問い合わせる。 (通知) 選択されて初期化されたデバイスの種類。 |
0x40 |
|
|
|
(通知) エラーが発生した。 01:システムがBUSY状態。 02:スリープ・モードのデバイス選択間違い。 03:シリアル受信エラー。 04:チェックサム・エラー。 05:ファイル指定の範囲超え。 06:指定されたファイルが見つからない。 07:コマンドのエラー。 |
0x41 |
|
|
(通知) コマンドの受信を正常に受け付けた。 コマンド列の「フィードバック・フラグ」によって選択。 |
|
0x42 |
|
|
|
現在の動作状態を問い合わせる。 |
0x43 |
|
|
|
現在の音量を問い合わせる。 |
0x44 |
|
|
|
現在のイコライザー設定を問い合わせる。 |
0x45 |
|
|
|
現在の再生モードを問い合わせる。 00:通常再生。 (単独再生) 01:フォルダー内を繰り返し再生。 02:1曲を繰り返し再生。 03:デバイス内のトラックをシャッフル再生。 04:フォルダー内のトラックをシャッフル再生。 05:フォルダー内のトラックを一通り再生。 |
0x46 |
|
|
|
ソフトウェアのバージョンを問い合わせる。 |
0x47 |
|
|
|
USBメモリーのファイルの総数を問い合わせる。 |
0x48 |
|
|
|
SDカードのファイルの総数を問い合わせる。 |
0x49 |
|
|
|
フラッシュメモリーのファイルの総数を問い合わせる。 |
0x4A |
|
|
|
−予約済− |
0x4B |
|
|
|
USBメモリーの現在のファイル位置を問い合わせる。 |
0x4C |
|
|
|
SDカードの現在のファイル位置を問い合わせる。 |
0x4D |
|
|
|
フラッシュメモリーの現在のファイル位置を問い合わせる。 |
0x4E |
|
|
|
指定したフォルダー内のトラックの総数を問い合わせる。 (返信) トラック数。 |
0x4F |
|
|
デバイス内のフォルダー数を問い合わせる。 |
FUNCTION |
SENDING INSTRUCTIONS |
NOTES |
|
[Next] |
7E FF 06 01 00 00 00 FE FA EF |
||
[Previous] |
7E FF 06 02 00 00 00 FE F9 EF |
||
[Specify tracking] |
7E FF 06 03 00 00 01 FE F7 EF |
Specify track 1 |
|
7E FF 06 03 00 00 02 FE F6 EF |
Specify track 2 |
||
7E FF 06 03 00 00 0A FE EE EF |
Specify track 10 |
||
VOL+ |
7E FF 06 04 00 00 00 FE F7 EF |
||
VOL- |
7E FF 06 05 00 00 00 FE F6 EF |
[Specify volume] |
7E FF 06 06 00 00 1E FE D7 EF |
30 |
|
[Specify EQ] |
7E FF 06 07 00 00 01 FE F3 EF |
STAY |
|
[LOOP PLAYBACK] |
7E FF 06 08 00 00 01 FE F2 EF |
Loop play track 1 |
|
7E FF 06 08 00 00 02 FE F1 EF |
Loop play track 2 |
||
7E FF 06 08 00 00 0A FE E9 EF |
Loop play track 10 |
||
[Specify play device] |
7E FF 06 09 00 00 01 FE F1 EF |
Specify to play UDISK |
|
7E FF 06 09 00 00 02 FE F0 EF |
Specify to play TF |
||
7E FF 06 09 00 00 03 FE EF EF |
Specify play device-PC(download mode) |
||
7E FF 06 09 00 00 04 FE EE EF |
Specify to play FLASH |
||
7E FF 06 09 00 00 05 FE ED EF |
Specify play device-SLEEP |
||
[Enter into sleep mode] |
7E FF 06 0A 00 00 00 FE F1 EF |
||
[Waking sleep] |
7E FF 06 0B 00 00 00 FE F0 EF |
||
[Reset] |
7E FF 06 0C 00 00 00 FE EF EF |
||
[Play] |
7E FF 06 0D 00 00 00 FE EE EF |
||
[Pause] |
7E FF 06 0E 00 00 00 FE ED EF |
||
[Specify folder name] |
7E FF 06 0F 00 01 01 FE EA EF |
"01" folder,track "001" |
|
7E FF 06 0F 00 01 02 FE E9 EF |
"01" folder,track "002" |
||
Stop playing |
7E FF 06 16 00 00 00 FE E5 EF |
Stop the software decoding |
|
Specify folder to loop playback |
7E FF 06 17 00 00 01 FE E3 EF |
Specify folder 01 to loop playback |
|
7E FF 06 17 00 00 02 FE E2 EF |
Specify folder 02 to loop playback |
||
Single repeat |
7E FF 06 19 00 00 00 FE E2 EF |
Open single repeat playback |
|
7E FF 06 19 00 00 01 FE E1 EF |
Close single repeat playback |
||
Play with volume |
7E FF 06 22 00 1E 01 FE BA EF |
Play the track 1 with volume 30 |
|
7E FF 06 22 00 0F 01 FE C9 EF |
Play the track 1 with volume 15 |
||
7E FF 06 22 00 0F 02 FE C8 EF |
Play the track 2 with volume 15 |
||
set DAC |
7E FF 06 1A 00 00 00 FE E1 EF |
open DAC |
|
7E FF 06 1A 00 00 01 FE E0 EF |
close DAC[high resistance] |
||
Combination playback |
7E FF 09 21 01 02 02 03 01 04 EF |
Play
[1,2][2,3][1,4] The first number in |
|
7E
FF 15 21 01 02 02 03 01 04 01 |
with
checkout [1,2][2,3][1,4][1,3][1,4][1,5] |
||
7E
FF 15 21 01 02 02 03 01 04 01 |
[1,2][2,3][1,4][1,3][1,4][1,5][1,8][3,4][3,1] |
Stop playing ads |
7E FF 06 15 00 00 00 FE E6 EF |
Stop ads, return to play background music |
|
Stop playing |
7E FF 06 16 00 00 00 FE E5 EF |
Stop software decoding |
|
Spot ads. |
7E FF 06 13 00 00 01 FE E7 EF |
"ADVERT"foldertrack"0001" |
|
7E FF 06 13 00 00 02 FE E6 EF |
"ADVERT"foldertrack"0002" |
||
7E FF 06 13 00 00 FF FD E9 EF |
"ADVERT"foldertrack"0255" |
||
7E FF 06 13 00 07 CF FE 12 EF |
"ADVERT"foldertrack"1999" |
||
7E FF 06 13 00 0B B8 FE 25 EF |
"ADVERT"foldertrack"3000" |
||
Spot ads. - multiple folders |
7E FF 06 25 00 01 01 FE D4 EF |
"ADVERT1"foldertrack"001" |
|
7E FF 06 25 00 01 02 FE D3 EF |
"ADVERT1"foldertrack"002" |
||
7E FF 06 25 00 02 01 FE D3 EF |
"ADVERT2"foldertrack"001" |
||
Random playback |
7E FF 06 18 00 00 00 FE E3 EF |
Random playback of entire device |
[Query volume ] |
7E |
FF |
06 |
43 |
00 |
00 |
00 |
FE |
B8 |
EF |
||
[Query the current EQ] |
7E |
FF |
06 |
44 |
00 |
00 |
00 |
FE |
B7 |
EF |
Keep the function |
|
Query the total number of U-disk files |
7E |
FF |
06 |
47 |
00 |
00 |
00 |
FE |
B4 |
EF |
Total files of device |
|
Query the total number of TF files |
7E |
FF |
06 |
48 |
00 |
00 |
00 |
FE |
B3 |
EF |
||
Query the total number of FLASH files |
7E |
FF |
06 |
49 |
00 |
00 |
00 |
FE |
B2 |
EF |
||
Current track of U-disk |
7E |
FF |
06 |
4B |
00 |
00 |
00 |
FE |
B0 |
EF |
Play the current track |
|
Current track of TF |
7E |
FF |
06 |
4C |
00 |
00 |
00 |
FE |
AF |
EF |
||
Current track of FLASH |
7E |
FF |
06 |
4D |
00 |
00 |
00 |
FE |
AE |
EF |
||
Query
the total number of specified |
7E |
FF |
06 |
4E |
00 |
00 |
01 |
FE |
AC |
EF |
||
Query the total number of device files |
7E |
FF |
06 |
4F |
00 |
00 |
00 |
FE |
AC |
EF |
Support
TF card |
U-disk plug in |
7E FF 06 3A 00 00 01 xx xx EF |
||
TF plug in |
7E FF 06 3A 00 00 02 xx xx EF |
||
PC plug in |
7E FF 06 3A 00 00 04 xx xx EF |
||
U-disk pull out |
7E FF 06 3B 00 00 01 xx xx EF |
||
TF pull out |
7E FF 06 3B 00 00 02 xx xx EF |
||
PC pull out |
7E FF 06 3B 00 00 04 xx xx EF |
||
Played track 1 of U-disk |
7E FF 06 3C 00 00 01 xx xx EF |
Complete play track 1 of U-disk |
|
Played track 2 of U-disk |
7E FF 06 3C 00 00 02 xx xx EF |
Complete play track 2 of U-disk |
|
Played track 1 of TF card |
7E FF 06 3D 00 00 01 xx xx EF |
Complete play track 1 of TF card |
|
Played track 2 of TF card |
7E FF 06 3D 00 00 02 xx xx EF |
Complete play track 2 of TF card |
|
FLASH played track 1 |
7E FF 06 3E 00 01 01 xx xx EF |
Played track 1 of FOLDER1 |
|
FLASH played track 2 |
7E FF 06 3E 00 02 02 xx xx EF |
Played track 2 of FOLDER2 |
|
U-disk -- available |
7E FF 06 3F 00 00 01 xx xx EF |
Relative of all devices |
|
TF -- available |
7E FF 06 3F 00 00 02 xx xx EF |
||
PC -- available |
7E FF 06 3F 00 00 04 xx xx EF |
||
FLASH -- available |
7E FF 06 3F 00 00 08 xx xx EF |
||
U-diskTF -- available |
7E FF 06 3F 00 00 03 xx xx EF |
||
TF card andFLASH |
7E FF 06 3F 00 00 0A xx xx EF |
||
TF and PC online |
7E FF 06 3F 00 00 06 FE B6 EF |
||
U-disk
TF cardPC |
7E FF 06 3F 00 00 1F xx xx EF |
||
Played track 1 of U-disk |
7E FF 06 3C 00 00 01 xx xx EF |
Complete play track 1 of U-disk |
|
Played track 2 of U-disk |
7E FF 06 3C 00 00 02 xx xx EF |
Complete play track 2 of U-disk |
|
Played track 1 of TF card |
7E FF 06 3D 00 00 01 xx xx EF |
Complete play track 1 of TF card |
|
Played track 2 of TF card |
7E FF 06 3D 00 00 02 xx xx EF |
Complete play track 2 of TF card |
|
FLASH played track 1 |
7E FF 06 3E 00 01 01 xx xx EF |
Played track 1 of FOLDER1 |
|
FLASH played track 2 |
7E FF 06 3E 00 02 02 xx xx EF |
Played track 2 of FOLDER2 |
Return |
7E |
FF |
06 |
40 |
00 |
00 |
01 |
xx |
xx |
EF |
Module is initialized in the file system |
|
Current sleep mode |
7E |
FF |
06 |
40 |
00 |
00 |
02 |
xx |
xx |
EF |
Only
support the specified device |
|
Serial receiving error |
7E |
FF |
06 |
40 |
00 |
00 |
03 |
xx |
xx |
EF |
The
serial port has not received |
|
Checkout error |
7E |
FF |
06 |
40 |
00 |
00 |
04 |
xx |
xx |
EF |
Checksum error |
|
Specified file overreach |
7E |
FF |
06 |
40 |
00 |
00 |
05 |
xx |
xx |
EF |
Specify
the folder exceed the |
|
Specify folder not found |
7E |
FF |
06 |
40 |
00 |
00 |
06 |
xx |
xx |
EF |
Specify folder not found |
|
Inter cut error |
7E |
FF |
06 |
40 |
00 |
00 |
07 |
xx |
xx |
EF |
Spots
are only allowed in playing |
|
TF card play error |
7E |
FF |
06 |
40 |
00 |
00 |
08 |
xx |
xx |
EF |
TF
card read failed or TF card |
|
FLASH |
7E |
FF |
06 |
40 |
00 |
00 |
09 |
xx |
xx |
EF |
FLASH file error |
|
SLEPP |
7E |
FF |
06 |
40 |
00 |
00 |
0A |
xx |
xx |
EF |
Prompt to enter SLEPP mode |
ソースを掲載します。
このコードを実行するに当たり追加が必要なライブラリは、
※DFRobotDFPlayerMiniは使用しません。シリアルのコマンド管理はLabVIEW側で行い、返答を確実に受信するためには、ライブラリが使用できません。
LED&KEYのS1〜S8に機能を付加しています。
/* * 2020/2/25 T.Wanibe DFPlayer用プロジェクトコード * 制御コマンドはほぼ期待通りになったので問い合わせコマンドを対応すべく修正した。 * ライブラリを使用するとコマンド送信は出来ても返信を横取りされてしまう。ライブラリにthruコマンドが用意されていれば * なんとかなるのだが。。 * そこで『DFRobotDFPlayerMini』を使う事を止めたバージョンです。 * LabVIEWからシリアルコンバータ経由でDFPlayerにシリアル接続した場合は問い合わせコマンドも普通に動くことを確認出来たので * STM32duino側のコードにてシリアルコードを受け取ったらそのままTCPSocketに送るように変更する * STM32MINIShield基板に『DfPlayer』をシリアル接続して,LabVIEW(Windows)からLAN経由でSTM32MINIShield基板に接続し * 制御コマンドを送ると、そのコマンドをブリッジして『DfPlayer』に送って制御するというモノです。 * 接続する『DfPlayer』はKeyStudio社のKS0387が都合がいいです。KS0387はスピーカをドライブしなければSTM32MINIShield基板 * からの供給電力で賄えると考えています。そうで無い場合は接続するシリアルポートの電源供給はせず、KS0387内に別途電源供給する * 必要があります。 * コマンドはLabVIEW側で管理し、Socketで渡ってきたコマンド文字列をそのままSerial1の回します。 * コマンド文字列は以下のフォーマットです。 * $S VER Len CMD FB PRAM1 PARAM2 CSUMH CSUML $O * の10バイト固定長です。チェックサムを外した8バイト長も有効なデバイスもあるようですが、此処ではチェックサム付きとします。 * * 最大131072バイトのフラッシュメモリのうち、スケッチが53560バイト(40%)を使っています。 * 最大20480バイトのRAMのうち、グローバル変数が4792バイト(23%)を使っていて、ローカル変数で15688バイト使うことができます。 */ #include <SPI.h> #include <Ethernet3.h> #include <TextFinder.h> //WebSetting #include <EEPROM.h> #include <avr/pgmspace.h> #include <TM1638.h> //#include "DFRobotDFPlayerMini.h" #define SocketPort 50001 #define HttpPort 80 #define OKMSG "" #define NGMSG "" #define Version "STM32MINIShield_DFPlayer" #define W550io_CS PA4 //PB12 SPI_1に変更 #define W550io_Rst PA8 // #define LED1_Pin PB8 #define LED2_Pin PB9 #define BUSY_Pin PB0 //DFPlayer_Busy #define dataPin PB4 //LED&KEY #define clockPin PB3 //LED&KEY #define strobePin PA15 //LED&KEY //DFPlayer #define TimeOut 0 #define WrongStack 1 #define DFPlayerCardInserted 2 #define DFPlayerCardRemoved 3 #define DFPlayerCardOnline 4 #define DFPlayerPlayFinished 5 #define DFPlayerError 6 #define DFPlayerUSBInserted 7 #define DFPlayerUSBRemoved 8 #define DFPlayerUSBOnline 9 #define DFPlayerCardUSBOnline 10 #define DFPlayerFeedBack 11 #define Busy 1 #define Sleeping 2 #define SerialWrongStack 3 #define CheckSumNotMatch 4 #define FileIndexOut 5 #define FileMismatch 6 #define Advertise 7 byte mac[6] = {0x00, 0x08, 0xDC, 0x54, 0x4D, 0xD0}; //WiZ550ioに添付されているMACアドレス byte ip[] = {192,168,0,200}; //配列で扱わないと処理が難しい byte subnet[] = {255,255,255,0}; byte gateway[] = {192,168,0,1}; byte server[] = {192,168,0,255}; volatile bool EnableLAN = true; //ADC処理中はEnableLAN = falseにする。 volatile bool LANConnect = false; //LanModuleにアクセスしているときTrue volatile bool Continuation = false; //TCPパケットの続きがあるか? volatile bool ContinueRun = false; //連続パタン出力か? long int interval; long int gCycle = 100; //100msec 10S/s volatile long int LoopCount = 0; volatile long int MainLoopCount = 0; uint8_t toggle1 = 0; char buf[20]; bool alreadyConnected= false; const byte ID = 0x92; char next_msg[] = {0x7E, 0xFF, 0x06, 0x01, 0x00, 0x00, 0x00, 0xFE, 0xFA, 0xEF}; char prev_msg[] = {0x7E, 0xFF, 0x06, 0x02, 0x00, 0x00, 0x00, 0xFE, 0xF9, 0xEF}; char volp_msg[] = {0x7E, 0xFF, 0x06, 0x04, 0x00, 0x00, 0x00, 0xFE, 0xF7, 0xEF}; char volm_msg[] = {0x7E, 0xFF, 0x06, 0x05, 0x00, 0x00, 0x00, 0xFE, 0xF6, 0xEF}; char rpet_msg[] = {0x7E, 0xFF, 0x06, 0x19, 0x00, 0x00, 0x00, 0xFE, 0xE2, 0xEF}; char play_msg[] = {0x7E, 0xFF, 0x06, 0x0D, 0x00, 0x00, 0x00, 0xFE, 0xEE, 0xEF}; char paus_msg[] = {0x7E, 0xFF, 0x06, 0x0E, 0x00, 0x00, 0x00, 0xFE, 0xED, 0xEF}; char stop_msg[] = {0x7E, 0xFF, 0x06, 0x16, 0x00, 0x00, 0x00, 0xFE, 0xE5, 0xEF}; char volr_msg[] = {0x7E, 0xFF, 0x06, 0x43, 0x00, 0x00, 0x00, 0xFE, 0xB8, 0xEF}; EthernetServer socketServer = EthernetServer(SocketPort); EthernetServer webServer = EthernetServer(HttpPort); EthernetClient tcp; EthernetClient web; size_t size; char BUF[32]; char rSTR[48]; String EventBUF; TM1638 LedAndKey(dataPin, clockPin, strobePin); //データピンPB4、クロックピンPB3、およびストローブピンPA15でモジュールを定義する //--------------------- void printDetail(EthernetClient tcp,uint8_t type, int value){ switch (type) { case TimeOut: Serial.println(F("Time Out!")); //tcp.write("Time Out!\r\n"); break; case WrongStack: Serial.println(F("Stack Wrong!")); //tcp.write("Stack Wrong!\r\n"); break; case DFPlayerCardInserted: Serial.println(F("Card Inserted!")); //tcp.write("Card Inserted!\r\n"); break; case DFPlayerCardRemoved: Serial.println(F("Card Removed!")); //tcp.write("Card Removed!"); break; case DFPlayerCardOnline: Serial.println(F("Card Online!")); //tcp.write("Card Online!\r\n"); break; case DFPlayerUSBInserted: Serial.println(F("USB Inserted!")); //tcp.write("USB Inserted!\r\n"); break; case DFPlayerUSBRemoved: Serial.println(F("USB Removed!")); //tcp.write("USB Removed!\r\n"); break; case DFPlayerPlayFinished: Serial.print(F("Number:")); Serial.print(value); Serial.println(F(" Play Finished!")); //sprintf(BUF,"Number:%d Play Finished!\n",value); //tcp.write(BUF); break; case DFPlayerError: Serial.print(F("DFPlayerError:")); switch (value) { case Busy: Serial.println(F("Card not found")); //tcp.write("DFPlayerError:Card not found\r\n"); break; case Sleeping: Serial.println(F("Sleeping")); //tcp.write("DFPlayerError:Sleeping\r\n"); break; case SerialWrongStack: Serial.println(F("Get Wrong Stack")); //tcp.write("DFPlayerError:Get Wrong Stack\r\n"); break; case CheckSumNotMatch: Serial.println(F("Check Sum Not Match")); //tcp.write("DFPlayerError:Check Sum Not Match\r\n"); break; case FileIndexOut: Serial.println(F("File Index Out of Bound")); //tcp.write("DFPlayerError:File Index Out of Bound\r\n"); break; case FileMismatch: Serial.println(F("Cannot Find File")); //tcp.write("DFPlayerError:Cannot Find File\r\n"); break; case Advertise: Serial.println(F("In Advertise")); //tcp.write("DFPlayerError:In Advertise\r\n"); break; default: break; } break; default: break; } } //------------------LAN設定 void LANSetup(){ int idcheck = EEPROM.read(0); Serial.print(F("LocalID = 0x"));Serial.println(idcheck,HEX); if (idcheck == ID){ //idがIDと同じ値の場合、 //これは、このスケッチがシールドを設定するために使用されたことを意味します。 //EERPOMの値を読み取ってシールドを設定します。 for (int i = 0; i < 6; i++){ mac[i] = EEPROM.read(i+1); } for (int i = 0; i < 4; i++){ ip[i] = EEPROM.read(i+7); } for (int i = 0; i < 4; i++){ subnet[i] = EEPROM.read(i+11); } for (int i = 0; i < 4; i++){ gateway[i] = EEPROM.read(i+15); } }else{ //idが一致しない場合、初期値を書き込む事にします。 for (int i = 0 ; i < 6; i++){ EEPROM.write(i + 1,mac[i]); } for (int i = 0 ; i < 4; i++){ EEPROM.write(i + 7, ip[i]); } for (int i = 0 ; i < 4; i++){ EEPROM.write(i + 11, subnet[i]); } for (int i = 0 ; i < 4; i++){ EEPROM.write(i + 15, gateway[i]); } // IDを既知のビットに設定します。したがって、Arduinoをリセットすると、EEPROM値が使用されます。 EEPROM.write(0, ID); } Ethernet.begin(mac, ip); // Ethernet.begin(mac); //DHCPの場合 // Ethernet.begin(mac, ip, subnet); //SubnetMaskを意識した場合。 // Ethernet.begin(mac, ip, subnet, gateway); //gatewayを意識した場合。 } //-------------------------- void SetWebPage( EthernetClient client){ client.print(F("<!DOCTYPE HTML PUBLIC \"\"><html><HEAD><META http-equiv=\"Content-Type\" charset=UTF-8\">")); client.print(F("<META http-equiv=\"Content-Style-Type\"><TITLE>")); client.print(Version); client.print(F(" Setup Page</TITLE></HEAD>")); client.print(F("<BODY marginwidth=\"0\" marginheight=\"0\" leftmargin=\"0\" style=\"margin: 0; padding: 0;\"><BLOCKQUOTE><BLOCKQUOTE>")); client.print(F("<table bgcolor=\"#17A1A5\" border=\"0\" width=\"100%\" cellpadding=\"1\" style=\"font-family:Verdana;color:#ffffff;font-size:12px;\">")); client.print(F("<tr><td> ")); client.print(Version); client.print(F(" Setup Page</td></tr></table><br>")); // client.print(F("<script>function hex2num (s_hex) {eval(\"var n_num=0X\" + s_hex);return n_num;}</script>")); client.print(F("<FORM><input type=\"hidden\" name=\"SBM\" value=\"1\"><table><tr><td>MAC:</td><td>")); client.print(F("<input id=\"T1\" type=\"text\" size=\"3\" maxlength=\"2\" name=\"DT1\" value=\"")); client.print(mac[0],HEX); client.print(F("\">.<input id=\"T3\" type=\"text\" size=\"3\" maxlength=\"2\" name=\"DT2\" value=\"")); client.print(mac[1],HEX); client.print(F("\">.<input id=\"T5\" type=\"text\" size=\"3\" maxlength=\"2\" name=\"DT3\" value=\"")); client.print(mac[2],HEX); client.print(F("\">.<input id=\"T7\" type=\"text\" size=\"3\" maxlength=\"2\" name=\"DT4\" value=\"")); client.print(mac[3],HEX); client.print(F("\">.<input id=\"T9\" type=\"text\" size=\"3\" maxlength=\"2\" name=\"DT5\" value=\"")); client.print(mac[4],HEX); client.print(F("\">.<input id=\"T11\" type=\"text\" size=\"3\" maxlength=\"2\" name=\"DT6\" value=\"")); client.print(mac[5],HEX); // client.print(F("\"><input id=\"T2\" type=\"hidden\" name=\"DT1\"><input id=\"T4\" type=\"hidden\" name=\"DT2")); client.print(F("\"><input id=\"T6\" type=\"hidden\" name=\"DT3\"><input id=\"T8\" type=\"hidden\" name=\"DT4")); client.print(F("\"><input id=\"T10\" type=\"hidden\" name=\"DT5\"><input id=\"T12\" type=\"hidden\" name=\"DT6")); client.print(F("\"></td></tr><tr><td>IP:</td><td><input type=\"text\" size=\"3\" maxlength=\"3\" name=\"DT7\" value=\"")); client.print(ip[0],DEC); client.print(F("\">.<input type=\"text\" size=\"3\" maxlength=\"3\" name=\"DT8\" value=\"")); client.print(ip[1],DEC); client.print(F("\">.<input type=\"text\" size=\"3\" maxlength=\"3\" name=\"DT9\" value=\"")); client.print(ip[2],DEC); client.print(F("\">.<input type=\"text\" size=\"3\" maxlength=\"3\" name=\"DT10\" value=\"")); client.print(ip[3],DEC); // client.print(F("\"></td></tr><tr><td>MASK: </td><td><input type= \"text\" size=\"3\" maxlength=\"3\" name=\"DT11\" value=\"")); client.print(subnet[0],DEC); client.print(F("\">.<input type=\"text\" size=\"3\" maxlength=\"3\" name=\"DT12\" value=\"")); client.print(subnet[1],DEC); client.print(F("\">.<input type=\"text\" size=\"3\" maxlength=\"3\" name=\"DT13\" value=\"")); client.print(subnet[2],DEC); client.print(F("\">.<input type=\"text\" size=\"3\" maxlength=\"3\" name=\"DT14\" value=\"")); client.print(subnet[3],DEC); // client.print(F("\"></td></tr><tr><td>GW: </td><td><input type=\"text\" size=\"3\" maxlength=\"3\" name=\"DT15\" value=\"")); client.print(gateway[0],DEC); client.print(F("\">.<input type=\"text\" size=\"3\" maxlength=\"3\" name=\"DT16\" value=\"")); client.print(gateway[1],DEC); client.print(F("\">.<input type=\"text\" size=\"3\" maxlength=\"3\" name=\"DT17\" value=\"")); client.print(gateway[2],DEC); client.print(F("\">.<input type=\"text\" size=\"3\" maxlength=\"3\" name=\"DT18\" value=\"")); client.print(gateway[3],DEC); client.print(F("\"></td></tr><tr><td><br></td></tr><tr><td><input id=\"button1\"type=\"submit\" value=\"SUBMIT\" ")); // client.print(F("Onclick=\"document.getElementById('T2').value ")); client.print(F("= hex2num(document.getElementById('T1').value);")); client.print(F("document.getElementById('T4').value = hex2num(document.getElementById('T3').value);")); client.print(F("document.getElementById('T6').value = hex2num(document.getElementById('T5').value);")); client.print(F("document.getElementById('T8').value = hex2num(document.getElementById('T7').value);")); client.print(F("document.getElementById('T10').value = hex2num(document.getElementById('T9').value);")); client.print(F("document.getElementById('T12').value = hex2num(document.getElementById('T11').value);\"")); // client.print(F("></td><td></td></tr></form></table></BLOCKQUOTE></BLOCKQUOTE></BODY></html>")); } //-------------------------- void checkWebPage( EthernetClient client) { Serial.println(F("new webClient")); if (client) { TextFinder finder(client ); while (client.connected()) { //digitalWrite(LED, HIGH); if (client.available()) { //この部分はすべてのテキスト検索を行います。 if( finder.find("GET /") ) { // 「setup」とい語が見つかった場合は、さらに探してください。 // その単語が見つからない場合は、検索を停止して先に進みます。 // これにより、後でスケッチに独自のWebページを配置できます。 if (finder.findUntil("setup", "\n\r")){ // 「SBM」という単語が見つかった場合は、さらに探し続けます。 // その言葉が見つからない場合は、探して停止します。 // SUBMITボタンが押されていない、何も押されていないことを意味します // セットアップページが構築されている場所に移動し、クライアントのブラウザに表示します。 if (finder.findUntil("SBM", "\n\r")){ byte SET = finder.getValue(); // これで、「DT」という文字を探している間に、「DT」の後ろにあるすべての数字を覚えて、 // 値を一致させて、mac、ip、subnet、およびgatewayに入れる必要があります。 while(finder.findUntil("DT", "\n\r")){ int val = finder.getValue(); // 「DT」のvalが1?6の場合、対応する値はMAC値でなければなりません。 if(val >= 1 && val <= 6) { mac[val - 1] = finder.getValue(); } // 「DT」のvalが7?10の場合、対応する値はIP値でなければなりません。 if(val >= 7 && val <= 10) { ip[val - 7] = finder.getValue(); } // 「DT」のvalが11?14の場合、対応する値はMASK値でなければなりません。 if(val >= 11 && val <= 14) { subnet[val - 11] = finder.getValue(); } // 「DT」のvalが15?18の場合、対応する値はGW値でなければなりません。 if(val >= 15 && val <= 18) { gateway[val - 15] = finder.getValue(); } } // すべてのデータを取得したので、EEPROMに保存できます for (int i = 0 ; i < 6; i++){ EEPROM.write(i + 1,mac[i]); } for (int i = 0 ; i < 4; i++){ EEPROM.write(i + 7, ip[i]); } for (int i = 0 ; i < 4; i++){ EEPROM.write(i + 11, subnet[i]); } for (int i = 0 ; i < 4; i++){ EEPROM.write(i + 15, gateway[i]); } // IDを既知のビットに設定します。したがって、Arduinoをリセットすると、EEPROM値が使用されます。 EEPROM.write(0, ID); // すべてのデータがEEPROMに書き込まれている場合、arduinoをリセットする必要があります。 //ハードウェアリセットボタンを使用する必要があります。 } // この時点から、セットアップページの構築を開始し、クライアントのブラウザーに表示できます。 client.println("HTTP/1.1 200 OK"); client.println("Content-Type: text/html"); client.println(); // SetWebPage(client); break; } } client.println("HTTP/1.1 200 OK"); client.println("Content-Type: text/html"); client.println(); // put your own html from here on client.print("IT WORKS: go to "); client.print(ip[0],DEC); for (int i= 1; i < 4; i++){ client.print("."); client.print(ip[i],DEC); } client.print("/setup"); // put your own html until here break; } } //digitalWrite(LED, LOW); delay(1); client.stop(); } } //------------------ void setup() { Serial.begin(115200); //USB経由の接続 Serial1.begin(9600,SERIAL_8N1); //DFPlayerとの接続 D8bit S1bit Nonパリティ delay(1000); Serial.println(F("\nStart")); pinMode(LED1_Pin, OUTPUT); digitalWrite(LED1_Pin, LOW); pinMode(LED2_Pin, OUTPUT); digitalWrite(LED2_Pin, LOW); pinMode(BUSY_Pin, INPUT); if(digitalRead(BUSY_Pin)) digitalWrite(LED1_Pin, HIGH); else digitalWrite(LED1_Pin, LOW); //Ethernet3で使用可能なAPI Ethernet.setCsPin(W550io_CS); // set Pin PA4 for CS Ethernet.setRstPin(W550io_Rst); // set Pin PA8 for RST //リセット処理 pinMode(W550io_Rst, OUTPUT); digitalWrite(W550io_Rst, LOW); delay(10); digitalWrite(W550io_Rst, HIGH); LANSetup(); //Setup Serial.print(F("Server is at ")); Serial.print(Ethernet.localIP());Serial.print(F("\t"));Serial.println(Version); socketServer.begin(); webServer.begin(); //LED&KEY 初期化 LedAndKey.setDisplayToString("00000000",0,0); /* if (!myDFPlayer.begin(Serial1)) { //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(0); // Code to compatible with ESP8266 watch dog. } */ Serial.println(F("EndSetUp")); } //------------------ void loop() { if (MainLoopCount++ % 50) { if (EnableLAN) { Serial.print(F(".")); } else { Serial.print(F("x")); } } else { Serial.print(F("\n")); Serial.println(MainLoopCount); } long int StartTCP = micros(); LANConnect = true; delay(1); tcp = socketServer.available(); //Serial.println(F("socketServer.available")); if (tcp) { if (!alreadyConnected) { tcp.flush(); // clear out the input buffer: alreadyConnected = true; } while ((size = tcp.available()) > 0) { //受信処理 char* msg = (char*)malloc(size); delay(1); size = tcp.read((unsigned char*)msg, size); Serial.print(F("PacketSize = ")); Serial.println(size); for(int i=0;i<size;i++){ sprintf(BUF,"%02x",msg[i]); Serial.print(BUF); } Serial.println(); switch (msg[0]) { case 0x7E: //コマンド要求 if(msg[3] == 0x39){ //内部処理 //Serial.println(EventBUF.length()); if(EventBUF.length()){ for(int i=0;i<9;i++){ tcp.write(EventBUF.c_str()[i]); Serial.print(EventBUF.c_str()[i],HEX); } tcp.write('0xEF'); EventBUF = EventBUF.substring(0,0);; }else{ for(int i=0;i<size;i++){ tcp.write(msg[i]); } } }else{ for(int i=0;i<size;i++){ Serial1.write(msg[i]); //Serial1.print()ではうまくゆかない sprintf(BUF,"%02x",msg[i]); Serial.print(BUF); } Serial.println(); delay(30); //1文字でも受信させる時間余裕を設定 if(Serial1.available()){ String recivedSTR = Serial1.readStringUntil(0xEF);//デリミタはhEF for(int i=0;i<recivedSTR.length();i++){ tcp.write(recivedSTR.c_str()[i]); sprintf(BUF,"%02x",recivedSTR.c_str()[i]); Serial.print(BUF); } tcp.write('0xEF'); Serial.print(F("EF")); if (EnableLAN) tcp.write(OKMSG); tcp.flush(); } } break; default: if (EnableLAN) tcp.write(NGMSG); break; } free(msg); } } if (Serial1.available()) { //バッファにメッセージが残っていたら String EventSTR = Serial1.readStringUntil(0xEF); //デリミタはhEF int length = EventSTR.length(); //for(int i=0;i<length;i++){ // tcp.write(EventSTR.c_str()[i]); //} //tcp.write('0xEF');tcp.flush(); EventBUF = EventSTR.substring(0); byte cmd1 = EventSTR.c_str()[length-6]; int err1 = EventSTR.c_str()[length-3]; sprintf(BUF,"\nL=%d C=0x%x E=%d\n",length,cmd1,err1); Serial.print(BUF); printDetail(tcp,cmd1,err1); } web = webServer.available(); if(web) checkWebPage(web); web.stop(); // コネクションを閉じる。 LANConnect = false; String recivedSTR; int volValue; byte keys = LedAndKey.getButtons(); switch(keys){ case 0x01: LedAndKey.setDisplayToString("next ",0,0); for(int i=0;i<10;i++){ Serial1.write(next_msg[i]); //Serial1.print()ではうまくゆかない } break; case 0x02: LedAndKey.setDisplayToString("volp ",0,0); for(int i=0;i<10;i++){ Serial1.write(volp_msg[i]); //Serial1.print()ではうまくゆかない } delay(20); for(int i=0;i<10;i++){ Serial1.write(volr_msg[i]); //Serial1.print()ではうまくゆかない } delay(20); recivedSTR = Serial1.readStringUntil(0xEF); volValue = recivedSTR.c_str()[6]; Serial.println(volValue); sprintf(BUF,"%02d\0",volValue); LedAndKey.setDisplayToString(BUF,0,6); break; case 0x04: LedAndKey.setDisplayToString("vol- ",0,0); for(int i=0;i<10;i++){ Serial1.write(volm_msg[i]); //Serial1.print()ではうまくゆかない } delay(20); for(int i=0;i<10;i++){ Serial1.write(volr_msg[i]); //Serial1.print()ではうまくゆかない } delay(20); recivedSTR = Serial1.readStringUntil(0xEF); volValue = recivedSTR.c_str()[6]; sprintf(BUF,"%02d\0",volValue); LedAndKey.setDisplayToString(BUF,0,6); break; case 0x08: LedAndKey.setDisplayToString("prev ",0,0); for(int i=0;i<10;i++){ Serial1.write(prev_msg[i]); //Serial1.print()ではうまくゆかない } break; case 0x10: LedAndKey.setDisplayToString("repeat ",0,0); for(int i=0;i<10;i++){ Serial1.write(rpet_msg[i]); //Serial1.print()ではうまくゆかない } break; case 0x20: LedAndKey.setDisplayToString("play ",0,0); for(int i=0;i<10;i++){ Serial1.write(play_msg[i]); //Serial1.print()ではうまくゆかない } break; case 0x40: LedAndKey.setDisplayToString("pause ",0,0); for(int i=0;i<10;i++){ Serial1.write(paus_msg[i]); //Serial1.print()ではうまくゆかない } break; case 0x80: LedAndKey.setDisplayToString("stop ",0,0); for(int i=0;i<10;i++){ Serial1.write(stop_msg[i]); //Serial1.print()ではうまくゆかない } break; default: break; //何もしない } if(digitalRead(BUSY_Pin)) digitalWrite(LED1_Pin, HIGH); else digitalWrite(LED1_Pin, LOW); delay(100); }
LabVIEW側パネルを紹介します。
|
||
@ | 終了ボタン | アプリケーションを終了する。 |
A | 接続インジケータ | TCP乃至はシリアルで接続していたら点灯します |
B | 再生 | 停止位置からの再生開始 |
C | 一時停止 | 再生位置の停止/再開 |
D | 停止 | 再生の停止 |
E | 1曲戻る | 次の曲の再生 |
F | 1曲進む | 前の曲の再生 |
G | トラック指定 | Jで指定したトラックに移行 |
H | リピート再生 | Iで指定したフォルダ内をリピート再生 |
I | フォルダインデックス |
01−99 下の数字はフォルダ数(隠しフォルダや、無効フォルダも形状) 『99』と云うフォルダが無いのに“99”を指定するとエラーが発生します。I参照 |
J | トラックインデックス | 001−255,1-3000 下の数字はファイルの総数 |
K | ボリューム値 | 0-30 |
L | ボリューム設定 | Kで示した値でボリュームを直接設定 |
M | ボリューム上下 | ▲を一回押すとボリューム値をインクリメント▼でデクリメント |
N | イコライザ設定 | Normal/POPS/ROCK/JAZZ/CLASSIC |
@ | VISA識別子 | COMx或いはNI-MAXで登録された値 |
A | 接続・切断 | |
B | インタフェース設定 | TF/USB/FLASH |
@ | コマンド | YX5200-24SSの資料にあるコマンド |
A | 応答要求? |
制御コマンドはECHOバックを期待するかどうか? ※応答が必要なコマンドは0x01にするとエラーになる。 |
B | SUM設定? |
チェックサムを使っても使わなくても使用できるため、この設定が用意されている。 チェックサムを使うと10[Byte]使わないと8[Byte] 0xEFが終端なのでなんとでもなる |
C | パラメータHigh | |
D | パラメータLow | |
E | コマンド送信 | |
F | コマンド送信バイト列 | 送信文字列を16進数表示 |
G | コマンド送信文字列 | 表示形式の変更が出来ます。通常は16進表記 |
H | コマンド受信文字列 | 表示形式の変更が出来ます。通常は16進表記 |
I | Actionメッセージ | イベントが起きたときのメッセージを表示 |
@ | エラー履歴 |
LabVIEW側はVISAを使ってコーディングしていますので、接続先がシリアル接続でもTCP接続でも使用できます。
シリアル接続ではメッセージをリファレンス通りに扱います。
TCPソケット通信ではコマンド応答後にOK/NGのメッセージを必ず変更するようにしています。
ネットワークで接続する場合はデバイス識別子は予めNI-MAXで登録します。新規のTCPネットワークRAWデバイスとして登録してください。
音楽を聴こうとされる場合、ノイズ対策は必須です。『Keyestudio YX5200-24SS MP3』のようにノイズ対策された物も有りますが、DFPlayerMiniでは基本そのような対策はされていないようです。『Keyestudio
YX5200-24SS MP3』のDAC出力回路を載せておきます。
RC回路ハイパスフィルタなのでfc値がこれでいいのか。ただ、ローカットフィルタ入れないと音楽を聴くには耐えられないと思います。
市販されているMP3Playerをリストアップしておきます。搭載されているchipは『YX5200-24SS』となっており、『KT403A』とかセカンドソースもいくつかあるようです。そのため同等品モジュールもたくさん出ています。
『YX5200-24SS』でもないのに、ラベル上は『YX5200-24SS』となっている偽造品もあるとのことです。
メーカ | コメント | |
@ | DFROBOT |
DFR0299 搭載チップは YX5200-24SS このモデルがオリジナルと思われる |
A | Grove |
MP3モジュール v2.0 秋月で扱っている 直接ミニジャックが接続出来て使いやすそう |
B | Flyron Technology Co.、Ltd. |
FN-M10P小型シリアルMP3モジュール |
C | 不明 |
WTV020-SDモジュール このタイプのソケットは使いづらそうです。 |
D | 不明 |
M2801002 ロスレス WAV デコーダボード MP3 デコーダ |
E | Keyestudio YX5200-24SS MP3 |
このモジュールは電源供給すればこのボードとTFカードのみでMP3プレーヤとして機能します。ヘッドフォン/イヤフォン或いはスピーカを接続すれば音が出ます。 制御はUSB経由かシリアル接続です。 $6.5と安価です。 |
このプロジェクト例にVUメータ表示を追加して見ました。
SH1106というチップが搭載されたOLCDで128×64ドットのものがそこそこ安価で提供されています。これを使ってみます。
音楽を再生し、その出力音圧をADCに戻して演算してVU値としてOLCDに表示するという物です。
グラフィック表示がそれなりに思い処理なので、LAN通信部は外してMP3Player本体のボタンコントロールのみとしておきます。
ソースを掲載します。
このコードを実行するに当たり追加が必要なライブラリは、
/* * 2020/03/18 T.Wanibe 入手したOLCDのデモコードとして作成しました。 * * ArduinoForumで公開されたいたスケッチをSTM32miniShiledで動く様にコンバートしています。 * このコードはSH1106が搭載された128×64のOLCDにVUメータ機能を施した物です。 * 1ch版で、PA0に入力されたアナログ信号を SPI2接続されたSH1106に描画しています。 * MOSI PA15 * SCK PA13 * OLED_DC PA9 * OLED_CS PB12 * OLED_RESET PA10 * Analog In PA0 * * 以下は参考にしたスケッチの作者のコメントです。 * * OLEDMeterは、128x64 olcdを使用するように作成されました。 * 私はメーターをアニメーション化しようとするわずかな試みを見てきましたが、これが基準を設定することを願っています。 * 128x64 LCDまたはOLEDのこのコードを自由に変更して共有してください。 * OLEDMeterスケッチは、I2C SH1106で使用するために作成されました。 * このコードは、他のディスプレイデバイスで動作するように変更する必要があります。 * * コードの作業部分は、AdafruitマイクアンプのAdafruit Example Sound Level Sketchから取られました。 * https://learn.adafruit.com/adafruit-microphone-amplifier-breakout/measuring-sound-levels * * 残りのコードはGreg Stievenartによって記述されており、このコードで提供されている画像や情報は一切請求されていません。 * 2016年5月26日に無料公開。 * * バックグラウンドマスクを128x64に変換するソフトウェア: https://www.ablab.in/image2glcd-software/ * * 重要: * 動作させるには、音源をArduinoまたは他のMCUに接地する必要があります。 * 通常、TRSまたはTRRSコネクタのベーススリーブコンタクトはアースです。 * * 最大131072バイトのフラッシュメモリのうち、スケッチが36268バイト(27%)を使っています。 * 最大20480バイトのRAMのうち、グローバル変数が5584バイト(27%)を使っていて、ローカル変数で14896バイト使うことができます。 */ //#include <Wire.h> // requried to run I2C SH1106 #include <SPI.h> // requried to run I2C SH1106 #include <Adafruit_GFX.h> // https://github.com/adafruit/Adafruit-GFX-Library //#include <Adafruit_SH1106.h> // https://github.com/wonho-maker/Adafruit_SH1106 #include <Adafruit_SH1106_STM32.h> //#define OLED_RESET 4 // reset required for SH1106 //Adafruit_SH1106 display(OLED_RESET); // reset required for SH1106 #define SPI_PORT 2 // 1:SPI1, 2:SPI2 // hardware SPI1 (the default case): SDA(MOSI)=PA7, SCK=PA5 // SPI2 : SDA(MOSI)=PA15, SCK=PA13 #define OLED_DC PA9 #define OLED_CS PB12 #define OLED_RESET PA10 Adafruit_SH1106 olcd(OLED_DC, OLED_RESET, OLED_CS, SPI_PORT); int analogInput = PA0; // analog input for outside audio source int hMeter = 65; // horizontal center for needle animation int vMeter = 85; // vertical center for needle animation (outside of dislay limits) int rMeter = 80; // length of needle animation or arch of needle travel const int sampleWindow = 50; // sample window width in mS (50 mS = 20Hz) unsigned int sample; // VU meter background mask image:このBitMapは0x00のみの行が多いのでメモリ節約のために改善の余地はあるかもしれない。 static const unsigned char PROGMEM VUMeter[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x03, 0x00, 0x60, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x09, 0x04, 0x80, 0x21, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x98, 0x08, 0x06, 0x03, 0x80, 0x21, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xA4, 0x10, 0x09, 0x00, 0x80, 0x21, 0x20, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xA4, 0x10, 0x06, 0x03, 0x00, 0x20, 0xC0, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x71, 0x80, 0xA4, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0A, 0x40, 0x98, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x3C, 0x00, 0x00, 0x00, 0x00, 0x3A, 0x40, 0x00, 0x00, 0x02, 0x01, 0x00, 0x40, 0x80, 0x07, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x42, 0x40, 0x00, 0x08, 0x02, 0x01, 0x08, 0x40, 0x80, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x00, 0x79, 0x80, 0x04, 0x08, 0x02, 0x01, 0x08, 0x81, 0x10, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x08, 0x02, 0x01, 0x08, 0x81, 0x11, 0x04, 0x00, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x04, 0x02, 0x01, 0x08, 0x81, 0x21, 0x04, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x84, 0x02, 0x04, 0x0F, 0xFF, 0xFF, 0xC3, 0xE2, 0x04, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0xC2, 0x01, 0x07, 0xF0, 0x00, 0x00, 0x3B, 0xFE, 0x08, 0x40, 0x40, 0x08, 0x00, 0x00, 0xFE, 0x00, 0x62, 0x01, 0xF8, 0x00, 0x00, 0x00, 0x03, 0xFF, 0xE8, 0x40, 0x80, 0x7F, 0x00, 0x00, 0x00, 0x00, 0x21, 0x1E, 0x00, 0x04, 0x00, 0x80, 0x00, 0x7F, 0xFE, 0x80, 0x80, 0x08, 0x00, 0x00, 0x00, 0x03, 0x31, 0xE0, 0x00, 0x04, 0x00, 0x80, 0x04, 0x01, 0xFF, 0xC1, 0x00, 0x08, 0x00, 0x00, 0x00, 0x07, 0x1E, 0x00, 0x40, 0x00, 0x00, 0x00, 0x04, 0x00, 0x1F, 0xFA, 0x00, 0x08, 0x00, 0x00, 0x00, 0x07, 0xF0, 0x00, 0x40, 0x3B, 0x07, 0x60, 0x00, 0x00, 0x01, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x80, 0x00, 0x00, 0x34, 0x81, 0x90, 0xCC, 0xC0, 0x00, 0x3F, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x03, 0x30, 0x0C, 0x82, 0x90, 0x53, 0x20, 0x00, 0x07, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x70, 0x40, 0x00, 0xC8, 0x3B, 0x02, 0x60, 0x53, 0x20, 0x00, 0x00, 0xFE, 0x00, 0x00, 0x00, 0x01, 0x80, 0x20, 0x01, 0xC8, 0x00, 0x00, 0x00, 0x4C, 0xC0, 0x00, 0x00, 0x3F, 0x80, 0x00, 0x00, 0x06, 0x00, 0x00, 0x03, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xE0, 0x00, 0x00, 0x08, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xFC, 0x00, 0x00, 0x30, 0x00, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x00, 0x00, 0x00, 0x40, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0xA0, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x44, 0x00, 0x00, 0x00, 0x02, 0x02, 0x30, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x03, 0x06, 0x30, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x01, 0x8C, 0x30, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x00, 0xD8, 0x30, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x70, 0x19, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x20, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; //------------- void setup(){ pinMode(analogInput, INPUT); // analog input for outside audio source olcd.begin(SH1106_SWITCHCAPVCC, 0x3C); // needed for SH1106 display olcd.clearDisplay(); // clears display from any library info displayed } //------------- void loop(){ unsigned long startMillis = millis(); // start of sample window unsigned int PeaktoPeak = 0; // peak-to-peak level unsigned int SignalMax = 0; unsigned int SignalMin = 4096; //BluePill の ADC分解能は12bitなので while ( millis() - startMillis < sampleWindow ){ sample = analogRead(analogInput); if (sample < 4096) { if (sample > SignalMax) SignalMax = sample; // saves just the max levels else if (sample < SignalMin) SignalMin = sample; // saves just the min levels } } PeaktoPeak = SignalMax - SignalMin; // max - min = peak-peak amplitude float MeterValue = PeaktoPeak * 330 / 4096; // convert volts to arrow information MeterValue = MeterValue - 34; // shifts needle to zero position olcd.clearDisplay(); // refresh display for next step olcd.drawBitmap(0, 0, VUMeter, 128, 64, WHITE); // draws background float tValue = MeterValue / 57.296; int a1 = hMeter + sin(tValue) * rMeter; // meter needle horizontal coordinate int a2 = vMeter - cos(tValue) * rMeter; // meter needle vertical coordinate olcd.drawLine(a1, a2, hMeter, vMeter, WHITE); // draws needle olcd.display(); }VUメータの検証方法を検討します。校正とまではいかなくても方電源のADCでどのように解釈できるのか気になっています。
Wikipediaによると、
- VUメーターは、全波整流器付きの電圧計
- 入力電圧(dBm)は600Ωに対する1mWを示す電圧(0.775V)を0dbとしとし、4dBm=0VUとする。
- VUメーターの特性インピーダンスは7500Ω±3%が望ましい
- VUメーターの指示範囲は-20VUから+3VU
- 無信号のあと、0VUにあたる1KHz正弦波信号を入力した場合、針が0VUの99%の点を通過するまでの時間は300ms
ということで、1.228Vp-pの正弦波を全波整流してADCに入力し0VUになっていればOKぐらいで良さそうです。
オブジェクト自体はベクター殿のストレージをお借りしています。
https://www.vector.co.jp/vpack/browse/person/an051501.html 免責事項
本ソフトウエアは、あなたに対して何も保証しません。本ソフトウエアの関係者(他の利用者も含む)は、あなたに対して一切責任を負いません。
あなたが、本ソフトウエアを利用(コンパイル後の再利用など全てを含む)する場合は、自己責任で行う必要があります。本ソフトウエアの著作権はToolsBoxに帰属します。
本ソフトウエアをご利用の結果生じた損害について、ToolsBoxは一切責任を負いません。
ToolsBoxはコンテンツとして提供する全ての文章、画像等について、内容の合法性・正確性・安全性等、において最善の注意をし、作成していますが、保証するものではありません。
ToolsBoxはリンクをしている外部サイトについては、何ら保証しません。
ToolsBoxは事前の予告無く、本ソフトウエアの開発・提供を中止する可能性があります。
商標・登録商標
Microsoft、Windows、WindowsNTは米国Microsoft Corporationの米国およびその他の国における登録商標です。
Windows Vista、Windows XPは、米国Microsoft Corporation.の商品名称です。
LabVIEW、National Instruments、NI、ni.comはNational Instrumentsの登録商標です。
I2Cは、NXP Semiconductors社の登録商標です。
その他の企業名ならびに製品名は、それぞれの会社の商標もしくは登録商標です。
すべての商標および登録商標は、それぞれの所有者に帰属します。