Compare commits

..

No commits in common. "main" and "v1.1" have entirely different histories.
main ... v1.1

32 changed files with 194 additions and 454 deletions

View File

@ -1,17 +1,15 @@
# ESP-NOW switch for ESP8266 # ESP-NOW switch for ESP8266
ESP-NOW based switch for ESP8266. Alternate firmware for Tuya/SmartLife/eWeLink WiFi switches. ESP-NOW based switch for ESP8266. Alternate firmware for Tuya/SmartLife WiFi switches.
## Features ## Features
1. After turn on (or after rebooting) creates an access point named "ESP-NOW switch XXXXXXXXXXXX" with password "12345678" (IP 192.168.4.1). Access point will be shown during 5 minutes. The rest of the time access point is a hidden. 1. After turn on (or after rebooting) creates an access point named "ESP-NOW Switch XXXXXXXXXXXX" with password "12345678" (IP 192.168.4.1). Access point will be shown during 5 minutes. The rest of the time access point is a hidden.
2. Periodically transmission of system information (every 60 seconds), availability status (every 10 seconds) and state status (every 300 seconds) to the gateway. 2. Periodically transmission of system information (every 60 seconds), availability status (every 10 seconds) and state status (every 300 seconds) to the gateway.
3. Saves the last state when the power is turned off. Goes to the last state when the power is turned on. 3. Saves the last state when the power is turned off. Goes to the last state when the power is turned on.
4. Automatically adds switch configuration to Home Assistan via MQTT discovery as a switch. 4. Automatically adds switch configuration to Home Assistan via MQTT discovery as a switch.
5. Possibility firmware update over OTA (if is allows the size of the flash memory). 5. Possibility firmware update over OTA (if is allows the size of the flash memory).
6. Web interface for settings. 6. Web interface for settings.
7. Optionally support one external one wire digital climate sensor (DS18B20, DHT11 or DHT22) with automatically added sensor configuration to Home Assistan via MQTT discovery as a sensor. Periodically transmission sensor status (every 300 seconds) to the gateway.
8. Normal or reverse relay mode (normal by default).
## Notes ## Notes
@ -21,14 +19,11 @@ ESP-NOW based switch for ESP8266. Alternate firmware for Tuya/SmartLife/eWeLink
## Tested on ## Tested on
See [here](https://github.com/aZholtikov/ESP-NOW-Switch/tree/main/hardware). Coming soon.
## Attention ## Attention
1. A gateway is required. For details see [ESP-NOW Gateway](https://github.com/aZholtikov/ESP-NOW-Gateway). 1. A gateway is required. For details see [ESP-NOW Gateway](https://github.com/aZholtikov/ESP-NOW-Gateway).
2. ESP-NOW network name must be set same of all another ESP-NOW devices in network. 2. ESP-NOW network name must be set same of all another ESP-NOW devices in network.
3. If encryption is used, the key must be set same of all another ESP-NOW devices in network. 3. Upload the "data" folder (with web interface) into the filesystem before flashing.
4. Upload the "data" folder (with web interface) into the filesystem before flashing. 4. For using this firmware on Tuya/SmartLife WiFi switches, the WiFi module must be replaced with an ESP8266 compatible module (if necessary).
5. For using this firmware on Tuya/SmartLife/eWeLink WiFi switches, the WiFi module must be replaced with an ESP8266 compatible module (if necessary).
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('GET', '/config', true); xmlHttp.open('PUT', '/config.json', true);
xmlHttp.send(null); xmlHttp.send(null);
xmlHttp.onload = function () { xmlHttp.onload = function () {
jsonResponse = JSON.parse(xmlHttp.responseText); jsonResponse = JSON.parse(xmlHttp.responseText);
@ -29,7 +29,6 @@ function loadBlock() {
} }
document.getElementsByTagName('body')[0].innerHTML = newString; document.getElementsByTagName('body')[0].innerHTML = newString;
setFirmvareValue('version', 'firmware'); setFirmvareValue('version', 'firmware');
setGpioValue('workModeSelect', 'workMode');
setGpioValue('relayPinSelect', 'relayPin'); setGpioValue('relayPinSelect', 'relayPin');
setGpioValue('relayPinTypeSelect', 'relayPinType'); setGpioValue('relayPinTypeSelect', 'relayPinType');
setGpioValue('ledPinSelect', 'ledPin'); setGpioValue('ledPinSelect', 'ledPin');
@ -38,8 +37,6 @@ function loadBlock() {
setGpioValue('buttonPinTypeSelect', 'buttonPinType'); setGpioValue('buttonPinTypeSelect', 'buttonPinType');
setGpioValue('extButtonPinSelect', 'extButtonPin'); setGpioValue('extButtonPinSelect', 'extButtonPin');
setGpioValue('extButtonPinTypeSelect', 'extButtonPinType'); setGpioValue('extButtonPinTypeSelect', 'extButtonPinType');
setGpioValue('sensorPinSelect', 'sensorPin');
setGpioValue('sensorTypeSelect', 'sensorType');
handleServerResponse(); handleServerResponse();
} }
@ -70,10 +67,7 @@ function saveSetting(submit) {
+ "&buttonPin=" + getSelectValue('buttonPinSelect') + "&buttonPin=" + getSelectValue('buttonPinSelect')
+ "&buttonPinType=" + getSelectValue('buttonPinTypeSelect') + "&buttonPinType=" + getSelectValue('buttonPinTypeSelect')
+ "&extButtonPin=" + getSelectValue('extButtonPinSelect') + "&extButtonPin=" + getSelectValue('extButtonPinSelect')
+ "&extButtonPinType=" + getSelectValue('extButtonPinTypeSelect') + "&extButtonPinType=" + getSelectValue('extButtonPinTypeSelect');
+ "&sensorPin=" + getSelectValue('sensorPinSelect')
+ "&sensorType=" + getSelectValue('sensorTypeSelect')
+ "&workMode=" + getSelectValue('workModeSelect');
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 Switch</h1> <h1>ESP-NOW Switch </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>
@ -29,15 +29,6 @@
title="ESP-NOW network name (1 to 20 characters)" /> title="ESP-NOW network name (1 to 20 characters)" />
</div> </div>
<div class="wrapper">
<p class="text-select">Work mode:</p>
<input id="workMode" value="{{workMode}}" hidden />
<p><select id="workModeSelect">
<option value="0">NORMAL</option>
<option value="1">REVERSE</option>
</select></p>
</div>
<div class="wrapper"> <div class="wrapper">
<p class="text-select">Relay GPIO:</p> <p class="text-select">Relay GPIO:</p>
<input id="relayPin" value="{{relayPin}}" hidden /> <input id="relayPin" value="{{relayPin}}" hidden />
@ -154,37 +145,6 @@
</select></p> </select></p>
</div> </div>
<div class="wrapper">
<p class="text-select">Ext sensor GPIO:</p>
<input id="sensorPin" value="{{sensorPin}}" hidden />
<input id="sensorType" value="{{sensorType}}" hidden />
<p><select id="sensorPinSelect">
<option value="0">NONE</option>
<option value="1">GPIO01</option>
<option value="2">GPIO02</option>
<option value="3">GPIO03</option>
<option value="4">GPIO04</option>
<option value="5">GPIO05</option>
<option value="6">GPIO06</option>
<option value="7">GPIO07</option>
<option value="8">GPIO08</option>
<option value="9">GPIO09</option>
<option value="10">GPIO10</option>
<option value="11">GPIO11</option>
<option value="12">GPIO12</option>
<option value="13">GPIO13</option>
<option value="14">GPIO14</option>
<option value="15">GPIO15</option>
<option value="16">GPIO16</option>
</select></p>
<p><select id="sensorTypeSelect">
<option value="0">NONE</option>
<option value="1">DS18B20</option>
<option value="2">DHT11</option>
<option value="3">DHT22</option>
</select></p>
</div>
<div class="wrapper"> <div class="wrapper">
<input class="btn" type="submit" value="Save" onclick="saveSetting(this);"> <input class="btn" type="submit" value="Save" onclick="saveSetting(this);">
<input class="btn" type="submit" value="Restart" onclick="restart(this);"> <input class="btn" type="submit" value="Restart" onclick="restart(this);">

View File

@ -1,7 +1,3 @@
p{
margin: 0 0;
}
body { body {
font-family: "Gill Sans", sans-serif; font-family: "Gill Sans", sans-serif;
background: rgb(255, 255, 255); background: rgb(255, 255, 255);
@ -25,15 +21,13 @@ h1 {
font-weight: 600; font-weight: 600;
flex-shrink: 0; flex-shrink: 0;
margin-right: 10px; margin-right: 10px;
margin-left: 10px;
margin: 10px 0;
} }
.text-select { .text-select {
width: 35%; width: 35%;
font-weight: 600; font-weight: 600;
flex-shrink: 0; flex-shrink: 0;
margin-right: 10px; margin-right: 10px;
} }
.wrapper { .wrapper {
@ -48,7 +42,6 @@ input {
border-radius: 5px; border-radius: 5px;
border: none; border: none;
margin-bottom: 10px; margin-bottom: 10px;
margin-left: 10px;
padding: 0 10px; padding: 0 10px;
color: rgb(0, 0, 0); color: rgb(0, 0, 0);
background: #a3e0f1; background: #a3e0f1;
@ -83,8 +76,6 @@ select:hover {
background: rgb(65, 125, 238); background: rgb(65, 125, 238);
color: white; color: white;
transition: .5s; transition: .5s;
margin-left: 0;
margin-top: 8px;
} }
.btn:hover { .btn:hover {
@ -98,10 +89,6 @@ select:hover {
width: 100%; width: 100%;
} }
#espnowNetName {
margin-bottom: 15px;
}
.wrapper.wrapper--end { .wrapper.wrapper--end {
align-items: baseline; align-items: baseline;
} }

Binary file not shown.

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.3 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.4 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.3 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.5 MiB

Binary file not shown.

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.5 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.5 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.0 MiB

View File

@ -1,28 +0,0 @@
# Tested on
1. MOES 1CH 10A. Built on Tuya WiFi module WA2 (WB2S) (BK7231T chip). Replacement required. Performed replacement with ESP-02S (analogue of TYWE2S but with 2Mb flash). [Photo](https://github.com/aZholtikov/ESP-NOW-Switch/tree/main/hardware/MOES_1CH_10A).
```text
Relay GPIO GPIO12 HIGH
Led GPIO GPIO04 LOW
Button GPIO GPIO13 RISING
```
2. MINI 1CH 16A. Built on Tuya WiFi module WB2S (BK7231T chip). Replacement required. Performed replacement with ESP-02S (analogue of TYWE2S but with 2Mb flash). [Photo](https://github.com/aZholtikov/ESP-NOW-Switch/tree/main/hardware/MINI_1CH_16A).
```text
Relay GPIO GPIO13 HIGH
Led GPIO GPIO04 LOW
Button GPIO GPIO03 RISING
Ext button GPIO GPIO14 FALLING
```
3. TH 1CH 16A + SENSOR. Built on ITEAD WiFi module PSF-B85 (ESP8285 chip). Replacement not required. [Photo](https://github.com/aZholtikov/ESP-NOW-Switch/tree/main/hardware/TH_1CH_16A). Attention! Because the button is connected to GPIO00 and the firmware does not work with GPIO00 required connect GPIO00 to GPIO04 on the module.
```text
Relay GPIO GPIO12 HIGH
Button GPIO GPIO04 RISING
Ext sensor GPIO GPIO14 DS18B20
```
4. LIGHT E27 SOCKET (Coming soon)

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.9 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.4 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.3 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.1 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.1 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 987 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.3 MiB

View File

@ -1,78 +1,47 @@
[env:ESP-12E] [env:esp8266]
platform = espressif8266 platform = espressif8266
board = esp12e board = esp12e
framework = arduino framework = arduino
build_flags = -D PIO_FRAMEWORK_ARDUINO_ESPRESSIF_SDK305 lib_extra_dirs =
board_build.filesystem = littlefs
board_build.ldscript = eagle.flash.4m1m.ld
lib_deps = lib_deps =
https://github.com/aZholtikov/ZHNetwork https://github.com/aZholtikov/ZHNetwork
https://github.com/aZholtikov/ZHConfig https://github.com/aZholtikov/ZHConfig
https://github.com/aZholtikov/Async-Web-Server bblanchon/ArduinoJson@^6.19.4
https://github.com/bblanchon/ArduinoJson me-no-dev/ESP Async WebServer@^1.2.3
https://github.com/milesburton/Arduino-Temperature-Control-Library
https://github.com/beegee-tokyo/DHTesp
[env:ESP-12E-OTA] [env:esp8266-ota]
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.ldscript = eagle.flash.4m1m.ld
upload_port = 192.168.4.1 upload_port = 192.168.4.1
upload_protocol = espota upload_protocol = espota
lib_extra_dirs =
lib_deps = lib_deps =
https://github.com/aZholtikov/ZHNetwork https://github.com/aZholtikov/ZHNetwork
https://github.com/aZholtikov/ZHConfig https://github.com/aZholtikov/ZHConfig
https://github.com/aZholtikov/Async-Web-Server bblanchon/ArduinoJson@^6.19.4
https://github.com/bblanchon/ArduinoJson me-no-dev/ESP Async WebServer@^1.2.3
https://github.com/milesburton/Arduino-Temperature-Control-Library
https://github.com/beegee-tokyo/DHTesp
[env:ESP-02S] [env:esp8285]
platform = espressif8266 platform = espressif8266
board = esp8285 board = esp8285
framework = arduino framework = arduino
build_flags = -D PIO_FRAMEWORK_ARDUINO_ESPRESSIF_SDK305 lib_extra_dirs =
board_build.filesystem = littlefs
board_build.ldscript = eagle.flash.2m64.ld
lib_deps = lib_deps =
https://github.com/aZholtikov/ZHNetwork https://github.com/aZholtikov/ZHNetwork
https://github.com/aZholtikov/ZHConfig https://github.com/aZholtikov/ZHConfig
https://github.com/aZholtikov/Async-Web-Server bblanchon/ArduinoJson@^6.19.4
https://github.com/bblanchon/ArduinoJson me-no-dev/ESP Async WebServer@^1.2.3
https://github.com/milesburton/Arduino-Temperature-Control-Library
https://github.com/beegee-tokyo/DHTesp
[env:ESP-02S-OTA] [env:esp8285-ota]
platform = espressif8266 platform = espressif8266
board = esp8285 board = esp8285
framework = arduino framework = arduino
build_flags = -D PIO_FRAMEWORK_ARDUINO_ESPRESSIF_SDK305
board_build.filesystem = littlefs
board_build.ldscript = eagle.flash.2m64.ld
upload_port = 192.168.4.1 upload_port = 192.168.4.1
upload_protocol = espota upload_protocol = espota
lib_extra_dirs =
lib_deps = lib_deps =
https://github.com/aZholtikov/ZHNetwork https://github.com/aZholtikov/ZHNetwork
https://github.com/aZholtikov/ZHConfig https://github.com/aZholtikov/ZHConfig
https://github.com/aZholtikov/Async-Web-Server bblanchon/ArduinoJson@^6.19.4
https://github.com/bblanchon/ArduinoJson me-no-dev/ESP Async WebServer@^1.2.3
https://github.com/milesburton/Arduino-Temperature-Control-Library
https://github.com/beegee-tokyo/DHTesp
[env:PSF-B85]
platform = espressif8266
board = esp8285
framework = arduino
build_flags = -D PIO_FRAMEWORK_ARDUINO_ESPRESSIF_SDK305
board_build.filesystem = littlefs
board_build.ldscript = eagle.flash.1m64.ld
lib_deps =
https://github.com/aZholtikov/ZHNetwork
https://github.com/aZholtikov/ZHConfig
https://github.com/aZholtikov/Async-Web-Server
https://github.com/bblanchon/ArduinoJson
https://github.com/milesburton/Arduino-Temperature-Control-Library
https://github.com/beegee-tokyo/DHTesp

View File

@ -1,60 +1,41 @@
#include "ArduinoJson.h" #include "ArduinoJson.h"
#include "ArduinoOTA.h" #include "ArduinoOTA.h"
#include "ESPAsyncWebServer.h" // https://github.com/aZholtikov/Async-Web-Server #include "ESPAsyncWebServer.h"
#include "LittleFS.h"
#include "EEPROM.h"
#include "Ticker.h" #include "Ticker.h"
#include "DallasTemperature.h"
#include "DHTesp.h"
#include "ZHNetwork.h" #include "ZHNetwork.h"
#include "ZHConfig.h" #include "ZHConfig.h"
void onBroadcastReceiving(const char *data, const uint8_t *sender); void onBroadcastReceiving(const char *data, const uint8_t *sender);
void onUnicastReceiving(const char *data, const uint8_t *sender); void onUnicastReceiving(const char *data, const uint8_t *sender);
void onConfirmReceiving(const uint8_t *target, const uint16_t id, const bool status); void onConfirmReceiving(const uint8_t *target, const bool status);
void loadConfig(void); void loadConfig(void);
void saveConfig(void); void saveConfig(void);
void loadStatus(void);
void saveStatus(void);
void setupWebServer(void); void setupWebServer(void);
void buttonInterrupt(void); void buttonInterrupt(void);
void switchingRelay(void); void switchingRelay(void);
void sendAttributesMessage(const uint8_t type = ENST_NONE); void sendAttributesMessage(void);
void sendKeepAliveMessage(void); void sendKeepAliveMessage(void);
void sendConfigMessage(const uint8_t type = ENST_NONE); void sendConfigMessage(void);
void sendStatusMessage(const uint8_t type = ENST_NONE); void sendStatusMessage(void);
typedef struct const String firmware{"1.1"};
{
uint16_t id{0};
char message[200]{0};
} espnow_message_t;
struct deviceConfig String espnowNetName{"DEFAULT"};
{
String espnowNetName{"DEFAULT"};
uint8_t workMode{0};
String deviceName = "ESP-NOW switch " + String(ESP.getChipId(), HEX);
uint8_t relayPin{0};
uint8_t relayPinType{1};
uint8_t buttonPin{0};
uint8_t buttonPinType{0};
uint8_t extButtonPin{0};
uint8_t extButtonPinType{0};
uint8_t ledPin{0};
uint8_t ledPinType{0};
uint8_t sensorPin{0};
uint8_t sensorType{0};
} config;
std::vector<espnow_message_t> espnowMessage; String deviceName{"ESP-NOW switch"};
const String firmware{"1.42"};
bool relayStatus{false}; bool relayStatus{false};
uint8_t relayPin{0};
uint8_t relayPinType{1};
uint8_t buttonPin{0};
uint8_t buttonPinType{0};
uint8_t extButtonPin{0};
uint8_t extButtonPinType{0};
uint8_t ledPin{0};
uint8_t ledPinType{0};
bool wasMqttAvailable{false}; bool wasMqttAvailable{false};
@ -63,11 +44,6 @@ uint8_t gatewayMAC[6]{0};
ZHNetwork myNet; ZHNetwork myNet;
AsyncWebServer webServer(80); AsyncWebServer webServer(80);
OneWire oneWire;
DallasTemperature ds18b20(&oneWire);
DHTesp dht;
Ticker buttonInterruptTimer; Ticker buttonInterruptTimer;
Ticker gatewayAvailabilityCheckTimer; Ticker gatewayAvailabilityCheckTimer;
@ -80,57 +56,54 @@ void apModeHideTimerCallback(void);
Ticker attributesMessageTimer; Ticker attributesMessageTimer;
bool attributesMessageTimerSemaphore{true}; bool attributesMessageTimerSemaphore{true};
void attributesMessageTimerCallback(void); void attributesMessageTimerCallback(void);
Ticker attributesMessageResendTimer;
bool attributesMessageResendTimerSemaphore{false};
Ticker keepAliveMessageTimer; Ticker keepAliveMessageTimer;
bool keepAliveMessageTimerSemaphore{true}; bool keepAliveMessageTimerSemaphore{true};
void keepAliveMessageTimerCallback(void); void keepAliveMessageTimerCallback(void);
Ticker keepAliveMessageResendTimer;
bool keepAliveMessageResendTimerSemaphore{false};
Ticker configMessageResendTimer;
bool configMessageResendTimerSemaphore{false};
Ticker statusMessageTimer; Ticker statusMessageTimer;
bool statusMessageTimerSemaphore{true}; bool statusMessageTimerSemaphore{true};
void statusMessageTimerCallback(void); void statusMessageTimerCallback(void);
Ticker statusMessageResendTimer;
bool statusMessageResendTimerSemaphore{false};
void setup() void setup()
{ {
LittleFS.begin(); SPIFFS.begin();
loadConfig(); loadConfig();
loadStatus();
if (config.sensorPin) if (relayPin)
{ {
if (config.sensorType == ENST_DS18B20) pinMode(relayPin, OUTPUT);
oneWire.begin(config.sensorPin); digitalWrite(relayPin, relayPinType ? relayStatus : !relayStatus);
if (config.sensorType == ENST_DHT11 || config.sensorType == ENST_DHT22)
dht.setup(config.sensorPin, DHTesp::AUTO_DETECT);
} }
if (config.relayPin) if (ledPin)
{ {
pinMode(config.relayPin, OUTPUT); pinMode(ledPin, OUTPUT);
if (config.workMode) digitalWrite(ledPin, ledPinType ? relayStatus : !relayStatus);
digitalWrite(config.relayPin, config.relayPinType ? !relayStatus : relayStatus);
else
digitalWrite(config.relayPin, config.relayPinType ? relayStatus : !relayStatus);
} }
if (config.ledPin) if (buttonPin)
{ attachInterrupt(buttonPin, buttonInterrupt, buttonPinType ? RISING : FALLING);
pinMode(config.ledPin, OUTPUT); if (extButtonPin)
digitalWrite(config.ledPin, config.ledPinType ? relayStatus : !relayStatus); attachInterrupt(extButtonPin, buttonInterrupt, extButtonPinType ? RISING : FALLING);
}
if (config.buttonPin)
attachInterrupt(config.buttonPin, buttonInterrupt, config.buttonPinType ? RISING : FALLING);
if (config.extButtonPin)
attachInterrupt(config.extButtonPin, buttonInterrupt, config.extButtonPinType ? RISING : FALLING);
WiFi.setSleepMode(WIFI_NONE_SLEEP); WiFi.setSleepMode(WIFI_NONE_SLEEP);
myNet.begin(config.espnowNetName.c_str()); myNet.begin(espnowNetName.c_str());
// 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(onBroadcastReceiving); myNet.setOnBroadcastReceivingCallback(onBroadcastReceiving);
myNet.setOnUnicastReceivingCallback(onUnicastReceiving); myNet.setOnUnicastReceivingCallback(onUnicastReceiving);
myNet.setOnConfirmReceivingCallback(onConfirmReceiving); myNet.setOnConfirmReceivingCallback(onConfirmReceiving);
WiFi.mode(WIFI_AP_STA); WiFi.mode(WIFI_AP_STA);
WiFi.softAP(("ESP-NOW switch " + String(ESP.getChipId(), HEX)).c_str(), "12345678", 1, 0); WiFi.softAP(("ESP-NOW Switch " + myNet.getNodeMac()).c_str(), "12345678", 1, 0);
apModeHideTimer.once(300, apModeHideTimerCallback); apModeHideTimer.once(300, apModeHideTimerCallback);
setupWebServer(); setupWebServer();
@ -145,19 +118,11 @@ void setup()
void loop() void loop()
{ {
if (attributesMessageTimerSemaphore) if (attributesMessageTimerSemaphore)
{
sendAttributesMessage(); sendAttributesMessage();
if (config.sensorPin)
sendAttributesMessage(config.sensorType);
}
if (keepAliveMessageTimerSemaphore) if (keepAliveMessageTimerSemaphore)
sendKeepAliveMessage(); sendKeepAliveMessage();
if (statusMessageTimerSemaphore) if (statusMessageTimerSemaphore)
{
sendStatusMessage(); sendStatusMessage();
if (config.sensorPin)
sendStatusMessage(config.sensorType);
}
myNet.maintenance(); myNet.maintenance();
ArduinoOTA.handle(); ArduinoOTA.handle();
} }
@ -169,28 +134,18 @@ void onBroadcastReceiving(const char *data, const uint8_t *sender)
if (incomingData.deviceType != ENDT_GATEWAY) if (incomingData.deviceType != ENDT_GATEWAY)
return; return;
if (myNet.macToString(gatewayMAC) != myNet.macToString(sender) && incomingData.payloadsType == ENPT_KEEP_ALIVE) if (myNet.macToString(gatewayMAC) != myNet.macToString(sender) && incomingData.payloadsType == ENPT_KEEP_ALIVE)
memcpy(&gatewayMAC, sender, 6); memcpy(gatewayMAC, sender, 6);
if (myNet.macToString(gatewayMAC) == myNet.macToString(sender) && incomingData.payloadsType == ENPT_KEEP_ALIVE) if (myNet.macToString(gatewayMAC) == myNet.macToString(sender) && incomingData.payloadsType == ENPT_KEEP_ALIVE)
{ {
isGatewayAvailable = true; isGatewayAvailable = true;
DynamicJsonDocument json(sizeof(esp_now_payload_data_t::message)); StaticJsonDocument<sizeof(esp_now_payload_data_t::message)> json;
deserializeJson(json, incomingData.message); deserializeJson(json, incomingData.message);
bool temp = json["MQTT"] == "online" ? true : false; bool temp = json["MQTT"] == "online" ? true : false;
if (wasMqttAvailable != temp) if (wasMqttAvailable != temp)
{ {
wasMqttAvailable = temp; wasMqttAvailable = temp;
if (temp) if (temp)
{
sendConfigMessage(); sendConfigMessage();
if (config.sensorPin)
sendConfigMessage(config.sensorType);
sendAttributesMessage();
if (config.sensorPin)
sendAttributesMessage(config.sensorType);
sendStatusMessage();
if (config.sensorPin)
sendStatusMessage(config.sensorType);
}
} }
gatewayAvailabilityCheckTimer.once(15, gatewayAvailabilityCheckTimerCallback); gatewayAvailabilityCheckTimer.once(15, gatewayAvailabilityCheckTimerCallback);
} }
@ -202,169 +157,130 @@ void onUnicastReceiving(const char *data, const uint8_t *sender)
memcpy(&incomingData, data, sizeof(esp_now_payload_data_t)); memcpy(&incomingData, data, sizeof(esp_now_payload_data_t));
if (incomingData.deviceType != ENDT_GATEWAY || myNet.macToString(gatewayMAC) != myNet.macToString(sender)) if (incomingData.deviceType != ENDT_GATEWAY || myNet.macToString(gatewayMAC) != myNet.macToString(sender))
return; return;
DynamicJsonDocument json(sizeof(esp_now_payload_data_t::message)); StaticJsonDocument<sizeof(esp_now_payload_data_t::message)> json;
if (incomingData.payloadsType == ENPT_SET) if (incomingData.payloadsType == ENPT_SET)
{ {
deserializeJson(json, incomingData.message); deserializeJson(json, incomingData.message);
relayStatus = json["set"] == "ON" ? true : false; relayStatus = json["set"] == "ON" ? true : false;
if (config.relayPin) if (relayPin)
{ digitalWrite(relayPin, relayPinType ? relayStatus : !relayStatus);
if (config.workMode) if (ledPin)
digitalWrite(config.relayPin, config.relayPinType ? !relayStatus : relayStatus); digitalWrite(ledPin, ledPinType ? relayStatus : !relayStatus);
else saveConfig();
digitalWrite(config.relayPin, config.relayPinType ? relayStatus : !relayStatus);
}
if (config.ledPin)
{
if (config.workMode)
digitalWrite(config.ledPin, config.ledPinType ? !relayStatus : relayStatus);
else
digitalWrite(config.ledPin, config.ledPinType ? relayStatus : !relayStatus);
}
saveStatus();
sendStatusMessage(); sendStatusMessage();
} }
if (incomingData.payloadsType == ENPT_UPDATE) if (incomingData.payloadsType == ENPT_UPDATE)
{ {
WiFi.softAP(("ESP-NOW switch " + String(ESP.getChipId(), HEX)).c_str(), "12345678", 1, 0); WiFi.softAP(("ESP-NOW Switch " + myNet.getNodeMac()).c_str(), "12345678", 1, 0);
webServer.begin();
apModeHideTimer.once(300, apModeHideTimerCallback); apModeHideTimer.once(300, apModeHideTimerCallback);
} }
if (incomingData.payloadsType == ENPT_RESTART) if (incomingData.payloadsType == ENPT_RESTART)
ESP.restart(); ESP.restart();
} }
void onConfirmReceiving(const uint8_t *target, const uint16_t id, const bool status) void onConfirmReceiving(const uint8_t *target, const bool status)
{ {
for (uint16_t i{0}; i < espnowMessage.size(); ++i) if (status)
{ {
espnow_message_t message = espnowMessage[i]; if (attributesMessageResendTimerSemaphore)
if (message.id == id)
{ {
if (status) attributesMessageResendTimerSemaphore = false;
espnowMessage.erase(espnowMessage.begin() + i); attributesMessageResendTimer.detach();
else }
{ if (keepAliveMessageResendTimerSemaphore)
message.id = myNet.sendUnicastMessage(message.message, gatewayMAC, true); {
espnowMessage.at(i) = message; keepAliveMessageResendTimerSemaphore = false;
} keepAliveMessageResendTimer.detach();
}
if (configMessageResendTimerSemaphore)
{
configMessageResendTimerSemaphore = false;
configMessageResendTimer.detach();
}
if (statusMessageResendTimerSemaphore)
{
statusMessageResendTimerSemaphore = false;
statusMessageResendTimer.detach();
} }
} }
} }
void loadConfig() void loadConfig()
{ {
ETS_GPIO_INTR_DISABLE(); if (!SPIFFS.exists("/config.json"))
EEPROM.begin(4096);
if (EEPROM.read(4095) == 254)
{
EEPROM.get(0, config);
EEPROM.end();
}
else
{
EEPROM.end();
saveConfig(); saveConfig();
} File file = SPIFFS.open("/config.json", "r");
delay(50); String jsonFile = file.readString();
ETS_GPIO_INTR_ENABLE(); StaticJsonDocument<512> json;
deserializeJson(json, jsonFile);
espnowNetName = json["espnowNetName"].as<String>();
deviceName = json["deviceName"].as<String>();
relayStatus = json["relayStatus"];
relayPin = json["relayPin"];
relayPinType = json["relayPinType"];
buttonPin = json["buttonPin"];
buttonPinType = json["buttonPinType"];
extButtonPin = json["extButtonPin"];
extButtonPinType = json["extButtonPinType"];
ledPin = json["ledPin"];
ledPinType = json["ledPinType"];
file.close();
} }
void saveConfig() void saveConfig()
{ {
ETS_GPIO_INTR_DISABLE(); StaticJsonDocument<512> json;
EEPROM.begin(4096); json["firmware"] = firmware;
EEPROM.write(4095, 254); json["espnowNetName"] = espnowNetName;
EEPROM.put(0, config); json["deviceName"] = deviceName;
EEPROM.end(); json["relayStatus"] = relayStatus;
delay(50); json["relayPin"] = relayPin;
ETS_GPIO_INTR_ENABLE(); json["relayPinType"] = relayPinType;
} json["buttonPin"] = buttonPin;
json["buttonPinType"] = buttonPinType;
void loadStatus(void) json["extButtonPin"] = extButtonPin;
{ json["extButtonPinType"] = extButtonPinType;
ETS_GPIO_INTR_DISABLE(); json["ledPin"] = ledPin;
if (!LittleFS.exists("/status.json")) json["ledPinType"] = ledPinType;
saveStatus();
File file = LittleFS.open("/status.json", "r");
String jsonFile = file.readString();
DynamicJsonDocument json(64); // To calculate the buffer size uses https://arduinojson.org/v6/assistant.
deserializeJson(json, jsonFile);
relayStatus = json["status"];
file.close();
delay(50);
ETS_GPIO_INTR_ENABLE();
}
void saveStatus(void)
{
ETS_GPIO_INTR_DISABLE();
DynamicJsonDocument json(48); // To calculate the buffer size uses https://arduinojson.org/v6/assistant.
json["status"] = relayStatus;
json["system"] = "empty"; json["system"] = "empty";
File file = LittleFS.open("/status.json", "w"); File file = SPIFFS.open("/config.json", "w");
serializeJsonPretty(json, file); serializeJsonPretty(json, file);
file.close(); file.close();
delay(50);
ETS_GPIO_INTR_ENABLE();
} }
void setupWebServer() void setupWebServer()
{ {
webServer.on("/", HTTP_GET, [](AsyncWebServerRequest *request) webServer.on("/", HTTP_GET, [](AsyncWebServerRequest *request)
{ request->send(LittleFS, "/index.htm"); }); { request->send(SPIFFS, "/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)
{ {
config.relayPin = request->getParam("relayPin")->value().toInt(); relayPin = request->getParam("relayPin")->value().toInt();
config.relayPinType = request->getParam("relayPinType")->value().toInt(); relayPinType = request->getParam("relayPinType")->value().toInt();
config.buttonPin = request->getParam("buttonPin")->value().toInt(); buttonPin = request->getParam("buttonPin")->value().toInt();
config.buttonPinType = request->getParam("buttonPinType")->value().toInt(); buttonPinType = request->getParam("buttonPinType")->value().toInt();
config.extButtonPin = request->getParam("extButtonPin")->value().toInt(); extButtonPin = request->getParam("extButtonPin")->value().toInt();
config.extButtonPinType = request->getParam("extButtonPinType")->value().toInt(); extButtonPinType = request->getParam("extButtonPinType")->value().toInt();
config.ledPin = request->getParam("ledPin")->value().toInt(); ledPin = request->getParam("ledPin")->value().toInt();
config.ledPinType = request->getParam("ledPinType")->value().toInt(); ledPinType = request->getParam("ledPinType")->value().toInt();
config.sensorPin = request->getParam("sensorPin")->value().toInt(); deviceName = request->getParam("deviceName")->value();
config.sensorType = request->getParam("sensorType")->value().toInt(); espnowNetName = request->getParam("espnowNetName")->value();
config.workMode = request->getParam("workMode")->value().toInt();
config.deviceName = request->getParam("deviceName")->value();
config.espnowNetName = request->getParam("espnowNetName")->value();
request->send(200); request->send(200);
saveConfig(); }); saveConfig(); });
webServer.on("/config", HTTP_GET, [](AsyncWebServerRequest *request)
{
String configJson;
DynamicJsonDocument json(384); // To calculate the buffer size uses https://arduinojson.org/v6/assistant.
json["firmware"] = firmware;
json["espnowNetName"] = config.espnowNetName;
json["deviceName"] = config.deviceName;
json["relayPin"] = config.relayPin;
json["relayPinType"] = config.relayPinType;
json["buttonPin"] = config.buttonPin;
json["buttonPinType"] = config.buttonPinType;
json["extButtonPin"] = config.extButtonPin;
json["extButtonPinType"] = config.extButtonPinType;
json["ledPin"] = config.ledPin;
json["ledPinType"] = config.ledPinType;
json["sensorPin"] = config.sensorPin;
json["sensorType"] = config.sensorType;
json["workMode"] = config.workMode;
serializeJsonPretty(json, configJson);
request->send(200, "application/json", configJson); });
webServer.on("/restart", HTTP_GET, [](AsyncWebServerRequest *request) webServer.on("/restart", HTTP_GET, [](AsyncWebServerRequest *request)
{request->send(200); {
request->send(200);
ESP.restart(); }); ESP.restart(); });
webServer.onNotFound([](AsyncWebServerRequest *request) webServer.onNotFound([](AsyncWebServerRequest *request)
{ request->send(404, "text/plain", "File Not Found"); }); {
if (SPIFFS.exists(request->url()))
request->send(SPIFFS, request->url());
else
{
request->send(404, "text/plain", "File Not Found");
} });
webServer.begin(); webServer.begin();
} }
@ -372,32 +288,22 @@ void setupWebServer()
void IRAM_ATTR buttonInterrupt() void IRAM_ATTR buttonInterrupt()
{ {
ETS_GPIO_INTR_DISABLE(); ETS_GPIO_INTR_DISABLE();
buttonInterruptTimer.once_ms(500, switchingRelay); // For prevent contact chatter. buttonInterruptTimer.once_ms(50, switchingRelay); // For prevent contact chatter.
} }
void switchingRelay() void switchingRelay()
{ {
ETS_GPIO_INTR_ENABLE();
relayStatus = !relayStatus; relayStatus = !relayStatus;
if (config.relayPin) if (relayPin)
{ digitalWrite(relayPin, relayPinType ? relayStatus : !relayStatus);
if (config.workMode) if (ledPin)
digitalWrite(config.relayPin, config.relayPinType ? !relayStatus : relayStatus); digitalWrite(ledPin, ledPinType ? relayStatus : !relayStatus);
else saveConfig();
digitalWrite(config.relayPin, config.relayPinType ? relayStatus : !relayStatus);
}
if (config.ledPin)
{
if (config.workMode)
digitalWrite(config.ledPin, config.ledPinType ? !relayStatus : relayStatus);
else
digitalWrite(config.ledPin, config.ledPinType ? relayStatus : !relayStatus);
}
saveStatus();
sendStatusMessage(); sendStatusMessage();
ETS_GPIO_INTR_ENABLE();
} }
void sendAttributesMessage(const uint8_t type) void sendAttributesMessage()
{ {
if (!isGatewayAvailable) if (!isGatewayAvailable)
return; return;
@ -407,25 +313,22 @@ void sendAttributesMessage(const uint8_t type)
uint32_t hours = mins / 60; uint32_t hours = mins / 60;
uint32_t days = hours / 24; uint32_t days = hours / 24;
esp_now_payload_data_t outgoingData{ENDT_SWITCH, ENPT_ATTRIBUTES}; esp_now_payload_data_t outgoingData{ENDT_SWITCH, ENPT_ATTRIBUTES};
espnow_message_t message; StaticJsonDocument<sizeof(esp_now_payload_data_t::message)> json;
DynamicJsonDocument json(sizeof(esp_now_payload_data_t::message)); json["Type"] = "ESP-NOW Switch";
if (type == ENST_NONE)
json["Type"] = "ESP-NOW switch";
else
{
outgoingData.deviceType = ENDT_SENSOR;
json["Type"] = getValueName(esp_now_sensor_type_t(type));
}
json["MCU"] = "ESP8266"; json["MCU"] = "ESP8266";
json["MAC"] = myNet.getNodeMac(); json["MAC"] = myNet.getNodeMac();
json["Firmware"] = firmware; json["Firmware"] = firmware;
json["Library"] = myNet.getFirmwareVersion(); json["Library"] = myNet.getFirmwareVersion();
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));
serializeJsonPretty(json, outgoingData.message); char buffer[sizeof(esp_now_payload_data_t::message)]{0};
memcpy(&message.message, &outgoingData, sizeof(esp_now_payload_data_t)); serializeJsonPretty(json, buffer);
message.id = myNet.sendUnicastMessage(message.message, gatewayMAC, true); memcpy(outgoingData.message, buffer, sizeof(esp_now_payload_data_t::message));
char temp[sizeof(esp_now_payload_data_t)]{0};
memcpy(&temp, &outgoingData, sizeof(esp_now_payload_data_t));
myNet.sendUnicastMessage(temp, gatewayMAC, true);
espnowMessage.push_back(message); attributesMessageResendTimerSemaphore = true;
attributesMessageResendTimer.once(1, sendAttributesMessage);
} }
void sendKeepAliveMessage() void sendKeepAliveMessage()
@ -434,104 +337,64 @@ void sendKeepAliveMessage()
return; return;
keepAliveMessageTimerSemaphore = false; keepAliveMessageTimerSemaphore = false;
esp_now_payload_data_t outgoingData{ENDT_SWITCH, ENPT_KEEP_ALIVE}; esp_now_payload_data_t outgoingData{ENDT_SWITCH, ENPT_KEEP_ALIVE};
espnow_message_t message; char temp[sizeof(esp_now_payload_data_t)]{0};
memcpy(&message.message, &outgoingData, sizeof(esp_now_payload_data_t)); memcpy(&temp, &outgoingData, sizeof(esp_now_payload_data_t));
message.id = myNet.sendUnicastMessage(message.message, gatewayMAC, true); myNet.sendUnicastMessage(temp, gatewayMAC, true);
espnowMessage.push_back(message); keepAliveMessageResendTimerSemaphore = true;
keepAliveMessageResendTimer.once(1, sendKeepAliveMessage);
} }
void sendConfigMessage(const uint8_t type) void sendConfigMessage()
{ {
if (!isGatewayAvailable) if (!isGatewayAvailable)
return; return;
esp_now_payload_data_t outgoingData{ENDT_SWITCH, ENPT_CONFIG}; esp_now_payload_data_t outgoingData{ENDT_SWITCH, ENPT_CONFIG};
espnow_message_t message; StaticJsonDocument<sizeof(esp_now_payload_data_t::message)> json;
DynamicJsonDocument json(sizeof(esp_now_payload_data_t::message)); json["name"] = deviceName;
if (type == ENST_NONE) json["unit"] = 1;
{ json["type"] = HACT_SWITCH;
json[MCMT_DEVICE_NAME] = config.deviceName; json["class"] = HASWDC_SWITCH;
json[MCMT_DEVICE_UNIT] = 1; json["reverse"] = "false";
json[MCMT_COMPONENT_TYPE] = HACT_SWITCH; char buffer[sizeof(esp_now_payload_data_t::message)]{0};
json[MCMT_DEVICE_CLASS] = HASWDC_SWITCH; serializeJsonPretty(json, buffer);
json[MCMT_VALUE_TEMPLATE] = "state"; memcpy(outgoingData.message, buffer, sizeof(esp_now_payload_data_t::message));
} char temp[sizeof(esp_now_payload_data_t)]{0};
if (type == ENST_DS18B20 || type == ENST_DHT11 || type == ENST_DHT22) memcpy(&temp, &outgoingData, sizeof(esp_now_payload_data_t));
{ myNet.sendUnicastMessage(temp, gatewayMAC, true);
outgoingData.deviceType = ENDT_SENSOR;
json[MCMT_DEVICE_NAME] = config.deviceName + " temperature";
json[MCMT_DEVICE_UNIT] = 2;
json[MCMT_COMPONENT_TYPE] = HACT_SENSOR;
json[MCMT_DEVICE_CLASS] = HASDC_TEMPERATURE;
json[MCMT_VALUE_TEMPLATE] = "temperature";
json[MCMT_UNIT_OF_MEASUREMENT] = "°C";
json[MCMT_EXPIRE_AFTER] = 900;
}
serializeJsonPretty(json, outgoingData.message);
memcpy(&message.message, &outgoingData, sizeof(esp_now_payload_data_t));
message.id = myNet.sendUnicastMessage(message.message, gatewayMAC, true);
espnowMessage.push_back(message); configMessageResendTimerSemaphore = true;
configMessageResendTimer.once(5, sendConfigMessage);
if (type == ENST_DHT11 || type == ENST_DHT22)
{
outgoingData.deviceType = ENDT_SENSOR;
json[MCMT_DEVICE_NAME] = config.deviceName + " humidity";
json[MCMT_DEVICE_UNIT] = 3;
json[MCMT_COMPONENT_TYPE] = HACT_SENSOR;
json[MCMT_DEVICE_CLASS] = HASDC_HUMIDITY;
json[MCMT_VALUE_TEMPLATE] = "humidity";
json[MCMT_UNIT_OF_MEASUREMENT] = "%";
json[MCMT_EXPIRE_AFTER] = 900;
serializeJsonPretty(json, outgoingData.message);
memcpy(&message.message, &outgoingData, sizeof(esp_now_payload_data_t));
message.id = myNet.sendUnicastMessage(message.message, gatewayMAC, true);
espnowMessage.push_back(message);
}
} }
void sendStatusMessage(const uint8_t type) void sendStatusMessage()
{ {
if (!isGatewayAvailable) if (!isGatewayAvailable)
return; return;
statusMessageTimerSemaphore = false; statusMessageTimerSemaphore = false;
esp_now_payload_data_t outgoingData{ENDT_SWITCH, ENPT_STATE}; esp_now_payload_data_t outgoingData{ENDT_SWITCH, ENPT_STATE};
espnow_message_t message; StaticJsonDocument<sizeof(esp_now_payload_data_t::message)> json;
DynamicJsonDocument json(sizeof(esp_now_payload_data_t::message)); json["state"] = relayStatus ? "ON" : "OFF";
if (type == ENST_NONE) char buffer[sizeof(esp_now_payload_data_t::message)]{0};
json["state"] = relayStatus ? "ON" : "OFF"; serializeJsonPretty(json, buffer);
if (type == ENST_DS18B20) memcpy(&outgoingData.message, &buffer, sizeof(esp_now_payload_data_t::message));
{ char temp[sizeof(esp_now_payload_data_t)]{0};
outgoingData.deviceType = ENDT_SENSOR; memcpy(&temp, &outgoingData, sizeof(esp_now_payload_data_t));
ds18b20.requestTemperatures(); myNet.sendUnicastMessage(temp, gatewayMAC, true);
json["temperature"] = int8_t(ds18b20.getTempCByIndex(0));
}
if (type == ENST_DHT11 || type == ENST_DHT22)
{
outgoingData.deviceType = ENDT_SENSOR;
json["temperature"] = int8_t(dht.getTemperature());
json["humidity"] = int8_t(dht.getHumidity());
}
serializeJsonPretty(json, outgoingData.message);
memcpy(&message.message, &outgoingData, sizeof(esp_now_payload_data_t));
message.id = myNet.sendUnicastMessage(message.message, gatewayMAC, true);
espnowMessage.push_back(message); statusMessageResendTimerSemaphore = true;
statusMessageResendTimer.once(1, sendStatusMessage);
} }
void gatewayAvailabilityCheckTimerCallback() void gatewayAvailabilityCheckTimerCallback()
{ {
isGatewayAvailable = false; isGatewayAvailable = false;
memset(&gatewayMAC, 0, 6); memset(gatewayMAC, 0, 6);
espnowMessage.clear();
} }
void apModeHideTimerCallback() void apModeHideTimerCallback()
{ {
WiFi.softAP(("ESP-NOW switch " + String(ESP.getChipId(), HEX)).c_str(), "12345678", 1, 1); WiFi.softAP(("ESP-NOW Switch " + myNet.getNodeMac()).c_str(), "12345678", 1, 1);
webServer.end();
} }
void attributesMessageTimerCallback() void attributesMessageTimerCallback()