4 Commits
v1.13 ... v1.2

Author SHA1 Message Date
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
24 changed files with 224 additions and 44 deletions

View File

@ -1,15 +1,16 @@
# 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.
## Notes
@ -25,5 +26,6 @@ See [here](https://github.com/aZholtikov/ESP-NOW-Switch/tree/main/hardware).
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).

View File

@ -37,6 +37,8 @@ function loadBlock() {
setGpioValue('buttonPinTypeSelect', 'buttonPinType');
setGpioValue('extButtonPinSelect', 'extButtonPin');
setGpioValue('extButtonPinTypeSelect', 'extButtonPinType');
setGpioValue('sensorPinSelect', 'sensorPin');
setGpioValue('sensorTypeSelect', 'sensorType');
handleServerResponse();
}
@ -67,7 +69,9 @@ function saveSetting(submit) {
+ "&buttonPin=" + getSelectValue('buttonPinSelect')
+ "&buttonPinType=" + getSelectValue('buttonPinTypeSelect')
+ "&extButtonPin=" + getSelectValue('extButtonPinSelect')
+ "&extButtonPinType=" + getSelectValue('extButtonPinTypeSelect');
+ "&extButtonPinType=" + getSelectValue('extButtonPinTypeSelect')
+ "&sensorPin=" + getSelectValue('sensorPinSelect')
+ "&sensorType=" + getSelectValue('sensorTypeSelect');
sendRequest(submit, server);
alert("Please restart device for changes apply.");
}

View File

@ -145,6 +145,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);">

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

View File

@ -1,12 +1,29 @@
# Tested on
1. MOES 1CH 10A. Built on Tuya WiFi module WA2 (WB2S) (BK7231T chip). Replacement required. Performed replacement with ESP-02S (TYWE2S). [Photo](https://github.com/aZholtikov/ESP-NOW-Switch/tree/main/hardware/MOES_1CH_10A).
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 GPIO04 HIGH
Led GPIO GPIO12 LOW
Button GPIO GPIO14 RISING
Relay GPIO GPIO12 HIGH
Led GPIO GPIO04 LOW
Button GPIO GPIO13 RISING
```
2. AUBESS 1CH 16A (Coming soon)
3. LIGHT E27 SOCKET (Coming soon)
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
Led GPIO GPIO13 LOW
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

@ -2,33 +2,72 @@
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: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: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: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_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
[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,7 +1,10 @@
#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"
@ -16,10 +19,10 @@ 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);
typedef struct
{
@ -29,7 +32,7 @@ typedef struct
std::vector<espnow_message_t> espnowMessage;
const String firmware{"1.13"};
const String firmware{"1.2"};
String espnowNetName{"DEFAULT"};
@ -45,6 +48,9 @@ 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};
@ -55,6 +61,11 @@ const String payloadOff{"OFF"};
ZHNetwork myNet;
AsyncWebServer webServer(80);
OneWire oneWire;
DallasTemperature ds18b20(&oneWire);
DHTesp dht;
Ticker buttonInterruptTimer;
Ticker gatewayAvailabilityCheckTimer;
@ -78,10 +89,18 @@ void statusMessageTimerCallback(void);
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);
@ -99,6 +118,7 @@ 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);
@ -120,11 +140,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();
}
@ -147,7 +175,11 @@ void onBroadcastReceiving(const char *data, const uint8_t *sender)
{
wasMqttAvailable = temp;
if (temp)
{
sendConfigMessage();
if (sensorPin)
sendConfigMessage(sensorType);
}
}
gatewayAvailabilityCheckTimer.once(15, gatewayAvailabilityCheckTimerCallback);
}
@ -187,6 +219,7 @@ void onConfirmReceiving(const uint8_t *target, const uint16_t id, const bool sta
{
espnow_message_t message = espnowMessage[i];
if (message.id == id)
{
if (status)
espnowMessage.erase(espnowMessage.begin() + i);
else
@ -194,14 +227,15 @@ void onConfirmReceiving(const uint8_t *target, const uint16_t id, const bool sta
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);
@ -216,6 +250,8 @@ void loadConfig()
extButtonPinType = json["extButtonPinType"];
ledPin = json["ledPin"];
ledPinType = json["ledPinType"];
sensorPin = json["sensorPin"];
sensorType = json["sensorType"];
file.close();
}
@ -234,8 +270,10 @@ void saveConfig()
json["extButtonPinType"] = extButtonPinType;
json["ledPin"] = ledPin;
json["ledPinType"] = ledPinType;
json["sensorPin"] = sensorPin;
json["sensorType"] = sensorType;
json["system"] = "empty";
File file = SPIFFS.open("/config.json", "w");
File file = LittleFS.open("/config.json", "w");
serializeJsonPretty(json, file);
file.close();
}
@ -243,7 +281,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)
{
@ -255,6 +293,8 @@ 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();
deviceName = request->getParam("deviceName")->value();
espnowNetName = request->getParam("espnowNetName")->value();
request->send(200);
@ -267,8 +307,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");
@ -295,7 +335,7 @@ void switchingRelay()
ETS_GPIO_INTR_ENABLE();
}
void sendAttributesMessage()
void sendAttributesMessage(const uint8_t type)
{
if (!isGatewayAvailable)
return;
@ -307,7 +347,13 @@ void sendAttributesMessage()
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;
@ -333,28 +379,56 @@ void 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"] = HACT_SWITCH;
json["class"] = HASWDC_SWITCH;
json["template"] = "state";
json["payload_on"] = payloadOn;
json["payload_off"] = payloadOff;
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);
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;
@ -362,7 +436,20 @@ void sendStatusMessage()
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 ? payloadOn : payloadOff;
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);