Variable read from ESP32 SPIFFS not persistent

Hi, I am relatively new in Arduino programming. I want to connect my ESP32 board as a station to a WiFi network. The SSID and password are available in the files inputSSID.txt and inputPassword.txt in the SPIFFS. Reading the values into variables (storedSSID and storedPassword) with a readFile procedure and using those in the WiFi initialization routine does not work. When I print the variables, it appears that they are empty. This part is commented out in the code below.

When I use the call to the readFile procedure within the WiFi initalization call, everything works fine. I do not understand why the I can't assign the values in the files to the variables. Does anyone have suggestions how I can use the storedSSID variable?
Thanks, Fred

    String readFile(fs::FS &fs, const char * path){
  //Serial.printf("Reading file: %s\r\n", path);
  File file = fs.open(path, "r");
  if(!file || file.isDirectory()){
    Serial.println("- empty file or failed to open file");
    return String();
  }
  //Serial.println("- read from file:");
  String fileContent;
  while(file.available()){
    fileContent+=String((char)file.read());
  }
  //Serial.println(fileContent);
  return fileContent;
}


  //const char* readFile(SPIFFS, "/inputSSID.txt").c_str();
  //const char* readFile(SPIFFS, "/inputPassword.txt").c_str());
  //Serial.print("Connecting to WiFi (DHCP) .."); Serial.println(soredSSID);  //prints empty
  //WiFi.begin(storedSSID, storedPassword);   //does not work
  
  Serial.print("Connecting to WiFi (DHCP) .."); Serial.println(readFile(SPIFFS, "/inputSSID.txt").c_str());
  WiFi.begin(readFile(SPIFFS, "/inputSSID.txt").c_str(), readFile(SPIFFS, "/inputPassword.txt").c_str());

Forget about WiFi until you can read the 2 variables from SPIFFS satisfactorily. Write a simple sketch that does only that. Once you have it working you can put the majority of it in a function in your main sketch

Do the SPIFFS examples work ?

If you are having difficulty using SPIFFS you might want to try using either Preferences.h or EEPROM.h. They both provide simple access to NVS and they are both libraries included in the Arduino core for the ESP32.

1 Like

@UKHeliBob, yes the SPIFFS examples work fine!

I am trying to reproduce the output when I use the commented out lines. Takes me a while.

Fred

@cattledog, thanks! I will consider that as I have more parameters that must be initialized. Nevertheless I want to understand what is going wrong.

@UKHeliBob, here is the output when I run the original code. I send this again as I had to adapt some things....but the problem remains the same.

The code in readFile reproduces the file contents (De Riesen for SSID and empty string for password. The println outside the readFile does NOT print the file contents.

 //------ SPIFFS file access procedures ------
String readFile(fs::FS &fs, const char * path){
  Serial.printf("Reading file: %s\r\n", path);
  File file = fs.open(path, "r");
  if(!file || file.isDirectory()){
    Serial.println("- empty file or failed to open file");
    return String();
  }
  Serial.println("- read from file:");
  String fileContent;
  while(file.available()){
    fileContent+=String((char)file.read());
  }
  Serial.println(fileContent);
  return fileContent;
}

  const char* storedSSID = readFile(SPIFFS, "/inputSSID.txt").c_str();
  const char* storedPassword = readFile(SPIFFS, "/inputPassword.txt").c_str();
  Serial.print("Connecting to WiFi (DHCP) .."); Serial.println(storedSSID);  //prints empty
  WiFi.begin(storedSSID, storedPassword);   //does not work
 
  /*
  Serial.print("Connecting to WiFi (DHCP) .."); Serial.println(readFile(SPIFFS, "/inputSSID.txt").c_str());
  WiFi.begin(readFile(SPIFFS, "/inputSSID.txt").c_str(), readFile(SPIFFS, "/inputPassword.txt").c_str());
  */

  //"busy" indicator
  //add a time-out 
  while (WiFi.status() != WL_CONNECTED) {
    Serial.print('.');
    delay(1000);
  } 

Output:
Reading file: /inputSSID.txt

  • read from file:
    De Riesen
    Reading file: /inputPassword.txt
  • read from file:

Connecting to WiFi (DHCP) .. (does not show SSID)

Since you only posted a snippets, are you calling SPIFFs.begin() anywhere??

1 Like

I'm not exactly clear on why only one of your pointers to a returned String converted to .c_str() is working correctly. I would expect both to either fail or work.

The solution is to use memcpy() to transfer the .c_str() converted returns to char arrays. The Null terminator is added by the .c_str conversion.

#include "FS.h"
#include "SPIFFS.h"

void setup() {
  Serial.begin(115200);
  SPIFFS.begin();
  delay(1000);
  //writeFile(SPIFFS, "/inputSSID.txt", "1234567");
  //writeFile(SPIFFS, "/inputPassword.txt", "Hello World");

  char storedSSID[20] = "";//size appropriately
  char storedPassword[20] = "";//size appropriately  
  memcpy(storedSSID, readFile(SPIFFS, "/inputSSID.txt").c_str(), strlen(readFile(SPIFFS, "/inputSSID.txt").c_str())+1);
  memcpy(storedPassword, readFile(SPIFFS, "/inputPassword.txt").c_str(), strlen(readFile(SPIFFS, "/inputPassword.txt").c_str())+1); 
  
  //const char* storedSSID = readFile(SPIFFS, "/inputSSID.txt").c_str();
  //const char* storedPassword = readFile(SPIFFS, "/inputPassword.txt").c_str();
  
  Serial.print("storedSSID: ");
  Serial.println(storedSSID);  //prints empty
  Serial.print("storedPassword: ");
  Serial.println(storedPassword);//prints Hello World
  
}

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

}

void writeFile(fs::FS &fs, const char * path, const char * message){
    Serial.printf("Writing file: %s\r\n", path);

    File file = fs.open(path, FILE_WRITE);
    if(!file){
        Serial.println("- failed to open file for writing");
        return;
    }
    if(file.print(message)){
        Serial.println("- file written");
    } else {
        Serial.println("- write failed");
    }
    file.close();
}

//------ SPIFFS file access procedures ------
String readFile(fs::FS &fs, const char * path){
  Serial.printf("Reading file: %s\r\n", path);
  File file = fs.open(path, "r");
  if(!file || file.isDirectory()){
    Serial.println("- empty file or failed to open file");
    return String();
  }
  Serial.println("- read from file:");
  String fileContent;
  while(file.available()){
    fileContent+=String((char)file.read());
  }
  Serial.println(fileContent);
  
  return fileContent;
}  
1 Like

Thanks Ray,

Eh believe so, I have this code in the setup():

// Initialize SPIFFS #ifdef ESP32 if(!SPIFFS.begin(true)){ Serial.println("An Error has occurred while mounting SPIFFS"); return; } #else if(!SPIFFS.begin()){ Serial.println("An Error has occurred while mounting SPIFFS"); return; } #endif

OK, I'm going to give that a try. Should I just replace .c_str() by memcpy() ?

Fred

Don't bother!I looked up a reference to memcpy. I will give it a try.
Fred

@cattledog,

Both conversions behave the same. The printout of the password does not show anything as the file content is an empty string in the current case (open network).

I tried your suggestion to use memcpy() to convert String to const char*. I tried some variations, but without success. Here is my last codet:

  //const char* storedSSID = readFile(SPIFFS, "/inputSSID.txt").c_str();
  String inputSSID = readFile(SPIFFS, "/inputSSID.txt");
  Serial.print("inputSSID = ");Serial.println(inputSSID);  //prints the correct inputSSID, so reading SPIFFS works fine
  //convert input String to const char*, as apparently WiFi.begin requires const char* 
  const char* storedSSID;                                 
  memcpy(storedSSID, inputSSID, strlen(inputSSID)+1);        //memcpy(destination, source, num) 
  Serial.print("storedSSID = ");Serial.println(storedSSID);  //check

Result: cannot convert 'String' to 'const char*' for argument '1' to 'size_t strlen(const char*)'

Reading the SPIFFS file is OK . I checked it by printing the SSID string. So it is really in the conversion from String to const char*.

I tried to look things up on the web, but did not find a nice tutorial with examples. Reference pages are precise, but complex and often lack examples.

Fred

Result: cannot convert ‘String’ to ‘const char*’ for argument ‘1’ to ‘size_t strlen(const char*)’

You did not follow the example I gave. Don't use the char*. I believe that it is a null pointer the way you have declared it and If either dest or src is an invalid or null pointer the behavior is undefined, even if count is zero.

//const char* storedSSID;                                 
  //memcpy(storedSSID, inputSSID, strlen(inputSSID)+1); 
  
  char storedSSID[20] = "";//size appropriately
  char storedPassword[20] = "";//size appropriately  
  memcpy(storedSSID, readFile(SPIFFS, "/inputSSID.txt").c_str(), strlen(readFile(SPIFFS, "/inputSSID.txt").c_str())+1);
  memcpy(storedPassword, readFile(SPIFFS, "/inputPassword.txt").c_str(), strlen(readFile(SPIFFS, "/inputPassword.txt").c_str())+1);
1 Like

@cattledog,

This way of using memcpy gives a code that is doing the job! Thanks.

One question: in the declaration of the variables you mention "size appropriately". Does this mean that I have to size it large enough to hold the max. length of the expected SSID and password?

I do not quite understand what char* means and why my way of defining it yields a null pointer.

  1. I have to read about that
  2. I have to look at the place where I "stole" this code (probably in Random Nerd Tutorials). For some reason it was proposed over there. And for some reason I mis-treated the proposal.

Yes. With one additional space for the null terminator.

This topic was automatically closed 120 days after the last reply. New replies are no longer allowed.