15 Commits
v1.0 ... v1.31

Author SHA1 Message Date
551866b040 Version 1.31
Fixed one minor bug.
2023-02-18 10:07:15 +03:00
b940f325f2 Minor changes 2023-02-17 17:01:52 +03:00
0b603489fd Version 1.3
Added relay work modes.
2023-02-17 09:28:23 +03:00
dc17e39ff6 Version 1.21
Minor changes.
2023-02-12 18:33:42 +03:00
e1d74e98eb Minor changes 2023-02-11 08:41:56 +03:00
4ece7281d3 Minor changes 2023-02-09 21:30:13 +03:00
4bf392c380 Version 1.2
Updated "Tested on".
Changed FS from SPIFFS to LittleFS.
Added support one external sensor.
2023-02-05 13:11:31 +03:00
ac58f4dc00 Updated "Tested on" 2023-01-25 22:00:39 +03:00
3953473ea8 Minor changes 2023-01-24 19:40:00 +03:00
619b3a6cdf Version 1.14
Added encrypting messages.
2023-01-22 12:22:34 +03:00
ef625740d9 Version 1.13
Added value template to config message.
2023-01-15 11:47:04 +03:00
549d9cf916 Version 1.12
Main code refactoring.
2023-01-14 11:16:48 +03:00
82e736dc8b Version 1.11
Web interface minor redesign.
Main code minor changes.
2023-01-12 12:30:16 +03:00
439722511f Updated "Tested on" 2023-01-10 11:32:08 +03:00
5d9784c9ae Version 1.1
Fixed one minor bug.
2023-01-06 13:00:46 +03:00
32 changed files with 341 additions and 117 deletions

View File

@ -1,15 +1,17 @@
# ESP-NOW switch for ESP8266
ESP-NOW based switch for ESP8266. Alternate firmware for Tuya/SmartLife WiFi switches.
ESP-NOW based switch for ESP8266. Alternate firmware for Tuya/SmartLife/eWeLink WiFi switches.
## 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.
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.
5. Possibility firmware update over OTA (if is allows the size of the flash memory).
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
@ -19,11 +21,14 @@ ESP-NOW based switch for ESP8266. Alternate firmware for Tuya/SmartLife WiFi swi
## Tested on
Coming soon.
See [here](https://github.com/aZholtikov/ESP-NOW-Switch/tree/main/hardware).
## Attention
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.
3. 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).
3. If encryption is used, the key must be set same of all another ESP-NOW devices in network.
4. Upload the "data" folder (with web interface) into the filesystem before flashing.
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

@ -29,6 +29,7 @@ function loadBlock() {
}
document.getElementsByTagName('body')[0].innerHTML = newString;
setFirmvareValue('version', 'firmware');
setGpioValue('workModeSelect', 'workMode');
setGpioValue('relayPinSelect', 'relayPin');
setGpioValue('relayPinTypeSelect', 'relayPinType');
setGpioValue('ledPinSelect', 'ledPin');
@ -37,6 +38,8 @@ function loadBlock() {
setGpioValue('buttonPinTypeSelect', 'buttonPinType');
setGpioValue('extButtonPinSelect', 'extButtonPin');
setGpioValue('extButtonPinTypeSelect', 'extButtonPinType');
setGpioValue('sensorPinSelect', 'sensorPin');
setGpioValue('sensorTypeSelect', 'sensorType');
handleServerResponse();
}
@ -67,7 +70,10 @@ function saveSetting(submit) {
+ "&buttonPin=" + getSelectValue('buttonPinSelect')
+ "&buttonPinType=" + getSelectValue('buttonPinTypeSelect')
+ "&extButtonPin=" + getSelectValue('extButtonPinSelect')
+ "&extButtonPinType=" + getSelectValue('extButtonPinTypeSelect');
+ "&extButtonPinType=" + getSelectValue('extButtonPinTypeSelect')
+ "&sensorPin=" + getSelectValue('sensorPinSelect')
+ "&sensorType=" + getSelectValue('sensorTypeSelect')
+ "&workMode=" + getSelectValue('workModeSelect');
sendRequest(submit, server);
alert("Please restart device for changes apply.");
}

View File

@ -29,6 +29,15 @@
title="ESP-NOW network name (1 to 20 characters)" />
</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">
<p class="text-select">Relay GPIO:</p>
<input id="relayPin" value="{{relayPin}}" hidden />
@ -145,6 +154,37 @@
</select></p>
</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">
<input class="btn" type="submit" value="Save" onclick="saveSetting(this);">
<input class="btn" type="submit" value="Restart" onclick="restart(this);">

View File

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

Binary file not shown.

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 MiB

Binary file not shown.

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 MiB

28
hardware/README.md Normal file
View File

@ -0,0 +1,28 @@
# 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.

After

Width:  |  Height:  |  Size: 2.9 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 987 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 MiB

View File

@ -1,47 +1,73 @@
[env:esp8266]
[env:ESP-12E]
platform = espressif8266
board = esp12e
framework = arduino
lib_extra_dirs =
board_build.filesystem = littlefs
board_build.ldscript = eagle.flash.4m2m.ld
lib_deps =
https://github.com/aZholtikov/ZHNetwork
https://github.com/aZholtikov/ZHConfig
bblanchon/ArduinoJson@^6.19.4
me-no-dev/ESP Async WebServer@^1.2.3
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
[env:esp8266-ota]
[env:ESP-12E-OTA]
platform = espressif8266
board = esp12e
framework = arduino
board_build.filesystem = littlefs
board_build.ldscript = eagle.flash.4m2m.ld
upload_port = 192.168.4.1
upload_protocol = espota
lib_extra_dirs =
lib_deps =
https://github.com/aZholtikov/ZHNetwork
https://github.com/aZholtikov/ZHConfig
bblanchon/ArduinoJson@^6.19.4
me-no-dev/ESP Async WebServer@^1.2.3
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
[env:esp8285]
[env:ESP-02S]
platform = espressif8266
board = esp8285
framework = arduino
lib_extra_dirs =
board_build.filesystem = littlefs
board_build.ldscript = eagle.flash.2m256.ld
lib_deps =
https://github.com/aZholtikov/ZHNetwork
https://github.com/aZholtikov/ZHConfig
bblanchon/ArduinoJson@^6.19.4
me-no-dev/ESP Async WebServer@^1.2.3
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
[env:esp8285-ota]
[env:ESP-02S-OTA]
platform = espressif8266
board = esp8285
framework = arduino
board_build.filesystem = littlefs
board_build.ldscript = eagle.flash.2m256.ld
upload_port = 192.168.4.1
upload_protocol = espota
lib_extra_dirs =
lib_deps =
https://github.com/aZholtikov/ZHNetwork
https://github.com/aZholtikov/ZHConfig
bblanchon/ArduinoJson@^6.19.4
me-no-dev/ESP Async WebServer@^1.2.3
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
[env:PSF-B85]
platform = espressif8266
board = esp8285
framework = arduino
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,13 +1,16 @@
#include "ArduinoJson.h"
#include "ArduinoOTA.h"
#include "ESPAsyncWebServer.h"
#include "ESPAsyncWebServer.h" // https://github.com/aZholtikov/Async-Web-Server
#include "LittleFS.h"
#include "Ticker.h"
#include "DallasTemperature.h"
#include "DHTesp.h"
#include "ZHNetwork.h"
#include "ZHConfig.h"
void onBroadcastReceiving(const char *data, const uint8_t *sender);
void onUnicastReceiving(const char *data, const uint8_t *sender);
void onConfirmReceiving(const uint8_t *target, const bool status);
void onConfirmReceiving(const uint8_t *target, const uint16_t id, const bool status);
void loadConfig(void);
void saveConfig(void);
@ -16,16 +19,26 @@ void setupWebServer(void);
void buttonInterrupt(void);
void switchingRelay(void);
void sendAttributesMessage(void);
void sendAttributesMessage(const uint8_t type = ENST_NONE);
void sendKeepAliveMessage(void);
void sendConfigMessage(void);
void sendStatusMessage(void);
void sendConfigMessage(const uint8_t type = ENST_NONE);
void sendStatusMessage(const uint8_t type = ENST_NONE);
const String firmware{"1.0"};
typedef struct
{
uint16_t id{0};
char message[200]{0};
} espnow_message_t;
std::vector<espnow_message_t> espnowMessage;
const String firmware{"1.31"};
String espnowNetName{"DEFAULT"};
String deviceName{"ESP-NOW switch"};
uint8_t workMode{0};
String deviceName = "ESP-NOW switch " + String(ESP.getChipId(), HEX);
bool relayStatus{false};
uint8_t relayPin{0};
@ -37,13 +50,24 @@ uint8_t extButtonPinType{0};
uint8_t ledPin{0};
uint8_t ledPinType{0};
uint8_t sensorPin{0};
uint8_t sensorType{0};
bool wasMqttAvailable{false};
uint8_t gatewayMAC[6]{0};
const String payloadOn{"ON"};
const String payloadOff{"OFF"};
ZHNetwork myNet;
AsyncWebServer webServer(80);
OneWire oneWire;
DallasTemperature ds18b20(&oneWire);
DHTesp dht;
Ticker buttonInterruptTimer;
Ticker gatewayAvailabilityCheckTimer;
@ -56,34 +80,35 @@ void apModeHideTimerCallback(void);
Ticker attributesMessageTimer;
bool attributesMessageTimerSemaphore{true};
void attributesMessageTimerCallback(void);
Ticker attributesMessageResendTimer;
bool attributesMessageResendTimerSemaphore{false};
Ticker keepAliveMessageTimer;
bool keepAliveMessageTimerSemaphore{true};
void keepAliveMessageTimerCallback(void);
Ticker keepAliveMessageResendTimer;
bool keepAliveMessageResendTimerSemaphore{false};
Ticker configMessageResendTimer;
bool configMessageResendTimerSemaphore{false};
Ticker statusMessageTimer;
bool statusMessageTimerSemaphore{true};
void statusMessageTimerCallback(void);
Ticker statusMessageResendTimer;
bool statusMessageResendTimerSemaphore{false};
void setup()
{
SPIFFS.begin();
LittleFS.begin();
loadConfig();
if (sensorPin)
{
if (sensorType == ENST_DS18B20)
oneWire.begin(sensorPin);
if (sensorType == ENST_DHT11 || sensorType == ENST_DHT22)
dht.setup(sensorPin, DHTesp::AUTO_DETECT);
}
if (relayPin)
{
pinMode(relayPin, OUTPUT);
digitalWrite(relayPin, relayPinType ? relayStatus : !relayStatus);
if (workMode)
digitalWrite(relayPin, relayPinType ? !relayStatus : relayStatus);
else
digitalWrite(relayPin, relayPinType ? relayStatus : !relayStatus);
}
if (ledPin)
{
@ -97,13 +122,14 @@ void setup()
WiFi.setSleepMode(WIFI_NONE_SLEEP);
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.setOnUnicastReceivingCallback(onUnicastReceiving);
myNet.setOnConfirmReceivingCallback(onConfirmReceiving);
WiFi.mode(WIFI_AP_STA);
WiFi.softAP(("ESP-NOW Switch " + myNet.getNodeMac()).c_str(), "12345678", 1, 0);
WiFi.softAP(("ESP-NOW switch " + String(ESP.getChipId(), HEX)).c_str(), "12345678", 1, 0);
apModeHideTimer.once(300, apModeHideTimerCallback);
setupWebServer();
@ -118,11 +144,19 @@ void setup()
void loop()
{
if (attributesMessageTimerSemaphore)
{
sendAttributesMessage();
if (sensorPin)
sendAttributesMessage(sensorType);
}
if (keepAliveMessageTimerSemaphore)
sendKeepAliveMessage();
if (statusMessageTimerSemaphore)
{
sendStatusMessage();
if (sensorPin)
sendStatusMessage(sensorType);
}
myNet.maintenance();
ArduinoOTA.handle();
}
@ -134,7 +168,7 @@ void onBroadcastReceiving(const char *data, const uint8_t *sender)
if (incomingData.deviceType != ENDT_GATEWAY)
return;
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)
{
isGatewayAvailable = true;
@ -145,7 +179,17 @@ void onBroadcastReceiving(const char *data, const uint8_t *sender)
{
wasMqttAvailable = temp;
if (temp)
{
sendConfigMessage();
if (sensorPin)
sendConfigMessage(sensorType);
sendAttributesMessage();
if (sensorPin)
sendAttributesMessage(sensorType);
sendStatusMessage();
if (sensorPin)
sendStatusMessage(sensorType);
}
}
gatewayAvailabilityCheckTimer.once(15, gatewayAvailabilityCheckTimerCallback);
}
@ -161,55 +205,57 @@ void onUnicastReceiving(const char *data, const uint8_t *sender)
if (incomingData.payloadsType == ENPT_SET)
{
deserializeJson(json, incomingData.message);
relayStatus = json["set"] == "ON" ? true : false;
relayStatus = json["set"] == payloadOn ? true : false;
if (relayPin)
digitalWrite(relayPin, relayPinType ? relayStatus : !relayStatus);
{
if (workMode)
digitalWrite(relayPin, relayPinType ? !relayStatus : relayStatus);
else
digitalWrite(relayPin, relayPinType ? relayStatus : !relayStatus);
}
if (ledPin)
digitalWrite(ledPin, ledPinType ? relayStatus : !relayStatus);
{
if (workMode)
digitalWrite(ledPin, ledPinType ? !relayStatus : relayStatus);
else
digitalWrite(ledPin, ledPinType ? relayStatus : !relayStatus);
}
saveConfig();
sendStatusMessage();
}
if (incomingData.payloadsType == ENPT_UPDATE)
{
WiFi.softAP(("ESP-NOW Switch " + myNet.getNodeMac()).c_str(), "12345678", 1, 0);
WiFi.softAP(("ESP-NOW switch " + String(ESP.getChipId(), HEX)).c_str(), "12345678", 1, 0);
webServer.begin();
apModeHideTimer.once(300, apModeHideTimerCallback);
}
if (incomingData.payloadsType == ENPT_RESTART)
ESP.restart();
}
void onConfirmReceiving(const uint8_t *target, const bool status)
void onConfirmReceiving(const uint8_t *target, const uint16_t id, const bool status)
{
if (status)
for (uint16_t i{0}; i < espnowMessage.size(); ++i)
{
if (attributesMessageResendTimerSemaphore)
espnow_message_t message = espnowMessage[i];
if (message.id == id)
{
attributesMessageResendTimerSemaphore = false;
attributesMessageResendTimer.detach();
}
if (keepAliveMessageResendTimerSemaphore)
{
keepAliveMessageResendTimerSemaphore = false;
keepAliveMessageResendTimer.detach();
}
if (configMessageResendTimerSemaphore)
{
configMessageResendTimerSemaphore = false;
configMessageResendTimer.detach();
}
if (statusMessageResendTimerSemaphore)
{
statusMessageResendTimerSemaphore = false;
statusMessageResendTimer.detach();
if (status)
espnowMessage.erase(espnowMessage.begin() + i);
else
{
message.id = myNet.sendUnicastMessage(message.message, gatewayMAC, true);
espnowMessage.at(i) = message;
}
}
}
}
void loadConfig()
{
if (!SPIFFS.exists("/config.json"))
if (!LittleFS.exists("/config.json"))
saveConfig();
File file = SPIFFS.open("/config.json", "r");
File file = LittleFS.open("/config.json", "r");
String jsonFile = file.readString();
StaticJsonDocument<512> json;
deserializeJson(json, jsonFile);
@ -224,6 +270,9 @@ void loadConfig()
extButtonPinType = json["extButtonPinType"];
ledPin = json["ledPin"];
ledPinType = json["ledPinType"];
sensorPin = json["sensorPin"];
sensorType = json["sensorType"];
workMode = json["workMode"];
file.close();
}
@ -242,8 +291,11 @@ void saveConfig()
json["extButtonPinType"] = extButtonPinType;
json["ledPin"] = ledPin;
json["ledPinType"] = ledPinType;
json["sensorPin"] = sensorPin;
json["sensorType"] = sensorType;
json["workMode"] = workMode;
json["system"] = "empty";
File file = SPIFFS.open("/config.json", "w");
File file = LittleFS.open("/config.json", "w");
serializeJsonPretty(json, file);
file.close();
}
@ -251,7 +303,7 @@ void saveConfig()
void setupWebServer()
{
webServer.on("/", HTTP_GET, [](AsyncWebServerRequest *request)
{ request->send(SPIFFS, "/index.htm"); });
{ request->send(LittleFS, "/index.htm"); });
webServer.on("/setting", HTTP_GET, [](AsyncWebServerRequest *request)
{
@ -263,6 +315,9 @@ void setupWebServer()
extButtonPinType = request->getParam("extButtonPinType")->value().toInt();
ledPin = request->getParam("ledPin")->value().toInt();
ledPinType = request->getParam("ledPinType")->value().toInt();
sensorPin = request->getParam("sensorPin")->value().toInt();
sensorType = request->getParam("sensorType")->value().toInt();
workMode = request->getParam("workMode")->value().toInt();
deviceName = request->getParam("deviceName")->value();
espnowNetName = request->getParam("espnowNetName")->value();
request->send(200);
@ -275,8 +330,8 @@ void setupWebServer()
webServer.onNotFound([](AsyncWebServerRequest *request)
{
if (SPIFFS.exists(request->url()))
request->send(SPIFFS, request->url());
if (LittleFS.exists(request->url()))
request->send(LittleFS, request->url());
else
{
request->send(404, "text/plain", "File Not Found");
@ -288,22 +343,32 @@ void setupWebServer()
void IRAM_ATTR buttonInterrupt()
{
ETS_GPIO_INTR_DISABLE();
buttonInterruptTimer.once_ms(50, switchingRelay); // For prevent contact chatter.
buttonInterruptTimer.once_ms(100, switchingRelay); // For prevent contact chatter.
}
void switchingRelay()
{
relayStatus = !relayStatus;
if (relayPin)
digitalWrite(relayPin, relayPinType ? relayStatus : !relayStatus);
{
if (workMode)
digitalWrite(relayPin, relayPinType ? !relayStatus : relayStatus);
else
digitalWrite(relayPin, relayPinType ? relayStatus : !relayStatus);
}
if (ledPin)
digitalWrite(ledPin, ledPinType ? relayStatus : !relayStatus);
{
if (workMode)
digitalWrite(ledPin, ledPinType ? !relayStatus : relayStatus);
else
digitalWrite(ledPin, ledPinType ? relayStatus : !relayStatus);
}
saveConfig();
sendStatusMessage();
ETS_GPIO_INTR_ENABLE();
}
void sendAttributesMessage()
void sendAttributesMessage(const uint8_t type)
{
if (!isGatewayAvailable)
return;
@ -313,22 +378,25 @@ void sendAttributesMessage()
uint32_t hours = mins / 60;
uint32_t days = hours / 24;
esp_now_payload_data_t outgoingData{ENDT_SWITCH, ENPT_ATTRIBUTES};
espnow_message_t message;
StaticJsonDocument<sizeof(esp_now_payload_data_t::message)> json;
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["MAC"] = myNet.getNodeMac();
json["Firmware"] = firmware;
json["Library"] = myNet.getFirmwareVersion();
json["Uptime"] = "Days:" + String(days) + " Hours:" + String(hours - (days * 24)) + " Mins:" + String(mins - (hours * 60));
char buffer[sizeof(esp_now_payload_data_t::message)]{0};
serializeJsonPretty(json, buffer);
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);
serializeJsonPretty(json, outgoingData.message);
memcpy(&message.message, &outgoingData, sizeof(esp_now_payload_data_t));
message.id = myNet.sendUnicastMessage(message.message, gatewayMAC, true);
attributesMessageResendTimerSemaphore = true;
attributesMessageResendTimer.once(1, sendAttributesMessage);
espnowMessage.push_back(message);
}
void sendKeepAliveMessage()
@ -337,64 +405,102 @@ void sendKeepAliveMessage()
return;
keepAliveMessageTimerSemaphore = false;
esp_now_payload_data_t outgoingData{ENDT_SWITCH, ENPT_KEEP_ALIVE};
char temp[sizeof(esp_now_payload_data_t)]{0};
memcpy(&temp, &outgoingData, sizeof(esp_now_payload_data_t));
myNet.sendUnicastMessage(temp, gatewayMAC, true);
espnow_message_t message;
memcpy(&message.message, &outgoingData, sizeof(esp_now_payload_data_t));
message.id = myNet.sendUnicastMessage(message.message, gatewayMAC, true);
keepAliveMessageResendTimerSemaphore = true;
keepAliveMessageResendTimer.once(1, sendKeepAliveMessage);
espnowMessage.push_back(message);
}
void sendConfigMessage()
void sendConfigMessage(const uint8_t type)
{
if (!isGatewayAvailable)
return;
esp_now_payload_data_t outgoingData{ENDT_SWITCH, ENPT_CONFIG};
espnow_message_t message;
StaticJsonDocument<sizeof(esp_now_payload_data_t::message)> json;
json["name"] = deviceName;
json["unit"] = 1;
json["type"] = getValueName(HACT_SWITCH);
json["class"] = getValueName(HASWDC_SWITCH);
json["reverse"] = "false";
char buffer[sizeof(esp_now_payload_data_t::message)]{0};
serializeJsonPretty(json, buffer);
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);
if (type == ENST_NONE)
{
json["name"] = deviceName;
json["unit"] = 1;
json["type"] = HACT_SWITCH;
json["class"] = HASWDC_SWITCH;
json["template"] = "state";
json["payload_on"] = payloadOn;
json["payload_off"] = payloadOff;
}
if (type == ENST_DS18B20 || type == ENST_DHT11 || type == ENST_DHT22)
{
outgoingData.deviceType = ENDT_SENSOR;
json["name"] = deviceName + " temperature";
json["unit"] = 2;
json["type"] = HACT_SENSOR;
json["class"] = HASDC_TEMPERATURE;
json["template"] = "temperature";
}
serializeJsonPretty(json, outgoingData.message);
memcpy(&message.message, &outgoingData, sizeof(esp_now_payload_data_t));
message.id = myNet.sendUnicastMessage(message.message, gatewayMAC, true);
configMessageResendTimerSemaphore = true;
configMessageResendTimer.once(5, sendConfigMessage);
espnowMessage.push_back(message);
if (type == ENST_DHT11 || type == ENST_DHT22)
{
outgoingData.deviceType = ENDT_SENSOR;
json["name"] = deviceName + " humidity";
json["unit"] = 3;
json["type"] = HACT_SENSOR;
json["class"] = HASDC_HUMIDITY;
json["template"] = "humidity";
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()
void sendStatusMessage(const uint8_t type)
{
if (!isGatewayAvailable)
return;
statusMessageTimerSemaphore = false;
esp_now_payload_data_t outgoingData{ENDT_SWITCH, ENPT_STATE};
espnow_message_t message;
StaticJsonDocument<sizeof(esp_now_payload_data_t::message)> json;
json["state"] = relayStatus ? "ON" : "OFF";
char buffer[sizeof(esp_now_payload_data_t::message)]{0};
serializeJsonPretty(json, buffer);
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);
if (type == ENST_NONE)
json["state"] = relayStatus ? payloadOn : payloadOff;
if (type == ENST_DS18B20)
{
outgoingData.deviceType = ENDT_SENSOR;
ds18b20.requestTemperatures();
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);
statusMessageResendTimerSemaphore = true;
statusMessageResendTimer.once(1, sendStatusMessage);
espnowMessage.push_back(message);
}
void gatewayAvailabilityCheckTimerCallback()
{
isGatewayAvailable = false;
memset(gatewayMAC, 0, 6);
memset(&gatewayMAC, 0, 6);
espnowMessage.clear();
}
void apModeHideTimerCallback()
{
WiFi.softAP(("ESP-NOW Switch " + myNet.getNodeMac()).c_str(), "12345678", 1, 1);
WiFi.softAP(("ESP-NOW switch " + String(ESP.getChipId(), HEX)).c_str(), "12345678", 1, 1);
webServer.end();
}
void attributesMessageTimerCallback()