From 7eb029dcb607d1f2d87ee6625665dee579eedd16 Mon Sep 17 00:00:00 2001 From: Blaz Kristan Date: Sat, 15 Jan 2022 14:04:16 +0100 Subject: [PATCH] Complete segment syncing. Reduced complexity of colorUpdated regarding effect/color change. Minor optimizations. --- wled00/FX_fcn.cpp | 2 +- wled00/colors.cpp | 9 +-- wled00/fcn_declare.h | 5 +- wled00/json.cpp | 130 ++++++++++++++++++++---------------------- wled00/led.cpp | 101 +++++++++++--------------------- wled00/set.cpp | 133 ++++++++++++++++++++----------------------- wled00/udp.cpp | 46 +++++++-------- wled00/wled.h | 5 +- 8 files changed, 188 insertions(+), 243 deletions(-) diff --git a/wled00/FX_fcn.cpp b/wled00/FX_fcn.cpp index 20ff12b8..e8aaba26 100644 --- a/wled00/FX_fcn.cpp +++ b/wled00/FX_fcn.cpp @@ -607,7 +607,7 @@ void WS2812FX::setSegment(uint8_t n, uint16_t i1, uint16_t i2, uint8_t grouping, } void WS2812FX::resetSegments() { - for (uint8_t i = 0; i < MAX_NUM_SEGMENTS; i++) if (_segments[i].name) delete _segments[i].name; + for (uint8_t i = 0; i < MAX_NUM_SEGMENTS; i++) if (_segments[i].name) delete[] _segments[i].name; mainSegment = 0; memset(_segments, 0, sizeof(_segments)); //memset(_segment_runtimes, 0, sizeof(_segment_runtimes)); diff --git a/wled00/colors.cpp b/wled00/colors.cpp index 424c9916..0655060e 100644 --- a/wled00/colors.cpp +++ b/wled00/colors.cpp @@ -22,11 +22,6 @@ void colorFromUint24(uint32_t in, bool secondary) _col[2] = B(in); } -//store color components in uint32_t -uint32_t colorFromRgbw(byte* rgbw) { - return RGBW32(rgbw[0], rgbw[1], rgbw[2], rgbw[3]); -} - //relatively change white brightness, minumum A=5 void relativeChangeWhite(int8_t amount, byte lowerBoundary) { @@ -259,7 +254,7 @@ uint32_t colorBalanceFromKelvin(uint16_t kelvin, uint32_t rgb) rgbw[1] = ((uint16_t) correctionRGB[1] * G(rgb)) /255; // correct G rgbw[2] = ((uint16_t) correctionRGB[2] * B(rgb)) /255; // correct B rgbw[3] = W(rgb); - return colorFromRgbw(rgbw); + return RGBW32(rgbw[0],rgbw[1],rgbw[2],rgbw[3]); } //approximates a Kelvin color temperature from an RGB color. @@ -299,4 +294,4 @@ uint16_t approximateKelvinFromRGB(uint32_t rgb) { uint16_t k = 8080 + (225-r) *86; return (k > 10091) ? 10091 : k; } -} \ No newline at end of file +} diff --git a/wled00/fcn_declare.h b/wled00/fcn_declare.h index af9e4a53..a139cb2f 100644 --- a/wled00/fcn_declare.h +++ b/wled00/fcn_declare.h @@ -15,9 +15,11 @@ void handleAlexa(); void onAlexaChange(EspalexaDevice* dev); //blynk.cpp +#ifndef WLED_DISABLE_BLYNK void initBlynk(const char* auth, const char* host, uint16_t port); void handleBlynk(); void updateBlynk(); +#endif //button.cpp void shortPressAction(uint8_t b=0); @@ -56,7 +58,7 @@ bool getJsonValue(const JsonVariant& element, DestType& destination, const Defau //colors.cpp void colorFromUint32(uint32_t in, bool secondary = false); void colorFromUint24(uint32_t in, bool secondary = false); -uint32_t colorFromRgbw(byte* rgbw); +inline uint32_t colorFromRgbw(byte* rgbw) { return uint32_t((byte(rgbw[3]) << 24) | (byte(rgbw[0]) << 16) | (byte(rgbw[1]) << 8) | (byte(rgbw[2]))); } void relativeChangeWhite(int8_t amount, byte lowerBoundary = 0); void colorHStoRGB(uint16_t hue, byte sat, byte* rgb); //hue, sat to rgb void colorKtoRGB(uint16_t kelvin, byte* rgb); @@ -141,7 +143,6 @@ void resetTimebase(); void toggleOnOff(); void setAllLeds(); void setLedsStandard(); -bool colorChanged(); void colorUpdated(int callMode); void updateInterfaces(uint8_t callMode); void handleTransitions(); diff --git a/wled00/json.cpp b/wled00/json.cpp index ae52de29..a9cf7585 100644 --- a/wled00/json.cpp +++ b/wled00/json.cpp @@ -146,14 +146,11 @@ void deserializeSegment(JsonObject elem, byte it, byte presetId) } if (!colValid) continue; - if (id == strip.getMainSegmentId() && i < 2) //temporary, to make transition work on main segment - { - if (i == 0) {col[0] = rgbw[0]; col[1] = rgbw[1]; col[2] = rgbw[2]; col[3] = rgbw[3];} - if (i == 1) {colSec[0] = rgbw[0]; colSec[1] = rgbw[1]; colSec[2] = rgbw[2]; colSec[3] = rgbw[3];} - } else { //normal case, apply directly to segment - seg.setColor(i, ((rgbw[3] << 24) | ((rgbw[0]&0xFF) << 16) | ((rgbw[1]&0xFF) << 8) | ((rgbw[2]&0xFF))), id); - if (seg.mode == FX_MODE_STATIC) strip.trigger(); //instant refresh - } + + uint32_t color = RGBW32(rgbw[0],rgbw[1],rgbw[2],rgbw[3]); + colorChanged |= (seg.colors[i] != color); + seg.setColor(i, color, id); + if (seg.mode == FX_MODE_STATIC) strip.trigger(); //instant refresh } } @@ -176,26 +173,18 @@ void deserializeSegment(JsonObject elem, byte it, byte presetId) if (!(elem[F("sel")].isNull() && elem["rev"].isNull() && elem["on"].isNull() && elem[F("mi")].isNull())) effectChanged = true; //send UDP - //temporary, strip object gets updated via colorUpdated() - if (id == strip.getMainSegmentId()) { - byte effectPrev = effectCurrent; - if (getVal(elem["fx"], &effectCurrent, 1, strip.getModeCount())) { //load effect ('r' random (1-255), '~' inc/dec (1-255), 0-255 exact value) - if (!presetId && effectCurrent != effectPrev) unloadPlaylist(); //stop playlist if active and FX changed manually - } - effectSpeed = elem[F("sx")] | effectSpeed; - effectIntensity = elem[F("ix")] | effectIntensity; - getVal(elem["pal"], &effectPalette, 1, strip.getPaletteCount()); - } else { //permanent - byte fx = seg.mode; - byte fxPrev = fx; - if (getVal(elem["fx"], &fx, 1, strip.getModeCount())) { //load effect ('r' random (1-255), '~' inc/dec (1-255), 0-255 exact value) - strip.setMode(id, fx); - if (!presetId && seg.mode != fxPrev) unloadPlaylist(); //stop playlist if active and FX changed manually - } - seg.speed = elem[F("sx")] | seg.speed; - seg.intensity = elem[F("ix")] | seg.intensity; - getVal(elem["pal"], &seg.palette, 1, strip.getPaletteCount()); + byte fx = seg.mode; + byte fxPrev = fx; + if (getVal(elem["fx"], &fx, 1, strip.getModeCount())) { //load effect ('r' random, '~' inc/dec, 1-255 exact value) + strip.setMode(id, fx); + if (!presetId && seg.mode != fxPrev) effectChanged = true; //send UDP } + byte prevSpd = seg.speed; + byte prevInt = seg.intensity; + byte prevPal = seg.palette; + if (getVal(elem[F("sx")], &seg.speed, 0, 255) && !presetId && prevSpd != seg.speed) effectChanged = true; //also supports inc/decrementing and random + if (getVal(elem[F("ix")], &seg.intensity, 0, 255) && !presetId && prevInt != seg.intensity) effectChanged = true; //also supports inc/decrementing and random + if (getVal(elem["pal"], &seg.palette, 1, strip.getPaletteCount()) && !presetId && prevPal != seg.palette) effectChanged = true; //also supports inc/decrementing and random JsonArray iarr = elem[F("i")]; //set individual LEDs if (!iarr.isNull()) { @@ -289,16 +278,16 @@ bool deserializeState(JsonObject root, byte callMode, byte presetId) tr = root[F("tb")] | -1; if (tr >= 0) strip.timebase = ((uint32_t)tr) - millis(); - JsonObject nl = root["nl"]; + JsonObject nl = root["nl"]; nightlightActive = nl["on"] | nightlightActive; nightlightDelayMins = nl[F("dur")] | nightlightDelayMins; nightlightMode = nl[F("mode")] | nightlightMode; nightlightTargetBri = nl[F("tbri")] | nightlightTargetBri; - JsonObject udpn = root["udpn"]; + JsonObject udpn = root["udpn"]; notifyDirect = udpn["send"] | notifyDirect; receiveNotifications = udpn["recv"] | receiveNotifications; - bool noNotification = udpn[F("nn")]; //send no notification just for this request + if ((bool)udpn[F("nn")]) callMode = CALL_MODE_NO_NOTIFY; //send no notification just for this request unsigned long timein = root[F("time")] | UINT32_MAX; //backup time source if NTP not synced if (timein != UINT32_MAX) { @@ -319,22 +308,21 @@ bool deserializeState(JsonObject root, byte callMode, byte presetId) byte prevMain = strip.getMainSegmentId(); strip.mainSegment = root[F("mainseg")] | prevMain; - if (strip.getMainSegmentId() != prevMain) setValuesFromMainSeg(); + //if (strip.getMainSegmentId() != prevMain) setValuesFromMainSeg(); int it = 0; JsonVariant segVar = root["seg"]; if (segVar.is()) { int id = segVar["id"] | -1; - - if (id < 0) { //set all selected segments + //if "seg" is not an array and ID not specified, apply to all selected/checked segments + if (id < 0) { + //apply all selected segments bool didSet = false; byte lowestActive = 99; - for (byte s = 0; s < strip.getMaxSegments(); s++) - { - WS2812FX::Segment sg = strip.getSegment(s); - if (sg.isActive()) - { + for (byte s = 0; s < strip.getMaxSegments(); s++) { + WS2812FX::Segment &sg = strip.getSegment(s); + if (sg.isActive()) { if (lowestActive == 99) lowestActive = s; if (sg.isSelected()) { deserializeSegment(segVar, s, presetId); @@ -342,9 +330,10 @@ bool deserializeState(JsonObject root, byte callMode, byte presetId) } } } + //TODO: not sure if it is good idea to change first active but unselected segment if (!didSet && lowestActive < strip.getMaxSegments()) deserializeSegment(segVar, lowestActive, presetId); - } else { //set only the segment with the specified ID - deserializeSegment(segVar, it, presetId); + } else { + deserializeSegment(segVar, id, presetId); //apply only the segment with the specified ID } } else { JsonArray segs = segVar.as(); @@ -354,6 +343,8 @@ bool deserializeState(JsonObject root, byte callMode, byte presetId) it++; } } + setValuesFromMainSeg(); //to make transition work on main segment + if (effectChanged) unloadPlaylist(); //if any of the effect parameter changed unload playlist #ifndef WLED_DISABLE_CRONIXIE if (root["nx"].is()) { @@ -394,12 +385,13 @@ bool deserializeState(JsonObject root, byte callMode, byte presetId) JsonObject playlist = root[F("playlist")]; if (!playlist.isNull() && loadPlaylist(playlist, presetId)) { //do not notify here, because the first playlist entry will do - noNotification = true; + if (root["on"].isNull()) callMode = CALL_MODE_NO_NOTIFY; + else callMode = CALL_MODE_DIRECT_CHANGE; // possible bugfix for playlist only containing HTTP API preset FX=~ } else { interfaceUpdateCallMode = CALL_MODE_WS_SEND; } - colorUpdated(noNotification ? CALL_MODE_NO_NOTIFY : callMode); + colorUpdated(callMode); return stateResponse; } @@ -423,35 +415,35 @@ void serializeSegment(JsonObject& root, WS2812FX::Segment& seg, byte id, bool fo if (segmentBounds && seg.name != nullptr) root["n"] = reinterpret_cast(seg.name); //not good practice, but decreases required JSON buffer - char colstr[70]; colstr[0] = '['; colstr[1] = '\0'; //max len 68 (5 chan, all 255) - - for (uint8_t i = 0; i < 3; i++) - { + // to conserve RAM we will serialize the col array manually + // this will reduce RAM footprint from ~300 bytes to 84 bytes per segment + char colstr[70]; colstr[0] = '['; colstr[1] = '\0'; //max len 68 (5 chan, all 255) + const char *format = strip.isRgbw ? PSTR("[%u,%u,%u,%u]") : PSTR("[%u,%u,%u]"); + for (uint8_t i = 0; i < 3; i++) + { byte segcol[4]; byte* c = segcol; - if (id == strip.getMainSegmentId() && i < 2) //temporary, to make transition work on main segment { c = (i == 0)? col:colSec; } else { - segcol[0] = (byte)(seg.colors[i] >> 16); segcol[1] = (byte)(seg.colors[i] >> 8); - segcol[2] = (byte)(seg.colors[i]); segcol[3] = (byte)(seg.colors[i] >> 24); + segcol[0] = R(seg.colors[i]); + segcol[1] = G(seg.colors[i]); + segcol[2] = B(seg.colors[i]); + segcol[3] = W(seg.colors[i]); } - char tmpcol[22]; - if (strip.isRgbw) sprintf_P(tmpcol, PSTR("[%u,%u,%u,%u]"), c[0], c[1], c[2], c[3]); - else sprintf_P(tmpcol, PSTR("[%u,%u,%u]"), c[0], c[1], c[2]); - - strcat(colstr, i<2 ? strcat(tmpcol,",") : tmpcol); - } - strcat(colstr,"]"); + sprintf_P(tmpcol, format, (unsigned)c[0], (unsigned)c[1], (unsigned)c[2], (unsigned)c[3]); + strcat(colstr, i<2 ? strcat_P(tmpcol, PSTR(",")) : tmpcol); + } + strcat_P(colstr, PSTR("]")); root["col"] = serialized(colstr); - root["fx"] = seg.mode; + root["fx"] = seg.mode; root[F("sx")] = seg.speed; root[F("ix")] = seg.intensity; - root["pal"] = seg.palette; + root["pal"] = seg.palette; root[F("sel")] = seg.isSelected(); - root["rev"] = seg.getOption(SEG_OPTION_REVERSED); + root["rev"] = seg.getOption(SEG_OPTION_REVERSED); root[F("mi")] = seg.getOption(SEG_OPTION_MIRROR); } @@ -464,7 +456,7 @@ void serializeState(JsonObject root, bool forPreset, bool includeBri, bool segme } if (!forPreset) { - if (errorFlag) root[F("error")] = errorFlag; + if (errorFlag) {root[F("error")] = errorFlag; errorFlag = ERR_NONE;} //prevent error message to persist on screen root["ps"] = (currentPreset > 0) ? currentPreset : -1; root[F("pl")] = currentPlaylist; @@ -492,11 +484,9 @@ void serializeState(JsonObject root, bool forPreset, bool includeBri, bool segme root[F("mainseg")] = strip.getMainSegmentId(); JsonArray seg = root.createNestedArray("seg"); - for (byte s = 0; s < strip.getMaxSegments(); s++) - { - WS2812FX::Segment sg = strip.getSegment(s); - if (sg.isActive()) - { + for (byte s = 0; s < strip.getMaxSegments(); s++) { + WS2812FX::Segment &sg = strip.getSegment(s); + if (sg.isActive()) { JsonObject seg0 = seg.createNestedObject(); serializeSegment(seg0, sg, s, forPreset, segmentBounds); } else if (forPreset && segmentBounds) { //disable segments not part of preset @@ -622,9 +612,11 @@ void serializeInfo(JsonObject root) #endif root[F("freeheap")] = ESP.getFreeHeap(); + #if defined(ARDUINO_ARCH_ESP32) && defined(WLED_USE_PSRAM) + if (psramFound()) root[F("psram")] = ESP.getFreePsram(); + #endif root[F("uptime")] = millis()/1000 + rolloverMillis*4294967; - usermods.addToJsonInfo(root); byte os = 0; @@ -671,7 +663,7 @@ void setPaletteColors(JsonArray json, CRGBPalette16 palette) for (int i = 0; i < 16; i++) { JsonArray colors = json.createNestedArray(); CRGB color = palette[i]; - colors.add((((float)i / (float)16) * 255)); + colors.add(i<<4); colors.add(color.red); colors.add(color.green); colors.add(color.blue); @@ -903,13 +895,13 @@ void serveJson(AsyncWebServerRequest* request) bool serveLiveLeds(AsyncWebServerRequest* request, uint32_t wsClient) { + #ifdef WLED_ENABLE_WEBSOCKETS AsyncWebSocketClient * wsc = nullptr; if (!request) { //not HTTP, use Websockets - #ifdef WLED_ENABLE_WEBSOCKETS wsc = ws.client(wsClient); if (!wsc || wsc->queueLength() > 0) return false; //only send if queue free - #endif } + #endif uint16_t used = strip.getLengthTotal(); uint16_t n = (used -1) /MAX_LIVE_LEDS +1; //only serve every n'th LED if count over MAX_LIVE_LEDS diff --git a/wled00/led.cpp b/wled00/led.cpp index 311ec75e..57733708 100644 --- a/wled00/led.cpp +++ b/wled00/led.cpp @@ -44,8 +44,8 @@ byte scaledBri(byte in) void setAllLeds() { - strip.setColor(0, col[0], col[1], col[2], col[3]); - strip.setColor(1, colSec[0], colSec[1], colSec[2], colSec[3]); + strip.setColor(0, RGBW32(col[0], col[1], col[2], col[3])); + strip.setColor(1, RGBW32(colSec[0], colSec[1], colSec[2], colSec[3])); if (!realtimeMode || !arlsForceMaxBri) { strip.setBrightness(scaledBri(briT)); @@ -61,106 +61,67 @@ void setLedsStandard() } -bool colorChanged() -{ - for (byte i=0; i<4; i++) - { - if (col[i] != colIT[i]) return true; - if (colSec[i] != colSecIT[i]) return true; - } - if (bri != briIT) return true; - return false; -} - - void colorUpdated(int callMode) { //call for notifier -> 0: init 1: direct change 2: button 3: notification 4: nightlight 5: other (No notification) // 6: fx changed 7: hue 8: preset cycle 9: blynk 10: alexa 11: ws send only 12: button preset - if (callMode != CALL_MODE_INIT && - callMode != CALL_MODE_DIRECT_CHANGE && - callMode != CALL_MODE_NO_NOTIFY && - callMode != CALL_MODE_BUTTON_PRESET) strip.applyToAllSelected = true; //if not from JSON api, which directly sets segments - bool someSel = false; - - if (callMode == CALL_MODE_NOTIFICATION) { - someSel = (receiveNotificationBrightness || receiveNotificationColor || receiveNotificationEffects || receiveSegmentOptions); - } - - //Notifier: apply received FX to selected segments only if actually receiving FX - if (someSel) strip.applyToAllSelected = receiveNotificationEffects; - - bool fxChanged = strip.setEffectConfig(effectCurrent, effectSpeed, effectIntensity, effectPalette) || effectChanged; - bool colChanged = colorChanged(); - - //Notifier: apply received color to selected segments only if actually receiving color - if (someSel) strip.applyToAllSelected = receiveNotificationColor; - - if (fxChanged || colChanged) - { - effectChanged = false; + if (bri != briOld || effectChanged || colorChanged) { if (realtimeTimeout == UINT32_MAX) realtimeTimeout = 0; - currentPreset = 0; //something changed, so we are no longer in the preset + if (effectChanged) currentPreset = 0; //something changed, so we are no longer in the preset - notify(callMode); + if (callMode != CALL_MODE_NOTIFICATION && callMode != CALL_MODE_NO_NOTIFY) notify(callMode); //set flag to update blynk, ws and mqtt interfaceUpdateCallMode = callMode; + effectChanged = false; + colorChanged = false; } else { - if (nightlightActive && !nightlightActiveOld && - callMode != CALL_MODE_NOTIFICATION && - callMode != CALL_MODE_NO_NOTIFY) - { + if (nightlightActive && !nightlightActiveOld && callMode != CALL_MODE_NOTIFICATION && callMode != CALL_MODE_NO_NOTIFY) { notify(CALL_MODE_NIGHTLIGHT); interfaceUpdateCallMode = CALL_MODE_NIGHTLIGHT; } } - if (!colChanged) return; //following code is for e.g. initiating transitions - - if (callMode != CALL_MODE_NO_NOTIFY && nightlightActive && (nightlightMode == NL_MODE_FADE || nightlightMode == NL_MODE_COLORFADE)) - { + if (callMode != CALL_MODE_NO_NOTIFY && nightlightActive && (nightlightMode == NL_MODE_FADE || nightlightMode == NL_MODE_COLORFADE)) { briNlT = bri; nightlightDelayMs -= (millis() - nightlightStartTime); nightlightStartTime = millis(); } - for (byte i=0; i<4; i++) - { - colIT[i] = col[i]; - colSecIT[i] = colSec[i]; - } - if (briT == 0) - { + if (briT == 0) { if (callMode != CALL_MODE_NOTIFICATION) resetTimebase(); //effect start from beginning } - briIT = bri; if (bri > 0) briLast = bri; //deactivate nightlight if target brightness is reached if (bri == nightlightTargetBri && callMode != CALL_MODE_NO_NOTIFY && nightlightMode != NL_MODE_SUN) nightlightActive = false; - if (fadeTransition) - { + if (fadeTransition) { //set correct delay if not using notification delay if (callMode != CALL_MODE_NOTIFICATION && !jsonTransitionOnce) transitionDelayTemp = transitionDelay; jsonTransitionOnce = false; strip.setTransition(transitionDelayTemp); - if (transitionDelayTemp == 0) {setLedsStandard(); strip.trigger(); return;} - - if (transitionActive) - { + if (transitionDelayTemp == 0) { + //setLedsStandard(); + briOld = briT = bri; + if (!realtimeMode || !arlsForceMaxBri) strip.setBrightness(scaledBri(briT)); + strip.trigger(); + return; + } + + if (transitionActive) { briOld = briT; tperLast = 0; } strip.setTransitionMode(true); transitionActive = true; transitionStartTime = millis(); - } else - { + } else { strip.setTransition(0); - setLedsStandard(); + //setLedsStandard(); + briOld = briT = bri; + if (!realtimeMode || !arlsForceMaxBri) strip.setBrightness(scaledBri(briT)); strip.trigger(); } } @@ -168,11 +129,9 @@ void colorUpdated(int callMode) void updateInterfaces(uint8_t callMode) { + lastInterfaceUpdate = millis(); sendDataWs(); - if (callMode == CALL_MODE_WS_SEND) { - lastInterfaceUpdate = millis(); - return; - } + if (callMode == CALL_MODE_WS_SEND) return; #ifndef WLED_DISABLE_ALEXA if (espalexaDevice != nullptr && callMode != CALL_MODE_ALEXA) { @@ -185,7 +144,6 @@ void updateInterfaces(uint8_t callMode) callMode != CALL_MODE_NO_NOTIFY) updateBlynk(); #endif doPublishMqtt = true; - lastInterfaceUpdate = millis(); } @@ -221,6 +179,13 @@ void handleTransitions() void handleNightlight() { +/* + static unsigned long lastNlUpdate; + unsigned long now = millis(); + if (now < 100 && lastNlUpdate > 0) lastNlUpdate = 0; //take care of millis() rollover + if (now - lastNlUpdate < 100) return; //allow only 10 NL updates per second + lastNlUpdate = now; +*/ if (nightlightActive) { if (!nightlightActiveOld) //init diff --git a/wled00/set.cpp b/wled00/set.cpp index 13dd4b35..df4c18ab 100644 --- a/wled00/set.cpp +++ b/wled00/set.cpp @@ -307,6 +307,7 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage) //start ntp if not already connected if (ntpEnabled && WLED_CONNECTED && !ntpConnected) ntpConnected = ntpUdp.begin(ntpLocalPort); + ntpLastSyncTime = 0; // force new NTP query longitude = request->arg(F("LN")).toFloat(); latitude = request->arg(F("LT")).toFloat(); @@ -586,7 +587,7 @@ bool handleSet(AsyncWebServerRequest *request, const String& req, bool apply) DEBUG_PRINT(F("API req: ")); DEBUG_PRINTLN(req); - strip.applyToAllSelected = false; + strip.applyToAllSelected = true; //segment select (sets main segment) byte prevMain = strip.getMainSegmentId(); @@ -597,22 +598,28 @@ bool handleSet(AsyncWebServerRequest *request, const String& req, bool apply) byte selectedSeg = strip.getMainSegmentId(); if (selectedSeg != prevMain) setValuesFromMainSeg(); + //snapshot to check if request changed values later, temporary. + byte prevCol[4] = {col[0], col[1], col[2], col[3]}; + byte prevColSec[4] = {colSec[0], colSec[1], colSec[2], colSec[3]}; + byte prevEffect = effectCurrent; + byte prevSpeed = effectSpeed; + byte prevIntensity = effectIntensity; + byte prevPalette = effectPalette; + pos = req.indexOf(F("SS=")); if (pos > 0) { byte t = getNumVal(&req, pos); - if (t < strip.getMaxSegments()) selectedSeg = t; + if (t < strip.getMaxSegments()) { + selectedSeg = t; + strip.applyToAllSelected = false; + } } WS2812FX::Segment& selseg = strip.getSegment(selectedSeg); pos = req.indexOf(F("SV=")); //segment selected if (pos > 0) { byte t = getNumVal(&req, pos); - if (t == 2) { - for (uint8_t i = 0; i < strip.getMaxSegments(); i++) - { - strip.getSegment(i).setOption(SEG_OPTION_SELECTED, 0); - } - } + if (t == 2) for (uint8_t i = 0; i < strip.getMaxSegments(); i++) strip.getSegment(i).setOption(SEG_OPTION_SELECTED, 0); // unselect other segments selseg.setOption(SEG_OPTION_SELECTED, t); } @@ -678,26 +685,21 @@ bool handleSet(AsyncWebServerRequest *request, const String& req, bool apply) applyPreset(presetCycCurr); } - //snapshot to check if request changed values later, temporary. - byte prevCol[4] = {col[0], col[1], col[2], col[3]}; - byte prevColSec[4] = {colSec[0], colSec[1], colSec[2], colSec[3]}; - byte prevEffect = effectCurrent; - byte prevSpeed = effectSpeed; - byte prevIntensity = effectIntensity; - byte prevPalette = effectPalette; - //set brightness updateVal(&req, "&A=", &bri); + bool col0Changed = false, col1Changed = false, col2Changed = false; //set colors updateVal(&req, "&R=", &col[0]); updateVal(&req, "&G=", &col[1]); updateVal(&req, "&B=", &col[2]); updateVal(&req, "&W=", &col[3]); + for (byte i=0; i<4; i++) if (prevCol[i]!=col[i]) col0Changed = colorChanged = true; updateVal(&req, "R2=", &colSec[0]); updateVal(&req, "G2=", &colSec[1]); updateVal(&req, "B2=", &colSec[2]); updateVal(&req, "W2=", &colSec[3]); + for (byte i=0; i<4; i++) if (prevColSec[i]!=colSec[i]) col1Changed = colorChanged = true; #ifdef WLED_ENABLE_LOXONE //lox parser @@ -728,52 +730,64 @@ bool handleSet(AsyncWebServerRequest *request, const String& req, bool apply) if (pos > 0) { tempsat = getNumVal(&req, pos); } - colorHStoRGB(temphue,tempsat,(req.indexOf(F("H2"))>0)? colSec:col); + byte sec = req.indexOf(F("H2")); + colorHStoRGB(temphue, tempsat, (sec>0) ? colSec : col); + if (sec>0) col1Changed = true; + else col0Changed = true; + colorChanged = true; } //set white spectrum (kelvin) pos = req.indexOf(F("&K=")); if (pos > 0) { - colorKtoRGB(getNumVal(&req, pos),(req.indexOf(F("K2"))>0)? colSec:col); + byte sec = req.indexOf(F("K2")); + colorKtoRGB(getNumVal(&req, pos), (sec>0) ? colSec : col); + if (sec>0) col1Changed = true; + else col0Changed = true; + colorChanged = true; } //set color from HEX or 32bit DEC + byte tmpCol[4]; pos = req.indexOf(F("CL=")); if (pos > 0) { colorFromDecOrHexString(col, (char*)req.substring(pos + 3).c_str()); + selseg.setColor(0, RGBW32(col[0], col[1], col[2], col[3]), selectedSeg); // defined above (SS= or main) + col0Changed = colorChanged = true; } pos = req.indexOf(F("C2=")); if (pos > 0) { colorFromDecOrHexString(colSec, (char*)req.substring(pos + 3).c_str()); + selseg.setColor(1, RGBW32(colSec[0], colSec[1], colSec[2], colSec[3]), selectedSeg); // defined above (SS= or main) + col1Changed = colorChanged = true; } pos = req.indexOf(F("C3=")); if (pos > 0) { - byte t[4]; - colorFromDecOrHexString(t, (char*)req.substring(pos + 3).c_str()); - if (selectedSeg != strip.getMainSegmentId()) { - strip.applyToAllSelected = true; - strip.setColor(2, t[0], t[1], t[2], t[3]); - } else { - selseg.setColor(2, RGBW32(t[0], t[1], t[2], t[3]), selectedSeg); // defined above (SS=) - } + colorFromDecOrHexString(tmpCol, (char*)req.substring(pos + 3).c_str()); + selseg.setColor(2, RGBW32(tmpCol[0], tmpCol[1], tmpCol[2], tmpCol[3]), selectedSeg); // defined above (SS= or main) + col2Changed = colorChanged = true; } //set to random hue SR=0->1st SR=1->2nd pos = req.indexOf(F("SR")); if (pos > 0) { - _setRandomColor(getNumVal(&req, pos)); + byte sec = getNumVal(&req, pos); + _setRandomColor(sec); + if (sec>0) col1Changed = true; + else col0Changed = true; + colorChanged = true; } //swap 2nd & 1st pos = req.indexOf(F("SC")); if (pos > 0) { byte temp; - for (uint8_t i=0; i<4; i++) - { - temp = col[i]; - col[i] = colSec[i]; + for (uint8_t i=0; i<4; i++) { + temp = col[i]; + col[i] = colSec[i]; colSec[i] = temp; } + col0Changed = col1Changed = colorChanged = true; } //set effect parameters @@ -781,6 +795,11 @@ bool handleSet(AsyncWebServerRequest *request, const String& req, bool apply) updateVal(&req, "SX=", &effectSpeed); updateVal(&req, "IX=", &effectIntensity); updateVal(&req, "FP=", &effectPalette, 0, strip.getPaletteCount()-1); + strip.setMode(selectedSeg, effectCurrent); + selseg.speed = effectSpeed; + selseg.intensity = effectIntensity; + selseg.palette = effectPalette; + if (effectCurrent != prevEffect || effectSpeed != prevSpeed || effectIntensity != prevIntensity || effectPalette != prevPalette) effectChanged = true; //set advanced overlay pos = req.indexOf(F("OL=")); @@ -911,45 +930,21 @@ bool handleSet(AsyncWebServerRequest *request, const String& req, bool apply) //you can add more if you need //apply to all selected manually to prevent #1618. Temporary - bool col0Changed = false, col1Changed = false; - for (uint8_t i = 0; i < 4; i++) { - if (col[i] != prevCol[i]) col0Changed = true; - if (colSec[i] != prevColSec[i]) col1Changed = true; - } - for (uint8_t i = 0; i < strip.getMaxSegments(); i++) - { - WS2812FX::Segment& seg = strip.getSegment(i); - if (!seg.isSelected()) continue; - if (effectCurrent != prevEffect) { - strip.setMode(i, effectCurrent); - effectChanged = true; - } - if (effectSpeed != prevSpeed) { - seg.speed = effectSpeed; - effectChanged = true; - } - if (effectIntensity != prevIntensity) { - seg.intensity = effectIntensity; - effectChanged = true; - } - if (effectPalette != prevPalette) { - seg.palette = effectPalette; - effectChanged = true; - } - } - - if (col0Changed) { - if (selectedSeg == strip.getMainSegmentId()) { - strip.applyToAllSelected = true; - strip.setColor(0, colorFromRgbw(col)); - } - } - if (col1Changed) { - if (selectedSeg == strip.getMainSegmentId()) { - strip.applyToAllSelected = true; - strip.setColor(1, colorFromRgbw(colSec)); + if (strip.applyToAllSelected) { + for (uint8_t i = 0; i < strip.getMaxSegments(); i++) { + WS2812FX::Segment& seg = strip.getSegment(i); + if (!seg.isActive() || !seg.isSelected() || i == selectedSeg) continue; + if (effectCurrent != prevEffect) strip.setMode(i, effectCurrent); + if (effectSpeed != prevSpeed) seg.speed = effectSpeed; + if (effectIntensity != prevIntensity) seg.intensity = effectIntensity; + if (effectPalette != prevPalette) seg.palette = effectPalette; + if (col0Changed) seg.colors[0] = RGBW32(col[0], col[1], col[2], col[3]); + if (col1Changed) seg.colors[1] = RGBW32(colSec[0], colSec[1], colSec[2], colSec[3]); + if (col2Changed) seg.colors[2] = RGBW32(tmpCol[0], tmpCol[1], tmpCol[2], tmpCol[3]); } } + strip.applyToAllSelected = false; + setValuesFromMainSeg(); //end of temporary fix code if (!apply) return true; //when called by JSON API, do not call colorUpdated() here @@ -958,8 +953,6 @@ bool handleSet(AsyncWebServerRequest *request, const String& req, bool apply) pos = req.indexOf(F("IN")); if (pos < 1) XML_response(request); - strip.applyToAllSelected = false; - pos = req.indexOf(F("&NN")); //do not send UDP notifications this time colorUpdated((pos > 0) ? CALL_MODE_NO_NOTIFY : CALL_MODE_DIRECT_CHANGE); diff --git a/wled00/udp.cpp b/wled00/udp.cpp index 2cddc4b8..87a94f64 100644 --- a/wled00/udp.cpp +++ b/wled00/udp.cpp @@ -282,24 +282,26 @@ void handleNotifications() bool someSel = (receiveNotificationBrightness || receiveNotificationColor || receiveNotificationEffects); //apply colors from notification - if (receiveNotificationColor || !someSel) - { - col[0] = udpIn[3]; - col[1] = udpIn[4]; - col[2] = udpIn[5]; + if (receiveNotificationColor || !someSel) { + if (version < 11 || !receiveSegmentOptions) { + // only change col[] if not syncing full segments + col[0] = udpIn[3]; + col[1] = udpIn[4]; + col[2] = udpIn[5]; + } if (version > 0) //sending module's white val is intended { - col[3] = udpIn[10]; - if (version > 1) - { + // only change col[3] if not syncing full segments + if (version < 11 || !receiveSegmentOptions) col[3] = udpIn[10]; + if (version > 1 && (version < 11 || !receiveSegmentOptions)) { + // only change colSec[] if not syncing full segments colSec[0] = udpIn[12]; colSec[1] = udpIn[13]; colSec[2] = udpIn[14]; colSec[3] = udpIn[15]; } - if (version > 6) - { - strip.setColor(2, udpIn[20], udpIn[21], udpIn[22], udpIn[23]); //tertiary color + if (version > 6 && (version < 11 || !receiveSegmentOptions)) { + strip.setColor(2, RGBW32(udpIn[20], udpIn[21], udpIn[22], udpIn[23])); //tertiary color } if (version > 9 && version < 200 && udpIn[37] < 255) { //valid CCT/Kelvin value uint8_t cct = udpIn[38]; @@ -324,22 +326,20 @@ void handleNotifications() for (uint8_t i = 0; i < srcSegs; i++) { WS2812FX::Segment& selseg = strip.getSegment(i); uint16_t ofs = 41 + i*UDP_SEG_SIZE; //start of segment offset byte - for (uint8_t j = 0; j<4; j++) selseg.setOption(j, (udpIn[44+i*22] >> j) & 0x01); //only take into account mirrored, selected, on, reversed - uint16_t offset = udpIn[2+ofs]<<8 | udpIn[3+ofs]; - selseg.setOpacity(udpIn[4+ofs], i); - if (i == strip.getMainSegmentId()) { //temporary, to make transition work on main segment - //TODO - } else { //permanent - strip.setMode(i, udpIn[5+ofs]); - selseg.speed = udpIn[6+ofs]; - selseg.intensity = udpIn[7+ofs]; - selseg.palette = udpIn[8+ofs]; - } + for (uint8_t j = 0; j<4; j++) selseg.setOption(j, (udpIn[4 +ofs] >> j) & 0x01); //only take into account mirrored, selected, on, reversed + selseg.setOpacity( udpIn[5+ofs], i); + strip.setMode(i, udpIn[6+ofs]); + selseg.speed = udpIn[7+ofs]; + selseg.intensity = udpIn[8+ofs]; + selseg.palette = udpIn[9+ofs]; selseg.setColor(0, RGBW32(udpIn[10+ofs],udpIn[11+ofs],udpIn[12+ofs],udpIn[13+ofs]), i); selseg.setColor(1, RGBW32(udpIn[14+ofs],udpIn[15+ofs],udpIn[16+ofs],udpIn[17+ofs]), i); selseg.setColor(2, RGBW32(udpIn[18+ofs],udpIn[19+ofs],udpIn[20+ofs],udpIn[21+ofs]), i); - strip.setSegment(i, selseg.start, selseg.stop, udpIn[0+ofs], udpIn[1+ofs], offset); //also properly resets segments + strip.setSegment(i, selseg.start, selseg.stop, udpIn[0+ofs], udpIn[1+ofs], (udpIn[2+ofs]<<8 | udpIn[3+ofs])); //also properly resets segments } + setValuesFromMainSeg(); + effectChanged = true; + colorChanged = true; } else { //simple effect sync, applies to all selected if (udpIn[8] < strip.getModeCount()) effectCurrent = udpIn[8]; effectSpeed = udpIn[9]; diff --git a/wled00/wled.h b/wled00/wled.h index 4283ba9f..1cbecb81 100644 --- a/wled00/wled.h +++ b/wled00/wled.h @@ -16,6 +16,7 @@ // ESP8266-01 (blue) got too little storage space to work with WLED. 0.10.2 is the last release supporting this unit. // ESP8266-01 (black) has 1MB flash and can thus fit the whole program, although OTA update is not possible. Use 1M(128K SPIFFS). +// 2-step OTA may still be possible: https://github.com/Aircoookie/WLED/issues/2040#issuecomment-981111096 // Uncomment some of the following lines to disable features: // Alternatively, with platformio pass your chosen flags to your custom build target in platformio_override.ini @@ -417,9 +418,6 @@ WLED_GLOBAL bool interfacesInited _INIT(false); WLED_GLOBAL bool wasConnected _INIT(false); // color -WLED_GLOBAL byte colIT[] _INIT_N(({ 0, 0, 0, 0 })); // color that was last sent to LEDs -WLED_GLOBAL byte colSecIT[] _INIT_N(({ 0, 0, 0, 0 })); - WLED_GLOBAL byte lastRandomIndex _INIT(0); // used to save last random color so the new one is not the same // transitions @@ -470,6 +468,7 @@ WLED_GLOBAL byte effectSpeed _INIT(128); WLED_GLOBAL byte effectIntensity _INIT(128); WLED_GLOBAL byte effectPalette _INIT(0); WLED_GLOBAL bool effectChanged _INIT(false); +WLED_GLOBAL bool colorChanged _INIT(false); // network WLED_GLOBAL bool udpConnected _INIT(false), udp2Connected _INIT(false), udpRgbConnected _INIT(false);