From 9257433a50639400e8d06d3930d9bccc9a4490b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Le=C5=9Bniewski?= Date: Wed, 3 Jul 2024 18:39:58 +0200 Subject: [PATCH] [SYS] Use PicoMQTT for MQTT communication (#1954) * Update Arduino core for selected configurations * Use PicoMQTT for MQTT communication --- docs/upload/builds.md | 2 +- docs/upload/troubleshoot.md | 6 +- docs/use/ir.md | 2 - environments.ini | 8 +- main/User_config.h | 4 +- main/ZgatewayBT.ino | 2 +- main/main.ino | 415 +++++++++++++++++------------------- platformio.ini | 8 +- 8 files changed, 214 insertions(+), 233 deletions(-) diff --git a/docs/upload/builds.md b/docs/upload/builds.md index a12edb5..f97c6ce 100644 --- a/docs/upload/builds.md +++ b/docs/upload/builds.md @@ -18,7 +18,7 @@ PlatformIO config files work on the concept of overriding. At first, a very simp [env] framework = arduino lib_deps = - ${libraries.pubsubclient} + ${libraries.picomqtt} ${libraries.arduinojson} ${libraries.arduinolog} build_flags = diff --git a/docs/upload/troubleshoot.md b/docs/upload/troubleshoot.md index e8a55c7..d5c4158 100644 --- a/docs/upload/troubleshoot.md +++ b/docs/upload/troubleshoot.md @@ -38,9 +38,9 @@ instead of ## Repetitive MQTT disconnections or/and commands sent to the gateway not taken into account Most probably a network issue, don't use a guest network and if going through a firewall check its rules. To put aside gateway issue, try to connect to a local broker on the same network. -## You don't see the messages appearing on your broker but they appears on the serial monitor -This is due to a too small MQTT packet size, open User_config.h and set: -`#define mqtt_max_packet_size 1024` +## OMG ignores messages sent to it via MQTT +This can happen if the messages are too big and exceed the internal buffer size limit. To fix this, check the size of the message you're sending (in bytes). Next, open `User_config.h` and set `mqtt_max_payload_size` to be greater than that size, e.g.: +`#define mqtt_max_payload_size 1024` ## ESP Continuous restart or strange behaviour: This can be due to corruption of the ESP flash memory, try to erase flash and upload OMG on it again. diff --git a/docs/use/ir.md b/docs/use/ir.md index 51e95c8..35121aa 100644 --- a/docs/use/ir.md +++ b/docs/use/ir.md @@ -107,12 +107,10 @@ In this case the best way is to use hex values instead, but if you can't you may In User_config.h replace: ```cpp # define JSON_MSG_BUFFER 512 -# define mqtt_max_packet_size 1024 ``` by ```cpp # define JSON_MSG_BUFFER 1280 -# define mqtt_max_packet_size 1280 ``` ## Repeat the IR signal OpenMQTTGateway receive diff --git a/environments.ini b/environments.ini index a8edf37..11ac179 100644 --- a/environments.ini +++ b/environments.ini @@ -26,7 +26,7 @@ custom_description = RF gateway for the Sonoff RF Bridge relying on the internal custom_hardware = RFBridge v1 [env:rfbridge-direct] -platform = espressif8266@^2 +platform = ${com.esp8266_platform} board = esp8285 lib_deps = ${com-esp.lib_deps} @@ -42,6 +42,7 @@ build_flags = '-DZsensorGPIOInput="GPIOInput"' '-DINPUT_GPIO=0' '-DGateway_Name="OMG_SRFB_Direct"' + '-fcommon' board_build.flash_mode = dout board_build.ldscript = eagle.flash.1m64.ld ;this frees more space for firmware uplad via OTA. ;extra_scripts = scripts/compressFirmware.py ;uncomment this to compress the firmware. This helps updating e.g. Sonoff RF Bridge via OTA flash by saving space for the uploaded firmware. @@ -1459,7 +1460,7 @@ board_build.flash_mode = dout custom_description = KAKU RF gateway using NewRemoteSwitch library, need CC1101 [env:nodemcuv2-pilight] -platform = espressif8266@^2 +platform = ${com.esp8266_platform} board = nodemcuv2 lib_deps = ${com-esp.lib_deps} @@ -1470,6 +1471,7 @@ build_flags = ${com-esp.build_flags} '-DZgatewayPilight="Pilight"' '-DGateway_Name="OMG_ESP8266_Pilight"' + '-fcommon' board_build.flash_mode = dout custom_description = Gateway using ESPilight without the need of CC1101 @@ -1779,5 +1781,5 @@ build_flags = '-DLED_SEND_RECEIVE_ON=0' '-DGateway_Name="OMG_ESP32_BLE"' '-DUSE_BLUFI=1' - '-DARDUINO_LOOP_STACK_SIZE=15000' + '-DARDUINO_LOOP_STACK_SIZE=16000' custom_description = Regular BLE gateway based on esp-idf with adaptive scanning activated, automatically adapts the scan parameters depending on your devices diff --git a/main/User_config.h b/main/User_config.h index 522d0ba..3ec5b34 100644 --- a/main/User_config.h +++ b/main/User_config.h @@ -162,8 +162,8 @@ # endif #endif -#ifndef mqtt_max_topic_size -# define mqtt_max_packet_size JSON_MSG_BUFFER_MAX + mqtt_topic_max_size + 10 // maximum size of the MQTT packet +#ifndef mqtt_max_payload_size +# define mqtt_max_payload_size JSON_MSG_BUFFER_MAX // maximum size of the MQTT payload #endif #ifndef MQTT_USER diff --git a/main/ZgatewayBT.ino b/main/ZgatewayBT.ino index 7fdb9b5..d80178b 100644 --- a/main/ZgatewayBT.ino +++ b/main/ZgatewayBT.ino @@ -803,7 +803,7 @@ void coreTask(void* pvParameters) { while (true) { if (!BTProcessLock) { int n = 0; - while (client.state() != 0 && n <= InitialMQTTConnectionTimeout && !BTProcessLock) { + while (mqtt->connected() && n <= InitialMQTTConnectionTimeout && !BTProcessLock) { n++; delay(1000); } diff --git a/main/main.ino b/main/main.ino index 6983e40..edf691e 100644 --- a/main/main.ino +++ b/main/main.ino @@ -77,7 +77,9 @@ bool ready_to_sleep = false; #include #include -#include +#include + +#include struct JsonBundle { StaticJsonDocument doc; @@ -221,9 +223,6 @@ struct GfSun2000Data {}; void setupTLS(int index = CNT_DEFAULT_INDEX); -//adding this to bypass the problem of the arduino builder issue 50 -void callback(char* topic, byte* payload, unsigned int length); - char ota_pass[parameters_size] = gw_password; #ifdef USE_MAC_AS_GATEWAY_NAME # undef WifiManager_ssid @@ -238,12 +237,12 @@ int failure_number_ntwk = 0; // number of failure connecting to network int failure_number_mqtt = 0; // number of failure connecting to MQTT unsigned long timer_led_measures = 0; -static void* eClient = nullptr; static unsigned long last_ota_activity_millis = 0; // Global struct to store live SYS configuration data SYSConfig_s SYSConfig; bool failSafeMode = false; +static bool mqttSetupPending = true; static int cnt_index = CNT_DEFAULT_INDEX; #ifdef ESP32 @@ -300,8 +299,8 @@ ESP8266WiFiMulti wifiMulti; # include #endif -// client link to pubsub MQTT -PubSubClient client; +std::unique_ptr<::Client> eClient; +std::unique_ptr mqtt; template // Declared here to avoid pre-compilation issue (missing "template" in auto declaration by pio) void Config_update(JsonObject& data, const char* key, T& var); @@ -661,12 +660,12 @@ void pubMQTT(const char* topic, const char* payload) { */ void pubMQTT(const char* topic, const char* payload, bool retainFlag) { if (SYSConfig.XtoMQTT) { - if (client.connected()) { + if (mqtt->connected()) { SendReceiveIndicatorON(); Log.trace(F("[ OMG->MQTT ] topic: %s msg: %s " CR), topic, payload); - client.publish(topic, payload, retainFlag); + mqtt->publish(topic, payload, 0, retainFlag); } else { - Log.warning(F("Client not connected, aborting the publication" CR)); + Log.warning(F("MQTT not connected, aborting the publication" CR)); } } else { Log.notice(F("[ OMG->MQTT CANCELED] topic: %s msg: %s " CR), topic, payload); @@ -848,49 +847,117 @@ void SYSConfig_load() { void SYSConfig_load() {} #endif -#if AWS_IOT -// Global variables to track reconnections -unsigned long reconnection_count = 0; -unsigned long start_reconnection_window_millis = 0; -// Define a threshold for instability detection -const unsigned long reconnection_window_millis = 120000; -const unsigned int reconnection_threshold = 5; // Consider unstable if more than 5 reconnections in 2 minutes +#if defined(MDNS_SD) +std::pair discoverMQTTbroker() { + Log.trace(F("Browsing for MQTT service" CR)); + int n = MDNS.queryService("mqtt", "tcp"); + if (n == 0) { + Log.error(F("no services found" CR)); + } else { + Log.trace(F("%d service(s) found" CR), n); + for (int i = 0; i < n; ++i) { + Log.trace(F("Service %d %s found" CR), i, MDNS.hostname(i).c_str()); + Log.trace(F("IP %s Port %d" CR), MDNS.IP(i).toString().c_str(), MDNS.port(i)); + } + if (n == 1) { + Log.trace(F("One MQTT server found setting parameters" CR)); + return {MDNS.IP(0).toString(), uint16_t(MDNS.port(0))}; + } else { + Log.error(F("Several MQTT servers found, please deactivate mDNS and set your default server" CR)); + } + } + return {"", 0}; +} #endif -void connectMQTT() { -#ifndef ESPWifiManualSetup - checkButton(); // check if a reset of wifi/mqtt settings is asked -#endif +struct ss_cnt_parameters_backup { + ss_cnt_parameters parameters; + int cnt_index; + bool saveOnSuccess; +}; - Log.warning(F("MQTT connection..." CR)); -#ifdef ESP32 - //esp_task_wdt_add(NULL); - //esp_task_wdt_reset(); -#endif - char topic[mqtt_topic_max_size]; - strcpy(topic, mqtt_topic); - strcat(topic, gateway_name); - strcat(topic, will_Topic); - client.setBufferSize(mqtt_max_packet_size); - client.setSocketTimeout(GeneralTimeOut - 1); -#if AWS_IOT - if (client.connect(gateway_name, cnt_parameters_array[cnt_index].mqtt_user, cnt_parameters_array[cnt_index].mqtt_pass)) { // AWS doesn't support will topic for the moment - unsigned long current_millis = millis(); - if (start_reconnection_window_millis == 0 || current_millis - start_reconnection_window_millis > reconnection_window_millis) { - // Reset the count and timestamp at the start of a new window - reconnection_count = 0; - start_reconnection_window_millis = current_millis; - } - reconnection_count++; // Increment reconnection count - Log.trace(F("MQTT connection count: %d" CR), reconnection_count); - if (reconnection_count > reconnection_threshold && SYSConfig.XtoMQTT) { - // Detected instability in MQTT connection - Log.warning(F("MQTT connection instability detected: %d reconnections in the last %d seconds" CR), reconnection_count, reconnection_window_millis / 1000); - // Stop xtoMQTT to see if it helps and still enables to receive data - SYSConfig.XtoMQTT = false; +static std::unique_ptr cnt_parameters_backup; + +void setupMQTT() { + Log.notice(F("Reconfiguring MQTT client..." CR)); + + const auto& parameters = cnt_parameters_array[cnt_index]; + + // free the old MQTT client and underlying connection + mqtt.reset(); + eClient.reset(); + failure_number_mqtt = 0; + + // configure socket + if (parameters.isConnectionSecure) { + eClient.reset(new WiFiClientSecure); + if (parameters.isCertValidate) { + setupTLS(cnt_index); + } else { + static_cast(eClient.get())->setInsecure(); } + } else { + eClient.reset(new WiFiClient); + } + +#if defined(MDNS_SD) + Log.trace(F("Connecting to MQTT by mDNS without MQTT hostname" CR)); + const auto discovered_broker = discoverMQTTbroker(); + const auto broker_host = discovered_broker.first.c_str(); + const auto broker_port = discovered_broker.second; #else - if (client.connect(gateway_name, cnt_parameters_array[cnt_index].mqtt_user, cnt_parameters_array[cnt_index].mqtt_pass, topic, will_QoS, will_Retain, will_Message)) { + const auto broker_host = parameters.mqtt_server; + const auto broker_port = String(parameters.mqtt_port).toInt(); +#endif + + Log.trace(F("Mqtt server: %s" CR), broker_host); + Log.trace(F("Port: %u" CR), broker_port); + + mqtt.reset(new PicoMQTT::Client(*eClient, broker_host, broker_port, gateway_name, + parameters.mqtt_user, parameters.mqtt_pass, + 0, // minimum reconnect attempt interval [ms] + 60 * 1000, // keep alive interval [ms] + (GeneralTimeOut - 1) * 1000 // socket timeout [ms] + )); + +#if AWS_IOT + // AWS doesn't support will topic for the moment + mqtt->will.topic = ""; + mqtt->will.payload = ""; + mqtt->will.qos = 0; + mqtt->will.retain = false; +#else + mqtt->will.topic = String(mqtt_topic) + gateway_name + will_Topic; + mqtt->will.payload = will_Message; + mqtt->will.qos = will_QoS; + mqtt->will.retain = will_Retain; +#endif + + mqtt->connected_callback = [] { +#if AWS_IOT + { + // Define a threshold for instability detection + constexpr unsigned int reconnection_threshold = 5; + constexpr unsigned long reconnection_window_millis = 120000; // Consider unstable if more than 5 reconnections in 2 minutes + + static unsigned int reconnection_count = 0; + static unsigned long start_reconnection_window_millis = 0; + + const unsigned long current_millis = millis(); + if (start_reconnection_window_millis == 0 || current_millis - start_reconnection_window_millis > reconnection_window_millis) { + // Reset the count and timestamp at the start of a new window + reconnection_count = 0; + start_reconnection_window_millis = current_millis; + } + reconnection_count++; // Increment reconnection count + Log.trace(F("MQTT connection count: %d" CR), reconnection_count); + if (reconnection_count > reconnection_threshold && SYSConfig.XtoMQTT) { + // Detected instability in MQTT connection + Log.warning(F("MQTT connection instability detected: %d reconnections in the last %d seconds" CR), reconnection_count, reconnection_window_millis / 1000); + // Stop xtoMQTT to see if it helps and still enables to receive data + SYSConfig.XtoMQTT = false; + } + } #endif displayPrint("MQTT connected"); @@ -898,37 +965,58 @@ void connectMQTT() { failure_number_mqtt = 0; // Once connected, publish an announcement... pub(will_Topic, Gateway_AnnouncementMsg, will_Retain); - //Subscribing to topic - char topic2[mqtt_topic_max_size]; - strcpy(topic2, mqtt_topic); - strcat(topic2, gateway_name); - strcat(topic2, subjectMQTTtoX); - if (client.subscribe(topic2)) { -#ifdef ZgatewayRF - client.subscribe(subjectMultiGTWRF); // subject on which other OMG will publish, this OMG will store these msg and by the way don't republish them if they have been already published + + if (cnt_parameters_backup) { + // this was the first attempt to connect to a new server and it succeeded + Log.notice(F("MQTT connection successful" CR)); + cnt_parameters_array[cnt_index].validConnection = true; + readCntParameters(cnt_index); + +#ifndef ESPWifiManualSetup + if (cnt_parameters_backup->saveOnSuccess) // Save the new parameters to the flash + saveConfig(); #endif -#ifdef ZgatewayIR - client.subscribe(subjectMultiGTWIR); // subject on which other OMG will publish, this OMG will store these msg and by the way don't republish them if they have been already published -#endif - Log.trace(F("Subscription OK to the subjects %s" CR), topic2); + + cnt_parameters_backup.reset(); + ESPRestart(7); + } + }; + + mqtt->connection_failure_callback = [] { + if (WiFi.status() != WL_CONNECTED) { + // No WiFi connection, MQTT couldn't connect, ignore this failure + return; } - } else { failure_number_mqtt++; // we count the failure Log.warning(F("failure_number_mqtt: %d" CR), failure_number_mqtt); - Log.warning(F("failed, rc=%d" CR), client.state()); + + const auto& parameters = cnt_parameters_array[cnt_index]; + + if (parameters.isConnectionSecure) { + WiFiClientSecure* client = static_cast(eClient.get()); #if defined(ESP32) - if (cnt_parameters_array[cnt_index].isConnectionSecure) - Log.warning(F("failed, ssl error code=%d" CR), ((WiFiClientSecure*)eClient)->lastError(nullptr, 0)); + Log.warning(F("failed, ssl error code=%d" CR), client->lastError(nullptr, 0)); #elif defined(ESP8266) - if (cnt_parameters_array[cnt_index].isConnectionSecure) - Log.warning(F("failed, ssl error code=%d" CR), ((WiFiClientSecure*)eClient)->getLastSSLError()); + Log.warning(F("failed, ssl error code=%d" CR), client->getLastSSLError()); #endif + } + + if (cnt_parameters_backup) { + // this was the first attempt to connect to a new server and it failed, revert to old settings + Log.error(F("MQTT connection failed, reverting to previous settings" CR)); + cnt_parameters_array[cnt_index] = cnt_parameters_backup->parameters; + cnt_index = cnt_parameters_backup->cnt_index; + mqttSetupPending = true; + cnt_parameters_backup.reset(); + ESPRestart(7); + return; + } + ErrorIndicatorON(); delayWithOTA(5000); ErrorIndicatorOFF(); delayWithOTA(5000); - // If we have failed to connect to the MQTT broker, we will try to connect to the next set of connection parameters if (failure_number_mqtt > maxRetryWatchDog) { #ifndef ESPWifiManualSetup // Look for the next valid connection @@ -946,7 +1034,6 @@ void connectMQTT() { } } #endif - unsigned long millis_since_last_ota; while ( // When @@ -962,29 +1049,21 @@ void connectMQTT() { } ESPRestart(1); } - } -} + }; -// Callback function, when the gateway receive an MQTT value on the topics subscribed this function is called -void callback(char* topic, byte* payload, unsigned int length) { - // In order to republish this payload, a copy must be made - // as the original payload buffer will be overwritten whilst - // constructing the PUBLISH packet. - Log.trace(F("Hey I got a callback %s" CR), topic); - // Allocate the correct amount of memory for the payload copy - byte* p = (byte*)malloc(length + 1); - // Copy the payload to the new buffer - memcpy(p, payload, length); - // Conversion to a printable string - p[length] = '\0'; - //launch the function to treat received data if this data concern OpenMQTTGateway - if ((strstr(topic, subjectMultiGTWKey) != NULL) || - (strstr(topic, subjectGTWSendKey) != NULL) || - (strstr(topic, subjectMQTTtoSYSupdate) != NULL)) - receivingMQTT(topic, (char*)p); + mqtt->subscribe(String(mqtt_topic) + gateway_name + subjectMQTTtoX, receivingMQTT, mqtt_max_payload_size); - // Free the memory - free(p); +#ifdef ZgatewayRF + // subject on which other OMG will publish, this OMG will store these msg and by the way don't republish them if they have been already published + mqtt->subscribe(subjectMultiGTWRF, receivingMQTT, mqtt_max_payload_size); +#endif + +#ifdef ZgatewayIR + // subject on which other OMG will publish, this OMG will store these msg and by the way don't republish them if they have been already published + mqtt->subscribe(subjectMultiGTWIR, receivingMQTT, mqtt_max_payload_size); +#endif + + mqtt->begin(); } #if defined(ESP32) && (defined(WifiGMode) || defined(WifiPower)) @@ -1205,26 +1284,6 @@ void setup() { modules.add(ZwebUI); #endif - if (cnt_parameters_array[cnt_index].isConnectionSecure) { - eClient = new WiFiClientSecure; - setupTLS(cnt_index); - } else { - eClient = new WiFiClient; - } - client.setClient(*(Client*)eClient); - -#if defined(MDNS_SD) - Log.trace(F("Connecting to MQTT by mDNS without MQTT hostname" CR)); - connectMQTTmdns(); -#else - uint16_t port = strtol(cnt_parameters_array[cnt_index].mqtt_port, NULL, 10); - Log.trace(F("Port: %l" CR), port); - Log.trace(F("Mqtt server: %s" CR), cnt_parameters_array[cnt_index].mqtt_server); - client.setServer(cnt_parameters_array[cnt_index].mqtt_server, port); -#endif - - client.setCallback(callback); - delay(1500); #if defined(ZgatewayRF) || defined(ZgatewayPilight) || defined(ZgatewayRTL_433) || defined(ZgatewayRF2) || defined(ZactuatorSomfy) setupCommonRF(); @@ -1382,7 +1441,7 @@ void setup() { setupRTL_433(); modules.add(ZgatewayRTL_433); #endif - Log.trace(F("mqtt_max_packet_size: %d" CR), mqtt_max_packet_size); + Log.trace(F("mqtt_max_payload_size: %d" CR), mqtt_max_payload_size); char jsonChar[100]; serializeJson(modules, jsonChar, measureJson(modules) + 1); @@ -1481,7 +1540,7 @@ void setOTA() { void setupTLS(int index) { configTime(0, 0, NTP_SERVER); - WiFiClientSecure* sClient = (WiFiClientSecure*)eClient; + WiFiClientSecure* sClient = static_cast(eClient.get()); Log.notice(F("cnt index used: %d" CR), index); if (!cnt_parameters_array[index].isCertValidate) { Log.notice(F("Disabling cert validation" CR)); @@ -2195,27 +2254,6 @@ void WiFiEvent(WiFiEvent_t event) { # endif #endif -#if defined(MDNS_SD) -void connectMQTTmdns() { - Log.trace(F("Browsing for MQTT service" CR)); - int n = MDNS.queryService("mqtt", "tcp"); - if (n == 0) { - Log.error(F("no services found" CR)); - } else { - Log.trace(F("%d service(s) found" CR), n); - for (int i = 0; i < n; ++i) { - Log.trace(F("Service %d %s found" CR), i, MDNS.hostname(i).c_str()); - Log.trace(F("IP %s Port %d" CR), MDNS.IP(i).toString().c_str(), MDNS.port(i)); - } - if (n == 1) { - Log.trace(F("One MQTT server found setting parameters" CR)); - client.setServer(MDNS.IP(0), int(MDNS.port(0))); - } else { - Log.error(F("Several MQTT servers found, please deactivate mDNS and set your default server" CR)); - } - } -} -#endif void loop() { #ifndef ESPWifiManualSetup checkButton(); // check if a reset of wifi/mqtt settings is asked @@ -2225,6 +2263,11 @@ void loop() { //esp_task_wdt_reset(); #endif + if (mqttSetupPending) { + setupMQTT(); + mqttSetupPending = false; + } + unsigned long now = millis(); // Switch off of the LED after TimeLedON @@ -2243,11 +2286,12 @@ void loop() { #if defined(ZwebUI) && defined(ESP32) WebUILoop(); #endif - if (client.loop()) { // MQTT client is still connected + mqtt->loop(); + if (mqtt->connected()) { // MQTT client is still connected InfoIndicatorON(); failure_number_ntwk = 0; // We have just re-connected if connected was previously false - bool justReconnected = !connected; + const bool justReconnected = !connected; #ifdef ZmqttDiscovery // Deactivate autodiscovery after DiscoveryAutoOffTimer. // Exception: when discovery_republish_on_reconnect is enabled, we never never automatically disable discovery @@ -2419,7 +2463,6 @@ void loop() { } else { // MQTT disconnected connected = false; - connectMQTT(); } } else { // disconnected from network #ifdef ESP32 @@ -2984,8 +3027,9 @@ void MQTTHttpsFWUpdate(char* topicOri, JsonObject& HttpsFwUpdateData) { } else { WiFiClientSecure update_client; if (cnt_parameters_array[cnt_index].isConnectionSecure) { - client.disconnect(); - update_client = *(WiFiClientSecure*)eClient; + mqtt.reset(); + mqttSetupPending = true; + update_client = *static_cast(eClient.get()); } else { syncNTP(); } @@ -3126,7 +3170,7 @@ void MQTTtoSYS(char* topicOri, JsonObject& SYSdata) { // json object decoding #endif String prev_ssid = WiFi.SSID(); String prev_pass = WiFi.psk(); - client.disconnect(); + // NOTE: There's no need to disconnect MQTT manually WiFi.disconnect(true); Log.warning(F("Attempting connection to new AP %s" CR), (const char*)SYSdata["wifi_ssid"]); @@ -3147,8 +3191,6 @@ void MQTTtoSYS(char* topicOri, JsonObject& SYSdata) { // json object decoding restartESP = true; } - bool disconnectClient = false; // Trigger client.disconnet if a user/password change doesn't - if ((SYSdata.containsKey("mqtt_topic") && SYSdata["mqtt_topic"].is()) || (SYSdata.containsKey("gateway_name") && SYSdata["gateway_name"].is()) || (SYSdata.containsKey("gw_pass") && SYSdata["gw_pass"].is())) { @@ -3165,7 +3207,7 @@ void MQTTtoSYS(char* topicOri, JsonObject& SYSdata) { // json object decoding #ifndef ESPWifiManualSetup saveConfig(); #endif - disconnectClient = true; // trigger reconnect in loop using the new topic/name + mqttSetupPending = true; // trigger reconnect in loop using the new topic/name } #ifdef MQTTsetMQTT @@ -3173,6 +3215,7 @@ void MQTTtoSYS(char* topicOri, JsonObject& SYSdata) { // json object decoding bool save_cnt = false; bool read_cnt = false; bool test_cnt = false; + if (SYSdata.containsKey("save_cnt") && SYSdata["save_cnt"].is()) { save_cnt = SYSdata["save_cnt"].as(); } @@ -3183,23 +3226,20 @@ void MQTTtoSYS(char* topicOri, JsonObject& SYSdata) { // json object decoding test_cnt = SYSdata["test_cnt"].as(); } - int prev_cnt_index = cnt_index; - char prev_mqtt_user[parameters_size]; - strcpy(prev_mqtt_user, cnt_parameters_array[prev_cnt_index].mqtt_user); - char prev_mqtt_pass[parameters_size]; - strcpy(prev_mqtt_pass, cnt_parameters_array[prev_cnt_index].mqtt_pass); - char prev_mqtt_server[parameters_size]; - strcpy(prev_mqtt_server, cnt_parameters_array[prev_cnt_index].mqtt_server); - char prev_mqtt_port[6]; - strcpy(prev_mqtt_port, cnt_parameters_array[prev_cnt_index].mqtt_port); - bool prev_mqtt_secure = cnt_parameters_array[prev_cnt_index].isConnectionSecure; - if (SYSdata.containsKey("cnt_index") && SYSdata["cnt_index"].is()) { if (SYSdata["cnt_index"].as() < 0 || SYSdata["cnt_index"].as() > 2) { Log.error(F("Invalid cnt index provided - ignoring command" CR)); return; } + + // we're overwrittng parameters, create a backup to be able to revert + cnt_parameters_backup.reset(new ss_cnt_parameters_backup()); + cnt_parameters_backup->cnt_index = cnt_index; + cnt_parameters_backup->saveOnSuccess = save_cnt; + cnt_index = SYSdata["cnt_index"].as(); + cnt_parameters_backup->parameters = cnt_parameters_array[cnt_index]; + Log.notice(F("MQTT cnt index %d" CR), cnt_index); if (SYSdata.containsKey("mqtt_user") && SYSdata["mqtt_user"].is() && SYSdata.containsKey("mqtt_pass") && SYSdata["mqtt_pass"].is()) { @@ -3244,6 +3284,7 @@ void MQTTtoSYS(char* topicOri, JsonObject& SYSdata) { // json object decoding cnt_parameters_array[cnt_index].ota_server_cert = processCert(SYSdata["ota_server_cert"].as()); Log.trace(F("Assigning OTA server cert %s" CR), generateHash(cnt_parameters_array[cnt_index].ota_server_cert).c_str()); } + // Read the memory certs hash to MQTT if (read_cnt) readCntParameters(cnt_index); @@ -3260,74 +3301,14 @@ void MQTTtoSYS(char* topicOri, JsonObject& SYSdata) { // json object decoding stopProcessing(); # endif # endif - client.disconnect(); - prev_client = eClient; - - if (cnt_parameters_array[cnt_index].isConnectionSecure) { - Log.notice(F("Connecting to secure MQTT broker" CR)); - eClient = new WiFiClientSecure; - } else { - Log.warning(F("Connecting to unsecure MQTT broker" CR)); - eClient = new WiFiClient; - } - client.setClient(*(Client*)eClient); - - if (cnt_parameters_array[cnt_index].isConnectionSecure) { - setupTLS(cnt_index); - } - } - - if (save_cnt || (test_cnt && !read_cnt)) { - Log.notice(F("Attempting connection to new MQTT broker" CR)); - client.setServer(cnt_parameters_array[cnt_index].mqtt_server, strtol(cnt_parameters_array[cnt_index].mqtt_port, NULL, 10)); - // Connect the client - connectMQTT(); - // If the connection is successful we save the parameters to the flash memory - if (client.connected()) { - Log.notice(F("Connection successful" CR)); - cnt_parameters_array[cnt_index].validConnection = true; - readCntParameters(cnt_index); - - if (prev_client != nullptr) { - delete prev_client; - } -# ifndef ESPWifiManualSetup - if (save_cnt) // Save the new parameters to the flash - saveConfig(); -# endif - } else { // Revert to previous settings - Log.error(F("Connection failed, reverting to previous settings" CR)); - cnt_index = prev_cnt_index; - if (SYSdata.containsKey("mqtt_user") && SYSdata.containsKey("mqtt_pass")) { - strcpy(cnt_parameters_array[cnt_index].mqtt_user, prev_mqtt_user); - strcpy(cnt_parameters_array[cnt_index].mqtt_pass, prev_mqtt_pass); - } - if (SYSdata.containsKey("mqtt_server") && SYSdata.containsKey("mqtt_port")) { - client.setServer(prev_mqtt_server, strtol(prev_mqtt_port, NULL, 10)); - } - // Revert the change of secure connection - if (SYSdata.containsKey("mqtt_secure")) { - cnt_parameters_array[cnt_index].isConnectionSecure = prev_mqtt_secure; - } - if (prev_client != nullptr) { - delete eClient; - eClient = prev_client; - client.setClient(*(Client*)eClient); - } - // Restest the connection - connectMQTT(); - // If the parameters given does not enable to connect we want to exit directly and not take into account any associated parameters with the connection - ESPRestart(7); - } - restartESP = true; + mqttSetupPending = true; + } else { + // no request to test or save the parameters, forget the old settings + cnt_parameters_backup.reset(); } #endif - if (disconnectClient) { - client.disconnect(); - } - if (SYSdata.containsKey("xtomqtt") && SYSdata["xtomqtt"].is()) { SYSConfig.XtoMQTT = SYSdata["xtomqtt"]; Log.notice(F("xtomqtt: %T" CR), SYSConfig.XtoMQTT); @@ -3395,4 +3376,4 @@ String toString(uint32_t input) { return result; } -#endif \ No newline at end of file +#endif diff --git a/platformio.ini b/platformio.ini index 4d85dab..7e611a3 100644 --- a/platformio.ini +++ b/platformio.ini @@ -113,7 +113,7 @@ extra_configs = [libraries] arduinojson = ArduinoJson@6.18.3 arduinolog = https://github.com/1technophile/Arduino-Log.git#f634509 -pubsubclient = PubSubClient@2.8 +picomqtt = PicoMQTT@1.1.1 rc-switch = https://github.com/1technophile/rc-switch.git#98537e9 newremoteswitch = https://github.com/1technophile/NewRemoteSwitch.git#8eb980e ble = https://github.com/h2zero/NimBLE-Arduino.git#1.4.1 @@ -161,7 +161,7 @@ rn8209 = https://github.com/theengs/RN8209C-SDK.git#arduino [env] framework = arduino lib_deps = - ${libraries.pubsubclient} + ${libraries.picomqtt} ${libraries.arduinojson} ${libraries.arduinolog} build_flags = @@ -174,7 +174,7 @@ monitor_speed = 115200 [com] esp8266_platform = espressif8266@4.1.0 esp32_platform = espressif32@6.1.0 -esp32_solo_platform = https://github.com/tasmota/platform-espressif32/releases/download/v.2.0.5/platform-espressif32-v.2.0.5.zip +esp32_solo_platform = https://github.com/tasmota/platform-espressif32/releases/download/2023.02.00/platform-espressif32.zip atmelavr_platform = atmelavr@3.3.0 [com-esp] ; Used by all ESP8266/ESP8285 based builds @@ -219,4 +219,4 @@ lib_deps = ${libraries.ethernet} build_flags = ${env.build_flags} - '-DsimpleReceiving=false' \ No newline at end of file + '-DsimpleReceiving=false'