MQTT-подключение не работает при использовании строковых объектов
На моем ESP32 я пытаюсь подключиться к серверу HomeAssistant, используя библиотеку ArduinoHA.
Я могу легко подключиться, если жестко запрограммирую учетные данные: mqtt.begin("server", "username", "password");
Но недавно я попытался перенести учетные данные в файл конфигурации, который я читал при настройке:
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());
Подключение не устанавливается, и состояние MQTT возвращает -2 (StateConnectionFailed).
Я попробовал сравнить считанные значения с жестко запрограммированными значениями:
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()));
Но все возвращает 0 (т.е. это одна и та же строка).
Из любопытства я попробовал это сделать, не читая файл конфигурации, и получил тот же результат:
String url = "192.168.1.45";
String username = "username";
String password = "password";
mqtt.begin(url.c_str(), username.c_str(), password.c_str());
Итак, кто-нибудь знает, почему соединение не устанавливается при использовании объекта String? Может, я что-то упускаю?
== ИЗМЕНИТЬ ==
Похоже, проблема связана с памятью: строки удаляются до того, как mqtt использует их для подключения.
Поэтому я попытался извлечь char* из объекта String, но получил тот же результат:
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);
Я заметил одну интересную вещь: использование const char* вместо String действительно работает, но, полагаю, компилятор просто преобразует его в исходную жестко закодированную версию.
Единственное, что мне помогло, — это определение объекта Credentials как глобальной переменной. Таким образом, учетные данные не будут удалены из памяти.
@SagiZiv, 👍5
Обсуждение1 ответ
Лучший ответ:
Благодаря комментариям @thebusybee и @6v6gt, а также просмотру исходного кода класса HAMqtt я понял, что метод begin сохраняет ссылку на строки и не создает копию.
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;
}
Поскольку объекты String удаляются из памяти после выполнения функции настройки, мне пришлось сохранить объект Credentials, сделав его либо глобальной переменной, либо статическим. Вот решение, которое мне помогло:
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();
}
Код теперь работает?, @liaifat85
@liaifat85 Да, кажется, работает отлично ☺, @SagiZiv
- Как преобразовать форматированный оператор print в строковую переменную?
- Преобразование строки в IP-адрес
- esp32-cam публикует изображение в mqtt
- Как прочитать входящие ШЕСТНАДЦАТИРИЧНОЕ значение из serial метод read ()?
- Публиковать данные json в mqtt
- Не удалось подключиться к брокеру MQTT через esp8266/32 и pub/sub client
- Преобразование JSON в строку для MQTT
- PubSubClient: Подписка работает, но обратный вызов никогда не вызывался
Библиотека могла бы хранить адреса C-строк и использовать их позже, но ваши аргументы хранятся недостаточно долго. Проверьте, что произойдёт, если скопировать C-строки в другие переменные. -- Или используйте C-строковые переменные в качестве эксперимента (в этом комментарии просто сокращённый вариант:
char url[] = "192.168.1.45"; /* ... */ mqtt.begin(url, ...);), @the busybee@thebusybee Интересно, похоже, это связано с памятью. Я обновлю вопрос, внеся изменения., @SagiZiv
Похоже, метод begin() ожидает, что пользователь предоставит статическое хранилище для аргументов типа char*, как и сказал @the busybee. Класс сохраняет только копию указателя. См.: https://github.com/dawidchyrzynski/arduino-home-assistant/blob/main/src/HAMqtt.cpp и соответствующий файл .h. Вот похожий случай (см. мои комментарии). К сожалению, придётся покопаться в исходном коде, чтобы узнать, сохраняет ли метод развёрнутую копию аргументов или просто указатель. https://arduinoprosto.ru/q/96796/wifi-credentials-pulled-from-an-sd-card-fail-but-work-hard-coded, @6v6gt
@6v6gt Да, я тоже только что посмотрел исходный код. Похоже, класс не создаёт копию учётных данных, поэтому мне нужно сделать учётные данные глобальной переменной..., @SagiZiv
Вам не нужны глобальные переменные для строк, только статические переменные., @the busybee
3 строки кода для назначения массива символов?
char url[]="192.168.1.45";..., @dda