Dateinamen auf SD-Karte finden

Hallo zusammen,

um mein Projekt "MP3-Player for Kids" abzuschliessen benötige ich nochmal Hilfe aus dem Forum. Ich benutzer dafür folgende Komponenten:

-Arduino MEGA
-Musicmaker Shield von Adafruit
-LCD Display 16/2 L2C
-SD Karte 32 GB
-Drehencoder
-Tasten

Die Ordnerstruktur auf der SD-Karte ist wie folg:
Im Root-Verzeichnis ist die Datei "current.txt". Hier wird der zuletzt gespielte Titel gespeichert. Dann gibt es für jede "CD" einen Ordner, Nummeriert von "01" bis "99". In diesen Verzeichnissen sind die mp3-Dateien gespeichert sowie eine "mp3.txt". Hier ein Beispiel für den Inhalt eines Verzeichnisses:

Type any character to start
  16013927 2017-01-30 20:49:42 04 - Feuerwehrmann Sam - Verirrt in den Pontypandy Berger.mp3
  15673139 2017-01-30 20:50:58 05 - Feuerwehrmann Sam - Die Liebesboten.mp3
  17144942 2017-01-30 20:44:44 01 - Feuerwehrmann Sam - Auf d?nnem Eis.mp3
  15322108 2017-01-30 20:46:34 02 - Feuerwehrmann Sam - L?mmchen flieg davon.mp3
  16558970 2017-01-30 20:48:16 03 - Feuerwehrmann Sam - Das supergruselige Frostmonster.mp3
        31 2017-02-04 13:21:56 mp3.txt
Done!

Die Navigation durch die Verzeichnisse habe ich so gelöst:

  char dirName[8];
  sprintf(dirName,"%02i",index);
  sd.chdir(dirName);

und mit sd.chdir() wechle ich in das entsprechende Verzeichnis. In dem Verzeichnis werden dann die mp3-Datein gezählt. Hier also 5 Dateien.
Und nun zu meinem Problem:
ich möchte vermeiden die Dateien umzubenennen in "01.mp3" bis "05.mp3", denn so wie sie oben angezeigt werden bekomme ich sie aus meinem Ripp Programm.
Also wenn ich die Dateiname so abändere funktioniert das ganzu schon. Aber gibt es die Möglichkeit anhand von einem Integer (wäre die Track Nummer) einen dazugehörigen Dateinamen zu finden?
Hier ein Beispiel: Titel Nr. 4 soll gespielt werden alse such mir eine Datei die mit "04" beginnt.

ich habe schon einige Beispiele von der SDFat library untersucht, komme aber nicht weiter.

Gruß Michael

So, wie ich es verstanden habe, kann die Lib nur Dateinemen im 8.3 DOS-Format bearbeiten.
Wenn das also die Dateinamen sind, dann sind die zu lang und haben auch noch Leerzeichen drin.

Da der Feuerwehrmann schon zu lang ist, musst Du Dir wohl andere Dateinamen einfallen lassen.

Gruß Tommy

Abgesehen von dem 8.3 Problem...

Hier ein Beispiel: Titel Nr. 4 soll gespielt werden alse such mir eine Datei die mit "04" beginnt.

Verwende die SdFat Library. Damit kann man weit, weit mehr machen. Die hat in der SdFile Klasse eine name() Methode die einen Zeiger auf den Dateinamen liefert. Da kann man dann mit strncmp() den Anfang vergleichen. Oder mit strtr() den ganzen Namen durchsuchen

EDIT:
Gerade mal probiert und es scheint auch mit der Arduino SD Library zu gehen. Die basiert schließlich auch nur auf SdFat. Da scheint wohl einiges nicht dokumentiert zu sein.
Jedensfalls kompiliert es:

const char* test = myFile.name();

Das heißt die Methode ist bekannt. Richtig getestet habe ich es nicht

Die SDFat verwende ich bereits. Die Dateiliste oben ist die serielle ausgabe des Beispiels "next File" in einem Verzeichnis auf der SD Karte.
Also das 8.3 Problem habe ich damit nicht.
Zur SdFat library finde ich leider keine Beschreibung außer die in den Beispielen.
Morgen werde ich Mal das mit der name() Methode ausprobieren.
Wenn ich den Dateinamen in eine Variable bekomme, dann kann ich den auch im LCD anzeigen lassen.

mire:
Morgen werde ich Mal das mit der name() Methode ausprobieren.
Wenn ich den Dateinamen in eine Variable bekomme, dann kann ich den auch im LCD anzeigen lassen.

Wozu die Variable?

Warum nicht etwas wie:

lcd.print(myFile.name());

Wenn ich den Dateinamen in eine Variable bekomme, dann kann ich den auch im LCD anzeigen lassen.

Der Dateiname steht schon irgendwo in den Tiefen des File Objekts. Was du bekommst ist ein Zeiger darauf. Das reicht um den Namen anzuzeigen oder mit C String Funktionen darin nach Teil-Strings zu suchen.

so, nun hab ich mal was probiert:

#include <SPI.h>
#include "SdFat.h"

// SD default chip select pin.
const uint8_t chipSelect = 4;

// file system object
SdFat sd;

SdFile file;

int currentDir=9;
int track=4;

char *getFileName(byte trackNummer){
    char *fileName;
    sprintf(fileName,"%02i",trackNummer);
    while (file.openNext(sd.vwd(), O_READ)) {
      if (strcmp(file.printName,fileName,2)==0){
        file.printName(&Serial);
      }
      Serial.println();
      file.close();
  }

  
}

void setup() {
  Serial.begin(9600);
  if (!sd.begin(chipSelect, SPI_HALF_SPEED)) {
    sd.initErrorHalt();
  }

  //char fileName[20];
  char dirName[4]="";
  char* playFile;
  
  sprintf(dirName,"%02i",currentDir);
  strcat(dirName,"/");
  sd.chdir(dirName);
  playFile=file.printName();
  playFile=getFileName(track);
}

void loop() {
  // put your main code here, to run repeatedly:

}

aber da hab ich wohl ein Problem mit dem char array und dem Zeiger, oder?

Arduino: 1.6.13 (Windows 7), Board: "Arduino/Genuino Mega or Mega 2560, ATmega2560 (Mega 2560)"

C:\Users\Michael\Documents\Arduino\testfile\testfile.ino: In function 'char* getFileName(byte)':

testfile:19: error: cannot resolve overloaded function 'printName' based on conversion to type 'const char*'

       if (strcmp(file.printName,fileName,2)==0){

                                           ^

C:\Users\Michael\Documents\Arduino\testfile\testfile.ino: In function 'void setup()':

C:\Users\Michael\Documents\Arduino\testfile\testfile.ino:42:27: warning: invalid conversion from 'size_t {aka unsigned int}' to 'char*' [-fpermissive]

   playFile=file.printName();

                           ^

exit status 1
cannot resolve overloaded function 'printName' based on conversion to type 'const char*'

Dieser Bericht wäre detaillierter, wenn die Option
"Ausführliche Ausgabe während der Kompilierung"
in Datei -> Voreinstellungen aktiviert wäre.

So viele Fehler :frowning:

 if (strcmp(file.printName,fileName,2)==0)

file.printName ? Was soll das sein? So es da steht ist es kein Methoden-Aufruf und dann geht das klar nicht. Ich dachte du wolltest den Namen? Dann wäre es name()

Und strcmp() hat nicht 3 Parameter, sondern 2. Das vergleicht komplette Strings.

Und schau dir unbedingt den Zusammenhang zwischen Arrays und Zeigern genau an. Und Zeiger allgemeinen. Du machst da katastrophalen Unsinn (das ist keine Übertreibung. Der Controller stürzt an der Stelle ab, bzw. verlässt den normalen Programmablauf):

char *fileName;
sprintf(fileName,"%02i",trackNummer);

char* ist lediglich ein Zeiger. Du brauchst schon ein Array mit richtigem Speicher. Auch daran denken dass das Platz für den Null-Terminator haben muss

Dann sind wird schon beim nächsten Fehler. Deine Funktion soll einen char* zurückgeben. Dann kannst du das Array aber nicht lokal deklarieren, da dieser Speicher am Ende der Funktion aufhört zu existieren. Du kannst es wie bei den Standard C Funktionen machen und das Array als Parameter übergeben:

char* getFileName(char* buffer, byte trackNummer)
{
   ...

   sprintf(buffer,"%02i",trackNummer);

   ...

   return buffer;
}

Und dann so aufrufen:

char buffer[10];
Serial.println(getFileName(buffer), 0));

Aber um klar zu sein: den Rückgabewert brauchst du nur wenn du bequem die Funktion direkt als Parameter in andere Funktionen einsetzten willst. Es geht auch ohne.

Und wieso machst du das strcat(dirName,"/") nicht schon in sprintf()? Das ist wenig durchdacht

Das ist wenig durchdacht

das ist auch mein erstes Arduino Projenkt an dem ich schon seit letztem September arbeite. Mit c hatte ich zuvor kaum zu tun.

Vielleicht sollte ich es lieber mit einer einfacheren Funktion ohne Rückgabewert probieren.
kannst du mir noch sagen wie ich die Methode Name aufrufe?

Wie man Methoden immer aufruft. file.name()

file.name wäre in dem Fall die Adresse der Methode. Wenn name eine öffentliche interne Variable wäre dann wäre das richtig, aber Methoden-Aufrufe brauchen immer Parameter-Klammern.

Mit c hatte ich zuvor kaum zu tun.

Dann sei mit Zeigern (und Arrays) besonders vorsichtig. C erlaubt sehr viel, aber das ist eine Stelle an der man sehr schnell, sehr große Fehler macht. Auch Profis haben da schon viele Bugs produziert.

Immer darauf achten dass man auf gültigen Speicher schreibt (also Arrays groß genug machen und daran denken dass C Strings Null-terminiert sind) und niemals Zeiger auf lokale Variablen zurückgeben.

Und wenn eine Funktion einen char* als Parameter hat, dann ist damit ein char Array gemeint. Dieses zerfällt bei der Übergabe in einen Zeiger auf das erste Element.

wenn ich das so aufrufe bekomm ich folgende Meldung:

exit status 1
'class SdFile' has no member named 'name'

dieser Code gibt mir die Dateinamen aus:

#include <SPI.h>
#include "SdFat.h"

// SD default chip select pin.
const uint8_t chipSelect = 4;

// file system object
SdFat sd;

SdFile file;

byte currentDir=9;
byte track=3;

void setup() {
  Serial.begin(9600);
  if (!sd.begin(chipSelect, SPI_HALF_SPEED)) {
    sd.initErrorHalt();
  }
  
  char dirName[4]="";
    
  sprintf(dirName,"%02i",currentDir);
  strcat(dirName,"/");
  sd.chdir(dirName);
  
  getFileName();
}

void loop() {
  // put your main code here, to run repeatedly:

}

void getFileName(){
    char fileName[4]="";
    
    sprintf(fileName,"%02i",track);         //formatieren der der Anfangszeichen
    while (file.openNext(sd.vwd(), O_READ)) {
      file.printName(&Serial);
      Serial.println();
      file.close();
  }  
}

das sieht dann so aus:

04 - Feuerwehrmann Sam - Verirrt in den Pontypandy Berger.mp3
05 - Feuerwehrmann Sam - Die Liebesboten.mp3
01 - Feuerwehrmann Sam - Auf d?nnem Eis.mp3
02 - Feuerwehrmann Sam - L?mmchen flieg davon.mp3
03 - Feuerwehrmann Sam - Das supergruselige Frostmonster.mp3
mp3.txt

mit

sprintf(fileName,"%02i",track);

erstelle ich ein Text, den ich mit den ersten beiden Zeichen der Dateinamen vergleichen möchte.

Wie komme ich an den Dateinamen? hier wird er offenbar mit
file.printName(&Serial);
angezeigt und file.name() funktioniert bei mir nicht.

Laut Library existiert die Methode (wie gesagt auch in der Standard Arduino SD Library)

  /** \return a pointer to the file's name. */
char* name() 
{
    m_name[0] = 0;
    getFilename(m_name);
    return m_name;
  }

Die liefert einen Zeiger (der seltsamerweise nicht const ist). Den setzt man dann einfach in eine andere Funktion ein. z.B.:

Serial.println(file.name());

Etwas C++ musst du schon lernen. Parameter und Rückgabewerte von Methoden ansehen. Dann sieht man wie man oft schon wie man die verwenden muss.

Ausprobiert habe ich das aber nicht :slight_smile:

Und wie gesagt: eigentlich sollte das nur mit 8.3 Namen richtig funktionieren. Wundert mich etwas dass da die langen Namen richtig gedruckt werden

die name Methode scheint nicht mehr unterstützt zu werden:

No longer implemented due to Long File Names.
Use getName(char* name, size_t size).

nun habe ich getName() probiert:

#include <SPI.h>
#include "SdFat.h"

// SD default chip select pin.
const uint8_t chipSelect = 4;

// file system object
SdFat sd;

SdFile file;

byte currentDir=9;
byte track=3;

void setup() {
  Serial.begin(9600);
  if (!sd.begin(chipSelect, SPI_HALF_SPEED)) {
    sd.initErrorHalt();
  }
  
  char dirName[4]="";
    
  sprintf(dirName,"%02i",currentDir);
  strcat(dirName,"/");
  sd.chdir(dirName);
  
  getFileName();
}

void loop() {
  // put your main code here, to run repeatedly:

}

void getFileName(){
    char fileName[4]="";
    char* fBuffer;
    sprintf(fileName,"%02i",track);         //formatieren der der Anfangszeichen
    while (file.openNext(sd.vwd(), O_READ)) {
      Serial.print(file.getName(fBuffer,1024));
      file.printName(&Serial);
      Serial.println();
      file.close();
  }  
}

und das kommt dabei raus:

04!�04 - Feuerwehrmann Sam - Verirrt in den Pontypandy Berger.mp3
05!�05 - Feuerwehrmann Sam - Die Liebesboten.mp3
01!�01 - Feuerwehrmann Sam - Auf d?nnem Eis.mp3
02!�02 - Feuerwehrmann Sam - L?mmchen flieg davon.mp3
03!�03 - Feuerwehrmann Sam - Das supergruselige Frostmonster.mp3
1p!�mp3.txt

wo liegt der Fehler?

wo liegt der Fehler?

Welcher Fehler?

Meinst du das '?' im Wort "L?mmchen" ?

Oder dass du sowas schreibst:

char* fBuffer; // undefiniert
Serial.print(file.getName(fBuffer,1024));

Ob das gut geht?

mire:
und das kommt dabei raus:

04!�04 - Feuerwehrmann Sam - Verirrt in den Pontypandy Berger.mp3

05!�05 - Feuerwehrmann Sam - Die Liebesboten.mp3
01!�01 - Feuerwehrmann Sam - Auf d?nnem Eis.mp3
02!�02 - Feuerwehrmann Sam - L?mmchen flieg davon.mp3
03!�03 - Feuerwehrmann Sam - Das supergruselige Frostmonster.mp3
1p!�mp3.txt



wo liegt der Fehler?

Der serielle Monitor kennt das Zeichen nicht. Irgendein Windoof-Zeichen?

Gruß Tommy

Das ist doch das gleiche wie vorhin. Wie deutlicher muss man noch machen, dass du einer Funktion die einen char* als Parameter hat ein char Array übergeben musst? Ein Zeiger ist lediglich eine Speicher-Adresse.

Der serielle Monitor kennt das Zeichen nicht. Irgendein Windoof-Zeichen?

Was ist das denn für ein Kommentar?
Ausser den ASCII - Zeichen zwischen 0x20 (' ') und 0x7e ('~') ist alles unklar und muss vereinbart werden.

Ein 'ü' zählt z.B. nicht zu den allgemeingültigen ASCII-Zeichen, und du kannst froh sein, dass die meisten Webserver sich mit den Browsern verstehen, so dass du überhaupt ein ü siehst. (Egal welcher linux-webserver welchen Code dafür verwendet :wink: )

Was auf der Karte steht, wird auch das sein was der Arduino sendet, denn den tangieren unicode / utf-8 und alle anderen Zeichensätze dieser Welt nicht mal peripher. (= Am Arsch vorbeigehen).
Was nimmt nun SerialMonitor an, wenn er ein Zeichen >0x7F empfängt? Einfach ignorieren tut er den Mist nicht, sonst würde ein Sketch mit   Serial.println("Gar nicht so blöd");  nicht zumindest in der IDE auf dem SerialMonitor richtig angezeigt, in der der Sketch übersetzt wurde.

Was mire uns mit " !� " sagen will, muss er wohl selbst erstmal rauskriegen..

um die Umlaute geht es mir garnicht. Folgender Code listet den Inhalt eines Verzeichnisses auf der SD Karte:

  while (file.openNext(sd.vwd(), O_READ)) {
    file.printName(&Serial);
    Serial.println();
    file.close();
  }

dabei ist mir nicht klar was das hier macht:

file.printName(&Serial);

ohne diese Zeile lauft die Schleife nicht durch und geht nicht zur nächsten Datei.

Mit dieser Zeile:

Serial.print(file.getName(fBuffer,1024));

wollte ich probieren, den Dateinamen nochmal anzuzeigen aber es zeigt nur folgendes an:

04!�

anstelle von

04 - Feuerwehrmann Sam - Verirrt in den Pontypandy Berger.mp3

meine Fragen:
-ist diese Schleife geeignet um das Problem zu lösen?
-warum funktionioert sie nicht ohne "file.printName(&Serial)"
-und wie wende ich "file.getName()" richtig an???