diff --git a/README.md b/README.md index 65c3da5..5850b0d 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # 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 @@ -10,6 +10,7 @@ ESP-NOW based switch for ESP8266. Alternate firmware for Tuya/SmartLife WiFi swi 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 @@ -27,4 +28,4 @@ See [here](https://github.com/aZholtikov/ESP-NOW-Switch/tree/main/hardware). 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. 4. Upload the "data" folder (with web interface) into the filesystem before flashing. -5. 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). diff --git a/data/function.js b/data/function.js index a4acbd2..a078387 100755 --- a/data/function.js +++ b/data/function.js @@ -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."); } diff --git a/data/index.htm b/data/index.htm index be8d55d..333fd45 100644 --- a/data/index.htm +++ b/data/index.htm @@ -145,6 +145,37 @@

+
+

Ext sensor GPIO:

+ + +

+

+
+
diff --git a/hardware/README.md b/hardware/README.md index 47b5537..581414c 100644 --- a/hardware/README.md +++ b/hardware/README.md @@ -3,17 +3,27 @@ 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 + 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 + Relay GPIO GPIO13 HIGH + Led GPIO GPIO04 LOW + Button GPIO GPIO03 RISING + Ext button GPIO GPIO14 FALLING ``` -3. LIGHT E27 SOCKET (Coming soon) + +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) diff --git a/hardware/TH_1CH_16A/PSF-B85.pdf b/hardware/TH_1CH_16A/PSF-B85.pdf new file mode 100644 index 0000000..70e0b6c Binary files /dev/null and b/hardware/TH_1CH_16A/PSF-B85.pdf differ diff --git a/hardware/TH_1CH_16A/inside1.jpeg b/hardware/TH_1CH_16A/inside1.jpeg new file mode 100644 index 0000000..6e06104 Binary files /dev/null and b/hardware/TH_1CH_16A/inside1.jpeg differ diff --git a/hardware/TH_1CH_16A/inside2.jpeg b/hardware/TH_1CH_16A/inside2.jpeg new file mode 100644 index 0000000..7438af6 Binary files /dev/null and b/hardware/TH_1CH_16A/inside2.jpeg differ diff --git a/hardware/TH_1CH_16A/inside3.jpeg b/hardware/TH_1CH_16A/inside3.jpeg new file mode 100644 index 0000000..226a169 Binary files /dev/null and b/hardware/TH_1CH_16A/inside3.jpeg differ diff --git a/hardware/TH_1CH_16A/inside4.jpeg b/hardware/TH_1CH_16A/inside4.jpeg new file mode 100644 index 0000000..f2560ca Binary files /dev/null and b/hardware/TH_1CH_16A/inside4.jpeg differ diff --git a/hardware/TH_1CH_16A/main1.jpeg b/hardware/TH_1CH_16A/main1.jpeg new file mode 100644 index 0000000..edc0b36 Binary files /dev/null and b/hardware/TH_1CH_16A/main1.jpeg differ diff --git a/hardware/TH_1CH_16A/main2.jpeg b/hardware/TH_1CH_16A/main2.jpeg new file mode 100644 index 0000000..f319585 Binary files /dev/null and b/hardware/TH_1CH_16A/main2.jpeg differ diff --git a/hardware/TH_1CH_16A/pins.jpeg b/hardware/TH_1CH_16A/pins.jpeg new file mode 100644 index 0000000..e7476a3 Binary files /dev/null and b/hardware/TH_1CH_16A/pins.jpeg differ diff --git a/hardware/TH_1CH_16A/sensor.jpeg b/hardware/TH_1CH_16A/sensor.jpeg new file mode 100644 index 0000000..c963eee Binary files /dev/null and b/hardware/TH_1CH_16A/sensor.jpeg differ diff --git a/hardware/TH_1CH_16A/sensor_pin.jpeg b/hardware/TH_1CH_16A/sensor_pin.jpeg new file mode 100644 index 0000000..7fb2f7d Binary files /dev/null and b/hardware/TH_1CH_16A/sensor_pin.jpeg differ diff --git a/platformio.ini b/platformio.ini index 795627e..45e9abe 100644 --- a/platformio.ini +++ b/platformio.ini @@ -2,46 +2,72 @@ platform = espressif8266 board = esp12e framework = arduino +board_build.filesystem = littlefs board_build.ldscript = eagle.flash.4m2m.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/me-no-dev/ESPAsyncWebServer + 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_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/me-no-dev/ESPAsyncWebServer + https://github.com/milesburton/Arduino-Temperature-Control-Library + https://github.com/beegee-tokyo/DHTesp [env:ESP-02S] platform = espressif8266 board = esp8285 framework = arduino +board_build.filesystem = littlefs board_build.ldscript = eagle.flash.2m256.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/me-no-dev/ESPAsyncWebServer + 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/me-no-dev/ESPAsyncWebServer + 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 \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp index 7acd53d..25daf78 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -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 espnowMessage; -const String firmware{"1.14"}; +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); @@ -121,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(); } @@ -148,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); } @@ -188,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 @@ -195,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); @@ -217,6 +250,8 @@ void loadConfig() extButtonPinType = json["extButtonPinType"]; ledPin = json["ledPin"]; ledPinType = json["ledPinType"]; + sensorPin = json["sensorPin"]; + sensorType = json["sensorType"]; file.close(); } @@ -235,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(); } @@ -244,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) { @@ -256,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); @@ -268,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"); @@ -296,7 +335,7 @@ void switchingRelay() ETS_GPIO_INTR_ENABLE(); } -void sendAttributesMessage() +void sendAttributesMessage(const uint8_t type) { if (!isGatewayAvailable) return; @@ -308,7 +347,13 @@ void sendAttributesMessage() esp_now_payload_data_t outgoingData{ENDT_SWITCH, ENPT_ATTRIBUTES}; espnow_message_t message; StaticJsonDocument 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; @@ -334,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 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; @@ -363,7 +436,20 @@ void sendStatusMessage() esp_now_payload_data_t outgoingData{ENDT_SWITCH, ENPT_STATE}; espnow_message_t message; StaticJsonDocument 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);