Merge branch 'full-seg-sync' into 4ld-font

This commit is contained in:
Blaz Kristan 2022-01-16 15:23:32 +01:00
commit 99b8888a1b
12 changed files with 1151 additions and 1163 deletions

View File

@ -390,7 +390,7 @@ uint8_t WS2812FX::getPaletteCount()
//TODO effect transitions //TODO effect transitions
/*
bool WS2812FX::setEffectConfig(uint8_t m, uint8_t s, uint8_t in, uint8_t p) { bool WS2812FX::setEffectConfig(uint8_t m, uint8_t s, uint8_t in, uint8_t p) {
Segment& seg = _segments[getMainSegmentId()]; Segment& seg = _segments[getMainSegmentId()];
uint8_t modePrev = seg.mode, speedPrev = seg.speed, intensityPrev = seg.intensity, palettePrev = seg.palette; uint8_t modePrev = seg.mode, speedPrev = seg.speed, intensityPrev = seg.intensity, palettePrev = seg.palette;
@ -425,7 +425,7 @@ bool WS2812FX::setEffectConfig(uint8_t m, uint8_t s, uint8_t in, uint8_t p) {
void WS2812FX::setColor(uint8_t slot, uint8_t r, uint8_t g, uint8_t b, uint8_t w) { void WS2812FX::setColor(uint8_t slot, uint8_t r, uint8_t g, uint8_t b, uint8_t w) {
setColor(slot, RGBW32(r, g, b, w)); setColor(slot, RGBW32(r, g, b, w));
} }
*/
void WS2812FX::setColor(uint8_t slot, uint32_t c) { void WS2812FX::setColor(uint8_t slot, uint32_t c) {
if (slot >= NUM_COLORS) return; if (slot >= NUM_COLORS) return;

View File

@ -22,11 +22,6 @@ void colorFromUint24(uint32_t in, bool secondary)
_col[2] = B(in); _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 //relatively change white brightness, minumum A=5
void relativeChangeWhite(int8_t amount, byte lowerBoundary) 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[1] = ((uint16_t) correctionRGB[1] * G(rgb)) /255; // correct G
rgbw[2] = ((uint16_t) correctionRGB[2] * B(rgb)) /255; // correct B rgbw[2] = ((uint16_t) correctionRGB[2] * B(rgb)) /255; // correct B
rgbw[3] = W(rgb); 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. //approximates a Kelvin color temperature from an RGB color.

View File

@ -10,7 +10,6 @@ var nlDur = 60, nlTar = 0;
var nlMode = false; var nlMode = false;
var selectedFx = 0, prevFx = -1; var selectedFx = 0, prevFx = -1;
var selectedPal = 0; var selectedPal = 0;
var sliderControl = ""; //WLEDSR: used by togglePcMode
var csel = 0; var csel = 0;
var currentPreset = -1, prevPS = -1; var currentPreset = -1, prevPS = -1;
var lastUpdate = 0; var lastUpdate = 0;
@ -1612,6 +1611,11 @@ ${makePlSel(true)}
Save segment bounds Save segment bounds
<input type="checkbox" id="p${i}sbtgl" checked> <input type="checkbox" id="p${i}sbtgl" checked>
<span class="checkmark schk"></span> <span class="checkmark schk"></span>
</label>
<label class="check revchkl">
Checked segments only
<input type="checkbox" id="p${i}sbchk">
<span class="checkmark schk"></span>
</label>`; </label>`;
return `<input type="text" class="ptxt noslide" id="p${i}txt" autocomplete="off" maxlength=32 value="${(i>0)?pName(i):""}" placeholder="Enter name..."/> return `<input type="text" class="ptxt noslide" id="p${i}txt" autocomplete="off" maxlength=32 value="${(i>0)?pName(i):""}" placeholder="Enter name..."/>
@ -1909,6 +1913,7 @@ function saveP(i,pl)
} else { } else {
obj.ib = gId(`p${i}ibtgl`).checked; obj.ib = gId(`p${i}ibtgl`).checked;
obj.sb = gId(`p${i}sbtgl`).checked; obj.sb = gId(`p${i}sbtgl`).checked;
obj.sc = gId(`p${i}sbchk`).checked;
} }
} }

View File

@ -58,7 +58,7 @@ bool getJsonValue(const JsonVariant& element, DestType& destination, const Defau
//colors.cpp //colors.cpp
void colorFromUint32(uint32_t in, bool secondary = false); void colorFromUint32(uint32_t in, bool secondary = false);
void colorFromUint24(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 relativeChangeWhite(int8_t amount, byte lowerBoundary = 0);
void colorHStoRGB(uint16_t hue, byte sat, byte* rgb); //hue, sat to rgb void colorHStoRGB(uint16_t hue, byte sat, byte* rgb); //hue, sat to rgb
void colorKtoRGB(uint16_t kelvin, byte* rgb); void colorKtoRGB(uint16_t kelvin, byte* rgb);
@ -146,7 +146,6 @@ void resetTimebase();
void toggleOnOff(); void toggleOnOff();
void setAllLeds(); void setAllLeds();
void setLedsStandard(); void setLedsStandard();
bool colorChanged();
void colorUpdated(int callMode); void colorUpdated(int callMode);
void updateInterfaces(uint8_t callMode); void updateInterfaces(uint8_t callMode);
void handleTransitions(); void handleTransitions();

File diff suppressed because it is too large Load Diff

View File

@ -131,16 +131,13 @@ void deserializeSegment(JsonObject elem, byte it, byte presetId)
} }
if (!colValid) continue; if (!colValid) continue;
if (id == strip.getMainSegmentId() && i < 2) //temporary, to make transition work on main segment
{ uint32_t color = RGBW32(rgbw[0],rgbw[1],rgbw[2],rgbw[3]);
if (i == 0) {col[0] = rgbw[0]; col[1] = rgbw[1]; col[2] = rgbw[2]; col[3] = rgbw[3];} colorChanged |= (seg.colors[i] != color);
if (i == 1) {colSec[0] = rgbw[0]; colSec[1] = rgbw[1]; colSec[2] = rgbw[2]; colSec[3] = rgbw[3];} seg.setColor(i, color, id);
} 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 if (seg.mode == FX_MODE_STATIC) strip.trigger(); //instant refresh
} }
} }
}
// lx parser // lx parser
#ifdef WLED_ENABLE_LOXONE #ifdef WLED_ENABLE_LOXONE
@ -161,26 +158,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 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, '~' inc/dec, 1-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 fx = seg.mode;
byte fxPrev = fx; byte fxPrev = fx;
if (getVal(elem["fx"], &fx, 1, strip.getModeCount())) { //load effect ('r' random, '~' inc/dec, 1-255 exact value) if (getVal(elem["fx"], &fx, 1, strip.getModeCount())) { //load effect ('r' random, '~' inc/dec, 1-255 exact value)
strip.setMode(id, fx); strip.setMode(id, fx);
if (!presetId && seg.mode != fxPrev) unloadPlaylist(); //stop playlist if active and FX changed manually if (!presetId && seg.mode != fxPrev) effectChanged = true; //send UDP
}
seg.speed = elem[F("sx")] | seg.speed;
seg.intensity = elem[F("ix")] | seg.intensity;
getVal(elem["pal"], &seg.palette, 1, strip.getPaletteCount());
} }
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 JsonArray iarr = elem[F("i")]; //set individual LEDs
if (!iarr.isNull()) { if (!iarr.isNull()) {
@ -307,32 +296,32 @@ bool deserializeState(JsonObject root, byte callMode, byte presetId)
byte prevMain = strip.getMainSegmentId(); byte prevMain = strip.getMainSegmentId();
strip.mainSegment = root[F("mainseg")] | prevMain; strip.mainSegment = root[F("mainseg")] | prevMain;
if (strip.getMainSegmentId() != prevMain) setValuesFromMainSeg(); //if (strip.getMainSegmentId() != prevMain) setValuesFromMainSeg();
int it = 0; int it = 0;
JsonVariant segVar = root["seg"]; JsonVariant segVar = root["seg"];
if (segVar.is<JsonObject>()) if (segVar.is<JsonObject>())
{ {
int id = segVar["id"] | -1; int id = segVar["id"] | -1;
//if "seg" is not an array and ID not specified, apply to all selected/checked segments
if (id < 0) { //set all selected segments if (id < 0) {
bool didSet = false; //apply all selected segments
byte lowestActive = 99; //bool didSet = false;
for (byte s = 0; s < strip.getMaxSegments(); s++) //byte lowestActive = 99;
{ for (byte s = 0; s < strip.getMaxSegments(); s++) {
WS2812FX::Segment &sg = strip.getSegment(s); WS2812FX::Segment &sg = strip.getSegment(s);
if (sg.isActive()) if (sg.isActive()) {
{ //if (lowestActive == 99) lowestActive = s;
if (lowestActive == 99) lowestActive = s;
if (sg.isSelected()) { if (sg.isSelected()) {
deserializeSegment(segVar, s, presetId); deserializeSegment(segVar, s, presetId);
didSet = true; //didSet = true;
} }
} }
} }
if (!didSet && lowestActive < strip.getMaxSegments()) deserializeSegment(segVar, lowestActive, presetId); //TODO: not sure if it is good idea to change first active but unselected segment
} else { //set only the segment with the specified ID //if (!didSet && lowestActive < strip.getMaxSegments()) deserializeSegment(segVar, lowestActive, presetId);
deserializeSegment(segVar, it, presetId); } else {
deserializeSegment(segVar, id, presetId); //apply only the segment with the specified ID
} }
} else { } else {
JsonArray segs = segVar.as<JsonArray>(); JsonArray segs = segVar.as<JsonArray>();
@ -342,6 +331,8 @@ bool deserializeState(JsonObject root, byte callMode, byte presetId)
it++; 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 #ifndef WLED_DISABLE_CRONIXIE
if (root["nx"].is<const char*>()) { if (root["nx"].is<const char*>()) {
@ -483,12 +474,12 @@ void serializeState(JsonObject root, bool forPreset, bool includeBri, bool segme
root[F("mainseg")] = strip.getMainSegmentId(); root[F("mainseg")] = strip.getMainSegmentId();
bool selectedSegmentsOnly = root[F("sc")] | false;
JsonArray seg = root.createNestedArray("seg"); JsonArray seg = root.createNestedArray("seg");
for (byte s = 0; s < strip.getMaxSegments(); s++) for (byte s = 0; s < strip.getMaxSegments(); s++) {
{
WS2812FX::Segment &sg = strip.getSegment(s); WS2812FX::Segment &sg = strip.getSegment(s);
if (sg.isActive()) if (selectedSegmentsOnly && !sg.isSelected()) continue;
{ if (sg.isActive()) {
JsonObject seg0 = seg.createNestedObject(); JsonObject seg0 = seg.createNestedObject();
serializeSegment(seg0, sg, s, forPreset, segmentBounds); serializeSegment(seg0, sg, s, forPreset, segmentBounds);
} else if (forPreset && segmentBounds) { //disable segments not part of preset } else if (forPreset && segmentBounds) { //disable segments not part of preset

View File

@ -44,8 +44,8 @@ byte scaledBri(byte in)
void setAllLeds() { void setAllLeds() {
strip.setColor(0, col[0], col[1], col[2], col[3]); strip.setColor(0, RGBW32(col[0], col[1], col[2], col[3]));
strip.setColor(1, colSec[0], colSec[1], colSec[2], colSec[3]); strip.setColor(1, RGBW32(colSec[0], colSec[1], colSec[2], colSec[3]));
if (!realtimeMode || !arlsForceMaxBri) if (!realtimeMode || !arlsForceMaxBri)
{ {
strip.setBrightness(scaledBri(briT)); 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) void colorUpdated(int callMode)
{ {
//call for notifier -> 0: init 1: direct change 2: button 3: notification 4: nightlight 5: other (No notification) //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 // 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 (bri != briOld || effectChanged || colorChanged) {
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 (realtimeTimeout == UINT32_MAX) realtimeTimeout = 0; 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 //set flag to update blynk, ws and mqtt
interfaceUpdateCallMode = callMode; interfaceUpdateCallMode = callMode;
effectChanged = false;
colorChanged = false;
} else { } else {
if (nightlightActive && !nightlightActiveOld && if (nightlightActive && !nightlightActiveOld && callMode != CALL_MODE_NOTIFICATION && callMode != CALL_MODE_NO_NOTIFY) {
callMode != CALL_MODE_NOTIFICATION &&
callMode != CALL_MODE_NO_NOTIFY)
{
notify(CALL_MODE_NIGHTLIGHT); notify(CALL_MODE_NIGHTLIGHT);
interfaceUpdateCallMode = 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; briNlT = bri;
nightlightDelayMs -= (millis() - nightlightStartTime); nightlightDelayMs -= (millis() - nightlightStartTime);
nightlightStartTime = millis(); nightlightStartTime = millis();
} }
for (byte i=0; i<4; i++) if (briT == 0) {
{
colIT[i] = col[i];
colSecIT[i] = colSec[i];
}
if (briT == 0)
{
if (callMode != CALL_MODE_NOTIFICATION) resetTimebase(); //effect start from beginning if (callMode != CALL_MODE_NOTIFICATION) resetTimebase(); //effect start from beginning
} }
briIT = bri;
if (bri > 0) briLast = bri; if (bri > 0) briLast = bri;
//deactivate nightlight if target brightness is reached //deactivate nightlight if target brightness is reached
if (bri == nightlightTargetBri && callMode != CALL_MODE_NO_NOTIFY && nightlightMode != NL_MODE_SUN) nightlightActive = false; 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 //set correct delay if not using notification delay
if (callMode != CALL_MODE_NOTIFICATION && !jsonTransitionOnce) transitionDelayTemp = transitionDelay; if (callMode != CALL_MODE_NOTIFICATION && !jsonTransitionOnce) transitionDelayTemp = transitionDelay;
jsonTransitionOnce = false; jsonTransitionOnce = false;
strip.setTransition(transitionDelayTemp); strip.setTransition(transitionDelayTemp);
if (transitionDelayTemp == 0) {setLedsStandard(); strip.trigger(); return;} if (transitionDelayTemp == 0) {
//setLedsStandard();
briOld = briT = bri;
if (!realtimeMode || !arlsForceMaxBri) strip.setBrightness(scaledBri(briT));
strip.trigger();
return;
}
if (transitionActive) if (transitionActive) {
{
briOld = briT; briOld = briT;
tperLast = 0; tperLast = 0;
} }
strip.setTransitionMode(true); strip.setTransitionMode(true);
transitionActive = true; transitionActive = true;
transitionStartTime = millis(); transitionStartTime = millis();
} else } else {
{
strip.setTransition(0); strip.setTransition(0);
setLedsStandard(); //setLedsStandard();
briOld = briT = bri;
if (!realtimeMode || !arlsForceMaxBri) strip.setBrightness(scaledBri(briT));
strip.trigger(); strip.trigger();
} }
} }
@ -218,6 +179,12 @@ void handleTransitions()
void handleNightlight() 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 (nightlightActive)
{ {
if (!nightlightActiveOld) //init if (!nightlightActiveOld) //init

View File

@ -63,7 +63,6 @@ void handlePresets()
if (!errorFlag && presetToApply < 255) currentPreset = presetToApply; if (!errorFlag && presetToApply < 255) currentPreset = presetToApply;
effectChanged = true; //force UDP notification
colorUpdated(callModeToApply); colorUpdated(callModeToApply);
updateInterfaces(callModeToApply); updateInterfaces(callModeToApply);
@ -102,6 +101,7 @@ void savePreset(byte index, bool persist, const char* pname, JsonObject saveobj)
sObj.remove("o"); sObj.remove("o");
sObj.remove("ib"); sObj.remove("ib");
sObj.remove("sb"); sObj.remove("sb");
sObj.remove(F("sc"));
sObj.remove(F("error")); sObj.remove(F("error"));
sObj.remove(F("time")); sObj.remove(F("time"));

View File

@ -518,7 +518,7 @@ bool handleSet(AsyncWebServerRequest *request, const String& req, bool apply)
DEBUG_PRINT(F("API req: ")); DEBUG_PRINT(F("API req: "));
DEBUG_PRINTLN(req); DEBUG_PRINTLN(req);
strip.applyToAllSelected = false; strip.applyToAllSelected = true;
//segment select (sets main segment) //segment select (sets main segment)
byte prevMain = strip.getMainSegmentId(); byte prevMain = strip.getMainSegmentId();
@ -529,22 +529,28 @@ bool handleSet(AsyncWebServerRequest *request, const String& req, bool apply)
byte selectedSeg = strip.getMainSegmentId(); byte selectedSeg = strip.getMainSegmentId();
if (selectedSeg != prevMain) setValuesFromMainSeg(); 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=")); pos = req.indexOf(F("SS="));
if (pos > 0) { if (pos > 0) {
byte t = getNumVal(&req, pos); 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); WS2812FX::Segment& selseg = strip.getSegment(selectedSeg);
pos = req.indexOf(F("SV=")); //segment selected pos = req.indexOf(F("SV=")); //segment selected
if (pos > 0) { if (pos > 0) {
byte t = getNumVal(&req, pos); byte t = getNumVal(&req, pos);
if (t == 2) { if (t == 2) for (uint8_t i = 0; i < strip.getMaxSegments(); i++) strip.getSegment(i).setOption(SEG_OPTION_SELECTED, 0); // unselect other segments
for (uint8_t i = 0; i < strip.getMaxSegments(); i++)
{
strip.getSegment(i).setOption(SEG_OPTION_SELECTED, 0);
}
}
selseg.setOption(SEG_OPTION_SELECTED, t); selseg.setOption(SEG_OPTION_SELECTED, t);
} }
@ -610,26 +616,21 @@ bool handleSet(AsyncWebServerRequest *request, const String& req, bool apply)
applyPreset(presetCycCurr); 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 //set brightness
updateVal(&req, "&A=", &bri); updateVal(&req, "&A=", &bri);
bool col0Changed = false, col1Changed = false, col2Changed = false;
//set colors //set colors
updateVal(&req, "&R=", &col[0]); updateVal(&req, "&R=", &col[0]);
updateVal(&req, "&G=", &col[1]); updateVal(&req, "&G=", &col[1]);
updateVal(&req, "&B=", &col[2]); updateVal(&req, "&B=", &col[2]);
updateVal(&req, "&W=", &col[3]); 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, "R2=", &colSec[0]);
updateVal(&req, "G2=", &colSec[1]); updateVal(&req, "G2=", &colSec[1]);
updateVal(&req, "B2=", &colSec[2]); updateVal(&req, "B2=", &colSec[2]);
updateVal(&req, "W2=", &colSec[3]); updateVal(&req, "W2=", &colSec[3]);
for (byte i=0; i<4; i++) if (prevColSec[i]!=colSec[i]) col1Changed = colorChanged = true;
#ifdef WLED_ENABLE_LOXONE #ifdef WLED_ENABLE_LOXONE
//lox parser //lox parser
@ -660,52 +661,64 @@ bool handleSet(AsyncWebServerRequest *request, const String& req, bool apply)
if (pos > 0) { if (pos > 0) {
tempsat = getNumVal(&req, pos); 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) //set white spectrum (kelvin)
pos = req.indexOf(F("&K=")); pos = req.indexOf(F("&K="));
if (pos > 0) { 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 //set color from HEX or 32bit DEC
byte tmpCol[4];
pos = req.indexOf(F("CL=")); pos = req.indexOf(F("CL="));
if (pos > 0) { if (pos > 0) {
colorFromDecOrHexString(col, (char*)req.substring(pos + 3).c_str()); 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=")); pos = req.indexOf(F("C2="));
if (pos > 0) { if (pos > 0) {
colorFromDecOrHexString(colSec, (char*)req.substring(pos + 3).c_str()); 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=")); pos = req.indexOf(F("C3="));
if (pos > 0) { if (pos > 0) {
byte t[4]; colorFromDecOrHexString(tmpCol, (char*)req.substring(pos + 3).c_str());
colorFromDecOrHexString(t, (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)
if (selectedSeg != strip.getMainSegmentId()) { col2Changed = colorChanged = true;
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=)
}
} }
//set to random hue SR=0->1st SR=1->2nd //set to random hue SR=0->1st SR=1->2nd
pos = req.indexOf(F("SR")); pos = req.indexOf(F("SR"));
if (pos > 0) { 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 //swap 2nd & 1st
pos = req.indexOf(F("SC")); pos = req.indexOf(F("SC"));
if (pos > 0) { if (pos > 0) {
byte temp; byte temp;
for (uint8_t i=0; i<4; i++) for (uint8_t i=0; i<4; i++) {
{
temp = col[i]; temp = col[i];
col[i] = colSec[i]; col[i] = colSec[i];
colSec[i] = temp; colSec[i] = temp;
} }
col0Changed = col1Changed = colorChanged = true;
} }
//set effect parameters //set effect parameters
@ -713,6 +726,11 @@ bool handleSet(AsyncWebServerRequest *request, const String& req, bool apply)
updateVal(&req, "SX=", &effectSpeed); updateVal(&req, "SX=", &effectSpeed);
updateVal(&req, "IX=", &effectIntensity); updateVal(&req, "IX=", &effectIntensity);
updateVal(&req, "FP=", &effectPalette, 0, strip.getPaletteCount()-1); 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 //set advanced overlay
pos = req.indexOf(F("OL=")); pos = req.indexOf(F("OL="));
@ -843,45 +861,21 @@ bool handleSet(AsyncWebServerRequest *request, const String& req, bool apply)
//you can add more if you need //you can add more if you need
//apply to all selected manually to prevent #1618. Temporary //apply to all selected manually to prevent #1618. Temporary
bool col0Changed = false, col1Changed = false; if (strip.applyToAllSelected) {
for (uint8_t i = 0; i < 4; i++) { for (uint8_t i = 0; i < strip.getMaxSegments(); 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); WS2812FX::Segment& seg = strip.getSegment(i);
if (!seg.isSelected()) continue; if (!seg.isActive() || !seg.isSelected() || i == selectedSeg) continue;
if (effectCurrent != prevEffect) { if (effectCurrent != prevEffect) strip.setMode(i, effectCurrent);
strip.setMode(i, effectCurrent); if (effectSpeed != prevSpeed) seg.speed = effectSpeed;
effectChanged = true; if (effectIntensity != prevIntensity) seg.intensity = effectIntensity;
} if (effectPalette != prevPalette) seg.palette = effectPalette;
if (effectSpeed != prevSpeed) { if (col0Changed) seg.colors[0] = RGBW32(col[0], col[1], col[2], col[3]);
seg.speed = effectSpeed; if (col1Changed) seg.colors[1] = RGBW32(colSec[0], colSec[1], colSec[2], colSec[3]);
effectChanged = true; if (col2Changed) seg.colors[2] = RGBW32(tmpCol[0], tmpCol[1], tmpCol[2], tmpCol[3]);
}
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));
} }
} }
strip.applyToAllSelected = false;
setValuesFromMainSeg();
//end of temporary fix code //end of temporary fix code
if (!apply) return true; //when called by JSON API, do not call colorUpdated() here if (!apply) return true; //when called by JSON API, do not call colorUpdated() here
@ -890,8 +884,6 @@ bool handleSet(AsyncWebServerRequest *request, const String& req, bool apply)
pos = req.indexOf(F("IN")); pos = req.indexOf(F("IN"));
if (pos < 1) XML_response(request); if (pos < 1) XML_response(request);
strip.applyToAllSelected = false;
pos = req.indexOf(F("&NN")); //do not send UDP notifications this time pos = req.indexOf(F("&NN")); //do not send UDP notifications this time
colorUpdated((pos > 0) ? CALL_MODE_NO_NOTIFY : CALL_MODE_DIRECT_CHANGE); colorUpdated((pos > 0) ? CALL_MODE_NO_NOTIFY : CALL_MODE_DIRECT_CHANGE);

View File

@ -4,7 +4,9 @@
* UDP sync notifier / Realtime / Hyperion / TPM2.NET * UDP sync notifier / Realtime / Hyperion / TPM2.NET
*/ */
#define WLEDPACKETSIZE (40+(MAX_NUM_SEGMENTS*3)) #define UDP_SEG_SIZE 22
#define SEG_OFFSET (41+(MAX_NUM_SEGMENTS*UDP_SEG_SIZE))
#define WLEDPACKETSIZE (41+(MAX_NUM_SEGMENTS*UDP_SEG_SIZE)+0)
#define UDP_IN_MAXSIZE 1472 #define UDP_IN_MAXSIZE 1472
#define PRESUMED_NETWORK_DELAY 3 //how many ms could it take on avg to reach the receiver? This will be added to transmitted times #define PRESUMED_NETWORK_DELAY 3 //how many ms could it take on avg to reach the receiver? This will be added to transmitted times
@ -86,13 +88,37 @@ void notify(byte callMode, bool followUp)
udpOut[38] = mainseg.cct; udpOut[38] = mainseg.cct;
udpOut[39] = strip.getMaxSegments(); udpOut[39] = strip.getMaxSegments();
udpOut[40] = UDP_SEG_SIZE; //size of each loop iteration (one segment)
for (uint8_t i = 0; i < strip.getMaxSegments(); i++) { for (uint8_t i = 0; i < strip.getMaxSegments(); i++) {
WS2812FX::Segment &selseg = strip.getSegment(i); WS2812FX::Segment &selseg = strip.getSegment(i);
udpOut[40+i*3] = selseg.options & 0x0F; //only take into account mirrored, selected, on, reversed uint16_t ofs = 41 + i*UDP_SEG_SIZE; //start of segment offset byte
udpOut[41+i*3] = selseg.spacing; udpOut[0 +ofs] = selseg.grouping;
udpOut[42+i*3] = selseg.grouping; udpOut[1 +ofs] = selseg.spacing;
udpOut[2 +ofs] = selseg.offset >> 8;
udpOut[3 +ofs] = selseg.offset & 0xFF;
udpOut[4 +ofs] = selseg.options & 0x0F; //only take into account mirrored, selected, on, reversed
udpOut[5 +ofs] = selseg.opacity;
udpOut[6 +ofs] = selseg.mode;
udpOut[7 +ofs] = selseg.speed;
udpOut[8 +ofs] = selseg.intensity;
udpOut[9 +ofs] = selseg.palette;
udpOut[10+ofs] = R(selseg.colors[0]);
udpOut[11+ofs] = G(selseg.colors[0]);
udpOut[12+ofs] = B(selseg.colors[0]);
udpOut[13+ofs] = W(selseg.colors[0]);
udpOut[14+ofs] = R(selseg.colors[1]);
udpOut[15+ofs] = G(selseg.colors[1]);
udpOut[16+ofs] = B(selseg.colors[1]);
udpOut[17+ofs] = W(selseg.colors[1]);
udpOut[18+ofs] = R(selseg.colors[2]);
udpOut[19+ofs] = G(selseg.colors[2]);
udpOut[20+ofs] = B(selseg.colors[2]);
udpOut[21+ofs] = W(selseg.colors[2]);
} }
//uint16_t offs = SEG_OFFSET;
//next value to be added has index: udpOut[offs + 0]
IPAddress broadcastIp; IPAddress broadcastIp;
broadcastIp = ~uint32_t(Network.subnetMask()) | uint32_t(Network.gatewayIP()); broadcastIp = ~uint32_t(Network.subnetMask()) | uint32_t(Network.gatewayIP());
@ -256,24 +282,26 @@ void handleNotifications()
bool someSel = (receiveNotificationBrightness || receiveNotificationColor || receiveNotificationEffects); bool someSel = (receiveNotificationBrightness || receiveNotificationColor || receiveNotificationEffects);
//apply colors from notification //apply colors from notification
if (receiveNotificationColor || !someSel) if (receiveNotificationColor || !someSel) {
{ if (version < 11 || !receiveSegmentOptions) {
// only change col[] if not syncing full segments
col[0] = udpIn[3]; col[0] = udpIn[3];
col[1] = udpIn[4]; col[1] = udpIn[4];
col[2] = udpIn[5]; col[2] = udpIn[5];
}
if (version > 0) //sending module's white val is intended if (version > 0) //sending module's white val is intended
{ {
col[3] = udpIn[10]; // only change col[3] if not syncing full segments
if (version > 1) 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[0] = udpIn[12];
colSec[1] = udpIn[13]; colSec[1] = udpIn[13];
colSec[2] = udpIn[14]; colSec[2] = udpIn[14];
colSec[3] = udpIn[15]; colSec[3] = udpIn[15];
} }
if (version > 6) if (version > 6 && (version < 11 || !receiveSegmentOptions)) {
{ strip.setColor(2, RGBW32(udpIn[20], udpIn[21], udpIn[22], udpIn[23])); //tertiary color
strip.setColor(2, udpIn[20], udpIn[21], udpIn[22], udpIn[23]); //tertiary color
} }
if (version > 9 && version < 200 && udpIn[37] < 255) { //valid CCT/Kelvin value if (version > 9 && version < 200 && udpIn[37] < 255) { //valid CCT/Kelvin value
uint8_t cct = udpIn[38]; uint8_t cct = udpIn[38];
@ -291,24 +319,34 @@ void handleNotifications()
if (version < 200 && (receiveNotificationEffects || !someSel)) if (version < 200 && (receiveNotificationEffects || !someSel))
{ {
if (currentPlaylist>=0) unloadPlaylist(); if (currentPlaylist>=0) unloadPlaylist();
if (version>10) { if (version > 10 && receiveSegmentOptions) {
if (receiveSegmentOptions) { //does not sync start & stop
// will not sync start & stop
uint8_t srcSegs = udpIn[39]; uint8_t srcSegs = udpIn[39];
if (srcSegs > strip.getMaxSegments()) srcSegs = strip.getMaxSegments(); if (srcSegs > strip.getMaxSegments()) srcSegs = strip.getMaxSegments();
for (uint8_t i = 0; i < srcSegs; i++) { for (uint8_t i = 0; i < srcSegs; i++) {
WS2812FX::Segment& selseg = strip.getSegment(i); WS2812FX::Segment& selseg = strip.getSegment(i);
for (uint8_t j = 0; j<4; j++) selseg.setOption(j, (udpIn[40+i*3] >> j) & 0x01); //only take into account mirrored, selected, on, reversed uint16_t ofs = 41 + i*UDP_SEG_SIZE; //start of segment offset byte
strip.setSegment(i, selseg.start, selseg.stop, udpIn[42+i*3], udpIn[41+i*3], selseg.offset); // will also properly reset segments 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], (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]; if (udpIn[8] < strip.getModeCount()) effectCurrent = udpIn[8];
effectSpeed = udpIn[9]; effectSpeed = udpIn[9];
if (version > 2) effectIntensity = udpIn[16]; if (version > 2) effectIntensity = udpIn[16];
if (version > 4 && udpIn[19] < strip.getPaletteCount()) effectPalette = udpIn[19]; if (version > 4 && udpIn[19] < strip.getPaletteCount()) effectPalette = udpIn[19];
if (version > 5) }
{ if (version > 5) {
uint32_t t = (udpIn[25] << 24) | (udpIn[26] << 16) | (udpIn[27] << 8) | (udpIn[28]); uint32_t t = (udpIn[25] << 24) | (udpIn[26] << 16) | (udpIn[27] << 8) | (udpIn[28]);
t += PRESUMED_NETWORK_DELAY; //adjust trivially for network delay t += PRESUMED_NETWORK_DELAY; //adjust trivially for network delay
t -= millis(); t -= millis();
@ -462,7 +500,7 @@ void handleNotifications()
// API over UDP // API over UDP
udpIn[packetSize] = '\0'; udpIn[packetSize] = '\0';
if (!requestJSONBufferLock(18)) return; if (requestJSONBufferLock(18)) {
if (udpIn[0] >= 'A' && udpIn[0] <= 'Z') { //HTTP API if (udpIn[0] >= 'A' && udpIn[0] <= 'Z') { //HTTP API
String apireq = "win&"; String apireq = "win&";
apireq += (char*)udpIn; apireq += (char*)udpIn;
@ -474,6 +512,7 @@ void handleNotifications()
} }
releaseJSONBufferLock(); releaseJSONBufferLock();
} }
}
void setRealtimePixel(uint16_t i, byte r, byte g, byte b, byte w) void setRealtimePixel(uint16_t i, byte r, byte g, byte b, byte w)

View File

@ -187,7 +187,7 @@ void _setRandomColor(bool _sec, bool fromButton)
} else { } else {
colorHStoRGB(lastRandomIndex*256,255,col); colorHStoRGB(lastRandomIndex*256,255,col);
} }
if (fromButton) colorUpdated(2); if (fromButton) colorUpdated(CALL_MODE_BUTTON);
} }

View File

@ -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 (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). // 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: // 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 // Alternatively, with platformio pass your chosen flags to your custom build target in platformio_override.ini
@ -422,9 +423,6 @@ WLED_GLOBAL bool interfacesInited _INIT(false);
WLED_GLOBAL bool wasConnected _INIT(false); WLED_GLOBAL bool wasConnected _INIT(false);
// color // 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 WLED_GLOBAL byte lastRandomIndex _INIT(0); // used to save last random color so the new one is not the same
// transitions // transitions
@ -475,6 +473,7 @@ WLED_GLOBAL byte effectSpeed _INIT(128);
WLED_GLOBAL byte effectIntensity _INIT(128); WLED_GLOBAL byte effectIntensity _INIT(128);
WLED_GLOBAL byte effectPalette _INIT(0); WLED_GLOBAL byte effectPalette _INIT(0);
WLED_GLOBAL bool effectChanged _INIT(false); WLED_GLOBAL bool effectChanged _INIT(false);
WLED_GLOBAL bool colorChanged _INIT(false);
// network // network
WLED_GLOBAL bool udpConnected _INIT(false), udp2Connected _INIT(false), udpRgbConnected _INIT(false); WLED_GLOBAL bool udpConnected _INIT(false), udp2Connected _INIT(false), udpRgbConnected _INIT(false);