passing parameters to SD.open problem

Hi,

I’m working on a project using the caralex SD breakout board to expand the arduinos memory. I am aware the arduino is low on memory and RAM so this seems like a good solution. I’ve wired and ran tests on the SD card breakout board and I know its working fine.

The project requires the arduino to extract strings from the SD card and display them. At the moment printing to serial would be a good start.

In order to keep the work done by the Arduino minimal I am storing each string as a separate .txt file on the SD card. The files are named by numbers 10 through to 40. I have done this so that when I’m programming I don’t have to contend with variable lengths of file name (e.g. "1.txt" is one character shorter than "10.txt"). This is to minimise on the work arduino is doing and keep the code straight forward.

The SD library requires the parameter for the function SD.open() to be passed in as a constant char*. so it would be SD.open(myChar).

As my files are named after numbers the strategy I was seeking to employ was to:

  1. create a random number within the scope of the file names stored on the SD card.
  2. create a new String which converts this int to a String, concatenating the file extension ".txt" onto the end.
  3. convert this String to a const Char and pass it as a parameter to the SD.open()

This is illustrated in the code below, which returns errors. I’ve tried several ways around but I can’t seem to make it work. The initialisation of the SD card is done in void setup(), so I have not posted this in the code below, only the code I'm having challanges with in void loop(). The line that causes the compiler to give an error message is:

char* fileName[] = {n.toCharArray(charBuff, 6)};

The error message is: "void value not ignored as it should be". I'm having no success with solving this with google. Can someone explain to me where I’m going wrong with this and give me an example of how to make it work?

void loop()
{
  ran = random(10, 41);
  
  String n = String(ran)+".txt";
  
  char* fileName[] = {n.toCharArray(charBuff, 6)};



  myFile = SD.open(fileName);

  if(myFile)
  {
    Serial.print("myFile has openned ok");
    myFile.close();
    Serial.println("file closed");
  }
  else
  {
    Serial.print("error openning file");
  }
  delay(1000); 


}

Thanks for taking the time to read.

First of all toCharArray doesn't return anything. It copies the string's characters to the supplied buffer.

You can do it this way:

String myFile = "40.txt";
char buff[7];
myFile.toCharArray(buff, 7);
char *p = buff;
GetSDFileData(p);

but you don't need to explicitly create the pointer, just use the array name:

String myFile = "40.txt";
char buff[7];
myFile.toCharArray(buff, 7);
GetSDFileData(buff);

"In order to keep the work done by the Arduino minimal I am storing each string as a separate .txt file on the SD card."

I don't think storing all your strings in separate files will reduce the work done.

If your strings are all separated by a new line in one file, then you could simply pass the name of the file to a function and get your strings like this:

Sample File Data:
string1
string2
string3

void GetSDFileData(char* fileName)
{
String s = "";
if (SD.exists(fileName) == false)
{
Serial.println(F("File Not Found!"));
return;
}
File myFile = SD.open(fileName, FILE_READ);
if (!myFile)
{
Serial.println(F("Open File Failed!"));
return;
}
myFile.seek(0);
while (myFile.available())
{
s = myFile.readStringUntil('\n');
Serial.println(s);
}
myFile.close();
}

String myFile = "40.txt";
char buff[7];
myFile.toCharArray(buff, 7);
char *p = buff;

p and buff are both "character" pointers.

To assign the address of an array to a pointer, you don't need to use the address-of (&) operator because the name of an array behaves like the address of that array in this context. Hence you also don't need to use an ampersand when you pass a string variable to a function like scanf():

int num;
char ch[10];
scanf("%d%s", &num, ch);

Serial.println(p[1]); this would print the second character of the file name.

Thanks for your Reply b4ip,

After posting and more googling I realised that toCharArray must be the thing returning void. I think what confused me was that instead of passing the value to a new variable on the otherside of an equal sign, the value is passed as you say to one of the parameters inside the brackets (the buffer). when I got this I eventually got it working like this:

void loop()
{
  ran = random(10, 40);

  String n = String(ran)+".txt";
  char fileName[sizeof(n)];
  n.toCharArray(fileName, sizeof(fileName));

  myFile = SD.open(fileName);

  if(myFile)
  {
    Serial.println("myFile has openned ok");

    while(myFile.available())
    {
      Serial.write(myFile.read());
    }

    myFile.close();
    Serial.println();
    Serial.println("file closed");
  }
  else
  {
    Serial.println("error openning file");
    return;
  }
  delay(1000);  
}

The question of the memory overhead in Arduino is interesting. Everytime I do an Arduino project, I always wish it had more of it. If I was writing for another platform I'd have my Strings in a collections class and then iterate through them to get the ones I want. The idea of loading a ton of strings into Arduinos memory in one go makes me think it might cause the platform to crash. I haven't tried it, so I could be very wrong, but from reading around the difficulties others seem to have had working with Strings in Arduino memory I decided it was best to only load one in and out at a time. Hence sorting them myself before storing them on the SD card.

I've been programming for years in C# on a full-blown PC so I rarely have to worry about RAM, but in the Arduino world resources are tiny.

Even the Arduino Mega has very limited memory: about 240KB will be left for your code and just 8KB of SRAM!

If the data you want to work with is not going to change during the course of your sketch, you can use PROGMEM to put the variables in the flash memory instead of SRAM, but this has some issues.

I've started working with a NetDuino 3 which has much more memory and most importantly I can program it from a proper object-oriented language like C#, and using a powerful IDE. The Arduino IDE is fine for small projects, but not very suitable for large scale.

You can also program the Arduino from C# using Arduino IDE for Visual Studio.

That's good to know! I've never had a go at C# as most of my programming experience is Mac based in Objective-C and more recently Swift. The Netduino sounds good. I was also looking at the intel Edison ones. For this project an uno is fine but I'd definitely like something with a bit more of an overhead for future experiments.

Thanks for your help!

There is NO reason to use a String at all. Therefore, the issues with toCharArray() go away.

char fileName[13]; // Room for an 8.3 name with a NULL terminator

int randomNumber = random(lowerLimit, upperLimit);

sprintf(fileName, "%2d.txt", randomNumber);

If upperLimit is 40 and lowerLimit is 1, this will result in names like 09.txt, 35.txt, etc, without pissing away resources on the String class.

Thanks Paul,

I'm interested to know exactly how much memory resources I'm 'pissing' away with the solution I shared and how much I'm saving with yours? I think others who have similar challenges in the future who visit this forum might find such an explanation useful too.

I'm interested to know exactly how much memory resources I'm 'pissing' away with the solution I shared and how much I'm saving with yours?

Feel free to find out.

http://playground.arduino.cc/Code/AvailableMemory

Call the method before and after populating the char away each way. The IDE tells you how much Flash space you use.