8 Commits
v1.11 ... v1.15

Author SHA1 Message Date
ed42021c9b Version 1.15
Minor changes.
2023-02-12 18:42:57 +03:00
02b8326b80 Minor changes 2023-02-11 08:42:57 +03:00
0795911f7a Minor changes 2023-02-09 21:28:56 +03:00
dc9f08e82c Version 1.14
Changed FS from SPIFFS to LittleFS.
2023-02-04 10:57:34 +03:00
4d8146e64b Changed FS from SPIFFS to LittleFS 2023-02-02 20:31:16 +03:00
39a13599c3 Minor changes 2023-01-28 14:52:38 +03:00
bcda1c9ea5 Version 1.13
Added encrypting messages.
2023-01-22 12:38:53 +03:00
7f51f05eb8 Version 1.12
Main code refactoring.
2023-01-14 10:57:25 +03:00
3 changed files with 77 additions and 83 deletions

View File

@ -4,7 +4,7 @@ ESP-NOW based light/led strip controller for ESP8266. Alternate firmware for Tuy
## Features
1. After turn on (or after rebooting) creates an access point named "ESP-NOW Light 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 light 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 light/led strip configuration to Home Assistan via MQTT discovery as a light.
@ -25,5 +25,8 @@ See [here](https://github.com/aZholtikov/ESP-NOW-Light-Led-Strip/tree/main/hardw
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 light/led strip controllers, 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 WiFi light/led strip controllers, 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

@ -2,20 +2,24 @@
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
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-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
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

View File

@ -1,13 +1,14 @@
#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 "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);
@ -18,11 +19,19 @@ void sendKeepAliveMessage(void);
void sendConfigMessage(void);
void sendStatusMessage(void);
String getValue(String data, char separator, byte index);
String getValue(String data, char separator, uint8_t index);
void changeLedState(void);
const String firmware{"1.11"};
typedef struct
{
uint16_t id{0};
char message[200]{0};
} espnow_message_t;
std::vector<espnow_message_t> espnowMessage;
const String firmware{"1.15"};
String espnowNetName{"DEFAULT"};
@ -62,29 +71,20 @@ 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()
{
analogWriteRange(255);
SPIFFS.begin();
LittleFS.begin();
loadConfig();
@ -103,6 +103,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);
@ -133,14 +134,14 @@ void loop()
ArduinoOTA.handle();
}
void onBroadcastReceiving(const char *data, const byte *sender)
void onBroadcastReceiving(const char *data, const uint8_t *sender)
{
esp_now_payload_data_t incomingData;
memcpy(&incomingData, data, sizeof(esp_now_payload_data_t));
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;
@ -151,13 +152,17 @@ void onBroadcastReceiving(const char *data, const byte *sender)
{
wasMqttAvailable = temp;
if (temp)
{
sendConfigMessage();
sendAttributesMessage();
sendStatusMessage();
}
}
gatewayAvailabilityCheckTimer.once(15, gatewayAvailabilityCheckTimerCallback);
}
}
void onUnicastReceiving(const char *data, const byte *sender)
void onUnicastReceiving(const char *data, const uint8_t *sender)
{
esp_now_payload_data_t incomingData;
memcpy(&incomingData, data, sizeof(esp_now_payload_data_t));
@ -192,38 +197,29 @@ void onUnicastReceiving(const char *data, const byte *sender)
ESP.restart();
}
void onConfirmReceiving(const uint8_t *target, const bool status)
void onConfirmReceiving(const uint8_t *target, const uint16_t id, const bool status)
{
for (uint16_t i{0}; i < espnowMessage.size(); ++i)
{
espnow_message_t message = espnowMessage[i];
if (message.id == id)
{
if (status)
espnowMessage.erase(espnowMessage.begin() + i);
else
{
if (attributesMessageResendTimerSemaphore)
{
attributesMessageResendTimerSemaphore = false;
attributesMessageResendTimer.detach();
message.id = myNet.sendUnicastMessage(message.message, gatewayMAC, true);
espnowMessage.at(i) = message;
}
if (keepAliveMessageResendTimerSemaphore)
{
keepAliveMessageResendTimerSemaphore = false;
keepAliveMessageResendTimer.detach();
}
if (configMessageResendTimerSemaphore)
{
configMessageResendTimerSemaphore = false;
configMessageResendTimer.detach();
}
if (statusMessageResendTimerSemaphore)
{
statusMessageResendTimerSemaphore = false;
statusMessageResendTimer.detach();
}
}
}
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);
@ -263,7 +259,7 @@ void saveConfig()
json["green"] = green;
json["blue"] = blue;
json["system"] = "empty";
File file = SPIFFS.open("/config.json", "w");
File file = LittleFS.open("/config.json", "w");
serializeJsonPretty(json, file);
file.close();
}
@ -271,7 +267,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)
{
@ -293,8 +289,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");
@ -313,6 +309,7 @@ void sendAttributesMessage()
uint32_t hours = mins / 60;
uint32_t days = hours / 24;
esp_now_payload_data_t outgoingData{ENDT_LED, ENPT_ATTRIBUTES};
espnow_message_t message;
StaticJsonDocument<sizeof(esp_now_payload_data_t::message)> json;
json["Type"] = "ESP-NOW light";
json["MCU"] = "ESP8266";
@ -320,15 +317,11 @@ void sendAttributesMessage()
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,12 +330,11 @@ void sendKeepAliveMessage()
return;
keepAliveMessageTimerSemaphore = false;
esp_now_payload_data_t outgoingData{ENDT_LED, 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()
@ -350,6 +342,7 @@ void sendConfigMessage()
if (!isGatewayAvailable)
return;
esp_now_payload_data_t outgoingData{ENDT_LED, ENPT_CONFIG};
espnow_message_t message;
StaticJsonDocument<sizeof(esp_now_payload_data_t::message)> json;
json["name"] = deviceName;
json["unit"] = 1;
@ -357,15 +350,11 @@ void sendConfigMessage()
json["class"] = ledType;
json["payload_on"] = payloadOn;
json["payload_off"] = payloadOff;
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);
configMessageResendTimerSemaphore = true;
configMessageResendTimer.once(1, sendConfigMessage);
espnowMessage.push_back(message);
}
void sendStatusMessage()
@ -374,28 +363,25 @@ void sendStatusMessage()
return;
statusMessageTimerSemaphore = false;
esp_now_payload_data_t outgoingData{ENDT_LED, ENPT_STATE};
espnow_message_t message;
StaticJsonDocument<sizeof(esp_now_payload_data_t::message)> json;
json["state"] = ledStatus ? payloadOn : payloadOff;
json["brightness"] = brightness;
json["temperature"] = temperature;
json["rgb"] = String(red) + "," + String(green) + "," + String(blue);
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);
statusMessageResendTimerSemaphore = true;
statusMessageResendTimer.once(1, sendStatusMessage);
espnowMessage.push_back(message);
}
String getValue(String data, char separator, byte index)
String getValue(String data, char separator, uint8_t index)
{
byte found{0};
uint8_t found{0};
int strIndex[]{0, -1};
int maxIndex = data.length() - 1;
for (byte i{0}; i <= maxIndex && found <= index; i++)
for (uint8_t i{0}; i <= maxIndex && found <= index; i++)
if (data.charAt(i) == separator || i == maxIndex)
{
found++;
@ -471,7 +457,8 @@ void changeLedState(void)
void gatewayAvailabilityCheckTimerCallback()
{
isGatewayAvailable = false;
memset(gatewayMAC, 0, 6);
memset(&gatewayMAC, 0, 6);
espnowMessage.clear();
}
void apModeHideTimerCallback()