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.

Rohit Gupta
  • 618
  • 2
  • 5
  • 18
SagiZiv
  • 201
  • 1
  • 8

1 Answers1

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(); }

SagiZiv
  • 201
  • 1
  • 8