Merge pull request #4595 from wled/cherry-pick-mulifix
Cherry pick mulifix
This commit is contained in:
		| @@ -543,7 +543,7 @@ typedef struct Segment { | ||||
|     static void     handleRandomPalette(); | ||||
|     inline static const CRGBPalette16 &getCurrentPalette() { return Segment::_currentPalette; } | ||||
|  | ||||
|     void    setGeometry(uint16_t i1, uint16_t i2, uint8_t grp=1, uint8_t spc=0, uint16_t ofs=UINT16_MAX, uint16_t i1Y=0, uint16_t i2Y=1); | ||||
|     void    setGeometry(uint16_t i1, uint16_t i2, uint8_t grp=1, uint8_t spc=0, uint16_t ofs=UINT16_MAX, uint16_t i1Y=0, uint16_t i2Y=1, uint8_t m12 = 0); | ||||
|     Segment &setColor(uint8_t slot, uint32_t c); | ||||
|     Segment &setCCT(uint16_t k); | ||||
|     Segment &setOpacity(uint8_t o); | ||||
|   | ||||
| @@ -458,19 +458,24 @@ void Segment::handleRandomPalette() { | ||||
| } | ||||
|  | ||||
| // segId is given when called from network callback, changes are queued if that segment is currently in its effect function | ||||
| void Segment::setGeometry(uint16_t i1, uint16_t i2, uint8_t grp, uint8_t spc, uint16_t ofs, uint16_t i1Y, uint16_t i2Y) { | ||||
| void Segment::setGeometry(uint16_t i1, uint16_t i2, uint8_t grp, uint8_t spc, uint16_t ofs, uint16_t i1Y, uint16_t i2Y, uint8_t m12) { | ||||
|   // return if neither bounds nor grouping have changed | ||||
|   bool boundsUnchanged = (start == i1 && stop == i2); | ||||
|   #ifndef WLED_DISABLE_2D | ||||
|   if (Segment::maxHeight>1) boundsUnchanged &= (startY == i1Y && stopY == i2Y); // 2D | ||||
|   #endif | ||||
|  | ||||
|   m12 = constrain(m12, 0, 7); | ||||
|   if (stop && (spc > 0 || m12 != map1D2D)) fill(BLACK); | ||||
|   if (m12 != map1D2D) map1D2D = m12; | ||||
| /* | ||||
|   if (boundsUnchanged | ||||
|       && (!grp || (grouping == grp && spacing == spc)) | ||||
|       && (ofs == UINT16_MAX || ofs == offset)) return; | ||||
|  | ||||
|       && (m12 == map1D2D) | ||||
|      ) return; | ||||
| */ | ||||
|   stateChanged = true; // send UDP/WS broadcast | ||||
|  | ||||
|   if (stop) fill(BLACK); // turn old segment range off (clears pixels if changing spacing) | ||||
|   if (grp) { // prevent assignment of 0 | ||||
|     grouping = grp; | ||||
|     spacing = spc; | ||||
| @@ -480,10 +485,7 @@ void Segment::setGeometry(uint16_t i1, uint16_t i2, uint8_t grp, uint8_t spc, ui | ||||
|   } | ||||
|   if (ofs < UINT16_MAX) offset = ofs; | ||||
|  | ||||
|   DEBUG_PRINT(F("setUp segment: ")); DEBUG_PRINT(i1); | ||||
|   DEBUG_PRINT(','); DEBUG_PRINT(i2); | ||||
|   DEBUG_PRINT(F(" -> ")); DEBUG_PRINT(i1Y); | ||||
|   DEBUG_PRINT(','); DEBUG_PRINTLN(i2Y); | ||||
|   DEBUG_PRINTF_P(PSTR("Segment geometry: %d,%d -> %d,%d\n"), (int)i1, (int)i2, (int)i1Y, (int)i2Y); | ||||
|   markForReset(); | ||||
|   if (boundsUnchanged) return; | ||||
|  | ||||
| @@ -1063,36 +1065,25 @@ void Segment::fade_out(uint8_t rate) { | ||||
|   const int cols = is2D() ? virtualWidth() : virtualLength(); | ||||
|   const int rows = virtualHeight(); // will be 1 for 1D | ||||
|  | ||||
|   rate = (255-rate) >> 1; | ||||
|   float mappedRate = 1.0f / (float(rate) + 1.1f); | ||||
|  | ||||
|   uint32_t color = colors[1]; // SEGCOLOR(1); // target color | ||||
|   int w2 = W(color); | ||||
|   int r2 = R(color); | ||||
|   int g2 = G(color); | ||||
|   int b2 = B(color); | ||||
|   rate = (256-rate) >> 1; | ||||
|   const int mappedRate = 256 / (rate + 1); | ||||
|  | ||||
|   for (int y = 0; y < rows; y++) for (int x = 0; x < cols; x++) { | ||||
|     color = is2D() ? getPixelColorXY(x, y) : getPixelColor(x); | ||||
|     uint32_t color = is2D() ? getPixelColorXY(x, y) : getPixelColor(x); | ||||
|     if (color == colors[1]) continue; // already at target color | ||||
|     int w1 = W(color); | ||||
|     int r1 = R(color); | ||||
|     int g1 = G(color); | ||||
|     int b1 = B(color); | ||||
|  | ||||
|     int wdelta = (w2 - w1) * mappedRate; | ||||
|     int rdelta = (r2 - r1) * mappedRate; | ||||
|     int gdelta = (g2 - g1) * mappedRate; | ||||
|     int bdelta = (b2 - b1) * mappedRate; | ||||
|  | ||||
|     // if fade isn't complete, make sure delta is at least 1 (fixes rounding issues) | ||||
|     wdelta += (w2 == w1) ? 0 : (w2 > w1) ? 1 : -1; | ||||
|     rdelta += (r2 == r1) ? 0 : (r2 > r1) ? 1 : -1; | ||||
|     gdelta += (g2 == g1) ? 0 : (g2 > g1) ? 1 : -1; | ||||
|     bdelta += (b2 == b1) ? 0 : (b2 > b1) ? 1 : -1; | ||||
|  | ||||
|     if (is2D()) setPixelColorXY(x, y, r1 + rdelta, g1 + gdelta, b1 + bdelta, w1 + wdelta); | ||||
|     else        setPixelColor(x, r1 + rdelta, g1 + gdelta, b1 + bdelta, w1 + wdelta); | ||||
|     for (int i = 0; i < 32; i += 8) { | ||||
|       uint8_t c2 = (colors[1]>>i);  // get background channel | ||||
|       uint8_t c1 = (color>>i);      // get foreground channel | ||||
|       // we can't use bitshift since we are using int | ||||
|       int delta = (c2 - c1) * mappedRate / 256; | ||||
|       // if fade isn't complete, make sure delta is at least 1 (fixes rounding issues) | ||||
|       if (delta == 0) delta += (c2 == c1) ? 0 : (c2 > c1) ? 1 : -1; | ||||
|       // stuff new value back into color | ||||
|       color &= ~(0xFF<<i); | ||||
|       color |= ((c1 + delta) & 0xFF) << i; | ||||
|     } | ||||
|     if (is2D()) setPixelColorXY(x, y, color); | ||||
|     else        setPixelColor(x, color); | ||||
|   } | ||||
| } | ||||
|  | ||||
| @@ -1366,7 +1357,7 @@ void WS2812FX::service() { | ||||
|   _segment_index = 0; | ||||
|  | ||||
|   for (segment &seg : _segments) { | ||||
|     if (_suspend) return; // immediately stop processing segments if suspend requested during service() | ||||
|     if (_suspend) break; // immediately stop processing segments if suspend requested during service() | ||||
|  | ||||
|     // process transition (mode changes in the middle of transition) | ||||
|     seg.handleTransition(); | ||||
| @@ -1431,8 +1422,8 @@ void WS2812FX::service() { | ||||
|   if (doShow) { | ||||
|     yield(); | ||||
|     Segment::handleRandomPalette(); // slowly transition random palette; move it into for loop when each segment has individual random palette | ||||
|     show(); | ||||
|     _lastServiceShow = nowUp; // update timestamp, for precise FPS control | ||||
|     if (!_suspend) show(); | ||||
|   } | ||||
|   #ifdef WLED_DEBUG | ||||
|   if ((_targetFps != FPS_UNLIMITED) && (millis() - nowUp > _frametime)) DEBUG_PRINTF_P(PSTR("Slow strip %u/%d.\n"), (unsigned)(millis()-nowUp), (int)_frametime); | ||||
|   | ||||
| @@ -40,7 +40,7 @@ void longPressAction(uint8_t b) | ||||
| { | ||||
|   if (!macroLongPress[b]) { | ||||
|     switch (b) { | ||||
|       case 0: setRandomColor(col); colorUpdated(CALL_MODE_BUTTON); break; | ||||
|       case 0: setRandomColor(colPri); colorUpdated(CALL_MODE_BUTTON); break; | ||||
|       case 1:  | ||||
|         if(buttonBriDirection) { | ||||
|           if (bri == 255) break; // avoid unnecessary updates to brightness | ||||
| @@ -230,7 +230,7 @@ void handleAnalog(uint8_t b) | ||||
|       effectPalette = constrain(effectPalette, 0, strip.getPaletteCount()-1);  // map is allowed to "overshoot", so we need to contrain the result | ||||
|     } else if (macroDoublePress[b] == 200) { | ||||
|       // primary color, hue, full saturation | ||||
|       colorHStoRGB(aRead*256,255,col); | ||||
|       colorHStoRGB(aRead*256,255,colPri); | ||||
|     } else { | ||||
|       // otherwise use "double press" for segment selection | ||||
|       Segment& seg = strip.getSegment(macroDoublePress[b]); | ||||
|   | ||||
| @@ -773,8 +773,8 @@ function populateSegments(s) | ||||
| 		} | ||||
|  | ||||
| 		let segp = `<div id="segp${i}" class="sbs">`+ | ||||
| 						`<i class="icons slider-icon pwr ${inst.on ? "act":""}" id="seg${i}pwr" onclick="setSegPwr(${i})"></i>`+ | ||||
| 						`<div class="sliderwrap il">`+ | ||||
| 						`<i class="icons slider-icon pwr ${inst.on ? "act":""}" id="seg${i}pwr" title="Power" onclick="setSegPwr(${i})"></i>`+ | ||||
| 						`<div class="sliderwrap il" title="Opacity/Brightness">`+ | ||||
| 							`<input id="seg${i}bri" class="noslide" onchange="setSegBri(${i})" oninput="updateTrail(this)" max="255" min="1" type="range" value="${inst.bri}" />`+ | ||||
| 							`<div class="sliderdisplay"></div>`+ | ||||
| 						`</div>`+ | ||||
| @@ -810,7 +810,7 @@ function populateSegments(s) | ||||
| 		cn += `<div class="seg lstI ${i==s.mainseg && !simplifiedUI ? 'selected' : ''} ${exp ? "expanded":""}" id="seg${i}" data-set="${inst.set}">`+ | ||||
| 				`<label class="check schkl ${smpl}">`+ | ||||
| 					`<input type="checkbox" id="seg${i}sel" onchange="selSeg(${i})" ${inst.sel ? "checked":""}>`+ | ||||
| 					`<span class="checkmark"></span>`+ | ||||
| 					`<span class="checkmark" title="Select"></span>`+ | ||||
| 				`</label>`+ | ||||
| 				`<div class="segname ${smpl}" onclick="selSegEx(${i})">`+ | ||||
| 					`<i class="icons e-icon frz" id="seg${i}frz" title="(un)Freeze" onclick="event.preventDefault();tglFreeze(${i});">&#x${inst.frz ? (li.live && li.liveseg==i?'e410':'e0e8') : 'e325'};</i>`+ | ||||
| @@ -1659,13 +1659,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; | ||||
| @@ -1879,7 +1883,7 @@ function makeSeg() | ||||
| function resetUtil(off=false) | ||||
| { | ||||
| 	gId('segutil').innerHTML = `<div class="seg btn btn-s${off?' off':''}" style="padding:0;margin-bottom:12px;">` | ||||
| 	+ '<label class="check schkl"><input type="checkbox" id="selall" onchange="selSegAll(this)"><span class="checkmark"></span></label>' | ||||
| 	+ '<label class="check schkl"><input type="checkbox" id="selall" onchange="selSegAll(this)"><span class="checkmark" title="Select all"></span></label>' | ||||
| 	+ `<div class="segname" ${off?'':'onclick="makeSeg()"'}><i class="icons btn-icon"></i>Add segment</div>` | ||||
| 	+ '<div class="pop hide" onclick="event.stopPropagation();">' | ||||
| 	+ `<i class="icons g-icon" title="Select group" onclick="this.nextElementSibling.classList.toggle('hide');"></i>` | ||||
| @@ -2649,28 +2653,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; | ||||
| @@ -2678,7 +2682,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); | ||||
|   | ||||
| @@ -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, ()=>{ | ||||
| @@ -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 | ||||
| @@ -399,7 +400,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 | ||||
| @@ -422,7 +423,7 @@ | ||||
| 			}); | ||||
|  | ||||
| 			if ((n==1 && i>=maxB+maxV) || (n==-1 && i==0)) return; | ||||
| 			var s = String.fromCharCode((i<10?48:55)+i); | ||||
| 			var s = chrID(i); | ||||
|  | ||||
| 			if (n==1) { | ||||
| // npm run build has trouble minimizing spaces inside string | ||||
| @@ -506,7 +507,7 @@ mA/LED: <select name="LAsel${s}" onchange="enLA(this,'${s}');UI();"> | ||||
| 		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 = `<div class="com_entry"> | ||||
| <hr class="sml"> | ||||
| ${i+1}: Start: <input type="number" name="XS${s}" id="xs${s}" class="l starts" min="0" max="65535" value="${start}" oninput="UI();" required="">  | ||||
| @@ -560,7 +561,7 @@ Swap: <select id="xw${s}" name="XW${s}"> | ||||
|  | ||||
| 		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: <input type="number" name="BT${s}" onchange="UI()" class="xs" value="${p}">`; | ||||
| 			c += ` <select name="BE${s}">` | ||||
| 			c += `<option value="0" ${t==0?"selected":""}>Disabled</option>`; | ||||
| @@ -584,8 +585,10 @@ Swap: <select id="xw${s}" name="XW${s}"> | ||||
| 		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; | ||||
| @@ -617,7 +620,7 @@ Swap: <select id="xw${s}" name="XW${s}"> | ||||
| 				var c = JSON.parse(lines);  | ||||
| 				if (c.hw) { | ||||
| 					if (c.hw.led) { | ||||
| 						for (var i=0; i<10; i++) addLEDs(-1); | ||||
| 						for (var i=0; i<oMaxB+maxV; i++) addLEDs(-1); | ||||
| 						var l = c.hw.led; | ||||
| 						l.ins.forEach((v,i,a)=>{ | ||||
| 							addLEDs(1); | ||||
|   | ||||
| @@ -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; | ||||
|   | ||||
							
								
								
									
										102
									
								
								wled00/json.cpp
									
									
									
									
									
								
							
							
						
						
									
										102
									
								
								wled00/json.cpp
									
									
									
									
									
								
							| @@ -91,19 +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; | ||||
|  | ||||
|   if ((spc>0 && spc!=seg.spacing) || seg.map1D2D!=map1D2D) seg.fill(BLACK); // clear spacing gaps | ||||
|  | ||||
|   seg.map1D2D  = constrain(map1D2D, 0, 7); | ||||
|   seg.soundSim = constrain(soundSim, 0, 3); | ||||
|  | ||||
|   uint8_t set = elem[F("set")] | seg.set; | ||||
|   seg.set = constrain(set, 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 = 1; | ||||
|   if (stop > start) len = stop - start; | ||||
| @@ -117,7 +118,7 @@ bool deserializeSegment(JsonObject elem, byte it, byte presetId) | ||||
|   if (stop > start && of > len -1) of = len -1; | ||||
|  | ||||
|   // update segment (delete if necessary) | ||||
|   seg.setGeometry(start, stop, grp, spc, of, startY, stopY); // strip needs to be suspended for this to work without issues | ||||
|   seg.setGeometry(start, stop, grp, spc, of, startY, stopY, map1D2D); // strip needs to be suspended for this to work without issues | ||||
|  | ||||
|   if (newSeg) seg.refreshLightCapabilities(); // fix for #3403 | ||||
|  | ||||
| @@ -206,20 +207,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; | ||||
| @@ -393,35 +390,38 @@ bool deserializeState(JsonObject root, byte callMode, byte presetId) | ||||
|  | ||||
|   int it = 0; | ||||
|   JsonVariant segVar = root["seg"]; | ||||
|   if (!segVar.isNull()) strip.suspend(); | ||||
|   if (segVar.is<JsonObject>()) | ||||
|   { | ||||
|     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 waitUntil = millis() + strip.getFrameTime(); | ||||
|     while (strip.isServicing() && millis() < waitUntil) delay(1); // wait until frame is over | ||||
|     #ifdef WLED_DEBUG | ||||
|     if (millis() >= waitUntil) DEBUG_PRINTLN(F("JSON: Waited for strip to finish servicing.")); | ||||
|     #endif | ||||
|     if (segVar.is<JsonObject>()) { | ||||
|       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<JsonArray>(); | ||||
|       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<JsonArray>(); | ||||
|     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); | ||||
|  | ||||
|   | ||||
| @@ -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);} | ||||
| @@ -157,14 +157,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 | ||||
| @@ -221,7 +221,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 | ||||
| @@ -246,7 +246,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); | ||||
|     } | ||||
|   | ||||
| @@ -99,7 +99,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)) { | ||||
| @@ -165,7 +165,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, 33); | ||||
|   strcat_P(subuf, PSTR("/c")); | ||||
|   mqtt->publish(subuf, 0, retainMqttMsg, s);         // optionally retain message (#2263) | ||||
|   | ||||
| @@ -144,7 +144,7 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage) | ||||
|  | ||||
|     bool busesChanged = false; | ||||
|     for (int s = 0; s < WLED_MAX_BUSSES+WLED_MIN_VIRTUAL_BUSSES; s++) { | ||||
|       int offset = s < 10 ? 48 : 55; | ||||
|       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 | ||||
| @@ -163,7 +163,7 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage) | ||||
|         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; | ||||
|       } | ||||
| @@ -218,7 +218,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 | ||||
| @@ -257,7 +257,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(); | ||||
| @@ -1185,7 +1185,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 | ||||
|   | ||||
| @@ -459,7 +459,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); | ||||
| @@ -540,11 +540,12 @@ void WLED::beginStrip() | ||||
|   } else { | ||||
|     // fix for #3196 | ||||
|     if (bootPreset > 0) { | ||||
|       bool oldTransition = fadeTransition;    // workaround if transitions are enabled | ||||
|       fadeTransition = false;                 // ignore transitions temporarily | ||||
|       strip.setColor(0, BLACK);               // set all segments black | ||||
|       fadeTransition = oldTransition;         // restore transitions | ||||
|       col[0] = col[1] = col[2] = col[3] = 0;  // needed for colorUpdated() | ||||
|       // set all segments black (no transition) | ||||
|       for (unsigned i = 0; i < strip.getSegmentsNum(); i++) { | ||||
|         Segment &seg = strip.getSegment(i); | ||||
|         if (seg.isActive()) seg.colors[0] = BLACK; | ||||
|       } | ||||
|       colPri[0] = colPri[1] = colPri[2] = colPri[3] = 0;  // needed for colorUpdated() | ||||
|     } | ||||
|     briLast = briS; bri = 0; | ||||
|     strip.fill(BLACK); | ||||
|   | ||||
| @@ -407,7 +407,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 | ||||
|   | ||||
| @@ -11,7 +11,7 @@ void XML_response(Print& dest) | ||||
|   dest.printf_P(PSTR("<?xml version=\"1.0\" ?><vs><ac>%d</ac>"), (nightlightActive && nightlightMode > NL_MODE_SET) ? briT : bri); | ||||
|   for (int i = 0; i < 3; i++) | ||||
|   { | ||||
|    dest.printf_P(PSTR("<cl>%d</cl>"), col[i]); | ||||
|    dest.printf_P(PSTR("<cl>%d</cl>"), colPri[i]); | ||||
|   } | ||||
|   for (int i = 0; i < 3; i++) | ||||
|   { | ||||
| @@ -20,7 +20,7 @@ void XML_response(Print& dest) | ||||
|   dest.printf_P(PSTR("<ns>%d</ns><nr>%d</nr><nl>%d</nl><nf>%d</nf><nd>%d</nd><nt>%d</nt><fx>%d</fx><sx>%d</sx><ix>%d</ix><fp>%d</fp><wv>%d</wv><ws>%d</ws><ps>%d</ps><cy>%d</cy><ds>%s%s</ds><ss>%d</ss></vs>"), | ||||
|     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() | ||||
|   ); | ||||
| @@ -293,9 +293,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 | ||||
| @@ -313,7 +313,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()); | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 netmindz
					netmindz