Invalid conversion from 'char**' to 'char'

After 2 hours of trying to get this to compile....I am frustrated.

I have a function I would like to call, it will read the contents of a file which will hold wifi SSID and password.

SSID
PASSWORD

I read the values into to a 2 element array and return the array.

I am getting this error

error: invalid conversion from 'char**' to 'char' [-fpermissive]
     return wifiinfo;
char readWiFi(fs::FS &fs){
  File file;
  char *wifiinfo[2]; //2 elements 100 max characters
  file = fs.open("/WiFi.txt");
  if(file){
    int i = 0;
    while(file.available()){
      char line[100];
      int line_length = file.readBytesUntil('\n', line, 100);
      Serial.write(line, line_length);
      Serial.write('\n');
      Serial.println(line);
      strcpy(wifiinfo[i],line);
      i++;
    }
    file.close();
    return wifiinfo;
  }
  else{
    Serial.println("SD Card: Error opening WiFi.txt");
  }
  
}

Here is my entire test sketch.

FILE-HANDLING-TEST.INO

#include "FS.h"
#include "SD.h"
#include "SPI.h"

// ***SD CARD INFO
//
// sd card pins
#define SD_MISO  12
#define SD_MOSI  11
#define SD_SCLK  13
#define SD_CS    10


int writeonce =0;
char *SSID = "HAYNES_635";
char *password = "635Haynes";

char *wifidata[2];


void setup() {
    Serial.begin(115200);
    delay(2000);
    Serial.println("starting");

    SPI.begin(SD_SCLK, SD_MISO, SD_MOSI, SD_CS);
    Serial.println("SPI and AD started");

    if(!SD.begin(SD_CS)){
        Serial.println("Card Mount Failed");
        return;
    }
    uint8_t cardType = SD.cardType();

    if(cardType == CARD_NONE){
        Serial.println("No SD card attached");
        return;
    }





}

void loop() {
  // put your main code here, to run repeatedly:
  if(writeonce == 0){
    readFile(SD, "/WiFi.txt");
    Serial.println("");
    writeWiFi(SD, SSID, password);
    Serial.println("");
    wifidata = readWiFi(SD);
    writeonce =1;
  }

}

FILEHANDLE.INO

void writeWiFi(fs::FS &fs, char * SSID, char  * password){
    fs.remove("/WiFi.txt");  //delete file first
    File file = fs.open("/WiFi.txt", FILE_WRITE);
    if(!file){
        Serial.println("Failed to open file for writing");
        return;
    }
    file.println(SSID);
    file.println(password);
    file.close();
}

void readFile(fs::FS &fs, const char * path){
    Serial.printf("Reading file: %s\n", path);

    File file = fs.open(path);
    if(!file){
        Serial.println("Failed to open file for reading");
        return;
    }

    Serial.print("Read from file: ");
    while(file.available()){
        Serial.write(file.read());
    }
    file.close();
}

char* readWiFi(fs::FS &fs){
  File file;
  char *wifiinfo[2]; //2 elements 100 max characters
  file = fs.open("/WiFi.txt");
  if(file){
    int i = 0;
    while(file.available()){
      char line[100];
      int line_length = file.readBytesUntil('\n', line, 100);
      Serial.write(line, line_length);
      Serial.write('\n');
      Serial.println(line);
      strcpy(wifiinfo[i],line);
      i++;
    }
    file.close();
    return wifiinfo;
  }
  else{
    Serial.println("SD Card: Error opening WiFi.txt");
  }
  
}

Just as well that you're getting a compile error because that code would have crashed if it actually ran. This:

      strcpy(wifiinfo[i],line);

is attempting to copy the data from the cstring 'line' into a random memory location pointed to by the uninitialized pointer 'wifiinfo[i]'.

The other problem is that you can't of course return an array from a function.

umm...what are these lines doing then.

int i = 0;
char *wifiinfo[2];

So I guess the solution would be to write to a global variable i.e. array then correct?

This creates a local variable of type int named 'i' and initializes it to 0:

int i = 0;

This creates an array of two character pointers that are pointing to random memory locations.

char *wifiinfo[2];

That's the simplest approach, there are others.

so if i = 1

strcpy(wifiinfo[i],line);

should translate into

strcpy(wifiinfo[1],line);

still unsure why that logic doesn't work in a while loop were i++:

As I said, wifiinfo[0] and wifiinfo[1] are uninitialized pointers that point to random locations in memory. You can't go writing data into random memory locations as the results are usually disastrous.

Doesn't this line initialize the 2 data points?

char *wifiinfo[2];

wifiinfo[0]
wifiinfo[1]

???

again thanks for the help. I am writing in like 6 different languages for this project and C++ is the biggest pain of them all.

NOT A PROGRAMMER.....lol

No, it only creates the array of pointers.

You need to reserve enough space to hold the character string. One approach might be

char password[50]={0}; //reserve a 50 byte slot for the password string.
...
strncpy(password,line,sizeof(password));  //make sure "line" is a zero-terminated character string.

Note that if "line" has more than 50 characters in it, the copy will truncate the password.

1 Like

The problem is, when strncpy() truncates, it does not append the terminating null character to the end of the array. So, the above code will leave 'password' unterminated if 'line' contains 50 or more characters before the null. Here's a workaround for that:

  char password[50] = {0};
  ...
  strncpy(password, line, sizeof(password)-1);

I have this test code for concept.

I have a writeWiFi function that simulates user input data and writes it to a file.
I have a readWiFi function that reads the data, then updates 2 global char arrays.

My issue is that before I exit the function, the global variable is correct. When the function exits the main loop then prints the same global variable, and it has a different value than expected.

MAIN FILE

#include "FS.h"
#include "SD.h"
#include "SPI.h"

// ***SD CARD INFO
//
// sd card pins
#define SD_MISO  12
#define SD_MOSI  11
#define SD_SCLK  13
#define SD_CS    10


int writeonce =0;
char *SSID = "HAYNES_635";
char *password = "635Haynes";

char *stored_ssid = "";
char *stored_ssidpassword = "";
char buf[100];


void setup() {
    Serial.begin(115200);
    delay(2000);
    Serial.println("starting");

    SPI.begin(SD_SCLK, SD_MISO, SD_MOSI, SD_CS);
    Serial.println("SPI and AD started");

    if(!SD.begin(SD_CS)){
        Serial.println("Card Mount Failed");
        return;
    }
    uint8_t cardType = SD.cardType();

    if(cardType == CARD_NONE){
        Serial.println("No SD card attached");
        return;
    }
}

void loop() {
  // put your main code here, to run repeatedly:
 
    //readFile(SD, "/WiFi.txt");
    Serial.println("");
    delay(2000);
    writeWiFi(SD, SSID, password);
    Serial.println("");
    delay(2000);
    Serial.println("");

    readWiFi(SD);
    Serial.print("SSID is - ");
    Serial.println(stored_ssid);
    Serial.print("password is - ");
    Serial.println(stored_ssidpassword);
    delay(10000);

}

FILEHANDLE.INO

void writeWiFi(fs::FS &fs, char * SSID, char  * password){
    fs.remove("/WiFi.txt");  //delete file first
    File file = fs.open("/WiFi.txt", FILE_WRITE);
    if(!file){
        Serial.println("Failed to open file for writing");
        return;
    }
    file.print(SSID);
    file.print("\n");
    file.print(password);
    file.print("\n");
    Serial.println("FILE WROTE");
    file.close();
}

void readFile(fs::FS &fs, const char * path){
    Serial.printf("Reading file: %s\n", path);

    File file = fs.open(path);
    if(!file){
        Serial.println("Failed to open file for reading");
        return;
    }

    Serial.println("Read from file: ");
    while(file.available()){
        Serial.write(file.read());
    }
    file.close();
}

void readWiFi(fs::FS &fs){
  File file;
  file = fs.open("/WiFi.txt");
  Serial.println("FILE OPEN");
  if(file){
    int i = 0;
    while(file.available()){
      Serial.println("READING FILE");
      //clear the buffer
      memset(buf, 0, sizeof(buf));
      file.readBytesUntil('\n', buf, 100);
      Serial.print("Iternation = ");
      Serial.println(i);
      Serial.print("Value of buf = ");
      Serial.print("\"");
      Serial.print(buf);
      Serial.print("\"\n");
      
      if(i == 0){
        Serial.print("loop 0 value = ");
        Serial.println(buf);
        stored_ssid = buf;
        Serial.print("Value of ssid after update - ");
        Serial.println(stored_ssid);
      }
      if(i == 1){
        Serial.print("loop 1 value = ");
        Serial.println(buf);
        stored_ssidpassword = buf;
        Serial.print("Checking SSID value again - ");
        Serial.println(stored_ssid);
        Serial.print("Value of ssidpassword after update - ");
        Serial.println(stored_ssidpassword);
      }
      i = i+1;
    }
    Serial.println("closing file");
    file.close();
  }
  else{
    Serial.println("SD Card: Error opening WiFi.txt");
  }
}

OUTPUT OF PRINT STATEMENTS

FILE OPEN

READING FILE

Iternation = 0

Value of buf = "HAYNES_635"

loop 0 value = HAYNES_635

Value of ssid after update - HAYNES_635

READING FILE

Iternation = 1

Value of buf = "635Haynes"

loop 1 value = 635Haynes

Checking SSID value again - 635Haynes

Value of ssidpassword after update - 635Haynes

closing file

SSID is - 635Haynes

password is - 635Haynes


The value of stored_ssid = stored_ssidpassword in the main loop. but in the function it is correct, I don't even know how it is getting different values into the global seeing the globals are correct before function exits.

actually after checking this again. on the 2nd iteration of the while loop. The value of stored_ssid gets changed. I am not sure why it's doing that......

Your variable

is not global char array, it is only pointer to char without memory to store actual array contents.

When you write

char* xx = "hhhhh";

it created a constant literal "hhhhh" that can't be changed in the run time.

So, your

char *stored_ssid = "";

is complete non-sense, because no point to create an empty constant.

To create a char array to be edited in run time you need to specify a size:

char stored_ssid[33] = "";

Note that the size must be enough to store your SSID with string terminator '\0'

You're setting stored_ssid and stored_ssidpassword to both point to buf. Then you read new stuff into buf. So now those pointers point to buf and it has whatever the last thing you read into it was.

To do what you want you need to create arrays, not pointers out of those two variables and use strcpy or better yet strncpy to copy the contents of buf to wherever it needs to be copied to.

Thank you Delta_G!!!!!!!

Worked like a charm.

 if(i == 0){
        Serial.print("loop 0 value = ");
        Serial.println(buf);
        strncpy(stored_ssid, buf, sizeof(stored_ssid));
        Serial.print("Value of ssid after update - ");
        Serial.println(stored_ssid);
      }
      if(i == 1){
        Serial.print("loop 1 value = ");
        Serial.println(buf);
        strncpy(stored_ssidpassword, buf, sizeof(stored_ssidpassword));
        Serial.print("Checking SSID value again - ");
        Serial.println(stored_ssid);
        Serial.print("Value of ssidpassword after update - ");
        Serial.println(stored_ssidpassword);

@b707

Thank you too. fixed my globals as well.

char stored_ssid[100] = "";
char stored_ssidpassword[100] = "";
char buf[100];
2 Likes

@thaynes ,
I've merged your 2 topics as the second is obviously a continuation of the first and very much related to it. Sure they might be different questions but they are about the same thing as far as I can see, so the information in one is relevant to the other.

Please, if you have more questions about this add them here so the context is obvious.

Thank you.

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