SD.open(myString.c_str()) once worked but suddenly stopped working

Status
Not open for further replies.

AdmiralCrunch

Well-known member
hi

on my SDCard (T_3.6) I have the following structure:

| - startCfg.txt
| configs \ default.txt
| configs \ myConf.txt
| pattern \ default.txt
| pattern \ myPattern.txt

I read a main-configfile startCfg.txt to know which further config-files I need ro load.

Everything worked well until I added the pattern-folder & files to the SD-Card .. oO .. I really did NO change to the code.. anyway, now the SD.open(fileToLoad.c_str()) does not work. When I write the path and file directly in SD.open() it works..

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

#include <string.h>
#include <ctype.h>
#include <cfg.h>

bool debug = true;

/* #### READ SD #### */
File root;
File startCfg;
File loadedCfg;
File loadedPattern;
const int chipSelect = BUILTIN_SDCARD;
String cfgName;
String cfgValue;
String helpVarString;

void setup() {
   // SERIAL PORT   
  Serial.begin(9600);
  while ( !Serial && (millis() < 4000) ) {
    // ..
  } 
  
  // INIT SD & LOAD CONFIG 
 Serial.println(".................. LOADING SD CONFIG  ");     
   
  if (!SD.begin(chipSelect)) {
    if(debug == true) {
      Serial.println("SD initialization failed!");
    }    
    return;
  }


  // READ INIT-CONFIG & LOAD INIT-MAPPING
  startCfg = SD.open("startCfg.txt");
  if (startCfg) { 
    Serial.println(".................. LOADING startCfg  "); 
    while (startCfg.available()) {
      helpVarString = startCfg.readStringUntil('\n');     
      cfgName = getValue(helpVarString, ':', 0);
      cfgValue = getValue(helpVarString, ':', 1); 
      
      if(cfgName == "MappingPreset") {
        // load cfg file
        loadCfgFile(cfgValue);
      } 
      
    }
    startCfg.close();
  } else {
    if(debug == true) {
      Serial.println("error opening startCfg.txt");
    }    
  }

}

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

}



void loadCfgFile(String cfgFile) {  
  String fileToLoad = "/configs/" + cfgFile + ".txt";
  int trackCount = 0;
  
  loadedCfg = SD.open(fileToLoad.c_str());
  
  if (loadedCfg) {    
    while (loadedCfg.available()) {
      helpVarString = loadedCfg.readStringUntil('\n');      
      cfgName = getValue(helpVarString, ':', 0);
      cfgValue = getValue(helpVarString, ':', 1);
      
      if(cfgName == "END") {
        trackCount++;        
      }
        
      int b = atoi(cfgValue.c_str()); 
      //trackMidiMapping[trackCount].setTrackCtrl(cfgName, b, trackCount);
      Serial.println(cfgName);
    }
    loadedCfg.close();
  } else {
    Serial.print("error opening "); Serial.print(cfgFile); Serial.println(".txt");
  }  
}


String getValue(String data, char separator, int index) {
  int found = 0;
  int strIndex[] = { 0, -1 };
  int maxIndex = data.length() - 1;

  for (int i = 0; i <= maxIndex && found <= index; i++) {
    if (data.charAt(i) == separator || i == maxIndex) {
      found++;
      strIndex[0] = strIndex[1] + 1;
      strIndex[1] = (i == maxIndex) ? i + 1 : i;
    }
  }
  return found > index ? data.substring(strIndex[0], strIndex[1]) : "";
}
 
One would need to know what's in startCfg.txt ...
if cfgValue is not terminated with a ":", then the readStringUntil('\n') might be picking up a '\r'
 
If it were me, probably the first thing I would do would be to print out what the myString.c_str()) actually produces...

I would probably first try simply print it as a string. But if still not obvious would probably print out all of the bytes in HEX...
 
One would need to know what's in startCfg.txt ...
if cfgValue is not terminated with a ":", then the readStringUntil('\n') might be picking up a '\r'

hi
so the content of startCfg.txt is:
Code:
MappingPreset:Default
Pattern:Default


If it were me, probably the first thing I would do would be to print out what the myString.c_str()) actually produces...

I would probably first try simply print it as a string. But if still not obvious would probably print out all of the bytes in HEX...
the fileToLoad.c_str() prints me "Default.txt"
 
the fileToLoad.c_str() prints me "Default.txt"
Where did the "'s come from? Did that actually print out with the print, or just your typing it in? or...

Again probably the same reason as manitou mentioned, the string might contain the \r character in it, which if you then simply did something like:
Code:
Serial.println(fileToLoad.c_str());

Would simply also print out the CR without you really noticing the data...
So if I were doing simple prints without printing complete string in HEX, I would maybe also do things like:
Code:
Serial.println(strlen(fileToLoad.c_str()));

Or I might do something like:
Code:
Serial.printf("'%s'\n", fileToLoad.c_str());

And of course that assumes that "Default.txt" is valid? What happend to the other part of the string?
 
Where did the "'s come from? Did that actually print out with the print, or just your typing it in? or...
oh I just typed it in here.., these are not there in the output.

Again probably the same reason as manitou mentioned, the string might contain the \r character in it, which if you then simply did something like:
Code:
Serial.println(fileToLoad.c_str());

Would simply also print out the CR without you really noticing the data...
So if I were doing simple prints without printing complete string in HEX, I would maybe also do things like:
Code:
Serial.println(strlen(fileToLoad.c_str()));

Or I might do something like:
Code:
Serial.printf("'%s'\n", fileToLoad.c_str());

I have deleted the file and written everything again from scratch, no whitespaces or nothing.. simply:
Code:
MappingPreset:Default
Pattern:Default

which tells me, that I need to load
/configs/Default.txt


but I will try strlen() out :)

And of course that assumes that "Default.txt" is valid?

yes Default.txt is valid.. I can write the path in the function like SD.open("/configs/Default.txt"); and it works.

What happend to the other part of the string?

what part do you mean?
Code:
helpVarString = startCfg.readStringUntil('\n');     
cfgName = getValue(helpVarString, ':', 0); // gets me the string before ":"
cfgValue = getValue(helpVarString, ':', 1); // gets me the string after ":"
 
what part do you mean?
Code:
helpVarString = startCfg.readStringUntil('\n');     
cfgName = getValue(helpVarString, ':', 0); // gets me the string before ":"
cfgValue = getValue(helpVarString, ':', 1); // gets me the string after ":"

You say that this works: SD.open("/configs/Default.txt");
But that this: SD.open(fileToLoad.c_str());

Does not work, and if you say Serial.println(fileToLoad.c_str())) prints only: Default.txt
What happened to the /configs/ part of the string?

I am assuming it is being generated by: String fileToLoad = "/configs/" + cfgFile + ".txt";
 
Perhaps you're getting extra whitespace in the string, like a \r before the \n?

Maybe try adding something like:

Serial.println(strlen(fileToLoad.c_str()));
 
you say Serial.println(fileToLoad.c_str())) prints only: Default.txt
What happened to the /configs/ part of the string?

That it only prints "Default.txt" probably confirms that '\r' is embedded in cfgFile string as suggested in post #2. the '\r' causes monitor to start the output at the left, wiping out /configs/

as others have suggested, you could print out cfgValue in HEX to see "all" the characters in the string

possible fix: change to readStringUntil('\r'), or add ":" to end of each line in startCfg.txt

Windows editors terminate lines with CR LF, UNIX editors just LF (\n)

EDIT: adding code to print cfgValue in HEX
Code:
...
      cfgValue = getValue(helpVarString, ':', 1); 
      char str[32];
      for(int i=0;i<cfgValue.length();i++) {
        sprintf(str,"%02x ",cfgValue[i]);
        Serial.print(str);
      }
...

and it prints 44 65 66 61 75 6c 74 0d
notice the 0x0D at the end == '\r'
 
Last edited:
Interestingly you had the same bug in https://forum.pjrc.com/threads/52969-String-Comparsion-sometimes-works-and-sometimes-not
:confused:
bug.gif
asked and answered
 
Last edited:
Perhaps you're getting extra whitespace in the string, like a \r before the \n?

Maybe try adding something like:

Serial.println(strlen(fileToLoad.c_str()));

you are right ..oO .. there should be 20 chars in the string, but there are 21 ..

is there a easy way to trim that in c++? .. I am searching my ass off but all I find seems way to complicated.
 
As noted in post #9, you could add ":" to the end of each line in startCfg.txt
or
cfgValue.trim(); removes trailing white space (including '\r') so that should do it.
 
Last edited:
Status
Not open for further replies.
Back
Top