10 Commits
v1.3 ... v1.5

Author SHA1 Message Date
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
4 changed files with 141 additions and 67 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).
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.
6. Possibility firmware update over OTA (at ESP_NOW_LAN mode via access point only).
7. Web interface for settings (at ESP_NOW_LAN mode via access point only).
8. 3 operating modes:
6. Automatically adds supported nRF24 devices configurations to Home Assistan via MQTT discovery.
7. Possibility firmware update over OTA (at ESP_NOW_LAN mode via access point only).
8. Web interface for settings (at ESP_NOW_LAN mode via access point only).
9. 3 operating modes:
```text
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
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)
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)
@ -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] LAN connection support.
- [ ] nRF24 device support (in current time uses "RF Gateway").
- [ ] BLE device support (for ESP32).
- [ ] LoRa device support.
- [X] nRF24 device support (via [RF Gateway](https://github.com/aZholtikov/RF-Gateway)).
- [ ] BLE device support (via BLE Gateway).
- [ ] 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).

View File

@ -52,14 +52,14 @@ function sendRequest(submit, server) {
function saveSetting(submit) {
server = "/setting?ssid=" + getValue('ssid') + "&password=" + encodeURIComponent(getValue('password'))
+ "&host=" + getValue('mqttHostName') + "&port=" + getValue('mqttHostPort')
+ "&login=" + getValue('mqttUserLogin') + "&pass=" + encodeURIComponent(getValue('mqttUserPassword'))
+ "&prefix=" + getValue('topicPrefix')
+ "&name=" + getValue('deviceName')
+ "&net=" + getValue('espnowNetName')
+ "&mode=" + getSelectValue('workModeSelect')
+ "&ntp=" + getValue('ntpHostName')
+ "&zone=" + getValue('gmtOffset');
+ "&mqttHostName=" + getValue('mqttHostName') + "&mqttHostPort=" + getValue('mqttHostPort')
+ "&mqttUserLogin=" + getValue('mqttUserLogin') + "&mqttUserPassword=" + encodeURIComponent(getValue('mqttUserPassword'))
+ "&topicPrefix=" + getValue('topicPrefix')
+ "&deviceName=" + getValue('deviceName')
+ "&espnowNetName=" + getValue('espnowNetName')
+ "&workMode=" + getSelectValue('workModeSelect')
+ "&ntpHostName=" + getValue('ntpHostName')
+ "&gmtOffset=" + getValue('gmtOffset');
sendRequest(submit, server);
alert("Please restart device for changes apply.");
}

View File

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

View File

@ -15,13 +15,6 @@
#include "ESP32SSDP.h"
#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 onMqttMessage(char *topic, byte *payload, unsigned int length);
@ -42,7 +35,14 @@ void checkMqttAvailability(void);
void mqttPublish(const char *topic, const char *payload, bool retained);
const String firmware{"1.3"};
typedef enum : uint8_t
{
ESP_NOW,
ESP_NOW_WIFI,
ESP_NOW_LAN
} work_mode_t;
const String firmware{"1.5"};
String espnowNetName{"DEFAULT"};
@ -227,25 +227,26 @@ void onEspnowMessage(const char *data, const uint8_t *sender)
memcpy(&configData.message, &incomingData.message, sizeof(esp_now_payload_data_t::message));
StaticJsonDocument<sizeof(esp_now_payload_data_t::message)> json;
deserializeJson(json, configData.message);
uint8_t unit = json["unit"].as<uint8_t>();
ha_component_type_t type = json["type"].as<ha_component_type_t>();
uint8_t unit = json[MCMT_DEVICE_UNIT].as<uint8_t>();
StaticJsonDocument<2048> jsonConfig;
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<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["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["json_attributes_topic"] = topicPrefix + "/" + getValueName(incomingData.deviceType) + "/" + myNet.macToString(sender) + "/attributes";
jsonConfig["availability_topic"] = topicPrefix + "/" + getValueName(incomingData.deviceType) + "/" + myNet.macToString(sender) + "/status";
jsonConfig["payload_on"] = json["payload_on"];
jsonConfig["payload_off"] = json["payload_off"];
if (json[MCMT_PAYLOAD_ON])
jsonConfig["payload_on"] = json[MCMT_PAYLOAD_ON];
if (json[MCMT_PAYLOAD_OFF])
jsonConfig["payload_off"] = json[MCMT_PAYLOAD_OFF];
jsonConfig["optimistic"] = "false";
jsonConfig["retain"] = "true";
char buffer[2048]{0};
serializeJsonPretty(jsonConfig, buffer);
mqttPublish((topicPrefix + "/" + getValueName(type) + "/" + myNet.macToString(sender) + "-" + unit + "/config").c_str(), buffer, true);
mqttPublish((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)
{
@ -253,12 +254,11 @@ void onEspnowMessage(const char *data, const uint8_t *sender)
memcpy(&configData.message, &incomingData.message, sizeof(esp_now_payload_data_t::message));
StaticJsonDocument<sizeof(esp_now_payload_data_t::message)> json;
deserializeJson(json, configData.message);
uint8_t unit = json["unit"].as<uint8_t>();
ha_component_type_t type = json["type"].as<ha_component_type_t>();
esp_now_led_type_t ledClass = json["class"];
uint8_t unit = json[MCMT_DEVICE_UNIT].as<uint8_t>();
esp_now_led_type_t ledClass = json[MCMT_DEVICE_CLASS];
StaticJsonDocument<2048> jsonConfig;
jsonConfig["platform"] = "mqtt";
jsonConfig["name"] = json["name"];
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_value_template"] = "{{ value_json.state }}";
@ -280,13 +280,15 @@ void onEspnowMessage(const char *data, const uint8_t *sender)
}
jsonConfig["json_attributes_topic"] = topicPrefix + "/" + getValueName(incomingData.deviceType) + "/" + myNet.macToString(sender) + "/attributes";
jsonConfig["availability_topic"] = topicPrefix + "/" + getValueName(incomingData.deviceType) + "/" + myNet.macToString(sender) + "/status";
jsonConfig["payload_on"] = json["payload_on"];
jsonConfig["payload_off"] = json["payload_off"];
if (json[MCMT_PAYLOAD_ON])
jsonConfig["payload_on"] = json[MCMT_PAYLOAD_ON];
if (json[MCMT_PAYLOAD_OFF])
jsonConfig["payload_off"] = json[MCMT_PAYLOAD_OFF];
jsonConfig["optimistic"] = "false";
jsonConfig["retain"] = "true";
char buffer[2048]{0};
serializeJsonPretty(jsonConfig, buffer);
mqttPublish((topicPrefix + "/" + getValueName(type) + "/" + myNet.macToString(sender) + "-" + unit + "/config").c_str(), buffer, true);
mqttPublish((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)
{
@ -294,29 +296,96 @@ void onEspnowMessage(const char *data, const uint8_t *sender)
memcpy(&configData.message, &incomingData.message, sizeof(esp_now_payload_data_t::message));
StaticJsonDocument<sizeof(esp_now_payload_data_t::message)> json;
deserializeJson(json, configData.message);
uint8_t unit = json["unit"].as<uint8_t>();
ha_component_type_t type = json["type"].as<ha_component_type_t>();
uint8_t unit = json[MCMT_DEVICE_UNIT].as<uint8_t>();
ha_component_type_t type = json[MCMT_COMPONENT_TYPE].as<ha_component_type_t>();
StaticJsonDocument<2048> jsonConfig;
jsonConfig["platform"] = "mqtt";
jsonConfig["name"] = json["name"];
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["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["force_update"] = "true";
jsonConfig["retain"] = "true";
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["payload_on"] = json["payload_on"];
jsonConfig["payload_off"] = json["payload_off"];
jsonConfig["device_class"] = getValueName(json[MCMT_DEVICE_CLASS].as<ha_sensor_device_class_t>());
jsonConfig["unit_of_measurement"] = json[MCMT_UNIT_OF_MEASUREMENT];
}
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};
serializeJsonPretty(jsonConfig, buffer);
mqttPublish((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<sizeof(esp_now_payload_data_t::message)> json;
deserializeJson(json, configData.message);
uint8_t unit = json["u"].as<uint8_t>();
ha_component_type_t type = json["t"].as<ha_component_type_t>();
rf_sensor_type_t rf = json["r"].as<rf_sensor_type_t>();
uint16_t id = json["i"].as<uint16_t>();
String tmp = json["v"].as<String>();
StaticJsonDocument<2048> jsonConfig;
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<ha_sensor_device_class_t>());
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<ha_binary_sensor_device_class_t>();
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["force_update"] = "true";
jsonConfig["retain"] = "true";
char buffer[2048]{0};
serializeJsonPretty(jsonConfig, buffer);
mqttPublish((topicPrefix + "/" + getValueName(type) + "/" + id + "-" + 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<sizeof(esp_now_payload_data_t::message)> json;
deserializeJson(json, configData.message);
uint8_t unit = json["unit"].as<uint8_t>();
StaticJsonDocument<2048> jsonConfig;
jsonConfig["platform"] = "mqtt";
jsonConfig["name"] = json["name"];
jsonConfig["unique_id"] = myNet.macToString(sender) + "-" + unit;
jsonConfig["device_class"] = getValueName(json["class"].as<ha_binary_sensor_device_class_t>());
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["force_update"] = "true";
jsonConfig["retain"] = "true";
char buffer[2048]{0};
serializeJsonPretty(jsonConfig, buffer);
mqttPublish((topicPrefix + "/" + getValueName(json["type"].as<ha_component_type_t>()) + "/" + myNet.macToString(sender) + "-" + unit + "/config").c_str(), buffer, true);
}
}
if (incomingData.payloadsType == ENPT_FORWARD)
{
@ -324,7 +393,8 @@ void onEspnowMessage(const char *data, const uint8_t *sender)
memcpy(&forwardData.message, &incomingData.message, sizeof(esp_now_payload_data_t::message));
StaticJsonDocument<sizeof(esp_now_payload_data_t::message)> json;
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((topicPrefix + "/rf_sensor/" + getValueName(json["type"].as<rf_sensor_type_t>()) + "/" + json["id"].as<uint16_t>() + "/state").c_str(), incomingData.message, false);
}
}
@ -339,11 +409,10 @@ void onMqttMessage(char *topic, byte *payload, unsigned int length)
}
esp_now_payload_data_t outgoingData;
outgoingData.deviceType = ENDT_GATEWAY;
outgoingData.payloadsType = ENPT_SET;
StaticJsonDocument<sizeof(esp_now_payload_data_t::message)> json;
if (message == "update" || message == "restart")
{
mqttPublish(topic, "", true);
mqttPublish((String(topic) + "/status").c_str(), "offline", true);
if (mac == myNet.getNodeMac() && message == "restart")
ESP.restart();
flag = true;
@ -372,11 +441,9 @@ void onMqttMessage(char *topic, byte *payload, unsigned int length)
{
if (message == "restart")
outgoingData.payloadsType = ENPT_RESTART;
else
outgoingData.payloadsType = message == "update" ? ENPT_UPDATE : ENPT_SET;
char buffer[sizeof(esp_now_payload_data_t::message)]{0};
serializeJsonPretty(json, buffer);
memcpy(&outgoingData.message, &buffer, sizeof(esp_now_payload_data_t::message));
if (message == "update")
outgoingData.payloadsType = ENPT_UPDATE;
serializeJsonPretty(json, outgoingData.message);
char temp[sizeof(esp_now_payload_data_t)]{0};
memcpy(&temp, &outgoingData, sizeof(esp_now_payload_data_t));
uint8_t target[6];
@ -396,7 +463,7 @@ void sendKeepAliveMessage()
StaticJsonDocument<sizeof(esp_now_payload_data_t::message)> json;
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)
if (workMode == ESP_NOW_WIFI && WiFi.isConnected())
{
ntpWiFiClient.update();
uint64_t epochTime = ntpWiFiClient.getEpochTime();
@ -404,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)
if (workMode == ESP_NOW_LAN && Ethernet.linkStatus() == LinkON)
{
ntpEthClient.update();
uint64_t epochTime = ntpEthClient.getEpochTime();
@ -569,16 +636,16 @@ void setupWebServer()
{
ssid = request->getParam("ssid")->value();
password = request->getParam("password")->value();
mqttHostName = request->getParam("host")->value();
mqttHostPort = request->getParam("port")->value().toInt();
mqttUserLogin = request->getParam("login")->value();
mqttUserPassword = request->getParam("pass")->value();
topicPrefix = request->getParam("prefix")->value();
deviceName = request->getParam("name")->value();
espnowNetName = request->getParam("net")->value();
workMode = request->getParam("mode")->value().toInt();
ntpHostName = request->getParam("ntp")->value();
gmtOffset = request->getParam("zone")->value().toInt();
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();
request->send(200);
saveConfig(); });
@ -620,6 +687,8 @@ void checkMqttAvailability()
mqttWifiClient.subscribe((topicPrefix + "/espnow_led/#").c_str());
sendConfigMessage();
sendAttributesMessage();
sendKeepAliveMessage();
}
}
@ -637,6 +706,8 @@ void checkMqttAvailability()
mqttEthClient.subscribe((topicPrefix + "/espnow_led/#").c_str());
sendConfigMessage();
sendAttributesMessage();
sendKeepAliveMessage();
}
}
}