Ich antworte mal hier, damit es im Kontext einigermaßen erhalten bleibt.
→ Die Zusammenfassung bez. GB3200B folgt unten.
.
Das ganze DFPlayer Handling ist bei mir eigentlich in den Tonuino_DFPlayer Dateien (.h und .cpp) zusammengefasst.
Da meine Software sich strukturell deutlich von der Standardversion unterscheidet, kann ich leider nicht sagen, ob und wie der nachfolgende Code dafür relevant ist.
Trotzdem folgen hier ein paar vermeintlich relevante Auszüge.
.
Zunächst ein paar Definitionen zum nachfolgenden Verständnis
static const uint8_t largeFolders = 15; // 1-15
static const uint8_t smallFolders = 84; // 16-99
static const uint8_t FOLDERCODE_MP3 = 0;
static const uint8_t FOLDERCODE_ADVERTISEMENT = 100;
static const uint8_t TRACKNUMBER_SILENCE = 0;
static uint16_t tracksFolderLarge[largeFolders];
static uint8_t tracksFolderSmall[smallFolders];
.
Hier ein Auszug aus der
Tonuino_DFPlayer.cpp:
void TonuinoDFPlayer::setup(uint8_t pinBusy, bool hasChip_GB3200B, bool hasChip_MH2024_16SS)
{
pin_Busy = pinBusy;
hasGB3200B = hasChip_GB3200B;
pinMode(pinBusy, INPUT);
// DFPlayer Mini initialisieren
mp3.begin();
mp3.ignoreCheckSum = hasChip_MH2024_16SS;
// Zwei Sekunden warten bis der DFPlayer Mini initialisiert ist
delay(2000);
tonuinoPlayer.pauseAndStandBy();
}
void TonuinoDFPlayer::start()
{
if (!isPlaying())
{
Serial.println(F("Start player"));
mp3.start();
delay(800);
}
}
uint16_t TonuinoDFPlayer::getFolderTrackCount(uint16_t folder)
{
if (hasGB3200B)
{
// return count from last request
if (folder > largeFolders)
{
uint8_t index = folder - largeFolders - 1;
if (tracksFolderSmall[index] > 0)
{
return tracksFolderSmall[index];
}
}
else
{
uint8_t index = folder - 1;
if (tracksFolderLarge[index] > 0)
{
return tracksFolderLarge[index];
}
}
playTrack(folder, TRACKNUMBER_SILENCE);
delay(100);
pause();
}
uint16_t trackCount = mp3.getFolderTrackCount(folder);
// save track count for this folder
if (folder > largeFolders)
{
uint8_t index = folder - largeFolders - 1;
tracksFolderSmall[index] = trackCount;
}
else
{
uint8_t index = folder - 1;
tracksFolderLarge[index] = trackCount;
}
return trackCount;
}
void TonuinoDFPlayer::playTrack(uint8_t folder, uint16_t track)
{
Serial.print(F("Play track "));
Serial.print(track);
Serial.print(F(" from folder "));
Serial.println(folder);
lastStartedFolder = folder;
lastStartedTrack = track;
if (folder == FOLDERCODE_ADVERTISEMENT)
{
mp3.playAdvertisement(track);
}
else
{
activeFolder = folder;
activeTrack = track;
if (folder == 0 || folder == FOLDERCODE_MP3)
{
tonuinoPlayer.pauseAndStandBy();
mp3.playMp3FolderTrack(track);
}
else
{
if (track > 255)
{
if (folder > 0 && folder <= largeFolders)
{
mp3.playFolderTrack16(folder, track);
}
else
{
Serial.print(F("No large file support for that folder"));
}
}
else
{
mp3.playFolderTrack(folder, track);
}
delay(PLAYTRACK_DELAY);
}
}
}
void TonuinoDFPlayer::trackFinished()
{
if (hasGB3200B && (lastStartedFolder == FOLDERCODE_ADVERTISEMENT))
{
Serial.print(F("Last started track "));
Serial.print(lastStartedTrack);
Serial.print(F(" in folder "));
Serial.println(lastStartedFolder);
lastStartedFolder = activeFolder;
lastStartedTrack = activeTrack;
return;
}
Serial.print(F("Active track "));
Serial.print(activeTrack);
Serial.print(F(" in folder "));
Serial.print(activeFolder);
Serial.print(F(", Current track "));
Serial.println(tonuinoPlayer.currentTrack());
// Somehow the DFPlayer finished event is raised twice
// Ignore the second finish event
// (every new track would have the play track delay)
if (millis() - timeLastFinished < PLAYTRACK_DELAY)
{
Serial.println(F("Finish event ignored!"));
return;
}
bool isCurrentTrack = activeTrack == tonuinoPlayer.currentTrack();
activeTrack = 0;
if (isCurrentTrack)
{
tonuinoPlayer.trackFinished();
if (tonuinoPlayer.isPlaying && musicDSLoaded)
{
nextTrack();
}
}
timeLastFinished = millis();
}
.
Auszug aus der modifizierten
Tonuino_DFMiniMp3.h:
void sendPacket(uint8_t command, uint16_t arg = 0, uint16_t sendSpaceNeeded = c_msSendSpace)
{
Serial.print("Send DFPlayer command: ");
Serial.print(command);
Serial.print(" with argument ");
Serial.println(arg);
uint8_t out[DfMp3_Packet_SIZE] = { 0x7E,
0xFF,
06,
command,
00,
static_cast<uint8_t>(arg >> 8),
static_cast<uint8_t>(arg & 0x00ff),
00,
00,
0xEF };
if (ignoreCheckSum)
{
out[DfMp3_Packet_HiByteCheckSum] = 0xEF;
out[DfMp3_Packet_LowByteCheckSum] = 00;
out[DfMp3_Packet_EndCode] = 00;
}
else
{
setChecksum(out);
}
// wait for spacing since last send
while (((millis() - _lastSend) < _lastSendSpace))
{
// check for event messages from the device while
// we wait
loop();
delay(1);
}
_lastSendSpace = sendSpaceNeeded;
_serial.write(out, DfMp3_Packet_SIZE);
_lastSend = millis();
}
uint16_t listenForReply(uint8_t command)
{
uint8_t replyCommand = 0;
uint16_t replyArg = 0;
do
{
if (readPacket(&replyCommand, &replyArg))
{
if (command != 0 && command == replyCommand)
{
return replyArg;
}
else
{
switch (replyCommand)
{
case 0x3c: // usb
T_NOTIFICATION_METHOD::OnPlayFinished(DfMp3_PlaySources_Usb, replyArg);
break;
case 0x3d: // micro sd
case 0x4c:
T_NOTIFICATION_METHOD::OnPlayFinished(DfMp3_PlaySources_Sd, replyArg);
break;
case 0x3e: // flash
T_NOTIFICATION_METHOD::OnPlayFinished(DfMp3_PlaySources_Flash, replyArg);
break;
case 0x3F:
_isOnline = true;
T_NOTIFICATION_METHOD::OnPlaySourceOnline(static_cast<DfMp3_PlaySources>(replyArg));
break;
case 0x3A:
_isOnline = true;
T_NOTIFICATION_METHOD::OnPlaySourceInserted(static_cast<DfMp3_PlaySources>(replyArg));
break;
case 0x3B:
_isOnline = true;
T_NOTIFICATION_METHOD::OnPlaySourceRemoved(static_cast<DfMp3_PlaySources>(replyArg));
break;
case 0x40:
T_NOTIFICATION_METHOD::OnError(replyArg);
return 0;
break;
default:
// unknown/unsupported command reply
break;
}
}
}
else
{
if (replyArg != 0)
{
T_NOTIFICATION_METHOD::OnError(replyArg);
if (_serial.available() == 0)
{
return 0;
}
}
}
} while (command != 0);
return 0;
}
.
Auszug aus der
Tonuino_MainController.cpp:
// DFPlayer Mini initialisieren
dfPlayer.setup(pinConfig.DFPlayer_Busy, hwConfig.Chip_GB3200B, hwConfig.Chip_MH2024_16SS);
// set settings
dfPlayer.volumeMin = swConfig.VolumeMin;
dfPlayer.volumeMax = swConfig.VolumeMax;
dfPlayer.volumeIncrement = swConfig.VolumeIncrement;
dfPlayer.setVolume(swConfig.VolumeInit);
dfPlayer.setEqualizer(swConfig.Equalizer);
[...]
dfPlayer.start();
// give DFPlayer some more time to init (otherwise finish event of MP3 track is not generated)
delay(1000);
// play startup sound
dfPlayer.playMP3AndWait(261);
.
.
.
Anmerkungen
In meiner getFolderTrackCount()
Methode habe ich noch folgende Anpassungen drin:
-
Speichern der Titelanzahl in einem Array
- Da sich die Titelanzahl in dem Ordner während des Betriebs nicht ändert
- Bei der nächsten Abfrage für diesen Ordner, wird dieser Wert zurückgegeben
- Es wird nicht erneut „Stille“ abgespielt und auch kein
mp3.getFolderTrackCount(folder)
Aufruf
-
Da ich in meiner Software LargeFolder * unterstütze, ist das o.g. Array-Handling auf „Large“ und „Small“ folder aufgeteilt (um 84 Byte Speicherplatz zu sparen)
- .* „Large“ Ordner sind die Ordner (0 bzw.) 1-15, wo mehr als 255 Titel enthalten sein können.
- Um genau zu sein, können bis zu 3000 Titel in diesen Ordnern enthalten sein
- .* „Large“ Ordner sind die Ordner (0 bzw.) 1-15, wo mehr als 255 Titel enthalten sein können.
.
.
.
Anpassungen für Chip GB3200B:
Wenn ich das richtig überblicke, sind folgende Anpassungen relevant:
- Einen Titel aus genau dem Ordner abspielen, wo man die Titelanzahl mit
getFolderTrackCount(folderNumber)
ermitteln will.- Vorzugsweise einen Titel ohne echte Geräusche also „Stille“, bspw:
playTrack(folder, TRACKNUMBER_SILENCE);
delay(100);
pause();
uint16_t trackCount = mp3.getFolderTrackCount(folder);
- Das Finish-Event für Advertisement Titel abfangen, bspw. in meinem Kontext (s.o.):
if (hasGB3200B && (lastStartedFolder == FOLDERCODE_ADVERTISEMENT))
{
lastStartedFolder = activeFolder;
lastStartedTrack = activeTrack;
return;
}
.
.
.
Anpassungen für Chip MH2024_16SS:
Wenn ich das richtig überblicke, sind folgende Anpassungen relevant:
- Ausreichend Verzögerung nach dem mp3.begin() und mp3.start()
- Afaik 1-x Sekunden, bspw.
delay(1000);
- Im DFMiniMP3 die Notification-Case 0x4C hinzufügen, um das FinishEvent zu erzeugen
case 0x3d: // micro sd
case 0x4c:
T_NOTIFICATION_METHOD::OnPlayFinished(DfMp3_PlaySources_Sd, replyArg);
break;
Sowie die Checksummen Prüfung ignorieren:
uint8_t out[DfMp3_Packet_SIZE] = { 0x7E,
0xFF,
06,
command,
00,
static_cast<uint8_t>(arg >> 8),
static_cast<uint8_t>(arg & 0x00ff),
00,
00,
0xEF };
if (ignoreCheckSum)
{
out[DfMp3_Packet_HiByteCheckSum] = 0xEF;
out[DfMp3_Packet_LowByteCheckSum] = 00;
out[DfMp3_Packet_EndCode] = 00;
}
else
{
setChecksum(out);
}