`+
@@ -1666,13 +1668,17 @@ function setEffectParameters(idx)
paOnOff[0] = paOnOff[0].substring(0,dPos);
}
if (paOnOff.length>0 && paOnOff[0] != "!") text = paOnOff[0];
+ gId("adPal").classList.remove("hide");
+ if (lastinfo.cpalcount>0) gId("rmPal").classList.remove("hide");
} else {
// disable palette list
text += ' not used';
palw.style.display = "none";
+ gId("adPal").classList.add("hide");
+ gId("rmPal").classList.add("hide");
// Close palette dialog if not available
- if (gId("palw").lastElementChild.tagName == "DIALOG") {
- gId("palw").lastElementChild.close();
+ if (palw.lastElementChild.tagName == "DIALOG") {
+ palw.lastElementChild.close();
}
}
pall.innerHTML = icon + text;
@@ -1887,7 +1893,7 @@ function makeSeg()
function resetUtil(off=false)
{
gId('segutil').innerHTML = `
`
- + '
'
+ + '
'
+ `
Add segment
`
+ '
'
+ `
`
@@ -1901,15 +1907,16 @@ function resetUtil(off=false)
if (lSeg>2) d.querySelectorAll("#Segments .pop").forEach((e)=>{e.classList.remove("hide");});
}
-function makePlSel(el, incPl=false)
+function makePlSel(p, el)
{
var plSelContent = "";
delete pJson["0"]; // remove filler preset
Object.entries(pJson).sort(cmpP).forEach((a)=>{
var n = a[1].n ? a[1].n : "Preset " + a[0];
+ if (isPlaylist(a[1])) n += ' ▶'; // mark playlist
if (cfg.comp.idsort) n = a[0] + ' ' + n;
- if (!(!incPl && a[1].playlist && a[1].playlist.ps)) // skip playlists, sub-playlists not yet supported
- plSelContent += `
${n} `;
+ // skip endless playlists and itself
+ if (!isPlaylist(a[1]) || (a[1].playlist.repeat > 0 && a[0]!=p)) plSelContent += `
${n} `;
});
return plSelContent;
}
@@ -1934,21 +1941,23 @@ function refreshPlE(p)
});
}
-// p: preset ID, i: ps index
+// p: preset ID, i: playlist item index
function addPl(p,i)
{
- plJson[p].ps.splice(i+1,0,0);
- plJson[p].dur.splice(i+1,0,plJson[p].dur[i]);
- plJson[p].transition.splice(i+1,0,plJson[p].transition[i]);
+ const pl = plJson[p];
+ pl.ps.splice(i+1,0,1);
+ pl.dur.splice(i+1,0,pl.dur[i]);
+ pl.transition.splice(i+1,0,pl.transition[i]);
refreshPlE(p);
}
function delPl(p,i)
{
- if (plJson[p].ps.length < 2) return;
- plJson[p].ps.splice(i,1);
- plJson[p].dur.splice(i,1);
- plJson[p].transition.splice(i,1);
+ const pl = plJson[p];
+ if (pl.ps.length < 2) return;
+ pl.ps.splice(i,1);
+ pl.dur.splice(i,1);
+ pl.transition.splice(i,1);
refreshPlE(p);
}
@@ -1965,6 +1974,13 @@ function pleDur(p,i,field)
function pleTr(p,i,field)
{
+ const du = gId(`pl${p}du${i}`);
+ const dv = parseFloat(du.value);
+ if (dv > 0) {
+ field.max = dv;
+ if (parseFloat(field.value) > dv)
+ field.value = du.value;
+ }
if (field.validity.valid)
plJson[p].transition[i] = Math.floor(field.value*10);
}
@@ -1984,6 +2000,17 @@ function plR(p)
}
}
+function plM(p)
+{
+ const man = gId(`pl${p}manual`).checked;
+ plJson[p].dur.forEach((e,i)=>{
+ const d = gId(`pl${p}du${i}`);
+ plJson[p].dur[i] = e = man ? 0 : 100;
+ d.value = e/10; // 10s default
+ d.readOnly = man;
+ });
+}
+
function makeP(i,pl)
{
var content = "";
@@ -1997,12 +2024,17 @@ function makeP(i,pl)
r: false,
end: 0
};
- var rep = plJson[i].repeat ? plJson[i].repeat : 0;
+ const rep = plJson[i].repeat ? plJson[i].repeat : 0;
+ const man = plJson[i].dur == 0;
content =
`
Shuffle
+
Manual advance
+
+
+
Repeat indefinitely
0?"":"checked"}>
@@ -2013,7 +2045,7 @@ function makeP(i,pl)
None
Restore preset
-${makePlSel(plJson[i].end?plJson[i].end:0, true)}
+${makePlSel(i, plJson[i].end?plJson[i].end:0)}
`;
@@ -2086,25 +2118,26 @@ function makePUtil()
function makePlEntry(p,i)
{
+ const man = gId(`pl${p}manual`).checked;
return `
@@ -2657,28 +2690,28 @@ function fromRgb()
var g = gId('sliderG').value;
var b = gId('sliderB').value;
setPicker(`rgb(${r},${g},${b})`);
- let cd = gId('csl').children; // color slots
- cd[csel].dataset.r = r;
- cd[csel].dataset.g = g;
- cd[csel].dataset.b = b;
- setCSL(cd[csel]);
+ let cd = gId('csl').children[csel]; // color slots
+ cd.dataset.r = r;
+ cd.dataset.g = g;
+ cd.dataset.b = b;
+ setCSL(cd);
}
function fromW()
{
let w = gId('sliderW');
- let cd = gId('csl').children; // color slots
- cd[csel].dataset.w = w.value;
- setCSL(cd[csel]);
+ let cd = gId('csl').children[csel]; // color slots
+ cd.dataset.w = w.value;
+ setCSL(cd);
updateTrail(w);
}
// sr 0: from RGB sliders, 1: from picker, 2: from hex
function setColor(sr)
{
- var cd = gId('csl').children; // color slots
- let cdd = cd[csel].dataset;
- let w = 0, r,g,b;
+ var cd = gId('csl').children[csel]; // color slots
+ let cdd = cd.dataset;
+ let w = parseInt(cdd.w), r = parseInt(cdd.r), g = parseInt(cdd.g), b = parseInt(cdd.b);
if (sr == 1 && isRgbBlack(cdd)) cpick.color.setChannel('hsv', 'v', 100);
if (sr != 2 && hasWhite) w = parseInt(gId('sliderW').value);
var col = cpick.color.rgb;
@@ -2686,7 +2719,7 @@ function setColor(sr)
cdd.g = g = hasRGB ? col.g : w;
cdd.b = b = hasRGB ? col.b : w;
cdd.w = w;
- setCSL(cd[csel]);
+ setCSL(cd);
var obj = {"seg": {"col": [[],[],[]]}};
obj.seg.col[csel] = [r, g, b, w];
requestJson(obj);
diff --git a/wled00/data/settings_leds.htm b/wled00/data/settings_leds.htm
index 0900be399..a45ed050e 100644
--- a/wled00/data/settings_leds.htm
+++ b/wled00/data/settings_leds.htm
@@ -24,6 +24,7 @@
function is16b(t) { return !!(gT(t).c & 0x10); } // is digital 16 bit type
function mustR(t) { return !!(gT(t).c & 0x20); } // Off refresh is mandatory
function numPins(t){ return Math.max(gT(t).t.length, 1); } // type length determines number of GPIO pins
+ function chrID(x) { return String.fromCharCode((x<10?48:55)+x); }
function S() {
getLoc();
loadJS(getURL('/settings/s.js?p=2'), false, ()=>{
@@ -42,10 +43,10 @@
if (loc) d.Sf.action = getURL('/settings/leds');
}
function bLimits(b,v,p,m,l,o=5,d=2,a=6) {
- oMaxB = maxB = b; // maxB - max buses (can be changed if using ESP32 parallel I2S): 19 - ESP32, 14 - S3/S2, 6 - C3, 4 - 8266
- maxD = d; // maxD - max digital channels (can be changed if using ESP32 parallel I2S): 16 - ESP32, 12 - S3/S2, 2 - C3, 3 - 8266
+ oMaxB = maxB = b; // maxB - max buses (can be changed if using ESP32 parallel I2S): 20 - ESP32, 14 - S3/S2, 6 - C3, 4 - 8266
+ maxD = d; // maxD - max digital channels (can be changed if using ESP32 parallel I2S): 17 - ESP32, 12 - S3/S2, 2 - C3, 3 - 8266
maxA = a; // maxA - max analog channels: 16 - ESP32, 8 - S3/S2, 6 - C3, 5 - 8266
- maxV = v; // maxV - min virtual buses: 4 - ESP32/S3, 3 - S2/C3, 2 - ESP8266
+ maxV = v; // maxV - min virtual buses: 6 - ESP32/S3, 4 - S2/C3, 3 - ESP8266 (only used to distinguish S2/S3)
maxPB = p; // maxPB - max LEDs per bus
maxM = m; // maxM - max LED memory
maxL = l; // maxL - max LEDs (will serve to determine ESP >1664 == ESP32)
@@ -138,7 +139,7 @@
gId("ppldis").style.display = ppl ? 'inline' : 'none';
// set PPL minimum value and clear actual PPL limit if ABL is disabled
d.Sf.querySelectorAll("#mLC input[name^=MA]").forEach((i,x)=>{
- var n = String.fromCharCode((x<10?48:55)+x);
+ var n = chrID(x);
gId("PSU"+n).style.display = ppl ? "inline" : "none";
const t = parseInt(d.Sf["LT"+n].value); // LED type SELECT
const c = parseInt(d.Sf["LC"+n].value); //get LED count
@@ -169,7 +170,7 @@
// select appropriate LED current
d.Sf.querySelectorAll("#mLC select[name^=LAsel]").forEach((sel,x)=>{
sel.value = 0; // set custom
- var n = String.fromCharCode((x<10?48:55)+x);
+ var n = chrID(x);
if (en)
switch (parseInt(d.Sf["LA"+n].value)) {
case 0: break; // disable ABL
@@ -359,8 +360,9 @@
gId("prl").classList.add("hide");
} else
gId("prl").classList.remove("hide");
+ // S2 supports mono I2S as well as parallel so we need to take that into account; S3 only supports parallel
maxD = (S2 || S3 ? 4 : 8) + (d.Sf["PR"].checked ? 8 : S2); // TODO: use bLimits() : 4/8RMT + (x1/x8 parallel) I2S1
- maxB = oMaxB - (d.Sf["PR"].checked ? 0 : 7 + S3); // S2 (maxV==3) does support single I2S
+ maxB = oMaxB - (d.Sf["PR"].checked ? 0 : 7 + S3); // S2 (maxV==4) does support mono I2S
}
// distribute ABL current if not using PPL
enPPL(sDI);
@@ -399,7 +401,7 @@
}
function lastEnd(i) {
if (i-- < 1) return 0;
- var s = String.fromCharCode((i<10?48:55)+i);
+ var s = chrID(i);
v = parseInt(d.getElementsByName("LS"+s)[0].value) + parseInt(d.getElementsByName("LC"+s)[0].value);
var t = parseInt(d.getElementsByName("LT"+s)[0].value);
if (isPWM(t)) v = 1; //PWM busses
@@ -421,8 +423,8 @@
if (isVir(t)) virtB++;
});
- if ((n==1 && i>=maxB+maxV) || (n==-1 && i==0)) return;
- var s = String.fromCharCode((i<10?48:55)+i);
+ if ((n==1 && i>=36) || (n==-1 && i==0)) return; // used to be i>=maxB+maxV when virtual buses were limited (now :"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ")
+ var s = chrID(i);
if (n==1) {
// npm run build has trouble minimizing spaces inside string
@@ -495,7 +497,7 @@ mA/LED:
o[i].querySelector("[name^=LT]").disabled = false;
}
- gId("+").style.display = (i0) ? "inline":"none";
if (!init) {
@@ -506,7 +508,7 @@ mA/LED:
function addCOM(start=0,len=1,co=0) {
var i = gEBCN("com_entry").length;
if (i >= maxCO) return;
- var s = String.fromCharCode((i<10?48:55)+i);
+ var s = chrID(i);
var b = `
${i+1}: Start:
@@ -560,7 +562,7 @@ Swap:
function addBtn(i,p,t) {
var c = gId("btns").innerHTML;
- var s = String.fromCharCode((i<10?48:55)+i);
+ var s = chrID(i);
c += `Button ${i} GPIO: `;
c += ` `
c += `Disabled `;
@@ -584,8 +586,10 @@ Swap:
function checkSi() { //on load, checks whether there are custom start fields
var cs = false;
for (var i=1; i < gEBCN("iST").length; i++) {
- var v = parseInt(gId("ls"+(i-1)).value) + parseInt(gN("LC"+(i-1)).value);
- if (v != parseInt(gId("ls"+i).value)) {cs = true; startsDirty[i] = true;}
+ var s = chrID(i);
+ var p = chrID(i-1); // cover edge case 'A' previous char being '9'
+ var v = parseInt(gId("ls"+p).value) + parseInt(gN("LC"+p).value);
+ if (v != parseInt(gId("ls"+s).value)) {cs = true; startsDirty[i] = true;}
}
if (gId("ls0") && parseInt(gId("ls0").value) != 0) {cs = true; startsDirty[0] = true;}
gId("si").checked = cs;
@@ -614,22 +618,32 @@ Swap:
function receivedText(e) {
let lines = e.target.result;
- var c = JSON.parse(lines);
+ let c = JSON.parse(lines);
if (c.hw) {
if (c.hw.led) {
- for (var i=0; i<10; i++) addLEDs(-1);
- var l = c.hw.led;
+ // remove all existing outputs
+ for (const i=0; i<36; i++) addLEDs(-1); // was i{
addLEDs(1);
for (var j=0; j>4) & 0x0F;
+ d.getElementsByName("SP"+i)[0].value = v.freq;
+ d.getElementsByName("LA"+i)[0].value = v.ledma;
+ d.getElementsByName("MA"+i)[0].value = v.maxpwr;
});
+ d.getElementsByName("PR")[0].checked = l.prl | 0;
+ d.getElementsByName("LD")[0].checked = l.ld;
+ d.getElementsByName("MA")[0].value = l.maxpwr;
+ d.getElementsByName("ABL")[0].checked = l.maxpwr > 0;
}
if(c.hw.com) {
resetCOM();
@@ -637,22 +651,28 @@ Swap:
addCOM(e.start, e.len, e.order);
});
}
- if (c.hw.btn) {
- var b = c.hw.btn;
+ let b = c.hw.btn;
+ if (b) {
if (Array.isArray(b.ins)) gId("btns").innerHTML = "";
b.ins.forEach((v,i,a)=>{
addBtn(i,v.pin[0],v.type);
});
d.getElementsByName("TT")[0].value = b.tt;
}
- if (c.hw.ir) {
- d.getElementsByName("IR")[0].value = c.hw.ir.pin;
- d.getElementsByName("IT")[0].value = c.hw.ir.type;
+ let ir = c.hw.ir;
+ if (ir) {
+ d.getElementsByName("IR")[0].value = ir.pin;
+ d.getElementsByName("IT")[0].value = ir.type;
}
- if (c.hw.relay) {
- d.getElementsByName("RL")[0].value = c.hw.relay.pin;
- d.getElementsByName("RM")[0].checked = c.hw.relay.rev;
- d.getElementsByName("RO")[0].checked = c.hw.relay.odrain;
+ let rl = c.hw.relay;
+ if (rl) {
+ d.getElementsByName("RL")[0].value = rl.pin;
+ d.getElementsByName("RM")[0].checked = rl.rev;
+ d.getElementsByName("RO")[0].checked = rl.odrain;
+ }
+ let li = c.light;
+ if (li) {
+ d.getElementsByName("MS")[0].checked = li.aseg;
}
UI();
}
diff --git a/wled00/dmx_input.h b/wled00/dmx_input.h
index 7845778d7..29f015bdc 100644
--- a/wled00/dmx_input.h
+++ b/wled00/dmx_input.h
@@ -19,6 +19,9 @@ public:
void disable();
void enable();
+ /// True if dmx is currently connected
+ bool isConnected() const { return connected; }
+
private:
/// @return true if rdm identify is active
bool isIdentifyOn() const;
diff --git a/wled00/fcn_declare.h b/wled00/fcn_declare.h
index f04e88459..497a775ee 100644
--- a/wled00/fcn_declare.h
+++ b/wled00/fcn_declare.h
@@ -327,6 +327,7 @@ void serializePlaylist(JsonObject obj);
//presets.cpp
const char *getPresetsFileName(bool persistent = true);
+bool presetNeedsSaving();
void initPresetsFile();
void handlePresets();
bool applyPreset(byte index, byte callMode = CALL_MODE_DIRECT_CHANGE);
diff --git a/wled00/hue.cpp b/wled00/hue.cpp
index 950c54896..d5fcb7cb9 100644
--- a/wled00/hue.cpp
+++ b/wled00/hue.cpp
@@ -195,9 +195,9 @@ void onHueData(void* arg, AsyncClient* client, void *data, size_t len)
{
switch(hueColormode)
{
- case 1: if (hueX != hueXLast || hueY != hueYLast) colorXYtoRGB(hueX,hueY,col); hueXLast = hueX; hueYLast = hueY; break;
- case 2: if (hueHue != hueHueLast || hueSat != hueSatLast) colorHStoRGB(hueHue,hueSat,col); hueHueLast = hueHue; hueSatLast = hueSat; break;
- case 3: if (hueCt != hueCtLast) colorCTtoRGB(hueCt,col); hueCtLast = hueCt; break;
+ case 1: if (hueX != hueXLast || hueY != hueYLast) colorXYtoRGB(hueX,hueY,colPri); hueXLast = hueX; hueYLast = hueY; break;
+ case 2: if (hueHue != hueHueLast || hueSat != hueSatLast) colorHStoRGB(hueHue,hueSat,colPri); hueHueLast = hueHue; hueSatLast = hueSat; break;
+ case 3: if (hueCt != hueCtLast) colorCTtoRGB(hueCt,colPri); hueCtLast = hueCt; break;
}
}
hueReceived = true;
diff --git a/wled00/json.cpp b/wled00/json.cpp
index 200307464..24988be15 100644
--- a/wled00/json.cpp
+++ b/wled00/json.cpp
@@ -91,14 +91,20 @@ bool deserializeSegment(JsonObject elem, byte it, byte presetId)
}
}
- uint16_t grp = elem["grp"] | seg.grouping;
- uint16_t spc = elem[F("spc")] | seg.spacing;
- uint16_t of = seg.offset;
- uint8_t soundSim = elem["si"] | seg.soundSim;
- uint8_t map1D2D = elem["m12"] | seg.map1D2D;
- uint8_t set = elem[F("set")] | seg.set;
- seg.set = constrain(set, 0, 3);
- seg.soundSim = constrain(soundSim, 0, 3);
+ uint16_t grp = elem["grp"] | seg.grouping;
+ uint16_t spc = elem[F("spc")] | seg.spacing;
+ uint16_t of = seg.offset;
+ uint8_t soundSim = elem["si"] | seg.soundSim;
+ uint8_t map1D2D = elem["m12"] | seg.map1D2D;
+ uint8_t set = elem[F("set")] | seg.set;
+ bool selected = getBoolVal(elem["sel"], seg.selected);
+ bool reverse = getBoolVal(elem["rev"], seg.reverse);
+ bool mirror = getBoolVal(elem["mi"] , seg.mirror);
+ #ifndef WLED_DISABLE_2D
+ bool reverse_y = getBoolVal(elem["rY"] , seg.reverse_y);
+ bool mirror_y = getBoolVal(elem["mY"] , seg.mirror_y);
+ bool transpose = getBoolVal(elem[F("tp")], seg.transpose);
+ #endif
int len = (stop > start) ? stop - start : 1;
int offset = elem[F("of")] | INT32_MAX;
@@ -200,20 +206,16 @@ bool deserializeSegment(JsonObject elem, byte it, byte presetId)
}
#endif
+ //seg.map1D2D = constrain(map1D2D, 0, 7); // done in setGeometry()
+ seg.set = constrain(set, 0, 3);
+ seg.soundSim = constrain(soundSim, 0, 3);
+ seg.selected = selected;
+ seg.reverse = reverse;
+ seg.mirror = mirror;
#ifndef WLED_DISABLE_2D
- bool reverse = seg.reverse;
- bool mirror = seg.mirror;
- #endif
- seg.selected = getBoolVal(elem["sel"], seg.selected);
- seg.reverse = getBoolVal(elem["rev"], seg.reverse);
- seg.mirror = getBoolVal(elem["mi"] , seg.mirror);
- #ifndef WLED_DISABLE_2D
- bool reverse_y = seg.reverse_y;
- bool mirror_y = seg.mirror_y;
- seg.reverse_y = getBoolVal(elem["rY"] , seg.reverse_y);
- seg.mirror_y = getBoolVal(elem["mY"] , seg.mirror_y);
- seg.transpose = getBoolVal(elem[F("tp")], seg.transpose);
- if (seg.is2D() && seg.map1D2D == M12_pArc && (reverse != seg.reverse || reverse_y != seg.reverse_y || mirror != seg.mirror || mirror_y != seg.mirror_y)) seg.fill(BLACK); // clear entire segment (in case of Arc 1D to 2D expansion)
+ seg.reverse_y = reverse_y;
+ seg.mirror_y = mirror_y;
+ seg.transpose = transpose;
#endif
byte fx = seg.mode;
@@ -392,35 +394,38 @@ bool deserializeState(JsonObject root, byte callMode, byte presetId)
int it = 0;
JsonVariant segVar = root["seg"];
- if (!segVar.isNull()) strip.suspend();
- if (segVar.is())
- {
- int id = segVar["id"] | -1;
- //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;
- for (size_t s = 0; s < strip.getSegmentsNum(); s++) {
- Segment &sg = strip.getSegment(s);
- if (sg.isActive() && sg.isSelected()) {
- deserializeSegment(segVar, s, presetId);
- //didSet = true;
+ if (!segVar.isNull()) {
+ // we may be called during strip.service() so we must not modify segments while effects are executing
+ strip.suspend();
+ const unsigned long start = millis();
+ while (strip.isServicing() && millis() - start < strip.getFrameTime()) yield(); // wait until frame is over
+ #ifdef WLED_DEBUG
+ if (millis() - start > 0) DEBUG_PRINTLN(F("JSON: Waited for strip to finish servicing."));
+ #endif
+ if (segVar.is()) {
+ int id = segVar["id"] | -1;
+ //if "seg" is not an array and ID not specified, apply to all selected/checked segments
+ if (id < 0) {
+ //apply all selected segments
+ for (size_t s = 0; s < strip.getSegmentsNum(); s++) {
+ Segment &sg = strip.getSegment(s);
+ if (sg.isActive() && sg.isSelected()) {
+ deserializeSegment(segVar, s, presetId);
+ }
}
+ } else {
+ deserializeSegment(segVar, id, presetId); //apply only the segment with the specified ID
}
- //TODO: not sure if it is good idea to change first active but unselected segment
- //if (!didSet) deserializeSegment(segVar, strip.getMainSegmentId(), presetId);
} else {
- deserializeSegment(segVar, id, presetId); //apply only the segment with the specified ID
+ size_t deleted = 0;
+ JsonArray segs = segVar.as();
+ for (JsonObject elem : segs) {
+ if (deserializeSegment(elem, it++, presetId) && !elem["stop"].isNull() && elem["stop"]==0) deleted++;
+ }
+ if (strip.getSegmentsNum() > 3 && deleted >= strip.getSegmentsNum()/2U) strip.purgeSegments(); // batch deleting more than half segments
}
- } else {
- size_t deleted = 0;
- JsonArray segs = segVar.as();
- for (JsonObject elem : segs) {
- if (deserializeSegment(elem, it++, presetId) && !elem["stop"].isNull() && elem["stop"]==0) deleted++;
- }
- if (strip.getSegmentsNum() > 3 && deleted >= strip.getSegmentsNum()/2U) strip.purgeSegments(); // batch deleting more than half segments
+ strip.resume();
}
- strip.resume();
UsermodManager::readFromJsonState(root);
diff --git a/wled00/led.cpp b/wled00/led.cpp
index 9731da655..2d2f5b6f2 100644
--- a/wled00/led.cpp
+++ b/wled00/led.cpp
@@ -9,10 +9,10 @@ void setValuesFromFirstSelectedSeg() { setValuesFromSegment(strip.getFirstSelect
void setValuesFromSegment(uint8_t s)
{
Segment& seg = strip.getSegment(s);
- col[0] = R(seg.colors[0]);
- col[1] = G(seg.colors[0]);
- col[2] = B(seg.colors[0]);
- col[3] = W(seg.colors[0]);
+ colPri[0] = R(seg.colors[0]);
+ colPri[1] = G(seg.colors[0]);
+ colPri[2] = B(seg.colors[0]);
+ colPri[3] = W(seg.colors[0]);
colSec[0] = R(seg.colors[1]);
colSec[1] = G(seg.colors[1]);
colSec[2] = B(seg.colors[1]);
@@ -39,7 +39,7 @@ void applyValuesToSelectedSegs()
if (effectIntensity != selsegPrev.intensity) {seg.intensity = effectIntensity; stateChanged = true;}
if (effectPalette != selsegPrev.palette) {seg.setPalette(effectPalette);}
if (effectCurrent != selsegPrev.mode) {seg.setMode(effectCurrent);}
- uint32_t col0 = RGBW32( col[0], col[1], col[2], col[3]);
+ uint32_t col0 = RGBW32(colPri[0], colPri[1], colPri[2], colPri[3]);
uint32_t col1 = RGBW32(colSec[0], colSec[1], colSec[2], colSec[3]);
if (col0 != selsegPrev.colors[0]) {seg.setColor(0, col0);}
if (col1 != selsegPrev.colors[1]) {seg.setColor(1, col1);}
@@ -112,10 +112,11 @@ void stateUpdated(byte callMode) {
}
}
+ unsigned long now = millis();
if (callMode != CALL_MODE_NO_NOTIFY && nightlightActive && (nightlightMode == NL_MODE_FADE || nightlightMode == NL_MODE_COLORFADE)) {
briNlT = bri;
- nightlightDelayMs -= (millis() - nightlightStartTime);
- nightlightStartTime = millis();
+ nightlightDelayMs -= (now - nightlightStartTime);
+ nightlightStartTime = now;
}
if (briT == 0) {
if (callMode != CALL_MODE_NOTIFICATION) strip.resetTimebase(); //effect start from beginning
@@ -141,7 +142,7 @@ void stateUpdated(byte callMode) {
} else
strip.setTransitionMode(true); // force all segments to transition mode
transitionActive = true;
- transitionStartTime = millis();
+ transitionStartTime = now;
}
@@ -150,14 +151,14 @@ void updateInterfaces(uint8_t callMode) {
sendDataWs();
lastInterfaceUpdate = millis();
- interfaceUpdateCallMode = 0; //disable further updates
+ interfaceUpdateCallMode = CALL_MODE_INIT; //disable further updates
if (callMode == CALL_MODE_WS_SEND) return;
#ifndef WLED_DISABLE_ALEXA
if (espalexaDevice != nullptr && callMode != CALL_MODE_ALEXA) {
espalexaDevice->setValue(bri);
- espalexaDevice->setColor(col[0], col[1], col[2]);
+ espalexaDevice->setColor(colPri[0], colPri[1], colPri[2]);
}
#endif
#ifndef WLED_DISABLE_MQTT
@@ -211,7 +212,7 @@ void handleNightlight() {
nightlightDelayMs = (unsigned)(nightlightDelayMins*60000);
nightlightActiveOld = true;
briNlT = bri;
- for (unsigned i=0; i<4; i++) colNlT[i] = col[i]; // remember starting color
+ for (unsigned i=0; i<4; i++) colNlT[i] = colPri[i]; // remember starting color
if (nightlightMode == NL_MODE_SUN)
{
//save current
@@ -236,7 +237,7 @@ void handleNightlight() {
bri = briNlT + ((nightlightTargetBri - briNlT)*nper);
if (nightlightMode == NL_MODE_COLORFADE) // color fading only is enabled with "NF=2"
{
- for (unsigned i=0; i<4; i++) col[i] = colNlT[i]+ ((colSec[i] - colNlT[i])*nper); // fading from actual color to secondary color
+ for (unsigned i=0; i<4; i++) colPri[i] = colNlT[i]+ ((colSec[i] - colNlT[i])*nper); // fading from actual color to secondary color
}
colorUpdated(CALL_MODE_NO_NOTIFY);
}
diff --git a/wled00/mqtt.cpp b/wled00/mqtt.cpp
index 8cbe79093..a462881ec 100644
--- a/wled00/mqtt.cpp
+++ b/wled00/mqtt.cpp
@@ -103,7 +103,7 @@ static void onMqttMessage(char* topic, char* payload, AsyncMqttClientMessageProp
//Prefix is stripped from the topic at this point
if (strcmp_P(topic, PSTR("/col")) == 0) {
- colorFromDecOrHexString(col, payloadStr);
+ colorFromDecOrHexString(colPri, payloadStr);
colorUpdated(CALL_MODE_DIRECT_CHANGE);
} else if (strcmp_P(topic, PSTR("/api")) == 0) {
if (requestJSONBufferLock(15)) {
@@ -169,7 +169,7 @@ void publishMqtt()
strcat_P(subuf, PSTR("/g"));
mqtt->publish(subuf, 0, retainMqttMsg, s); // optionally retain message (#2263)
- sprintf_P(s, PSTR("#%06X"), (col[3] << 24) | (col[0] << 16) | (col[1] << 8) | (col[2]));
+ sprintf_P(s, PSTR("#%06X"), (colPri[3] << 24) | (colPri[0] << 16) | (colPri[1] << 8) | (colPri[2]));
strlcpy(subuf, mqttDeviceTopic, MQTT_MAX_TOPIC_LEN + 1);
strcat_P(subuf, PSTR("/c"));
mqtt->publish(subuf, 0, retainMqttMsg, s); // optionally retain message (#2263)
diff --git a/wled00/playlist.cpp b/wled00/playlist.cpp
index 1908334d7..2e51503e3 100644
--- a/wled00/playlist.cpp
+++ b/wled00/playlist.cpp
@@ -10,19 +10,19 @@ typedef struct PlaylistEntry {
uint16_t tr; //Duration of the transition TO this entry (in tenths of seconds)
} ple;
-byte playlistRepeat = 1; //how many times to repeat the playlist (0 = infinitely)
-byte playlistEndPreset = 0; //what preset to apply after playlist end (0 = stay on last preset)
-byte playlistOptions = 0; //bit 0: shuffle playlist after each iteration. bits 1-7 TBD
+static byte playlistRepeat = 1; //how many times to repeat the playlist (0 = infinitely)
+static byte playlistEndPreset = 0; //what preset to apply after playlist end (0 = stay on last preset)
+static byte playlistOptions = 0; //bit 0: shuffle playlist after each iteration. bits 1-7 TBD
-PlaylistEntry *playlistEntries = nullptr;
-byte playlistLen; //number of playlist entries
-int8_t playlistIndex = -1;
-uint16_t playlistEntryDur = 0; //duration of the current entry in tenths of seconds
+static PlaylistEntry *playlistEntries = nullptr;
+static byte playlistLen; //number of playlist entries
+static int8_t playlistIndex = -1;
+static uint16_t playlistEntryDur = 0; //duration of the current entry in tenths of seconds
//values we need to keep about the parent playlist while inside sub-playlist
-//int8_t parentPlaylistIndex = -1;
-//byte parentPlaylistRepeat = 0;
-//byte parentPlaylistPresetId = 0; //for re-loading
+static int16_t parentPlaylistIndex = -1;
+static byte parentPlaylistRepeat = 0;
+static byte parentPlaylistPresetId = 0; //for re-loading
void shufflePlaylist() {
@@ -54,6 +54,12 @@ void unloadPlaylist() {
int16_t loadPlaylist(JsonObject playlistObj, byte presetId) {
+ if (currentPlaylist > 0 && parentPlaylistPresetId > 0) return -1; // we are already in nested playlist, do nothing
+ if (currentPlaylist > 0) {
+ parentPlaylistIndex = playlistIndex;
+ parentPlaylistRepeat = playlistRepeat;
+ parentPlaylistPresetId = currentPlaylist;
+ }
unloadPlaylist();
JsonArray presets = playlistObj["ps"];
@@ -79,7 +85,7 @@ int16_t loadPlaylist(JsonObject playlistObj, byte presetId) {
} else {
for (int dur : durations) {
if (it >= playlistLen) break;
- playlistEntries[it].dur = (dur > 1) ? dur : 100;
+ playlistEntries[it].dur = constrain(dur, 0, 65530);
it++;
}
}
@@ -117,6 +123,19 @@ int16_t loadPlaylist(JsonObject playlistObj, byte presetId) {
shuffle = shuffle || playlistObj["r"];
if (shuffle) playlistOptions |= PL_OPTION_SHUFFLE;
+ if (parentPlaylistPresetId == 0 && parentPlaylistIndex > -1) {
+ // we are re-loading playlist when returning from nested playlist
+ playlistIndex = parentPlaylistIndex;
+ playlistRepeat = parentPlaylistRepeat;
+ parentPlaylistIndex = -1;
+ parentPlaylistRepeat = 0;
+ } else if (rep == 0) {
+ // endless playlist will never return to parent so erase parent information if it was called from it
+ parentPlaylistPresetId = 0;
+ parentPlaylistIndex = -1;
+ parentPlaylistRepeat = 0;
+ }
+
currentPlaylist = presetId;
DEBUG_PRINTLN(F("Playlist loaded."));
return currentPlaylist;
@@ -127,7 +146,7 @@ void handlePlaylist() {
static unsigned long presetCycledTime = 0;
if (currentPlaylist < 0 || playlistEntries == nullptr) return;
-if (millis() - presetCycledTime > (100 * playlistEntryDur) || doAdvancePlaylist) {
+ if ((playlistEntryDur < UINT16_MAX && millis() - presetCycledTime > 100 * playlistEntryDur) || doAdvancePlaylist) {
presetCycledTime = millis();
if (bri == 0 || nightlightActive) return;
@@ -137,7 +156,10 @@ if (millis() - presetCycledTime > (100 * playlistEntryDur) || doAdvancePlaylist)
if (!playlistIndex) {
if (playlistRepeat == 1) { //stop if all repetitions are done
unloadPlaylist();
- if (playlistEndPreset) applyPresetFromPlaylist(playlistEndPreset);
+ if (parentPlaylistPresetId > 0) {
+ applyPresetFromPlaylist(parentPlaylistPresetId); // reload previous playlist (unfortunately asynchronous)
+ parentPlaylistPresetId = 0; // reset previous playlist but do not reset Index or Repeat (they will be loaded & reset in loadPlaylist())
+ } else if (playlistEndPreset) applyPresetFromPlaylist(playlistEndPreset);
return;
}
if (playlistRepeat > 1) playlistRepeat--; // decrease repeat count on each index reset if not an endless playlist
@@ -147,7 +169,7 @@ if (millis() - presetCycledTime > (100 * playlistEntryDur) || doAdvancePlaylist)
jsonTransitionOnce = true;
strip.setTransition(playlistEntries[playlistIndex].tr * 100);
- playlistEntryDur = playlistEntries[playlistIndex].dur;
+ playlistEntryDur = playlistEntries[playlistIndex].dur > 0 ? playlistEntries[playlistIndex].dur : UINT16_MAX;
applyPresetFromPlaylist(playlistEntries[playlistIndex].preset);
doAdvancePlaylist = false;
}
diff --git a/wled00/presets.cpp b/wled00/presets.cpp
index 15eed3e46..54f052637 100644
--- a/wled00/presets.cpp
+++ b/wled00/presets.cpp
@@ -22,6 +22,10 @@ const char *getPresetsFileName(bool persistent) {
return persistent ? presets_json : tmp_json;
}
+bool presetNeedsSaving() {
+ return presetToSave;
+}
+
static void doSaveState() {
bool persist = (presetToSave < 251);
@@ -269,7 +273,7 @@ void savePreset(byte index, const char* pname, JsonObject sObj)
quickLoad = nullptr;
} else {
// store playlist
- // WARNING: playlist will be loaded in json.cpp after this call and will have repeat counter increased by 1
+ // WARNING: playlist will be loaded in json.cpp after this call and will have repeat counter increased by 1 it will also be randomised if selected
includeBri = true; // !sObj["on"].isNull();
playlistSave = true;
}
diff --git a/wled00/set.cpp b/wled00/set.cpp
index b2340066f..00333788d 100644
--- a/wled00/set.cpp
+++ b/wled00/set.cpp
@@ -145,8 +145,8 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
#endif
bool busesChanged = false;
- for (int s = 0; s < WLED_MAX_BUSSES+WLED_MIN_VIRTUAL_BUSSES; s++) {
- int offset = s < 10 ? 48 : 55;
+ for (int s = 0; s < 36; s++) { // theoretical limit is 36 : "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+ int offset = s < 10 ? '0' : 'A';
char lp[4] = "L0"; lp[2] = offset+s; lp[3] = 0; //ascii 0-9 //strip data pin
char lc[4] = "LC"; lc[2] = offset+s; lc[3] = 0; //strip length
char co[4] = "CO"; co[2] = offset+s; co[3] = 0; //strip color order
@@ -161,11 +161,11 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
char la[4] = "LA"; la[2] = offset+s; la[3] = 0; //LED mA
char ma[4] = "MA"; ma[2] = offset+s; ma[3] = 0; //max mA
if (!request->hasArg(lp)) {
- DEBUG_PRINTF_P(PSTR("No data for %d\n"), s);
+ DEBUG_PRINTF_P(PSTR("# of buses: %d\n"), s+1);
break;
}
for (int i = 0; i < 5; i++) {
- lp[1] = offset+i;
+ lp[1] = '0'+i;
if (!request->hasArg(lp)) break;
pins[i] = (request->arg(lp).length() > 0) ? request->arg(lp).toInt() : 255;
}
@@ -212,7 +212,7 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
type |= request->hasArg(rf) << 7; // off refresh override
// actual finalization is done in WLED::loop() (removing old busses and adding new)
// this may happen even before this loop is finished so we do "doInitBusses" after the loop
- busConfigs.push_back(std::move(BusConfig(type, pins, start, length, colorOrder | (channelSwap<<4), request->hasArg(cv), skip, awmode, freq, useGlobalLedBuffer, maPerLed, maMax)));
+ busConfigs.emplace_back(type, pins, start, length, colorOrder | (channelSwap<<4), request->hasArg(cv), skip, awmode, freq, useGlobalLedBuffer, maPerLed, maMax);
busesChanged = true;
}
//doInitBusses = busesChanged; // we will do that below to ensure all input data is processed
@@ -220,7 +220,7 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
// we will not bother with pre-allocating ColorOrderMappings vector
BusManager::getColorOrderMap().reset();
for (int s = 0; s < WLED_MAX_COLOR_ORDER_MAPPINGS; s++) {
- int offset = s < 10 ? 48 : 55;
+ int offset = s < 10 ? '0' : 'A';
char xs[4] = "XS"; xs[2] = offset+s; xs[3] = 0; //start LED
char xc[4] = "XC"; xc[2] = offset+s; xc[3] = 0; //strip length
char xo[4] = "XO"; xo[2] = offset+s; xo[3] = 0; //color order
@@ -259,7 +259,7 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
disablePullUp = (bool)request->hasArg(F("IP"));
touchThreshold = request->arg(F("TT")).toInt();
for (int i = 0; i < WLED_MAX_BUTTONS; i++) {
- int offset = i < 10 ? 48 : 55;
+ int offset = i < 10 ? '0' : 'A';
char bt[4] = "BT"; bt[2] = offset+i; bt[3] = 0; // button pin (use A,B,C,... if WLED_MAX_BUTTONS>10)
char be[4] = "BE"; be[2] = offset+i; be[3] = 0; // button type (use A,B,C,... if WLED_MAX_BUTTONS>10)
int hw_btn_pin = request->arg(bt).toInt();
@@ -1193,7 +1193,7 @@ bool handleSet(AsyncWebServerRequest *request, const String& req, bool apply)
}
// you can add more if you need
- // global col[], effectCurrent, ... are updated in stateChanged()
+ // global colPri[], effectCurrent, ... are updated in stateChanged()
if (!apply) return true; // when called by JSON API, do not call colorUpdated() here
pos = req.indexOf(F("&NN")); //do not send UDP notifications this time
diff --git a/wled00/wled.cpp b/wled00/wled.cpp
index 9bc1b8f48..34caeefa3 100644
--- a/wled00/wled.cpp
+++ b/wled00/wled.cpp
@@ -109,7 +109,6 @@ void WLED::loop()
if (WLED_CONNECTED && aOtaEnabled && !otaLock && correctPIN) ArduinoOTA.handle();
#endif
handleNightlight();
- handlePlaylist();
yield();
#ifndef WLED_DISABLE_HUESYNC
@@ -117,6 +116,10 @@ void WLED::loop()
yield();
#endif
+ if (!presetNeedsSaving()) {
+ handlePlaylist();
+ yield();
+ }
handlePresets();
yield();
@@ -461,7 +464,7 @@ void WLED::setup()
#endif
// fill in unique mdns default
- if (strcmp(cmDNS, "x") == 0) sprintf_P(cmDNS, PSTR("wled-%*s"), 6, escapedMac.c_str() + 6);
+ if (strcmp(cmDNS, DEFAULT_MDNS_NAME) == 0) sprintf_P(cmDNS, PSTR("wled-%*s"), 6, escapedMac.c_str() + 6);
#ifndef WLED_DISABLE_MQTT
if (mqttDeviceTopic[0] == 0) sprintf_P(mqttDeviceTopic, PSTR("wled/%*s"), 6, escapedMac.c_str() + 6);
if (mqttClientID[0] == 0) sprintf_P(mqttClientID, PSTR("WLED-%*s"), 6, escapedMac.c_str() + 6);
@@ -544,16 +547,16 @@ void WLED::beginStrip()
Segment &seg = strip.getSegment(i);
if (seg.isActive()) seg.colors[0] = BLACK;
}
- col[0] = col[1] = col[2] = col[3] = 0; // needed for colorUpdated()
+ colPri[0] = colPri[1] = colPri[2] = colPri[3] = 0; // needed for colorUpdated()
}
briLast = briS; bri = 0;
strip.fill(BLACK);
strip.show();
}
+ colorUpdated(CALL_MODE_INIT); // will not send notification but will initiate transition
if (bootPreset > 0) {
applyPreset(bootPreset, CALL_MODE_INIT);
}
- colorUpdated(CALL_MODE_INIT); // will not send notification
// init relay pin
if (rlyPin >= 0) {
diff --git a/wled00/wled.h b/wled00/wled.h
index 0b65a9508..ea40c5dfe 100644
--- a/wled00/wled.h
+++ b/wled00/wled.h
@@ -217,6 +217,10 @@ using PSRAMDynamicJsonDocument = BasicJsonDocument;
#define WLED_AP_PASS DEFAULT_AP_PASS
#endif
+#ifndef WLED_PIN
+ #define WLED_PIN ""
+#endif
+
#ifndef SPIFFS_EDITOR_AIRCOOOKIE
#error You are not using the Aircoookie fork of the ESPAsyncWebserver library.\
Using upstream puts your WiFi password at risk of being served by the filesystem.\
@@ -277,7 +281,11 @@ WLED_GLOBAL char releaseString[] _INIT(WLED_RELEASE_NAME); // must include the q
// AP and OTA default passwords (for maximum security change them!)
WLED_GLOBAL char apPass[65] _INIT(WLED_AP_PASS);
+#ifdef WLED_OTA_PASS
+WLED_GLOBAL char otaPass[33] _INIT(WLED_OTA_PASS);
+#else
WLED_GLOBAL char otaPass[33] _INIT(DEFAULT_OTA_PASS);
+#endif
// Hardware and pin config
#ifndef BTNPIN
@@ -411,7 +419,7 @@ WLED_GLOBAL bool gammaCorrectCol _INIT(true); // use gamma correction on col
WLED_GLOBAL bool gammaCorrectBri _INIT(false); // use gamma correction on brightness
WLED_GLOBAL float gammaCorrectVal _INIT(2.8f); // gamma correction value
-WLED_GLOBAL byte col[] _INIT_N(({ 255, 160, 0, 0 })); // current RGB(W) primary color. col[] should be updated if you want to change the color.
+WLED_GLOBAL byte colPri[] _INIT_N(({ 255, 160, 0, 0 })); // current RGB(W) primary color. colPri[] should be updated if you want to change the color.
WLED_GLOBAL byte colSec[] _INIT_N(({ 0, 0, 0, 0 })); // current RGB(W) secondary color
WLED_GLOBAL byte nightlightTargetBri _INIT(0); // brightness after nightlight is over
@@ -570,11 +578,15 @@ WLED_GLOBAL byte macroLongPress[WLED_MAX_BUTTONS] _INIT({0});
WLED_GLOBAL byte macroDoublePress[WLED_MAX_BUTTONS] _INIT({0});
// Security CONFIG
-WLED_GLOBAL bool otaLock _INIT(false); // prevents OTA firmware updates without password. ALWAYS enable if system exposed to any public networks
-WLED_GLOBAL bool wifiLock _INIT(false); // prevents access to WiFi settings when OTA lock is enabled
-WLED_GLOBAL bool aOtaEnabled _INIT(true); // ArduinoOTA allows easy updates directly from the IDE. Careful, it does not auto-disable when OTA lock is on
-WLED_GLOBAL char settingsPIN[5] _INIT(""); // PIN for settings pages
-WLED_GLOBAL bool correctPIN _INIT(true);
+#ifdef WLED_OTA_PASS
+WLED_GLOBAL bool otaLock _INIT(true); // prevents OTA firmware updates without password. ALWAYS enable if system exposed to any public networks
+#else
+WLED_GLOBAL bool otaLock _INIT(false); // prevents OTA firmware updates without password. ALWAYS enable if system exposed to any public networks
+#endif
+WLED_GLOBAL bool wifiLock _INIT(false); // prevents access to WiFi settings when OTA lock is enabled
+WLED_GLOBAL bool aOtaEnabled _INIT(true); // ArduinoOTA allows easy updates directly from the IDE. Careful, it does not auto-disable when OTA lock is on
+WLED_GLOBAL char settingsPIN[5] _INIT(WLED_PIN); // PIN for settings pages
+WLED_GLOBAL bool correctPIN _INIT(!strlen(settingsPIN));
WLED_GLOBAL unsigned long lastEditTime _INIT(0);
WLED_GLOBAL uint16_t userVar0 _INIT(0), userVar1 _INIT(0); //available for use in usermod
@@ -895,12 +907,11 @@ WLED_GLOBAL ESPAsyncE131 ddp _INIT_N(((handleE131Packet)));
WLED_GLOBAL bool e131NewData _INIT(false);
// led fx library object
-WLED_GLOBAL BusManager busses _INIT(BusManager());
-WLED_GLOBAL WS2812FX strip _INIT(WS2812FX());
-WLED_GLOBAL std::vector busConfigs; //temporary, to remember values from network callback until after
-WLED_GLOBAL bool doInitBusses _INIT(false);
-WLED_GLOBAL int8_t loadLedmap _INIT(-1);
-WLED_GLOBAL uint8_t currentLedmap _INIT(0);
+WLED_GLOBAL WS2812FX strip _INIT(WS2812FX());
+WLED_GLOBAL std::vector busConfigs; //temporary, to remember values from network callback until after
+WLED_GLOBAL bool doInitBusses _INIT(false);
+WLED_GLOBAL int8_t loadLedmap _INIT(-1);
+WLED_GLOBAL uint8_t currentLedmap _INIT(0);
#ifndef ESP8266
WLED_GLOBAL char *ledmapNames[WLED_MAX_LEDMAPS-1] _INIT_N(({nullptr}));
#endif
diff --git a/wled00/wled_server.cpp b/wled00/wled_server.cpp
index da7fd2a3a..77f4133c0 100644
--- a/wled00/wled_server.cpp
+++ b/wled00/wled_server.cpp
@@ -567,13 +567,14 @@ void serveSettings(AsyncWebServerRequest* request, bool post) {
//else if (url.indexOf("/edit") >= 0) subPage = 10;
else subPage = SUBPAGE_WELCOME;
- if (!correctPIN && strlen(settingsPIN) > 0 && (subPage > 0 && subPage < 11)) {
+ bool pinRequired = !correctPIN && strlen(settingsPIN) > 0 && (subPage > (WLED_WIFI_CONFIGURED ? SUBPAGE_MENU : SUBPAGE_WIFI) && subPage < SUBPAGE_LOCK);
+ if (pinRequired) {
originalSubPage = subPage;
subPage = SUBPAGE_PINREQ; // require PIN
}
// if OTA locked or too frequent PIN entry requests fail hard
- if ((subPage == SUBPAGE_WIFI && wifiLock && otaLock) || (post && !correctPIN && millis()-lastEditTime < PIN_RETRY_COOLDOWN))
+ if ((subPage == SUBPAGE_WIFI && wifiLock && otaLock) || (post && pinRequired && millis()-lastEditTime < PIN_RETRY_COOLDOWN))
{
serveMessage(request, 401, FPSTR(s_accessdenied), FPSTR(s_unlock_ota), 254); return;
}
@@ -609,7 +610,7 @@ void serveSettings(AsyncWebServerRequest* request, bool post) {
if (!s2[0]) strcpy_P(s2, s_redirecting);
bool redirectAfter9s = (subPage == SUBPAGE_WIFI || ((subPage == SUBPAGE_SEC || subPage == SUBPAGE_UM) && doReboot));
- serveMessage(request, (correctPIN ? 200 : 401), s, s2, redirectAfter9s ? 129 : (correctPIN ? 1 : 3));
+ serveMessage(request, (!pinRequired ? 200 : 401), s, s2, redirectAfter9s ? 129 : (!pinRequired ? 1 : 3));
return;
}
}
diff --git a/wled00/xml.cpp b/wled00/xml.cpp
index 9178a87a6..19868d01d 100644
--- a/wled00/xml.cpp
+++ b/wled00/xml.cpp
@@ -11,7 +11,7 @@ void XML_response(Print& dest)
dest.printf_P(PSTR("%d "), (nightlightActive && nightlightMode > NL_MODE_SET) ? briT : bri);
for (int i = 0; i < 3; i++)
{
- dest.printf_P(PSTR("%d "), col[i]);
+ dest.printf_P(PSTR("%d "), colPri[i]);
}
for (int i = 0; i < 3; i++)
{
@@ -20,7 +20,7 @@ void XML_response(Print& dest)
dest.printf_P(PSTR("%d %d %d %d %d %d %d %d %d %d %d %d %d %d %s%s %d "),
notifyDirect, receiveGroups!=0, nightlightActive, nightlightMode > NL_MODE_SET, nightlightDelayMins,
nightlightTargetBri, effectCurrent, effectSpeed, effectIntensity, effectPalette,
- strip.hasWhiteChannel() ? col[3] : -1, colSec[3], currentPreset, currentPlaylist >= 0,
+ strip.hasWhiteChannel() ? colPri[3] : -1, colSec[3], currentPreset, currentPlaylist >= 0,
serverDescription, realtimeMode ? PSTR(" (live)") : "",
strip.getFirstSelectedSegId()
);
@@ -275,7 +275,7 @@ void getSettingsJS(byte subPage, Print& settingsScript)
// set limits
settingsScript.printf_P(PSTR("bLimits(%d,%d,%d,%d,%d,%d,%d,%d);"),
WLED_MAX_BUSSES,
- WLED_MIN_VIRTUAL_BUSSES,
+ WLED_MIN_VIRTUAL_BUSSES, // irrelevant, but kept to distinguish S2/S3 in UI
MAX_LEDS_PER_BUS,
MAX_LED_MEMORY,
MAX_LEDS,
@@ -296,9 +296,9 @@ void getSettingsJS(byte subPage, Print& settingsScript)
unsigned sumMa = 0;
for (int s = 0; s < BusManager::getNumBusses(); s++) {
- Bus* bus = BusManager::getBus(s);
- if (bus == nullptr) continue;
- int offset = s < 10 ? 48 : 55;
+ const Bus* bus = BusManager::getBus(s);
+ if (!bus || !bus->isOk()) break; // should not happen but for safety
+ int offset = s < 10 ? '0' : 'A';
char lp[4] = "L0"; lp[2] = offset+s; lp[3] = 0; //ascii 0-9 //strip data pin
char lc[4] = "LC"; lc[2] = offset+s; lc[3] = 0; //strip length
char co[4] = "CO"; co[2] = offset+s; co[3] = 0; //strip color order
@@ -316,7 +316,7 @@ void getSettingsJS(byte subPage, Print& settingsScript)
uint8_t pins[5];
int nPins = bus->getPins(pins);
for (int i = 0; i < nPins; i++) {
- lp[1] = offset+i;
+ lp[1] = '0'+i;
if (PinManager::isPinOk(pins[i]) || bus->isVirtual()) printSetFormValue(settingsScript,lp,pins[i]);
}
printSetFormValue(settingsScript,lc,bus->getLength());
@@ -361,7 +361,7 @@ void getSettingsJS(byte subPage, Print& settingsScript)
const ColorOrderMap& com = BusManager::getColorOrderMap();
for (int s = 0; s < com.count(); s++) {
const ColorOrderMapEntry* entry = com.get(s);
- if (entry == nullptr) break;
+ if (!entry || !entry->len) break;
settingsScript.printf_P(PSTR("addCOM(%d,%d,%d);"), entry->start, entry->len, entry->colorOrder);
}