From b87092d4e15c10ae08ad5965232009c4e016da6e Mon Sep 17 00:00:00 2001 From: Alexey Zholtikov Date: Tue, 7 Mar 2023 11:54:29 +0300 Subject: [PATCH] Version 1.6 Changed config message for ESP-NOW sensors. Changed config message for RF gateway. Changed config message for nRF24 sensors. Minor main code optimization. Changed config data storage location to EEPROM. Fixed bug with saving config from Web interface. --- data/function.js | 2 +- data/index.htm | 2 +- src/main.cpp | 383 +++++++++++++++++++++++------------------------ 3 files changed, 193 insertions(+), 194 deletions(-) diff --git a/data/function.js b/data/function.js index 4393e98..b448501 100755 --- a/data/function.js +++ b/data/function.js @@ -10,7 +10,7 @@ function createXmlHttpObject() { function load() { if (xmlHttp.readyState == 0 || xmlHttp.readyState == 4) { - xmlHttp.open('PUT', '/config.json', true); + xmlHttp.open('GET', '/config', true); xmlHttp.send(null); xmlHttp.onload = function () { jsonResponse = JSON.parse(xmlHttp.responseText); diff --git a/data/index.htm b/data/index.htm index 63865c3..d42ce73 100644 --- a/data/index.htm +++ b/data/index.htm @@ -10,7 +10,7 @@
-

ESP-NOW Gateway

+

ESP-NOW Gateway

Firmware:

diff --git a/src/main.cpp b/src/main.cpp index 2775722..4b41124 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -4,6 +4,7 @@ #include "Ethernet.h" // https://github.com/arduino-libraries/Ethernet #include "PubSubClient.h" #include "LittleFS.h" +#include "EEPROM.h" #include "Ticker.h" #include "NTPClient.h" #include "ZHNetwork.h" @@ -42,31 +43,30 @@ typedef enum : uint8_t ESP_NOW_LAN } work_mode_t; -const String firmware{"1.5"}; - -String espnowNetName{"DEFAULT"}; - -uint8_t workMode{ESP_NOW}; - +struct deviceConfig +{ #if defined(ESP8266) -String deviceName = "ESP-NOW gateway " + String(ESP.getChipId(), HEX); + String deviceName = "ESP-NOW gateway " + String(ESP.getChipId(), HEX); #endif #if defined(ESP32) -String deviceName = "ESP-NOW gateway " + String(ESP.getEfuseMac(), HEX); + String deviceName = "ESP-NOW gateway " + String(ESP.getEfuseMac(), HEX); #endif - -String ssid{"SSID"}; -String password{"PASSWORD"}; - -String mqttHostName{"MQTT"}; -uint16_t mqttHostPort{1883}; -String mqttUserLogin{""}; -String mqttUserPassword{""}; -String topicPrefix{"homeassistant"}; -const char *mqttUserID{"ESP32"}; - -String ntpHostName{"NTP"}; -uint16_t gmtOffset{10800}; + String espnowNetName{"DEFAULT"}; + uint8_t workMode{ESP_NOW}; + String ssid{"SSID"}; + String password{"PASSWORD"}; + String mqttHostName{"MQTT"}; + uint16_t mqttHostPort{1883}; + String mqttUserLogin{""}; + String mqttUserPassword{""}; + String topicPrefix{"homeassistant"}; + String ntpHostName{"NTP"}; + uint16_t gmtOffset{10800}; +} config; + +const String firmware{"1.6"}; + +const char *mqttUserID{"ESP"}; uint8_t w5500Mac[6] = {0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED}; // Change it if necessary. @@ -82,8 +82,8 @@ PubSubClient mqttWifiClient(wifiClient); WiFiUDP udpWiFiClient; EthernetUDP udpEthClient; -NTPClient ntpWiFiClient(udpWiFiClient, ntpHostName.c_str(), gmtOffset); -NTPClient ntpEthClient(udpEthClient, ntpHostName.c_str(), gmtOffset); +NTPClient ntpWiFiClient(udpWiFiClient, config.ntpHostName.c_str(), config.gmtOffset); +NTPClient ntpEthClient(udpEthClient, config.ntpHostName.c_str(), config.gmtOffset); Ticker mqttAvailabilityCheckTimer; bool mqttAvailabilityCheckTimerSemaphore{true}; @@ -109,13 +109,13 @@ void setup() loadConfig(); - if (workMode == ESP_NOW_LAN) + if (config.workMode == ESP_NOW_LAN) { Ethernet.init(5); Ethernet.begin(w5500Mac); } - if (workMode == ESP_NOW_WIFI) + if (config.workMode == ESP_NOW_WIFI) { #if defined(ESP8266) WiFi.setSleepMode(WIFI_NONE_SLEEP); @@ -128,9 +128,9 @@ void setup() WiFi.setAutoReconnect(true); } - myNet.begin(espnowNetName.c_str(), true); + myNet.begin(config.espnowNetName.c_str(), true); - if (workMode) + if (config.workMode) { // myNet.setCryptKey("VERY_LONG_CRYPT_KEY"); // If encryption is used, the key must be set same of all another ESP-NOW devices in network. myNet.setOnBroadcastReceivingCallback(onEspnowMessage); @@ -144,7 +144,7 @@ void setup() WiFi.softAP(("ESP-NOW gateway " + String(ESP.getEfuseMac(), HEX)).c_str(), "12345678"); #endif - if (workMode == ESP_NOW_WIFI) + if (config.workMode == ESP_NOW_WIFI) { uint8_t scan = WiFi.scanNetworks(false, false); String name; @@ -161,24 +161,24 @@ void setup() #if defined(ESP32) WiFi.getNetworkInfo(i, name, encryption, rssi, bssid, channel); #endif - if (name == ssid) - WiFi.begin(ssid.c_str(), password.c_str()); + if (name == config.ssid) + WiFi.begin(config.ssid.c_str(), config.password.c_str()); } } - if (workMode == ESP_NOW_WIFI) + if (config.workMode == ESP_NOW_WIFI) { ntpWiFiClient.begin(); mqttWifiClient.setBufferSize(2048); - mqttWifiClient.setServer(mqttHostName.c_str(), mqttHostPort); + mqttWifiClient.setServer(config.mqttHostName.c_str(), config.mqttHostPort); mqttWifiClient.setCallback(onMqttMessage); } - if (workMode == ESP_NOW_LAN) + if (config.workMode == ESP_NOW_LAN) { ntpEthClient.begin(); mqttEthClient.setBufferSize(2048); - mqttEthClient.setServer(mqttHostName.c_str(), mqttHostPort); + mqttEthClient.setServer(config.mqttHostName.c_str(), config.mqttHostPort); mqttEthClient.setCallback(onMqttMessage); } @@ -199,9 +199,9 @@ void loop() sendKeepAliveMessage(); if (attributesMessageTimerSemaphore) sendAttributesMessage(); - if (workMode == ESP_NOW_WIFI) + if (config.workMode == ESP_NOW_WIFI) mqttWifiClient.loop(); - if (workMode == ESP_NOW_LAN) + if (config.workMode == ESP_NOW_LAN) mqttEthClient.loop(); myNet.maintenance(); ArduinoOTA.handle(); @@ -214,30 +214,30 @@ void onEspnowMessage(const char *data, const uint8_t *sender) esp_now_payload_data_t incomingData; memcpy(&incomingData, data, sizeof(esp_now_payload_data_t)); if (incomingData.payloadsType == ENPT_ATTRIBUTES) - mqttPublish((topicPrefix + "/" + getValueName(incomingData.deviceType) + "/" + myNet.macToString(sender) + "/" + getValueName(incomingData.payloadsType)).c_str(), incomingData.message, true); + mqttPublish((config.topicPrefix + "/" + getValueName(incomingData.deviceType) + "/" + myNet.macToString(sender) + "/" + getValueName(incomingData.payloadsType)).c_str(), incomingData.message, true); if (incomingData.payloadsType == ENPT_KEEP_ALIVE) - mqttPublish((topicPrefix + "/" + getValueName(incomingData.deviceType) + "/" + myNet.macToString(sender) + "/" + getValueName(incomingData.payloadsType)).c_str(), "online", true); + mqttPublish((config.topicPrefix + "/" + getValueName(incomingData.deviceType) + "/" + myNet.macToString(sender) + "/" + getValueName(incomingData.payloadsType)).c_str(), "online", true); if (incomingData.payloadsType == ENPT_STATE) - mqttPublish((topicPrefix + "/" + getValueName(incomingData.deviceType) + "/" + myNet.macToString(sender) + "/" + getValueName(incomingData.payloadsType)).c_str(), incomingData.message, true); + mqttPublish((config.topicPrefix + "/" + getValueName(incomingData.deviceType) + "/" + myNet.macToString(sender) + "/" + getValueName(incomingData.payloadsType)).c_str(), incomingData.message, true); if (incomingData.payloadsType == ENPT_CONFIG) { if (incomingData.deviceType == ENDT_SWITCH) { esp_now_payload_data_t configData; memcpy(&configData.message, &incomingData.message, sizeof(esp_now_payload_data_t::message)); - StaticJsonDocument json; + DynamicJsonDocument json(sizeof(esp_now_payload_data_t::message)); deserializeJson(json, configData.message); uint8_t unit = json[MCMT_DEVICE_UNIT].as(); - StaticJsonDocument<2048> jsonConfig; + DynamicJsonDocument jsonConfig(2048); // Same as PubSubClient buffer size. jsonConfig["platform"] = "mqtt"; jsonConfig["name"] = json[MCMT_DEVICE_NAME]; jsonConfig["unique_id"] = myNet.macToString(sender) + "-" + unit; jsonConfig["device_class"] = getValueName(json[MCMT_DEVICE_CLASS].as()); - jsonConfig["state_topic"] = topicPrefix + "/" + getValueName(incomingData.deviceType) + "/" + myNet.macToString(sender) + "/state"; + jsonConfig["state_topic"] = config.topicPrefix + "/" + getValueName(incomingData.deviceType) + "/" + myNet.macToString(sender) + "/state"; jsonConfig["value_template"] = "{{ value_json." + json[MCMT_VALUE_TEMPLATE].as() + " }}"; - jsonConfig["command_topic"] = topicPrefix + "/" + getValueName(incomingData.deviceType) + "/" + myNet.macToString(sender) + "/set"; - jsonConfig["json_attributes_topic"] = topicPrefix + "/" + getValueName(incomingData.deviceType) + "/" + myNet.macToString(sender) + "/attributes"; - jsonConfig["availability_topic"] = topicPrefix + "/" + getValueName(incomingData.deviceType) + "/" + myNet.macToString(sender) + "/status"; + jsonConfig["command_topic"] = config.topicPrefix + "/" + getValueName(incomingData.deviceType) + "/" + myNet.macToString(sender) + "/set"; + jsonConfig["json_attributes_topic"] = config.topicPrefix + "/" + getValueName(incomingData.deviceType) + "/" + myNet.macToString(sender) + "/attributes"; + jsonConfig["availability_topic"] = config.topicPrefix + "/" + getValueName(incomingData.deviceType) + "/" + myNet.macToString(sender) + "/status"; if (json[MCMT_PAYLOAD_ON]) jsonConfig["payload_on"] = json[MCMT_PAYLOAD_ON]; if (json[MCMT_PAYLOAD_OFF]) @@ -246,40 +246,40 @@ void onEspnowMessage(const char *data, const uint8_t *sender) jsonConfig["retain"] = "true"; char buffer[2048]{0}; serializeJsonPretty(jsonConfig, buffer); - mqttPublish((topicPrefix + "/" + getValueName(json[MCMT_COMPONENT_TYPE].as()) + "/" + myNet.macToString(sender) + "-" + unit + "/config").c_str(), buffer, true); + mqttPublish((config.topicPrefix + "/" + getValueName(json[MCMT_COMPONENT_TYPE].as()) + "/" + myNet.macToString(sender) + "-" + unit + "/config").c_str(), buffer, true); } if (incomingData.deviceType == ENDT_LED) { esp_now_payload_data_t configData; memcpy(&configData.message, &incomingData.message, sizeof(esp_now_payload_data_t::message)); - StaticJsonDocument json; + DynamicJsonDocument json(sizeof(esp_now_payload_data_t::message)); deserializeJson(json, configData.message); uint8_t unit = json[MCMT_DEVICE_UNIT].as(); esp_now_led_type_t ledClass = json[MCMT_DEVICE_CLASS]; - StaticJsonDocument<2048> jsonConfig; + DynamicJsonDocument jsonConfig(2048); // Same as PubSubClient buffer size. jsonConfig["platform"] = "mqtt"; jsonConfig["name"] = json[MCMT_DEVICE_NAME]; jsonConfig["unique_id"] = myNet.macToString(sender) + "-" + unit; - jsonConfig["state_topic"] = topicPrefix + "/" + getValueName(incomingData.deviceType) + "/" + myNet.macToString(sender) + "/state"; + jsonConfig["state_topic"] = config.topicPrefix + "/" + getValueName(incomingData.deviceType) + "/" + myNet.macToString(sender) + "/state"; jsonConfig["state_value_template"] = "{{ value_json.state }}"; - jsonConfig["command_topic"] = topicPrefix + "/" + getValueName(incomingData.deviceType) + "/" + myNet.macToString(sender) + "/set"; - jsonConfig["brightness_state_topic"] = topicPrefix + "/" + getValueName(incomingData.deviceType) + "/" + myNet.macToString(sender) + "/state"; + jsonConfig["command_topic"] = config.topicPrefix + "/" + getValueName(incomingData.deviceType) + "/" + myNet.macToString(sender) + "/set"; + jsonConfig["brightness_state_topic"] = config.topicPrefix + "/" + getValueName(incomingData.deviceType) + "/" + myNet.macToString(sender) + "/state"; jsonConfig["brightness_value_template"] = "{{ value_json.brightness }}"; - jsonConfig["brightness_command_topic"] = topicPrefix + "/" + getValueName(incomingData.deviceType) + "/" + myNet.macToString(sender) + "/brightness"; + jsonConfig["brightness_command_topic"] = config.topicPrefix + "/" + getValueName(incomingData.deviceType) + "/" + myNet.macToString(sender) + "/brightness"; if (ledClass == ENLT_RGB || ledClass == ENLT_RGBW || ledClass == ENLT_RGBWW) { - jsonConfig["rgb_state_topic"] = topicPrefix + "/" + getValueName(incomingData.deviceType) + "/" + myNet.macToString(sender) + "/state"; + jsonConfig["rgb_state_topic"] = config.topicPrefix + "/" + getValueName(incomingData.deviceType) + "/" + myNet.macToString(sender) + "/state"; jsonConfig["rgb_value_template"] = "{{ value_json.rgb | join(',') }}"; - jsonConfig["rgb_command_topic"] = topicPrefix + "/" + getValueName(incomingData.deviceType) + "/" + myNet.macToString(sender) + "/rgb"; + jsonConfig["rgb_command_topic"] = config.topicPrefix + "/" + getValueName(incomingData.deviceType) + "/" + myNet.macToString(sender) + "/rgb"; } if (ledClass == ENLT_WW || ledClass == ENLT_RGBWW) { - jsonConfig["color_temp_state_topic"] = topicPrefix + "/" + getValueName(incomingData.deviceType) + "/" + myNet.macToString(sender) + "/state"; + jsonConfig["color_temp_state_topic"] = config.topicPrefix + "/" + getValueName(incomingData.deviceType) + "/" + myNet.macToString(sender) + "/state"; jsonConfig["color_temp_value_template"] = "{{ value_json.temperature }}"; - jsonConfig["color_temp_command_topic"] = topicPrefix + "/" + getValueName(incomingData.deviceType) + "/" + myNet.macToString(sender) + "/temperature"; + jsonConfig["color_temp_command_topic"] = config.topicPrefix + "/" + getValueName(incomingData.deviceType) + "/" + myNet.macToString(sender) + "/temperature"; } - jsonConfig["json_attributes_topic"] = topicPrefix + "/" + getValueName(incomingData.deviceType) + "/" + myNet.macToString(sender) + "/attributes"; - jsonConfig["availability_topic"] = topicPrefix + "/" + getValueName(incomingData.deviceType) + "/" + myNet.macToString(sender) + "/status"; + jsonConfig["json_attributes_topic"] = config.topicPrefix + "/" + getValueName(incomingData.deviceType) + "/" + myNet.macToString(sender) + "/attributes"; + jsonConfig["availability_topic"] = config.topicPrefix + "/" + getValueName(incomingData.deviceType) + "/" + myNet.macToString(sender) + "/status"; if (json[MCMT_PAYLOAD_ON]) jsonConfig["payload_on"] = json[MCMT_PAYLOAD_ON]; if (json[MCMT_PAYLOAD_OFF]) @@ -288,23 +288,23 @@ void onEspnowMessage(const char *data, const uint8_t *sender) jsonConfig["retain"] = "true"; char buffer[2048]{0}; serializeJsonPretty(jsonConfig, buffer); - mqttPublish((topicPrefix + "/" + getValueName(json[MCMT_COMPONENT_TYPE].as()) + "/" + myNet.macToString(sender) + "-" + unit + "/config").c_str(), buffer, true); + mqttPublish((config.topicPrefix + "/" + getValueName(json[MCMT_COMPONENT_TYPE].as()) + "/" + myNet.macToString(sender) + "-" + unit + "/config").c_str(), buffer, true); } if (incomingData.deviceType == ENDT_SENSOR) { esp_now_payload_data_t configData; memcpy(&configData.message, &incomingData.message, sizeof(esp_now_payload_data_t::message)); - StaticJsonDocument json; + DynamicJsonDocument json(sizeof(esp_now_payload_data_t::message)); deserializeJson(json, configData.message); uint8_t unit = json[MCMT_DEVICE_UNIT].as(); ha_component_type_t type = json[MCMT_COMPONENT_TYPE].as(); - StaticJsonDocument<2048> jsonConfig; + DynamicJsonDocument jsonConfig(2048); // Same as PubSubClient buffer size. jsonConfig["platform"] = "mqtt"; jsonConfig["name"] = json[MCMT_DEVICE_NAME]; jsonConfig["unique_id"] = myNet.macToString(sender) + "-" + unit; - jsonConfig["state_topic"] = topicPrefix + "/" + getValueName(incomingData.deviceType) + "/" + myNet.macToString(sender) + "/state"; + jsonConfig["state_topic"] = config.topicPrefix + "/" + getValueName(incomingData.deviceType) + "/" + myNet.macToString(sender) + "/state"; jsonConfig["value_template"] = "{{ value_json." + json[MCMT_VALUE_TEMPLATE].as() + " }}"; - jsonConfig["json_attributes_topic"] = topicPrefix + "/" + getValueName(incomingData.deviceType) + "/" + myNet.macToString(sender) + "/attributes"; + jsonConfig["json_attributes_topic"] = config.topicPrefix + "/" + getValueName(incomingData.deviceType) + "/" + myNet.macToString(sender) + "/attributes"; jsonConfig["force_update"] = "true"; jsonConfig["retain"] = "true"; if (type == HACT_SENSOR) @@ -324,77 +324,77 @@ void onEspnowMessage(const char *data, const uint8_t *sender) jsonConfig["payload_off"] = json[MCMT_PAYLOAD_OFF]; char buffer[2048]{0}; serializeJsonPretty(jsonConfig, buffer); - mqttPublish((topicPrefix + "/" + getValueName(type) + "/" + myNet.macToString(sender) + "-" + unit + "/config").c_str(), buffer, true); + mqttPublish((config.topicPrefix + "/" + getValueName(type) + "/" + myNet.macToString(sender) + "-" + unit + "/config").c_str(), buffer, true); } if (incomingData.deviceType == ENDT_RF_SENSOR) { esp_now_payload_data_t configData; memcpy(&configData.message, &incomingData.message, sizeof(esp_now_payload_data_t::message)); - StaticJsonDocument json; + DynamicJsonDocument json(sizeof(esp_now_payload_data_t::message)); deserializeJson(json, configData.message); - uint8_t unit = json["u"].as(); - ha_component_type_t type = json["t"].as(); - rf_sensor_type_t rf = json["r"].as(); - uint16_t id = json["i"].as(); - String tmp = json["v"].as(); - StaticJsonDocument<2048> jsonConfig; + uint8_t unit = json[MCMT_DEVICE_UNIT].as(); + ha_component_type_t haComponentType = json[MCMT_COMPONENT_TYPE].as(); + rf_sensor_type_t rfSensorType = json[MCMT_RF_SENSOR_TYPE].as(); + uint16_t rfSensorId = json[MCMT_RF_SENSOR_ID].as(); + String valueTemplate = json[MCMT_VALUE_TEMPLATE].as(); + DynamicJsonDocument jsonConfig(2048); // Same as PubSubClient buffer size. jsonConfig["platform"] = "mqtt"; - jsonConfig["name"] = getValueName(rf) + " " + id + " " + tmp; - jsonConfig["unique_id"] = String(id) + "-" + unit; - jsonConfig["state_topic"] = topicPrefix + "/rf_sensor/" + getValueName(rf) + "/" + id + "/state"; - jsonConfig["value_template"] = "{{ value_json." + tmp + " }}"; - if (type == HACT_SENSOR) - { - jsonConfig["device_class"] = getValueName(json["h"].as()); - jsonConfig["unit_of_measurement"] = json["m"]; - jsonConfig["expire_after"] = json["e"]; - } - if (type == HACT_BINARY_SENSOR) - { - ha_binary_sensor_device_class_t deviceClass = json["h"].as(); - if (deviceClass == HABSDC_MOISTURE) - jsonConfig["expire_after"] = json["e"]; - jsonConfig["device_class"] = getValueName(deviceClass); - jsonConfig["payload_on"] = json["o"]; - jsonConfig["payload_off"] = json["f"]; - } + jsonConfig["name"] = getValueName(rfSensorType) + " " + rfSensorId + " " + valueTemplate; + jsonConfig["unique_id"] = String(rfSensorId) + "-" + unit; + jsonConfig["state_topic"] = config.topicPrefix + "/rf_sensor/" + getValueName(rfSensorType) + "/" + rfSensorId + "/state"; + jsonConfig["value_template"] = "{{ value_json." + valueTemplate + " }}"; jsonConfig["force_update"] = "true"; jsonConfig["retain"] = "true"; + if (haComponentType == HACT_SENSOR) + { + jsonConfig["device_class"] = getValueName(json[MCMT_DEVICE_CLASS].as()); + jsonConfig["unit_of_measurement"] = json[MCMT_UNIT_OF_MEASUREMENT]; + } + if (haComponentType == HACT_BINARY_SENSOR) + jsonConfig["device_class"] = getValueName(json[MCMT_DEVICE_CLASS].as()); + if (json[MCMT_EXPIRE_AFTER]) + jsonConfig["expire_after"] = json[MCMT_EXPIRE_AFTER]; + if (json[MCMT_OFF_DELAY]) + jsonConfig["off_delay"] = json[MCMT_OFF_DELAY]; + if (json[MCMT_PAYLOAD_ON]) + jsonConfig["payload_on"] = json[MCMT_PAYLOAD_ON]; + if (json[MCMT_PAYLOAD_OFF]) + jsonConfig["payload_off"] = json[MCMT_PAYLOAD_OFF]; char buffer[2048]{0}; serializeJsonPretty(jsonConfig, buffer); - mqttPublish((topicPrefix + "/" + getValueName(type) + "/" + id + "-" + unit + "/config").c_str(), buffer, true); + mqttPublish((config.topicPrefix + "/" + getValueName(haComponentType) + "/" + rfSensorId + "-" + unit + "/config").c_str(), buffer, true); } if (incomingData.deviceType == ENDT_RF_GATEWAY) { esp_now_payload_data_t configData; memcpy(&configData.message, &incomingData.message, sizeof(esp_now_payload_data_t::message)); - StaticJsonDocument json; + DynamicJsonDocument json(sizeof(esp_now_payload_data_t::message)); deserializeJson(json, configData.message); - uint8_t unit = json["unit"].as(); - StaticJsonDocument<2048> jsonConfig; + uint8_t unit = json[MCMT_DEVICE_UNIT].as(); + DynamicJsonDocument jsonConfig(2048); // Same as PubSubClient buffer size. jsonConfig["platform"] = "mqtt"; - jsonConfig["name"] = json["name"]; + jsonConfig["name"] = json[MCMT_DEVICE_NAME]; jsonConfig["unique_id"] = myNet.macToString(sender) + "-" + unit; - jsonConfig["device_class"] = getValueName(json["class"].as()); - jsonConfig["state_topic"] = topicPrefix + "/" + getValueName(incomingData.deviceType) + "/" + myNet.macToString(sender) + "/status"; - jsonConfig["json_attributes_topic"] = topicPrefix + "/" + getValueName(incomingData.deviceType) + "/" + myNet.macToString(sender) + "/attributes"; - jsonConfig["payload_on"] = json["payload_on"]; - jsonConfig["expire_after"] = json["expire_after"]; + jsonConfig["device_class"] = getValueName(json[MCMT_DEVICE_CLASS].as()); + jsonConfig["state_topic"] = config.topicPrefix + "/" + getValueName(incomingData.deviceType) + "/" + myNet.macToString(sender) + "/status"; + jsonConfig["json_attributes_topic"] = config.topicPrefix + "/" + getValueName(incomingData.deviceType) + "/" + myNet.macToString(sender) + "/attributes"; + jsonConfig["payload_on"] = json[MCMT_PAYLOAD_ON]; + jsonConfig["expire_after"] = json[MCMT_EXPIRE_AFTER]; jsonConfig["force_update"] = "true"; jsonConfig["retain"] = "true"; char buffer[2048]{0}; serializeJsonPretty(jsonConfig, buffer); - mqttPublish((topicPrefix + "/" + getValueName(json["type"].as()) + "/" + myNet.macToString(sender) + "-" + unit + "/config").c_str(), buffer, true); + mqttPublish((config.topicPrefix + "/" + getValueName(json[MCMT_COMPONENT_TYPE].as()) + "/" + myNet.macToString(sender) + "-" + unit + "/config").c_str(), buffer, true); } } if (incomingData.payloadsType == ENPT_FORWARD) { esp_now_payload_data_t forwardData; memcpy(&forwardData.message, &incomingData.message, sizeof(esp_now_payload_data_t::message)); - StaticJsonDocument json; + DynamicJsonDocument json(sizeof(esp_now_payload_data_t::message)); deserializeJson(json, forwardData.message); if (incomingData.deviceType == ENDT_RF_GATEWAY) - mqttPublish((topicPrefix + "/rf_sensor/" + getValueName(json["type"].as()) + "/" + json["id"].as() + "/state").c_str(), incomingData.message, false); + mqttPublish((config.topicPrefix + "/rf_sensor/" + getValueName(json["type"].as()) + "/" + json["id"].as() + "/state").c_str(), incomingData.message, false); } } @@ -410,29 +410,29 @@ void onMqttMessage(char *topic, byte *payload, unsigned int length) esp_now_payload_data_t outgoingData; outgoingData.deviceType = ENDT_GATEWAY; outgoingData.payloadsType = ENPT_SET; - StaticJsonDocument json; + DynamicJsonDocument json(sizeof(esp_now_payload_data_t::message)); if (message == "update" || message == "restart") { if (mac == myNet.getNodeMac() && message == "restart") ESP.restart(); flag = true; } - if (String(topic) == topicPrefix + "/espnow_switch/" + mac + "/set" || String(topic) == topicPrefix + "/espnow_led/" + mac + "/set") + if (String(topic) == config.topicPrefix + "/espnow_switch/" + mac + "/set" || String(topic) == config.topicPrefix + "/espnow_led/" + mac + "/set") { flag = true; json["set"] = message; } - if (String(topic) == topicPrefix + "/espnow_led/" + mac + "/brightness") + if (String(topic) == config.topicPrefix + "/espnow_led/" + mac + "/brightness") { flag = true; json["brightness"] = message; } - if (String(topic) == topicPrefix + "/espnow_led/" + mac + "/temperature") + if (String(topic) == config.topicPrefix + "/espnow_led/" + mac + "/temperature") { flag = true; json["temperature"] = message; } - if (String(topic) == topicPrefix + "/espnow_led/" + mac + "/rgb") + if (String(topic) == config.topicPrefix + "/espnow_led/" + mac + "/rgb") { flag = true; json["rgb"] = message; @@ -456,14 +456,14 @@ void sendKeepAliveMessage() { keepAliveMessageTimerSemaphore = false; if (isMqttAvailable) - mqttPublish((topicPrefix + "/espnow_gateway/" + myNet.getNodeMac() + "/status").c_str(), "online", true); + mqttPublish((config.topicPrefix + "/espnow_gateway/" + myNet.getNodeMac() + "/status").c_str(), "online", true); esp_now_payload_data_t outgoingData; outgoingData.deviceType = ENDT_GATEWAY; outgoingData.payloadsType = ENPT_KEEP_ALIVE; - StaticJsonDocument json; + DynamicJsonDocument json(sizeof(esp_now_payload_data_t::message)); json["MQTT"] = isMqttAvailable ? "online" : "offline"; json["frequency"] = 10; // For compatibility with the previous version. Will be removed in future releases. - if (workMode == ESP_NOW_WIFI && WiFi.isConnected()) + if (config.workMode == ESP_NOW_WIFI && WiFi.isConnected()) { ntpWiFiClient.update(); uint64_t epochTime = ntpWiFiClient.getEpochTime(); @@ -471,7 +471,7 @@ void sendKeepAliveMessage() json["time"] = ntpWiFiClient.getFormattedTime(); json["date"] = String(time->tm_mday) + "." + String(time->tm_mon + 1) + "." + String(time->tm_year + 1900); } - if (workMode == ESP_NOW_LAN && Ethernet.linkStatus() == LinkON) + if (config.workMode == ESP_NOW_LAN && Ethernet.linkStatus() == LinkON) { ntpEthClient.update(); uint64_t epochTime = ntpEthClient.getEpochTime(); @@ -496,7 +496,7 @@ void sendAttributesMessage() uint32_t mins = secs / 60; uint32_t hours = mins / 60; uint32_t days = hours / 24; - StaticJsonDocument json; + DynamicJsonDocument json(sizeof(esp_now_payload_data_t::message)); json["Type"] = "ESP-NOW gateway"; #if defined(ESP8266) json["MCU"] = "ESP8266"; @@ -507,32 +507,32 @@ void sendAttributesMessage() json["MAC"] = myNet.getNodeMac(); json["Firmware"] = firmware; json["Library"] = myNet.getFirmwareVersion(); - if (workMode == ESP_NOW_WIFI) + if (config.workMode == ESP_NOW_WIFI) json["IP"] = WiFi.localIP().toString(); - if (workMode == ESP_NOW_LAN) + if (config.workMode == ESP_NOW_LAN) json["IP"] = Ethernet.localIP().toString(); json["Uptime"] = "Days:" + String(days) + " Hours:" + String(hours - (days * 24)) + " Mins:" + String(mins - (hours * 60)); char buffer[sizeof(esp_now_payload_data_t::message)]{0}; serializeJsonPretty(json, buffer); - mqttPublish((topicPrefix + "/espnow_gateway/" + myNet.getNodeMac() + "/attributes").c_str(), buffer, true); + mqttPublish((config.topicPrefix + "/espnow_gateway/" + myNet.getNodeMac() + "/attributes").c_str(), buffer, true); } void sendConfigMessage() { - StaticJsonDocument<1024> json; + DynamicJsonDocument json(2048); // Same as PubSubClient buffer size. json["platform"] = "mqtt"; - json["name"] = deviceName; + json["name"] = config.deviceName; json["unique_id"] = myNet.getNodeMac() + "-1"; json["device_class"] = "connectivity"; - json["state_topic"] = topicPrefix + "/espnow_gateway/" + myNet.getNodeMac() + "/status"; - json["json_attributes_topic"] = topicPrefix + "/espnow_gateway/" + myNet.getNodeMac() + "/attributes"; + json["state_topic"] = config.topicPrefix + "/espnow_gateway/" + myNet.getNodeMac() + "/status"; + json["json_attributes_topic"] = config.topicPrefix + "/espnow_gateway/" + myNet.getNodeMac() + "/attributes"; json["payload_on"] = "online"; json["expire_after"] = 30; json["force_update"] = "true"; json["retain"] = "true"; char buffer[1024]{0}; serializeJsonPretty(json, buffer); - mqttPublish((topicPrefix + "/binary_sensor/" + myNet.getNodeMac() + "-1" + "/config").c_str(), buffer, true); + mqttPublish((config.topicPrefix + "/binary_sensor/" + myNet.getNodeMac() + "-1" + "/config").c_str(), buffer, true); } String getValue(String data, char separator, uint8_t index) @@ -552,47 +552,27 @@ String getValue(String data, char separator, uint8_t index) void loadConfig() { - if (!LittleFS.exists("/config.json")) + EEPROM.begin(4096); + if (EEPROM.read(4095) == 254) + { + EEPROM.get(0, config); + EEPROM.end(); + } + else + { + EEPROM.end(); saveConfig(); - File file = LittleFS.open("/config.json", "r"); - String jsonFile = file.readString(); - StaticJsonDocument<1024> json; - deserializeJson(json, jsonFile); - espnowNetName = json["espnowNetName"].as(); - deviceName = json["deviceName"].as(); - ssid = json["ssid"].as(); - password = json["password"].as(); - mqttHostName = json["mqttHostName"].as(); - mqttHostPort = json["mqttHostPort"]; - mqttUserLogin = json["mqttUserLogin"].as(); - mqttUserPassword = json["mqttUserPassword"].as(); - topicPrefix = json["topicPrefix"].as(); - workMode = json["workMode"]; - ntpHostName = json["ntpHostName"].as(); - gmtOffset = json["gmtOffset"]; - file.close(); + } + delay(50); } void saveConfig() { - StaticJsonDocument<1024> json; - json["firmware"] = firmware; - json["espnowNetName"] = espnowNetName; - json["deviceName"] = deviceName; - json["ssid"] = ssid; - json["password"] = password; - json["mqttHostName"] = mqttHostName; - json["mqttHostPort"] = mqttHostPort; - json["mqttUserLogin"] = mqttUserLogin; - json["mqttUserPassword"] = mqttUserPassword; - json["topicPrefix"] = topicPrefix; - json["workMode"] = workMode; - json["ntpHostName"] = ntpHostName; - json["gmtOffset"] = gmtOffset; - json["system"] = "empty"; - File file = LittleFS.open("/config.json", "w"); - serializeJsonPretty(json, file); - file.close(); + EEPROM.begin(4096); + EEPROM.write(4095, 254); + EEPROM.put(0, config); + EEPROM.end(); + delay(50); } String xmlNode(String tags, String data) @@ -614,7 +594,7 @@ void setupWebServer() ssdpHeader = xmlNode("specVersion", ssdpHeader); ssdpHeader += xmlNode("URLBase", "http://" + WiFi.localIP().toString()); String ssdpDescription = xmlNode("deviceType", "upnp:rootdevice"); - ssdpDescription += xmlNode("friendlyName", deviceName); + ssdpDescription += xmlNode("friendlyName", config.deviceName); ssdpDescription += xmlNode("presentationURL", "/"); ssdpDescription += xmlNode("serialNumber", "0000000" + String(random(1000))); ssdpDescription += xmlNode("modelName", "ESP-NOW gateway"); @@ -632,38 +612,57 @@ void setupWebServer() webServer.on("/", HTTP_GET, [](AsyncWebServerRequest *request) { request->send(LittleFS, "/index.htm"); }); + webServer.on("/function.js", HTTP_GET, [](AsyncWebServerRequest *request) + { request->send(LittleFS, "/function.js"); }); + + webServer.on("/style.css", HTTP_GET, [](AsyncWebServerRequest *request) + { request->send(LittleFS, "/style.css"); }); + webServer.on("/setting", HTTP_GET, [](AsyncWebServerRequest *request) { - ssid = request->getParam("ssid")->value(); - password = request->getParam("password")->value(); - mqttHostName = request->getParam("mqttHostName")->value(); - mqttHostPort = request->getParam("mqttHostPor")->value().toInt(); - mqttUserLogin = request->getParam("mqttUserLogin")->value(); - mqttUserPassword = request->getParam("mqttUserPassword")->value(); - topicPrefix = request->getParam("topicPrefix")->value(); - deviceName = request->getParam("deviceName")->value(); - espnowNetName = request->getParam("espnowNetName")->value(); - workMode = request->getParam("workMode")->value().toInt(); - ntpHostName = request->getParam("ntpHostName")->value(); - gmtOffset = request->getParam("gmtOffset")->value().toInt(); + config.ssid = request->getParam("ssid")->value(); + config.password = request->getParam("password")->value(); + config.mqttHostName = request->getParam("mqttHostName")->value(); + config.mqttHostPort = request->getParam("mqttHostPort")->value().toInt(); + config.mqttUserLogin = request->getParam("mqttUserLogin")->value(); + config.mqttUserPassword = request->getParam("mqttUserPassword")->value(); + config.topicPrefix = request->getParam("topicPrefix")->value(); + config.deviceName = request->getParam("deviceName")->value(); + config.espnowNetName = request->getParam("espnowNetName")->value(); + config.workMode = request->getParam("workMode")->value().toInt(); + config.ntpHostName = request->getParam("ntpHostName")->value(); + config.gmtOffset = request->getParam("gmtOffset")->value().toInt(); request->send(200); saveConfig(); }); - webServer.on("/restart", HTTP_GET, [](AsyncWebServerRequest *request) + webServer.on("/config", HTTP_GET, [](AsyncWebServerRequest *request) { - request->send(200); + String configJson; + DynamicJsonDocument json(2048); // For overflow protection. + json["firmware"] = firmware; + json["espnowNetName"] = config.espnowNetName; + json["deviceName"] = config.deviceName; + json["ssid"] = config.ssid; + json["password"] = config.password; + json["mqttHostName"] = config.mqttHostName; + json["mqttHostPort"] = config.mqttHostPort; + json["mqttUserLogin"] = config.mqttUserLogin; + json["mqttUserPassword"] = config.mqttUserPassword; + json["topicPrefix"] = config.topicPrefix; + json["workMode"] = config.workMode; + json["ntpHostName"] = config.ntpHostName; + json["gmtOffset"] = config.gmtOffset; + serializeJsonPretty(json, configJson); + request->send(200, "application/json", configJson); }); + + webServer.on("/restart", HTTP_GET, [](AsyncWebServerRequest *request) + {request->send(200); ESP.restart(); }); webServer.onNotFound([](AsyncWebServerRequest *request) - { - if (LittleFS.exists(request->url())) - request->send(LittleFS, request->url()); - else - { - request->send(404, "text/plain", "File Not Found"); - } }); + { request->send(404, "text/plain", "File Not Found"); }); - if (workMode == ESP_NOW_WIFI) + if (config.workMode == ESP_NOW_WIFI) SSDP.begin(); webServer.begin(); @@ -673,18 +672,18 @@ void checkMqttAvailability() { mqttAvailabilityCheckTimerSemaphore = false; - if (workMode == ESP_NOW_WIFI) + if (config.workMode == ESP_NOW_WIFI) if (WiFi.isConnected()) if (!mqttWifiClient.connected()) { isMqttAvailable = false; - if (mqttWifiClient.connect(mqttUserID, mqttUserLogin.c_str(), mqttUserPassword.c_str())) + if (mqttWifiClient.connect(mqttUserID, config.mqttUserLogin.c_str(), config.mqttUserPassword.c_str())) { isMqttAvailable = true; - mqttWifiClient.subscribe((topicPrefix + "/espnow_gateway/#").c_str()); - mqttWifiClient.subscribe((topicPrefix + "/espnow_switch/#").c_str()); - mqttWifiClient.subscribe((topicPrefix + "/espnow_led/#").c_str()); + mqttWifiClient.subscribe((config.topicPrefix + "/espnow_gateway/#").c_str()); + mqttWifiClient.subscribe((config.topicPrefix + "/espnow_switch/#").c_str()); + mqttWifiClient.subscribe((config.topicPrefix + "/espnow_led/#").c_str()); sendConfigMessage(); sendAttributesMessage(); @@ -692,18 +691,18 @@ void checkMqttAvailability() } } - if (workMode == ESP_NOW_LAN) + if (config.workMode == ESP_NOW_LAN) if (Ethernet.linkStatus() == LinkON) if (!mqttEthClient.connected()) { isMqttAvailable = false; - if (mqttEthClient.connect(mqttUserID, mqttUserLogin.c_str(), mqttUserPassword.c_str())) + if (mqttEthClient.connect(mqttUserID, config.mqttUserLogin.c_str(), config.mqttUserPassword.c_str())) { isMqttAvailable = true; - mqttEthClient.subscribe((topicPrefix + "/espnow_gateway/#").c_str()); - mqttEthClient.subscribe((topicPrefix + "/espnow_switch/#").c_str()); - mqttEthClient.subscribe((topicPrefix + "/espnow_led/#").c_str()); + mqttEthClient.subscribe((config.topicPrefix + "/espnow_gateway/#").c_str()); + mqttEthClient.subscribe((config.topicPrefix + "/espnow_switch/#").c_str()); + mqttEthClient.subscribe((config.topicPrefix + "/espnow_led/#").c_str()); sendConfigMessage(); sendAttributesMessage(); @@ -714,9 +713,9 @@ void checkMqttAvailability() void mqttPublish(const char *topic, const char *payload, bool retained) { - if (workMode == ESP_NOW_WIFI) + if (config.workMode == ESP_NOW_WIFI) mqttWifiClient.publish(topic, payload, retained); - if (workMode == ESP_NOW_LAN) + if (config.workMode == ESP_NOW_LAN) mqttEthClient.publish(topic, payload, retained); }