Go Down

Topic: SD card variable file name (Read 9978 times) previous topic - next topic

GTech13

I am creating the charges logger for a vending machine rebuilt to use RFID cards.  I would like so after every time the card is removed and replaced a new file is created with the current date.  I am using the SD library so the statement in question is file = SD.open("filename.txt", FILE_WRITE); I have an RTC which puts each element of time/date into an int so I have an int minute, int hour, and so on.  I am just starting to try to put minute as the file name but I will need to put pieces together to achieve a full date.  Also, I would need to be able to append a number if there is a duplicate file name as I probably will not use seconds in the file name (or maybe I could).  And finally, I don't how to get the .txt in as I don't want my client to have to modify the file name.  (The txt file gets pasted into excel for processing.)

I know I need some sort of character array but I can't get anything to work.  I am thinking "String - object" may be the right path but that's not working for me either.

Help is always greatly appreciated,

GTech

frank26080115

You said character array, so what went wrong when you tried that?

you might need to understand <string.h> a bit more, read this all the way through http://www.nongnu.org/avr-libc/user-manual/group__avr__string.html
Freelance engineer, consultant, contractor. Graduated from UW in 2013.

PaulS

Quote
I know I need some sort of character array but I can't get anything to work.

Posting what you have tried is always better than hand-waving.

Quote
I am thinking "String - object" may be the right path but that's not working for me either.

Don't think that String, which is just a wrapper around char arrays, is going to solve all of your problems.

There are a number of ways to convert int values to strings (lower case s - NULL terminated char array). The itoa() function can do it, with no formatting, one at a time. The sprintf() function can do it, with formatting, and converting multiple values at one time.

There is a tradeoff with using sprintf() which is that your code size goes up.

Don't forget the restrictions on the length of the name (8 characters). Don't forget the extension for the name.

s3jn

Um, WHILE we're on the subject of filenames on SD cards...  :\

My intent is to log sporadic events to an SD card, tagging them with the time.  I'd like to save a file-a-day and create the new file at midnight.  Embedding the ten bold lines below gives me a workable filename which changes correctly at midnight and the code recognizes it and everything's fine.

BUT, when I try to make a function out of it (I'll need to check for the date rollover several times in my code) I can't get the function to return anything when I do a "if(! SD.exists(filename)) {...}" later on.  (the error says "invalid conversion from 'char*' to 'char'" and highlights "return filename;" in the function)

Up until now I've pretty much banged everything out inside the regular setup and loop functions and this is my first real experience with making my own function and returning variables...  Everybody's comments & criticisms are appreciated.

  char getFilename() {
    DateTime now = RTC.now(); int year = now.year(); int month = now.month(); int day = now.day();
    char filename[] = "00000000.CSV";
    filename[0] = '2';
    filename[1] = '0';
    filename[2] = year%10 + '0';
    filename[3] = year%10 + '0';
    filename[4] = month/10 + '0';
    filename[5] = month%10 + '0';
    filename[6] = day/10 + '0';
    filename[7] = day%10 + '0';

    return filename;
  }

PaulS

Code: [Select]
  char getFilename() {
This says that the function is to return a character, which is not the same as an array of characters.

So, hey, look at that. The compiler was right. Well, we knew it was.

Now, before you think that you can actually return filename from that function, forget it. filename is a local variable which goes out of scope when the function ends. If you return a pointer to that memory, it will be garbage as soon as the memory is reused.

What you need to do is pass to the function the array that you want modified. Since arrays act a lot like pointers, define the function as taking a pointer to a char.
Code: [Select]
void getFilename(char *fileName)
{
}

and call it like so:
Code: [Select]
char myFileName[16];
getFilename(myFileName);


Let's see if you can figure out how to write to the argument in the function then. If not, come on back. We'll be here all week.

jcarrr

#5
Sep 30, 2011, 05:13 pm Last Edit: Sep 30, 2011, 07:13 pm by jcarrr Reason: 1
GTech13,

here is a snippet that might help:
Code: [Select]

      char file_name[] = "xxxxxxxx.CSV";    // prototype file name
      char extension[] = "CSV";  // sometimes the extension gets modified

     sprintf(file_name,"%c%c%cCALIB.%s",EEPROM.read(fpf0),EEPROM.read(fpf1),
     EEPROM.read(fpf2),extension);

     /*  open the CAL file on the SD.  New file each time
      The serial number, customer number, file number are on the processor EEPROM
      */

     if (file.open(&root, file_name, O_CREAT | O_APPEND | O_WRITE))
     {
       PgmPrintln("calibration file opened");
       Serial.println(file_name);
     }


 Use your date strings instead of the EE reads in sprintf().  I increment the EE bytes and save them again so each file name is a sequential number.  Should work with date strings.  Are you setting the file creation dates and the file modification dates? They are useful and facilitate sorting file names when looking at the directory.

JC

s3jn

I got it working - and can see where I was off and that I need to understand pointers better.  I know I probably shouldn't use "filename" inside-of and outside-of the function, but it compiles and the SD card functions accept it with no problems, so: <shrug>.

Thank you Paul.
Ex uno disce omnes
___
in setup:
char filename[] = "00000000.CSV";

function call:
getFilename(filename);

function:
  void getFilename(char *filename) {
    DateTime now = RTC.now(); int year = now.year(); int month = now.month(); int day = now.day();
    filename[0] = '2';
    filename[1] = '0';
    filename[2] = year%10 + '0';
    filename[3] = year%10 + '0';
    filename[4] = month/10 + '0';
    filename[5] = month%10 + '0';
    filename[6] = day/10 + '0';
    filename[7] = day%10 + '0';
    filename[8] = '.';
    filename[9] = 'C';
    filename[10] = 'S';
    filename[11] = 'V';
    return;
  }

mmcp42


I got it working - and can see where I was off and that I need to understand pointers better.  I know I probably shouldn't use "filename" inside-of and outside-of the function, but it compiles and the SD card functions accept it with no problems, so: <shrug>.

Thank you Paul.
Ex uno disce omnes
___
in setup:
char filename[] = "00000000.CSV";

function call:
getFilename(filename);

function:
  void getFilename(char *filename) {
    DateTime now = RTC.now(); int year = now.year(); int month = now.month(); int day = now.day();
    filename[0] = '2';
    filename[1] = '0';
    filename[2] = year%10 + '0';
    filename[3] = year%10 + '0';
    filename[4] = month/10 + '0';
    filename[5] = month%10 + '0';
    filename[6] = day/10 + '0';
    filename[7] = day%10 + '0';
    filename[8] = '.';
    filename[9] = 'C';
    filename[10] = 'S';
    filename[11] = 'V';
    return;
  }



suggest
filename[2] = year/10 + '0';
there are only 10 types of people
them that understands binary
and them that doesn't

Sergegsx

#8
Oct 03, 2011, 12:58 pm Last Edit: Oct 03, 2011, 01:04 pm by Sergegsx Reason: 1
very usefull code thanks a lot !!

as for the year, if i use the /10 suggested i get this.
Code: [Select]

   filename[0] = year/10 + '0';
   filename[1] = year%10 + '0';


Code: [Select]
รน1

I am using this as I assume no registries before year 2000 and this way if arduino reboots cause of a file corruption, it will create a new file with a different HOUR.

Code: [Select]

void getFilename(char *filename) {
   DateTime now = RTC.now(); int year = now.year(); int month = now.month(); int day = now.day(); int hour = now.hour();

   filename[0] = '1';  //year/10 + '0';        // im getting error here so for now force it to 1.
   filename[1] = year%10 + '0';
   filename[2] = month/10 + '0';
   filename[3] = month%10 + '0';
   filename[4] = day/10 + '0';
   filename[5] = day%10 + '0';
   filename[6] = hour/10 + '0';
   filename[7] = hour%10 + '0';
   filename[8] = '.';
   filename[9] = 'C';
   filename[10] = 'S';
   filename[11] = 'V';
   return;
 }


ive printed year and contains "2011", so i thing thats the problem.

I have done this and works
Code: [Select]

    filename[0] = (year-2000)/10 + '0';
    filename[1] = year%10 + '0';

I guess its good enough for me that it works till 2099 hehe

mmcp42

yup
(year-2000)/10 is even better
i suspect the original worked as it's 2011!!!
there are only 10 types of people
them that understands binary
and them that doesn't

Iexpress

You'd think the IDE would allow using String variables to use for the FileName.
Hopefully they can implement this.

Using Char Arrays is not dynamic enough because when we use char array for the filename, some of the chars might be null or invalid data.

It would make sense to use String variables for a String based filename, but they haven't implemented this yet.

My code for creating a filename is here, though it is not the method I wanted to use because of waisted memory space so I'll be entering the filename into an array coming over Serial.:


#include <SD.h>

char Data = '0';

byte FileNumber = 0;

char CommandChar = '0';

char FileName[] = "xxxxxxxx.xxx";

byte CharPosition = 0;
byte Counter = 0;

File MyFile;

// the setup routine runs once when you press reset:
void setup() {               
  // initialize the digital pin as an output.
  pinMode(10, OUTPUT);  //cs

 
  Serial.begin(9600);
  SD.begin(10);
}

// the loop routine runs over and over again forever:
void loop() {

  while (1){
    while (Serial.available() == 0){
    }
   if (Serial.available() > 0){
     CommandChar = Serial.read();
   }
   
   switch(CommandChar){
   
     //1 open file for read
     //2 open file for write
     //3 close file
     //4 set filename
     //5 delete FileName
     //6 write char to file
     //7 read char from file
     //8 send char to PC     
     //A Get Char from Serial
     
     case '0':
        //No Operation
        CommandChar= '0';
     break;
     
     case '1':
       //Open File For Read Access
       MyFile = SD.open(FileName, FILE_READ);
       
       CommandChar= '0';
       
     break;
     
     case '2':
       //Open FIle for Write Access
       MyFile = SD.open(FileName, FILE_WRITE);
       
       CommandChar= '0';
       
       break;
     
     case '3':
       //Close FileName
      MyFile.close();

      break;
     
     case '4':
     //Set FileName 8 chars + "." + 3 more extension chars
       
       
     for (Counter = 0; Counter < 13; Counter++){ 
       while (Serial.available() == 0){
       }
       if (Serial.available() > 0){
             Data = Serial.read();
             FileName[CharPosition] += Data;
             CharPosition++;
       }       
     }     
     CharPosition = 0;
     Counter = 0;     
     CommandChar = '0';

     break;
     
     case '5':
       //Remove Filename from sd card
       SD.remove(FileName);
       
       CommandChar = '0';
       
     break;
     
     case '6':
       //Write Char to File
       
       MyFile.write(Data);
       
       CommandChar = '0';
     break;
     
     case '7':
       //Read Char from File
       
       Data = MyFile.read();

       CommandChar= '0';
     break;
     
     case '8':
       //Send Char to PC
       
       Serial.println(Data);
       
       CommandChar= '0';
     break;

     case '9':
     // Load Char to Data
     while (Serial.available() == 0){
         }
           if (Serial.available() > 0){
              Data = Serial.read();
           }
      CommandChar = '0';
     break;
     
     

     
     }
     
  }
 
}


fat16lib

#11
Jan 15, 2015, 06:13 pm Last Edit: Jan 15, 2015, 06:57 pm by fat16lib
Quote
Using Char Arrays is not dynamic enough because when we use char array for the filename, some of the chars might be null or invalid data.
This is not true.  The data in String is just a char array.

Code: [Select]
char *buffer;        // the actual char array

Quote
I wanted to use because of waisted memory space so I'll be entering the filename into an array coming over Serial.:
This is also not true.  String uses realloc so when you add a character at a time to a string it can use several times as much as needed, depending on the state of the heap.

You can easily use String to open a file.

Code: [Select]

void setup() {
  String filename = "test.txt";
  file = SD.open(filename.c_str(), FILE_WRITE);
}


Iexpress

I was building filenames dynamically with:

 //Set FileName 8 chars + "." + 3 more extension chars
     while (Serial.available() == 0){
     }
       if (Serial.available() > 0){
            Char Data = Serial.read();
            temp += Data; String temp = temp+Data
         
     }   
     

//Then after building the filename, I did this:


temp.toCharArray(FileName, temp.length());
       MyFile = SD.open(FileName, FILE_WRITE);
       

Then writing data, I did this:

       
       MyFile.print(Data);
       



but I think this would work as well:

       
       MyFile.print(Serial.read());
       


The strange thing is, the filesize changes as shown on the PC and through getting SD FileInfo with the Arduino. Maybe Windows puts some filesystem info in the files on the Windows side.

I was trying to find a way to do file transfer but had to do this in binary.

I suppose doing this with Byte would work the same as with Char.
Trying to play wav files written with Chars I'm having trouble with.

On the PC side, I send Byte or chr(byte) to send an ASCII char
On the Arduino Side, I was testing between reading Byte and Char from serial.read.

Which one would I use for binary data transfer, and should I be using 512 byte array instead of writing each individual bytes/chars to the new files

Thanks?

fat16lib

#13
Jan 17, 2015, 11:28 pm Last Edit: Jan 17, 2015, 11:30 pm by fat16lib
Your program will fail if the file name is too long in this statement since memory will be overwritten.
Code: [Select]

  temp.toCharArray(FileName, temp.length());


If Data is type char, this will write the byte to the file.
Code: [Select]
MyFile.print(Data);

This will format the int value returned by read() as a decimal string and write the characters to the file.
Code: [Select]
MyFile.print(Serial.read());

What OS are you using on the PC?  Are you sure the file is opened in binary mode on the PC?  If the transfer is binary, the files will be the same size.

Go Up