/* OpenMQTTGateway - ESP8266 or Arduino program for home automation Act as a gateway between your 433mhz, infrared IR, BLE, LoRa signal and one interface like an MQTT broker Send and receiving command by MQTT This program enables to: - receive MQTT data from a topic and send signal (RF, IR, BLE, GSM) corresponding to the received MQTT data - publish MQTT data to a different topic related to received signals (RF, IR, BLE, GSM) Copyright: (c)Florian ROBERT This file is part of OpenMQTTGateway. OpenMQTTGateway is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. OpenMQTTGateway is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "User_config.h" #define ENV_NAME "Wemos_D1_Mini" enum GatewayState { WAITING_ONBOARDING, ONBOARDING, OFFLINE, NTWK_CONNECTED, BROKER_CONNECTED, PROCESSING, NTWK_DISCONNECTED, BROKER_DISCONNECTED, LOCAL_OTA_IN_PROGRESS, REMOTE_OTA_IN_PROGRESS, SLEEPING, ERROR }; GatewayState gatewayState = GatewayState::WAITING_ONBOARDING; // Macros and structure to enable the duplicates removing on the following gateways #if defined(ZgatewayRF) || defined(ZgatewayRF_RHASK) || defined(ZgatewayIR) || defined(ZgatewaySRFB) || defined(ZgatewayWeatherStation) || defined(ZgatewayRTL_433) // array to store previous received RFs, IRs codes and their timestamps struct ReceivedSignal { uint64_t value; uint32_t time; }; ReceivedSignal receivedSignal[] = {{0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}}; # define struct_size (sizeof(receivedSignal) / sizeof(ReceivedSignal)) #endif //Time used to wait for an interval before measures unsigned long timer_sys_measures = 0; // Time used to wait before system checkings unsigned long timer_sys_checks = 0; // First Start of offline mode modules bool firstStart = true; #define ARDUINOJSON_USE_LONG_LONG 1 #define ARDUINOJSON_ENABLE_STD_STRING 1 #include int queueLength = -1; // We want to give one cycle of the loop before starting modules that are big msgs producers (example BT), to avoid queue overloading unsigned long queueLengthSum = 0; unsigned long blockedMessages = 0; unsigned long receivedMessages = 0; int maxQueueLength = 0; #ifndef QueueSize # define QueueSize 18 #endif /** * Deep-sleep for the ESP8266 & ESP32 we need some form of indicator that we have posted the measurements and am ready to deep sleep. * Set this to true in the sensor code after publishing the measurement. */ bool ready_to_sleep = false; #include #include #include #include #include "LEDManager.h" #include "TheengsUtils.h" LEDManager ledManager; struct JsonBundle { StaticJsonDocument doc; }; std::queue jsonQueue; #ifdef ESP32 # include // Mutex to protect the queue SemaphoreHandle_t xQueueMutex; // Mutex to protect mqtt publish SemaphoreHandle_t xMqttMutex; #endif StaticJsonDocument modulesBuffer; JsonArray modules = modulesBuffer.to(); bool ethConnected = false; #ifndef ZgatewayGFSunInverter // Arduino IDE compiles, it automatically creates all the header declarations for all the functions you have in your *.ino file. // Unfortunately it ignores #if directives. // This is a simple workaround for this problem. struct GfSun2000Data {}; #endif // Modules config inclusion #if defined(ZwebUI) && defined(ESP32) # include "config_WebUI.h" #endif #if defined(ZgatewayRF) || defined(ZgatewayRF2) || defined(ZgatewayPilight) || defined(ZactuatorSomfy) || defined(ZgatewayRTL_433) # include "config_RF.h" #endif #ifdef ZgatewayRF_RHASK #include "config_RF_RHASK.h" #endif #ifdef ZgatewayWeatherStation # include "config_WeatherStation.h" #endif #ifdef ZgatewayGFSunInverter # include "config_GFSunInverter.h" #endif #ifdef ZgatewayLORA # include "config_LORA.h" #endif #ifdef ZgatewaySRFB # include "config_SRFB.h" #endif #ifdef ZgatewayBT # include "config_BT.h" #endif #ifdef ZgatewayIR # include "config_IR.h" #endif #ifdef Zgateway2G # include "config_2G.h" #endif #ifdef ZactuatorONOFF # include "config_ONOFF.h" #endif #ifdef ZsensorINA226 # include "config_INA226.h" #endif #ifdef ZsensorHCSR501 # include "config_HCSR501.h" #endif #ifdef ZsensorADC # include "config_ADC.h" #endif #ifdef ZsensorBH1750 # include "config_BH1750.h" #endif #ifdef ZsensorMQ2 # include "config_MQ2.h" #endif #ifdef ZsensorTEMT6000 # include "config_TEMT6000.h" #endif #ifdef ZsensorTSL2561 # include "config_TSL2561.h" #endif #ifdef ZsensorBME280 # include "config_BME280.h" #endif #ifdef ZsensorHTU21 # include "config_HTU21.h" #endif #ifdef ZsensorLM75 # include "config_LM75.h" #endif #ifdef ZsensorAHTx0 # include "config_AHTx0.h" #endif #ifdef ZsensorRN8209 # include "config_RN8209.h" #endif #ifdef ZsensorHCSR04 # include "config_HCSR04.h" #endif #ifdef ZsensorC37_YL83_HMRD # include "config_C37_YL83_HMRD.h" #endif #ifdef ZsensorDHT # include "config_DHT.h" #endif #ifdef ZsensorSHTC3 # include "config_SHTC3.h" #endif #ifdef ZsensorDS1820 # include "config_DS1820.h" #endif #ifdef ZgatewayRFM69 # include "config_RFM69.h" #endif #ifdef ZsensorGPIOInput # include "config_GPIOInput.h" #endif #ifdef ZsensorGPIOKeyCode # include "config_GPIOKeyCode.h" #endif #ifdef ZsensorTouch # include "config_Touch.h" #endif #ifdef ZmqttDiscovery # include "config_mqttDiscovery.h" #endif #ifdef ZactuatorFASTLED # include "config_FASTLED.h" #endif #ifdef ZactuatorPWM # include "config_PWM.h" #endif #ifdef ZactuatorSomfy # include "config_Somfy.h" #endif #if defined(ZboardM5STICKC) || defined(ZboardM5STICKCP) || defined(ZboardM5STACK) || defined(ZboardM5TOUGH) # include "config_M5.h" #endif #if defined(ZdisplaySSD1306) # include "config_SSD1306.h" #endif #if defined(ZgatewaySERIAL) # include "config_SERIAL.h" #endif /*------------------------------------------------------------------------*/ void setupTLS(int index = CNT_DEFAULT_INDEX); char ota_pass[parameters_size] = gw_password; #ifdef USE_MAC_AS_GATEWAY_NAME # undef WifiManager_ssid # undef ota_hostname # define MAC_NAME_MAX_LEN 30 char WifiManager_ssid[MAC_NAME_MAX_LEN]; char ota_hostname[MAC_NAME_MAX_LEN]; #endif int failure_number_ntwk = 0; // number of failure connecting to network int failure_number_mqtt = 0; // number of failure connecting to MQTT static unsigned long last_ota_activity_millis = 0; // Global struct to store live SYS configuration data SYSConfig_s SYSConfig; bool failSafeMode = false; bool ProcessLock = true; // Process lock when we want to use a critical function like OTA for example static bool mqttSetupPending = true; static int cnt_index = CNT_DEFAULT_INDEX; #ifdef ESP32 # include # include # include # include # include # include bool BTProcessLock = true; // Process lock when we want to use a critical function like OTA for example, at start to true so as to wait for critical functions to be performed before BLE start # if !defined(NO_INT_TEMP_READING) // ESP32 internal temperature reading # include # include "rom/ets_sys.h" # include "soc/rtc_cntl_reg.h" # include "soc/sens_reg.h" # endif # ifdef ESP32_ETHERNET # include void WiFiEvent(WiFiEvent_t event); # endif # include # include WiFiMulti wifiMulti; # include # ifdef MDNS_SD # include # endif #elif defined(ESP8266) # include # include # include # include # include # include # include X509List caCert; # if MQTT_SECURE_SIGNED_CLIENT X509List* pClCert = nullptr; PrivateKey* pClKey = nullptr; # endif ESP8266WiFiMulti wifiMulti; # ifdef MDNS_SD # include # endif #else # include #endif void handle_autodiscovery() { #ifdef ZmqttDiscovery static bool connectedOnce = false; const unsigned long now = millis(); // at first connection we publish the discovery payloads // or, when we have just re-connected (only when discovery_republish_on_reconnect is enabled) const bool publishDiscovery = SYSConfig.discovery && (!connectedOnce || discovery_republish_on_reconnect); if (publishDiscovery) { pubMqttDiscovery(); # ifdef ZgatewayLORA launchLORADiscovery(true); # endif # ifdef ZgatewayBT launchBTDiscovery(true); # endif # ifdef ZgatewayRTL_433 launchRTL_433Discovery(true); # endif } connectedOnce = true; #endif } #if MQTT_BROKER_MODE class MQTTServer : public PicoMQTT::Server { public: size_t get_client_count() const { return clients.size(); } bool connected() const { return !clients.empty(); } protected: virtual void on_subscribe(const char* client_id, const char* topic) override { // Whenever a client subscribes successfully to some topic, see if this is likely a subscription to a // autodiscovery topic. If it is, fire handle_autodiscovery(). const String pattern(topic); const bool is_autodiscovery_subscription = (pattern == "#") || (pattern.startsWith(String(discovery_prefix) + "/")); if (is_autodiscovery_subscription) handle_autodiscovery(); } }; std::unique_ptr mqtt; #else std::unique_ptr< ::Client> eClient; std::unique_ptr mqtt; #endif 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); template void Config_update(JsonObject& data, const char* key, T& var) { if (data.containsKey(key)) { if (var != data[key].as()) { var = data[key].as(); Log.notice(F("Config %s changed to: %T" CR), key, data[key].as()); } else { Log.notice(F("Config %s unchanged, currently: %T" CR), key, data[key].as()); } } } /* * Dispatch json messages towards the communication layer * */ bool jsonDispatch(JsonObject& data) { bool res = false; if (data.containsKey("origin") || data.containsKey("topic")) { GatewayState previousGatewayState = gatewayState; gatewayState = GatewayState::PROCESSING; #if message_UTCtimestamp == true data["UTCtime"] = TheengsUtils::UTCtimestamp(); #endif #if message_unixtimestamp == true data["unixtime"] = TheengsUtils::unixtimestamp(); #endif if (data.containsKey("origin")) { pubWebUI((char*)data["origin"].as(), data); } if (SYSConfig.mqtt && !SYSConfig.offline) { res = pub(data); } #ifdef ZgatewaySERIAL if (SYSConfig.serial) { char jsonStr[JSON_MSG_BUFFER_MAX]; serializeJson(data, jsonStr); receivingDATA("", jsonStr); res = true; // Return the state from receivingDATA } #endif gatewayState = previousGatewayState; // restore the previous state } else { Log.error(F("No origin or topic in JSON filtered" CR)); gatewayState = GatewayState::ERROR; } return res; } // Add a document to the queue boolean enqueueJsonObject(const StaticJsonDocument& jsonDoc, int timeout) { receivedMessages++; if (jsonDoc.size() == 0) { Log.error(F("Empty JSON, skipping" CR)); gatewayState = GatewayState::ERROR; return true; } if (queueLength >= QueueSize) { Log.warning(F("%d Doc(s) in queue, doc blocked" CR), queueLength); blockedMessages++; return false; } Log.trace(F("Enqueue JSON" CR)); std::string jsonString; serializeJson(jsonDoc, jsonString); #ifdef ESP32 // Semaphore check before enqueueing a document if (xSemaphoreTake(xQueueMutex, pdMS_TO_TICKS(timeout)) == pdFALSE) { Log.error(F("xQueueMutex not taken" CR)); gatewayState = GatewayState::ERROR; blockedMessages++; return false; } #endif jsonQueue.push(jsonString); #ifdef ESP32 xSemaphoreGive(xQueueMutex); #endif Log.trace(F("Queue length: %d" CR), jsonQueue.size()); return true; } // Semaphore check before enqueueing a document with default timeout QueueSemaphoreTimeOutLoop bool enqueueJsonObject(const StaticJsonDocument& jsonDoc) { return enqueueJsonObject(jsonDoc, QueueSemaphoreTimeOutLoop); } #ifdef ESP32 # include "mbedtls/sha256.h" std::string generateHash(const std::string& input) { unsigned char hash[32]; mbedtls_sha256((unsigned char*)input.c_str(), input.length(), hash, 0); char hashString[65]; // Room for null terminator for (int i = 0; i < 32; ++i) { sprintf(&hashString[i * 2], "%02x", hash[i]); } return std::string(hashString); } #else std::string generateHash(const std::string& input) { return "Not implemented for ESP8266"; } #endif /* * Add the jsonObject id as a topic to the jsonObject origin * */ void buildTopicFromId(JsonObject& Jsondata, const char* origin) { if (!Jsondata.containsKey("id")) { Log.error(F("No id in Jsondata" CR)); gatewayState = GatewayState::ERROR; return; } std::string topic = Jsondata["id"].as(); // Replace ":" in topic size_t pos = topic.find(":"); while (pos != std::string::npos) { topic.erase(pos, 1); pos = topic.find(":", pos); } #ifdef ZgatewayBT if (BTConfig.pubBeaconUuidForTopic && !BTConfig.extDecoderEnable && Jsondata.containsKey("model_id") && Jsondata["model_id"].as() == "IBEACON") { if (Jsondata.containsKey("uuid")) { topic = Jsondata["uuid"].as(); } else { Log.error(F("No uuid in Jsondata" CR)); gatewayState = GatewayState::ERROR; } } if (BTConfig.extDecoderEnable && !Jsondata.containsKey("model")) topic = BTConfig.extDecoderTopic.c_str(); #endif std::string subjectStr(origin); topic = subjectStr + "/" + topic; Jsondata["origin"] = topic; Log.trace(F("Origin: %s" CR), Jsondata["origin"].as()); } // Empty the documents queue void emptyQueue() { queueLength = jsonQueue.size(); if (queueLength > maxQueueLength) { maxQueueLength = queueLength; } if (queueLength <= 0) { return; } Log.trace(F("Dequeue JSON" CR)); DynamicJsonDocument jsonBuffer(JSON_MSG_BUFFER_MAX); JsonObject obj = jsonBuffer.to(); #ifdef ESP32 if (xSemaphoreTake(xQueueMutex, pdMS_TO_TICKS(QueueSemaphoreTimeOutTask)) == pdFALSE) { Log.error(F("xQueueMutex not taken" CR)); gatewayState = GatewayState::ERROR; return; } #endif auto error = deserializeJson(jsonBuffer, jsonQueue.front()); jsonQueue.pop(); #ifdef ESP32 xSemaphoreGive(xQueueMutex); #endif if (error) { Log.error(F("deserialize jsonQueue.front() failed: %s, buffer capacity: %u" CR), error.c_str(), jsonBuffer.capacity()); gatewayState = GatewayState::ERROR; } else { if (jsonDispatch(obj)) queueLengthSum++; } } /** * @brief Publish the payload on default MQTT topic. * * @param topicori suffix to add on default MQTT Topic * @param payload the message to sends * @param retainFlag true if you what a retain */ bool pub(const char* topicori, const char* payload, bool retainFlag) { String topic = String(mqtt_topic) + String(gateway_name) + String(topicori); return pubMQTT(topic.c_str(), payload, retainFlag); } /** * @brief Publish the payload on default MQTT topic * * @param topicori suffix to add on default MQTT Topic * @param data The Json Object that represents the message */ bool pub(JsonObject& data) { bool res = false; bool ret = sensor_Retain; if (data.containsKey("retain") && data["retain"].is()) { ret = data["retain"]; data.remove("retain"); } if (data.size() == 0) { Log.error(F("Empty JSON, not published" CR)); gatewayState = GatewayState::ERROR; return res; } String topic; if (data.containsKey("origin") && data["origin"].is()) { topic = String(mqtt_topic) + String(gateway_name) + String(data["origin"].as()); data.remove("origin"); } else if (data.containsKey("topic") && data["topic"].is()) { topic = data["topic"].as(); if (data.containsKey("info_topic") && data["info_topic"].is()) { // Sometimes it is necessary to provide information about the publishing topic, not just use it. // This is the case, for example, for the RF2MQTT device trigger announcement message where the // temporary variable info_topic provides information about the topic that will be used to publish the message, // and it can be different of current message topic (This is a clever pun, I hope it's clear). data["topic"].set(data["info_topic"]); data.remove("info_topic"); } else { data.remove("topic"); } } else { Log.error(F("No topic or origin in JSON, not published" CR)); gatewayState = GatewayState::ERROR; return res; } #if valueAsATopic # ifdef ZgatewayPilight String value = data["value"]; String protocol = data["protocol"]; if (value != "null" && value != 0) { topic = topic + "/" + protocol + "/" + value; } # else uint64_t value = data["value"]; if (value != 0) { topic = topic + "/" + TheengsUtils::toString(value); } # endif #endif #if jsonPublishing String dataAsString = ""; serializeJson(data, dataAsString); res = pubMQTT(topic.c_str(), dataAsString.c_str(), ret); #endif #if simplePublishing Log.trace(F("simplePub - ON" CR)); // Loop through all the key-value pairs in obj for (JsonPair p : data) { # if defined(ESP8266) yield(); # endif if (p.value().is() && strcmp(p.key().c_str(), "rssi") != 0) { //test rssi , bypass solution due to the fact that a int is considered as an uint64_t if (strcmp(p.key().c_str(), "value") == 0) { // if data is a value we don't integrate the name into the topic res = pubMQTT(topic, p.value().as()); } else { // if data is not a value we integrate the name into the topic res = pubMQTT(topic + "/" + String(p.key().c_str()), p.value().as()); } } else if (p.value().is()) { res = pubMQTT(topic + "/" + String(p.key().c_str()), p.value().as()); } else if (p.value().is()) { res = pubMQTT(topic + "/" + String(p.key().c_str()), p.value().as()); } else if (p.value().is()) { res = pubMQTT(topic + "/" + String(p.key().c_str()), p.value().as()); } } #endif return res; } /** * @brief Publish the payload on default MQTT topic * * @param topicori suffix to add on default MQTT Topic * @param payload the message to sends */ bool pub(const char* topicori, const char* payload) { String topic = String(mqtt_topic) + String(gateway_name) + String(topicori); return pubMQTT(topic, payload); } /** * @brief Low level MQTT functions without retain * * @param topic the topic * @param payload the payload */ bool pubMQTT(const char* topic, const char* payload) { return pubMQTT(topic, payload, sensor_Retain); } /** * @brief Very Low level MQTT functions with retain Flag * * @param topic the topic * @param payload the payload * @param retainFlag true if retain the retain Flag */ bool pubMQTT(const char* topic, const char* payload, bool retainFlag) { bool res = false; if (SYSConfig.mqtt && !SYSConfig.offline) { #ifdef ESP32 if (xSemaphoreTake(xMqttMutex, pdMS_TO_TICKS(QueueSemaphoreTimeOutTask)) == pdFALSE) { Log.error(F("xMqttMutex not taken" CR)); gatewayState = GatewayState::ERROR; return res; } #endif if (mqtt && mqtt->connected()) { Log.notice(F("[ OMG->MQTT ] topic: %s msg: %s " CR), topic, payload); res = mqtt->publish(topic, payload, 0, retainFlag); } else { Log.warning(F("MQTT not connected, aborting the publication" CR)); } #ifdef ESP32 xSemaphoreGive(xMqttMutex); #endif } else { Log.notice(F("[ OMG->MQTT deactivated or offline] topic: %s msg: %s " CR), topic, payload); } return res; } bool pubMQTT(String topic, const char* payload) { return pubMQTT(topic.c_str(), payload); } bool pubMQTT(const char* topic, unsigned long payload) { char val[11]; sprintf(val, "%lu", payload); return pubMQTT(topic, val); } bool pubMQTT(const char* topic, unsigned long long payload) { char val[21]; sprintf(val, "%llu", payload); return pubMQTT(topic, val); } bool pubMQTT(const char* topic, String payload) { return pubMQTT(topic, payload.c_str()); } bool pubMQTT(String topic, String payload) { return pubMQTT(topic.c_str(), payload.c_str()); } bool pubMQTT(String topic, int payload) { char val[12]; sprintf(val, "%d", payload); return pubMQTT(topic.c_str(), val); } bool pubMQTT(String topic, unsigned long long payload) { char val[21]; sprintf(val, "%llu", payload); return pubMQTT(topic.c_str(), val); } bool pubMQTT(String topic, float payload) { char val[12]; dtostrf(payload, 3, 1, val); return pubMQTT(topic.c_str(), val); } bool pubMQTT(const char* topic, float payload) { char val[12]; dtostrf(payload, 3, 1, val); return pubMQTT(topic, val); } bool pubMQTT(const char* topic, int payload) { char val[12]; sprintf(val, "%d", payload); return pubMQTT(topic, val); } bool pubMQTT(const char* topic, unsigned int payload) { char val[12]; sprintf(val, "%u", payload); return pubMQTT(topic, val); } bool pubMQTT(const char* topic, long payload) { char val[11]; sprintf(val, "%ld", payload); return pubMQTT(topic, val); } bool pubMQTT(const char* topic, double payload) { char val[16]; sprintf(val, "%f", payload); return pubMQTT(topic, val); } bool pubMQTT(String topic, unsigned long payload) { char val[11]; sprintf(val, "%lu", payload); return pubMQTT(topic.c_str(), val); } void delayWithOTA(long waitMillis) { long waitStep = 100; for (long waitedMillis = 0; waitedMillis < waitMillis; waitedMillis += waitStep) { #ifndef ESPWifiManualSetup checkButton(); // check if a reset of wifi/mqtt settings is asked #endif ArduinoOTA.handle(); #if defined(ZwebUI) && defined(ESP32) WebUILoop(); #endif #ifdef ESP32 //esp_task_wdt_reset(); #endif delay(waitStep); } } void SYSConfig_init() { SYSConfig.mqtt = DEFAULT_MQTT; SYSConfig.serial = DEFAULT_SERIAL; SYSConfig.offline = DEFAULT_OFFLINE; #if USE_BLUFI SYSConfig.blufi = DEFAULT_BLUFI; #endif #ifdef ZmqttDiscovery SYSConfig.discovery = DEFAULT_DISCOVERY; SYSConfig.ohdiscovery = OpenHABDiscovery; #endif #ifdef LED_ADDRESSABLE SYSConfig.rgbbrightness = DEFAULT_ADJ_BRIGHTNESS; #endif SYSConfig.powerMode = DEFAULT_LOW_POWER_MODE; } void SYSConfig_fromJson(JsonObject& SYSdata) { Config_update(SYSdata, "mqtt", SYSConfig.mqtt); Config_update(SYSdata, "serial", SYSConfig.serial); Config_update(SYSdata, "offline", SYSConfig.offline); #if USE_BLUFI Config_update(SYSdata, "blufi", SYSConfig.blufi); #endif #ifdef ZmqttDiscovery Config_update(SYSdata, "disc", SYSConfig.discovery); Config_update(SYSdata, "ohdisc", SYSConfig.ohdiscovery); #endif #ifdef LED_ADDRESSABLE Config_update(SYSdata, "rgbb", SYSConfig.rgbbrightness); #endif Config_update(SYSdata, "powermode", SYSConfig.powerMode); } #ifdef ESP32 void SYSConfig_save() { StaticJsonDocument jsonBuffer; JsonObject SYSdata = jsonBuffer.to(); SYSdata["mqtt"] = SYSConfig.mqtt; SYSdata["serial"] = SYSConfig.serial; SYSdata["offline"] = SYSConfig.offline; SYSdata["powermode"] = SYSConfig.powerMode; # if USE_BLUFI SYSdata["blufi"] = SYSConfig.blufi; # endif # ifdef ZmqttDiscovery SYSdata["disc"] = SYSConfig.discovery; SYSdata["ohdisc"] = SYSConfig.ohdiscovery; # endif # ifdef LED_ADDRESSABLE SYSdata["rgbb"] = SYSConfig.rgbbrightness; # endif String conf = ""; serializeJson(jsonBuffer, conf); preferences.begin(Gateway_Short_Name, false); int result = preferences.putString("SYSConfig", conf); preferences.end(); Log.notice(F("SYS Config_save: %s, result: %d" CR), conf.c_str(), result); } #else // Function not available for ESP8266 void SYSConfig_save() {} #endif bool cmpToMainTopic(const char* topicOri, const char* toAdd) { if (strcmp(topicOri, toAdd) == 0) return true; // Is string "" equal to ""? // Compare first part with first chunk if (strncmp(topicOri, mqtt_topic, strlen(mqtt_topic)) != 0) return false; // Move pointer of sizeof chunk topicOri += strlen(mqtt_topic); // And so on... if (strncmp(topicOri, gateway_name, strlen(gateway_name)) != 0) return false; topicOri += strlen(gateway_name); if (strncmp(topicOri, toAdd, strlen(toAdd)) != 0) return false; return true; } #if defined(ESP32) void SYSConfig_load() { StaticJsonDocument jsonBuffer; preferences.begin(Gateway_Short_Name, true); if (preferences.isKey("SYSConfig")) { auto error = deserializeJson(jsonBuffer, preferences.getString("SYSConfig", "{}")); preferences.end(); if (error) { Log.error(F("SYS config deserialization failed: %s, buffer capacity: %u" CR), error.c_str(), jsonBuffer.capacity()); gatewayState = GatewayState::ERROR; return; } if (jsonBuffer.isNull()) { Log.warning(F("SYS config is null" CR)); return; } JsonObject jo = jsonBuffer.as(); SYSConfig_fromJson(jo); Log.notice(F("SYS config loaded" CR)); } else { preferences.end(); Log.notice(F("SYS config not found" CR)); } } #else // Function not available for ESP8266 void SYSConfig_load() {} #endif #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.warning(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.warning(F("Several MQTT servers found, please deactivate mDNS and set your default server" CR)); } } return {"", 0}; } #endif #if MQTT_BROKER_MODE void setupMQTT() { Log.notice(F("Reconfiguring MQTT broker..." CR)); mqtt.reset(new MQTTServer()); mqtt->begin(); # ifdef ZgatewayBT BTProcessLock = !BTConfig.enabled; // Release BLE processes at start if enabled # endif } #else struct ss_cnt_parameters_backup { ss_cnt_parameters parameters; int cnt_index; bool saveOnSuccess; }; 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 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.mqtt) { // 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.mqtt = false; } } # endif # ifdef ZgatewayBT BTProcessLock = !BTConfig.enabled; // Release BLE processes at start if enabled # endif ProcessLock = false; // Release the loop process displayPrint("MQTT connected"); Log.notice(F("Connected to broker" CR)); gatewayState = GatewayState::BROKER_CONNECTED; failure_number_mqtt = 0; // Once connected, publish an announcement... pub(will_Topic, Gateway_AnnouncementMsg, will_Retain); if (cnt_parameters_backup) { // this was the first attempt to connect to a new server and it succeeded Log.notice(F("MQTT connection parameters %d successful" CR), cnt_index); 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 cnt_parameters_backup.reset(); ESPRestart(7); } handle_autodiscovery(); }; mqtt->connection_failure_callback = [] { if ((WiFi.status() != WL_CONNECTED && ethConnected == false) || SYSConfig.offline) { // No network connection or offline, ignore this failure return; } failure_number_mqtt++; // we count the failure gatewayState = GatewayState::BROKER_DISCONNECTED; Log.warning(F("failure_number_mqtt: %d" CR), failure_number_mqtt); const auto& parameters = cnt_parameters_array[cnt_index]; if (parameters.isConnectionSecure) { WiFiClientSecure* client = static_cast(eClient.get()); # if defined(ESP32) Log.warning(F("failed, ssl error code=%d" CR), client->lastError(nullptr, 0)); # elif defined(ESP8266) 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)); gatewayState = GatewayState::ERROR; 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; } delayWithOTA(10000); if (failure_number_mqtt > maxRetryWatchDog) { # ifndef ESPWifiManualSetup // Look for the next valid connection for (int i = 0; i < cnt_parameters_array_size; i++) { cnt_index++; if (cnt_index >= cnt_parameters_array_size) { cnt_index = 0; } if (cnt_parameters_array[cnt_index].validConnection) { Log.notice(F("Connection %d valid, switching" CR), cnt_index); saveConfig(); break; } else { Log.notice(F("Connection %d not valid" CR), cnt_index); } } # endif unsigned long millis_since_last_ota; while ( // When // ...incomplete OTA in progress (last_ota_activity_millis != 0) // ...AND last OTA activity fairly recently && ((millis_since_last_ota = millis() - last_ota_activity_millis) < ota_timeout_millis)) { // ... We consider that OTA might be still active, and we sleep for a while, and giving // OTA chance to proceed (ArduinoOTA.handle()) Log.warning(F("OTA might be still active (activity %d ms ago)" CR), millis_since_last_ota); ArduinoOTA.handle(); delay(100); } ESPRestart(1); } }; mqtt->subscribe(String(mqtt_topic) + gateway_name + subjectMQTTtoX, receivingDATA, mqtt_max_payload_size); # 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, receivingDATA, 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, receivingDATA, mqtt_max_payload_size); # endif # if enableMultiGTWSync mqtt->subscribe(String(mqtt_topic) + subjectTrackerSync, receivingDATA, mqtt_max_payload_size); # endif mqtt->begin(); } #endif #if defined(ESP32) && (defined(WifiGMode) || defined(WifiPower)) void setESPWifiProtocolTxPower() { //Reduce WiFi interference when using ESP32 using custom WiFi mode and tx power //https://github.com/espressif/arduino-esp32/search?q=WIFI_PROTOCOL_11G //https://www.letscontrolit.com/forum/viewtopic.php?t=671&start=20 # if WifiGMode == true if (esp_wifi_set_protocol(WIFI_IF_STA, WIFI_PROTOCOL_11B | WIFI_PROTOCOL_11G) != ESP_OK) { Log.error(F("Failed to change WifiMode." CR)); gatewayState = GatewayState::ERROR; } # endif # if WifiGMode == false if (esp_wifi_set_protocol(WIFI_IF_STA, WIFI_PROTOCOL_11B | WIFI_PROTOCOL_11G | WIFI_PROTOCOL_11N) != ESP_OK) { Log.error(F("Failed to change WifiMode." CR)); gatewayState = GatewayState::ERROR; } # endif uint8_t getprotocol; esp_err_t err; err = esp_wifi_get_protocol(WIFI_IF_STA, &getprotocol); if (err != ESP_OK) { Log.notice(F("Could not get protocol!" CR)); } if (getprotocol & WIFI_PROTOCOL_11N) { Log.notice(F("WiFi_Protocol_11n" CR)); } if (getprotocol & WIFI_PROTOCOL_11G) { Log.notice(F("WiFi_Protocol_11g" CR)); } if (getprotocol & WIFI_PROTOCOL_11B) { Log.notice(F("WiFi_Protocol_11b" CR)); } # ifdef WifiPower Log.notice(F("Requested WiFi power level: %i" CR), WifiPower); WiFi.setTxPower(WifiPower); # endif Log.notice(F("Operating WiFi power level: %i" CR), WiFi.getTxPower()); } #endif #if defined(ESP8266) && (defined(WifiGMode) || defined(WifiPower)) void setESPWifiProtocolTxPower() { # if WifiGMode == true if (!wifi_set_phy_mode(PHY_MODE_11G)) { Log.error(F("Failed to change WifiMode." CR)); gatewayState = GatewayState::ERROR; } # endif # if WifiGMode == false if (!wifi_set_phy_mode(PHY_MODE_11N)) { Log.error(F("Failed to change WifiMode." CR)); gatewayState = GatewayState::ERROR; } # endif phy_mode_t getprotocol = wifi_get_phy_mode(); if (getprotocol == PHY_MODE_11N) { Log.notice(F("WiFi_Protocol_11n" CR)); } if (getprotocol == PHY_MODE_11G) { Log.notice(F("WiFi_Protocol_11g" CR)); } if (getprotocol == PHY_MODE_11B) { Log.notice(F("WiFi_Protocol_11b" CR)); } # ifdef WifiPower Log.notice(F("Requested WiFi power level: %i dBm" CR), WifiPower); int i_dBm = int(WifiPower * 4.0f); // i_dBm 82 == 20.5 dBm if (i_dBm > 82) { i_dBm = 82; } else if (i_dBm < 0) { i_dBm = 0; } system_phy_set_max_tpw((uint8_t)i_dBm); # endif } #endif #ifdef ESP32 void updateAndHandleLEDsTask(void* pvParameters) { for (;;) { updateAndHandleLEDsTask(); vTaskDelay(pdMS_TO_TICKS(100)); } } #endif void updateAndHandleLEDsTask() { static GatewayState previousGatewayState; if (previousGatewayState != gatewayState) { #ifdef LED_POWER ledManager.setMode(STRIP_POWER, LED_POWER, LEDManager::STATIC, LED_POWER_COLOR, -1); #endif switch (gatewayState) { case PROCESSING: #ifdef LED_PROCESSING ledManager.setMode(STRIP_PROCESSING, LED_PROCESSING, LEDManager::BLINK, LED_PROCESSING_COLOR, 3); #endif break; case WAITING_ONBOARDING: #ifdef LED_BROKER ledManager.setMode(STRIP_BROKER, LED_BROKER, LEDManager::STATIC, LED_WAITING_ONBOARD_COLOR, -1); #endif break; case ONBOARDING: #ifdef LED_BROKER ledManager.setMode(STRIP_BROKER, LED_BROKER, LEDManager::STATIC, LED_ONBOARD_COLOR, -1); #endif break; case NTWK_CONNECTED: #ifdef LED_NETWORK ledManager.setMode(STRIP_NETWORK, LED_NETWORK, LEDManager::STATIC, LED_NETWORK_OK_COLOR, -1); #endif break; case BROKER_CONNECTED: #ifdef LED_BROKER ledManager.setMode(STRIP_BROKER, LED_BROKER, LEDManager::STATIC, LED_BROKER_OK_COLOR, -1); #endif break; case NTWK_DISCONNECTED: #ifdef LED_NETWORK ledManager.setMode(STRIP_NETWORK, LED_NETWORK, LEDManager::BLINK, LED_NETWORK_ERROR_COLOR, -1); #endif break; case BROKER_DISCONNECTED: #ifdef LED_BROKER ledManager.setMode(STRIP_BROKER, LED_BROKER, LEDManager::BLINK, LED_BROKER_ERROR_COLOR, -1); #endif break; case SLEEPING: ledManager.setMode(-1, -1, LEDManager::OFF, 0, -1); break; case OFFLINE: #ifdef LED_NETWORK ledManager.setMode(STRIP_NETWORK, LED_NETWORK, LEDManager::BLINK, LED_OFFLINE_COLOR, -1); #endif break; case LOCAL_OTA_IN_PROGRESS: #ifdef LED_PROCESSING ledManager.setMode(STRIP_PROCESSING, LED_PROCESSING, LEDManager::BLINK, LED_OTA_LOCAL_COLOR, -1); #endif break; case REMOTE_OTA_IN_PROGRESS: #ifdef LED_PROCESSING ledManager.setMode(STRIP_PROCESSING, LED_PROCESSING, LEDManager::BLINK, LED_OTA_REMOTE_COLOR, -1); #endif break; case ERROR: #ifdef LED_ERROR ledManager.setMode(STRIP_ERROR, LED_ERROR, LEDManager::BLINK, LED_ERROR_COLOR, 3); #endif break; default: break; } previousGatewayState = gatewayState; } ledManager.update(); } void setup() { //Launch serial for debugging purposes Serial.begin(SERIAL_BAUD); Log.begin(LOG_LEVEL, &Serial); Log.notice(F(CR "************* WELCOME TO OpenMQTTGateway **************" CR)); #if defined(TRIGGER_GPIO) && !defined(ESPWifiManualSetup) pinMode(TRIGGER_GPIO, INPUT_PULLUP); checkButton(); #endif delay(100); //give time to start the flash and avoid issue when reading the preferences SYSConfig_init(); SYSConfig_load(); if (SYSConfig.offline) { gatewayState = GatewayState::OFFLINE; } #ifdef LED_ADDRESSABLE # ifdef LED_ADDRESSABLE_PIN1 ledManager.addLEDStrip(LED_ADDRESSABLE_PIN1, LED_ADDRESSABLE_NUM); # endif # ifdef LED_ADDRESSABLE_PIN2 ledManager.addLEDStrip(LED_ADDRESSABLE_PIN2, LED_ADDRESSABLE_NUM); # endif # ifdef LED_ADDRESSABLE_PIN3 ledManager.addLEDStrip(LED_ADDRESSABLE_PIN3, LED_ADDRESSABLE_NUM); # endif ledManager.setBrightness(SYSConfig.rgbbrightness); #elif LED_PIN ledManager.addLEDStrip(LED_PIN, 1); #endif #ifdef ESP8266 # ifndef ZgatewaySRFB // if we are not in sonoff rf bridge case we apply the ESP8266 GPIO optimization Serial.end(); Serial.begin(SERIAL_BAUD, SERIAL_8N1, SERIAL_TX_ONLY); // enable on ESP8266 to free some pin # endif #elif ESP32 xTaskCreate(updateAndHandleLEDsTask, "updateAndHandleLEDsTask", 2500, NULL, 1, NULL); xQueueMutex = xSemaphoreCreateMutex(); xMqttMutex = xSemaphoreCreateMutex(); # if defined(ZboardM5STICKC) || defined(ZboardM5STICKCP) || defined(ZboardM5STACK) || defined(ZboardM5TOUGH) setupM5(); # endif # if defined(ZdisplaySSD1306) setupSSD1306(); modules.add(ZdisplaySSD1306); # endif #endif Log.notice(F("OpenMQTTGateway Version: " OMG_VERSION CR)); #ifdef ESP32_EXT0_WAKE_PIN Log.notice(F("Setting EXT0 Wakeup for deep sleep." CR)); gpio_num_t wake_pin0 = static_cast(ESP32_EXT0_WAKE_PIN); if (esp_sleep_enable_ext0_wakeup(wake_pin0, ESP32_EXT0_WAKE_PIN_STATE) != ESP_OK) { Log.error(F("Failed to set deep sleep EXT0 Wakeup." CR)); gatewayState = GatewayState::ERROR; } #endif #ifdef ESP32_EXT1_WAKE_PIN Log.notice(F("Setting EXT1 Wakeup for deep sleep." CR)); uint64_t wake_pin_bitmask = 1ULL << ESP32_EXT1_WAKE_PIN; // Adjust this line if multiple pins are used. esp_sleep_ext1_wakeup_mode_t wake_state1 = static_cast(ESP32_EXT1_WAKE_PIN_STATE); if (esp_sleep_enable_ext1_wakeup(wake_pin_bitmask, wake_state1) != ESP_OK) { Log.error(F("Failed to set deep sleep EXT1 Wakeup." CR)); gatewayState = GatewayState::ERROR; } #endif /* Note that the ONOFF module need to start after the RN8209 so that the overCurrent function is launched after the setup of the sensor */ #ifdef ZsensorRN8209 setupRN8209(); modules.add(ZsensorRN8209); #endif #ifdef ZactuatorONOFF setupONOFF(); modules.add(ZactuatorONOFF); #endif #ifdef ZgatewaySERIAL setupSERIAL(); modules.add(ZgatewaySERIAL); #endif #if defined(ESP32) && defined(USE_BLUFI) if (SYSConfig.blufi) startBlufi(); #endif #if defined(ESPWifiManualSetup) setupWiFiFromBuild(); #else if (loadConfigFromFlash()) { // Config present Log.notice(F("Config loaded from flash" CR)); # ifdef ESP32_ETHERNET setup_ethernet_esp32(); # endif // If not in failSafeMode and no connection to the network with Ethernet, launch the wifi manager if (!failSafeMode && !ethConnected) setupWiFiManager(); } else { // No config in flash # ifdef ESP32_ETHERNET setup_ethernet_esp32(); # endif # if SELF_TEST // Check serial input to trigger a Self Test sequence if required checkSerial(); # endif Log.notice(F("No config in flash, launching wifi manager" CR)); // In failSafeMode we don't want to setup wifi manager as it has already been done before if (!failSafeMode) setupWiFiManager(); } #endif Log.trace(F("OpenMQTTGateway mac: %s" CR), WiFi.macAddress().c_str()); Log.trace(F("OpenMQTTGateway ip: %s" CR), WiFi.localIP().toString().c_str()); Log.trace(F("OpenMQTTGateway index %d" CR), cnt_index); Log.trace(F("OpenMQTTGateway mqtt topic: %s" CR), mqtt_topic); #ifdef ZmqttDiscovery Log.trace(F("OpenMQTTGateway mqtt discovery prefix: %s" CR), discovery_prefix); #endif Log.trace(F("OpenMQTTGateway gateway name: %s" CR), gateway_name); #if !MQTT_BROKER_MODE Log.trace(F("OpenMQTTGateway mqtt server: %s" CR), cnt_parameters_array[cnt_index].mqtt_server); Log.trace(F("OpenMQTTGateway mqtt port: %s" CR), cnt_parameters_array[cnt_index].mqtt_port); Log.trace(F("OpenMQTTGateway mqtt user: %s" CR), cnt_parameters_array[cnt_index].mqtt_user); Log.trace(F("OpenMQTTGateway secure connection: %s" CR), cnt_parameters_array[cnt_index].isConnectionSecure ? "true" : "false"); Log.trace(F("OpenMQTTGateway validate cert: %s" CR), cnt_parameters_array[cnt_index].isCertValidate ? "true" : "false"); #endif setOTA(); #if defined(ZwebUI) && defined(ESP32) WebUISetup(); modules.add(ZwebUI); #endif delay(1500); #if defined(ZgatewayRF) || defined(ZgatewayPilight) || defined(ZgatewayRTL_433) || defined(ZgatewayRF2) || defined(ZactuatorSomfy) setupCommonRF(); #endif #ifdef ZsensorBME280 setupZsensorBME280(); modules.add(ZsensorBME280); #endif #ifdef ZsensorHTU21 setupZsensorHTU21(); modules.add(ZsensorHTU21); #endif #ifdef ZsensorLM75 setupZsensorLM75(); modules.add(ZsensorLM75); #endif #ifdef ZsensorAHTx0 setupZsensorAHTx0(); modules.add(ZsensorAHTx0); #endif #ifdef ZsensorBH1750 setupZsensorBH1750(); modules.add(ZsensorBH1750); #endif #ifdef ZsensorMQ2 setupZsensorMQ2(); modules.add(ZsensorMQ2); #endif #ifdef ZsensorTEMT6000 setupZsensorTEMT6000(); modules.add(ZsensorTEMT6000); #endif #ifdef ZsensorTSL2561 setupZsensorTSL2561(); modules.add(ZsensorTSL2561); #endif #ifdef Zgateway2G setup2G(); modules.add(Zgateway2G); #endif #ifdef ZgatewayIR setupIR(); modules.add(ZgatewayIR); #endif #ifdef ZgatewayLORA setupLORA(); modules.add(ZgatewayLORA); #endif #ifdef ZgatewayRF modules.add(ZgatewayRF); # define ACTIVE_RECEIVER ACTIVE_RF #endif #ifdef ZgatewayRF_RHASK setupRF(); #endif #ifdef ZgatewayRF2 modules.add(ZgatewayRF2); # ifdef ACTIVE_RECEIVER # undef ACTIVE_RECEIVER # endif # define ACTIVE_RECEIVER ACTIVE_RF2 #endif #ifdef ZgatewayPilight modules.add(ZgatewayPilight); # ifdef ACTIVE_RECEIVER # undef ACTIVE_RECEIVER # endif # define ACTIVE_RECEIVER ACTIVE_PILIGHT #endif #ifdef ZgatewayWeatherStation setupWeatherStation(); modules.add(ZgatewayWeatherStation); #endif #ifdef ZgatewayGFSunInverter setupGFSunInverter(); modules.add(ZgatewayGFSunInverter); #endif #ifdef ZgatewaySRFB setupSRFB(); modules.add(ZgatewaySRFB); #endif #ifdef ZgatewayBT setupBT(); modules.add(ZgatewayBT); #endif #ifdef ZgatewayRFM69 setupRFM69(); modules.add(ZgatewayRFM69); #endif #ifdef ZsensorINA226 setupINA226(); modules.add(ZsensorINA226); #endif #ifdef ZsensorHCSR501 setupHCSR501(); modules.add(ZsensorHCSR501); #endif #ifdef ZsensorHCSR04 setupHCSR04(); modules.add(ZsensorHCSR04); #endif #ifdef ZsensorGPIOInput setupGPIOInput(); modules.add(ZsensorGPIOInput); #endif #ifdef ZsensorGPIOKeyCode setupGPIOKeyCode(); modules.add(ZsensorGPIOKeyCode); #endif #ifdef ZactuatorFASTLED setupFASTLED(); modules.add(ZactuatorFASTLED); #endif #ifdef ZactuatorPWM setupPWM(); modules.add(ZactuatorPWM); #endif #ifdef ZactuatorSomfy # ifdef ACTIVE_RECEIVER # undef ACTIVE_RECEIVER # endif # define ACTIVE_RECEIVER ACTIVE_NONE setupSomfy(); modules.add(ZactuatorSomfy); #endif #ifdef ZsensorDS1820 setupZsensorDS1820(); modules.add(ZsensorDS1820); #endif #ifdef ZsensorADC setupADC(); modules.add(ZsensorADC); #endif #ifdef ZsensorTouch setupTouch(); modules.add(ZsensorTouch); #endif #ifdef ZsensorC37_YL83_HMRD setupZsensorC37_YL83_HMRD(); modules.add(ZsensorC37_YL83_HMRD); #endif #ifdef ZsensorDHT setupDHT(); modules.add(ZsensorDHT); #endif #ifdef ZsensorSHTC3 setupSHTC3(); #endif #ifdef ZgatewayRTL_433 # ifdef ACTIVE_RECEIVER # undef ACTIVE_RECEIVER # endif # define ACTIVE_RECEIVER ACTIVE_RTL setupRTL_433(); modules.add(ZgatewayRTL_433); #endif Log.trace(F("mqtt_max_payload_size: %d" CR), mqtt_max_payload_size); SYSConfig.offline ? Log.notice(F("Offline enabled" CR)) : Log.notice(F("Offline disabled" CR)); char jsonChar[100]; serializeJson(modules, jsonChar, measureJson(modules) + 1); Log.notice(F("OpenMQTTGateway modules: %s" CR), jsonChar); Log.notice(F("************** Setup OpenMQTTGateway end **************" CR)); } // Bypass for ESP not reconnecting automaticaly the second time https://github.com/espressif/arduino-esp32/issues/2501 bool wifi_reconnect_bypass() { #if defined(ESP32) && defined(USE_BLUFI) extern bool omg_blufi_ble_connected; if (omg_blufi_ble_connected) { Log.notice(F("BLUFI is connected, bypassing wifi reconnect" CR)); gatewayState = GatewayState::ONBOARDING; return true; } #endif uint8_t wifi_autoreconnect_cnt = 0; #ifdef ESP32 while (WiFi.status() != WL_CONNECTED && wifi_autoreconnect_cnt < maxConnectionRetryNetwork) { #else while (WiFi.waitForConnectResult() != WL_CONNECTED && wifi_autoreconnect_cnt < maxConnectionRetryNetwork) { #endif Log.notice(F("Attempting Wifi connection with saved AP: %d" CR), wifi_autoreconnect_cnt); WiFi.begin(); #if defined(WifiGMode) || defined(WifiPower) setESPWifiProtocolTxPower(); #endif delay(1000); wifi_autoreconnect_cnt++; } if (wifi_autoreconnect_cnt < maxConnectionRetryNetwork) { return true; } else { return false; } } void setOTA() { // Port defaults to 8266 ArduinoOTA.setPort(ota_port); // Hostname defaults to esp8266-[ChipID] ArduinoOTA.setHostname(ota_hostname); // No authentication by default ArduinoOTA.setPassword(ota_pass); ArduinoOTA.onStart([]() { Log.trace(F("Start OTA, lock other functions" CR)); last_ota_activity_millis = millis(); #ifdef ESP32 ProcessLock = true; # ifdef ZgatewayBT stopProcessing(true); # endif #endif lpDisplayPrint("OTA in progress"); }); ArduinoOTA.onEnd([]() { Log.trace(F("\nOTA done" CR)); last_ota_activity_millis = 0; lpDisplayPrint("OTA done"); ESPRestart(6); }); ArduinoOTA.onProgress([](unsigned int progress, unsigned int total) { Log.trace(F("Progress: %u%%\r" CR), (progress / (total / 100))); gatewayState = GatewayState::LOCAL_OTA_IN_PROGRESS; last_ota_activity_millis = millis(); }); ArduinoOTA.onError([](ota_error_t error) { last_ota_activity_millis = millis(); Serial.printf("Error[%u]: ", error); gatewayState = GatewayState::ERROR; if (error == OTA_AUTH_ERROR) Log.error(F("Auth Failed" CR)); else if (error == OTA_BEGIN_ERROR) Log.error(F("Begin Failed" CR)); else if (error == OTA_CONNECT_ERROR) Log.error(F("Connect Failed" CR)); else if (error == OTA_RECEIVE_ERROR) Log.error(F("Receive Failed" CR)); else if (error == OTA_END_ERROR) Log.error(F("End Failed" CR)); ESPRestart(6); }); ArduinoOTA.begin(); } #if !MQTT_BROKER_MODE void setupTLS(int index) { configTime(0, 0, NTP_SERVER); 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)); sClient->setInsecure(); } else { Log.notice(F("Enabling cert validation" CR)); # if defined(ESP32) if (cnt_parameters_array[index].server_cert.length() > MIN_CERT_LENGTH) { sClient->setCACert(cnt_parameters_array[index].server_cert.c_str()); Log.notice(F("Server cert found from cert array" CR)); } else if (strlen(ss_server_cert) > MIN_CERT_LENGTH) { sClient->setCACert(ss_server_cert); Log.notice(F("Server cert found from ss_server_cert" CR)); } else { Log.error(F("No server cert found" CR)); gatewayState = GatewayState::ERROR; } # if AWS_IOT if (strcmp(cnt_parameters_array[index].mqtt_port, "443") == 0) { Log.notice(F("Using ALPN" CR)); sClient->setAlpnProtocols(alpnProtocols); } # endif # if MQTT_SECURE_SIGNED_CLIENT if (cnt_parameters_array[index].client_cert.length() > MIN_CERT_LENGTH) { sClient->setCertificate(cnt_parameters_array[index].client_cert.c_str()); Log.notice(F("Client cert found from cert array" CR)); } else if (strlen(ss_client_cert) > MIN_CERT_LENGTH) { sClient->setCertificate(ss_client_cert); Log.notice(F("Client cert found from ss_client_cert" CR)); } else { Log.error(F("No client cert found" CR)); gatewayState = GatewayState::ERROR; } if (cnt_parameters_array[index].client_key.length() > MIN_CERT_LENGTH) { sClient->setPrivateKey(cnt_parameters_array[index].client_key.c_str()); Log.notice(F("Client key found from cert array" CR)); } else if (strlen(ss_client_key) > MIN_CERT_LENGTH) { sClient->setPrivateKey(ss_client_key); Log.notice(F("Client key found from ss_client_key" CR)); } else { Log.error(F("No client key found" CR)); gatewayState = GatewayState::ERROR; } # endif # elif defined(ESP8266) caCert.append(cnt_parameters_array[index].server_cert.c_str()); sClient->setTrustAnchors(&caCert); sClient->setBufferSizes(512, 512); # if MQTT_SECURE_SIGNED_CLIENT if (pClCert != nullptr) { delete pClCert; } if (pClKey != nullptr) { delete pClKey; } pClCert = new X509List(cnt_parameters_array[index].client_cert.c_str()); pClKey = new PrivateKey(cnt_parameters_array[index].client_key.c_str()); sClient->setClientRSACert(pClCert, pClKey); # endif # endif } } #endif /* Reboot for Reason Codes 0 - Erase and Restart 1 - Repeated MQTT Connection Failure 2 - Repeated WiFi Connection Failure 3 - Failed WiFiManager configuration portal 4 - BLE Scan watchdog 5 - User requested reboot 6 - OTA Update 7 - Parameters changed 8 - not enough memory to pursue 9 - SELFTEST end */ void ESPRestart(byte reason) { #ifdef SecondaryModule // Erase the secondary module config String restartCmdStr = "{\"cmd\":\"" + String(restartCmd) + "\"}"; Log.notice(F("Restarting secondary module : %s" CR), restartCmdStr.c_str()); receivingDATA(subjectMQTTtoSYSsetSecondaryModule, restartCmdStr.c_str()); delay(2000); #endif StaticJsonDocument<128> jsonBuffer; JsonObject jsondata = jsonBuffer.to(); jsondata["reason"] = reason; jsondata["retain"] = true; jsondata["uptime"] = uptime(); jsondata["origin"] = subjectLOGtoMQTT; pub(jsondata); // We go to MQTT bypassing the queue to ensure the message is sent // Clean queue while (!jsonQueue.empty()) { jsonQueue.pop(); } Log.warning(F("Rebooting for reason code %d" CR), reason); #if defined(ESP32) ESP.restart(); #elif defined(ESP8266) ESP.reset(); #endif } #if defined(ESPWifiManualSetup) void setupWiFiFromBuild() { WiFi.mode(WIFI_STA); wifiMulti.addAP(wifi_ssid, wifi_password); Log.trace(F("Connecting to %s" CR), wifi_ssid); # ifdef wifi_ssid1 wifiMulti.addAP(wifi_ssid1, wifi_password1); Log.trace(F("Connecting to %s" CR), wifi_ssid1); # endif delay(10); // We start by connecting to a WiFi network # ifdef NetworkAdvancedSetup IPAddress ip_adress; IPAddress gateway_adress; IPAddress subnet_adress; IPAddress dns_adress; ip_adress.fromString(NET_IP); gateway_adress.fromString(NET_GW); subnet_adress.fromString(NET_MASK); dns_adress.fromString(NET_DNS); if (!WiFi.config(ip_adress, gateway_adress, subnet_adress, dns_adress)) { Log.error(F("Wifi STA Failed to configure" CR)); gatewayState = GatewayState::ERROR; } # endif while (wifiMulti.run() != WL_CONNECTED) { delay(500); Log.trace(F("." CR)); failure_number_ntwk++; # if defined(ESP32) && defined(ZgatewayBT) if (SYSConfig.powerMode) { if (failure_number_ntwk > maxConnectionRetryNetwork) { sleep(); } } else { if (failure_number_ntwk > maxRetryWatchDog) { ESPRestart(2); } } # else if (failure_number_ntwk > maxRetryWatchDog) { ESPRestart(2); } # endif } Log.notice(F("WiFi ok with manual config credentials" CR)); displayPrint("Wifi connected"); } #else WiFiManager wifiManager; //flag for saving data bool shouldSaveConfig = false; //do we have been connected once to MQTT //callback notifying us of the need to save config void saveConfigCallback() { Log.trace(F("Should save config" CR)); shouldSaveConfig = true; } # ifdef TRIGGER_GPIO /** * Identify a long button press to trigger a reset or a failsafe mode * */ void blockingWaitForReset() { if (digitalRead(TRIGGER_GPIO) == LOW) { delay(50); if (digitalRead(TRIGGER_GPIO) == LOW) { Log.trace(F("Trigger button Pressed" CR)); delay(3000); // reset delay hold if (digitalRead(TRIGGER_GPIO) == LOW) { Log.notice(F("Button Held" CR)); // Switching off the relay during reset or failsafe operations # ifdef ZactuatorONOFF uint8_t level = digitalRead(ACTUATOR_ONOFF_GPIO); if (level == ACTUATOR_ON) { ActuatorTrigger(); } # endif gatewayState = GatewayState::WAITING_ONBOARDING; // Checking if the flash has already been erased to identify if we erase it or go into failsafe mode // going to failsafe mode is done by doing a long button press from a state where the flash has already been erased if (SPIFFS.begin()) { Log.trace(F("mounted file system" CR)); if (SPIFFS.exists("/config.json")) { Log.notice(F("Erasing ESP Config, restarting" CR)); eraseConfig(); } } delay(30000); if (digitalRead(TRIGGER_GPIO) == LOW) { Log.notice(F("Going into failsafe mode without peripherals" CR)); // Failsafe mode enable to connect to Wifi or change the firmware without the peripherals setup failSafeMode = true; setupWiFiManager(); } } } } } /** * Check if button is pressed so as to reset the credentials and parameters stored into the flash * */ void checkButton() { unsigned long timeFromStart = millis(); // Identify if the reset button is pushed at start if (timeFromStart < TimeToResetAtStart) { blockingWaitForReset(); } else { // When we are not at start we either check the button as a regular input (ZsensorGPIOInput used) or for a reset # if defined(INPUT_GPIO) && defined(ZsensorGPIOInput) && INPUT_GPIO == TRIGGER_GPIO MeasureGPIOInput(); # else blockingWaitForReset(); # endif } } # else void checkButton() {} # endif void saveConfig() { Log.trace(F("saving configs" CR)); int totalSize = 512; # if !MQTT_BROKER_MODE for (int i = 0; i < 3; ++i) { // index 0 contains the default values from the build, these values can't be changed at runtime if (cnt_parameters_array[i].validConnection) { totalSize += cnt_parameters_array[i].server_cert.length(); totalSize += cnt_parameters_array[i].client_cert.length(); totalSize += cnt_parameters_array[i].client_key.length(); totalSize += cnt_parameters_array[i].ota_server_cert.length(); } } # endif Log.notice(F("Total size: %d" CR), totalSize); DynamicJsonDocument json(512 + totalSize); # if !MQTT_BROKER_MODE for (int i = 0; i < 3; ++i) { if (cnt_parameters_array[i].validConnection) { char index_suffix[2]; if (i == 0) { index_suffix[0] = '\0'; } else { index_suffix[0] = '0' + i; index_suffix[1] = '\0'; } char key[mqtt_key_max_size]; if (cnt_parameters_array[i].server_cert.length() > MIN_CERT_LENGTH) { strcpy(key, "mqtt_broker_cert"); strcat(key, index_suffix); json[key] = cnt_parameters_array[i].server_cert; } if (cnt_parameters_array[i].client_cert.length() > MIN_CERT_LENGTH) { strcpy(key, "mqtt_client_cert"); strcat(key, index_suffix); json[key] = cnt_parameters_array[i].client_cert; } if (cnt_parameters_array[i].client_key.length() > MIN_CERT_LENGTH) { strcpy(key, "mqtt_client_key"); strcat(key, index_suffix); json[key] = cnt_parameters_array[i].client_key; } if (cnt_parameters_array[i].ota_server_cert.length() > MIN_CERT_LENGTH) { strcpy(key, "ota_server_cert"); strcat(key, index_suffix); json[key] = cnt_parameters_array[i].ota_server_cert; } strcpy(key, "mqtt_server"); strcat(key, index_suffix); json[key] = cnt_parameters_array[i].mqtt_server; strcpy(key, "mqtt_port"); strcat(key, index_suffix); json[key] = cnt_parameters_array[i].mqtt_port; strcpy(key, "mqtt_user"); strcat(key, index_suffix); json[key] = cnt_parameters_array[i].mqtt_user; strcpy(key, "mqtt_pass"); strcat(key, index_suffix); json[key] = cnt_parameters_array[i].mqtt_pass; strcpy(key, "mqtt_broker_secure"); strcat(key, index_suffix); json[key] = cnt_parameters_array[i].isConnectionSecure; strcpy(key, "mqtt_iscertvalid"); strcat(key, index_suffix); json[key] = cnt_parameters_array[i].isCertValidate; strcpy(key, "valid_cnt"); strcat(key, index_suffix); json[key] = cnt_parameters_array[i].validConnection; } } if (cnt_parameters_array[cnt_index].validConnection) { json["cnt_index"] = cnt_index; } # endif json["mqtt_topic"] = mqtt_topic; # ifdef ZmqttDiscovery json["discovery_prefix"] = discovery_prefix; # endif json["gateway_name"] = gateway_name; json["ota_pass"] = ota_pass; File configFile = SPIFFS.open("/config.json", "w"); if (!configFile) { Log.error(F("failed to open config file for writing" CR)); gatewayState = GatewayState::ERROR; } serializeJson(json, configFile); configFile.close(); } bool loadConfigFromFlash() { Log.trace(F("mounting FS..." CR)); bool result = false; if (SPIFFS.begin()) { Log.trace(F("mounted file system" CR)); } else { Log.warning(F("failed to mount FS -> formating" CR)); SPIFFS.format(); if (SPIFFS.begin()) Log.trace(F("mounted file system after formating" CR)); } if (SPIFFS.exists("/config.json")) { //file exists, reading and loading Log.trace(F("reading config file" CR)); File configFile = SPIFFS.open("/config.json", "r"); if (configFile) { Log.trace(F("opened config file" CR)); DynamicJsonDocument json(configFile.size() * 2); auto error = deserializeJson(json, configFile); if (error) { Log.error(F("deserialize config failed: %s, buffer capacity: %u" CR), error.c_str(), json.capacity()); gatewayState = GatewayState::ERROR; } if (!json.isNull()) { Log.trace(F("\nparsed json, size: %u" CR), json.memoryUsage()); // Print json to serial port //serializeJsonPretty(json, Serial); # if !MQTT_BROKER_MODE for (int i = 0; i < 3; ++i) { char index_suffix[2]; // Large enough for 0, 1, or 2 and the null terminator if (i == 0) { index_suffix[0] = '\0'; // Empty string } else { index_suffix[0] = '0' + i; // Convert int to char index_suffix[1] = '\0'; // Null terminator } char key[mqtt_key_max_size]; strcpy(key, "mqtt_broker_cert"); strcat(key, index_suffix); if (json.containsKey(key)) { cnt_parameters_array[i].server_cert = json[key].as(); } strcpy(key, "mqtt_client_cert"); strcat(key, index_suffix); if (json.containsKey(key)) { cnt_parameters_array[i].client_cert = json[key].as(); } strcpy(key, "mqtt_client_key"); strcat(key, index_suffix); if (json.containsKey(key)) { cnt_parameters_array[i].client_key = json[key].as(); } strcpy(key, "ota_server_cert"); strcat(key, index_suffix); if (json.containsKey(key)) { # ifdef ESP32 // Read hash from the file std::string hash = generateHash(json["ota_server_cert"]); // Compare the hash with the expected hash if (hash == GITHUB_OTA_SERVER_CERT_HASH) { // Do nothing Log.warning(F("Old Github OTA server detected, skipping" CR)); } else { Log.notice(F("OTA server cert hash: %s" CR), hash.c_str()); cnt_parameters_array[i].ota_server_cert = json["ota_server_cert"].as(); } # else cnt_parameters_array[i].ota_server_cert = json["ota_server_cert"].as(); # endif } strcpy(key, "mqtt_server"); strcat(key, index_suffix); if (json.containsKey(key)) { strcpy(cnt_parameters_array[i].mqtt_server, json[key].as()); } strcpy(key, "mqtt_port"); strcat(key, index_suffix); if (json.containsKey(key)) { strcpy(cnt_parameters_array[i].mqtt_port, json[key].as()); } strcpy(key, "mqtt_user"); strcat(key, index_suffix); if (json.containsKey(key)) { strcpy(cnt_parameters_array[i].mqtt_user, json[key].as()); } strcpy(key, "mqtt_pass"); strcat(key, index_suffix); if (json.containsKey(key)) { strcpy(cnt_parameters_array[i].mqtt_pass, json[key].as()); } strcpy(key, "mqtt_broker_secure"); strcat(key, index_suffix); if (json.containsKey(key)) { cnt_parameters_array[i].isConnectionSecure = json[key].as(); } strcpy(key, "mqtt_iscertvalid"); strcat(key, index_suffix); if (json.containsKey(key)) { cnt_parameters_array[i].isCertValidate = json[key].as(); } strcpy(key, "valid_cnt"); strcat(key, index_suffix); if (json.containsKey(key)) { cnt_parameters_array[i].validConnection = json[key].as(); } else if (i == CNT_DEFAULT_INDEX) { // For backward compatibility, if valid_cnt is not found, we assume the connection is valid for CNT_DEFAULT_INDEX Log.warning(F("valid_cnt not found, assuming connection is valid" CR)); cnt_parameters_array[i].validConnection = true; } } if (json.containsKey("cnt_index")) { cnt_index = json["cnt_index"].as(); } # endif if (json.containsKey("mqtt_topic")) strcpy(mqtt_topic, json["mqtt_topic"]); # ifdef ZmqttDiscovery if (json.containsKey("discovery_prefix")) strcpy(discovery_prefix, json["discovery_prefix"]); # endif if (json.containsKey("gateway_name")) strcpy(gateway_name, json["gateway_name"]); if (json.containsKey("ota_pass")) { strcpy(ota_pass, json["ota_pass"]); # ifdef WM_PWD_FROM_MAC // From ESP Mac Address, last 8 digits as the password // Compare the existing ota_pass if ota_pass = OTAPASSWORD then replace with the last 8 digits of the mac address // This enable user migrating from previous version to have the same WiFi portal password as previously unless they changed it if (strcmp(ota_pass, "OTAPASSWORD") == 0) { String s = WiFi.macAddress(); sprintf(ota_pass, "%.2s%.2s%.2s%.2s", s.c_str() + 6, s.c_str() + 9, s.c_str() + 12, s.c_str() + 15); } # endif } result = true; } else { Log.warning(F("failed to load json config" CR)); } configFile.close(); } } else { Log.notice(F("No config file found defining default values" CR)); # ifdef USE_MAC_AS_GATEWAY_NAME String s = WiFi.macAddress(); sprintf(gateway_name, "%.2s%.2s%.2s%.2s%.2s%.2s", s.c_str(), s.c_str() + 3, s.c_str() + 6, s.c_str() + 9, s.c_str() + 12, s.c_str() + 15); # endif # ifdef WM_PWD_FROM_MAC // From ESP Mac Address, last 8 digits as the password sprintf(ota_pass, "%.2s%.2s%.2s%.2s", s.c_str() + 6, s.c_str() + 9, s.c_str() + 12, s.c_str() + 15); # endif } return result; } void setupWiFiManager() { delay(10); WiFi.mode(WIFI_STA); # ifdef USE_MAC_AS_GATEWAY_NAME String s = WiFi.macAddress(); snprintf(WifiManager_ssid, MAC_NAME_MAX_LEN, "%s_%.2s%.2s", Gateway_Short_Name, s.c_str(), s.c_str() + 3); strcpy(ota_hostname, WifiManager_ssid); Log.notice(F("OTA Hostname: %s.local" CR), ota_hostname); # endif wifiManager.setDebugOutput(WM_DEBUG); // The extra parameters to be configured (can be either global or just in the setup) // After connecting, parameter.getValue() will get you the configured value // id/name placeholder/prompt default # ifndef WIFIMNG_HIDE_MQTT_CONFIG # if !MQTT_BROKER_MODE WiFiManagerParameter custom_mqtt_server("server", "mqtt server", cnt_parameters_array[CNT_DEFAULT_INDEX].mqtt_server, parameters_size, " minlength='1' maxlength='64' required"); WiFiManagerParameter custom_mqtt_port("port", "mqtt port", cnt_parameters_array[CNT_DEFAULT_INDEX].mqtt_port, 6, " minlength='1' maxlength='5' required"); WiFiManagerParameter custom_mqtt_user("user", "mqtt user", cnt_parameters_array[CNT_DEFAULT_INDEX].mqtt_user, parameters_size, " maxlength='64'"); WiFiManagerParameter custom_mqtt_pass("pass", "mqtt pass", MQTT_PASS, parameters_size, " input type='password' maxlength='64'"); WiFiManagerParameter custom_mqtt_secure("secure", "
mqtt secure", "1", 2, cnt_parameters_array[CNT_DEFAULT_INDEX].isConnectionSecure ? "type=\"checkbox\" checked" : "type=\"checkbox\""); WiFiManagerParameter custom_validate_cert("validate", "
validate cert", "1", 2, cnt_parameters_array[CNT_DEFAULT_INDEX].isCertValidate ? "type=\"checkbox\" checked" : "type=\"checkbox\""); WiFiManagerParameter custom_mqtt_cert("cert", "
mqtt server cert", "", 4096); WiFiManagerParameter custom_ota_server_cert("ota_cert", "
ota server cert", "", 4096); # if MQTT_SECURE_SIGNED_CLIENT WiFiManagerParameter custom_client_cert("client_cert", "
mqtt client cert", "", 4096); WiFiManagerParameter custom_client_key("client_key", "
mqtt client key", "", 4096); # endif # endif WiFiManagerParameter custom_mqtt_topic("topic", "mqtt base topic", mqtt_topic, mqtt_topic_max_size, " minlength='1' maxlength='64' required"); WiFiManagerParameter custom_gateway_name("name", "gateway name", gateway_name, parameters_size, " minlength='1' maxlength='64' required"); WiFiManagerParameter custom_ota_pass("ota", "gateway password", ota_pass, parameters_size, " input type='password' minlength='8' maxlength='64' required"); # endif //WiFiManager //Local intialization. Once its business is done, there is no need to keep it around wifiManager.setConnectTimeout(WiFi_TimeOut); //Set timeout before going to portal wifiManager.setConfigPortalTimeout(WifiManager_ConfigPortalTimeOut); //set config save notify callback wifiManager.setSaveConfigCallback(saveConfigCallback); //set static IP # ifdef NetworkAdvancedSetup Log.trace(F("Adv wifi cfg" CR)); IPAddress ip_adress; IPAddress gateway_adress; IPAddress subnet_adress; IPAddress dns_adress; ip_adress.fromString(NET_IP); gateway_adress.fromString(NET_GW); subnet_adress.fromString(NET_MASK); dns_adress.fromString(NET_DNS); wifiManager.setSTAStaticIPConfig(ip_adress, gateway_adress, subnet_adress, dns_adress); # endif # ifndef WIFIMNG_HIDE_MQTT_CONFIG //add all your parameters here # if !MQTT_BROKER_MODE wifiManager.addParameter(&custom_mqtt_server); wifiManager.addParameter(&custom_mqtt_port); wifiManager.addParameter(&custom_mqtt_user); wifiManager.addParameter(&custom_mqtt_pass); wifiManager.addParameter(&custom_mqtt_secure); wifiManager.addParameter(&custom_mqtt_cert); wifiManager.addParameter(&custom_validate_cert); wifiManager.addParameter(&custom_ota_server_cert); # if MQTT_SECURE_SIGNED_CLIENT wifiManager.addParameter(&custom_client_cert); wifiManager.addParameter(&custom_client_key); # endif # endif wifiManager.addParameter(&custom_gateway_name); wifiManager.addParameter(&custom_mqtt_topic); wifiManager.addParameter(&custom_ota_pass); # endif //set minimum quality of signal so it ignores AP's under that quality wifiManager.setMinimumSignalQuality(MinimumWifiSignalQuality); if (SPIFFS.begin()) { Log.trace(F("mounted file system" CR)); // Check if the config file exists and prevent the portal from showing if yes // Showing the portal if the config file exist would enable access to the configuration data and to the ESP update page // This is a security risk if an attacker has access to the gateway password if (SPIFFS.exists("/config.json")) { wifiManager.setEnableConfigPortal(false); } } # ifdef ESP32_ETHERNET wifiManager.setBreakAfterConfig(true); // If ethernet is used, we don't want to block the connection by keeping the portal up # endif if (!SYSConfig.offline && !wifi_reconnect_bypass()) // if we didn't connect with saved credential we start Wifimanager web portal { Log.notice(F("Connect your phone to WIFI AP: %s with PWD: %s" CR), WifiManager_ssid, ota_pass); gatewayState = GatewayState::ONBOARDING; //fetches ssid and pass and tries to connect //if it does not connect it starts an access point with the specified name //and goes into a blocking loop awaiting configuration if (!wifiManager.autoConnect(WifiManager_ssid, ota_pass)) { Log.warning(F("failed to connect and hit timeout" CR)); delay(3000); # ifdef ESP32 /* Workaround for bug in arduino core that causes the AP to become unsecure on reboot */ esp_wifi_set_mode(WIFI_MODE_AP); esp_wifi_start(); wifi_config_t conf; esp_wifi_get_config(WIFI_IF_AP, &conf); conf.ap.ssid_hidden = 1; esp_wifi_set_config(WIFI_IF_AP, &conf); # endif bool shouldRestart = (gatewayState != GatewayState::BROKER_CONNECTED && !ethConnected && gatewayState != GatewayState::NTWK_CONNECTED); # ifdef USE_BLUFI shouldRestart = shouldRestart && !isStaConnecting(); # endif // Restart/sleep only if not connected and not serial communication mode if (shouldRestart && !SYSConfig.serial) { # ifdef DEEP_SLEEP_IN_US sleep(); # endif ESPRestart(3); } } } displayPrint("Network connected"); gatewayState = GatewayState::NTWK_CONNECTED; if (shouldSaveConfig) { //read updated parameters cnt_index = CNT_DEFAULT_INDEX; # ifndef WIFIMNG_HIDE_MQTT_CONFIG # if !MQTT_BROKER_MODE strcpy(cnt_parameters_array[cnt_index].mqtt_server, custom_mqtt_server.getValue()); strcpy(cnt_parameters_array[cnt_index].mqtt_port, custom_mqtt_port.getValue()); strcpy(cnt_parameters_array[cnt_index].mqtt_user, custom_mqtt_user.getValue()); // Check if the MQTT password field contains the default value if (strcmp(custom_mqtt_pass.getValue(), MQTT_PASS) != 0) { // If it's not the default password, update the MQTT password strcpy(cnt_parameters_array[cnt_index].mqtt_pass, custom_mqtt_pass.getValue()); } strcpy(mqtt_topic, custom_mqtt_topic.getValue()); if (mqtt_topic[strlen(mqtt_topic) - 1] != '/' && strlen(mqtt_topic) < parameters_size) { strcat(mqtt_topic, "/"); } cnt_parameters_array[cnt_index].isConnectionSecure = *custom_mqtt_secure.getValue(); cnt_parameters_array[cnt_index].isCertValidate = *custom_validate_cert.getValue(); if (strlen(custom_mqtt_cert.getValue()) > MIN_CERT_LENGTH) { cnt_parameters_array[cnt_index].server_cert = TheengsUtils::processCert(custom_mqtt_cert.getValue()); } if (strlen(custom_ota_server_cert.getValue()) > MIN_CERT_LENGTH) { cnt_parameters_array[cnt_index].ota_server_cert = TheengsUtils::processCert(custom_ota_server_cert.getValue()); } # if MQTT_SECURE_SIGNED_CLIENT if (strlen(custom_client_cert.getValue()) > MIN_CERT_LENGTH) { cnt_parameters_array[cnt_index].client_cert = TheengsUtils::processCert(custom_client_cert.getValue()); } if (strlen(custom_client_key.getValue()) > MIN_CERT_LENGTH) { cnt_parameters_array[cnt_index].client_key = TheengsUtils::processCert(custom_client_key.getValue()); } # endif # endif strcpy(gateway_name, custom_gateway_name.getValue()); strcpy(ota_pass, custom_ota_pass.getValue()); # endif # if !MQTT_BROKER_MODE // We suppose the connection is valid (could be tested before) cnt_parameters_array[cnt_index].validConnection = true; # endif //save the custom parameters to FS saveConfig(); } } # ifdef ESP32_ETHERNET void setup_ethernet_esp32() { bool ethBeginSuccess = false; WiFi.onEvent(WiFiEvent); # ifdef NetworkAdvancedSetup IPAddress ip_adress; IPAddress gateway_adress; IPAddress subnet_adress; IPAddress dns_adress; ip.fromString(NET_IP); gateway.fromString(NET_GW); subnet.fromString(NET_MASK); Dns.fromString(NET_DNS); Log.trace(F("Adv eth cfg" CR)); ETH.config(ip, gateway, subnet, Dns); ethBeginSuccess = ETH.begin(); # else Log.notice(F("Spl eth cfg" CR)); ethBeginSuccess = ETH.begin(); # endif if (ethBeginSuccess) { Log.notice(F("Ethernet started" CR)); while (!ethConnected && failure_number_ntwk <= maxConnectionRetryNetwork) { delay(500); Log.notice(F("." CR)); failure_number_ntwk++; } } else { Log.error(F("Ethernet not started" CR)); gatewayState = GatewayState::ERROR; } } void WiFiEvent(WiFiEvent_t event) { switch (event) { case ARDUINO_EVENT_ETH_START: Log.trace(F("Ethernet Started" CR)); ETH.setHostname(gateway_name); break; case ARDUINO_EVENT_ETH_CONNECTED: Log.notice(F("Ethernet Connected" CR)); break; case ARDUINO_EVENT_ETH_GOT_IP: Log.notice(F("OpenMQTTGateway Ethernet MAC: %s" CR), ETH.macAddress().c_str()); Log.notice(F("OpenMQTTGateway Ethernet IP: %s" CR), ETH.localIP().toString().c_str()); Log.notice(F("OpenMQTTGateway Ethernet link speed: %d Mbps" CR), ETH.linkSpeed()); gatewayState = GatewayState::NTWK_CONNECTED; ethConnected = true; break; case ARDUINO_EVENT_ETH_DISCONNECTED: Log.warning(F("Ethernet Disconnected" CR)); ethConnected = false; break; case ARDUINO_EVENT_ETH_STOP: Log.warning(F("Ethernet Stopped" CR)); ethConnected = false; break; default: break; } } # endif #endif #if DEFAULT_LOW_POWER_MODE != DEACTIVATED && defined(ESP32) /** * Deep-sleep for the ESP32 - e.g. DEEP_SLEEP_IN_US 30000000 for 30 seconds / wake by ESP32_EXT0_WAKE_PIN/ESP32_EXT1_WAKE_PIN. * Everything is off and (almost) all execution state is lost. */ void sleep() { if (SYSConfig.powerMode < PowerMode::INTERVAL) return; Log.notice(F("Entering deep sleep" CR)); gatewayState = GatewayState::SLEEPING; delay(250); // To allow the LEDs to switch off and MQTT message to be sent # if defined(ZboardM5STACK) || defined(ZboardM5STICKC) || defined(ZboardM5STICKCP) || defined(ZboardM5TOUGH) sleepScreen(); esp_sleep_enable_ext0_wakeup((gpio_num_t)SLEEP_BUTTON, LOW); # endif Log.trace(F("Deactivating ESP32 components" CR)); # ifdef ZgatewayBT stopProcessing(true); ProcessLock = true; # endif # pragma GCC diagnostic push # pragma GCC diagnostic ignored "-Wdeprecated-declarations" adc_power_off(); # pragma GCC diagnostic pop esp_wifi_stop(); # ifdef ESP32_EXT0_WAKE_PIN Log.notice(F("Entering deep sleep, EXT0 Wakeup by pin : %l." CR), ESP32_EXT0_WAKE_PIN); # endif # ifdef ESP32_EXT1_WAKE_PIN Log.notice(F("Entering deep sleep, EXT1 Wakeup by pin : %l." CR), ESP32_EXT1_WAKE_PIN); # endif if (SYSConfig.powerMode == PowerMode::ACTION) { esp_deep_sleep_start(); } else if (SYSConfig.powerMode == PowerMode::INTERVAL) { Log.notice(F("Entering deep sleep for %l us." CR), DEEP_SLEEP_IN_US); esp_deep_sleep(DEEP_SLEEP_IN_US); } } #else void sleep() {} #endif void loop() { #ifndef ESPWifiManualSetup checkButton(); // check if a reset of wifi/mqtt settings is asked #endif #ifdef ESP8266 updateAndHandleLEDsTask(); // With ESP8266 we need to update the LEDs in the loop #endif if (!SYSConfig.offline) { // Online mode if (mqttSetupPending) { setupMQTT(); mqttSetupPending = false; } // When online the MQTT connection callback release the processes } if (firstStart) { #ifdef ZgatewaySERIAL if (SYSConfig.serial && isSerialReady()) { # ifdef ZgatewayBT BTProcessLock = !BTConfig.enabled; # endif ProcessLock = false; firstStart = false; } #else ProcessLock = false; firstStart = false; #endif } unsigned long now = millis(); #ifdef ZgatewaySERIAL // Serial is a module and a communication layer so it's always processed SERIALtoX(); #endif if (ethConnected || WiFi.status() == WL_CONNECTED) { if (ethConnected && WiFi.status() == WL_CONNECTED) { WiFi.disconnect(); // we disconnect the wifi as we are connected to ethernet } ArduinoOTA.handle(); failure_number_ntwk = 0; if (now > (timer_sys_checks + (TimeBetweenCheckingSYS * 1000)) || !timer_sys_checks) { #if message_UTCtimestamp || message_unixtimestamp TheengsUtils::syncNTP(); #endif if (!timer_sys_checks) { // Update check at start up only #if defined(ESP32) && defined(MQTT_HTTPS_FW_UPDATE) checkForUpdates(); #endif } timer_sys_checks = millis(); } #if defined(ZwebUI) && defined(ESP32) WebUILoop(); #endif mqtt->loop(); if (mqtt->connected()) { // MQTT client is still connected failure_number_ntwk = 0; #ifdef ZmqttDiscovery // Deactivate autodiscovery after DiscoveryAutoOffTimer. // Exception: when discovery_republish_on_reconnect is enabled, we never never automatically disable discovery if (!discovery_republish_on_reconnect && SYSConfig.discovery && (now > lastDiscovery + DiscoveryAutoOffTimer)) SYSConfig.discovery = false; #endif } } else if (!SYSConfig.offline && !SYSConfig.serial) { // disconnected from network Log.warning(F("Network disconnected" CR)); gatewayState = GatewayState::NTWK_DISCONNECTED; if (!wifi_reconnect_bypass()) { sleep(); } else { gatewayState = GatewayState::NTWK_CONNECTED; } } if (!ProcessLock) { if (now > (timer_sys_measures + (TimeBetweenReadingSYS * 1000)) || !timer_sys_measures) { timer_sys_measures = millis(); stateMeasures(); #ifdef ZgatewayBT stateBTMeasures(false); #endif #ifdef ZactuatorONOFF stateONOFFMeasures(); #endif #ifdef ZdisplaySSD1306 stateSSD1306Display(); #endif #ifdef ZgatewayLORA stateLORAMeasures(); #endif #if defined(ZgatewayRTL_433) || defined(ZgatewayPilight) || defined(ZgatewayRF) || defined(ZgatewayRF2) || defined(ZactuatorSomfy) stateRFMeasures(); #endif #if defined(ZwebUI) && defined(ESP32) stateWebUIStatus(); #endif } // Function that doesn't need an active connection #if defined(ZboardM5STICKC) || defined(ZboardM5STICKCP) || defined(ZboardM5STACK) || defined(ZboardM5TOUGH) loopM5(); #endif #if defined(ZdisplaySSD1306) loopSSD1306(); #endif #ifdef ZsensorBME280 MeasureTempHumAndPressure(); //Addon to measure Temperature, Humidity, Pressure and Altitude with a Bosch BME280/BMP280 #endif #ifdef ZsensorHTU21 MeasureTempHum(); //Addon to measure Temperature, Humidity, of a HTU21 sensor #endif #ifdef ZsensorLM75 MeasureTemp(); //Addon to measure Temperature of an LM75 sensor #endif #ifdef ZsensorAHTx0 MeasureAHTTempHum(); //Addon to measure Temperature, Humidity, of an 'AHTx0' sensor #endif #ifdef ZsensorHCSR04 MeasureDistance(); //Addon to measure distance with a HC-SR04 #endif #ifdef ZsensorBH1750 MeasureLightIntensity(); //Addon to measure Light Intensity with a BH1750 #endif #ifdef ZsensorMQ2 MeasureGasMQ2(); #endif #ifdef ZsensorTEMT6000 MeasureLightIntensityTEMT6000(); #endif #ifdef ZsensorTSL2561 MeasureLightIntensityTSL2561(); #endif #ifdef ZsensorC37_YL83_HMRD MeasureC37_YL83_HMRDWater(); //Addon for leak detection with a C-37 YL-83 H-MRD #endif #ifdef ZsensorDHT MeasureTempAndHum(); //Addon to measure the temperature with a DHT #endif #ifdef ZsensorSHTC3 MeasureTempAndHum(); //Addon to measure the temperature with a DHT #endif #ifdef ZsensorDS1820 MeasureDS1820Temp(); //Addon to measure the temperature with DS1820 sensor(s) #endif #ifdef ZsensorINA226 MeasureINA226(); #endif #ifdef ZsensorHCSR501 MeasureHCSR501(); #endif #ifdef ZsensorGPIOInput MeasureGPIOInput(); #endif #ifdef ZsensorGPIOKeyCode MeasureGPIOKeyCode(); #endif #ifdef ZsensorADC MeasureADC(); //Addon to measure the analog value of analog pin #endif #ifdef ZsensorTouch MeasureTouch(); #endif #ifdef ZgatewayLORA LORAtoX(); # ifdef ZmqttDiscovery if (SYSConfig.discovery) launchLORADiscovery(false); # endif #endif #ifdef ZgatewayRF RFtoX(); #endif #ifdef ZgatewayRF_RHASK RFtoX(); #endif #ifdef ZgatewayRF2 RF2toX(); #endif #ifdef ZgatewayWeatherStation ZgatewayWeatherStationtoX(); #endif #ifdef ZgatewayGFSunInverter ZgatewayGFSunInverterMQTT(); #endif #ifdef ZgatewayPilight PilighttoX(); #endif #ifdef ZgatewayBT # ifdef ZmqttDiscovery if (SYSConfig.discovery) launchBTDiscovery(false); # endif #endif #ifdef ZgatewaySRFB SRFBtoX(); #endif #ifdef ZgatewayIR IRtoX(); #endif #ifdef Zgateway2G if (_2GtoX()) Log.trace(F("2GtoMQTT OK" CR)); #endif #ifdef ZgatewayRFM69 if (RFM69toX()) Log.trace(F("RFM69toMQTT OK" CR)); #endif #ifdef ZactuatorFASTLED FASTLEDLoop(); #endif #ifdef ZactuatorPWM PWMLoop(); #endif #ifdef ZgatewayRTL_433 RTL_433Loop(); # ifdef ZmqttDiscovery if (SYSConfig.discovery) launchRTL_433Discovery(false); # endif #endif } // Empty the queue emptyQueue(); // Sleep if ready if (ready_to_sleep) { sleep(); } } /** * Calculate uptime and take into account the millis() rollover */ unsigned long uptime() { static unsigned long lastUptime = 0; static unsigned long uptimeAdd = 0; unsigned long uptime = millis() / 1000 + uptimeAdd; if (uptime < lastUptime) { uptime += 4294967; uptimeAdd += 4294967; } lastUptime = uptime; return uptime; } /** * Calculate internal ESP32 temperature */ #if defined(ESP32) && !defined(NO_INT_TEMP_READING) float intTemperatureRead() { SET_PERI_REG_BITS(SENS_SAR_MEAS_WAIT2_REG, SENS_FORCE_XPD_SAR, 3, SENS_FORCE_XPD_SAR_S); SET_PERI_REG_BITS(SENS_SAR_TSENS_CTRL_REG, SENS_TSENS_CLK_DIV, 10, SENS_TSENS_CLK_DIV_S); CLEAR_PERI_REG_MASK(SENS_SAR_TSENS_CTRL_REG, SENS_TSENS_POWER_UP); CLEAR_PERI_REG_MASK(SENS_SAR_TSENS_CTRL_REG, SENS_TSENS_DUMP_OUT); SET_PERI_REG_MASK(SENS_SAR_TSENS_CTRL_REG, SENS_TSENS_POWER_UP_FORCE); SET_PERI_REG_MASK(SENS_SAR_TSENS_CTRL_REG, SENS_TSENS_POWER_UP); ets_delay_us(100); SET_PERI_REG_MASK(SENS_SAR_TSENS_CTRL_REG, SENS_TSENS_DUMP_OUT); ets_delay_us(5); float temp_f = (float)GET_PERI_REG_BITS2(SENS_SAR_SLAVE_ADDR3_REG, SENS_TSENS_OUT, SENS_TSENS_OUT_S); float temp_c = (temp_f - 32) / 1.8; return temp_c; } #endif /* Erase config and restart the ESP */ void eraseConfig() { #ifdef SecondaryModule // Erase the secondary module config String eraseCmdStr = "{\"cmd\":\"" + String(eraseCmd) + "\"}"; Log.notice(F("Erasing secondary module config: %s" CR), eraseCmdStr.c_str()); receivingDATA(subjectMQTTtoSYSsetSecondaryModule, eraseCmdStr.c_str()); delay(2000); #endif Log.trace(F("Formatting requested, result: %d" CR), SPIFFS.format()); #if defined(ESP8266) WiFi.disconnect(true); ESP.eraseConfig(); #else nvs_flash_erase(); #endif ESPRestart(0); } String stateMeasures() { StaticJsonDocument SYSdata; SYSdata["uptime"] = uptime(); SYSdata["version"] = OMG_VERSION; #ifdef LED_ADDRESSABLE SYSdata["rgbb"] = SYSConfig.rgbbrightness; #endif #if USE_BLUFI SYSdata["blufi"] = SYSConfig.blufi; #endif SYSdata["mqtt"] = SYSConfig.mqtt; SYSdata["serial"] = SYSConfig.serial; #ifdef ZmqttDiscovery SYSdata["disc"] = SYSConfig.discovery; SYSdata["ohdisc"] = SYSConfig.ohdiscovery; #endif SYSdata["env"] = ENV_NAME; uint32_t freeMem; uint32_t minFreeMem; freeMem = ESP.getFreeHeap(); #ifdef ZgatewayRTL_433 // Some RTL_433 decoders have memory leak, this is a temporary workaround if (freeMem < MinimumMemory) { Log.error(F("Not enough memory %d, restarting" CR), freeMem); gatewayState = GatewayState::ERROR; ESPRestart(8); } #endif SYSdata["freemem"] = freeMem; #if !MQTT_BROKER_MODE SYSdata["mqttp"] = cnt_parameters_array[cnt_index].mqtt_port; SYSdata["mqtts"] = cnt_parameters_array[cnt_index].isConnectionSecure; SYSdata["mqttv"] = cnt_parameters_array[cnt_index].isCertValidate; #endif SYSdata["msgprc"] = queueLengthSum; SYSdata["msgblck"] = blockedMessages; SYSdata["msgrcv"] = receivedMessages; SYSdata["maxq"] = maxQueueLength; SYSdata["cnt_index"] = cnt_index; #ifdef ESP32 minFreeMem = ESP.getMinFreeHeap(); SYSdata["minmem"] = minFreeMem; # ifndef NO_INT_TEMP_READING SYSdata["tempc"] = TheengsUtils::round2(intTemperatureRead()); # endif SYSdata["freestck"] = uxTaskGetStackHighWaterMark(NULL); SYSdata["powermode"] = SYSConfig.powerMode; #endif SYSdata["eth"] = ethConnected; if (ethConnected) { #ifdef ESP32_ETHERNET SYSdata["mac"] = (char*)ETH.macAddress().c_str(); SYSdata["ip"] = TheengsUtils::ip2CharArray(ETH.localIP()); ETH.fullDuplex() ? SYSdata["fd"] = (bool)"true" : SYSdata["fd"] = (bool)"false"; SYSdata["linkspeed"] = (int)ETH.linkSpeed(); #endif } else { SYSdata["rssi"] = (long)WiFi.RSSI(); SYSdata["SSID"] = (char*)WiFi.SSID().c_str(); SYSdata["BSSID"] = (char*)WiFi.BSSIDstr().c_str(); SYSdata["ip"] = TheengsUtils::ip2CharArray(WiFi.localIP()); SYSdata["mac"] = (char*)WiFi.macAddress().c_str(); } #ifdef ZboardM5STACK M5.Power.begin(); SYSdata["m5battlevel"] = (int8_t)M5.Power.getBatteryLevel(); SYSdata["m5ischarging"] = (bool)M5.Power.isCharging(); SYSdata["m5ischargefull"] = (bool)M5.Power.isChargeFull(); #endif #if defined(ZboardM5STICKC) || defined(ZboardM5STICKCP) || defined(ZboardM5TOUGH) M5.Axp.EnableCoulombcounter(); SYSdata["m5batvoltage"] = (float)M5.Axp.GetBatVoltage(); SYSdata["m5batcurrent"] = (float)M5.Axp.GetBatCurrent(); SYSdata["m5vinvoltage"] = (float)M5.Axp.GetVinVoltage(); SYSdata["m5vincurrent"] = (float)M5.Axp.GetVinCurrent(); SYSdata["m5vbusvoltage"] = (float)M5.Axp.GetVBusVoltage(); SYSdata["m5vbuscurrent"] = (float)M5.Axp.GetVBusCurrent(); SYSdata["m5tempaxp"] = (float)M5.Axp.GetTempInAXP192(); SYSdata["m5batpower"] = (float)M5.Axp.GetBatPower(); SYSdata["m5batchargecurrent"] = (float)M5.Axp.GetBatChargeCurrent(); SYSdata["m5apsvoltage"] = (float)M5.Axp.GetAPSVoltage(); #endif SYSdata["modules"] = modules; SYSdata["origin"] = subjectSYStoMQTT; enqueueJsonObject(SYSdata); char jsonChar[100]; serializeJson(modules, jsonChar, 99); String _modules = jsonChar; _modules.replace(",", ", "); _modules.replace("[", ""); _modules.replace("]", ""); _modules.replace("\"", "'"); SYSdata["modules"] = _modules.c_str(); String output; serializeJson(SYSdata, output); Log.notice(F("SYS json: %s" CR), output.c_str()); return output; } #if defined(ZgatewayRF) || defined(ZgatewayRF_RHASK) || defined(ZgatewayIR) || defined(ZgatewaySRFB) || defined(ZgatewayWeatherStation) || defined(ZgatewayRTL_433) /** * Store signal values from RF, IR, SRFB or Weather stations so as to avoid duplicates */ void storeSignalValue(uint64_t MQTTvalue) { unsigned long now = millis(); // find oldest value of the buffer int o = getMin(); Log.trace(F("Min ind: %d" CR), o); // replace it by the new one receivedSignal[o].value = MQTTvalue; receivedSignal[o].time = now; // Casting "receivedSignal[o].value" to (unsigned long) because ArduinoLog doesn't support uint64_t for ESP's Log.trace(F("store code : %u / %u" CR), (unsigned long)receivedSignal[o].value, receivedSignal[o].time); Log.trace(F("Col: val/timestamp" CR)); for (int i = 0; i < struct_size; i++) { Log.trace(F("mem code : %u / %u" CR), (unsigned long)receivedSignal[i].value, receivedSignal[i].time); } } /** * get oldest time index from the values array from RF, IR, SRFB or Weather stations so as to avoid duplicates */ int getMin() { unsigned int minimum = receivedSignal[0].time; int minindex = 0; for (int i = 1; i < struct_size; i++) { if (receivedSignal[i].time < minimum) { minimum = receivedSignal[i].time; minindex = i; } } return minindex; } /** * Check if signal values from RF, IR, SRFB or Weather stations are duplicates */ bool isAduplicateSignal(uint64_t value) { Log.trace(F("isAdupl?" CR)); for (int i = 0; i < struct_size; i++) { if (receivedSignal[i].value == value) { unsigned long now = millis(); if (now - receivedSignal[i].time < time_avoid_duplicate) { // change Log.trace(F("no pub. dupl" CR)); return true; } } } return false; } #endif void receivingDATA(const char* topicOri, const char* datacallback) { std::string strTopicOri = topicOri; StaticJsonDocument jsonBuffer; JsonObject jsondata = jsonBuffer.to(); DeserializationError error = deserializeJson(jsonBuffer, datacallback); if (error || jsondata.isNull()) { Log.error(F("deserialize MQTT data failed: %s" CR), error.c_str()); gatewayState = GatewayState::ERROR; return; } if (topicOri == nullptr || strcmp(topicOri, "") == 0) { if (jsondata.containsKey("target") && jsondata["target"].is()) { strTopicOri = jsondata["target"].as(); Log.trace(F("BUS Msg target: %s" CR), strTopicOri.c_str()); } else if (jsondata.containsKey("origin") && jsondata["origin"].is()) { strTopicOri = jsondata["origin"].as(); Log.trace(F("BUS Msg origin: %s" CR), strTopicOri.c_str()); } } else { #if defined(SecondaryModule) // Redirect certain commands to Serial String topicSerial = String(mqtt_topic) + String(gateway_name) + subjectMQTTtoSERIAL; const char* cSecondaryModule = SecondaryModule; if (strcmp(cSecondaryModule, "BT") == 0) { if (cmpToMainTopic(topicOri, subjectMQTTtoBTset)) { strTopicOri = topicSerial.c_str(); jsondata["target"] = subjectMQTTtoBTset; } else if (cmpToMainTopic(topicOri, subjectMQTTtoBT)) { strTopicOri = topicSerial.c_str(); jsondata["target"] = subjectMQTTtoBT; } } if (cmpToMainTopic(topicOri, subjectMQTTtoSYSsetSecondaryModule)) { strTopicOri = topicSerial.c_str(); jsondata["target"] = subjectMQTTtoSYSsetSecondaryModule; } #endif Log.trace(F("MQTT Msg topic: %s" CR), strTopicOri.c_str()); } #if defined(ZgatewayRF) || defined(ZgatewayIR) || defined(ZgatewaySRFB) || defined(ZgatewayWeatherStation) if (strstr(strTopicOri.c_str(), subjectMultiGTWKey) != NULL) { // storing received value so as to avoid publishing this value if it has been already sent by this or another OpenMQTTGateway uint64_t data = jsondata.isNull() ? strtoull(datacallback, NULL, 10) : jsondata["value"]; if (data != 0 && !isAduplicateSignal(data)) { storeSignalValue(data); } } #endif if (!jsondata.isNull()) { // json object ok -> json decoding // log the received json String buffer = ""; serializeJson(jsondata, buffer); //Log.notice(F("[ MQTT->OMG ]: %s" CR), buffer.c_str()); #ifdef ZgatewayPilight // ZgatewayPilight is only defined with json publishing due to its numerous parameters XtoPilight(strTopicOri.c_str(), jsondata); #endif #if defined(ZgatewayRTL_433) || defined(ZgatewayPilight) || defined(ZgatewayRF) || defined(ZgatewayRF2) || defined(ZactuatorSomfy) XtoRFset(strTopicOri.c_str(), jsondata); #endif #if jsonReceiving # ifdef ZgatewayLORA XtoLORA(strTopicOri.c_str(), jsondata); # endif # ifdef ZgatewayRF XtoRF(strTopicOri.c_str(), jsondata); # endif # ifdef ZgatewayRF2 XtoRF2(strTopicOri.c_str(), jsondata); # endif # ifdef Zgateway2G Xto2G(strTopicOri.c_str(), jsondata); # endif # ifdef ZgatewaySRFB XtoSRFB(strTopicOri.c_str(), jsondata); # endif # ifdef ZgatewayIR XtoIR(strTopicOri.c_str(), jsondata); # endif # ifdef ZgatewayRFM69 XtoRFM69(strTopicOri.c_str(), jsondata); # endif # ifdef ZgatewayBT XtoBT(strTopicOri.c_str(), jsondata); # endif # ifdef ZactuatorFASTLED XtoFASTLED(strTopicOri.c_str(), jsondata); # endif # ifdef ZactuatorPWM XtoPWM(strTopicOri.c_str(), jsondata); # endif # if defined(ZboardM5STICKC) || defined(ZboardM5STICKCP) || defined(ZboardM5STACK) || defined(ZboardM5TOUGH) XtoM5(strTopicOri.c_str(), jsondata); # endif # if defined(ZdisplaySSD1306) XtoSSD1306(strTopicOri.c_str(), jsondata); # endif # ifdef ZactuatorONOFF XtoONOFF(strTopicOri.c_str(), jsondata); # endif # ifdef ZactuatorSomfy XtoSomfy(strTopicOri.c_str(), jsondata); # endif # ifdef ZgatewaySERIAL XtoSERIAL(strTopicOri.c_str(), jsondata); # endif # ifdef MQTT_HTTPS_FW_UPDATE MQTTHttpsFWUpdate(strTopicOri.c_str(), jsondata); # endif # if defined(ZwebUI) && defined(ESP32) XtoWebUI(strTopicOri.c_str(), jsondata); # endif #endif XtoSYS(strTopicOri.c_str(), jsondata); } else { // not a json object --> simple decoding #if simpleReceiving # ifdef ZgatewayLORA XtoLORA(strTopicOri.c_str(), datacallback); # endif # ifdef ZgatewayRF XtoRF(strTopicOri.c_str(), datacallback); # endif # ifdef ZgatewayRF315 XtoRF315(strTopicOri.c_str(), datacallback); # endif # ifdef ZgatewayRF2 XtoRF2(strTopicOri.c_str(), datacallback); # endif # ifdef Zgateway2G Xto2G(strTopicOri.c_str(), datacallback); # endif # ifdef ZgatewaySRFB XtoSRFB(strTopicOri.c_str(), datacallback); # endif # ifdef ZgatewayRFM69 XtoRFM69(strTopicOri.c_str(), datacallback); # endif # ifdef ZactuatorFASTLED XtoFASTLED(strTopicOri.c_str(), datacallback); # endif #endif #ifdef ZactuatorONOFF XtoONOFF(strTopicOri.c_str(), datacallback); #endif } } #ifdef MQTT_HTTPS_FW_UPDATE String latestVersion; # ifdef ESP32 # include # include "zzHTTPUpdate.h" # if CHECK_OTA_UPDATE /** * Check on a server the latest version information to build a releaseLink * The release link will be used when the user trigger an OTA update command * Only available for ESP32 */ bool checkForUpdates() { Log.notice(F("Update check, free heap: %d"), ESP.getFreeHeap()); HTTPClient http; http.setTimeout((GeneralTimeOut - 1) * 1000); // -1 to avoid WDT http.setFollowRedirects(HTTPC_STRICT_FOLLOW_REDIRECTS); std::string ota_cert; # if !MQTT_BROKER_MODE if (cnt_parameters_array[cnt_index].ota_server_cert.length() > MIN_CERT_LENGTH) { Log.notice(F("Using memory cert" CR)); ota_cert = cnt_parameters_array[cnt_index].ota_server_cert; } else # endif { Log.notice(F("Using config cert" CR)); ota_cert = OTAserver_cert; } http.begin(OTA_JSON_URL, ota_cert.c_str()); int httpCode = http.GET(); StaticJsonDocument jsonBuffer; JsonObject jsondata = jsonBuffer.to(); if (httpCode > 0) { //Check for the returning code String payload = http.getString(); auto error = deserializeJson(jsonBuffer, payload); if (error) { Log.error(F("Deserialize MQTT data failed: %s" CR), error.c_str()); gatewayState = GatewayState::ERROR; } Log.trace(F("HttpCode %d" CR), httpCode); Log.trace(F("Payload %s" CR), payload.c_str()); } else { Log.error(F("Error on HTTP request")); gatewayState = GatewayState::ERROR; } http.end(); //Free the resources Log.notice(F("Update check done, free heap: %d"), ESP.getFreeHeap()); if (jsondata.containsKey("latest_version")) { jsondata["installed_version"] = OMG_VERSION; jsondata["entity_picture"] = ENTITY_PICTURE; if (!jsondata.containsKey("release_summary")) jsondata["release_summary"] = ""; latestVersion = jsondata["latest_version"].as(); jsondata["origin"] = subjectRLStoMQTT; jsondata["retain"] = true; enqueueJsonObject(jsondata); Log.trace(F("Update file found on server" CR)); return true; } else { Log.trace(F("No update file found on server" CR)); return false; } } # else bool checkForUpdates() { return false; } # endif # elif ESP8266 # include # endif void MQTTHttpsFWUpdate(const char* topicOri, JsonObject& HttpsFwUpdateData) { if (strstr(topicOri, subjectMQTTtoSYSupdate) != NULL) { const char* version = HttpsFwUpdateData["version"] | "latest"; if (version && ((strlen(version) != strlen(OMG_VERSION)) || strcmp(version, OMG_VERSION) != 0)) { const char* url = HttpsFwUpdateData["url"]; String systemUrl; if (url) { if (!strstr((url + (strlen(url) - 5)), ".bin")) { Log.error(F("Invalid firmware extension" CR)); gatewayState = GatewayState::ERROR; return; } # if MQTT_HTTPS_FW_UPDATE_USE_PASSWORD > 0 const char* pwd = HttpsFwUpdateData["password"]; if (pwd) { if (strcmp(pwd, ota_pass) != 0) { Log.error(F("Invalid OTA password" CR)); gatewayState = GatewayState::ERROR; return; } } else { Log.error(F("No password sent" CR)); gatewayState = GatewayState::ERROR; return; } # endif # ifdef ESP32 } else if (strcmp(version, "latest") == 0) { systemUrl = RELEASE_LINK + latestVersion + "/" + ENV_NAME + "-firmware.bin"; url = systemUrl.c_str(); Log.notice(F("Using system OTA url with latest version %s" CR), url); } else if (strcmp(version, "dev") == 0) { systemUrl = String(RELEASE_LINK_DEV) + ENV_NAME + "-firmware.bin"; url = systemUrl.c_str(); Log.notice(F("Using system OTA url with dev version %s" CR), url); } else if (version[0] == 'v') { systemUrl = String(RELEASE_LINK) + version + "/" + ENV_NAME + "-firmware.bin"; url = systemUrl.c_str(); Log.notice(F("Using system OTA url with defined version %s" CR), url); # endif } else { Log.error(F("Invalid URL" CR)); gatewayState = GatewayState::ERROR; return; } # ifdef ESP32 ProcessLock = true; # ifdef ZgatewayBT stopProcessing(true); # endif # endif Log.warning(F("Starting firmware update with %d freeHeap" CR), ESP.getFreeHeap()); gatewayState = GatewayState::REMOTE_OTA_IN_PROGRESS; StaticJsonDocument jsondata; jsondata["release_summary"] = "Update in progress ..."; jsondata["origin"] = subjectRLStoMQTT; enqueueJsonObject(jsondata); std::string ota_cert = TheengsUtils::processCert(HttpsFwUpdateData["ota_server_cert"] | ""); Log.notice(F("OTA cert: %s" CR), ota_cert.c_str()); if (ota_cert.length() < MIN_CERT_LENGTH && !strstr(url, "http:")) { # if !MQTT_BROKER_MODE if (cnt_parameters_array[cnt_index].ota_server_cert.length() > MIN_CERT_LENGTH) { Log.notice(F("Using memory cert" CR)); ota_cert = cnt_parameters_array[cnt_index].ota_server_cert.c_str(); } else # endif { Log.notice(F("Using config cert" CR)); ota_cert = OTAserver_cert; } } t_httpUpdate_return result = HTTP_UPDATE_FAILED; if (strstr(url, "http:")) { Log.notice(F("Http update" CR)); WiFiClient update_client; # ifdef ESP32 httpUpdate.setFollowRedirects(HTTPC_STRICT_FOLLOW_REDIRECTS); result = httpUpdate.update(update_client, url); # elif ESP8266 ESPhttpUpdate.setFollowRedirects(HTTPC_STRICT_FOLLOW_REDIRECTS); result = ESPhttpUpdate.update(update_client, url); # endif } else { WiFiClientSecure update_client; # if !MQTT_BROKER_MODE if (cnt_parameters_array[cnt_index].isConnectionSecure) { mqtt.reset(); mqttSetupPending = true; update_client = *static_cast(eClient.get()); } # endif TheengsUtils::syncNTP(); # ifdef ESP32 update_client.setCACert(ota_cert.c_str()); update_client.setTimeout(12); httpUpdate.setFollowRedirects(HTTPC_STRICT_FOLLOW_REDIRECTS); httpUpdate.rebootOnUpdate(false); result = httpUpdate.update(update_client, url); # elif ESP8266 caCert.append(ota_cert.c_str()); update_client.setTrustAnchors(&caCert); update_client.setTimeout(12000); ESPhttpUpdate.setFollowRedirects(HTTPC_STRICT_FOLLOW_REDIRECTS); ESPhttpUpdate.rebootOnUpdate(false); result = ESPhttpUpdate.update(update_client, url); # endif } switch (result) { case HTTP_UPDATE_FAILED: # ifdef ESP32 Log.error(F("HTTP_UPDATE_FAILED Error (%d): %s\n" CR), httpUpdate.getLastError(), httpUpdate.getLastErrorString().c_str()); # elif ESP8266 Log.error(F("HTTP_UPDATE_FAILED Error (%d): %s\n" CR), ESPhttpUpdate.getLastError(), ESPhttpUpdate.getLastErrorString().c_str()); # endif gatewayState = GatewayState::ERROR; break; case HTTP_UPDATE_NO_UPDATES: Log.notice(F("HTTP_UPDATE_NO_UPDATES" CR)); break; case HTTP_UPDATE_OK: Log.notice(F("HTTP_UPDATE_OK" CR)); jsondata["release_summary"] = "Update success !"; jsondata["installed_version"] = latestVersion; jsondata["origin"] = subjectRLStoMQTT; enqueueJsonObject(jsondata); # if !MQTT_BROKER_MODE if (cnt_index != 0) // We don't enable the change of cert provided at build time cnt_parameters_array[cnt_index].ota_server_cert = ota_cert; # endif # ifndef ESPWifiManualSetup saveConfig(); # endif ESPRestart(6); break; } ESPRestart(6); } } } #endif #if !MQTT_BROKER_MODE /** * Read the certificates from the memory and publish a hash of the cert to the broker for identification purposes */ void readCntParameters(int index) { if (index < 0 || index > 2) { Log.warning(F("Invalid cnt index" CR)); return; } StaticJsonDocument jsonBuffer; JsonObject jsondata = jsonBuffer.to(); jsondata["cnt_index"] = index; jsondata["valid_cnt"] = cnt_parameters_array[index].validConnection; if (cnt_parameters_array[index].server_cert.length() > MIN_CERT_LENGTH) { jsondata["mqtt_server_cert_hash"] = generateHash(cnt_parameters_array[index].server_cert); } if (cnt_parameters_array[index].client_cert.length() > MIN_CERT_LENGTH) { jsondata["mqtt_client_cert_hash"] = generateHash(cnt_parameters_array[index].client_cert); } if (cnt_parameters_array[index].client_key.length() > MIN_CERT_LENGTH) { jsondata["mqtt_client_key_hash"] = generateHash(cnt_parameters_array[index].client_key); } if (cnt_parameters_array[index].ota_server_cert.length() > MIN_CERT_LENGTH) { jsondata["ota_server_cert_hash"] = generateHash(cnt_parameters_array[index].ota_server_cert); } jsondata["mqtt_server"] = cnt_parameters_array[index].mqtt_server; jsondata["mqtt_port"] = cnt_parameters_array[index].mqtt_port; jsondata["mqtt_user"] = cnt_parameters_array[index].mqtt_user; jsondata["mqtt_pass"] = generateHash(cnt_parameters_array[index].mqtt_pass); jsondata["mqtt_secure"] = cnt_parameters_array[index].isConnectionSecure; jsondata["mqtt_validate"] = cnt_parameters_array[index].isCertValidate; jsondata["origin"] = subjectSYStoMQTT; enqueueJsonObject(jsondata); } #endif void XtoSYS(const char* topicOri, JsonObject& SYSdata) { // json object decoding if (cmpToMainTopic(topicOri, subjectMQTTtoSYSset)) { bool restartESP = false; bool publishState = false; Log.trace(F("MQTTtoSYS json" CR)); if (SYSdata.containsKey("cmd")) { const char* cmd = SYSdata["cmd"]; Log.notice(F("Command: %s" CR), cmd); if (strstr(cmd, restartCmd) != NULL) { //restart ESPRestart(5); } else if (strstr(cmd, eraseCmd) != NULL) { //erase and restart eraseConfig(); } else if (strstr(cmd, statusCmd) != NULL) { publishState = true; } } #ifdef LED_ADDRESSABLE if (SYSdata.containsKey("rgbb") && SYSdata["rgbb"].is()) { if (SYSdata["rgbb"] >= 0 && SYSdata["rgbb"] <= 255) { SYSConfig.rgbbrightness = TheengsUtils::round2(SYSdata["rgbb"]); ledManager.setBrightness(SYSConfig.rgbbrightness); # ifdef ZactuatorONOFF updatePowerIndicator(); # endif Log.notice(F("RGB brightness: %d" CR), SYSConfig.rgbbrightness); publishState = true; } else { Log.warning(F("RGB brightness value invalid - ignoring command" CR)); } } #endif #ifdef ZmqttDiscovery if (SYSdata.containsKey("ohdisc") && SYSdata["ohdisc"].is()) { SYSConfig.ohdiscovery = SYSdata["ohdisc"]; Log.notice(F("OpenHAB discovery: %T" CR), SYSConfig.ohdiscovery); publishState = true; } #endif if (SYSdata.containsKey("wifi_ssid") && SYSdata["wifi_ssid"].is() && SYSdata.containsKey("wifi_pass") && SYSdata["wifi_pass"].is()) { #ifdef ESP32 ProcessLock = true; # ifdef ZgatewayBT stopProcessing(true); # endif #endif String prev_ssid = WiFi.SSID(); String prev_pass = WiFi.psk(); // 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"]); WiFi.begin((const char*)SYSdata["wifi_ssid"], (const char*)SYSdata["wifi_pass"]); #if defined(WifiGMode) || defined(WifiPower) setESPWifiProtocolTxPower(); #endif WiFi.waitForConnectResult(WiFi_TimeOut * 1000); if (WiFi.status() != WL_CONNECTED) { Log.warning(F("Failed to connect to new AP; falling back" CR)); WiFi.disconnect(true); WiFi.begin(prev_ssid.c_str(), prev_pass.c_str()); #if defined(WifiGMode) || defined(WifiPower) setESPWifiProtocolTxPower(); #endif } restartESP = true; } if ((SYSdata.containsKey("mqtt_topic") && SYSdata["mqtt_topic"].is()) || #ifdef ZmqttDiscovery (SYSdata.containsKey("discovery_prefix") && SYSdata["discovery_prefix"].is()) || #endif (SYSdata.containsKey("gateway_name") && SYSdata["gateway_name"].is()) || (SYSdata.containsKey("gw_pass") && SYSdata["gw_pass"].is())) { if (SYSdata.containsKey("mqtt_topic")) { strncpy(mqtt_topic, SYSdata["mqtt_topic"], parameters_size); } #ifdef ZmqttDiscovery if (SYSdata.containsKey("discovery_prefix")) { strncpy(discovery_prefix, SYSdata["discovery_prefix"], parameters_size); } #endif if (SYSdata.containsKey("gateway_name")) { strncpy(gateway_name, SYSdata["gateway_name"], parameters_size); } if (SYSdata.containsKey("gw_pass")) { strncpy(ota_pass, SYSdata["gw_pass"], parameters_size); restartESP = true; } #ifndef ESPWifiManualSetup saveConfig(); #endif mqttSetupPending = true; // trigger reconnect in loop using the new topic/name } #if !MQTT_BROKER_MODE # ifdef MQTTsetMQTT 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(); } if (SYSdata.containsKey("read_cnt") && SYSdata["read_cnt"].is()) { read_cnt = SYSdata["read_cnt"].as(); } if (SYSdata.containsKey("test_cnt") && SYSdata["test_cnt"].is()) { test_cnt = SYSdata["test_cnt"].as(); } if (SYSdata.containsKey("cnt_index") && SYSdata["cnt_index"].is()) { if (SYSdata["cnt_index"].as() < 0 || SYSdata["cnt_index"].as() > 2) { Log.warning(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()) { strcpy(cnt_parameters_array[cnt_index].mqtt_user, SYSdata["mqtt_user"]); strcpy(cnt_parameters_array[cnt_index].mqtt_pass, SYSdata["mqtt_pass"]); cnt_parameters_array[cnt_index].validConnection = false; } if (SYSdata.containsKey("mqtt_server") && SYSdata["mqtt_server"].is()) { strcpy(cnt_parameters_array[cnt_index].mqtt_server, SYSdata["mqtt_server"]); cnt_parameters_array[cnt_index].validConnection = false; } if (SYSdata.containsKey("mqtt_port") && SYSdata["mqtt_port"].is()) { strcpy(cnt_parameters_array[cnt_index].mqtt_port, SYSdata["mqtt_port"]); cnt_parameters_array[cnt_index].validConnection = false; } if (SYSdata.containsKey("mqtt_secure") && SYSdata["mqtt_secure"].is()) { cnt_parameters_array[cnt_index].isConnectionSecure = SYSdata["mqtt_secure"].as(); cnt_parameters_array[cnt_index].validConnection = false; } if (SYSdata.containsKey("mqtt_validate") && SYSdata["mqtt_validate"].is()) { cnt_parameters_array[cnt_index].isCertValidate = SYSdata["mqtt_validate"].as(); cnt_parameters_array[cnt_index].validConnection = false; } // Copy the certs to the memory if (SYSdata.containsKey("mqtt_server_cert") && SYSdata["mqtt_server_cert"].is()) { cnt_parameters_array[cnt_index].server_cert = TheengsUtils::processCert(SYSdata["mqtt_server_cert"].as()); Log.trace(F("Assigning server cert %s" CR), generateHash(cnt_parameters_array[cnt_index].server_cert).c_str()); cnt_parameters_array[cnt_index].validConnection = false; } if (SYSdata.containsKey("mqtt_client_cert") && SYSdata["mqtt_client_cert"].is()) { cnt_parameters_array[cnt_index].client_cert = TheengsUtils::processCert(SYSdata["mqtt_client_cert"].as()); Log.trace(F("Assigning client cert %s" CR), generateHash(cnt_parameters_array[cnt_index].client_cert).c_str()); cnt_parameters_array[cnt_index].validConnection = false; } if (SYSdata.containsKey("mqtt_client_key") && SYSdata["mqtt_client_key"].is()) { cnt_parameters_array[cnt_index].client_key = TheengsUtils::processCert(SYSdata["mqtt_client_key"].as()); Log.trace(F("Assigning client key %s" CR), generateHash(cnt_parameters_array[cnt_index].client_key).c_str()); cnt_parameters_array[cnt_index].validConnection = false; } if (SYSdata.containsKey("ota_server_cert") && SYSdata["ota_server_cert"].is()) { cnt_parameters_array[cnt_index].ota_server_cert = TheengsUtils::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); } // Change of mqtt secure connection or certs void* prev_client = nullptr; if (save_cnt || test_cnt) { // Stop the processing/disconnect # ifdef ESP32 ProcessLock = true; # ifdef ZgatewayBT stopProcessing(true); # endif # endif mqttSetupPending = true; } else { // no request to test or save the parameters, forget the old settings cnt_parameters_backup.reset(); } # endif #endif if (SYSdata.containsKey("mqtt") && SYSdata["mqtt"].is()) { SYSConfig.mqtt = SYSdata["mqtt"]; Log.notice(F("xtomqtt: %T" CR), SYSConfig.mqtt); } if (SYSdata.containsKey("serial") && SYSdata["serial"].is()) { SYSConfig.serial = SYSdata["serial"]; Log.notice(F("SERIAL: %T" CR), SYSConfig.serial); } if (SYSdata.containsKey("offline") && SYSdata["offline"].is()) { SYSConfig.offline = SYSdata["offline"]; Log.notice(F("offline: %T" CR), SYSConfig.offline); if (SYSConfig.offline) { gatewayState = GatewayState::OFFLINE; // Disconnect MQTT #if !MQTT_BROKER_MODE mqtt->disconnect(); #else mqtt->stop(); #endif // Disconnect network if (WiFi.status() == WL_CONNECTED) WiFi.disconnect(true); } } if (SYSdata.containsKey("powermode") && SYSdata["powermode"].is()) { SYSConfig.powerMode = SYSdata["powermode"]; Log.notice(F("Power mode: %d" CR), SYSConfig.powerMode); } #if USE_BLUFI if (SYSdata.containsKey("blufi") && SYSdata["blufi"].is()) { bool res = false; if (SYSdata["blufi"] && !SYSConfig.blufi) { // Start Blufi res = startBlufi(); } else if (!SYSdata["blufi"] && SYSConfig.blufi) { // Stop Blufi res = stopBlufi(); } if (res) SYSConfig.blufi = SYSdata["blufi"]; publishState = true; Log.notice(F("Blufi: %T" CR), SYSConfig.blufi); } #endif if (SYSdata.containsKey("disc")) { if (SYSdata["disc"].is()) { if (SYSdata["disc"] == true && SYSConfig.discovery == false) lastDiscovery = millis(); SYSConfig.discovery = SYSdata["disc"]; publishState = true; if (SYSConfig.discovery) pubMqttDiscovery(); } else { Log.warning(F("Discovery command not a boolean" CR)); } Log.notice(F("Discovery state: %T" CR), SYSConfig.discovery); } if (SYSdata.containsKey("save") && SYSdata["save"].as()) { SYSConfig_save(); } if (publishState) { stateMeasures(); } if (restartESP) { ESPRestart(7); } } }