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
6122 gold badges5 silver badges18 bronze badges
asked Sep 21, 2024 at 16:23
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();
}
answered Sep 21, 2024 at 20:30
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

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.