Pin dropdowns POC.

NeoPixelBusGammaMethod POC.
PixelMagic POC.
Button reassign POC.
This commit is contained in:
Blaz Kristan 2023-06-10 20:43:27 +02:00
parent 21387b9a83
commit 3ca58ee65f
14 changed files with 2040 additions and 751 deletions

View File

@ -222,6 +222,7 @@ writeHtmlGzipped("wled00/data/index.htm", "wled00/html_ui.h", 'index');
writeHtmlGzipped("wled00/data/simple.htm", "wled00/html_simple.h", 'simple');
writeHtmlGzipped("wled00/data/pixart/pixart.htm", "wled00/html_pixart.h", 'pixart');
writeHtmlGzipped("wled00/data/cpal/cpal.htm", "wled00/html_cpal.h", 'cpal');
//writeHtmlGzipped("wled00/data/pxmagic.htm", "wled00/html_pxmagic.h", 'pxmagic');
/*
writeChunks(
"wled00/data",

View File

@ -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

View File

@ -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;

View File

@ -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);

1064
wled00/data/pxmagic.htm Normal file

File diff suppressed because one or more lines are too long

View File

@ -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");}
@ -28,6 +28,7 @@
scE.addEventListener("load", () => {
GetV();checkSi();setABL();
if (d.um_p[0]==-1) d.um_p.shift();
pinDropdowns();
});
// error event
scE.addEventListener("error", (ev) => {
@ -546,7 +547,63 @@ Length: <input type="number" name="XC${i}" id="xc${i}" class="l" min="1" max="65
}
}
}
function S(){
function pinDropdowns() {
let fields = ["IR","RL"]; // IR & relay
gId("btns").querySelectorAll('input[type="number"]').forEach((e)=>{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
}
}
}
}
// 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);
// 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<sel.childNodes.length; i++) {
let c = sel.childNodes[i];
if (c.value == sel.dataset.val) sel.selectedIndex = i;
}
return opt;
}
function S() {
let l = window.location;
if (l.protocol == "file:") {
loc = true;

View File

@ -7,7 +7,7 @@
<title>Usermod Settings</title>
<script>
var d = document;
d.max_gpio = 39;
d.max_gpio = 50;
d.um_p = [];
d.rsvd = [];
d.ro_gpio = [];
@ -39,6 +39,7 @@
d.Sf.MISO.max = d.max_gpio;
let inp = d.getElementsByTagName("input");
for (let i of inp) if (i.type === "number" && i.name.replace("[]","").substr(-3) === "pin") i.max = d.max_gpio;
pinDropdowns();
});
// error event
scE.addEventListener("error", (ev) => {
@ -103,8 +104,10 @@
if (isO(o)) {
for (const [k,v] of Object.entries(o)) {
if (isO(v)) {
let oldO = owner; // keep parent name
owner = k;
getPins(v);
owner = oldO;
continue;
}
if (k.replace("[]","").substr(-3)=="pin") {
@ -163,11 +166,67 @@
urows += `<input type="${t==="int"?"number":t}" name="${k}:${f}${a?"[]":""}" ${c} oninput="check(this,'${k.substr(k.indexOf(":")+1)}')"><br>`;
}
}
function pinDropdowns() {
for (let i of d.Sf.elements) {
if (i.type === "number" && (i.name.includes("pin") || ["SDA","SCL","MOSI","MISO","SCLK"].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 = pins.indexOf(j);
let txt = (j === -1) ? "unused" : `${j}`;
if (foundPin >= 0 && j !== v) txt += ` ${pinO[foundPin]=="if"?"global":pinO[foundPin]}`; // 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 (pins.includes(j)) opt.disabled = true; // someone else's pin
}
}
}
}
function UI(e) {
// update changed select options across all usermods
let oldV = parseInt(e.dataset.val);
e.dataset.val = e.value;
let txt = e.name.split(":")[e.name.split(":").length-2];
let selects = d.Sf.querySelectorAll("select[class='pin']");
for (let sel of selects) {
if (sel == e) continue
Array.from(sel.options).forEach((i)=>{
if (!(i.value==oldV || i.value==e.value)) 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) {
i.disabled = true;
i.text += ` ${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(um,fld) {
let sel = d.createElement('select');
let arr = d.getElementsByName(um+":"+fld);
let inp = arr[1]; // assume 1st field to be hidden (type)
if (typeof(fld) === "string") { // parameter from usermod (field name)
if (fld.includes("pin")) sel.classList.add("pin");
um += ":"+fld;
} else if (typeof(fld) === "number") sel.classList.add("pin"); // a hack to add a class
let arr = d.getElementsByName(um);
let idx = arr[0].type==="hidden"?1:0; // ignore hidden field
if (arr.length > 2) {
// we have array of values (usually pins)
for (let i of arr) {
if (i.type === "number") break;
idx++;
}
}
let inp = arr[idx];
if (inp && inp.tagName === "INPUT" && (inp.type === "text" || inp.type === "number")) { // may also use nodeName
let v = inp.value;
let n = inp.name;
@ -181,8 +240,10 @@
}
}
sel.setAttribute("data-val", v);
sel.setAttribute("onchange", "UI(this)");
// finally, replace the old input element with the new select element
inp.parentElement.replaceChild(sel, inp);
if (arr[0].type==="hidden") arr[0].parentElement.removeChild(arr[0]); // remove hidden element from DOM
return sel;
}
return null;
@ -197,6 +258,7 @@
let c = sel.childNodes[i];
if (c.value == sel.dataset.val) sel.selectedIndex = i;
}
return opt;
}
// https://stackoverflow.com/questions/26440494/insert-text-after-this-input-element-with-javascript
function addInfo(name,el,txt, txt2="") {
@ -208,6 +270,10 @@
if (txt2!="") obj[el].insertAdjacentHTML('beforebegin', txt2 + '&nbsp;'); //add pre texts
}
}
// add Help Button
function addHB(um) {
addInfo(um + ':help',0,`<button onclick="location.href='https://kno.wled.ge/usermods/${um}'" type="button">?</button>`);
}
// load settings and insert values into DOM
function ldS() {
fetch(getURL('/cfg.json'), {

View File

@ -61,6 +61,9 @@ button.sml {
.hide {
display: none;
}
.err {
color: #f00;
}
.warn {
color: #fa0;
}
@ -117,6 +120,10 @@ select {
font-family: Verdana, sans-serif;
border: 0.5ch solid #333;
}
select.pin {
max-width: 120px;
text-overflow: ellipsis;
}
tr {
line-height: 100%;
}

View File

@ -50,6 +50,18 @@ bool getJsonValue(const JsonVariant& element, DestType& destination, const Defau
//colors.cpp
// similar to NeoPixelBus NeoGammaTableMethod but allows dynamic changes (superseded by NPB::NeoGammaDynamicTableMethod)
class NeoGammaWLEDMethod {
public:
static uint8_t Correct(uint8_t value); // apply Gamma to single channel
static uint32_t Correct32(uint32_t color); // apply Gamma to RGBW32 color (WLED specific, not used by NPB)
static void calcGammaTable(float gamma); // re-calculates & fills gamma table
static inline uint8_t rawGamma8(uint8_t val) { return gammaT[val]; } // get value from Gamma table (WLED specific, not used by NPB)
private:
static uint8_t gammaT[];
};
#define gamma32(c) NeoGammaWLEDMethod::Correct32(c)
#define gamma8(c) NeoGammaWLEDMethod::rawGamma8(c)
uint32_t color_blend(uint32_t,uint32_t,uint16_t,bool b16=false);
uint32_t color_add(uint32_t,uint32_t);
inline uint32_t colorFromRgbw(byte* rgbw) { return uint32_t((byte(rgbw[3]) << 24) | (byte(rgbw[0]) << 16) | (byte(rgbw[1]) << 8) | (byte(rgbw[2]))); }
@ -63,10 +75,6 @@ bool colorFromHexString(byte* rgb, const char* in);
uint32_t colorBalanceFromKelvin(uint16_t kelvin, uint32_t rgb);
uint16_t approximateKelvinFromRGB(uint32_t rgb);
void setRandomColor(byte* rgb);
uint8_t gamma8_cal(uint8_t b, float gamma);
void calcGammaTable(float gamma);
uint8_t gamma8(uint8_t b);
uint32_t gamma32(uint32_t);
//dmx.cpp
void initDMX();

File diff suppressed because it is too large Load Diff

View File

@ -455,6 +455,29 @@ bool deserializeState(JsonObject root, byte callMode, byte presetId)
}
}
// accepting config changes (not permanent, unless config is saved)
JsonObject cfg = root["cfg"];
if (!cfg.isNull()) {
// reconfigure button presets (on the fly)
JsonVariant btn = cfg["btn"];
if (btn.is<JsonObject>()) {
uint8_t btn_id = btn["id"] | 0;
macroButton[btn_id] = btn["s"] | macroButton[btn_id];
macroLongPress[btn_id] = btn["l"] | macroLongPress[btn_id];
macroDoublePress[btn_id] = btn["d"] | macroDoublePress[btn_id];
} else {
uint8_t btn_id = 0;
for (JsonObject _btn : btn.as<JsonArray>()) {
btn_id = _btn["id"] | btn_id;
if (btn_id >= WLED_MAX_BUTTONS) continue;
macroButton[btn_id] = _btn["s"] | macroButton[btn_id];
macroLongPress[btn_id] = _btn["l"] | macroLongPress[btn_id];
macroDoublePress[btn_id] = _btn["d"] | macroDoublePress[btn_id];
btn_id++;
}
}
}
stateUpdated(callMode);
if (presetToRestore) currentPreset = presetToRestore;

View File

@ -37,8 +37,8 @@ void applyValuesToSelectedSegs()
if (effectSpeed != selsegPrev.speed) {seg.speed = effectSpeed; stateChanged = true;}
if (effectIntensity != selsegPrev.intensity) {seg.intensity = effectIntensity; stateChanged = true;}
if (effectPalette != selsegPrev.palette) {seg.palette = effectPalette; stateChanged = true;}
if (effectCurrent != selsegPrev.mode) {strip.setMode(i, effectCurrent); stateChanged = true;}
if (effectPalette != selsegPrev.palette) {seg.setPalette(effectPalette); stateChanged = true;}
if (effectCurrent != selsegPrev.mode) {seg.setMode(effectCurrent); stateChanged = true;}
uint32_t col0 = RGBW32( col[0], col[1], col[2], col[3]);
uint32_t col1 = RGBW32(colSec[0], colSec[1], colSec[2], colSec[3]);
if (col0 != selsegPrev.colors[0]) {seg.setColor(0, col0); stateChanged = true;}

View File

@ -233,7 +233,7 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
gammaCorrectCol = request->hasArg(F("GC"));
gammaCorrectVal = request->arg(F("GV")).toFloat();
if (gammaCorrectVal > 1.0f && gammaCorrectVal <= 3)
calcGammaTable(gammaCorrectVal);
NeoGammaWLEDMethod::calcGammaTable(gammaCorrectVal);
else {
gammaCorrectVal = 1.0f; // no gamma correction
gammaCorrectBri = false;

View File

@ -197,7 +197,7 @@ void appendGPIOinfo() {
#elif defined(CONFIG_IDF_TARGET_ESP32C3)
oappend(SET_F("d.rsvd=[11,12,13,14,15,16,17"));
#elif defined(ESP32)
oappend(SET_F("d.rsvd=[6,7,8,9,10,11,24,28,29,30,31"));
oappend(SET_F("d.rsvd=[6,7,8,9,10,11,24,28,29,30,31,37,38"));
#else
oappend(SET_F("d.rsvd=[6,7,8,9,10,11"));
#endif