11 Commits
v1.3 ... main

Author SHA1 Message Date
b87092d4e1 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.
2023-03-07 11:54:29 +03:00
0038361bb5 Version 1.5
Changed SDK version for 3.0.5 for ESP8266.
Changed config message for ESP-NOW switch.
Changed config message for ESP-NOW light/led strip controller.
Fixed minor bugs.
2023-03-04 08:07:32 +03:00
894eed59f1 Version 1.43
Minor changes.
2023-02-19 12:19:54 +03:00
69cb07f721 Minor changes 2023-02-17 10:08:02 +03:00
5fafe9a538 Minor changes 2023-02-16 18:21:08 +03:00
cf32071ee0 Version 1.42
Fixed bug with ESP-NOW devices not getting restart or update command.
Minor main code refactoring.
2023-02-13 19:04:46 +03:00
e4572cc31a Version 1.42
Fixed bug with ESP-NOW devices not getting restart or update command.
Minor main code refactoring.
2023-02-13 18:56:39 +03:00
57d0bc6481 Version 1.42
Fixed bug with ESP-NOW devices not getting restart or update command.
Minor main code refactoring.
2023-02-13 18:53:41 +03:00
1e745724e4 Version 1.41
Minor changes.
2023-02-12 18:37:12 +03:00
25ce7e419a Minor changes 2023-02-12 16:41:54 +03:00
15472ab36b Version 1.4
Added support nRF24 devices via RF Gateway.
2023-02-12 16:38:54 +03:00
5 changed files with 278 additions and 205 deletions

View File

@ -9,9 +9,10 @@ Gateway for data exchange between ESP-NOW devices and MQTT broker via WiFi/LAN.
3. Periodically transmission of system information to the MQTT broker (every 60 seconds), availability status to the ESP-NOW network and to the MQTT broker (every 10 seconds) and current date and time to the ESP-NOW network (every 10 seconds). 3. Periodically transmission of system information to the MQTT broker (every 60 seconds), availability status to the ESP-NOW network and to the MQTT broker (every 10 seconds) and current date and time to the ESP-NOW network (every 10 seconds).
4. Automatically adds gateway configuration to Home Assistan via MQTT discovery as a binary_sensor. 4. Automatically adds gateway configuration to Home Assistan via MQTT discovery as a binary_sensor.
5. Automatically adds supported ESP-NOW devices configurations to Home Assistan via MQTT discovery. 5. Automatically adds supported ESP-NOW devices configurations to Home Assistan via MQTT discovery.
6. Possibility firmware update over OTA (at ESP_NOW_LAN mode via access point only). 6. Automatically adds supported nRF24 devices configurations to Home Assistan via MQTT discovery.
7. Web interface for settings (at ESP_NOW_LAN mode via access point only). 7. Possibility firmware update over OTA (at ESP_NOW_LAN mode via access point only).
8. 3 operating modes: 8. Web interface for settings (at ESP_NOW_LAN mode via access point only).
9. 3 operating modes:
```text ```text
ESP_NOW ESP-NOW node only. Default mode after flashing. ESP_NOW ESP-NOW node only. Default mode after flashing.
@ -47,7 +48,7 @@ ESP32 (GPIO05 - CS, GPIO18 - SCK, GPIO19 - MISO, GPIO23 - MOSI).
## Supported devices ## Supported devices
1. [RF Gateway](https://github.com/aZholtikov/RF-Gateway) (coming soon) 1. [RF Gateway](https://github.com/aZholtikov/RF-Gateway)
2. [ESP-NOW Switch](https://github.com/aZholtikov/ESP-NOW-Switch) 2. [ESP-NOW Switch](https://github.com/aZholtikov/ESP-NOW-Switch)
3. [ESP-NOW Light/Led Strip](https://github.com/aZholtikov/ESP-NOW-Light-Led-Strip) 3. [ESP-NOW Light/Led Strip](https://github.com/aZholtikov/ESP-NOW-Light-Led-Strip)
4. [ESP-NOW Window/Door Sensor](https://github.com/aZholtikov/ESP-NOW-Window-Door-Sensor) 4. [ESP-NOW Window/Door Sensor](https://github.com/aZholtikov/ESP-NOW-Window-Door-Sensor)
@ -57,8 +58,8 @@ ESP32 (GPIO05 - CS, GPIO18 - SCK, GPIO19 - MISO, GPIO23 - MOSI).
- [X] Automatically add ESP-NOW devices configurations to Home Assistan via MQTT discovery. - [X] Automatically add ESP-NOW devices configurations to Home Assistan via MQTT discovery.
- [X] LAN connection support. - [X] LAN connection support.
- [ ] nRF24 device support (in current time uses "RF Gateway"). - [X] nRF24 device support (via [RF Gateway](https://github.com/aZholtikov/RF-Gateway)).
- [ ] BLE device support (for ESP32). - [ ] BLE device support (via BLE Gateway).
- [ ] LoRa device support. - [ ] LoRa device support (via LoRa Gateway).
Any feedback via [e-mail](mailto:github@zh.com.ru) would be appreciated. Or... [Buy me a coffee](https://paypal.me/aZholtikov). Any feedback via [e-mail](mailto:github@zh.com.ru) would be appreciated. Or... [Buy me a coffee](https://paypal.me/aZholtikov).

View File

@ -10,7 +10,7 @@ function createXmlHttpObject() {
function load() { function load() {
if (xmlHttp.readyState == 0 || xmlHttp.readyState == 4) { if (xmlHttp.readyState == 0 || xmlHttp.readyState == 4) {
xmlHttp.open('PUT', '/config.json', true); xmlHttp.open('GET', '/config', true);
xmlHttp.send(null); xmlHttp.send(null);
xmlHttp.onload = function () { xmlHttp.onload = function () {
jsonResponse = JSON.parse(xmlHttp.responseText); jsonResponse = JSON.parse(xmlHttp.responseText);
@ -52,14 +52,14 @@ function sendRequest(submit, server) {
function saveSetting(submit) { function saveSetting(submit) {
server = "/setting?ssid=" + getValue('ssid') + "&password=" + encodeURIComponent(getValue('password')) server = "/setting?ssid=" + getValue('ssid') + "&password=" + encodeURIComponent(getValue('password'))
+ "&host=" + getValue('mqttHostName') + "&port=" + getValue('mqttHostPort') + "&mqttHostName=" + getValue('mqttHostName') + "&mqttHostPort=" + getValue('mqttHostPort')
+ "&login=" + getValue('mqttUserLogin') + "&pass=" + encodeURIComponent(getValue('mqttUserPassword')) + "&mqttUserLogin=" + getValue('mqttUserLogin') + "&mqttUserPassword=" + encodeURIComponent(getValue('mqttUserPassword'))
+ "&prefix=" + getValue('topicPrefix') + "&topicPrefix=" + getValue('topicPrefix')
+ "&name=" + getValue('deviceName') + "&deviceName=" + getValue('deviceName')
+ "&net=" + getValue('espnowNetName') + "&espnowNetName=" + getValue('espnowNetName')
+ "&mode=" + getSelectValue('workModeSelect') + "&workMode=" + getSelectValue('workModeSelect')
+ "&ntp=" + getValue('ntpHostName') + "&ntpHostName=" + getValue('ntpHostName')
+ "&zone=" + getValue('gmtOffset'); + "&gmtOffset=" + getValue('gmtOffset');
sendRequest(submit, server); sendRequest(submit, server);
alert("Please restart device for changes apply."); alert("Please restart device for changes apply.");
} }

View File

@ -10,7 +10,7 @@
<body onload="load();"> <body onload="load();">
<form class="box"> <form class="box">
<h1>ESP-NOW Gateway </h1> <h1>ESP-NOW Gateway</h1>
<div class="wrapper"> <div class="wrapper">
<p class="text">Firmware:</p> <p class="text">Firmware:</p>
<p class="text" id="version"></p> <p class="text" id="version"></p>

View File

@ -2,6 +2,7 @@
platform = espressif8266 platform = espressif8266
board = esp12e board = esp12e
framework = arduino framework = arduino
build_flags = -D PIO_FRAMEWORK_ARDUINO_ESPRESSIF_SDK305
board_build.filesystem = littlefs board_build.filesystem = littlefs
lib_deps = lib_deps =
https://github.com/aZholtikov/ZHNetwork https://github.com/aZholtikov/ZHNetwork
@ -16,6 +17,7 @@ lib_deps =
platform = espressif8266 platform = espressif8266
board = esp12e board = esp12e
framework = arduino framework = arduino
build_flags = -D PIO_FRAMEWORK_ARDUINO_ESPRESSIF_SDK305
board_build.filesystem = littlefs board_build.filesystem = littlefs
upload_port = 192.168.4.1 upload_port = 192.168.4.1
upload_protocol = espota upload_protocol = espota

View File

@ -4,6 +4,7 @@
#include "Ethernet.h" // https://github.com/arduino-libraries/Ethernet #include "Ethernet.h" // https://github.com/arduino-libraries/Ethernet
#include "PubSubClient.h" #include "PubSubClient.h"
#include "LittleFS.h" #include "LittleFS.h"
#include "EEPROM.h"
#include "Ticker.h" #include "Ticker.h"
#include "NTPClient.h" #include "NTPClient.h"
#include "ZHNetwork.h" #include "ZHNetwork.h"
@ -15,13 +16,6 @@
#include "ESP32SSDP.h" #include "ESP32SSDP.h"
#endif #endif
typedef enum : uint8_t
{
ESP_NOW,
ESP_NOW_WIFI,
ESP_NOW_LAN
} work_mode_t;
void onEspnowMessage(const char *data, const uint8_t *sender); void onEspnowMessage(const char *data, const uint8_t *sender);
void onMqttMessage(char *topic, byte *payload, unsigned int length); void onMqttMessage(char *topic, byte *payload, unsigned int length);
@ -42,31 +36,37 @@ void checkMqttAvailability(void);
void mqttPublish(const char *topic, const char *payload, bool retained); void mqttPublish(const char *topic, const char *payload, bool retained);
const String firmware{"1.3"}; typedef enum : uint8_t
{
String espnowNetName{"DEFAULT"}; ESP_NOW,
ESP_NOW_WIFI,
uint8_t workMode{ESP_NOW}; ESP_NOW_LAN
} work_mode_t;
struct deviceConfig
{
#if defined(ESP8266) #if defined(ESP8266)
String deviceName = "ESP-NOW gateway " + String(ESP.getChipId(), HEX); String deviceName = "ESP-NOW gateway " + String(ESP.getChipId(), HEX);
#endif #endif
#if defined(ESP32) #if defined(ESP32)
String deviceName = "ESP-NOW gateway " + String(ESP.getEfuseMac(), HEX); String deviceName = "ESP-NOW gateway " + String(ESP.getEfuseMac(), HEX);
#endif #endif
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;
String ssid{"SSID"}; const String firmware{"1.6"};
String password{"PASSWORD"};
String mqttHostName{"MQTT"}; const char *mqttUserID{"ESP"};
uint16_t mqttHostPort{1883};
String mqttUserLogin{""};
String mqttUserPassword{""};
String topicPrefix{"homeassistant"};
const char *mqttUserID{"ESP32"};
String ntpHostName{"NTP"};
uint16_t gmtOffset{10800};
uint8_t w5500Mac[6] = {0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED}; // Change it if necessary. uint8_t w5500Mac[6] = {0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED}; // Change it if necessary.
@ -82,8 +82,8 @@ PubSubClient mqttWifiClient(wifiClient);
WiFiUDP udpWiFiClient; WiFiUDP udpWiFiClient;
EthernetUDP udpEthClient; EthernetUDP udpEthClient;
NTPClient ntpWiFiClient(udpWiFiClient, ntpHostName.c_str(), gmtOffset); NTPClient ntpWiFiClient(udpWiFiClient, config.ntpHostName.c_str(), config.gmtOffset);
NTPClient ntpEthClient(udpEthClient, ntpHostName.c_str(), gmtOffset); NTPClient ntpEthClient(udpEthClient, config.ntpHostName.c_str(), config.gmtOffset);
Ticker mqttAvailabilityCheckTimer; Ticker mqttAvailabilityCheckTimer;
bool mqttAvailabilityCheckTimerSemaphore{true}; bool mqttAvailabilityCheckTimerSemaphore{true};
@ -109,13 +109,13 @@ void setup()
loadConfig(); loadConfig();
if (workMode == ESP_NOW_LAN) if (config.workMode == ESP_NOW_LAN)
{ {
Ethernet.init(5); Ethernet.init(5);
Ethernet.begin(w5500Mac); Ethernet.begin(w5500Mac);
} }
if (workMode == ESP_NOW_WIFI) if (config.workMode == ESP_NOW_WIFI)
{ {
#if defined(ESP8266) #if defined(ESP8266)
WiFi.setSleepMode(WIFI_NONE_SLEEP); WiFi.setSleepMode(WIFI_NONE_SLEEP);
@ -128,9 +128,9 @@ void setup()
WiFi.setAutoReconnect(true); 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.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); myNet.setOnBroadcastReceivingCallback(onEspnowMessage);
@ -144,7 +144,7 @@ void setup()
WiFi.softAP(("ESP-NOW gateway " + String(ESP.getEfuseMac(), HEX)).c_str(), "12345678"); WiFi.softAP(("ESP-NOW gateway " + String(ESP.getEfuseMac(), HEX)).c_str(), "12345678");
#endif #endif
if (workMode == ESP_NOW_WIFI) if (config.workMode == ESP_NOW_WIFI)
{ {
uint8_t scan = WiFi.scanNetworks(false, false); uint8_t scan = WiFi.scanNetworks(false, false);
String name; String name;
@ -161,24 +161,24 @@ void setup()
#if defined(ESP32) #if defined(ESP32)
WiFi.getNetworkInfo(i, name, encryption, rssi, bssid, channel); WiFi.getNetworkInfo(i, name, encryption, rssi, bssid, channel);
#endif #endif
if (name == ssid) if (name == config.ssid)
WiFi.begin(ssid.c_str(), password.c_str()); WiFi.begin(config.ssid.c_str(), config.password.c_str());
} }
} }
if (workMode == ESP_NOW_WIFI) if (config.workMode == ESP_NOW_WIFI)
{ {
ntpWiFiClient.begin(); ntpWiFiClient.begin();
mqttWifiClient.setBufferSize(2048); mqttWifiClient.setBufferSize(2048);
mqttWifiClient.setServer(mqttHostName.c_str(), mqttHostPort); mqttWifiClient.setServer(config.mqttHostName.c_str(), config.mqttHostPort);
mqttWifiClient.setCallback(onMqttMessage); mqttWifiClient.setCallback(onMqttMessage);
} }
if (workMode == ESP_NOW_LAN) if (config.workMode == ESP_NOW_LAN)
{ {
ntpEthClient.begin(); ntpEthClient.begin();
mqttEthClient.setBufferSize(2048); mqttEthClient.setBufferSize(2048);
mqttEthClient.setServer(mqttHostName.c_str(), mqttHostPort); mqttEthClient.setServer(config.mqttHostName.c_str(), config.mqttHostPort);
mqttEthClient.setCallback(onMqttMessage); mqttEthClient.setCallback(onMqttMessage);
} }
@ -199,9 +199,9 @@ void loop()
sendKeepAliveMessage(); sendKeepAliveMessage();
if (attributesMessageTimerSemaphore) if (attributesMessageTimerSemaphore)
sendAttributesMessage(); sendAttributesMessage();
if (workMode == ESP_NOW_WIFI) if (config.workMode == ESP_NOW_WIFI)
mqttWifiClient.loop(); mqttWifiClient.loop();
if (workMode == ESP_NOW_LAN) if (config.workMode == ESP_NOW_LAN)
mqttEthClient.loop(); mqttEthClient.loop();
myNet.maintenance(); myNet.maintenance();
ArduinoOTA.handle(); ArduinoOTA.handle();
@ -214,117 +214,187 @@ void onEspnowMessage(const char *data, const uint8_t *sender)
esp_now_payload_data_t incomingData; esp_now_payload_data_t incomingData;
memcpy(&incomingData, data, sizeof(esp_now_payload_data_t)); memcpy(&incomingData, data, sizeof(esp_now_payload_data_t));
if (incomingData.payloadsType == ENPT_ATTRIBUTES) 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) 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) 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.payloadsType == ENPT_CONFIG)
{ {
if (incomingData.deviceType == ENDT_SWITCH) if (incomingData.deviceType == ENDT_SWITCH)
{ {
esp_now_payload_data_t configData; esp_now_payload_data_t configData;
memcpy(&configData.message, &incomingData.message, sizeof(esp_now_payload_data_t::message)); memcpy(&configData.message, &incomingData.message, sizeof(esp_now_payload_data_t::message));
StaticJsonDocument<sizeof(esp_now_payload_data_t::message)> json; DynamicJsonDocument json(sizeof(esp_now_payload_data_t::message));
deserializeJson(json, configData.message); deserializeJson(json, configData.message);
uint8_t unit = json["unit"].as<uint8_t>(); uint8_t unit = json[MCMT_DEVICE_UNIT].as<uint8_t>();
ha_component_type_t type = json["type"].as<ha_component_type_t>(); DynamicJsonDocument jsonConfig(2048); // Same as PubSubClient buffer size.
StaticJsonDocument<2048> jsonConfig;
jsonConfig["platform"] = "mqtt"; jsonConfig["platform"] = "mqtt";
jsonConfig["name"] = json["name"]; jsonConfig["name"] = json[MCMT_DEVICE_NAME];
jsonConfig["unique_id"] = myNet.macToString(sender) + "-" + unit; jsonConfig["unique_id"] = myNet.macToString(sender) + "-" + unit;
jsonConfig["device_class"] = getValueName(json["class"].as<ha_switch_device_class_t>()); jsonConfig["device_class"] = getValueName(json[MCMT_DEVICE_CLASS].as<ha_switch_device_class_t>());
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["template"].as<String>() + " }}"; jsonConfig["value_template"] = "{{ value_json." + json[MCMT_VALUE_TEMPLATE].as<String>() + " }}";
jsonConfig["command_topic"] = topicPrefix + "/" + getValueName(incomingData.deviceType) + "/" + myNet.macToString(sender) + "/set"; jsonConfig["command_topic"] = config.topicPrefix + "/" + getValueName(incomingData.deviceType) + "/" + myNet.macToString(sender) + "/set";
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["availability_topic"] = topicPrefix + "/" + getValueName(incomingData.deviceType) + "/" + myNet.macToString(sender) + "/status"; jsonConfig["availability_topic"] = config.topicPrefix + "/" + getValueName(incomingData.deviceType) + "/" + myNet.macToString(sender) + "/status";
jsonConfig["payload_on"] = json["payload_on"]; if (json[MCMT_PAYLOAD_ON])
jsonConfig["payload_off"] = json["payload_off"]; jsonConfig["payload_on"] = json[MCMT_PAYLOAD_ON];
if (json[MCMT_PAYLOAD_OFF])
jsonConfig["payload_off"] = json[MCMT_PAYLOAD_OFF];
jsonConfig["optimistic"] = "false"; jsonConfig["optimistic"] = "false";
jsonConfig["retain"] = "true"; jsonConfig["retain"] = "true";
char buffer[2048]{0}; char buffer[2048]{0};
serializeJsonPretty(jsonConfig, buffer); serializeJsonPretty(jsonConfig, buffer);
mqttPublish((topicPrefix + "/" + getValueName(type) + "/" + myNet.macToString(sender) + "-" + unit + "/config").c_str(), buffer, true); mqttPublish((config.topicPrefix + "/" + getValueName(json[MCMT_COMPONENT_TYPE].as<ha_component_type_t>()) + "/" + myNet.macToString(sender) + "-" + unit + "/config").c_str(), buffer, true);
} }
if (incomingData.deviceType == ENDT_LED) if (incomingData.deviceType == ENDT_LED)
{ {
esp_now_payload_data_t configData; esp_now_payload_data_t configData;
memcpy(&configData.message, &incomingData.message, sizeof(esp_now_payload_data_t::message)); memcpy(&configData.message, &incomingData.message, sizeof(esp_now_payload_data_t::message));
StaticJsonDocument<sizeof(esp_now_payload_data_t::message)> json; DynamicJsonDocument json(sizeof(esp_now_payload_data_t::message));
deserializeJson(json, configData.message); deserializeJson(json, configData.message);
uint8_t unit = json["unit"].as<uint8_t>(); uint8_t unit = json[MCMT_DEVICE_UNIT].as<uint8_t>();
ha_component_type_t type = json["type"].as<ha_component_type_t>(); esp_now_led_type_t ledClass = json[MCMT_DEVICE_CLASS];
esp_now_led_type_t ledClass = json["class"]; DynamicJsonDocument jsonConfig(2048); // Same as PubSubClient buffer size.
StaticJsonDocument<2048> jsonConfig;
jsonConfig["platform"] = "mqtt"; jsonConfig["platform"] = "mqtt";
jsonConfig["name"] = json["name"]; jsonConfig["name"] = json[MCMT_DEVICE_NAME];
jsonConfig["unique_id"] = myNet.macToString(sender) + "-" + unit; 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["state_value_template"] = "{{ value_json.state }}";
jsonConfig["command_topic"] = topicPrefix + "/" + getValueName(incomingData.deviceType) + "/" + myNet.macToString(sender) + "/set"; jsonConfig["command_topic"] = config.topicPrefix + "/" + getValueName(incomingData.deviceType) + "/" + myNet.macToString(sender) + "/set";
jsonConfig["brightness_state_topic"] = topicPrefix + "/" + getValueName(incomingData.deviceType) + "/" + myNet.macToString(sender) + "/state"; jsonConfig["brightness_state_topic"] = config.topicPrefix + "/" + getValueName(incomingData.deviceType) + "/" + myNet.macToString(sender) + "/state";
jsonConfig["brightness_value_template"] = "{{ value_json.brightness }}"; 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) 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_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) 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_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["json_attributes_topic"] = config.topicPrefix + "/" + getValueName(incomingData.deviceType) + "/" + myNet.macToString(sender) + "/attributes";
jsonConfig["availability_topic"] = topicPrefix + "/" + getValueName(incomingData.deviceType) + "/" + myNet.macToString(sender) + "/status"; jsonConfig["availability_topic"] = config.topicPrefix + "/" + getValueName(incomingData.deviceType) + "/" + myNet.macToString(sender) + "/status";
jsonConfig["payload_on"] = json["payload_on"]; if (json[MCMT_PAYLOAD_ON])
jsonConfig["payload_off"] = json["payload_off"]; jsonConfig["payload_on"] = json[MCMT_PAYLOAD_ON];
if (json[MCMT_PAYLOAD_OFF])
jsonConfig["payload_off"] = json[MCMT_PAYLOAD_OFF];
jsonConfig["optimistic"] = "false"; jsonConfig["optimistic"] = "false";
jsonConfig["retain"] = "true"; jsonConfig["retain"] = "true";
char buffer[2048]{0}; char buffer[2048]{0};
serializeJsonPretty(jsonConfig, buffer); serializeJsonPretty(jsonConfig, buffer);
mqttPublish((topicPrefix + "/" + getValueName(type) + "/" + myNet.macToString(sender) + "-" + unit + "/config").c_str(), buffer, true); mqttPublish((config.topicPrefix + "/" + getValueName(json[MCMT_COMPONENT_TYPE].as<ha_component_type_t>()) + "/" + myNet.macToString(sender) + "-" + unit + "/config").c_str(), buffer, true);
} }
if (incomingData.deviceType == ENDT_SENSOR) if (incomingData.deviceType == ENDT_SENSOR)
{ {
esp_now_payload_data_t configData; esp_now_payload_data_t configData;
memcpy(&configData.message, &incomingData.message, sizeof(esp_now_payload_data_t::message)); memcpy(&configData.message, &incomingData.message, sizeof(esp_now_payload_data_t::message));
StaticJsonDocument<sizeof(esp_now_payload_data_t::message)> json; DynamicJsonDocument json(sizeof(esp_now_payload_data_t::message));
deserializeJson(json, configData.message); deserializeJson(json, configData.message);
uint8_t unit = json["unit"].as<uint8_t>(); uint8_t unit = json[MCMT_DEVICE_UNIT].as<uint8_t>();
ha_component_type_t type = json["type"].as<ha_component_type_t>(); ha_component_type_t type = json[MCMT_COMPONENT_TYPE].as<ha_component_type_t>();
StaticJsonDocument<2048> jsonConfig; DynamicJsonDocument jsonConfig(2048); // Same as PubSubClient buffer size.
jsonConfig["platform"] = "mqtt"; jsonConfig["platform"] = "mqtt";
jsonConfig["name"] = json["name"]; jsonConfig["name"] = json[MCMT_DEVICE_NAME];
jsonConfig["unique_id"] = myNet.macToString(sender) + "-" + unit; 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["template"].as<String>() + " }}"; jsonConfig["value_template"] = "{{ value_json." + json[MCMT_VALUE_TEMPLATE].as<String>() + " }}";
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["force_update"] = "true";
jsonConfig["retain"] = "true"; jsonConfig["retain"] = "true";
if (type == HACT_SENSOR) if (type == HACT_SENSOR)
jsonConfig["device_class"] = getValueName(json["class"].as<ha_sensor_device_class_t>());
if (type == HACT_BINARY_SENSOR)
{ {
jsonConfig["device_class"] = getValueName(json["class"].as<ha_binary_sensor_device_class_t>()); jsonConfig["device_class"] = getValueName(json[MCMT_DEVICE_CLASS].as<ha_sensor_device_class_t>());
jsonConfig["payload_on"] = json["payload_on"]; jsonConfig["unit_of_measurement"] = json[MCMT_UNIT_OF_MEASUREMENT];
jsonConfig["payload_off"] = json["payload_off"];
} }
if (type == HACT_BINARY_SENSOR)
jsonConfig["device_class"] = getValueName(json[MCMT_DEVICE_CLASS].as<ha_binary_sensor_device_class_t>());
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}; char buffer[2048]{0};
serializeJsonPretty(jsonConfig, buffer); 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));
DynamicJsonDocument json(sizeof(esp_now_payload_data_t::message));
deserializeJson(json, configData.message);
uint8_t unit = json[MCMT_DEVICE_UNIT].as<uint8_t>();
ha_component_type_t haComponentType = json[MCMT_COMPONENT_TYPE].as<ha_component_type_t>();
rf_sensor_type_t rfSensorType = json[MCMT_RF_SENSOR_TYPE].as<rf_sensor_type_t>();
uint16_t rfSensorId = json[MCMT_RF_SENSOR_ID].as<uint16_t>();
String valueTemplate = json[MCMT_VALUE_TEMPLATE].as<String>();
DynamicJsonDocument jsonConfig(2048); // Same as PubSubClient buffer size.
jsonConfig["platform"] = "mqtt";
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<ha_sensor_device_class_t>());
jsonConfig["unit_of_measurement"] = json[MCMT_UNIT_OF_MEASUREMENT];
}
if (haComponentType == HACT_BINARY_SENSOR)
jsonConfig["device_class"] = getValueName(json[MCMT_DEVICE_CLASS].as<ha_binary_sensor_device_class_t>());
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((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));
DynamicJsonDocument json(sizeof(esp_now_payload_data_t::message));
deserializeJson(json, configData.message);
uint8_t unit = json[MCMT_DEVICE_UNIT].as<uint8_t>();
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<ha_binary_sensor_device_class_t>());
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((config.topicPrefix + "/" + getValueName(json[MCMT_COMPONENT_TYPE].as<ha_component_type_t>()) + "/" + myNet.macToString(sender) + "-" + unit + "/config").c_str(), buffer, true);
} }
} }
if (incomingData.payloadsType == ENPT_FORWARD) if (incomingData.payloadsType == ENPT_FORWARD)
{ {
esp_now_payload_data_t forwardData; esp_now_payload_data_t forwardData;
memcpy(&forwardData.message, &incomingData.message, sizeof(esp_now_payload_data_t::message)); memcpy(&forwardData.message, &incomingData.message, sizeof(esp_now_payload_data_t::message));
StaticJsonDocument<sizeof(esp_now_payload_data_t::message)> json; DynamicJsonDocument json(sizeof(esp_now_payload_data_t::message));
deserializeJson(json, forwardData.message); deserializeJson(json, forwardData.message);
mqttPublish((topicPrefix + "/rf_sensor/" + getValueName(json["type"].as<rf_sensor_type_t>()) + "/" + json["id"].as<uint16_t>()).c_str(), incomingData.message, false); if (incomingData.deviceType == ENDT_RF_GATEWAY)
mqttPublish((config.topicPrefix + "/rf_sensor/" + getValueName(json["type"].as<rf_sensor_type_t>()) + "/" + json["id"].as<uint16_t>() + "/state").c_str(), incomingData.message, false);
} }
} }
@ -339,31 +409,30 @@ void onMqttMessage(char *topic, byte *payload, unsigned int length)
} }
esp_now_payload_data_t outgoingData; esp_now_payload_data_t outgoingData;
outgoingData.deviceType = ENDT_GATEWAY; outgoingData.deviceType = ENDT_GATEWAY;
StaticJsonDocument<sizeof(esp_now_payload_data_t::message)> json; outgoingData.payloadsType = ENPT_SET;
DynamicJsonDocument json(sizeof(esp_now_payload_data_t::message));
if (message == "update" || message == "restart") if (message == "update" || message == "restart")
{ {
mqttPublish(topic, "", true);
mqttPublish((String(topic) + "/status").c_str(), "offline", true);
if (mac == myNet.getNodeMac() && message == "restart") if (mac == myNet.getNodeMac() && message == "restart")
ESP.restart(); ESP.restart();
flag = true; 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; flag = true;
json["set"] = message; json["set"] = message;
} }
if (String(topic) == topicPrefix + "/espnow_led/" + mac + "/brightness") if (String(topic) == config.topicPrefix + "/espnow_led/" + mac + "/brightness")
{ {
flag = true; flag = true;
json["brightness"] = message; json["brightness"] = message;
} }
if (String(topic) == topicPrefix + "/espnow_led/" + mac + "/temperature") if (String(topic) == config.topicPrefix + "/espnow_led/" + mac + "/temperature")
{ {
flag = true; flag = true;
json["temperature"] = message; json["temperature"] = message;
} }
if (String(topic) == topicPrefix + "/espnow_led/" + mac + "/rgb") if (String(topic) == config.topicPrefix + "/espnow_led/" + mac + "/rgb")
{ {
flag = true; flag = true;
json["rgb"] = message; json["rgb"] = message;
@ -372,11 +441,9 @@ void onMqttMessage(char *topic, byte *payload, unsigned int length)
{ {
if (message == "restart") if (message == "restart")
outgoingData.payloadsType = ENPT_RESTART; outgoingData.payloadsType = ENPT_RESTART;
else if (message == "update")
outgoingData.payloadsType = message == "update" ? ENPT_UPDATE : ENPT_SET; outgoingData.payloadsType = ENPT_UPDATE;
char buffer[sizeof(esp_now_payload_data_t::message)]{0}; serializeJsonPretty(json, outgoingData.message);
serializeJsonPretty(json, buffer);
memcpy(&outgoingData.message, &buffer, sizeof(esp_now_payload_data_t::message));
char temp[sizeof(esp_now_payload_data_t)]{0}; char temp[sizeof(esp_now_payload_data_t)]{0};
memcpy(&temp, &outgoingData, sizeof(esp_now_payload_data_t)); memcpy(&temp, &outgoingData, sizeof(esp_now_payload_data_t));
uint8_t target[6]; uint8_t target[6];
@ -389,14 +456,14 @@ void sendKeepAliveMessage()
{ {
keepAliveMessageTimerSemaphore = false; keepAliveMessageTimerSemaphore = false;
if (isMqttAvailable) 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; esp_now_payload_data_t outgoingData;
outgoingData.deviceType = ENDT_GATEWAY; outgoingData.deviceType = ENDT_GATEWAY;
outgoingData.payloadsType = ENPT_KEEP_ALIVE; outgoingData.payloadsType = ENPT_KEEP_ALIVE;
StaticJsonDocument<sizeof(esp_now_payload_data_t::message)> json; DynamicJsonDocument json(sizeof(esp_now_payload_data_t::message));
json["MQTT"] = isMqttAvailable ? "online" : "offline"; json["MQTT"] = isMqttAvailable ? "online" : "offline";
json["frequency"] = 10; // For compatibility with the previous version. Will be removed in future releases. json["frequency"] = 10; // For compatibility with the previous version. Will be removed in future releases.
if (workMode == ESP_NOW_WIFI) if (config.workMode == ESP_NOW_WIFI && WiFi.isConnected())
{ {
ntpWiFiClient.update(); ntpWiFiClient.update();
uint64_t epochTime = ntpWiFiClient.getEpochTime(); uint64_t epochTime = ntpWiFiClient.getEpochTime();
@ -404,7 +471,7 @@ void sendKeepAliveMessage()
json["time"] = ntpWiFiClient.getFormattedTime(); json["time"] = ntpWiFiClient.getFormattedTime();
json["date"] = String(time->tm_mday) + "." + String(time->tm_mon + 1) + "." + String(time->tm_year + 1900); json["date"] = String(time->tm_mday) + "." + String(time->tm_mon + 1) + "." + String(time->tm_year + 1900);
} }
if (workMode == ESP_NOW_LAN) if (config.workMode == ESP_NOW_LAN && Ethernet.linkStatus() == LinkON)
{ {
ntpEthClient.update(); ntpEthClient.update();
uint64_t epochTime = ntpEthClient.getEpochTime(); uint64_t epochTime = ntpEthClient.getEpochTime();
@ -429,7 +496,7 @@ void sendAttributesMessage()
uint32_t mins = secs / 60; uint32_t mins = secs / 60;
uint32_t hours = mins / 60; uint32_t hours = mins / 60;
uint32_t days = hours / 24; uint32_t days = hours / 24;
StaticJsonDocument<sizeof(esp_now_payload_data_t::message)> json; DynamicJsonDocument json(sizeof(esp_now_payload_data_t::message));
json["Type"] = "ESP-NOW gateway"; json["Type"] = "ESP-NOW gateway";
#if defined(ESP8266) #if defined(ESP8266)
json["MCU"] = "ESP8266"; json["MCU"] = "ESP8266";
@ -440,32 +507,32 @@ void sendAttributesMessage()
json["MAC"] = myNet.getNodeMac(); json["MAC"] = myNet.getNodeMac();
json["Firmware"] = firmware; json["Firmware"] = firmware;
json["Library"] = myNet.getFirmwareVersion(); json["Library"] = myNet.getFirmwareVersion();
if (workMode == ESP_NOW_WIFI) if (config.workMode == ESP_NOW_WIFI)
json["IP"] = WiFi.localIP().toString(); json["IP"] = WiFi.localIP().toString();
if (workMode == ESP_NOW_LAN) if (config.workMode == ESP_NOW_LAN)
json["IP"] = Ethernet.localIP().toString(); json["IP"] = Ethernet.localIP().toString();
json["Uptime"] = "Days:" + String(days) + " Hours:" + String(hours - (days * 24)) + " Mins:" + String(mins - (hours * 60)); 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}; char buffer[sizeof(esp_now_payload_data_t::message)]{0};
serializeJsonPretty(json, buffer); 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() void sendConfigMessage()
{ {
StaticJsonDocument<1024> json; DynamicJsonDocument json(2048); // Same as PubSubClient buffer size.
json["platform"] = "mqtt"; json["platform"] = "mqtt";
json["name"] = deviceName; json["name"] = config.deviceName;
json["unique_id"] = myNet.getNodeMac() + "-1"; json["unique_id"] = myNet.getNodeMac() + "-1";
json["device_class"] = "connectivity"; json["device_class"] = "connectivity";
json["state_topic"] = topicPrefix + "/espnow_gateway/" + myNet.getNodeMac() + "/status"; json["state_topic"] = config.topicPrefix + "/espnow_gateway/" + myNet.getNodeMac() + "/status";
json["json_attributes_topic"] = topicPrefix + "/espnow_gateway/" + myNet.getNodeMac() + "/attributes"; json["json_attributes_topic"] = config.topicPrefix + "/espnow_gateway/" + myNet.getNodeMac() + "/attributes";
json["payload_on"] = "online"; json["payload_on"] = "online";
json["expire_after"] = 30; json["expire_after"] = 30;
json["force_update"] = "true"; json["force_update"] = "true";
json["retain"] = "true"; json["retain"] = "true";
char buffer[1024]{0}; char buffer[1024]{0};
serializeJsonPretty(json, buffer); 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) String getValue(String data, char separator, uint8_t index)
@ -485,47 +552,27 @@ String getValue(String data, char separator, uint8_t index)
void loadConfig() 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(); saveConfig();
File file = LittleFS.open("/config.json", "r"); }
String jsonFile = file.readString(); delay(50);
StaticJsonDocument<1024> json;
deserializeJson(json, jsonFile);
espnowNetName = json["espnowNetName"].as<String>();
deviceName = json["deviceName"].as<String>();
ssid = json["ssid"].as<String>();
password = json["password"].as<String>();
mqttHostName = json["mqttHostName"].as<String>();
mqttHostPort = json["mqttHostPort"];
mqttUserLogin = json["mqttUserLogin"].as<String>();
mqttUserPassword = json["mqttUserPassword"].as<String>();
topicPrefix = json["topicPrefix"].as<String>();
workMode = json["workMode"];
ntpHostName = json["ntpHostName"].as<String>();
gmtOffset = json["gmtOffset"];
file.close();
} }
void saveConfig() void saveConfig()
{ {
StaticJsonDocument<1024> json; EEPROM.begin(4096);
json["firmware"] = firmware; EEPROM.write(4095, 254);
json["espnowNetName"] = espnowNetName; EEPROM.put(0, config);
json["deviceName"] = deviceName; EEPROM.end();
json["ssid"] = ssid; delay(50);
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();
} }
String xmlNode(String tags, String data) String xmlNode(String tags, String data)
@ -547,7 +594,7 @@ void setupWebServer()
ssdpHeader = xmlNode("specVersion", ssdpHeader); ssdpHeader = xmlNode("specVersion", ssdpHeader);
ssdpHeader += xmlNode("URLBase", "http://" + WiFi.localIP().toString()); ssdpHeader += xmlNode("URLBase", "http://" + WiFi.localIP().toString());
String ssdpDescription = xmlNode("deviceType", "upnp:rootdevice"); String ssdpDescription = xmlNode("deviceType", "upnp:rootdevice");
ssdpDescription += xmlNode("friendlyName", deviceName); ssdpDescription += xmlNode("friendlyName", config.deviceName);
ssdpDescription += xmlNode("presentationURL", "/"); ssdpDescription += xmlNode("presentationURL", "/");
ssdpDescription += xmlNode("serialNumber", "0000000" + String(random(1000))); ssdpDescription += xmlNode("serialNumber", "0000000" + String(random(1000)));
ssdpDescription += xmlNode("modelName", "ESP-NOW gateway"); ssdpDescription += xmlNode("modelName", "ESP-NOW gateway");
@ -565,38 +612,57 @@ void setupWebServer()
webServer.on("/", HTTP_GET, [](AsyncWebServerRequest *request) webServer.on("/", HTTP_GET, [](AsyncWebServerRequest *request)
{ request->send(LittleFS, "/index.htm"); }); { 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) webServer.on("/setting", HTTP_GET, [](AsyncWebServerRequest *request)
{ {
ssid = request->getParam("ssid")->value(); config.ssid = request->getParam("ssid")->value();
password = request->getParam("password")->value(); config.password = request->getParam("password")->value();
mqttHostName = request->getParam("host")->value(); config.mqttHostName = request->getParam("mqttHostName")->value();
mqttHostPort = request->getParam("port")->value().toInt(); config.mqttHostPort = request->getParam("mqttHostPort")->value().toInt();
mqttUserLogin = request->getParam("login")->value(); config.mqttUserLogin = request->getParam("mqttUserLogin")->value();
mqttUserPassword = request->getParam("pass")->value(); config.mqttUserPassword = request->getParam("mqttUserPassword")->value();
topicPrefix = request->getParam("prefix")->value(); config.topicPrefix = request->getParam("topicPrefix")->value();
deviceName = request->getParam("name")->value(); config.deviceName = request->getParam("deviceName")->value();
espnowNetName = request->getParam("net")->value(); config.espnowNetName = request->getParam("espnowNetName")->value();
workMode = request->getParam("mode")->value().toInt(); config.workMode = request->getParam("workMode")->value().toInt();
ntpHostName = request->getParam("ntp")->value(); config.ntpHostName = request->getParam("ntpHostName")->value();
gmtOffset = request->getParam("zone")->value().toInt(); config.gmtOffset = request->getParam("gmtOffset")->value().toInt();
request->send(200); request->send(200);
saveConfig(); }); 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(); }); ESP.restart(); });
webServer.onNotFound([](AsyncWebServerRequest *request) webServer.onNotFound([](AsyncWebServerRequest *request)
{ { request->send(404, "text/plain", "File Not Found"); });
if (LittleFS.exists(request->url()))
request->send(LittleFS, request->url());
else
{
request->send(404, "text/plain", "File Not Found");
} });
if (workMode == ESP_NOW_WIFI) if (config.workMode == ESP_NOW_WIFI)
SSDP.begin(); SSDP.begin();
webServer.begin(); webServer.begin();
@ -606,46 +672,50 @@ void checkMqttAvailability()
{ {
mqttAvailabilityCheckTimerSemaphore = false; mqttAvailabilityCheckTimerSemaphore = false;
if (workMode == ESP_NOW_WIFI) if (config.workMode == ESP_NOW_WIFI)
if (WiFi.isConnected()) if (WiFi.isConnected())
if (!mqttWifiClient.connected()) if (!mqttWifiClient.connected())
{ {
isMqttAvailable = false; 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; isMqttAvailable = true;
mqttWifiClient.subscribe((topicPrefix + "/espnow_gateway/#").c_str()); mqttWifiClient.subscribe((config.topicPrefix + "/espnow_gateway/#").c_str());
mqttWifiClient.subscribe((topicPrefix + "/espnow_switch/#").c_str()); mqttWifiClient.subscribe((config.topicPrefix + "/espnow_switch/#").c_str());
mqttWifiClient.subscribe((topicPrefix + "/espnow_led/#").c_str()); mqttWifiClient.subscribe((config.topicPrefix + "/espnow_led/#").c_str());
sendConfigMessage(); sendConfigMessage();
sendAttributesMessage();
sendKeepAliveMessage();
} }
} }
if (workMode == ESP_NOW_LAN) if (config.workMode == ESP_NOW_LAN)
if (Ethernet.linkStatus() == LinkON) if (Ethernet.linkStatus() == LinkON)
if (!mqttEthClient.connected()) if (!mqttEthClient.connected())
{ {
isMqttAvailable = false; 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; isMqttAvailable = true;
mqttEthClient.subscribe((topicPrefix + "/espnow_gateway/#").c_str()); mqttEthClient.subscribe((config.topicPrefix + "/espnow_gateway/#").c_str());
mqttEthClient.subscribe((topicPrefix + "/espnow_switch/#").c_str()); mqttEthClient.subscribe((config.topicPrefix + "/espnow_switch/#").c_str());
mqttEthClient.subscribe((topicPrefix + "/espnow_led/#").c_str()); mqttEthClient.subscribe((config.topicPrefix + "/espnow_led/#").c_str());
sendConfigMessage(); sendConfigMessage();
sendAttributesMessage();
sendKeepAliveMessage();
} }
} }
} }
void mqttPublish(const char *topic, const char *payload, bool retained) 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); mqttWifiClient.publish(topic, payload, retained);
if (workMode == ESP_NOW_LAN) if (config.workMode == ESP_NOW_LAN)
mqttEthClient.publish(topic, payload, retained); mqttEthClient.publish(topic, payload, retained);
} }