5

On my ESP32 I am trying to connect to my HomeAssistant server using ArduinoHA library.

I can easily connect when hard-coding the credentials: mqtt.begin("server", "username", "password");

But I recently tried to move the credentials to a config file that I read on setup:

typedef struct {
  String url;
  String username;
  String password;
} Credentials;


Credentials creds = readCredentials(credsFile);
mqtt.begin(creds.url.c_str(), creds.username.c_str(), creds.password.c_str());

The connection fails and the MQTT state returns -2 (StateConnectionFailed).

I tried comparing the read values to the hard-coded values:

  Serial.println(strcmp("192.168.1.45", creds.url.c_str()));
  Serial.println(strcmp("username", creds.username.c_str()));
  Serial.println(strcmp("passwird", creds.password.c_str()));

But it all returns 0 (i.e., its the same string).

Out of curiosity I tried it without reading the config file, and got same result:

  String url = "192.168.1.45";
  String username = "username";
  String password = "password";
  mqtt.begin(url.c_str(), username.c_str(), password.c_str());

So, does anyone know why the connection fails when using String object? Is there something I am missing?

== EDIT ==

It seems that the issue is memory related, the Strings are deleted before mqtt uses them to connect.

So I tried extracting the char* from the String object, but still got the same result:

  String urlStr = String("192.168.1.45");
  char url[urlStr.length() + 1];
  strcpy(url, urlStr.c_str());
  String usernameStr = String("username");
  char username[usernameStr.length() + 1];
  strcpy(username, usernameStr.c_str());
  String passwordStr = String("password");
  char password[passwordStr.length() + 1];
  strcpy(password, passwordStr.c_str());
  mqtt.begin(url, username, password);

One interesting thing I noticed is that using const char* instead of String does work, but I guess its just the compiler converting it to the original hard-coded version.

The only thing that worked for me is to define the Credentials object as a global variable, that way the credentials won't be cleared from memory.

6
  • The library could possibly store the addresses of the C strings and use them later, but your arguments do not live long enough. Please check what happens if you copy the C strings into some other variables. -- Or use C string variables as experiment before (in this comment just shorted: char url[] = "192.168.1.45"; /* ... */ mqtt.begin(url, ...);) Commented Sep 21, 2024 at 17:47
  • @thebusybee Interesting, it does seems to be memory related. I'll update the question with the modifications. Commented Sep 21, 2024 at 18:39
  • It does look like the begin() method expects the user to provide static storage for the char* arguments as said by @the busybee. The class retains only a copy of the pointer. See: github.com/dawidchyrzynski/arduino-home-assistant/blob/main/src/… and the corresponding .h file. Here is a similar case (see my comments). Unfortunately, you have to poke around in the source code to see if the method retains an expanded copy of the arguments or simply a pointer. arduino.stackexchange.com/questions/96796/… Commented Sep 21, 2024 at 18:44
  • @6v6gt Yeah, I just looked at the source code as well. It seems that the class doesn't create a copy of the credentials so I need to make the credentials a global variable... Commented Sep 21, 2024 at 18:50
  • You do not need global variables for the strings, just static variables. Commented Sep 21, 2024 at 19:15

1 Answer 1

4

Thanks to the comments from @thebusybee and @6v6gt and looking at the source code for the HAMqtt class I realized that the begin method stores the reference to the strings and doesn't create a copy.

bool HAMqtt::begin(
    const char* serverHostname,
    const uint16_t serverPort,
    const char* username,
    const char* password
)
{
    /*
        ...
    */

    _username = username;
    _password = password;
    _initialized = true;

    _mqtt->setServer(serverHostname, serverPort);
    _mqtt->setCallback(onMessageReceived);

    return true;
}

Since the String objects I have are deleted from memory after the setup function, I had to save the Credentials object either by making it global variable or by making it static. So this is the solution that worked for me:

typedef struct {
  String url;
  String username;
  String password;
} Credentials;

HAMqtt mqtt(client, device);

void setup() {
  static Credentials creds = readCredentials();
  mqtt.begin(creds.url.c_str(), creds.username.c_str(), creds.password.c_str());
}

void loop() {
  mqtt.loop();
}

2
  • Is the code working now? Commented Sep 22, 2024 at 13:40
  • @liaifat85 Yeah, seems to work fine ☺ Commented Sep 22, 2024 at 14:03

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.