diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..40433be --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +.pio +.vscode +.DS_Store \ No newline at end of file diff --git a/README.md b/README.md index 5391fe3..0940e16 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,29 @@ -# ESP-NOW-Light-Led-Strip +# Light/led strip controller for ESP8266 +ESP-NOW based light/led strip controller for ESP8266. Alternate firmware for Tuya/SmartLife WiFi light/led strip controllers. + +## 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. +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. +5. Possibility firmware update over OTA (if is allows the size of the flash memory). +6. Web interface for settings. + +## Notes + +1. ESP-NOW mesh network based on the library [ZHNetwork](https://github.com/aZholtikov/ZHNetwork). +2. Regardless of the status of connection to gateway the device perform ESP-NOW node function. +3. For show the access point for setting or firmware update, send the command "update" to the device's root topic (example - "homeassistant/espnow_light/E8DB849CA148"). Access point will be shown during 5 minutes. Similarly, for restart send the command "restart". + +## Tested on + +Coming soon. + +## 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 light/led strip controllers, the WiFi module must be replaced with an ESP8266 compatible module (if necessary). diff --git a/data/function.js b/data/function.js new file mode 100755 index 0000000..fa51261 --- /dev/null +++ b/data/function.js @@ -0,0 +1,83 @@ +var xmlHttp = createXmlHttpObject(); +function createXmlHttpObject() { + if (window.XMLHttpRequest) { + xmlHttp = new XMLHttpRequest(); + } else { + xmlHttp = new ActiveXObject('Microsoft.XMLHTTP'); + } + return xmlHttp; +} + +function load() { + if (xmlHttp.readyState == 0 || xmlHttp.readyState == 4) { + xmlHttp.open('PUT', '/config.json', true); + xmlHttp.send(null); + xmlHttp.onload = function () { + jsonResponse = JSON.parse(xmlHttp.responseText); + loadBlock(); + } + } +} + +function loadBlock() { + newData = JSON.parse(xmlHttp.responseText); + data = document.getElementsByTagName('body')[0].innerHTML; + var newString; + for (var key in newData) { + newString = data.replace(new RegExp('{{' + key + '}}', 'g'), newData[key]); + data = newString; + } + document.getElementsByTagName('body')[0].innerHTML = newString; + setFirmvareValue('version', 'firmware'); + setGpioValue('ledTypeSelect', 'ledType'); + setGpioValue('coldWhitePinSelect', 'coldWhitePin'); + setGpioValue('warmWhitePinSelect', 'warmWhitePin'); + setGpioValue('redPinSelect', 'redPin'); + setGpioValue('greenPinSelect', 'greenPin'); + setGpioValue('bluePinSelect', 'bluePin'); + handleServerResponse(); +} + +function getValue(id) { + var value = document.getElementById(id).value; + return value; +} + +function getSelectValue(id) { + var select = document.getElementById(id); + var value = select.value; + return value; +} + +function sendRequest(submit, server) { + request = new XMLHttpRequest(); + request.open("GET", server, true); + request.send(); +} + +function saveSetting(submit) { + server = "/setting?deviceName=" + getValue('deviceName') + + "&espnowNetName=" + getValue('espnowNetName') + + "&ledType=" + getSelectValue('ledTypeSelect') + + "&coldWhitePin=" + getSelectValue('coldWhitePinSelect') + + "&warmWhitePin=" + getSelectValue('warmWhitePinSelect') + + "&redPin=" + getSelectValue('redPinSelect') + + "&greenPin=" + getSelectValue('greenPinSelect') + + "&bluePin=" + getSelectValue('bluePinSelect'); + sendRequest(submit, server); + alert("Please restart device for changes apply."); +} + +function restart(submit) { + server = "/restart"; + sendRequest(submit, server); +} + +function setFirmvareValue(id, value) { + document.getElementById(id).innerHTML = document.getElementById(value).value; +} + +function setGpioValue(id, value) { + var select = document.getElementById(id); + select.value = document.getElementById(value).value; +} diff --git a/data/index.htm b/data/index.htm new file mode 100644 index 0000000..14970d6 --- /dev/null +++ b/data/index.htm @@ -0,0 +1,172 @@ + + + + + + + + ESP-NOW Led/Light Strip + + + +
+

ESP-NOW Led/Light Strip

+
+

Firmware:

+

+ +
+ +
+

Device name:

+ +
+ +
+

ESP-NOW network name:

+ +
+ +
+

Device type:

+ +

+
+ +
+

Cold white GPIO:

+ +

+
+ +
+

Warm white GPIO:

+ +

+
+ +
+

Red GPIO:

+ +

+
+ +
+

Green GPIO:

+ +

+
+ +
+

Blue GPIO:

+ +

+
+ +
+ + +
+
+ + + \ No newline at end of file diff --git a/data/style.css b/data/style.css new file mode 100644 index 0000000..7b13ef7 --- /dev/null +++ b/data/style.css @@ -0,0 +1,94 @@ +body { + font-family: "Gill Sans", sans-serif; + background: rgb(255, 255, 255); +} + +.box { + width: 400px; + padding: 20px 20px; + margin: 20px auto; + background: #e0f5fb; + box-shadow: 4px 4px 30px rgba(0, 0, 0, 0.2); + border-radius: 10px; +} + +h1 { + color: rgb(65, 125, 238); + text-align: center; +} + +.text { + font-weight: 600; + flex-shrink: 0; + margin-right: 10px; +} + +.text-select { + width: 35%; + font-weight: 600; + flex-shrink: 0; + margin-right: 10px; +} + +.wrapper { + display: flex; + justify-content: space-between; + align-items: baseline; +} + +input { + width: 48%; + min-height: 30px; + border-radius: 5px; + border: none; + margin-bottom: 10px; + padding: 0 10px; + color: rgb(0, 0, 0); + background: #a3e0f1; + transition: .5s; +} + +select { + width: 110px; + min-height: 30px; + border-radius: 5px; + border: none; + margin-bottom: 10px; + padding: 0 10px; + color: rgb(0, 0, 0); + background: #a3e0f1; + transition: .5s; +} + +input:hover { + background: white; + cursor: pointer; +} + +select:hover { + background: white; + cursor: pointer; +} + + +.btn { + width: 48%; + background: rgb(65, 125, 238); + color: white; + transition: .5s; +} + +.btn:hover { + background: rgb(65, 125, 238); + opacity: 0.5; + transform: translatey(-3px); +} + +#deviceName, +#espnowNetName { + width: 100%; +} + +.wrapper.wrapper--end { + align-items: baseline; +} \ No newline at end of file diff --git a/platformio.ini b/platformio.ini new file mode 100644 index 0000000..628f7eb --- /dev/null +++ b/platformio.ini @@ -0,0 +1,43 @@ +[env:esp8266] +platform = espressif8266 +board = esp12e +framework = arduino +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 + +[env:esp8266-ota] +platform = espressif8266 +board = esp12e +framework = arduino +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 + +[env:esp8285] +platform = espressif8266 +board = esp8285 +framework = arduino +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 + +[env:esp8285-ota] +platform = espressif8266 +board = esp8285 +framework = arduino +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 diff --git a/src/main.cpp b/src/main.cpp new file mode 100644 index 0000000..f2913d9 --- /dev/null +++ b/src/main.cpp @@ -0,0 +1,489 @@ +#include "ArduinoJson.h" +#include "ArduinoOTA.h" +#include "ESPAsyncWebServer.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 loadConfig(void); +void saveConfig(void); +void setupWebServer(void); + +void sendAttributesMessage(void); +void sendKeepAliveMessage(void); +void sendConfigMessage(void); +void sendStatusMessage(void); + +String getValue(String data, char separator, byte index); + +void changeLedState(void); + +const String firmware{"1.0"}; + +String espnowNetName{"DEFAULT"}; + +String deviceName{"ESP-NOW Light/Led Strip"}; + +uint8_t ledType{ENLT_NONE}; +bool ledStatus{false}; +uint8_t coldWhitePin{0}; +uint8_t warmWhitePin{0}; +uint8_t redPin{0}; +uint8_t greenPin{0}; +uint8_t bluePin{0}; + +uint8_t brightness{255}; +uint16_t temperature{255}; +uint8_t red{255}; +uint8_t green{255}; +uint8_t blue{255}; + +bool wasMqttAvailable{false}; + +uint8_t gatewayMAC[6]{0}; + +ZHNetwork myNet; +AsyncWebServer webServer(80); + +Ticker gatewayAvailabilityCheckTimer; +bool isGatewayAvailable{false}; +void gatewayAvailabilityCheckTimerCallback(void); + +Ticker apModeHideTimer; +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(); + + loadConfig(); + + if (coldWhitePin) + pinMode(coldWhitePin, OUTPUT); + if (warmWhitePin) + pinMode(warmWhitePin, OUTPUT); + if (redPin) + pinMode(redPin, OUTPUT); + if (greenPin) + pinMode(greenPin, OUTPUT); + if (bluePin) + pinMode(bluePin, OUTPUT); + + changeLedState(); + + WiFi.setSleepMode(WIFI_NONE_SLEEP); + myNet.begin(espnowNetName.c_str()); + + myNet.setOnBroadcastReceivingCallback(onBroadcastReceiving); + myNet.setOnUnicastReceivingCallback(onUnicastReceiving); + myNet.setOnConfirmReceivingCallback(onConfirmReceiving); + + WiFi.mode(WIFI_AP_STA); + WiFi.softAP(("ESP-NOW Light " + myNet.getNodeMac()).c_str(), "12345678", 1, 0); + apModeHideTimer.once(300, apModeHideTimerCallback); + + setupWebServer(); + + ArduinoOTA.begin(); + + attributesMessageTimer.attach(60, attributesMessageTimerCallback); + keepAliveMessageTimer.attach(10, keepAliveMessageTimerCallback); + statusMessageTimer.attach(300, statusMessageTimerCallback); +} + +void loop() +{ + if (attributesMessageTimerSemaphore) + sendAttributesMessage(); + if (keepAliveMessageTimerSemaphore) + sendKeepAliveMessage(); + if (statusMessageTimerSemaphore) + sendStatusMessage(); + myNet.maintenance(); + ArduinoOTA.handle(); +} + +void onBroadcastReceiving(const char *data, const byte *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); + if (myNet.macToString(gatewayMAC) == myNet.macToString(sender) && incomingData.payloadsType == ENPT_KEEP_ALIVE) + { + isGatewayAvailable = true; + StaticJsonDocument json; + deserializeJson(json, incomingData.message); + bool temp = json["MQTT"] == "online" ? true : false; + if (wasMqttAvailable != temp) + { + wasMqttAvailable = temp; + if (temp) + sendConfigMessage(); + } + gatewayAvailabilityCheckTimer.once(15, gatewayAvailabilityCheckTimerCallback); + } +} + +void onUnicastReceiving(const char *data, const byte *sender) +{ + esp_now_payload_data_t incomingData; + memcpy(&incomingData, data, sizeof(esp_now_payload_data_t)); + if (incomingData.deviceType != ENDT_GATEWAY || myNet.macToString(gatewayMAC) != myNet.macToString(sender)) + return; + StaticJsonDocument json; + if (incomingData.payloadsType == ENPT_SET) + { + deserializeJson(json, incomingData.message); + if (json["set"]) + ledStatus = json["set"] == "ON" ? true : false; + if (json["brightness"]) + brightness = json["brightness"]; + if (json["temperature"]) + temperature = json["temperature"]; + if (json["rgb"]) + { + red = getValue(String(json["rgb"].as()).substring(0, sizeof(esp_now_payload_data_t::message)).c_str(), ',', 0).toInt(); + green = getValue(String(json["rgb"].as()).substring(0, sizeof(esp_now_payload_data_t::message)).c_str(), ',', 1).toInt(); + blue = getValue(String(json["rgb"].as()).substring(0, sizeof(esp_now_payload_data_t::message)).c_str(), ',', 2).toInt(); + } + changeLedState(); + sendStatusMessage(); + } + if (incomingData.payloadsType == ENPT_UPDATE) + { + WiFi.softAP(("ESP-NOW Light " + myNet.getNodeMac()).c_str(), "12345678", 1, 0); + apModeHideTimer.once(300, apModeHideTimerCallback); + } + if (incomingData.payloadsType == ENPT_RESTART) + ESP.restart(); +} + +void onConfirmReceiving(const uint8_t *target, const bool status) +{ + if (status) + { + if (attributesMessageResendTimerSemaphore) + { + attributesMessageResendTimerSemaphore = false; + attributesMessageResendTimer.detach(); + } + 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")) + saveConfig(); + File file = SPIFFS.open("/config.json", "r"); + String jsonFile = file.readString(); + StaticJsonDocument<512> json; + deserializeJson(json, jsonFile); + espnowNetName = json["espnowNetName"].as(); + deviceName = json["deviceName"].as(); + ledType = json["ledType"]; + ledStatus = json["ledStatus"]; + coldWhitePin = json["coldWhitePin"]; + warmWhitePin = json["warmWhitePin"]; + redPin = json["redPin"]; + greenPin = json["greenPin"]; + bluePin = json["bluePin"]; + brightness = json["brightness"]; + temperature = json["temperature"]; + red = json["red"]; + green = json["green"]; + blue = json["blue"]; + file.close(); +} + +void saveConfig() +{ + StaticJsonDocument<512> json; + json["firmware"] = firmware; + json["espnowNetName"] = espnowNetName; + json["deviceName"] = deviceName; + json["ledType"] = ledType; + json["ledStatus"] = ledStatus; + json["coldWhitePin"] = coldWhitePin; + json["warmWhitePin"] = warmWhitePin; + json["redPin"] = redPin; + json["greenPin"] = greenPin; + json["bluePin"] = bluePin; + json["brightness"] = brightness; + json["temperature"] = temperature; + json["red"] = red; + json["green"] = green; + json["blue"] = blue; + json["system"] = "empty"; + File file = SPIFFS.open("/config.json", "w"); + serializeJsonPretty(json, file); + file.close(); +} + +void setupWebServer() +{ + webServer.on("/", HTTP_GET, [](AsyncWebServerRequest *request) + { request->send(SPIFFS, "/index.htm"); }); + + webServer.on("/setting", HTTP_GET, [](AsyncWebServerRequest *request) + { + ledType = request->getParam("ledType")->value().toInt(); + coldWhitePin = request->getParam("coldWhitePin")->value().toInt(); + warmWhitePin = request->getParam("warmWhitePin")->value().toInt(); + redPin = request->getParam("redPin")->value().toInt(); + greenPin = request->getParam("greenPin")->value().toInt(); + bluePin = request->getParam("bluePin")->value().toInt(); + deviceName = request->getParam("deviceName")->value(); + espnowNetName = request->getParam("espnowNetName")->value(); + request->send(200); + saveConfig(); }); + + 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"); + } }); + + webServer.begin(); +} + +void sendAttributesMessage() +{ + if (!isGatewayAvailable) + return; + attributesMessageTimerSemaphore = false; + uint32_t secs = millis() / 1000; + uint32_t mins = secs / 60; + uint32_t hours = mins / 60; + uint32_t days = hours / 24; + esp_now_payload_data_t outgoingData{ENDT_LED, ENPT_ATTRIBUTES}; + StaticJsonDocument json; + json["Type"] = "ESP-NOW Led/Light Strip"; + 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); + + attributesMessageResendTimerSemaphore = true; + attributesMessageResendTimer.once(1, sendAttributesMessage); +} + +void sendKeepAliveMessage() +{ + if (!isGatewayAvailable) + 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); + + keepAliveMessageResendTimerSemaphore = true; + keepAliveMessageResendTimer.once(1, sendKeepAliveMessage); +} + +void sendConfigMessage() +{ + if (!isGatewayAvailable) + return; + esp_now_payload_data_t outgoingData{ENDT_LED, ENPT_CONFIG}; + StaticJsonDocument json; + json["name"] = deviceName; + json["unit"] = 1; + json["type"] = getValueName(HACT_LIGHT); + json["class"] = ledType; + 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); + + configMessageResendTimerSemaphore = true; + configMessageResendTimer.once(5, sendConfigMessage); +} + +void sendStatusMessage() +{ + if (!isGatewayAvailable) + return; + statusMessageTimerSemaphore = false; + esp_now_payload_data_t outgoingData{ENDT_LED, ENPT_STATE}; + StaticJsonDocument json; + json["state"] = ledStatus ? "ON" : "OFF"; + 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); + + statusMessageResendTimerSemaphore = true; + statusMessageResendTimer.once(1, sendStatusMessage); +} + +String getValue(String data, char separator, byte index) +{ + byte found{0}; + int strIndex[]{0, -1}; + int maxIndex = data.length() - 1; + for (byte i{0}; i <= maxIndex && found <= index; i++) + if (data.charAt(i) == separator || i == maxIndex) + { + found++; + strIndex[0] = strIndex[1] + 1; + strIndex[1] = (i == maxIndex) ? i + 1 : i; + } + return found > index ? data.substring(strIndex[0], strIndex[1]) : ""; +} + +void changeLedState(void) +{ + if (ledStatus) + { + if (red == 255 && green == 255 && blue == 255) + { + if (ledType == ENLT_W || ledType == ENLT_RGBW) + analogWrite(coldWhitePin, brightness); + if (ledType == ENLT_WW || ledType == ENLT_RGBWW) + { + analogWrite(coldWhitePin, map(brightness, 0, 255, 0, map(temperature, 500, 153, 0, 255))); + analogWrite(warmWhitePin, map(brightness, 0, 255, 0, map(temperature, 153, 500, 0, 255))); + } + if (ledType == ENLT_RGB) + { + analogWrite(redPin, map(red, 0, 255, 0, brightness)); + analogWrite(greenPin, map(green, 0, 255, 0, brightness)); + analogWrite(bluePin, map(blue, 0, 255, 0, brightness)); + } + if (ledType == ENLT_RGBW || ledType == ENLT_RGBWW) + { + digitalWrite(redPin, LOW); + digitalWrite(greenPin, LOW); + digitalWrite(bluePin, LOW); + } + } + else + { + if (ledType == ENLT_W) + analogWrite(coldWhitePin, brightness); + if (ledType == ENLT_WW) + { + analogWrite(coldWhitePin, map(brightness, 0, 255, 0, map(temperature, 500, 153, 0, 255))); + analogWrite(warmWhitePin, map(brightness, 0, 255, 0, map(temperature, 153, 500, 0, 255))); + } + if (ledType == ENLT_RGBW || ledType == ENLT_RGBWW) + digitalWrite(coldWhitePin, LOW); + if (ledType == ENLT_RGBWW) + digitalWrite(warmWhitePin, LOW); + if (ledType == ENLT_RGB || ledType == ENLT_RGBW || ledType == ENLT_RGBWW) + { + analogWrite(redPin, map(red, 0, 255, 0, brightness)); + analogWrite(greenPin, map(green, 0, 255, 0, brightness)); + analogWrite(bluePin, map(blue, 0, 255, 0, brightness)); + } + } + } + else + { + if (ledType == ENLT_W || ledType == ENLT_WW || ledType == ENLT_RGBW || ledType == ENLT_RGBWW) + digitalWrite(coldWhitePin, LOW); + if (ledType == ENLT_WW || ledType == ENLT_RGBWW) + digitalWrite(warmWhitePin, LOW); + if (ledType == ENLT_RGB || ledType == ENLT_RGBW || ledType == ENLT_RGBWW) + { + digitalWrite(redPin, LOW); + digitalWrite(greenPin, LOW); + digitalWrite(bluePin, LOW); + } + } + saveConfig(); +} + +void gatewayAvailabilityCheckTimerCallback() +{ + isGatewayAvailable = false; + memset(gatewayMAC, 0, 6); +} + +void apModeHideTimerCallback() +{ + WiFi.softAP(("ESP-NOW Light " + myNet.getNodeMac()).c_str(), "12345678", 1, 1); +} + +void attributesMessageTimerCallback() +{ + attributesMessageTimerSemaphore = true; +} + +void keepAliveMessageTimerCallback() +{ + keepAliveMessageTimerSemaphore = true; +} + +void statusMessageTimerCallback() +{ + statusMessageTimerSemaphore = true; +} \ No newline at end of file