MP3Player

最終更新日:2020年3月18日


【履歴】



    MP3Player

    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

デバイス内のフォルダー数を問い合わせる。

Control Instruction

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
parentheses is the folder name,
and the last number is the song name.

7E FF 15 21 01 02 02 03 01 04 01
03 01 04 01 05 02 08 03
04 03 01 FE 9A EF

with checkout [1,2][2,3][1,4][1,3][1,4][1,5]
[1,8][3,4][3,1]

7E FF 15 21 01 02 02 03 01 04 01
03 01 04 01 05 02 08 03
04 03 01 EF

[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

Inquiry Instructions

[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
folder tracks

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
and U-diskFLASH


Return Information

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
FLASH online

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


Error Information

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
in sleep mode

Serial receiving error

7E

FF

06

40

00

00

03

xx

xx

EF

The serial port has not received
a frame of data

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
setting range

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
status

TF card play error

7E

FF

06

40

00

00

08

xx

xx

EF

TF card read failed or TF card
pull out

FLASH
initialization error

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>&nbsp;"));
            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メータ】

このプロジェクト例に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社の登録商標です。
その他の企業名ならびに製品名は、それぞれの会社の商標もしくは登録商標です。
すべての商標および登録商標は、それぞれの所有者に帰属します。