From 2e6ce0481c2d6263e6c7b6b0abb88cc3b1d17698 Mon Sep 17 00:00:00 2001 From: Blaz Kristan Date: Sat, 30 Apr 2022 12:45:38 +0200 Subject: [PATCH] Allow swapping of W channel for RGBW LEDs. Backporting fix. --- wled00/FX.h | 1 - wled00/bus_manager.h | 8 +- wled00/bus_wrapper.h | 29 +- wled00/cfg.cpp | 5 +- wled00/data/settings_leds.htm | 12 +- wled00/html_settings.h | 879 +++++++++++++++++----------------- wled00/set.cpp | 33 +- wled00/wled.cpp | 3 +- wled00/wled.h | 2 +- wled00/wled_server.cpp | 8 +- wled00/xml.cpp | 4 +- 11 files changed, 504 insertions(+), 480 deletions(-) diff --git a/wled00/FX.h b/wled00/FX.h index a2b393d0..1cfa366b 100644 --- a/wled00/FX.h +++ b/wled00/FX.h @@ -663,7 +663,6 @@ class WS2812FX { paletteFade = 0, paletteBlend = 0, milliampsPerLed = 55, - autoWhiteMode = RGBW_MODE_DUAL, cctBlending = 0, getBrightness(void), getModeCount(void), diff --git a/wled00/bus_manager.h b/wled00/bus_manager.h index 58fc6979..4a137325 100644 --- a/wled00/bus_manager.h +++ b/wled00/bus_manager.h @@ -116,10 +116,11 @@ struct ColorOrderMap { inline uint8_t IRAM_ATTR getPixelColorOrder(uint16_t pix, uint8_t defaultColorOrder) const { if (_count == 0) return defaultColorOrder; - + // upper nibble containd W swap information + uint8_t swapW = defaultColorOrder >> 4; for (uint8_t i = 0; i < _count; i++) { if (pix >= _mappings[i].start && pix < (_mappings[i].start + _mappings[i].len)) { - return _mappings[i].colorOrder; + return _mappings[i].colorOrder | (swapW << 4); } } return defaultColorOrder; @@ -303,7 +304,8 @@ class BusDigital : public Bus { } void setColorOrder(uint8_t colorOrder) { - if (colorOrder > 5) return; + // upper nibble contains W swap information + if ((colorOrder & 0x0F) > 5) return; _colorOrder = colorOrder; } diff --git a/wled00/bus_wrapper.h b/wled00/bus_wrapper.h index 2ea04475..6a343442 100644 --- a/wled00/bus_wrapper.h +++ b/wled00/bus_wrapper.h @@ -434,17 +434,22 @@ class PolyBus { uint8_t w = c >> 24; RgbwColor col; - //reorder channels to selected order - switch (co) - { - case 0: col.G = g; col.R = r; col.B = b; break; //0 = GRB, default + // reorder channels to selected order + switch (co & 0x0F) { + default: col.G = g; col.R = r; col.B = b; break; //0 = GRB, default case 1: col.G = r; col.R = g; col.B = b; break; //1 = RGB, common for WS2811 case 2: col.G = b; col.R = r; col.B = g; break; //2 = BRG case 3: col.G = r; col.R = b; col.B = g; break; //3 = RBG case 4: col.G = b; col.R = g; col.B = r; break; //4 = BGR - default: col.G = g; col.R = b; col.B = r; break; //5 = GBR + case 5: col.G = g; col.R = b; col.B = r; break; //5 = GBR + } + // upper nibble contains W swap information + switch (co >> 4) { + default: col.W = w; break; // no swapping + case 1: col.W = col.B; col.B = w; break; // swap W & B + case 2: col.W = col.G; col.G = w; break; // swap W & G + case 3: col.W = col.R; col.R = w; break; // swap W & R } - col.W = w; switch (busType) { case I_NONE: break; @@ -629,10 +634,16 @@ class PolyBus { case I_SS_P98_3: col = (static_cast(busPtr))->GetPixelColor(pix); break; } - switch (co) - { + // upper nibble contains W swap information + uint8_t w = col.W; + switch (co >> 4) { + case 1: col.W = col.B; col.B = w; break; // swap W & B + case 2: col.W = col.G; col.G = w; break; // swap W & G + case 3: col.W = col.R; col.R = w; break; // swap W & R + } + switch (co & 0x0F) { // W G R B - case 0: return ((col.W << 24) | (col.G << 8) | (col.R << 16) | (col.B)); //0 = GRB, default + default: return ((col.W << 24) | (col.G << 8) | (col.R << 16) | (col.B)); //0 = GRB, default case 1: return ((col.W << 24) | (col.R << 8) | (col.G << 16) | (col.B)); //1 = RGB, common for WS2811 case 2: return ((col.W << 24) | (col.B << 8) | (col.R << 16) | (col.G)); //2 = BRG case 3: return ((col.W << 24) | (col.B << 8) | (col.G << 16) | (col.R)); //3 = RBG diff --git a/wled00/cfg.cpp b/wled00/cfg.cpp index 6f043b5e..280b7046 100644 --- a/wled00/cfg.cpp +++ b/wled00/cfg.cpp @@ -84,8 +84,7 @@ bool deserializeConfig(JsonObject doc, bool fromFS) { uint8_t autoWhiteMode = RGBW_MODE_MANUAL_ONLY; CJSON(strip.ablMilliampsMax, hw_led[F("maxpwr")]); CJSON(strip.milliampsPerLed, hw_led[F("ledma")]); - CJSON(strip.autoWhiteMode, hw_led[F("rgbwm")]); // global override - Bus::setAutoWhiteMode(strip.autoWhiteMode); + Bus::setAutoWhiteMode(hw_led[F("rgbwm")] | 255); CJSON(correctWB, hw_led["cct"]); CJSON(cctFromRgb, hw_led[F("cr")]); CJSON(strip.cctBlending, hw_led[F("cb")]); @@ -578,11 +577,11 @@ void serializeConfig() { hw_led[F("total")] = strip.getLengthTotal(); //no longer read, but provided for compatibility on downgrade hw_led[F("maxpwr")] = strip.ablMilliampsMax; hw_led[F("ledma")] = strip.milliampsPerLed; - hw_led[F("rgbwm")] = strip.autoWhiteMode; // global override hw_led["cct"] = correctWB; hw_led[F("cr")] = cctFromRgb; hw_led[F("cb")] = strip.cctBlending; hw_led["fps"] = strip.getTargetFps(); + hw_led[F("rgbwm")] = Bus::getAutoWhiteMode(); // global override JsonArray hw_led_ins = hw_led.createNestedArray("ins"); diff --git a/wled00/data/settings_leds.htm b/wled00/data/settings_leds.htm index 7541b878..29dc6292 100644 --- a/wled00/data/settings_leds.htm +++ b/wled00/data/settings_leds.htm @@ -160,6 +160,8 @@ gId("rf"+n).onclick = (t == 31) ? (function(){return false}) : (function(){}); // prevent change for TM1814 isRGBW |= (t == 30 || t == 31 || (t > 40 && t < 46 && t != 43)); // RGBW checkbox, TYPE_xxxx values from const.h gId("co"+n).style.display = ((t>=80 && t<96) || (t > 40 && t < 48)) ? "none":"inline"; // hide color order for PWM + gId("dig"+n+"w").style.display = (t == 30 || t == 31) ? "inline":"none"; // show swap channels dropdown + if (!(t == 30 || t == 31)) d.getElementsByName("WO"+n)[0].value = 0; // reset swapping gId("dig"+n+"c").style.display = (t > 40 && t < 48) ? "none":"inline"; // hide count for analog gId("dig"+n+"r").style.display = (t>=80 && t<96) ? "none":"inline"; // hide reversed for virtual gId("dig"+n+"s").style.display = ((t>=80 && t<96) || (t > 40 && t < 48)) ? "none":"inline"; // hide skip 1st for virtual & analog @@ -317,10 +319,12 @@ ${i+1}: -
+ + +
Start:   -
Length:
-
+
Length:

+
GPIO: @@ -420,7 +424,7 @@ Length: 8) return; - - if (correctPIN) lastEditTime = millis(); + if (subPage <1 || subPage >8 || !correctPIN) return; //WIFI SETTINGS if (subPage == 1) @@ -76,7 +74,7 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage) } } - uint8_t colorOrder, type, skip, awmode; + uint8_t colorOrder, type, skip, awmode, channelSwap; uint16_t length, start; uint8_t pins[5] = {255, 255, 255, 255, 255}; @@ -85,8 +83,7 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage) cctFromRgb = request->hasArg(F("CR")); strip.cctBlending = request->arg(F("CB")).toInt(); Bus::setCCTBlend(strip.cctBlending); - strip.autoWhiteMode = (request->arg(F("AW")).toInt()); - Bus::setAutoWhiteMode(strip.autoWhiteMode); + Bus::setAutoWhiteMode(request->arg(F("AW")).toInt()); strip.setTargetFps(request->arg(F("FR")).toInt()); for (uint8_t s = 0; s < WLED_MAX_BUSSES; s++) { @@ -99,6 +96,7 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage) char sl[4] = "SL"; sl[2] = 48+s; sl[3] = 0; //skip first N LEDs char rf[4] = "RF"; rf[2] = 48+s; rf[3] = 0; //refresh required char aw[4] = "AW"; aw[2] = 48+s; aw[3] = 0; //auto white mode + char wo[4] = "WO"; wo[2] = 48+s; wo[3] = 0; //channel swap if (!request->hasArg(lp)) { DEBUG_PRINTLN(F("No data.")); break; } @@ -118,9 +116,10 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage) break; // no parameter } awmode = request->arg(aw).toInt(); + channelSwap = (type == TYPE_SK6812_RGBW || type == TYPE_TM1814) ? request->arg(wo).toInt() : 0; // actual finalization is done in WLED::loop() (removing old busses and adding new) if (busConfigs[s] != nullptr) delete busConfigs[s]; - busConfigs[s] = new BusConfig(type, pins, start, length, colorOrder, request->hasArg(cv), skip, awmode); + busConfigs[s] = new BusConfig(type, pins, start, length, colorOrder | (channelSwap<<4), request->hasArg(cv), skip, awmode); doInitBusses = true; } @@ -400,8 +399,13 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage) if (request->hasArg(F("PIN"))) { const char *pin = request->arg(F("PIN")).c_str(); - if (strlen(pin) == 4 || strlen(pin) == 0) { - strlcpy(settingsPIN, pin, 5); + uint8_t pinLen = strlen(pin); + if (pinLen == 4 || pinLen == 0) { + uint8_t numZeros = 0; + for (uint8_t i = 0; i < pinLen; i++) numZeros += (pin[i] == '0'); + if (numZeros < pinLen || pinLen == 0) { // ignore 0000 input (placeholder) + strlcpy(settingsPIN, pin, 5); + } settingsPIN[4] = 0; } } @@ -411,21 +415,21 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage) { if (otaLock && strcmp(otaPass,request->arg(F("OP")).c_str()) == 0) { - pwdCorrect = true; + // brute force protection: do not unlock even if correct if last save was less than 3 seconds ago + if (millis() - lastEditTime > 3000) pwdCorrect = true; } if (!otaLock && request->arg(F("OP")).length() > 0) { - strlcpy(otaPass,request->arg(F("OP")).c_str(), 33); + strlcpy(otaPass,request->arg(F("OP")).c_str(), 33); // set new OTA password } } if (pwdCorrect) //allow changes if correct pwd or no ota active { - bool oldOTALock = otaLock; otaLock = request->hasArg(F("NO")); wifiLock = request->hasArg(F("OW")); aOtaEnabled = request->hasArg(F("AO")); - doReboot = (otaLock ^ oldOTALock); + //createEditHandler(correctPIN && !otaLock); } } @@ -543,7 +547,8 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage) releaseJSONBufferLock(); } - if (subPage != 2 && (subPage != 6 || !doReboot)) serializeConfig(); //do not save if factory reset or LED settings (which are saved after LED re-init) + lastEditTime = millis(); + if (subPage != 2 && !doReboot) serializeConfig(); //do not save if factory reset or LED settings (which are saved after LED re-init) if (subPage == 4) alexaInit(); } diff --git a/wled00/wled.cpp b/wled00/wled.cpp index c70c8f32..189a192b 100644 --- a/wled00/wled.cpp +++ b/wled00/wled.cpp @@ -143,8 +143,7 @@ void WLED::loop() // 15min PIN time-out if (strlen(settingsPIN)>0 && millis() - lastEditTime > 900000) { correctPIN = false; - server.removeHandler(editHandler); - createEditHandler(correctPIN); + createEditHandler(false); } //LED settings have been saved, re-init busses diff --git a/wled00/wled.h b/wled00/wled.h index bfefea76..1a40491f 100644 --- a/wled00/wled.h +++ b/wled00/wled.h @@ -8,7 +8,7 @@ */ // version code in format yymmddb (b = daily build) -#define VERSION 2204281 +#define VERSION 2204301 //uncomment this if you have a "my_config.h" file you'd like to use //#define WLED_USE_MY_CONFIG diff --git a/wled00/wled_server.cpp b/wled00/wled_server.cpp index 42245a3d..c6718d00 100644 --- a/wled00/wled_server.cpp +++ b/wled00/wled_server.cpp @@ -64,7 +64,7 @@ void createEditHandler(bool enable) { #endif } else { editHandler = &server.on("/edit", HTTP_ANY, [](AsyncWebServerRequest *request){ - serveMessage(request, 500, "Access Denied", otaLock ? FPSTR(s_unlock_ota) : F("Please enter PIN in settings!"), 254); + serveMessage(request, 500, "Access Denied", /*otaLock ? FPSTR(s_unlock_ota) :*/ F("Please enter PIN in settings!"), 254); //serveSettings(request,request->method() == HTTP_POST); // request PIN }); } @@ -255,7 +255,7 @@ void initServer() request->send(response); }); - createEditHandler(correctPIN && !otaLock); + createEditHandler(correctPIN /*&& !otaLock*/); #ifndef WLED_DISABLE_OTA //init ota page @@ -546,7 +546,7 @@ void serveSettings(AsyncWebServerRequest* request, bool post) } if (subPage == 252) { - createEditHandler(correctPIN && !otaLock); + createEditHandler(correctPIN /*&& !otaLock*/); } else strcat_P(s, PSTR(" settings saved.")); @@ -574,7 +574,7 @@ void serveSettings(AsyncWebServerRequest* request, bool post) case 9: response = request->beginResponse_P(200, "text/html", PAGE_update, PAGE_update_length); break; case 251: { correctPIN = !strlen(settingsPIN); // lock if a pin is set - createEditHandler(correctPIN && !otaLock); + createEditHandler(correctPIN /*&& !otaLock*/); serveMessage(request, 200, strlen(settingsPIN) > 0 ? PSTR("Settings locked") : PSTR("No PIN set"), FPSTR(s_redirecting), 1); return; } diff --git a/wled00/xml.cpp b/wled00/xml.cpp index 2ac30bb2..e0de9efb 100644 --- a/wled00/xml.cpp +++ b/wled00/xml.cpp @@ -333,6 +333,7 @@ void getSettingsJS(byte subPage, char* dest) char sl[4] = "SL"; sl[2] = 48+s; sl[3] = 0; //skip 1st LED char rf[4] = "RF"; rf[2] = 48+s; rf[3] = 0; //off refresh char aw[4] = "AW"; aw[2] = 48+s; aw[3] = 0; //auto white mode + char wo[4] = "WO"; wo[2] = 48+s; wo[3] = 0; //swap channels oappend(SET_F("addLEDs(1);")); uint8_t pins[5]; uint8_t nPins = bus->getPins(pins); @@ -342,12 +343,13 @@ void getSettingsJS(byte subPage, char* dest) } sappend('v',lc,bus->getLength()); sappend('v',lt,bus->getType()); - sappend('v',co,bus->getColorOrder()); + sappend('v',co,bus->getColorOrder() & 0x0F); sappend('v',ls,bus->getStart()); sappend('c',cv,bus->reversed); sappend('v',sl,bus->skippedLeds()); sappend('c',rf,bus->isOffRefreshRequired()); sappend('v',aw,bus->getAWMode()); + sappend('v',wo,bus->getColorOrder() >> 4); } sappend('v',SET_F("MA"),strip.ablMilliampsMax); sappend('v',SET_F("LA"),strip.milliampsPerLed);