read first file of SD card

Hi :smiley: ,

I search a way to find the first file of my SD card and open it. After I want to retrieve the value contained in this files, put this value in a variable and remove the file.

Help me please !

Thanks

Since an SD card can contain multiple files in multiple nested directories, how do you define "the first file of my SD card"?

By creation date? By some portion of the name?

Hi PaulS,

When my ethernet connection is failled, my program creates a new file in the SD card and save the value of the analog pin 0, while ethernet is disconnecting it creates a new files each second.When i'm connecting I want to retrieve each value one by one et send it to my server apache.

I don't create directories, my files are straight in the SD card.

For the name of the files, in my program i create a counter that give me time and date. Each new file take to name the time and date of my counter.

I want just a way to retrieve the first file contained in my SD and open it.

thanks

PS: Sorry for my bad English !! XD

There is no implied order to the files you create. You'll need to look at each one, and, from the name, determine which one is oldest. Then, deal with that file.

ok, but some people spoke to me of the fonctions "Dir" or "findfirstfile" in C language.

Can Someone tells me more on this fonctions ?

There are no guarantees about ordering. Different file systems have different rules. FindFirstFile (which is also used by the DOS DIR command) is used for enumerating files that match a given pattern in a directory. Generally, one constructs a loop to continue while FindNextFile doesn't return an error, if FindFirstFile was successful. Then FindClose is called.

I have no experience with the support lib's for SD, but they must offer some functions for enumerating files in a directory, and that's almost certainly what you want to do.

ok, thanks everybody.

I have an other problem. When my ethernet connection is failled, my program creates a new file in the SD card and save the value of the analog pin 0, while ethernet is disconnecting it creates a new files each second. I want that each new file take to name the time and date of my counter. When i retrieve the name for a file I use the function SD.open to create and open a new file with this name. But i have a problems of parameters.

Here is my code :

String name=day+hour+minute; // name= number of day, hour and minute of my counter

myFile = SD.open(name, FILE_WRITE); // the problem is here, this function don't accept a String

if (myFile)
{
Serial.println("fichier ouvert"); // serial print "file open"
myFile.println(data); // print the value of pin A0
myFile.close();
}
else
{
Serial.println("Impossible d'ouvrir le fichier"); // serial print "Can't open file"
}

I don't know how to solve the problem.

thanks you in advence

But i have a problems of parameters.

I don't know how to solve the problem.

Well, the first step is to tell us what the problem is.

What are day, hour, and minute? Are they String objects that can be concatenated like you are doing, or are they ints that are being added instead?

Maax:

    myFile = SD.open(name, FILE_WRITE); // the problem is here, this function don't accept a String

The SD.open() function takes a pointer to char or name of an array that holds the "string" in memory (an old-fashioned C-style "string" that is a sequence of chars in memory, terminated by a zero byte).

You can use the String library function toCharArray() to copy a String object value to a C-style "string" in memory:

void setup()
{
    Serial.begin(9600);
}

const int bufferSize = 30; // Must be large enough to hold the "string" in an array of chars.
void loop()
{
    String day = "12";
    String hour = "34";
    String minute = "56";
    String fileName = day+hour+minute;
    Serial.print("fileName as string: ");
    Serial.println(fileName);
    char nameBuffer[bufferSize];
    fileName.toCharArray(nameBuffer, bufferSize);
    Serial.print("fileName using printCharString: ");
    printCharString(nameBuffer);
    Serial.println();
    delay(1000);
}

// An example of a function that requires a C-style "string" as its argument
void printCharString(const char *s)
{
    Serial.print(s);
}

Output:


fileName as string: 123456
fileName using printCharString: 123456

Of course, you don't call my printCharString() function; you feed the name of the array to the String open() function.

Regards,

Dave

Creating a separate file for each individual analog read value is extremely inefficient with regards to memory usage on the SD card. You'll be far better off using a single file that contains analog readings + timestamp information.

What are day, hour, and minute? Are they String objects that can be concatenated like you are doing, or are they ints that are being added instead?

day, hour and minute are Int. I put them in a string to be concatenated.

Creating a separate file for each individual analog read value is extremely inefficient with regards to memory usage on the SD card. You'll be far better off using a single file that contains analog readings + timestamp information.

I have a problem if i create just one file. Because there is just one SPI link on the board and it use by SD card and W5100. For send the data of Sd card to webserver I must read sd card, copy the data in variable, inactivate the sd card, activate the W5100 and send the data to webserver. The problem is if I have a lots of data in the sd card and I copy all this data in a variable I can glut the RAM.

It is for this reason that i want create many files that contains only one data. I want that each new file take to name the time and date of my counter but it is not to have time, it is for there are not 2 files with the same name.

PS: I'm very sorry for my bad English

You can collect your samples in an array in RAM as much as possible before wrting them to disk. That might reduce the amount of files you need to create.
How much data per second do you generate.

You can collect your samples in an array in RAM as much as possible before wrting them to disk. That might reduce the amount of files you need to create.

Currently I'm reading the pin A0. But after I will have more sensors. While my ethernet connection is not restored, I will store all these values ??in my SD. If the connection is broken for a long time I would have lots of data. So I can not put those values ??into a single file and therefore in a single variable because it would block RAM.

Nothing forces you to read an entire file into RAM. You can still have all your data in a single file. Open that file, read the first line, close it, send out the data, re-open the file, read the second line, close it, send out the data, etc.

The problem with having a separate file for each reading is the SD card uses a FAT file system, which breaks the memory into discrete chunks of data called clusters. These clusters can range in size from a few kilobytes to tens of kilobytes, the larger your SD card, the larger the clusters. Each file is going to take up at least 1 cluster, regardless how much data is contained in the file. Even if you only write a couple bytes to each file, they'll each still consume kilobytes worth of disk space, and that is a huge waste of card space, and if you really are saving away lots of data across lots of files, you may very well run out of space on the disk.

Nothing forces you to read an entire file into RAM. You can still have all your data in a single file. Open that file, read the first line, close it, send out the data, re-open the file, read the second line, close it, send out the data, etc.

thanks jraskell I'm very interresting for your solution ! :smiley:

But how can you choose to read only he first line ?

As an example, you can write each line of data into your file delimited with a CR/LF (carriage return and line feed). Something like:

325
783
442

Or, if you need to include timestamp info, add that in with the appropriate delimiters:

325, 2:40:37
783, 2:40:38
442, 2:40:39

etc.

Then, when it's time to send the data out, you read data into a temporary buffer until you reach the line feed character. When you read a line feed character, you've read in an entire line. At that point, you can use the SD.position() call to store your current location in the file locally, close the file, then send the line you just read in out the Ethernet port.

After that, reopen the file, SD.seek() to the position you previously stored away, and read in the next line.
Rinse and repeat until you reach the end of the file. At that point, you can delete the file so you start fresh the next time you need to store data.

Thank you very much jraskell, I will try this solution !! :smiley:

hi,

For now I made a success to retrieve the first line by detecting the CR/LF (carriage return and line feed) as said jraskell. For the moment I just want retrieve the first line only and print it on the serial monitor

Here is my program:

#include <SPI.h>
#include <SD.h>

int SD_ChipSelect=4;      // Pin qui permet de sélectionner la SD 
int ecriture=0;
int lecture=0;

File myFile;

String a="";

void setup()
{
  Serial.begin(57600);
  pinMode(SD_ChipSelect, OUTPUT);    // Nécessaire pour pouvoir sélectionner la SD
  
  pinMode(53, OUTPUT); // Pin /SS sur la carte Mega2560, elle n'est pas utilisée pour sélectionner la SD ou le W5100 mais il faut la                                       //  mettre en sortie pour que la liaison SPI fonctionne

  digitalWrite(SD_ChipSelect,LOW);       // On sélectionne la SD

  if (!SD.begin(4)) // Si la chip select de la SD n'est pas la pin 4
    {
      Serial.println("initialization SD failed!"); // on affiche ...
      return;                                      // on reste bloqué ici
    }
    Serial.println("initialization SD done.");     // Si la chip select de la SD est la pin 4 on affiche et le setup continue
    
  delay(1000);
}

void loop()
{
  if(ecriture==0)
   {
     ecriture=1;
     myFile = SD.open("fichier1.txt", FILE_WRITE);
         
     if (myFile) 
      {
        Serial.println("fichier1 pret pour l'ecriture");
        myFile.println("bonjour");
        myFile.println("merci");
        myFile.println("aurevoir");
        myFile.close();
      }
   } 

   if(lecture==0)
   {
     lecture=1;
     myFile = SD.open("fichier1.txt");
     
    if (myFile) 
      { 
         Serial.println("fichier1 pret pour la lecture");     
         while(!a.endsWith(1310))    // while CR/LF not detected ...
         {
           a=a+myFile.read();
           Serial.println(a);
         }
         myFile.close();
      }
      
      Serial.println();
      Serial.print(a);
   }

On the serial monitor this appears:

initialization SD done.
fichier1 pret pour l'ecriture
fichier1 pret pour la lecture
98                        // =b
98111                     // =bo
98111110                  // =bon
98111110106               // =bonj
98111110106111            // =bonjo
98111110106111117         // =bonjou
98111110106111117114      // =bonjour
9811111010611111711413    // =bonjour + CR
981111101061111171141310  // =bonjour + CR+LF

981111101061111171141310  // =bonjour + CR+LF

When i display the variable "a" on the serial monitor I have the ASCII code in decimal. But I don't want the code, I want the character for display "bonjour".

I can not solve this problem. Can you help me please !?

Try changing:
a = a + myfile.read();
to
a = a + (char)myfile.read();

I believe the return type of myfile.read() is type int. So the automatic conversion when adding an int to a string is that it converts it to it's numeric string representation. By casting the value to char, you should get the ascii character representation instead.

I used this solution:

a=myFile.read();  
b=b+a;

With a is a char and b a String. It works !

I will now try to read the first line, save my position and read the second line ...