From 4f14de776322d2eafb4b64af9b36c5dee4ae3864 Mon Sep 17 00:00:00 2001 From: Alexey Zholtikov Date: Sun, 5 Mar 2023 10:34:53 +0300 Subject: [PATCH] Version 1.3 Minimized of config message size. Changed SDK version for 3.0.5 Changed config data storage location to EEPROM. Changed FS from SPIFFS to LittleFS. --- README.md | 4 +- data/function.js | 2 +- doc/README.md | 8 +- platformio.ini | 42 +++++++++- src/main.cpp | 204 +++++++++++++++++++++-------------------------- 5 files changed, 135 insertions(+), 125 deletions(-) diff --git a/README.md b/README.md index 32caad2..e0e72fe 100644 --- a/README.md +++ b/README.md @@ -4,10 +4,10 @@ ESP-NOW based window/door sensor for ESP8266. Alternate firmware for Tuya/SmartL ## Features -1. When triggered transmits system information, battery status (HIGH/MID/LOW) and sensor status (OPEN/CLOSED). +1. When triggered transmits system information, battery level charge and sensor status (OPEN/CLOSED). 2. Average response time of 1 second (depends on the MCU of the sensor). 3. In setup/update mode creates an access point named "ESP-NOW window XXXXXXXXXXXX" with password "12345678" (IP 192.168.4.1). -4. Automatically adds sensor and battery configuration to Home Assistan via MQTT discovery as a binary_sensors (2 different binary_sensor). +4. Automatically adds sensor and battery configuration to Home Assistan via MQTT discovery as a binary_sensor (for sensor) and sensor (for battery). 5. Possibility firmware update over OTA (if is allows the size of the flash memory). 6. Web interface for settings. diff --git a/data/function.js b/data/function.js index 1758539..64b7d04 100755 --- a/data/function.js +++ b/data/function.js @@ -10,7 +10,7 @@ function createXmlHttpObject() { function load() { if (xmlHttp.readyState == 0 || xmlHttp.readyState == 4) { - xmlHttp.open('PUT', '/config.json', true); + xmlHttp.open('GET', '/config', true); xmlHttp.send(null); xmlHttp.onload = function () { jsonResponse = JSON.parse(xmlHttp.responseText); diff --git a/doc/README.md b/doc/README.md index 031443c..9289859 100644 --- a/doc/README.md +++ b/doc/README.md @@ -11,20 +11,20 @@ Communication protocol used in the firmware (only necessary "cuts" from the orig Module sends 55 AA 00 01 00 00 00 (Initial message) MCU returns 55 AA 00 01 00 ............ (MCU system information) Module sends 55 AA 00 02 00 01 04 06 (Network connection established) - MCU returns 55 AA 00 02 00 00 01 (Confirmation message) + MCU returns 55 AA 00 02 00 00 01 (MCU confirmation message) MCU sends 55 AA 00 08 00 0C 00 01 01 01 01 01 03 04 00 01 02 23 (Battery status. 02 23 - high, 01 22 - medium, 00 21 - low) Module returns 55 AA 00 08 00 01 00 08 (Confirmation message) MCU sends 55 AA 00 08 00 0C 00 02 02 02 02 02 01 01 01 00 22 (Sensor position. 01 23 - open, 00 22 - closed) Module returns 55 AA 00 08 00 01 00 08 (Confirmation message) Module power off -2. Sending the battery status. Pressing the button. +2. Sending the battery status. Pressing the button. Not used in the firmware. Module power is on Module sends 55 AA 00 01 00 00 00 (Initial message) MCU returns 55 AA 00 01 00 00 ............ (MCU system information) Module sends 55 AA 00 02 00 01 04 06 (Network connection established) - MCU returns 55 AA 00 02 00 00 01 (Confirmation message) + MCU returns 55 AA 00 02 00 00 01 (MCU confirmation message) MCU sends 55 AA 00 08 00 0C 00 01 01 01 01 01 03 04 00 01 02 23 (Battery status. 02 23 - high, 01 22 - medium, 00 21 - low) Module returns 55 AA 00 08 00 01 00 08 (Confirmation message) Module power off @@ -35,7 +35,7 @@ Communication protocol used in the firmware (only necessary "cuts" from the orig Module sends 55 AA 00 01 00 00 00 (Initial message) MCU returns 55 AA 00 01 00 00 ............ (MCU system information) Module sends 55 AA 00 02 00 01 04 06 (Network connection established) - MCU returns 55 AA 00 02 00 00 01 (Confirmation message) + MCU returns 55 AA 00 02 00 00 01 (MCU confirmation message) MCU sends 55 AA 00 03 00 00 02 (Message for switching to setting mode) Module returns 55 AA 00 03 00 00 02 (Confirmation message) diff --git a/platformio.ini b/platformio.ini index 79af914..d8a837b 100644 --- a/platformio.ini +++ b/platformio.ini @@ -1,19 +1,53 @@ +[env:ESP-12E] +platform = espressif8266 +board = esp12e +framework = arduino +build_flags = -D PIO_FRAMEWORK_ARDUINO_ESPRESSIF_SDK305 +board_build.filesystem = littlefs +board_build.ldscript = eagle.flash.4m1m.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 + +[env:ESP-12E-OTA] +platform = espressif8266 +board = esp12e +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_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 + [env:TYWE3S] platform = espressif8266 board = esp01_1m 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 - 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 [env:ESP-M2] 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 - 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 diff --git a/src/main.cpp b/src/main.cpp index e0eb7e2..b8b0321 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,9 +1,13 @@ #include "ArduinoJson.h" #include "ArduinoOTA.h" -#include "ESPAsyncWebServer.h" +#include "ESPAsyncWebServer.h" // https://github.com/aZholtikov/Async-Web-Server +#include "LittleFS.h" +#include "EEPROM.h" #include "ZHNetwork.h" #include "ZHConfig.h" +ADC_MODE(ADC_VCC); + void onConfirmReceiving(const uint8_t *target, const uint16_t id, const bool status); void loadConfig(void); @@ -14,15 +18,13 @@ void sendSensorConfigMessage(void); void sendBatteryConfigMessage(void); void sendAttributesMessage(void); -const String firmware{"1.2"}; - -String espnowNetName{"DEFAULT"}; - -String deviceName = "ESP-NOW window " + String(ESP.getChipId(), HEX); -uint8_t deviceClass{HABSDC_WINDOW}; - -String sensorStatus{""}; -String batteryStatus{""}; +struct deviceConfig +{ + const String firmware{"1.3"}; + String espnowNetName{"DEFAULT"}; + String deviceName = "ESP-NOW window " + String(ESP.getChipId(), HEX); + uint8_t deviceClass{HABSDC_WINDOW}; +} config; char receivedBytes[128]{0}; uint8_t counter{0}; @@ -31,9 +33,6 @@ bool dataReceiving{false}; bool dataReceived{false}; bool semaphore{false}; -esp_now_payload_data_t outgoingData{ENDT_SENSOR, ENPT_STATE}; -StaticJsonDocument json; -char temp[sizeof(esp_now_payload_data_t)]{0}; const char initialMessage[] = {0x55, 0xAA, 0x00, 0x01, 0x00, 0x00, 0x00}; const char connectedMessage[] = {0x55, 0xAA, 0x00, 0x02, 0x00, 0x01, 0x04, 0x06}; const char settingMessage[] = {0x55, 0xAA, 0x00, 0x03, 0x00, 0x00, 0x02}; @@ -46,14 +45,11 @@ void setup() { Serial.begin(9600); - SPIFFS.begin(); + LittleFS.begin(); loadConfig(); - json["state"] = sensorStatus; - json["battery"] = batteryStatus; - - myNet.begin(espnowNetName.c_str()); + myNet.begin(config.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.setOnConfirmReceivingCallback(onConfirmReceiving); @@ -95,15 +91,15 @@ void loop() } if (dataReceived) { - if (receivedBytes[3] == 0x01) + if (receivedBytes[3] == 0x01) // MCU system information. { Serial.write(connectedMessage, sizeof(connectedMessage)); Serial.flush(); dataReceived = false; } - if (receivedBytes[3] == 0x02) + if (receivedBytes[3] == 0x02) // MCU confirmation message. dataReceived = false; - if (receivedBytes[3] == 0x03) + if (receivedBytes[3] == 0x03) // Message for switching to setting mode. { Serial.write(settingMessage, sizeof(settingMessage)); Serial.flush(); @@ -114,47 +110,26 @@ void loop() setupWebServer(); ArduinoOTA.begin(); } - if (receivedBytes[3] == 0x08) + if (receivedBytes[3] == 0x08) // Sensor status message. { - if (receivedBytes[7] == 0x01) + if (receivedBytes[7] == 0x01) // Battery status. { - if (receivedBytes[17] == 0x02) - { - json["battery"] = "HIGH"; - batteryStatus = "HIGH"; - } - if (receivedBytes[17] == 0x01) - { - json["battery"] = "MID"; - batteryStatus = "MID"; - } - if (receivedBytes[17] == 0x00) - { - json["battery"] = "LOW"; - batteryStatus = "LOW"; - } dataReceived = false; - saveConfig(); - serializeJsonPretty(json, outgoingData.message); - memcpy(&temp, &outgoingData, sizeof(esp_now_payload_data_t)); - myNet.sendBroadcastMessage(temp); - semaphore = true; + Serial.write(confirmationMessage, sizeof(confirmationMessage)); + Serial.flush(); } - if (receivedBytes[7] == 0x02) + if (receivedBytes[7] == 0x02) // Sensor position. { + esp_now_payload_data_t outgoingData{ENDT_SENSOR, ENPT_STATE}; + DynamicJsonDocument json(sizeof(esp_now_payload_data_t::message)); if (receivedBytes[17] == 0x01) - { json["state"] = "OPEN"; - sensorStatus = "OPEN"; - } if (receivedBytes[17] == 0x00) - { json["state"] = "CLOSED"; - sensorStatus = "CLOSED"; - } + json["battery"] = round((double(system_get_vdd33()) / 1000) * 100) / 100; dataReceived = false; - saveConfig(); serializeJsonPretty(json, outgoingData.message); + char temp[sizeof(esp_now_payload_data_t)]{0}; memcpy(&temp, &outgoingData, sizeof(esp_now_payload_data_t)); myNet.sendBroadcastMessage(temp); semaphore = true; @@ -176,61 +151,69 @@ void onConfirmReceiving(const uint8_t *target, const uint16_t id, const bool sta void loadConfig() { - if (!SPIFFS.exists("/config.json")) + ETS_GPIO_INTR_DISABLE(); + EEPROM.begin(4096); + if (EEPROM.read(4095) == 254) + { + EEPROM.get(0, config); + EEPROM.end(); + } + else + { + EEPROM.end(); saveConfig(); - File file = SPIFFS.open("/config.json", "r"); - String jsonFile = file.readString(); - StaticJsonDocument<1024> json; - deserializeJson(json, jsonFile); - espnowNetName = json["espnowNetName"].as(); - deviceName = json["deviceName"].as(); - deviceClass = json["deviceClass"]; - sensorStatus = json["sensorStatus"].as(); - batteryStatus = json["batteryStatus"].as(); - file.close(); + } + delay(50); + ETS_GPIO_INTR_ENABLE(); } void saveConfig() { - StaticJsonDocument<1024> json; - json["firmware"] = firmware; - json["espnowNetName"] = espnowNetName; - json["deviceName"] = deviceName; - json["deviceClass"] = deviceClass; - json["sensorStatus"] = sensorStatus; - json["batteryStatus"] = batteryStatus; - json["system"] = "empty"; - File file = SPIFFS.open("/config.json", "w"); - serializeJsonPretty(json, file); - file.close(); + ETS_GPIO_INTR_DISABLE(); + EEPROM.begin(4096); + EEPROM.write(4095, 254); + EEPROM.put(0, config); + EEPROM.end(); + delay(50); + ETS_GPIO_INTR_ENABLE(); } void setupWebServer() { webServer.on("/", HTTP_GET, [](AsyncWebServerRequest *request) - { request->send(SPIFFS, "/index.htm"); }); + { request->send(LittleFS, "/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) { - deviceName = request->getParam("deviceName")->value(); - deviceClass = request->getParam("deviceClass")->value().toInt(); - espnowNetName = request->getParam("espnowNetName")->value(); + config.deviceName = request->getParam("deviceName")->value(); + config.deviceClass = request->getParam("deviceClass")->value().toInt(); + config.espnowNetName = request->getParam("espnowNetName")->value(); request->send(200); saveConfig(); }); - webServer.on("/restart", HTTP_GET, [](AsyncWebServerRequest *request) + webServer.on("/config", HTTP_GET, [](AsyncWebServerRequest *request) { - request->send(200); + String configJson; + DynamicJsonDocument json(256); // To calculate the buffer size uses https://arduinojson.org/v6/assistant. + json["firmware"] = config.firmware; + json["espnowNetName"] = config.espnowNetName; + json["deviceName"] = config.deviceName; + json["deviceClass"] = config.deviceClass; + serializeJsonPretty(json, configJson); + request->send(200, "application/json", configJson); }); + + webServer.on("/restart", HTTP_GET, [](AsyncWebServerRequest *request) + {request->send(200); ESP.restart(); }); webServer.onNotFound([](AsyncWebServerRequest *request) - { - if (SPIFFS.exists(request->url())) - request->send(SPIFFS, request->url()); - else - { - request->send(404, "text/plain", "File Not Found"); - } }); + { request->send(404, "text/plain", "File Not Found"); }); webServer.begin(); } @@ -238,17 +221,15 @@ void setupWebServer() void sendSensorConfigMessage() { esp_now_payload_data_t outgoingData{ENDT_SENSOR, ENPT_CONFIG}; - StaticJsonDocument json; - json["name"] = deviceName; - json["unit"] = 1; - json["type"] = HACT_BINARY_SENSOR; - json["class"] = deviceClass; - json["template"] = "state"; - json["payload_on"] = "OPEN"; - json["payload_off"] = "CLOSED"; - char buffer[sizeof(esp_now_payload_data_t::message)]{0}; - serializeJsonPretty(json, buffer); - memcpy(outgoingData.message, buffer, sizeof(esp_now_payload_data_t::message)); + DynamicJsonDocument json(sizeof(esp_now_payload_data_t::message)); + json[MCMT_DEVICE_NAME] = config.deviceName; + json[MCMT_DEVICE_UNIT] = 1; + json[MCMT_COMPONENT_TYPE] = HACT_BINARY_SENSOR; + json[MCMT_DEVICE_CLASS] = config.deviceClass; + json[MCMT_VALUE_TEMPLATE] = "state"; + json[MCMT_PAYLOAD_ON] = "OPEN"; + json[MCMT_PAYLOAD_OFF] = "CLOSED"; + serializeJsonPretty(json, outgoingData.message); char temp[sizeof(esp_now_payload_data_t)]{0}; memcpy(&temp, &outgoingData, sizeof(esp_now_payload_data_t)); myNet.sendBroadcastMessage(temp); @@ -257,17 +238,14 @@ void sendSensorConfigMessage() void sendBatteryConfigMessage() { esp_now_payload_data_t outgoingData{ENDT_SENSOR, ENPT_CONFIG}; - StaticJsonDocument json; - json["name"] = deviceName + " battery"; - json["unit"] = 2; - json["type"] = HACT_BINARY_SENSOR; - json["class"] = HABSDC_BATTERY; - json["template"] = "battery"; - json["payload_on"] = "MID"; - json["payload_off"] = "HIGH"; - char buffer[sizeof(esp_now_payload_data_t::message)]{0}; - serializeJsonPretty(json, buffer); - memcpy(outgoingData.message, buffer, sizeof(esp_now_payload_data_t::message)); + DynamicJsonDocument json(sizeof(esp_now_payload_data_t::message)); + json[MCMT_DEVICE_NAME] = config.deviceName + " battery"; + json[MCMT_DEVICE_UNIT] = 2; + json[MCMT_COMPONENT_TYPE] = HACT_SENSOR; + json[MCMT_DEVICE_CLASS] = HASDC_VOLTAGE; + json[MCMT_VALUE_TEMPLATE] = "battery"; + json[MCMT_UNIT_OF_MEASUREMENT] = "V"; + serializeJsonPretty(json, outgoingData.message); char temp[sizeof(esp_now_payload_data_t)]{0}; memcpy(&temp, &outgoingData, sizeof(esp_now_payload_data_t)); myNet.sendBroadcastMessage(temp); @@ -276,16 +254,14 @@ void sendBatteryConfigMessage() void sendAttributesMessage() { esp_now_payload_data_t outgoingData{ENDT_SENSOR, ENPT_ATTRIBUTES}; - StaticJsonDocument json; + DynamicJsonDocument json(sizeof(esp_now_payload_data_t::message)); json["Type"] = "ESP-NOW window sensor"; json["MCU"] = "ESP8266"; json["MAC"] = myNet.getNodeMac(); - json["Firmware"] = firmware; + json["Firmware"] = config.firmware; json["Library"] = myNet.getFirmwareVersion(); - char buffer[sizeof(esp_now_payload_data_t::message)]{0}; - serializeJsonPretty(json, buffer); - memcpy(outgoingData.message, buffer, sizeof(esp_now_payload_data_t::message)); + serializeJsonPretty(json, outgoingData.message); char temp[sizeof(esp_now_payload_data_t)]{0}; - memcpy(temp, &outgoingData, sizeof(esp_now_payload_data_t)); + memcpy(&temp, &outgoingData, sizeof(esp_now_payload_data_t)); myNet.sendBroadcastMessage(temp); }