diff --git a/wled00/bus_wrapper.h b/wled00/bus_wrapper.h index 2437cfc6..56495aa8 100644 --- a/wled00/bus_wrapper.h +++ b/wled00/bus_wrapper.h @@ -122,6 +122,9 @@ #define I_SS_LPO_3 48 +// In the following NeoGammaNullMethod can be replaced with NeoGammaWLEDMethod to perform Gamma correction implicitly +// unfortunately that may apply Gamma correction to pre-calculated palettes which is undesired + /*** ESP8266 Neopixel methods ***/ #ifdef ESP8266 //RGB diff --git a/wled00/cfg.cpp b/wled00/cfg.cpp index d65fce9d..0bdf5032 100644 --- a/wled00/cfg.cpp +++ b/wled00/cfg.cpp @@ -338,7 +338,7 @@ bool deserializeConfig(JsonObject doc, bool fromFS) { if (light_gc_col > 1.0f) gammaCorrectCol = true; else gammaCorrectCol = false; if (gammaCorrectVal > 1.0f && gammaCorrectVal <= 3) { - if (gammaCorrectVal != 2.8f) calcGammaTable(gammaCorrectVal); + if (gammaCorrectVal != 2.8f) NeoGammaWLEDMethod::calcGammaTable(gammaCorrectVal); } else { gammaCorrectVal = 1.0f; // no gamma correction gammaCorrectBri = false; diff --git a/wled00/colors.cpp b/wled00/colors.cpp index 9874c314..8c4baabb 100644 --- a/wled00/colors.cpp +++ b/wled00/colors.cpp @@ -302,7 +302,7 @@ uint16_t approximateKelvinFromRGB(uint32_t rgb) { } //gamma 2.8 lookup table used for color correction -static byte gammaT[] = { +uint8_t NeoGammaWLEDMethod::gammaT[256] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, @@ -320,27 +320,22 @@ static byte gammaT[] = { 177,180,182,184,186,189,191,193,196,198,200,203,205,208,210,213, 215,218,220,223,225,228,231,233,236,239,241,244,247,249,252,255 }; -uint8_t gamma8_cal(uint8_t b, float gamma) -{ - return (int)(powf((float)b / 255.0f, gamma) * 255.0f + 0.5f); -} - // re-calculates & fills gamma table -void calcGammaTable(float gamma) +void NeoGammaWLEDMethod::calcGammaTable(float gamma) { - for (uint16_t i = 0; i < 256; i++) { - gammaT[i] = gamma8_cal(i, gamma); + for (size_t i = 0; i < 256; i++) { + gammaT[i] = (int)(powf((float)i / 255.0f, gamma) * 255.0f + 0.5f); } } -// used for individual channel or brightness gamma correction -uint8_t gamma8(uint8_t b) +uint8_t NeoGammaWLEDMethod::Correct(uint8_t value) { - return gammaT[b]; + if (!gammaCorrectCol) return value; + return gammaT[value]; } // used for color gamma correction -uint32_t gamma32(uint32_t color) +uint32_t NeoGammaWLEDMethod::Correct32(uint32_t color) { if (!gammaCorrectCol) return color; uint8_t w = W(color); diff --git a/wled00/data/settings_leds.htm b/wled00/data/settings_leds.htm index 3d25b316..9f822294 100644 --- a/wled00/data/settings_leds.htm +++ b/wled00/data/settings_leds.htm @@ -10,7 +10,7 @@ d.um_p = []; d.rsvd = []; d.ro_gpio = []; - d.max_gpio = 39; + d.max_gpio = 50; var customStarts=false,startsDirty=[],maxCOOverrides=5; var loc = false, locip, locproto = "http:"; function H(){window.open("https://kno.wled.ge/features/settings/#led-settings");} @@ -26,8 +26,12 @@ d.body.appendChild(scE); // success event scE.addEventListener("load", () => { - GetV();checkSi();setABL(); + GetV(); + checkSi(); + setABL(); + d.Sf.addEventListener("submit", trySubmit); if (d.um_p[0]==-1) d.um_p.shift(); + pinDropdowns(); }); // error event scE.addEventListener("error", (ev) => { @@ -49,7 +53,7 @@ maxB = b; maxV = v; maxM = m; maxPB = p; maxL = l; } function pinsOK() { - var LCs = d.getElementsByTagName("input"); + var LCs = d.Sf.querySelectorAll("#mLC input[name^=L]"); // input fields for (i=0; i=80) continue; } //check for pin conflicts - if (nm=="L0" || nm=="L1" || nm=="L2" || nm=="L3" || nm=="L4" || nm=="RL" || nm=="BT" || nm=="IR") + if (nm=="L0" || nm=="L1" || nm=="L2" || nm=="L3" || nm=="L4"/* || nm=="RL" || nm=="BT" || nm=="IR"*/) if (LCs[i].value!="" && LCs[i].value!="-1") { - var p = []; // used pin array - for (k=0;ke==parseInt(LCs[i].value,10))) {alert(`Sorry, pins ${JSON.stringify(p)} can't be used.`);LCs[i].value="";LCs[i].focus();return false;} - else if (!(nm == "IR" || nm=="BT") && d.ro_gpio.some((e)=>e==parseInt(LCs[i].value,10))) {alert(`Sorry, pins ${JSON.stringify(d.ro_gpio)} are input only.`);LCs[i].value="";LCs[i].focus();return false;} - for (j=i+1; j{if(e.value>-1)p.push(parseInt(e.value));}) // buttons, IR & relay + if (p.some((e)=>e==parseInt(LCs[i].value))) { + alert(`Sorry, pins ${JSON.stringify(p)} can't be used.`); + LCs[i].value=""; + LCs[i].focus(); + return false; + } + else if (/*!(nm == "IR" || nm=="BT") &&*/ d.ro_gpio.some((e)=>e==parseInt(LCs[i].value))) { + alert(`Sorry, pins ${JSON.stringify(d.ro_gpio)} are input only.`); + LCs[i].value=""; + LCs[i].focus(); + return false; + } + for (j=i+1; j=80) continue; } - if (LCs[j].value!="" && LCs[i].value==LCs[j].value) {alert(`Pin conflict between ${LCs[i].name}/${LCs[j].name}!`);LCs[j].value="";LCs[j].focus();return false;} + if (LCs[j].value!="" && LCs[i].value==LCs[j].value) { + alert(`Pin conflict between ${LCs[i].name}/${LCs[j].name}!`); + LCs[j].value=""; + LCs[j].focus(); + return false; + } } } } @@ -96,6 +113,7 @@ gId('abl').style.display = (en) ? 'inline':'none'; gId('psu2').style.display = (en) ? 'inline':'none'; if (d.Sf.LA.value > 0) setABL(); + UI(); } function enLA() { @@ -117,28 +135,28 @@ default: gId('LAdis').style.display = 'inline'; } gId('m1').innerHTML = maxM; - d.getElementsByName("Sf")[0].addEventListener("submit", trySubmit); - UI(); } //returns mem usage function getMem(t, n) { let len = parseInt(d.getElementsByName("LC"+n)[0].value); len += parseInt(d.getElementsByName("SL"+n)[0].value); // skipped LEDs are allocated too + let dbl = 0; + if (d.Sf.LD.checked) dbl = len * 3; // double buffering if (t < 32) { if (t==26 || t==29) len *= 2; // 16 bit LEDs if (maxM < 10000 && d.getElementsByName("L0"+n)[0].value == 3) { //8266 DMA uses 5x the mem - if (t > 28) return len*20; //RGBW - return len*15; + if (t > 28) return len*20 + dbl; //RGBW + return len*15 + dbl; } else if (maxM >= 10000) //ESP32 RMT uses double buffer? { - if (t > 28) return len*8; //RGBW - return len*6; + if (t > 28) return len*8 + dbl; //RGBW + return len*6 + dbl; } - if (t > 28) return len*4; //RGBW - return len*3; + if (t > 28) return len*4 + dbl; //RGBW + return len*3 + dbl; } - if (t > 31 && t < 48) return 5; - return len*3; + if (t > 31 && t < 48) return 5; // analog + return len*3 + dbl; } function UI(change=false) @@ -151,53 +169,50 @@ else if (d.Sf.LA.value > 0) laprev = d.Sf.LA.value; // enable/disable LED fields - var s = d.getElementsByTagName("select"); - for (i=0; i{ // is the field a LED type? - if (s[i].name.substring(0,2)=="LT") { - var n = s[i].name.substring(2); - var t = parseInt(s[i].value,10); - gId("p0d"+n).innerHTML = (t>=80 && t<96) ? "IP address:" : (t > 49) ? "Data GPIO:" : (t > 41) ? "GPIOs:" : "GPIO:"; - gId("p1d"+n).innerHTML = (t> 49 && t<64) ? "Clk GPIO:" : ""; - var LK = d.getElementsByName("L1"+n)[0]; // clock pin + var n = s.name.substring(2); + var t = parseInt(s.value); + gId("p0d"+n).innerHTML = (t>=80 && t<96) ? "IP address:" : (t > 49) ? "Data GPIO:" : (t > 41) ? "GPIOs:" : "GPIO:"; + gId("p1d"+n).innerHTML = (t> 49 && t<64) ? "Clk GPIO:" : ""; + //var LK = d.getElementsByName("L1"+n)[0]; // clock pin - memu += getMem(t, n); // calc memory + memu += getMem(t, n); // calc memory - // enumerate pins - for (p=1; p<5; p++) { - var LK = d.getElementsByName("L"+p+n)[0]; // secondary pins - if (!LK) continue; - if (((t>=80 && t<96) && p<4) || (t>49 && p==1) || (t>41 && t < 50 && (p+40 < t))) // TYPE_xxxx values from const.h - { - // display pin field - LK.style.display = "inline"; - LK.required = true; - } else { - // hide pin field - LK.style.display = "none"; - LK.required = false; - LK.value=""; - } + // enumerate pins + for (p=1; p<5; p++) { + var LK = d.getElementsByName("L"+p+n)[0]; // secondary pins + if (!LK) continue; + if (((t>=80 && t<96) && p<4) || (t>49 && p==1) || (t>41 && t < 50 && (p+40 < t))) // TYPE_xxxx values from const.h + { + // display pin field + LK.style.display = "inline"; + LK.required = true; + } else { + // hide pin field + LK.style.display = "none"; + LK.required = false; + LK.value=""; } - if (change) { - gId("rf"+n).checked = (gId("rf"+n).checked || t == 31); // LEDs require data in off state - if (t > 31 && t < 48) d.getElementsByName("LC"+n)[0].value = 1; // for sanity change analog count just to 1 LED - } - gId("rf"+n).onclick = (t == 31) ? (()=>{return false}) : (()=>{}); // prevent change for TM1814 - gRGBW |= isRGBW = ((t > 17 && t < 22) || (t > 28 && t < 32) || (t > 40 && t < 46 && t != 43) || t == 88); // RGBW checkbox, TYPE_xxxx values from const.h - gId("co"+n).style.display = ((t >= 80 && t < 96) || (t >= 40 && t < 48)) ? "none":"inline"; // hide color order for PWM - gId("dig"+n+"w").style.display = (t > 28 && t < 32) ? "inline":"none"; // show swap channels dropdown - if (!(t > 28 && t < 32)) d.getElementsByName("WO"+n)[0].value = 0; // reset swapping - gId("dig"+n+"c").style.display = (t >= 40 && t < 48) ? "none":"inline"; // hide count for analog - gId("dig"+n+"r").style.display = (t >= 80 && t < 96) ? "none":"inline"; // hide reversed for virtual - gId("dig"+n+"s").style.display = ((t >= 80 && t < 96) || (t >= 40 && t < 48)) ? "none":"inline"; // hide skip 1st for virtual & analog - gId("dig"+n+"f").style.display = ((t >= 16 && t < 32) || (t >= 50 && t < 64)) ? "inline":"none"; // hide refresh - gId("dig"+n+"a").style.display = (isRGBW && t != 40) ? "inline":"none"; // auto calculate white - gId("dig"+n+"l").style.display = (t > 48 && t < 64) ? "inline":"none"; // bus clock speed - gId("rev"+n).innerHTML = (t >= 40 && t < 48) ? "Inverted output":"Reversed (rotated 180°)"; // change reverse text for analog - gId("psd"+n).innerHTML = (t >= 40 && t < 48) ? "Index:":"Start:"; // change analog start description } - } + if (change) { + gId("rf"+n).checked = (gId("rf"+n).checked || t == 31); // LEDs require data in off state + if (t > 31 && t < 48) d.getElementsByName("LC"+n)[0].value = 1; // for sanity change analog count just to 1 LED + } + gId("rf"+n).onclick = (t == 31) ? (()=>{return false}) : (()=>{}); // prevent change for TM1814 + gRGBW |= isRGBW = ((t > 17 && t < 22) || (t > 28 && t < 32) || (t > 40 && t < 46 && t != 43) || t == 88); // RGBW checkbox, TYPE_xxxx values from const.h + gId("co"+n).style.display = ((t >= 80 && t < 96) || (t >= 40 && t < 48)) ? "none":"inline"; // hide color order for PWM + gId("dig"+n+"w").style.display = (t > 28 && t < 32) ? "inline":"none"; // show swap channels dropdown + if (!(t > 28 && t < 32)) d.getElementsByName("WO"+n)[0].value = 0; // reset swapping + gId("dig"+n+"c").style.display = (t >= 40 && t < 48) ? "none":"inline"; // hide count for analog + gId("dig"+n+"r").style.display = (t >= 80 && t < 96) ? "none":"inline"; // hide reversed for virtual + gId("dig"+n+"s").style.display = ((t >= 80 && t < 96) || (t >= 40 && t < 48)) ? "none":"inline"; // hide skip 1st for virtual & analog + gId("dig"+n+"f").style.display = ((t >= 16 && t < 32) || (t >= 50 && t < 64)) ? "inline":"none"; // hide refresh + gId("dig"+n+"a").style.display = (isRGBW && t != 40) ? "inline":"none"; // auto calculate white + gId("dig"+n+"l").style.display = (t > 48 && t < 64) ? "inline":"none"; // bus clock speed + gId("rev"+n).innerHTML = (t >= 40 && t < 48) ? "Inverted output":"Reversed (rotated 180°)"; // change reverse text for analog + gId("psd"+n).innerHTML = (t >= 40 && t < 48) ? "Index:":"Start:"; // change analog start description + }); // display global white channel overrides gId("wc").style.display = (gRGBW) ? 'inline':'none'; if (!gRGBW) { @@ -205,7 +220,7 @@ d.Sf.CR.checked = false; } // check for pin conflicts - var LCs = d.getElementsByTagName("input"); + var LCs = d.Sf.querySelectorAll("#mLC input[name^=L]"); // input fields var sLC = 0, sPC = 0, maxLC = 0; for (i=0; i{if(e.value>-1)p.push(parseInt(e.value));}) // buttons, IR & relay for (j=0; je==parseInt(LCs[i].value,10))) LCs[i].style.color="red"; else LCs[i].style.color=d.ro_gpio.some((e)=>e==parseInt(LCs[i].value,10))?"orange":"#fff"; + if (p.some((e)=>e==parseInt(LCs[i].value))) LCs[i].style.color="red"; else LCs[i].style.color=d.ro_gpio.some((e)=>e==parseInt(LCs[i].value))?"orange":"#fff"; } // check buttons, IR & relay - if (nm=="IR" || nm=="BT" || nm=="RL") { - LCs[i].max = d.max_gpio; - LCs[i].min = -1; - } + //if (nm=="IR" || nm=="BT" || nm=="RL") { + // LCs[i].max = d.max_gpio; + // LCs[i].min = -1; + //} } // update total led count gId("lc").textContent = sLC; @@ -366,11 +380,11 @@ ${i+1}: Start:  
Length:

-GPIO: - - - - +GPIO: + + + +

Reversed:

Skip first LEDs:

Off Refresh:
@@ -546,7 +560,103 @@ Length: {fields.push(e.name);}) // buttons + for (let i of d.Sf.elements) { + if (i.type === "number" && fields.includes(i.name)) { //select all pin select elements + let v = parseInt(i.value); + let sel = addDropdown(i.name,0); + for (var j = -1; j <= d.max_gpio; j++) { + if (d.rsvd.includes(j)) continue; + let foundPin = d.um_p.indexOf(j); + let txt = (j === -1) ? "unused" : `${j}`; + if (foundPin >= 0 && j !== v) txt += ` used`; // already reserved pin + if (d.ro_gpio.includes(j)) txt += " (R/O)"; + let opt = addOption(sel, txt, j); + if (j === v) opt.selected = true; // this is "our" pin + else if (d.um_p.includes(j)) opt.disabled = true; // someone else's pin + } + } + } + // update select options + d.Sf.querySelectorAll("select.pin").forEach((e)=>{pinUpd(e);}); + // add dataset values for LED GPIO pins + d.Sf.querySelectorAll(".iST input.s[name^=L]").forEach((i)=>{ + if (i.value!=="" && i.value>=0) + i.dataset.val = i.value; + }); + } + function pinUpd(e) { + // update changed select options across all usermods + let oldV = parseInt(e.dataset.val); + e.dataset.val = e.value; + let txt = e.name; + let pins = []; + d.Sf.querySelectorAll(".iST input.s[name^=L]").forEach((i)=>{ + if (i.value!=="" && i.value>=0 && i.max<255) + pins.push(i.value); + }); + let selects = d.Sf.querySelectorAll("select.pin"); + for (let sel of selects) { + if (sel == e) continue + Array.from(sel.options).forEach((i)=>{ + let led = pins.includes(i.value); + if (!(i.value==oldV || i.value==e.value || led)) return; + if (i.value == -1) { + i.text = "unused"; + return + } + i.text = i.value; + if (i.value==oldV) { + i.disabled = false; + } + if (i.value==e.value || led) { + i.disabled = true; + i.text += ` ${led?'LED':txt}`; + } + if (d.ro_gpio.includes(parseInt(i.value))) i.text += " (R/O)"; + }); + } + } + // https://stackoverflow.com/questions/39729741/javascript-change-input-text-to-select-option + function addDropdown(field) { + let sel = d.createElement('select'); + sel.classList.add("pin"); + let inp = d.getElementsByName(field)[0]; + if (inp && inp.tagName === "INPUT" && (inp.type === "text" || inp.type === "number")) { // may also use nodeName + let v = inp.value; + let n = inp.name; + // copy the existing input element's attributes to the new select element + for (var i = 0; i < inp.attributes.length; ++ i) { + var att = inp.attributes[i]; + // type and value don't apply, so skip them + // ** you might also want to skip style, or others -- modify as needed ** + if (att.name != 'type' && att.name != 'value' && att.name != 'class' && att.name != 'style') { + sel.setAttribute(att.name, att.value); + } + } + sel.setAttribute("data-val", v); + sel.setAttribute("onchange", "pinUpd(this)"); + // finally, replace the old input element with the new select element + inp.parentElement.replaceChild(sel, inp); + return sel; + } + return null; + } + function addOption(sel,txt,val) { + if (sel===null) return; // select object missing + let opt = d.createElement("option"); + opt.value = val; + opt.text = txt; + sel.appendChild(opt); + for (let i=0; i Make a segment for each output:
Custom bus start indices:
- Use global LED buffer:
+ Use global LED buffer:

Color Order Override: diff --git a/wled00/data/settings_um.htm b/wled00/data/settings_um.htm index bef1a4cd..8809668f 100644 --- a/wled00/data/settings_um.htm +++ b/wled00/data/settings_um.htm @@ -7,7 +7,7 @@ Usermod Settings