help with super simple? SD issue

boxxofrobots

Well-known member
Trying to use the SD examples, trying to learn to use SD. I have a 32GB card, had files on it, can access it with the examples as shown. However, something simple like this won't work and I don't know why

String foo = "datalog.txt";
File dataFile = SD.open(foo);

results with

no matching function for call to 'SDClass::eek:pen(String&)'

I don't get it. Please help?
 
SD.open isn't defined for String arguments. Use a C character string:
Code:
const char  *foo = "datalog.txt";
File dataFile = SD.open(foo);

Pete
 
Oooh, that just leaves me with the problem of converting a string to a c array? It jsut moves the compiler error.

ex:

String foo = "datalog.txt";
char *bar = foo
File dataFile = SD.open(bar);

cannot convert 'String' to 'char*' in initialization

Does there exist a tutorial on SD card handling (or a sketch that uses it) that I might refer to? ALL the examples use explicit filename declarations. I need to be able to iterate over files and to send a folder name to the SD handler.

foldername = somevariablestring;
filename = someotherstring;
path=foldername + "/" + filename;
SD.open(path);
etc.

This is all so easy in python...
 
Maybe if I show someone what I'm trying to do? I know this seems like it should be so simple, but I'm relearning c.

Code:
void sceneSave(){
  String _projectname = ""; //projectname is in an array of 16 midi registers
  for(int x = 0; x <=15; x++) {
    if (alphas[midivals[x+PROJECT_NAME] % 64] == " "){
      x=15; 
    } else {
    _projectname += alphas[midivals[x+PROJECT_NAME] % 64];
    }
  }//we now have project name
  String myvalues = "";
  for(int y = 0; y <=2047; y++) { 
    myvalues += String(midivals[y]);
    myvalues += ",";
  }//we now have dump of midi settings
  
  if (!SD.begin(SDchipSelect)) {
    Serial.println("initialization failed!");
    return;
  }
  char *_filepath = _projectname;
  File project = SD.open(_filepath);
  if (!project.isDirectory()) {
    SD.mkdir(_projectname);
  }
  String thisSceneNum = String(getLastScene(_projectname)+1);
  
  }
}
 
Use c_str(), like this:

Code:
#include <SD.h>

void setup() {
  if (SD.begin(10)) {
    String foo = "datalog.txt";
    File dataFile = SD.open(foo.c_str());
  }
}

void loop() {
}
 
Thanks heaps. I've made it work

I really appreciate your help. this thing is coming together and it's simple stuff like this that's getting in the way for me.

Edit: I'm allowing for 16 character file pathnames. I'm guessing that's limited to 8, so I limited it all to 8

Code:
void sceneSave(){
  String _projectname = ""; //projectname is in an array of 16 midi registers
  for(int x = 0; x <=15; x++) {
    if (alphas[midivals[x+PROJECT_NAME] % 64] == " "){
      x=15; 
    } else {
    _projectname += alphas[midivals[x+PROJECT_NAME] % 64];
    }
  }//we now have project name
  String myvalues = "";
  for(int y = 0; y <=2047; y++) { 
    myvalues += String(midivals[y]);
    myvalues += ",";
  }//we now have dump of midi settings
  
  if (!SD.begin(SDchipSelect)) {
    Serial.println("initialization failed!");
    return;
  }
  File project = SD.open(_projectname.c_str());
  if (!project.isDirectory()) {
    SD.mkdir(_projectname.c_str());
  }
  //int foo = getLastScene(_projectname);
  String thisSceneNum = String(getLastScene(_projectname)+1);
  thisSceneNum = zeroPad(8 - thisSceneNum.length()) + thisSceneNum;
  String thisScenePath = "/" + _projectname + "/" + thisSceneNum;
  File thisScene = SD.open(_projectname.c_str(),FILE_WRITE);
  if (thisScene){
    thisScene.println(myvalues.c_str());
    thisScene.close();
  } else {Serial.println("Error opening file " + thisScenePath);}
  project.close();
  Serial.println(thisScenePath);
}
 
Last edited:
Next foil I've encountered: How do I actually get sdobject.name() into a string? I can print it directly, but assiging it to a string always returns the same integer.

Code:
void printDirectory(File dir, int numSpaces) {
   while(true) {
     File entry = dir.openNextFile();
     if (! entry) {
       //Serial.println("** no more files **");
       break;
     }
     printSpaces(numSpaces);
     Serial.print(entry.name());                                                 //this works just fine as in the example
     String scenename = String(entry.name());                       //this returns 538987561
     if (entry.isDirectory()) {
       Serial.println("/");
       printDirectory(entry, numSpaces+2);
     } else {
      if (entry.name()[1] == '0'){
        int scenenum = int(scenename.c_str()) + 1;
        Serial.println(String(scenenum));                                  //this prints 00000001538987561, which should be 0000000100000002
      }
       Serial.println();
     }
     entry.close();
   }
}

Help is much appreciated. This SD stuff isn't as easy as it looks in the examples.
 
This is also out of the examples. The change I made was trying to assign the input from the file translate into output. This doesn't work and also returns long strings of numbers, which I am guessing are processids or something?

Code:
/*

  SD card read/write
 
 This example shows how to read and write data to and from an SD card file 	
 The circuit:
 * SD card attached to SPI bus as follows:
 ** MOSI - pin 11, pin 7 on Teensy with audio board
 ** MISO - pin 12
 ** CLK - pin 13, pin 14 on Teensy with audio board
 ** CS - pin 4, pin 10 on Teensy with audio board
 
 created   Nov 2010
 by David A. Mellis
 modified 9 Apr 2012
 by Tom Igoe
 
 This example code is in the public domain.
 	 
 */
 
#include <SD.h>
#include <SPI.h>

File myFile;

// change this to match your SD shield or module;
// Arduino Ethernet shield: pin 4
// Adafruit SD shields and modules: pin 10
// Sparkfun SD shield: pin 8
// Teensy audio board: pin 10
// Teensy 3.5 & 3.6 & 4.1 on-board: BUILTIN_SDCARD
// Wiz820+SD board: pin 4
// Teensy 2.0: pin 0
// Teensy++ 2.0: pin 20
const int chipSelect = BUILTIN_SDCARD;

void setup()
{
 //UNCOMMENT THESE TWO LINES FOR TEENSY AUDIO BOARD:
 //SPI.setMOSI(7);  // Audio shield has MOSI on pin 7
 //SPI.setSCK(14);  // Audio shield has SCK on pin 14
  
 // Open serial communications and wait for port to open:
  Serial.begin(9600);
   while (!Serial) {
    ; // wait for serial port to connect.
  }


  Serial.print("Initializing SD card...");

  if (!SD.begin(chipSelect)) {
    Serial.println("initialization failed!");
    return;
  }
  Serial.println("initialization done.");
  
  // open the file. 
  myFile = SD.open("test.txt", FILE_WRITE);
  
  // if the file opened okay, write to it:
  if (myFile) {
    Serial.print("Writing to test.txt...");
    myFile.println("testing 1, 2, 3.");
	// close the file:
    myFile.close();
    Serial.println("done.");
  } else {
    // if the file didn't open, print an error:
    Serial.println("error opening test.txt");
  }
  
  // re-open the file for reading:
  myFile = SD.open("test.txt");
  if (myFile) {
    Serial.println("test.txt:");
    String myoutput = "";
    // read from the file until there's nothing else in it:
    while (myFile.available()) {
      myoutput += myFile.read();
    }
      Serial.write(myoutput.c_str());
    // close the file:
    myFile.close();
  } else {
  	// if the file didn't open, print an error:
    Serial.println("error opening test.txt");
  }
}

void loop()
{
	// nothing happens after setup
}



Initializing SD card...initialization done.
Writing to test.txt...done.
test.txt:
1161011151161051101033249443250443251461310116101115116105110103324944325044325146131011610111511610511010332494432504432514613101161011151161051101033249443250443251461310
 
Edit: I'm allowing for 16 character file pathnames. I'm guessing that's limited to 8, so I limited it all to 8

Maybe you have an old version of the software? In Arduino, click Help > About to check.

Teensyduino 1.53 and earlier used the old Arduino SD library which was limited to 8.3 filenames. Newer versions support long filenames. Latest version is 1.56.
 
Here's an example of a csv i/o that's almost working....

Code:
// Function to read a text file one field at a time.
//
#include <SPI.h>
#include <SD.h>
#define CS_PIN BUILTIN_SDCARD

File file;

/*
 * Read a file one field at a time.
 *
 * file - File to read.
 *
 * str - Character array for the field.
 *
 * size - Size of str array.
 *
 * delim - String containing field delimiters.
 *
 * return - length of field including terminating delimiter.
 *
 * Note, the last character of str will not be a delimiter if
 * a read error occurs, the field is too long, or the file
 * does not end with a delimiter.  Consider this an error
 * if not at end-of-file.
 *
 */

 
size_t readField(File* file, char* str, size_t size, char* delim) {
  char ch;
  size_t n = 0;
  while ((n + 1) < size && file->read(&ch, 1) == 1) {
    // Delete CR.
    if (ch == '\r') {
      continue;
    }
    str[n++] = ch;
    if (strchr(delim, ch)) {
        break;
    }
  }
  str[n] = '\0';
  return n;
}
//------------------------------------------------------------------------------
#define errorHalt(msg) {Serial.println(F(msg)); while(1);}
//------------------------------------------------------------------------------
void setup() {
  Serial.begin(9600);

  // Initialize the SD.
  if (!SD.begin(CS_PIN)) errorHalt("begin failed");

  // Create or open the file.
  file = SD.open("/00000002", FILE_WRITE);
  if (!file) errorHalt("open failed");

  // Rewind file so test data is not appended.
  file.seek(0);

  // Write test data.
  file.print(F(
    "1,2,3\r\n"
    "4,5,6\r\n"
    "7,8\r\n"           // missing a field
    "9,10,11\r\n"
    "12,13,14"     // no delimiter
    ));

  // Rewind the file for read.
  file.seek(0);

  size_t n;      // Length of returned field with delimiter.
  char str[20];  // Must hold longest field with delimiter and zero byte.
  int _value = 0;
  // Read the file and print fields.
  while (true) {
    n = readField(&file, str, sizeof(str), ",\n");

    // done if Error or at EOF.
    if (n == 0) break;

    // Remove the delimiter.
    str[n-1] = 0;
    _value = int(str);                       //this should match the other
    // Print the field.
    Serial.println(str);                     //it doesn't
    Serial.println(_value);
  }
  file.close();
}
//------------------------------------------------------------------------------
void loop() {
}

The out is:

1
537329552
2
537329552
3
537329552
4
537329552
5
537329552
6
537329552
7
537329552
8
537329552
9
537329552
10
537329552
11
537329552
12
537329552
13
537329552
14
537329552
 
The int() function does not convert a string to an integer. It converts from the datatype in the parentheses to an int. In your code, str is a pointer to (i.e. the address of) a char array. The int() converts the address of the string (essentially &str[0]) to an integer. That is why the large number printed is always 537329552, because the address of the str array doesn't change.
When you want to convert an ASCII string to an integer, use atoi().

Pete
 
Thanks so much, that does it! Now I need to better understand what to do with the string pointer to convert it to String. c_str doesn't seem to work.
 
The assignment operator appears to be overloaded such that if the right hand side is a C string and the left hand side is a String, the compiler will generate the code required to perform what is intended. For example, this code worked for me:
Code:
  char *str = "Hello There";
  String fred;
  // assign the C string to a String
  fred = str;
  Serial.println(fred);

Pete
 
Back
Top