DFPlayer mini (MP3-Modul) - Auslesen der Anzahl von Tracks

Hi,

ich experementiere gerade mit oben genanntem MP3-Modul. Start / Stop, Track vor / zurück, Lautstärke hoch / runter .... soweit funktioniert alles.

Was ich nicht hinbekomme ist die Anzahl der vorhandenen Sound-Files auslesen.

Die Bibliothek "DFPlayer_Mini_Mp3.h" beinhaltet eine Funktion namens "mp3_get_tf_sum". Ich komme nicht dahinter, wie diese funktionieren soll (bin auch kein Profi).

Kann mir jemand weiterhelfen, wie ich weiterkomme?

Wenn ich das richtig sehe, dann gibt es da einen Fehler in der Player-Library*. Mit dem Befehl

mp3_get_tf_sum();

werden anscheinend die Tracks der "U-Disk" ausgelesen

und mit

mp3_get_u_sum();

werden die Tracks der "TF-Card" ausgelesen

also genau umgekehrt als es vorgesehen ist - jedenfalls gibt es da ein Diskrepanz zwischen der Doku auf der Website (siehe unten) und der Library.

Schick also mal den 2. "Befehl" und auf der (Software-)Seriellen Schnittstelle sollte dann der Player antworten und die Anzal der Tracks preisgeben.


*) Mit der Library ist das wieder mal so ne Sache - die ist eigentlich gar nicht nötig ist, weil die hauptsächlich einfache Kommandos an den Player schickt (siehe DFPlayer Mini Mp3 Player - DFRobot Wiki).
Das kann man auch einfach "per Hand" machen und weiß dann viel eher was da abgeht :slight_smile:
Find ich jedenfalls.

Danke für den Hinweis des "Drehers", hatte ich noch nichts drüber gelesen.

Der Funktionsaufruf leuchtet mir schon ein. Aber dann?
Wie bekomme ich das Lesen der seriellen Schnittstele hin, so dass ich eine Variable mit dem Wert füllen kann?

Ja, die Befehle kann man auch "per Hand" senden, aber so ist es für mich erst einmal einfacher zu lesen. Mit etwas mehr Erfahrung werde ich sicherlich "umstellen".

Bei der Library sind auch Beispiele dabei. Schau dir die mal an.
Beim Beispiel "DFPlayer_Mini_Test.ino" wird auch die Anzahl der Tracks auf der Karte ausgelesen. Zum Auslesen wird die Funktion

print_info ();

verwendet.
Damit kannst du dir die Antwort des Players am seriellen Monitor anschauen.
Schau dir mal an, wie das in dem Beispiel gemacht wird.

Allerdings ist das erwähnte Beispiel für einen Arduino Leonardo gedacht (Micro würde auch gehen). Dieser hat aber quasi 2 serielle Schnittstellen:
Serial - die zur Kommunikation über USB dient und
Serial1 - das sind die Pins D0 und D1

Wenn man einen anderen Arduino benutzt (du schreibst ja nicht, welchen du hast), zum Beispiel einen UNO, der nur eine serielle Schnittstelle in Hardware hat, dann musst man "Serial1" durch SoftwareSerial / "mySerial" ersetzen.

Ich kann dir leider nur so vage antworten, weil ich deine Hardware nicht habe und es deshalb nicht ausprobieren kann. Ich habe zwar schon öfter mit verschiedenen seriellen MP3-Playern gearbeitet, aber mit "deinem" noch nicht und ich weiß deshalb auch nicht genau, wie die Antwort des Players ausschaut. Von anderen Playern weiß ich aber, dass die Antwort (die auf der seriellen Schnittstelle zurückkommt) meist recht gut auszuwerten ist, ein wenig Erfahrung vorausgesetzt. :slight_smile:

Also die grundsätzliche Vorgehensweise habe ich so verstanden:
Mit dem Funktionsaufruf "mp3_get_tf_sum()" bzw. "mp3_get_u_sum()" veranlasse ich den mp3-Player Daten auf die serielle Schnittstelle zu schicken. Danach muss ich den Arduino - hier übrigens ein Uno - auf "Empfang schalten".

Den von Dir genannten Beispielsketch habe ich mir angeschaut. Darin wird eine Funktion "print_Hex" aufgerufen, die wohl Bestandteil der Bibliothek <DFRobot_utility.h> ist. Ich möchte diese aber nicht einbinden, damit der Code nicht aufgebläht wird und ausserdem will ich ja auch verstehen und lernen.

Hier mal ein Teil meines Codes "serielle Schnittstellen":

  #ifdef DEBUG                              // Debug-Routine
    Serial.begin(9600);                    // Ausgabe auf PC-Monitor
  #endif     
        
 mp3Serial.begin (9600);               // Serielle Verbindung zu MP3-Player
 mp3_set_serial (mp3Serial);           // Initilisierung Player

... und der Funktionsaufruf:

// Anzahl Files auf SD-Karte
void AnzahlTracks()
{
  byte incommingByte = 0;                                               // Variable für gelesene Daten
  mp3_get_u_sum();                                                      // Anzahl Files bestimmen
  delay (10);
  if (mp3Serial.available() > 0)
     {
       incommingByte = mp3Serial.read();     
       if (DEBUG == 1) Serial.println("Lese Wert ... ");
     }
  if (DEBUG == 1) Serial.print("Byte :  ");
  if (DEBUG == 1) Serial.println(incommingByte, DEC);
}

Lasse ich den Sketch laufen, dann erhalte ich als Ausgabe auf dem Monitor die Zahl "0". Die Meldung "Lese Wert ... " bleibt aus, also ist die If-Anweisung nicht erfüllt.

Da der Code der Arduino-Reference entnommen wurde und nicht funktioniert, stehe ich wieder auf dem Schlauch. :o

Verwende ich diesen Code ...

// Anzahl Files auf SD-Karte
void AnzahlTracks()
{  
  int daten = 0;
  mp3_get_u_sum();                
  daten = mp3Serial.read (); 
  if (daten)
   {
         Serial.println("Lese Wert ... ");
         Serial.println(daten);
    }
}

.... dann ist die If-Anweisung erfüllt und er meldet "-1". Es tut sich also was ...

Du verstehst nicht was du machst. -1 von read() bedeutet das keine Daten da sind. true/false ist aber so definiert, dass 0 false ist und alles ungleich 0 ist true. -1 ist aber dann wenn du dass so machst true.

-1 bedeutet nur, dass KEINE Daten vorhanden sind.
Du musst dem Player und der Seriellen Schnittstelle auch Zeit geben zu antworten. Die serielle Übertragung ist ziemlich langsam (im Vergleich zu den meherern Millionen Anweisungen, die der Arduino pro Sekunde ausführen kann). Am besten geht das, indem du bei jedem Loop-Durchlauf (wenn du keine Delays verwendest, dann sind das oft mehrere Tausend pro Sekunde) mit

if(mySerial.available()) {
// einlesen eines Bytes und hinzufügen in ein Array oder in einen Buffer
}

abfragst ob neue Daten vorhanden sind oder nicht.
Meist gibt es ein "Endzeichen" am Ende der Antwort - sehr häufig ein Zeilenvorschub oder ein Carriage Return oder beisdes. So kannst du herausfinden, wann die Antwort fertig übertragen ist. Aber welches Endzeichen dein Player sendet musst du erst mal herausfinden. Die Doku ist da leider nicht so besonders gesprächig.

Weiters:

daten = mp3Serial.read();

liest nur ein einziges Byte und nicht die ganze Antwort.
Das sind die Fehler, die fast alle am Anfang machen. :slight_smile:

Schau dir mal an, wie das mit serieller Übertragung so grundsätzlich funktioniert. Vor allem wie du die Antwort auswerten kannst die aus mehreren Zeichen bestehen. Zum Beispiel:
Serial Input Basics

Ganz grundsätzlich: Du musst erst mal den Player irgendwie zum Sprechen bekommen - bzw. sprechen tut er eh, du kannst es zur Zeit nur nicht lesen.

Den von Dir genannten Beispielsketch habe ich mir angeschaut. Darin wird eine Funktion "print_Hex" aufgerufen, die wohl Bestandteil der Bibliothek <DFRobot_utility.h> ist. Ich möchte diese aber nicht einbinden, damit der Code nicht aufgebläht wird und ausserdem will ich ja auch verstehen und lernen.

Ja dein Wille sei dir gegönnt, aber du musst jetzt mal IRGENDWIE die Antworten des Players zu Gesicht bekommen. Über Optimierung etc. kann man später nachdenken.

Als Hack kann man erst mal nach dem Kommando z.B. 50ms warten. Das ist mehr als genug Zeit um eine Multi-Byte Antwort zu empfangen (bei 9600 Baud dauert ein Zeichen 1 / 9600 * 10 Sekunden ~ 1ms). Und dann in einer while-Schleife read() machen (nicht-blockierend geht das natürlich nicht)

Laut Datenblatt wird anscheinend 0xEF als Endzeichen verwenden. Sowohl beim Senden als auch beim Empfangen. Generell sollte man vielleicht die empfangenen Daten als HEX anzeigen lassen

Also, der Beispiel-Sketch läuft nicht .....

Natürlich läuft der nicht - siehe #3 (Antwort 3), weil dieser für ein anderes Arduino Board geschrieben ist - nämlich für eine Leonardo, der hat "Serial" und "Serial1" (das habe ich schon in #3 erwähnt).
Dein Uno hat kein "Serial1", deshalb musst du Serial1 durch dein "SoftwareSerial" ersetzen - du nennst es glaube ich "mp3Serial".

Ich würde dir ja gerne einen getesteten Code schicken, bin aber im Moment weit weg von meinen Arduinos und mp3-Playern.
Und so ganz ins Blaue hinein will ich nichts coden...

Habe mal "versucht" Serial1 durch mp3Serial zu ersetzen.
und hinzugefügt

SoftwareSerial mp3Serial(10, 11); // Pins für RX, TX
mp3Serial.begin (9600); // Serielle Verbindung zu MP3-Player
mp3_set_serial (mp3Serial);

aber ...

Test_DFPlayer_Mini_Test:86: error: could not convert 'mp3Serial' from 'SoftwareSerial' to 'HardwareSerial'

int recv_leng = serialRead (mp3Serial, recv_buf, 10, 3);

da muss noch was nicht passen.

Also seriell "senden" kann ich. mp3_play, mp3_volume etc. werden ausgeführt. Theoretisch muss also die serielle Verbindung "stehen".

Ein "delay(x)" > für x = 1, 5, 10, 20, 50, 100, 500 < nach "mp3_get_u_sum" hat keine Änderung gebracht.

Dass die gesendete serielle Information aus mehreren Zeichen besteht, habe ich nun verstanden. Diese sind zu sammeln bis ein "Endezeichen" - welches ea auch hier sein mag - ankommt und dann auszuwerten.
Aber damit brauche ich ja erst gar nicht anzufangen, wenn ich noch nicht einmal "ein" Zeichen erhalte.
Die if-Anweisung

if (mp3Serial.available() > 0)

wird nicht befolgt, also kommt nichts an.

Ich habe jetzt mal folgendes probiert:

// Anzahl Files auf SD-Karte
void AnzahlTracks()
{  
  byte daten;
  mp3_get_volume ();  
  delay(5);
  int var = 0;             
  while (var < 20)
  {
    if (mp3Serial.available() > 0)
    {
     daten = mp3Serial.read(); 
     Serial.println("Lese Wert ... ");
     Serial.println(daten);
    }
    else 
    {
     Serial.println("Fehler");
    }
    var++;
 }
}

Da ich ja Volume einstellen kann, habe ich es jetzt mal versucht zu lesen. Bei den ersten 5 Durchgängen der while-Schleife wurde mir "Fehler" gemeldet. Dann erhielt ich 10 Werte (alle anders, aber immerhin) und dann wieder 5 Fehler.

Laienhaft vermute ich jetz mal, das da was mit dem "Timing" nicht funktioniert. Vielleicht fällt Euch ja dazu was ein :slight_smile:

Für heute Feierabend, wünsche alle Lesern eine schönes Wochenende !

Die serielle Kommunikation ist fürchterlich langsam im Vergleich zur Ausführung von Befehlen am Arduino und braucht bei 9600 Baud ca. 1 Millisekunde pro Zeichen - in dieser Zeit können wohl ein paar Tausend Befehle durchgeführt werden.

Aber wenn du 10 Werte erhalten hast, dann ist das ja gar nicht so schlecht.

Ich habe deine Hardware nicht, aber ich glaube, du könntest mal folgendes versuchen:

#include <SoftwareSerial.h>
#include <DFPlayer_Mini_Mp3.h>
SoftwareSerial mp3Serial(2,3);    // RX, TX

void setup() {
    mp3Serial.begin(9600);
    Serial.begin(9600);
    mp3_set_serial (mp3Serial);  //set softwareSerial for DFPlayer-mini mp3 module 
    delay(1000);
    mp3_get_volume();
    delay(1000);       // ein wenig warten auf Antwort, "dirty method" :-)
    if (mp3Serial.available())
    {  Serial.println("Der MP3-Player antwortet:");
        while(mp3Serial.available())
        {   Serial.print(mp3Serial.read(),HEX);
             Serial.print(" ");
        }
    }
}

void loop() 
{      
}

Damit wird die aktuelle Lautstärke abgefragt.
Das ist eher kein Code für "Normalbetrieb", sondern nur um "Quick&Dirty" mal eine Antwort zu bekommen.
Die Ausgabe erfolgt übrigens in HEX.

Du solltest auch bedenken, dass die Antwort vielleicht nicht unbedingt besonders "menschenlesbar" sein könnte...

Bei einem meiner MP3-Player zum Beispiel schaut eine Antwort auf die Frage nach der eingestellten Lautstärke so aus (es sind übrigens auch 10 Byte):

7E FF 06 43 00 00 1E FE 9A EF

Die Lautstärke "verbirgt" sich in diesem Fall im 1E, der Rest ist "Kommunikations-Beiwerk", bei meinem mp3-Player schaut das nämlich so aus:

Kommunikationsstruktur 
z.B. Die Antwort des mp3-Players auf die Abfrage der Lautstärke
Die Lautstärke hat aktuell den Wert 1E

7E FF 06 43 00 00 1E FE 9A EF
   1. Byte: 0x7E   Startzeichen der Kommunikationssequenz
   2. Byte: 0xFF   immer 0xFF
   3. Byte: 0x06   Länge des Kommandos in Byte (ohne Start, Ende, Parity) - meist 06
   4. Byte: 0x43   Kommando (in diesem Fall die Lautstärke: 43)
   5. Byte: 0x00   Feedback 00 = kein Feedback, 01 = Feedback senden (default: 00)
   6. Byte: 0x00   Data 1 (High Byte)
   7. Byte: 0x1E   Data 2 (Low Byte) - hier also der aktuelle Wert der Lautstärke
   8. Byte: 0xFE   Checksumme / Parity (High Byte)
   9. Byte: 0x9A   Checksumme / Parity (Low Byte)
  10. Byte: 0xEF   Endzeichen der Kommandosequenz

Auf die Frage nach der Anzahl der Tracks auf der SD-Karte lautet die Antwort meines MP3-Players übrigens:

7E FF 06 48 00 00 13 FE A0 EF

Die Struktur ist ganz gleich wie oben.
48 ist das Kommando ("Anzahl der Tracks auf SD") und die eigentliche Trackanzahl ist 13 - das ist ja eine "HEX-Zahl", auf der SD-Karte befinden sich also 19 Tracks (HEX 13 = 19 Dezimal).

Wie ich schon mal erwähnt habe, hab ich schon mir mehreren MP3-Playern, die sich seriell steuern lassen und Arduino gearbeitet. Die Kommunikationsstruktur war meist ziemlich ähnlich (wenn auch nicht ganz gleich). Es wäre also durchaus möglich, dass dein MP3-Player auch so ähnlich kommuniziert.

Übrigens:
Es gibt ein Manual, da ist das alles recht gut beschrieben, finde ich:
DFPlayer Mini Manul.pdf
War gar nicht so schwer zu finden.

Vielen Dank erst einmal !!!

Habe Deinen Code gerade mal ausgeführt. Rückmeldung:

Der MP3-Player antwortet:
7E FF 6 43 0 0 F FE A9 EF

bei "mp3_get_u_sum" meldet er:
7E FF 6 47 0 0 0 FE B4 EF

und bei "mp3_get_tf_sum":
7E FF 6 48 0 0 13 FE A0 EF

Wie Du schon richtig bemerkst hast, bin ich sicherlich bei "seriellen Daten" blutiger Anfänger. Ich muss mich jetzt erst einmal mit den "gewonnenen" Daten und deren Auswertung beschäftigen.
Wenn ich es richtig verstanden habe, dann werden - nennen wir es mal "Steuerzeichen" - gesendet und zwischendrin verbirgt sich der gesuchte Wert. Die Steuerzeichen kann ich "in die Tonne" hauen. Der 7te Wert ist für mich also der Interessante.
Verständnisfrage: Werden immer 10 Byte gesendet? Ist immer der 7te-Wert der Gesuchte?

Noch eine Frage:
In Deinem Beispiel-Sketch steht hinter "Delay" die Bemerkung "Dirty Code".
Weil Du den Befehl "Delay" grundsätzlich vermeiden willst oder dies viel eleganter gelöst werden kann?

Super, dass es jetzt schon mal anfängt zu klappen!

So wie es aussieht, funktioniert mp3_get_tf_sum also doch richtig - ausgezeichnet.

Ja, die serielle Kommunikation ist am Anfang mitunter recht verwirrend.

gj99:
Werden immer 10 Byte gesendet? Ist immer der 7te-Wert der Gesuchte?

So wie ich das sehe, werden eigentlich immer 10 Byte als Antwort vom Player gesendet (siehe Manual in #15).

Das "Protokoll" steht in #14:
Die "eigentlichen" Daten werden mit Byte 6 und 7 übertragen.
Byte 7 ist das "niedrigere" Byte und Byte 6 ist das "höhere" Byte.
Ein Byte kann maximal den Wert 255 haben. Also bei Antworten die einen maximalen Wert von 255 übertragen "steht" die Antwort allein in Byte 7, denn Byte 6 ist bis 255 ja immer Null, zum Beispiel bei der Lautstärke, die geht nur bis 30 (dezimal) bzw. 1E (hex).
Aber bei anderen Antworten musst du auch das 6. Byte auswerten, zum Beispiel bei der Abfrage der Trackanzahl auf der Karte - es wären ja auch mehr als 255 Tracks möglich.
Du musst also Byte 6 und Byte 7 auswerten: Byte 6 mit 256 multiplizieren und Byte 7 dazuzählen:

 Trackzahl    Byte   Byte
  (dezimal)     6      7
-------------------------------
       5        00     05
      12        00     0C
     255        00     FF
     256        01     00
     300        01     2C

gj99:
In Deinem Beispiel-Sketch steht hinter "Delay" die Bemerkung "Dirty Code".
Weil Du den Befehl "Delay" grundsätzlich vermeiden willst oder dies viel eleganter gelöst werden kann?

Beides.
Delay hält die Ausführung des Codes für ein bestimmte Zeit praktisch vollständig an. Es kann während dieser Zeit auf nichts reagiert werden (z.B. Tastendrücke des Benutzers, ankommende Signale auswerten) und es gib auch keine Möglichkeit ausgehende Signale zu senden (z.B. ein Display zu aktualisieren) weil eben "alles steht". Deshalb sollte man Delays vermeiden. Delay hat aber duchaus seine Berechtigung bei "ganz ganz einfachen" Aufgaben und wenn ohnehin sonst wirklich nichts anderes zu tun ist :slight_smile:
In diesem Fall ging es ja nur darum zu schauen, ob der Player überhaupt irgendetwas sendet, aber es musste eben einige Zeit gewartet werden. Für dieses Ausprobieren ist ein Delay voll OK und auch sehr anschaulich. Im fertigen Programm (vor allem in der Loop) sollte aber Delay vermieden werden.

Man kann die Verzögerung auch drastisch reduzieren. Bei 9600 Baud braucht man ca. 1ms pro Zeichen (1 / 9600 * 10 Sekunden). Wenn man dann dem Player etwas Zeit für die Bearbeitung gibt reichen vielleicht 20-50ms. Damit kann man u.U. auch leben.