Arduino IDE how to properly fill an array of strings

Thread Starter

zazas321

Joined Nov 29, 2015
737
Hey. I am having a lot of trouble filling an empty array of char arrays ( strings) .
I want to scan wifi networks and fill my string array with nearby wifi names. I need to save this data because I will be using it later.
Code:
char *list_of_networks[10];//maximum number 10 strings inside array
void scan_wifi_networks(){
int n = WiFi.scanNetworks();
    Serial.println("scan done");
    if (n == 0) {
        Serial.println("no networks found");
    } else {
        Serial.print(n);
        Serial.println(" networks found");
        for (int i = 1; i < n; i++) {
            // Print SSID and RSSI for each network found
            Serial.print(i + 1);
            Serial.print(": ");
            Serial.print(WiFi.SSID(i));
            Serial.print(" (");
            Serial.print(WiFi.RSSI(i));
            Serial.print(")");
            Serial.println((WiFi.encryptionType(i) == WIFI_AUTH_OPEN)?" ":"*");
            delay(10);
            //strcat(WiFi.SSID(i), list_of_networks);
            //char buffer[50];
            //WiFi.SSID(i).toCharArray(buffer, 50);
            
            //char str_array[WiFi.SSID(i).length()];
            //WiFi.SSID(i).toCharArray(str_array, WiFi.SSID(i).length());

            char temp_buffer[100];
            WiFi.SSID(i).toCharArray(temp_buffer, sizeof(temp_buffer));
            Serial.print("temp_buffer=");
            Serial.println(temp_buffer);
            strcpy(list_of_networks[i],"hello");
            Serial.println("OK");

        }
    }
    Serial.println("");
  
}
For now, I am trying to put "hello" string into my string array but I am getting an error and the device keeps restarting.
1605267504404.png

I really am not sure what is wrong here.
 

Thread Starter

zazas321

Joined Nov 29, 2015
737
My latest findings:
Code:
void scan_wifi_networks(){

int n = WiFi.scanNetworks();
    Serial.println("scan done");
    if (n == 0) {
        Serial.println("no networks found");
    } else {
        Serial.print(n);
        Serial.println(" networks found");
        for (int i = 0; i < n; i++) {
            // Print SSID and RSSI for each network found
            Serial.print(i );
            Serial.print(": ");
            Serial.print(WiFi.SSID(i));
            Serial.print(" (");
            Serial.print(WiFi.RSSI(i));
            Serial.print(")");
            Serial.println((WiFi.encryptionType(i) == WIFI_AUTH_OPEN)?" ":"*");
            delay(10);


            char temp_buffer[100];
            WiFi.SSID(i).toCharArray(temp_buffer, sizeof(temp_buffer));
            Serial.print("length of string=");
            Serial.println(WiFi.SSID(i).length());
            Serial.print("temp_buffer=");
            Serial.println(temp_buffer);
            list_of_networks[i]=temp_buffer;// If using this line, no more meditation guru error, but the string is still not set globally

            strncpy(list_of_networks[i],temp_buffer,WiFi.SSID(i).length());
            Serial.print("list_of_networks[i] after strncpy=");
            Serial.println(list_of_networks[i]);


        }
    }
    Serial.println("");
  
}
If I use line:
list_of_networks=temp_buffer;// If using this line, no more meditation guru error, but the string is still not set globally
The program does not give me medidation guru error anymore thought the list_of_networks is still not set globally. I tried to print it outside the function and it returns garbage

If I comment this line out, I get meditation guru error as previously shown
 

Thread Starter

zazas321

Joined Nov 29, 2015
737
I have found a nice solution. It can be done quite easiliy by declaring a String type variable array and storing it in there. I have read somewhere that it is best to avoid String type variables for Arduino IDE but I cannot seem to be able to do it with char arrays.
Code:
String list_strings[10];// declare the variable.

list_strings[i] = WiFi.SSID(i); // this is used inside for loop after scanning the network.
 

djsfantasi

Joined Apr 11, 2010
7,223
The value contained in the identifier temp_buffer is not your char array; it’s the pointer to the first memory location.

Your code doesn’t show the declaration of list_of_networks. If it’s insufficient to store a pointer, you are corrupting memory. Or if you assign a value to temp_buffer, your array now points to the wrong memory location, perhaps somewhere in the code.

Here is an Arduino forum thread on using pointers. You can Google “arduino pointers” for more tutorials.

Pointers are very useful when using char arrays. Try the following code and modify your sketch to use pointers

list_of_networks = &temp_buffer;


 

Thread Starter

zazas321

Joined Nov 29, 2015
737
The value contained in the identifier temp_buffer is not your char array; it’s the pointer to the first memory location.

Your code doesn’t show the declaration of list_of_networks. If it’s insufficient to store a pointer, you are corrupting memory. Or if you assign a value to temp_buffer, your array now points to the wrong memory location, perhaps somewhere in the code.

Here is an Arduino forum thread on using pointers. You can Google “arduino pointers” for more tutorials.

Pointers are very useful when using char arrays. Try the following code and modify your sketch to use pointers

list_of_networks = &temp_buffer;



Thank you for the reply but it is still not quite clear for me why my current code wont work.

I have declared my array of strings as following:
char *list_of_networks[10];//maximum number 10 strings inside array
now this list_of_networks should be able to hold 10 char arrays inside right?

he following code will convert WiFi.SSID string to char array so I can use strncpy operation. I also calculate length of each string.
I then use:
strncpy(list_of_networks,temp_buffer,WiFi.SSID(i).length());
The line above should copy the char array that is stored in temp_buffer to my array of char arrays(list_of_networks). It should be able to store 10 char arrays ( But it is usually between 3-5 so I am not going above 10 which could cause error)
Code:
            char temp_buffer[100];
            WiFi.SSID(i).toCharArray(temp_buffer, sizeof(temp_buffer));
            Serial.print("length of string=");
            Serial.println(WiFi.SSID(i).length());
            Serial.print("temp_buffer=");
            Serial.println(temp_buffer);
            strncpy(list_of_networks[i],temp_buffer,WiFi.SSID(i).length());
            Serial.print("list_of_networks[i] after strncpy=");
            Serial.println(list_of_networks[i]);
I really do not get what is wrong here
 

djsfantasi

Joined Apr 11, 2010
7,223
I have declared my array of strings as following:
char *list_of_networks[10];//maximum number 10 strings inside array
now this list_of_networks should be able to hold 10 char arrays inside right?
No, not right...

list_of_networks[10] will only hold one char array of up to 10 characters... Each subsequent strncpy() will overwrite the previous entry... Depending on the length of temp_buffer and the specific element of the array, you could be overwriting some random memory locations.

To understand, imagine first copying a string with length 5 writes to list_of_networks elements 0-5. The next copy (same length) writes to elements 1-6. And writing a string of 5 bytes to list_of_elements[5] writes beyond the end of the array... Corrupting memory!

To create an array of char arrays, you need to know the maximum length of the char arrays. Let’s say the maximum length is 6. Then, define a two-dimensional array for 10 elements of char arrays.

char list_of_elements[10][7];​

Then the line to copy temp_buffer to the array should be written as follows.

Code:
strncpy(list_of_networks[i][0],temp_buffer, WiFi.SSID(i).length());
Do you get this?
 
Last edited:

Thread Starter

zazas321

Joined Nov 29, 2015
737
No, not right...

list_of_networks[10] will only hold one char array of up to 10 characters... Each subsequent strncpy() will overwrite the previous entry... Depending on the length of temp_buffer and the specific element of the array, you could be overwriting some random memory locations.

To understand, imagine first copying a string with length 5 writes to list_of_networks elements 0-5. The next copy (same length) writes to elements 1-6. And writing a string of 5 bytes to list_of_elements[5] writes beyond the end of the array... Corrupting memory!

To create an array of char arrays, you need to know the maximum length of the char arrays. Let’s say the maximum length is 6. Then, define a two-dimensional array for 10 elements of char arrays.

char list_of_elements[10][7];​

Then the line to copy temp_buffer to the array should be written as follows.

Code:
strncpy(list_of_networks[i][0],temp_buffer, WiFi.SSID(i).length());
Do you get this?
Note that I have declared the list of strings as following:
char *list_of_networks[10]
instead of :
char list_of_networks[10]
Note the pointer sign

Does that make no difference?



Anyway, I understood your example with 2 dimensional arrays. I think the wifi network name should never be larger than 50 characters and I will probabaly never detect more than 20wifi signals so my array could look like :
char list_of_elements[20][50];


Thank you
 

djsfantasi

Joined Apr 11, 2010
7,223
Note that I have declared the list of strings as following:
char *list_of_networks[10]
instead of :
char list_of_networks[10]
Note the pointer sign

Does that make no difference?



Anyway, I understood your example with 2 dimensional arrays. I think the wifi network name should never be larger than 50 characters and I will probabaly never detect more than 20wifi signals so my array could look like :
char list_of_elements[20][50];


Thank you
I don’t think you understand what is behind what you’re doing.

You define temp_buffer in the for loop. And you save a pointer to it’s location in memory. Once you complete one iteration of the loop, the memory for temp_buffer is released. Now you have a pointer to a memory location that is not being used. The memory is available for something else.
 

bogosort

Joined Sep 24, 2011
571
Note that I have declared the list of strings as following:
char *list_of_networks[10]
instead of :
char list_of_networks[10]
Note the pointer sign

Does that make no difference?
Most certainly. The former is a an array of ten pointers to char; the latter is a single char array of length ten.

You can use the pointer version to store ten different char arrays ("strings"), but you must first allocate memory for those strings using malloc. One way of doing it:
C:
char *list_of_networks[10];

for (int i = 0; i < 10; ++i)
  list_of_networks[i] = malloc( 256 ); // string length up to 255 bytes + null

strcpy( list_of_networks[0], "this is a string" );
That is the dynamic (run-time) version of declaring a compile-time 10x256 char array, e.g., char list_of_networks[10][256]. The advantage of the dynamic method is that since it is constructed at runtime, you can allocate memory for exactly as many networks as you need, and for precisely the length of each network name. But if you only expect a small amount of networks, and you have plenty of RAM to spare, it's probably not worth the extra code complexity. (Don't forget to free() each string when you're done with it.)
 

djsfantasi

Joined Apr 11, 2010
7,223
Most certainly. The former is a an array of ten pointers to char; the latter is a single char array of length ten.

You can use the pointer version to store ten different char arrays ("strings"), but you must first allocate memory for those strings using malloc. One way of doing it:
C:
char *list_of_networks[10];

for (int i = 0; i < 10; ++i)
  list_of_networks[i] = malloc( 256 ); // string length up to 255 bytes + null

strcpy( list_of_networks[0], "this is a string" );
That is the dynamic (run-time) version of declaring a compile-time 10x256 char array, e.g., char list_of_networks[10][256]. The advantage of the dynamic method is that since it is constructed at runtime, you can allocate memory for exactly as many networks as you need, and for precisely the length of each network name. But if you only expect a small amount of networks, and you have plenty of RAM to spare, it's probably not worth the extra code complexity. (Don't forget to free() each string when you're done with it.)
Thanks @bogosort for the great explanation. I was trying to think of this method but didn’t remember it.

Either of these methods will work.
 

Thread Starter

zazas321

Joined Nov 29, 2015
737
I don’t think you understand what is behind what you’re doing.

You define temp_buffer in the for loop. And you save a pointer to it’s location in memory. Once you complete one iteration of the loop, the memory for temp_buffer is released. Now you have a pointer to a memory location that is not being used. The memory is available for something else.

That makes sense! So how to do it properly then? Since I need a placeholder to convert data from string to char array? I was assuming that strcpy will not point to memory address but just copy a value of that address as the function name suggests (string copy - strcpy)
 

Thread Starter

zazas321

Joined Nov 29, 2015
737
Okay nevermind. Using malloc seems to solve all issues. Just one quick thing though:
Code:
 char temp_buffer[100];
            WiFi.SSID(i).toCharArray(temp_buffer, sizeof(temp_buffer));
            Serial.print("length of string=");
            Serial.println(WiFi.SSID(i).length());
            Serial.print("temp_buffer=");
            Serial.println(temp_buffer);
            strncpy(list_of_networks[i],temp_buffer,WiFi.SSID(i).length());
            Serial.print("list_of_networks[i] after strncpy=");
            Serial.println(list_of_networks[i]);


1605854568909.png

As you can see it prints the correct length of string , and use function:
strncpy(list_of_networks,temp_buffer,WiFi.SSID(i).length());
But when I print an actual value inside list_of_networks, it prints the value and then follows up with a lot of garbage as you can see? Why would that be if I show the length of the string
 

Thread Starter

zazas321

Joined Nov 29, 2015
737
Also, for the same topic, can someone advice me regarding Strings and char arrays. From experience, I have learnt usually not to use String variable and always use char arrays.

However, it appears to me that the webserver and Spiffs is mostly working with String type variables therefore I always have to do conversions from one to another and I do not know if that is a good thing or not. For example, I have this piece of code here:

Code:
server.on("/action_page", HTTP_GET, [](AsyncWebServerRequest * request) {
    Serial.println("executing action page");
    int paramsNr = request->params();
    Serial.println(paramsNr);

    for(int i=0;i<paramsNr;i++){
        AsyncWebParameter* p = request->getParam(i);
        Serial.print("Param name: ");
        Serial.println(p->name());
        Serial.print("Param value: ");
        Serial.println(p->value());
        Serial.println("------");

        char temp_buffer[100];
        p->name().toCharArray(temp_buffer, sizeof(temp_buffer));


       
        if (strcmp(temp_buffer,"Network_ID")==0){
          Serial.println("network id received");
          Serial.print("ID: ");
          Serial.println(p->value());
          //writeFile(SPIFFS, "/ID.txt", p->value().c_str());
          writeFile(SPIFFS, "/ID.txt", temp_buffer);
        }

        if (strcmp(temp_buffer,"Network_pass")==0){
          Serial.println("network pass received");
          Serial.print("Pass: ");
          Serial.println(p->value());
          //writeFile(SPIFFS, "/Pass.txt", p->value().c_str());
          writeFile(SPIFFS, "/Pass.txt", temp_buffer);
        }

    }
   
    request->send(SPIFFS, "/action_page.html");
The code above will handle the webserver where User fills 2 textboxes.
First textbox is ID
Second textbox is Password


After receiving the information from the server, I write ID and Password values to Spiffs since I want to save the values even when i power off the device.

Now the question is, do I want to write these values as Strings or char arrays?
This should write the value as string;
writeFile(SPIFFS, "/Pass.txt", p->value().c_str());
whereas this will write char array:
writeFile(SPIFFS, "/Pass.txt", temp_buffer);


Since I will later be reading these values from Spiffs and putting them inside a function which expects both variables to be char arrays, it would make more sense for me to write these values as to Spiffs as char arrays so i do not need to do an extra conversion form String to char array
The function that I want to put values:
Code:
WiFi.begin(ssid,password);
I was using this website as a reference and copied Spiffs functions from here:
https://medium.com/@ladolcefarnient...piffs-with-esp32-and-arduino-ide-9e37011d7a91


The spiffs functions are described as following:
Code:
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;
}



void writeFile(fs::FS &fs, const char * path, const char * message){
Serial.printf(“Writing file: %s\r\n”, path);
File file = fs.open(path, “w”);
if(!file){
Serial.println(“- failed to open file for writing”);
return;
}
if(file.print(message)){
Serial.println(“- file written”);
} else {
Serial.println(“- write failed”);
}
}
As you can see, readFile returns a String type variable, so I should probably be using String instead of char array right?

Could someone advice me regarding this please? What is the most convenient solution? I could use String variables and then just do a conversion before I put values into wifi function such as:
Code:
char temp_buffer_ID[100];
char temp_buffer_Pass[100];
readFile(SPIFFS, "/ID.txt").toCharArray(temp_buffer_ID, sizeof(temp_buffer_ID));
readFile(SPIFFS, "/Pass.txt").toCharArray(temp_buffer_Pass, sizeof(temp_buffer_Pass));
    Serial.print("Spiffs id converted to char array=");
    Serial.println(temp_buffer_ID);

    Serial.print("Spiffs password converted to char array=");
    Serial.println(temp_buffer_Pass);
Function above seems to work fine, I now able to pass the values of temp_buffer_ID and temp_buffer_Pass
to the wifi function and sucessfully connect to the WIFI
WiFi.begin(temp_buffer_ID,temp_buffer_Pass);
 
Last edited:

djsfantasi

Joined Apr 11, 2010
7,223
But when I print an actual value inside list_of_networks, it prints the value and then follows up with a lot of garbage as you can see? Why would that be if I show the length of the string
Of course you’re going to get those garbage characters. What is the size of one entry in list_of_networks? It’s the parameter you passed in malloc(). You only want to print up to the first nul character. You are printing the entire area of memory allocated for each element in the list, which includes some memory locations in memory that does not contain your data.

Imagine if malloc returned memory that contained “ABCDEFGH” and you copy “123” into that memory space. Memory would then contain “123’0’EFGH”. If you were to print that entry, you’d see characters that aren’t in your value of “123”.

Those backward ? is how the Arduino IDE displays non-printable characters. They are a placeholder when displaying memory.

Since the char array contains the nul value in the fourth byte, functions that use a char array would still work.
 

Thread Starter

zazas321

Joined Nov 29, 2015
737
Thank you. I have declared the char array as following:
Code:
char *list_of_networks[10];
for (int i = 0; i < 10; ++i){
  list_of_networks[i] = (char*)malloc( 256 ); // string length up to 255 bytes + null
}
But is still not fully clear to me how do I detect and handle null character.

The function that I uses to convert from String to char array:

Code:
int scan_wifi_networks(){
int n = WiFi.scanNetworks();
    Serial.println("scan done");
    if (n == 0) {
        Serial.println("no networks found");
    } 
    else {
        Serial.print(n);
        Serial.println(" networks found");
        for (int i = 0; i < n; i++) {
            char temp_buffer[100];
            WiFi.SSID(i).toCharArray(temp_buffer, sizeof(temp_buffer));
            Serial.print("length of string=");
            Serial.println(WiFi.SSID(i).length());
            Serial.print("temp_buffer=");
            Serial.println(temp_buffer);
            strncpy(list_of_networks[i],temp_buffer,WiFi.SSID(i).length());
            Serial.print("list_of_networks[i] after strncpy=");
            Serial.println(list_of_networks[i]);
        }
    }
    Serial.println("");
    return n;
  
}
1605874828468.png

There is no visible termination character when I print out the string. How can I detect it to avoid reading garbage?
 

djsfantasi

Joined Apr 11, 2010
7,223
I haven’t tested this code, but you need to be able to individually test each byte.

This is a simple way which might work. You could also use a Union to address variables in two different ways.

But, take a look at this. Even if it doesn’t work, it demonstrates the principles you need to understand.

C:
char print_Buffer[100];
// . . .
print_buffer = *list_of_networks[i];
j=0;
while (print_buffer[j] != 0) {
   Serial.print(print_buffer[j]);
   j++;
   }
Serial.println();
// . . .
Update: fixed comparison operator.
 
Last edited:

Thread Starter

zazas321

Joined Nov 29, 2015
737
Okay il test that. Can you confirm regarding the operator that you have used?
Code:
while (print_buffer[j] <> 0) {
I have never seen this operator <>. Also was not able to find any information about it on the internet
 
Top