DFPlayer制御

最終更新日2021年3月2日

このプロジェクトの目的は、『DFPlayer』と呼ばれるMP3モジュールをLabVIEWから制御することです。

『DFPlayer』自体は単体でTFカード内のMP3/WMA等の音声ファイルを読み取ってDACして内蔵アンプを通してステレオ出力するモジュールです。このモジュールにはパワーアンプも搭載されており、Lch音声を最大3Wでスピーカドライブ可能です。
チップは『YX5200-24SS 』だと思われます。セカンドソースもたくさん出ているようです。
先人の書込を見ていると、昔小型の『iPod shuffle』が流行った頃に作られたカスタムチップが汎用品化されているという印象を持ちました。※Apple製品はTFカードが使えません。APODとして売られているパッチモンがそれですね。

PCから敢えてリモート制御してまでMP3プレーヤを制御する必要性があるのか、要検討ですが、音声データ再生をリモート制御するとなれば用途はあるのかと思い、このプロジェクトを起案した次第です。

とても安価でコンパクトなスピーカBOXを、LANケーブル接続し、音声再生を遠隔地のPCから行います。予め用意しておいた別人の音声にて対応出来るため、意味がありますし、基本的な音声対応はスピーカBOX自体で自己完結し、EventLogだけPCに転送するというやり方も有りなのかと思います。


とりあえず代表的なchipである『YX5200-24SS 』をコマンド制御出来るツールを作成しました。
『YX5200-24SS 』のchipメーカは判っていません。そのためコマンドリファレンスもモジュールメーカであるKEYSTUDIO社が提供している資料から判断しています。
https://wiki.keyestudio.com/KS0387_keyestudio_YX5200-24SS_MP3_Module

※KEYSTUDIO社のこのYX5200 MP3 デコーダミニですが、2020年5月29日にやっと入手出来ました。発注後実に90日経ってしまいました。
このモジュールですが、ヘッドフォンを接続してTFカードからMP3ファイルを再生したのですが、ノイズも少なくて好感が持てます。DIP型のモジュールが余りにノイズが酷いことを思うと、アナログ部に配慮があることは明かのようです

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



【ホストアプリケーション】

アプリケーションは『設定』タブで接続先を選択し、接続後、『制御』タブに移って音声再生します。
目的が音楽再生だと『シャッフル再生』だとか『リピート再生』だとかの機能が必要かと思います。確かにこのchipには用意されています。
一方フォルダでカテゴリ分けして幾つかの音声ファイルをデータベースライクに用意しておけば、気の利いたメッセージをピンポイントで再生することが出来ます。

@ 終了ボタン アプリケーションを終了する。
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メッセージ イベントが起きたときのメッセージを表示
@ エラー履歴  

内部ではコマンド部は一つのVIに纏めてあります。

ターゲットデバイスと接続形態はシリアル接続とLAN接続を用意しています。

シリアル接続はDFPlayerが用意しているUARTにUSB<->シリアル変換モジュールで接続することを想定しています。PCにCOMポートがあったとしても電気的に直接接続出来ません。安価なモジュールがたくさんリリースされていますので適当な物で接続します。
※PC側ボーレートの設定方法ですが、アプリケーション側から変更出来れば良いのですが、その限りでは無い場合、デバイスドライバにて初期値を決定しておくべきです。DFPlayer側のボーレートが9600bps 8bit StopBit1 NonParty NonFlow

LAN接続するにはLAN<->UART変換器が必要になります。コマンドを単に変換するだけですので市販品もあるのかと思ったのですが、判断しづらいです。で有れば、『STM32MINIShield』で実現した方が確実です。
WIZ107SRが説明文を読む限りLAN<->UART変換器のようです。
LabVIEWとはTCPソケット通信して、『STM32MINIShield』側で受信したコマンド文字列を接続されたDFPlayerに垂れ流す。
一方、DFPlayerからの応答もTCPソケット通信で返信するという物です。
この場合一寸問題があります。DFPlayerにTFカードが挿入されたとか抜かれたというEventが『STM32MINIShield』側迄は認識されてもLAN接続されたLabVIEWに通知する仕組みが用意出来ていないと云う事です。

一応LAN接続時の『STM32MINIShield』のコードも紹介しておきます。Arduino(STM32duino)のスケッチです。

このコードを実行するに当たり追加が必要なライブラリは、

/*
 * 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);     
 }

SlaveデバイスのIPアドレスやMACアドレスはBluePill内に書き込んでいます。Webブラウザでアクセスして変更が可能です。


折角『STM32MINIShield』を噛ませるのであれば、キーマップモジュールを用意して直接音声を選択出来る様な物を検討しても良いかと思います。

LED&KEYで採用されているchip、TM-1638は24個のタクトスイッチをスキャンできるように設計されています。
実際に24個タクトスイッチが搭載されたモジュールもあるのですが流通していません。
流通している物だと16個のタクトスイッチが搭載されて200円程度で提供されているQYF-TM1638があります。これを使用して16曲をダイレクトに選曲できるように出来ます。
ライブラリはhttps://github.com/rjbatista/tm1638-libraryがそのまま使える様です。


オブジェクト自体はベクター殿のストレージをお借りしています。

https://www.vector.co.jp/soft/winnt/hardware/se514018.html
https://www.vector.co.jp/soft/winnt/hardware/se514019.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社の登録商標です。
その他の企業名ならびに製品名は、それぞれの会社の商標もしくは登録商標です。
すべての商標および登録商標は、それぞれの所有者に帰属します。