Pass wifi credentials to & from functions

what is the simplest way please, to pass the ssid and password between these functions?

in my main code I have

#include <WiFi.h>
#define BAUDRATE 115200

char ssid[50];
char pass[50];

void setup() {
  Serial.begin(BAUDRATE);
  Serial.println();
  serGetCreds();   //gets credentilals but they arent passed to the connectWiFi() function
 /*   //  however like this it works
  Serial.print("Enter your WiFi credentials.\n");
  Serial.print("SSID: ");
  while (Serial.available() == 0) {
    // wait
  }
  Serial.readBytesUntil(10, ssid, 50);
  Serial.println(ssid);
  
  Serial.println("PASS: ");
  while (Serial.available() == 0) {
    // wait
  }
  Serial.readBytesUntil(10, pass, 50);
  Serial.println(pass);
  */
  connectWiFi(ssid, pass);
}

and in another tab

void serGetCreds(){
  Serial.print("Enter your WiFi credentials.\n");
  Serial.print("SSID: ");
  while (Serial.available() == 0) {
    // wait
  }
  Serial.readBytesUntil(10, ssid, 50);
  Serial.println(ssid);
  
  Serial.println("PASS: ");
  while (Serial.available() == 0) {
    // wait
  }
  Serial.readBytesUntil(10, pass, 50);
  Serial.println(pass);
  }

void connectWiFi(char* ssid, char* pass) {
  WiFi.begin(ssid, pass);
  //confirm ssid, pwd
  Serial.print("Initialising SSID: ");
  Serial.print(ssid);
  Serial.print("  with PASS: ");
  Serial.println(pass);
  delay(200); //allow time to connect
  // Wait until connection completed
  Serial.print("Connecting to AP...");
  while (WiFi.status() != WL_CONNECTED)  {
    delay(500);
    Serial.print(".");
  }
  Serial.println("Ok\n");
  Serial.print("IP number is ");
  Serial.print(WiFi.localIP());
  Serial.print("  MAC address is ");
  Serial.println(WiFi.macAddress());
  Serial.print("WiFi connection signal Strength (RRSI): ");
  Serial.println(WiFi.RSSI());
}

The simplest way would be to declare the variables as globals so that you do not need to pass and return them

Next simplest would be to pass the variables as references so that there is no need to return them as the target function can change them directly

A third way would be to pass and return a struct containing the variables

1 Like

Thanks @UKHeliBob

can you show me how to do thet please?

An example of passing a struct to a function and updating it

struct dataDef
{
    char ssid[20];
    byte byteValue0;
    byte byteValue1;
    byte result;
};

void setup()
{
    Serial.begin(115200);
    dataDef theData{
        "initial SSID",
        22,
        33,
        0
    };
    aFunction(theData);              //call function with struct as the paramater
    Serial.println(theData.result);  //print some of the data after function call
    Serial.println(theData.ssid);
}

void loop()
{
}

dataDef aFunction(struct dataDef &passedData)  //passed by reference.  Note the data type of the function and argument
{
    passedData.result = passedData.byteValue0 + passedData.byteValue1;  //update result in struct
    strcpy(passedData.ssid, "new SSID");                                //change a string in the struct
    //no need for a return as variables were passed by reference
}

Your code works for me

  • ssid and pass are declared as global variables, more specifically arrays
  • setGetCreds references those global variables: Serial.readBytesUntil stores those bytes there
  • in setup, those global variables are passed to connectWiFi
  • void connectWiFi(char* ssid, char* pass) {
    
    which declares two local variables that coincidentally have the same names as the global variables
  • the arrays variables "decay" to pointers to their first element. Those local variables now have copies of those pointers, which will work just fine

In other words, you could also have declared

void connectWiFi() {

the function would use the global variables directly, and it would have worked just the same.

Global variables are a waste of memory though, since you only need the credentials once. They do have one definite advantage: their memory is initialized to all-zero. So there's no need to NUL-terminate the input after readBytesUntil fills the buffer, since it's all zeroes to begin with.

You can use a struct to return multiple values at the same time. Declare it globally, like before setup

struct Creds {
  char ssid[50];
  char pass[50];
};

void setup() {

Instead of creating one and passing it twice -- to be filled, then to be used -- you can have setGetCreds return one

Creds serGetCreds() {
  Creds ret;  // the return value

You need to NUL-terminate

  ret.ssid[Serial.readBytesUntil('\n', ret.ssid, sizeof(ret.ssid) - 1)] = '\0';

which means you can't fill the entire array with input

  ret.pass[Serial.readBytesUntil('\n', ret.pass, sizeof(ret.pass) - 1)] = '\0';

and of course, the results have to be returned at the end

  return ret;
}

void connectWiFi(Creds &creds) {
  WiFi.begin(creds.ssid, creds.pass);

Meanwhile, the other function will receive it by reference, to save some copying and memory. Then you just need to do the hand-off in setup

  Creds creds = serGetCreds();
  connectWiFi(creds);

The hundred bytes used will be on the stack, which will be automatically "let go" once setup completes. If the compiler is smart enough, Return Value Optimization is performed: the ret instance in the called function (serGetCreds) is constructed in the place of the resulting creds in the calling function (setup), and there is no copy made.

1 Like

I would pass a pointer to the struct but that's not considered beginner code so it's a bit of a no-no here.

For interested beginners (not a beginner subject but a peek into the machine), all passed data goes onto the stack and is only removed when the function returns. When you have very limited memory (cough, Arduino!, cough) this matters easily.
The stack (local variables) grows from the top on memory down while the heap (global and static variables) grows from the bottom of memory up. If either crosses into the other, a crash or worse (keep running with corrupted values) will occur.

The "heap" is does not contain global and static variable. It's used for dynamically allocated storage but, it does indeed grow up.

Global and static variables are stored in a different area that does not "grow" at all during runtime. Its size only changes if modifications to your code cause it to do so.

And it's at the bottom below what you call the heap or at the top above the stack?

My idea of heap and stack come from pre-IBM_PC/XT days.
And if I load up a lot of global vars and the stack writes over them (can it?) then that's a bad thing regardless!

Thanks @kenb4

yes, just using global variables and it now works fine. I'll experiment with your other suggestions but the main lesson - KISS.

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