From 39b7b3ad53f4a96c7b8ab3a75934708ca547fcf0 Mon Sep 17 00:00:00 2001 From: Blaz Kristan Date: Wed, 20 Oct 2021 20:29:13 +0200 Subject: [PATCH 01/72] CCT (color white balance support) --- wled00/FX.h | 3 +- wled00/FX_fcn.cpp | 17 +++- wled00/bus_manager.h | 160 ++++++++++++++++++---------------- wled00/cfg.cpp | 4 +- wled00/data/index.css | 52 ++++++++++- wled00/data/index.htm | 17 ++-- wled00/data/index.js | 35 ++++---- wled00/data/settings_leds.htm | 72 ++++++++++++++- wled00/set.cpp | 1 + wled00/wled.h | 9 +- 10 files changed, 254 insertions(+), 116 deletions(-) diff --git a/wled00/FX.h b/wled00/FX.h index 4bdec48f9a..8a9691815f 100644 --- a/wled00/FX.h +++ b/wled00/FX.h @@ -247,7 +247,7 @@ class WS2812FX { // segment parameters public: - typedef struct Segment { // 29 (32 in memory?) bytes + typedef struct Segment { // 30 (33 in memory?) bytes uint16_t start; uint16_t stop; //segment invalid if stop == 0 uint16_t offset; @@ -259,6 +259,7 @@ class WS2812FX { uint8_t grouping, spacing; uint8_t opacity; uint32_t colors[NUM_COLORS]; + uint8_t cct; char *name; bool setColor(uint8_t slot, uint32_t c, uint8_t segn) { //returns true if changed if (slot >= NUM_COLORS || segn >= MAX_NUM_SEGMENTS) return false; diff --git a/wled00/FX_fcn.cpp b/wled00/FX_fcn.cpp index f89df5ed1f..05914e38e2 100644 --- a/wled00/FX_fcn.cpp +++ b/wled00/FX_fcn.cpp @@ -220,6 +220,17 @@ void WS2812FX::setPixelColor(uint16_t i, byte r, byte g, byte b, byte w) uint16_t realIndex = realPixelIndex(i); uint16_t len = SEGMENT.length(); + // determine if we can do white balance + int16_t cct = -1; + for (uint8_t b = 0; b < busses.getNumBusses(); b++) { + Bus *bus = busses.getBus(b); + if (bus == nullptr || !bus->containsPixel(realIndex)) continue; + if (allowCCT || bus->getType() == TYPE_ANALOG_2CH || bus->getType() == TYPE_ANALOG_5CH) { + cct = SEGMENT.cct; + break; + } + } + for (uint16_t j = 0; j < SEGMENT.grouping; j++) { uint16_t indexSet = realIndex + (IS_REVERSE ? -j : j); if (indexSet >= SEGMENT.start && indexSet < SEGMENT.stop) { @@ -230,14 +241,14 @@ void WS2812FX::setPixelColor(uint16_t i, byte r, byte g, byte b, byte w) if (indexMir >= SEGMENT.stop) indexMir -= len; if (indexMir < customMappingSize) indexMir = customMappingTable[indexMir]; - busses.setPixelColor(indexMir, col); + busses.setPixelColor(indexMir, col, cct); } /* offset/phase */ indexSet += SEGMENT.offset; if (indexSet >= SEGMENT.stop) indexSet -= len; if (indexSet < customMappingSize) indexSet = customMappingTable[indexSet]; - busses.setPixelColor(indexSet, col); + busses.setPixelColor(indexSet, col, cct); } } } else { //live data, etc. @@ -623,6 +634,7 @@ void WS2812FX::resetSegments() { _segments[0].setOption(SEG_OPTION_SELECTED, 1); _segments[0].setOption(SEG_OPTION_ON, 1); _segments[0].opacity = 255; + _segments[0].cct = 128; for (uint16_t i = 1; i < MAX_NUM_SEGMENTS; i++) { @@ -630,6 +642,7 @@ void WS2812FX::resetSegments() { _segments[i].grouping = 1; _segments[i].setOption(SEG_OPTION_ON, 1); _segments[i].opacity = 255; + _segments[i].cct = 128; _segments[i].speed = DEFAULT_SPEED; _segments[i].intensity = DEFAULT_INTENSITY; _segment_runtimes[i].reset(); diff --git a/wled00/bus_manager.h b/wled00/bus_manager.h index 799019f30f..ffe6a04fea 100644 --- a/wled00/bus_manager.h +++ b/wled00/bus_manager.h @@ -10,6 +10,9 @@ #include "bus_wrapper.h" #include +//color.cpp +uint32_t colorBalanceFromKelvin(uint16_t kelvin, uint32_t rgb); + // enable additional debug output #ifdef WLED_DEBUG #ifndef ESP8266 @@ -65,79 +68,48 @@ struct BusConfig { //parent class of BusDigital and BusPwm class Bus { public: - Bus(uint8_t type, uint16_t start) { - _type = type; - _start = start; - }; - - virtual void show() {} - virtual bool canShow() { return true; } - - virtual void setPixelColor(uint16_t pix, uint32_t c) {}; - - virtual void setBrightness(uint8_t b) {}; - - virtual uint32_t getPixelColor(uint16_t pix) { return 0; }; - - virtual void cleanup() {}; - - virtual ~Bus() { //throw the bus under the bus - } - - virtual uint8_t getPins(uint8_t* pinArray) { return 0; } - - inline uint16_t getStart() { - return _start; - } - - inline void setStart(uint16_t start) { - _start = start; - } - - virtual uint16_t getLength() { - return 1; - } - - virtual void setColorOrder() {} - - virtual uint8_t getColorOrder() { - return COL_ORDER_RGB; - } - - virtual bool isRgbw() { - return false; - } - - virtual uint8_t skippedLeds() { - return 0; - } - - inline uint8_t getType() { - return _type; - } - - inline bool isOk() { - return _valid; - } - - static bool isRgbw(uint8_t type) { - if (type == TYPE_SK6812_RGBW || type == TYPE_TM1814) return true; - if (type > TYPE_ONOFF && type <= TYPE_ANALOG_5CH && type != TYPE_ANALOG_3CH) return true; - return false; - } + Bus(uint8_t type, uint16_t start) { + _type = type; + _start = start; + }; - inline bool isOffRefreshRequired() { - return _needsRefresh; - } + virtual ~Bus() {} //throw the bus under the bus + + virtual void show() {} + virtual bool canShow() { return true; } + virtual void setPixelColor(uint16_t pix, uint32_t c) {}; + virtual void setPixelColor(uint16_t pix, uint32_t c, uint8_t cct) {}; + virtual uint32_t getPixelColor(uint16_t pix) { return 0; }; + virtual void setBrightness(uint8_t b) {}; + virtual void cleanup() {}; + virtual uint8_t getPins(uint8_t* pinArray) { return 0; } + virtual uint16_t getLength() { return 1; } + virtual void setColorOrder() {} + virtual uint8_t getColorOrder() { return COL_ORDER_RGB; } + virtual uint8_t skippedLeds() { return 0; } + + inline uint16_t getStart() { return _start; } + inline void setStart(uint16_t start) { _start = start; } + inline uint8_t getType() { return _type; } + inline bool isOk() { return _valid; } + inline bool isOffRefreshRequired() { return _needsRefresh; } + inline bool containsPixel(uint16_t pix) { return pix >= _start; } + + virtual bool isRgbw() { return false; } + static bool isRgbw(uint8_t type) { + if (type == TYPE_SK6812_RGBW || type == TYPE_TM1814) return true; + if (type > TYPE_ONOFF && type <= TYPE_ANALOG_5CH && type != TYPE_ANALOG_3CH) return true; + return false; + } - bool reversed = false; + bool reversed = false; protected: - uint8_t _type = TYPE_NONE; - uint8_t _bri = 255; - uint16_t _start = 0; - bool _valid = false; - bool _needsRefresh = false; + uint8_t _type = TYPE_NONE; + uint8_t _bri = 255; + uint16_t _start = 0; + bool _valid = false; + bool _needsRefresh = false; }; @@ -190,6 +162,11 @@ class BusDigital : public Bus { PolyBus::setPixelColor(_busPtr, _iType, pix, c, _colorOrder); } + void setPixelColor(uint16_t pix, uint32_t c, uint8_t cct) { + c = colorBalanceFromKelvin(2000+(cct<<5), c); // color correction from CCT + setPixelColor(pix, c); + } + uint32_t getPixelColor(uint16_t pix) { if (reversed) pix = _len - pix -1; else pix += _skip; @@ -285,6 +262,34 @@ class BusPwm : public Bus { _valid = true; }; + void setPixelColor(uint16_t pix, uint32_t c, uint8_t cct) { + if (pix != 0 || !_valid) return; //only react to first pixel + c = colorBalanceFromKelvin(2000+(cct<<5), c); // color correction from CCT (w remains unchanged) + uint8_t r = c >> 16; + uint8_t g = c >> 8; + uint8_t b = c ; + uint8_t w = c >> 24; + + switch (_type) { + case TYPE_ANALOG_1CH: //one channel (white), use highest RGBW value + _data[0] = max(r, max(g, max(b, w))); + break; + case TYPE_ANALOG_2CH: //warm white + cold white + // perhaps a non-linear adjustment would be in order. need to test + _data[1] = (w * cct) / 255; + _data[0] = 255 - _data[1]; // or (w * (255-cct)) / 255; + break; + case TYPE_ANALOG_5CH: //RGB + warm white + cold white + // perhaps a non-linear adjustment would be in order. need to test + _data[4] = (w * cct) / 255; w = 255 - w; // or (w * (255-cct)) / 255; + case TYPE_ANALOG_4CH: //RGBW + _data[3] = w; + case TYPE_ANALOG_3CH: //standard dumb RGB + _data[0] = r; _data[1] = g; _data[2] = b; + break; + } + } + void setPixelColor(uint16_t pix, uint32_t c) { if (pix != 0 || !_valid) return; //only react to first pixel uint8_t r = c >> 16; @@ -295,14 +300,11 @@ class BusPwm : public Bus { switch (_type) { case TYPE_ANALOG_1CH: //one channel (white), use highest RGBW value _data[0] = max(r, max(g, max(b, w))); break; - - case TYPE_ANALOG_2CH: //warm white + cold white, we'll need some nice handling here, for now just R+G channels + case TYPE_ANALOG_2CH: //warm white + cold white case TYPE_ANALOG_3CH: //standard dumb RGB - case TYPE_ANALOG_4CH: //RGBW + case TYPE_ANALOG_4CH: //standard dumb RGBW case TYPE_ANALOG_5CH: //we'll want the white handling from 2CH here + RGB _data[0] = r; _data[1] = g; _data[2] = b; _data[3] = w; _data[4] = 0; break; - - default: return; } } @@ -417,6 +419,11 @@ class BusNetwork : public Bus { if (_rgbw) _data[offset+3] = 0xFF & (c >> 24); } + void setPixelColor(uint16_t pix, uint32_t c, uint8_t cct) { + c = colorBalanceFromKelvin(2000+(cct<<5), c); // color correction from CCT + setPixelColor(pix, c); + } + uint32_t getPixelColor(uint16_t pix) { if (!_valid || pix >= _len) return 0; uint16_t offset = pix * _UDPchannels; @@ -538,12 +545,13 @@ class BusManager { } } - void setPixelColor(uint16_t pix, uint32_t c) { + void setPixelColor(uint16_t pix, uint32_t c, int16_t cct=-1) { for (uint8_t i = 0; i < numBusses; i++) { Bus* b = busses[i]; uint16_t bstart = b->getStart(); if (pix < bstart || pix >= bstart + b->getLength()) continue; - busses[i]->setPixelColor(pix - bstart, c); + if (cct<0) busses[i]->setPixelColor(pix - bstart, c); // no white balance + else busses[i]->setPixelColor(pix - bstart, c, cct); // do white balance } } diff --git a/wled00/cfg.cpp b/wled00/cfg.cpp index 13bcdd9642..d0bbc6f8e0 100644 --- a/wled00/cfg.cpp +++ b/wled00/cfg.cpp @@ -84,6 +84,7 @@ bool deserializeConfig(JsonObject doc, bool fromFS) { CJSON(strip.ablMilliampsMax, hw_led[F("maxpwr")]); CJSON(strip.milliampsPerLed, hw_led[F("ledma")]); CJSON(strip.rgbwMode, hw_led[F("rgbwm")]); + CJSON(allowCCT, hw_led["cct"]); JsonArray ins = hw_led["ins"]; @@ -530,6 +531,7 @@ void serializeConfig() { hw_led[F("maxpwr")] = strip.ablMilliampsMax; hw_led[F("ledma")] = strip.milliampsPerLed; hw_led[F("rgbwm")] = strip.rgbwMode; + hw_led["cct"] = allowCCT; JsonArray hw_led_ins = hw_led.createNestedArray("ins"); @@ -546,7 +548,7 @@ void serializeConfig() { ins[F("order")] = bus->getColorOrder(); ins["rev"] = bus->reversed; ins[F("skip")] = bus->skippedLeds(); - ins["type"] = bus->getType() & 0x7F;; + ins["type"] = bus->getType() & 0x7F; ins["ref"] = bus->isOffRefreshRequired(); ins[F("rgbw")] = bus->isRgbw(); } diff --git a/wled00/data/index.css b/wled00/data/index.css index f27024ac83..cb5aac3d1d 100644 --- a/wled00/data/index.css +++ b/wled00/data/index.css @@ -467,6 +467,22 @@ img { z-index: -1; } +#rwrap .sliderdisplay, +#gwrap .sliderdisplay, +#bwrap .sliderdisplay, +#wwrap .sliderdisplay, +#wbal .sliderdisplay { + height: 28px; + top: 0; bottom: 0; + left: 0; right: 0; + /*border: 1px solid var(--c-b);*/ +} +#rwrap .sliderdisplay { background: linear-gradient(90deg, #000 0%, #f00); } +#gwrap .sliderdisplay { background: linear-gradient(90deg, #000 0%, #0f0); } +#bwrap .sliderdisplay { background: linear-gradient(90deg, #000 0%, #00f); } +#wwrap .sliderdisplay { background: linear-gradient(90deg, #000 0%, #fff); } +#wbal .sliderdisplay { background: linear-gradient(90deg, #ff8f1f 0%, #fff 50%, #d4e0ff); } + .sliderbubble { width: 36px; line-height: 24px; @@ -492,6 +508,14 @@ input[type=range] { background-color: transparent; cursor: pointer; } +#rwrap input[type=range], +#gwrap input[type=range], +#bwrap input[type=range], +#wwrap input[type=range], +#wbal input[type=range] { + width: 252px; + margin: 0; +} input[type=range]:focus { outline: none; } @@ -527,8 +551,24 @@ input[type=range]:active + .sliderbubble { display: inline; transform: translateX(-50%); } - -#wwrap { +#rwrap input[type=range]::-webkit-slider-thumb, +#gwrap input[type=range]::-webkit-slider-thumb, +#bwrap input[type=range]::-webkit-slider-thumb, +#wwrap input[type=range]::-webkit-slider-thumb, +#wbal input[type=range]::-webkit-slider-thumb { + height: 18px; + width: 18px; + border: 2px solid #000; + margin-top: 5px; +} +#rwrap input[type=range]::-moz-range-thumb, +#gwrap input[type=range]::-moz-range-thumb, +#bwrap input[type=range]::-moz-range-thumb, +#wwrap input[type=range]::-moz-range-thumb, +#wbal input[type=range]::-moz-range-thumb { + border: 2px solid var(--c-1); +} +#wwrap, #wbal { display: none; } @@ -537,6 +577,14 @@ input[type=range]:active + .sliderbubble { width: 240px; position: relative; } +#rwrap .sliderwrap, +#gwrap .sliderwrap, +#bwrap .sliderwrap, +#wwrap .sliderwrap, +#wbal .sliderwrap { + width: 260px; + margin: 10px 0 0; +} .sbs { margin: 0px -20px 5px -6px; diff --git a/wled00/data/index.htm b/wled00/data/index.htm index 530f4cd443..5be885f7ea 100644 --- a/wled00/data/index.htm +++ b/wled00/data/index.htm @@ -47,22 +47,29 @@
- +

- +

- +

White channel

- + +
+
+
+
+

White balance

+
+
@@ -98,7 +105,7 @@
diff --git a/wled00/data/index.js b/wled00/data/index.js index 7bf47b25e4..26d1afe39b 100644 --- a/wled00/data/index.js +++ b/wled00/data/index.js @@ -51,7 +51,7 @@ var cpick = new iro.ColorPicker("#picker", { options: { sliderType: 'value' } - }, + }/*, { component: iro.ui.Slider, options: { @@ -59,7 +59,7 @@ var cpick = new iro.ColorPicker("#picker", { minTemperature: 2100, maxTemperature: 10000 } - } + }*/ ] }); @@ -854,21 +854,14 @@ function loadNodes() }); } -function updateTrail(e, slidercol) +function updateTrail(e) { if (e==null) return; var max = e.hasAttribute('max') ? e.attributes.max.value : 255; var perc = e.value * 100 / max; perc = parseInt(perc); if (perc < 50) perc += 2; - var scol; - switch (slidercol) { - case 1: scol = "#f00"; break; - case 2: scol = "#0f0"; break; - case 3: scol = "#00f"; break; - default: scol = "var(--c-f)"; - } - var val = `linear-gradient(90deg, ${scol} ${perc}%, var(--c-4) ${perc}%)`; + var val = `linear-gradient(90deg, var(--c-f) ${perc}%, var(--c-4) ${perc}%)`; e.parentNode.getElementsByClassName('sliderdisplay')[0].style.background = val; } @@ -939,8 +932,8 @@ function updateUI() updateTrail(d.getElementById('sliderBri')); updateTrail(d.getElementById('sliderSpeed')); updateTrail(d.getElementById('sliderIntensity')); - updateTrail(d.getElementById('sliderW')); - if (isRgbw) d.getElementById('wwrap').style.display = "block"; + d.getElementById('wwrap').style.display = (isRgbw) ? "block":"none"; + d.getElementById("wbal").style.display = (lastinfo.leds.cct) ? "block":"none"; updatePA(); updateHex(); @@ -1023,6 +1016,7 @@ function readState(s,command=false) { selectSlot(csel); } d.getElementById('sliderW').value = whites[csel]; + if (i.cct && i.cct>=0) d.getElementById("sliderA").value = i.cct; d.getElementById('sliderSpeed').value = i.sx; d.getElementById('sliderIntensity').value = i.ix; @@ -1560,6 +1554,11 @@ function setSegBri(s){ requestJson(obj); } +function setBalance(b) +{ + var obj = {"seg": {"cct": parseInt(b)}}; + requestJson(obj); +} function setX(ind = null) { if (ind === null) { ind = parseInt(d.querySelector('#fxlist input[name="fx"]:checked').value); @@ -1723,7 +1722,6 @@ function selectSlot(b) { cd[csel].style.width="50px"; cpick.color.set(cd[csel].style.backgroundColor); d.getElementById('sliderW').value = whites[csel]; - updateTrail(d.getElementById('sliderW')); updateHex(); updateRgb(); redrawPalPrev(); @@ -1748,12 +1746,9 @@ function pC(col) function updateRgb() { var col = cpick.color.rgb; - var s = d.getElementById('sliderR'); - s.value = col.r; updateTrail(s,1); - s = d.getElementById('sliderG'); - s.value = col.g; updateTrail(s,2); - s = d.getElementById('sliderB'); - s.value = col.b; updateTrail(s,3); + d.getElementById('sliderR').value = col.r; + d.getElementById('sliderG').value = col.g; + d.getElementById('sliderB').value = col.b; } function updateHex() diff --git a/wled00/data/settings_leds.htm b/wled00/data/settings_leds.htm index 187fd289fe..650d0d7cde 100644 --- a/wled00/data/settings_leds.htm +++ b/wled00/data/settings_leds.htm @@ -247,8 +247,8 @@ gId('m0').innerHTML = memu; bquot = memu / maxM * 100; gId('dbar').style.background = `linear-gradient(90deg, ${bquot > 60 ? (bquot > 90 ? "red":"orange"):"#ccc"} 0 ${bquot}%%, #444 ${bquot}%% 100%%)`; - gId('ledwarning').style.display = (sLC > maxPB || maxLC > 800 || bquot > 80) ? 'inline':'none'; - gId('ledwarning').style.color = (sLC > maxPB || maxLC > maxPB || bquot > 100) ? 'red':'orange'; + gId('ledwarning').style.display = (maxLC > Math.min(maxPB,800) || bquot > 80) ? 'inline':'none'; + gId('ledwarning').style.color = (maxLC > Math.max(maxPB,800) || bquot > 100) ? 'red':'orange'; gId('wreason').innerHTML = (bquot > 80) ? "80% of max. LED memory" +(bquot>100 ? ` (ERROR: Using over ${maxM}B!)` : "") : "800 LEDs per output"; // calculate power var val = Math.ceil((100 + sPC * laprev)/500)/2; @@ -389,11 +389,71 @@ req.send(formData); d.Sf.data.value = ''; return false; + } + // https://stackoverflow.com/questions/7346563/loading-local-json-file + function loadCfg(o) { + var f, fr; + + if (typeof window.FileReader !== 'function') { + alert("The file API isn't supported on this browser yet."); + return; + } + + if (!o.files) { + alert("This browser doesn't seem to support the `files` property of file inputs."); + } else if (!o.files[0]) { + alert("Please select a JSON file before clicking 'Apply'"); + } else { + f = o.files[0]; + fr = new FileReader(); + fr.onload = receivedText; + fr.readAsText(f); + } + o.value = ''; + + function receivedText(e) { + let lines = e.target.result; + var 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; + l.ins.forEach((v,i,a)=>{ + addLEDs(1); + for (var j=0; j{ + 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; + } + if (c.hw.relay) { + d.getElementsByName("RL")[0].value = c.hw.relay.pin; + d.getElementsByName("RM")[0].checked = c.hw.relay.inv; + } + UI(); + } + } } function GetV() { //values injected by server while sending HTML - //d.um_p=[6,7,8,9,10,11,1];bLimits(3,4096,4000,1664);d.Sf.MS.checked=1;addLEDs(1);d.Sf.L00.value=2;d.Sf.LC0.value=30;d.Sf.LT0.value=22;d.Sf.CO0.value=0;d.Sf.LS0.value=15;d.Sf.CV0.checked=1;d.Sf.SL0.checked=0;addLEDs(1);d.Sf.L01.value=10;d.Sf.L11.value=10;d.Sf.L21.value=1;d.Sf.L31.value=10;d.Sf.LC1.value=60;d.Sf.LT1.value=80;d.Sf.CO1.value=1;d.Sf.LS1.value=0;d.Sf.CV1.checked=0;d.Sf.SL1.checked=0;d.Sf.MA.value=850;d.Sf.LA.value=0;d.Sf.CA.value=56;d.Sf.AW.value=3;d.Sf.BO.checked=1;d.Sf.BP.value=80;d.Sf.GB.checked=0;d.Sf.GC.checked=1;d.Sf.TF.checked=1;d.Sf.TD.value=700;d.Sf.PF.checked=0;d.Sf.BF.value=100;d.Sf.TB.value=0;d.Sf.TL.value=60;d.Sf.TW.value=0;d.Sf.PB.selectedIndex=0;d.Sf.RL.value=12;d.Sf.RM.checked=1;addBtn(0,0,0);addBtn(1,-1,0);d.Sf.TT.value=32;d.Sf.IR.value=-1;d.Sf.IT.value=0; + //d.um_p=[6,7,8,9,10,11,14,15,13,1,21,19,22,25,26,27,5,23,18,17];bLimits(10,2048,64000,8192);d.Sf.MS.checked=1;d.Sf.CCT.checked=0;addLEDs(1);d.Sf.L00.value=192;d.Sf.L10.value=168;d.Sf.L20.value=0;d.Sf.L30.value=61;d.Sf.LC0.value=421;d.Sf.LT0.value=80;d.Sf.CO0.value=1;d.Sf.LS0.value=0;d.Sf.CV0.checked=0;d.Sf.SL0.checked=0;d.Sf.RF0.checked=0;d.Sf.MA.value=850;d.Sf.LA.value=0;d.Sf.CA.value=127;d.Sf.AW.value=3;d.Sf.BO.checked=0;d.Sf.BP.value=0;d.Sf.GB.checked=0;d.Sf.GC.checked=1;d.Sf.TF.checked=1;d.Sf.TD.value=700;d.Sf.PF.checked=1;d.Sf.BF.value=100;d.Sf.TB.value=0;d.Sf.TL.value=60;d.Sf.TW.value=1;d.Sf.PB.selectedIndex=0;d.Sf.RL.value=-1;d.Sf.RM.checked=1;addBtn(0,-1,0);addBtn(1,-1,0);addBtn(2,-1,0);addBtn(3,-1,0);d.Sf.TT.value=32;d.Sf.IR.value=-1;d.Sf.IT.value=8; }

WLED Software Update

-Installed version: 0.13.0-b4
Download the latest binary: Download the latest binary:


Incorrect configuration may require a factory reset or re-flashing of your ESP.
For security reasons, passwords are not backed up.

About

WLED - version 0.13.0-b4


Contributors, dependencies and special thanks
A huge thank you to everyone who helped me create WLED!

diff --git a/wled00/improv.cpp b/wled00/improv.cpp index cf31cc0390..56ee9e0a05 100644 --- a/wled00/improv.cpp +++ b/wled00/improv.cpp @@ -189,7 +189,7 @@ void sendImprovInfoResponse() { out[11] = 4; //Firmware len ("WLED") out[12] = 'W'; out[13] = 'L'; out[14] = 'E'; out[15] = 'D'; uint8_t lengthSum = 17; - uint8_t vlen = sprintf_P(out+lengthSum,PSTR("0.13.0-b4/%i"),VERSION); + uint8_t vlen = sprintf_P(out+lengthSum,PSTR("0.13.0-b5/%i"),VERSION); out[16] = vlen; lengthSum += vlen; uint8_t hlen = 7; #ifdef ESP8266 diff --git a/wled00/json.cpp b/wled00/json.cpp index 72cb4be044..4ac81fe77a 100644 --- a/wled00/json.cpp +++ b/wled00/json.cpp @@ -501,7 +501,7 @@ void serializeInfo(JsonObject root) leds[F("fps")] = strip.getFps(); leds[F("maxpwr")] = (strip.currentMilliamps)? strip.ablMilliampsMax : 0; leds[F("maxseg")] = strip.getMaxSegments(); - leds[F("seglock")] = false; //will be used in the future to prevent modifications to segment config + //leds[F("seglock")] = false; //might be used in the future to prevent modifications to segment config root[F("str")] = syncToggleReceive; @@ -563,7 +563,7 @@ void serializeInfo(JsonObject root) root[F("resetReason0")] = (int)rtc_get_reset_reason(0); root[F("resetReason1")] = (int)rtc_get_reset_reason(1); #endif - root[F("lwip")] = 0; + root[F("lwip")] = 0; //deprecated #else root[F("arch")] = "esp8266"; root[F("core")] = ESP.getCoreVersion(); diff --git a/wled00/wled.h b/wled00/wled.h index 331bcfead7..d173ff27af 100644 --- a/wled00/wled.h +++ b/wled00/wled.h @@ -3,12 +3,12 @@ /* Main sketch, global variable declarations @title WLED project sketch - @version 0.13.0-b4 + @version 0.13.0-b5 @author Christian Schwinne */ // version code in format yymmddb (b = daily build) -#define VERSION 2110110 +#define VERSION 2111170 //uncomment this if you have a "my_config.h" file you'd like to use //#define WLED_USE_MY_CONFIG From 4af1f62aab42c4ad0458b06d19fc8a71e4c7a3c6 Mon Sep 17 00:00:00 2001 From: cschwinne Date: Wed, 17 Nov 2021 01:19:04 +0100 Subject: [PATCH 21/72] Revert testing PIO changes --- platformio.ini | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/platformio.ini b/platformio.ini index 52142fc7b0..e700ed2be8 100644 --- a/platformio.ini +++ b/platformio.ini @@ -290,20 +290,20 @@ lib_deps = ${esp8266.lib_deps} [env:esp32dev] board = esp32dev -platform = espressif32@3.3.2 +platform = espressif32@2.0 build_unflags = ${common.build_unflags} build_flags = ${common.build_flags_esp32} -D WLED_RELEASE_NAME=ESP32 #-D WLED_DISABLE_BROWNOUT_DET lib_deps = ${esp32.lib_deps} -#board_build.partitions = ${esp32.default_partitions} +board_build.partitions = ${esp32.default_partitions} [env:esp32_eth] board = esp32-poe -platform = espressif32@3.3.2 +platform = espressif32@2.0 upload_speed = 921600 build_unflags = ${common.build_unflags} build_flags = ${common.build_flags_esp32} -D WLED_RELEASE_NAME=ESP32_Ethernet -D RLYPIN=-1 -D WLED_USE_ETHERNET -D BTNPIN=-1 lib_deps = ${esp32.lib_deps} -#board_build.partitions = ${esp32.default_partitions} +board_build.partitions = ${esp32.default_partitions} [env:esp32s2_saola] board = esp32dev From d31e4c7815cc2997fb983682d434fa430db6bc5c Mon Sep 17 00:00:00 2001 From: cschwinne Date: Wed, 17 Nov 2021 11:13:07 +0100 Subject: [PATCH 22/72] Added getPinOwner Only disable builtin LED if bus to avoid breaking other things on the pin --- wled00/button.cpp | 7 +++++-- wled00/pin_manager.cpp | 5 +++++ wled00/pin_manager.h | 2 ++ 3 files changed, 12 insertions(+), 2 deletions(-) diff --git a/wled00/button.cpp b/wled00/button.cpp index 39c1e4f7fd..aef18c77e7 100644 --- a/wled00/button.cpp +++ b/wled00/button.cpp @@ -299,8 +299,11 @@ void handleIO() #ifdef ESP8266 // turn off built-in LED if strip is turned off // this will break digital bus so will need to be reinitialised on On - pinMode(LED_BUILTIN, OUTPUT); - digitalWrite(LED_BUILTIN, HIGH); + PinOwner ledPinOwner = pinManager.getPinOwner(LED_BUILTIN); + if (!strip.isOffRefreshRequred && (ledPinOwner == PinOwner::None || ledPinOwner == PinOwner::BusDigital)) { + pinMode(LED_BUILTIN, OUTPUT); + digitalWrite(LED_BUILTIN, HIGH); + } #endif if (rlyPin>=0) { pinMode(rlyPin, OUTPUT); diff --git a/wled00/pin_manager.cpp b/wled00/pin_manager.cpp index 38e6e1621f..ece48c2485 100644 --- a/wled00/pin_manager.cpp +++ b/wled00/pin_manager.cpp @@ -125,6 +125,11 @@ bool PinManagerClass::isPinOk(byte gpio, bool output) return false; } +PinOwner PinManagerClass::getPinOwner(byte gpio) { + if (!isPinOk(gpio, false)) return PinOwner::None; + return ownerTag[gpio]; +} + #ifdef ARDUINO_ARCH_ESP32 byte PinManagerClass::allocateLedc(byte channels) { diff --git a/wled00/pin_manager.h b/wled00/pin_manager.h index 63f6d95e87..65d3196649 100644 --- a/wled00/pin_manager.h +++ b/wled00/pin_manager.h @@ -92,6 +92,8 @@ class PinManagerClass { bool isPinAllocated(byte gpio, PinOwner tag = PinOwner::None); bool isPinOk(byte gpio, bool output = true); + PinOwner getPinOwner(byte gpio); + #ifdef ARDUINO_ARCH_ESP32 byte allocateLedc(byte channels); void deallocateLedc(byte pos, byte channels); From 31b7cdff9b61f6ab4088c09a8989d9ac2d609875 Mon Sep 17 00:00:00 2001 From: cschwinne Date: Fri, 19 Nov 2021 12:34:14 +0100 Subject: [PATCH 23/72] Change effect names to be more consistent --- CHANGELOG.md | 9 ++++++++- wled00/FX.h | 4 ++-- wled00/const.h | 6 +++--- wled00/wled.h | 2 +- 4 files changed, 14 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5582843df9..ff04e1de1f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,13 @@ ### Builds after release 0.12.0 -#### Build 2111160 +#### Build 2111190 + +- Changed default ESP32 LED pin from 16 to 2 +- Renamed "Running 2" to "Chase 2" +- Renamed "Tri Chase" to "Chase 3" + +#### Build 2111170 - Version bump to 0.13.0-b5 "Toki" - Improv Serial support (PR #2334) @@ -388,6 +394,7 @@ - Added support for WESP32 ethernet board (PR #1764) - Added Caching for main UI (PR #1704) - Added Tetrix mode (PR #1729) +- Removed Merry Christmas mode (use "Chase 2" - called Running 2 before 0.13.0) - Added memory check on Bus creation #### Build 2102050 diff --git a/wled00/FX.h b/wled00/FX.h index 4bdec48f9a..2c12740613 100644 --- a/wled00/FX.h +++ b/wled00/FX.h @@ -912,9 +912,9 @@ const char JSON_mode_names[] PROGMEM = R"=====([ "Solid","Blink","Breathe","Wipe","Wipe Random","Random Colors","Sweep","Dynamic","Colorloop","Rainbow", "Scan","Scan Dual","Fade","Theater","Theater Rainbow","Running","Saw","Twinkle","Dissolve","Dissolve Rnd", "Sparkle","Sparkle Dark","Sparkle+","Strobe","Strobe Rainbow","Strobe Mega","Blink Rainbow","Android","Chase","Chase Random", -"Chase Rainbow","Chase Flash","Chase Flash Rnd","Rainbow Runner","Colorful","Traffic Light","Sweep Random","Running 2","Aurora","Stream", +"Chase Rainbow","Chase Flash","Chase Flash Rnd","Rainbow Runner","Colorful","Traffic Light","Sweep Random","Chase 2","Aurora","Stream", "Scanner","Lighthouse","Fireworks","Rain","Tetrix","Fire Flicker","Gradient","Loading","Police","Police All", -"Two Dots","Two Areas","Running Dual","Halloween","Tri Chase","Tri Wipe","Tri Fade","Lightning","ICU","Multi Comet", +"Two Dots","Two Areas","Running Dual","Halloween","Chase 3","Tri Wipe","Tri Fade","Lightning","ICU","Multi Comet", "Scanner Dual","Stream 2","Oscillate","Pride 2015","Juggle","Palette","Fire 2012","Colorwaves","Bpm","Fill Noise", "Noise 1","Noise 2","Noise 3","Noise 4","Colortwinkles","Lake","Meteor","Meteor Smooth","Railway","Ripple", "Twinklefox","Twinklecat","Halloween Eyes","Solid Pattern","Solid Pattern Tri","Spots","Spots Fade","Glitter","Candle","Fireworks Starburst", diff --git a/wled00/const.h b/wled00/const.h index 37afca7f61..5b834bf364 100644 --- a/wled00/const.h +++ b/wled00/const.h @@ -313,15 +313,15 @@ #ifdef ESP8266 #define LEDPIN 2 // GPIO2 (D4) on Wemod D1 mini compatible boards #else - #define LEDPIN 16 // aligns with GPIO2 (D4) on Wemos D1 mini32 compatible boards + #define LEDPIN 2 // Changed from 16 to restore compatibility with ESP32-pico #endif #endif #ifdef WLED_ENABLE_DMX #if (LEDPIN == 2) #undef LEDPIN - #define LEDPIN 3 - #warning "Pin conflict compiling with DMX and LEDs on pin 2. The default LED pin has been changed to pin 3." + #define LEDPIN 1 + #warning "Pin conflict compiling with DMX and LEDs on pin 2. The default LED pin has been changed to pin 1." #endif #endif diff --git a/wled00/wled.h b/wled00/wled.h index d173ff27af..0c53f56c13 100644 --- a/wled00/wled.h +++ b/wled00/wled.h @@ -8,7 +8,7 @@ */ // version code in format yymmddb (b = daily build) -#define VERSION 2111170 +#define VERSION 2111190 //uncomment this if you have a "my_config.h" file you'd like to use //#define WLED_USE_MY_CONFIG From b3324d22f56eecdb24172d239c70e52a02191524 Mon Sep 17 00:00:00 2001 From: cschwinne Date: Sun, 21 Nov 2021 23:46:44 +0100 Subject: [PATCH 24/72] allowCCT performance improvement --- wled00/FX.cpp | 2 +- wled00/FX.h | 2 +- wled00/colors.cpp | 31 ++++++++++++++++++------------- wled00/led.cpp | 1 - wled00/wled.h | 2 +- 5 files changed, 21 insertions(+), 17 deletions(-) diff --git a/wled00/FX.cpp b/wled00/FX.cpp index a2b6a9596a..d0767713bc 100644 --- a/wled00/FX.cpp +++ b/wled00/FX.cpp @@ -2614,7 +2614,7 @@ uint16_t WS2812FX::mode_glitter() -//each needs 11 bytes +//each needs 12 bytes //Spark type is used for popcorn, 1D fireworks, and drip typedef struct Spark { float pos; diff --git a/wled00/FX.h b/wled00/FX.h index 3b6bfab6a2..2195f6e57f 100644 --- a/wled00/FX.h +++ b/wled00/FX.h @@ -247,7 +247,7 @@ class WS2812FX { // segment parameters public: - typedef struct Segment { // 30 (33 in memory?) bytes + typedef struct Segment { // 30 (32 in memory) bytes uint16_t start; uint16_t stop; //segment invalid if stop == 0 uint16_t offset; diff --git a/wled00/colors.cpp b/wled00/colors.cpp index 0377a198f3..e7e2b7d19e 100644 --- a/wled00/colors.cpp +++ b/wled00/colors.cpp @@ -73,7 +73,7 @@ void colorKtoRGB(uint16_t kelvin, byte* rgb) //white spectrum to rgb, calc g = round(288.1221695283 * pow((temp - 60), -0.0755148492)); b = 255; } - //g += 15; //mod by Aircoookie, a bit less accurate but visibly less pinkish + //g += 12; //mod by Aircoookie, a bit less accurate but visibly less pinkish rgb[0] = (uint8_t) constrain(r, 0, 255); rgb[1] = (uint8_t) constrain(g, 0, 255); rgb[2] = (uint8_t) constrain(b, 0, 255); @@ -248,20 +248,25 @@ void colorRGBtoRGBW(byte* rgb) //rgb to rgbw (http://codewelt.com/rgbw). (RGBW_M // adjust RGB values based on color temperature in K (range [2800-10200]) (https://en.wikipedia.org/wiki/Color_balance) void colorBalanceFromKelvin(uint16_t kelvin, byte *rgb) { - byte rgbw[4] = {0,0,0,0}; - colorKtoRGB(kelvin, rgbw); // convert Kelvin to RGB - rgb[0] = ((uint16_t) rgbw[0] * rgb[0]) / 255; // correct R - rgb[1] = ((uint16_t) rgbw[1] * rgb[1]) / 255; // correct G - rgb[2] = ((uint16_t) rgbw[2] * rgb[2]) / 255; // correct B + uint32_t col = RGBW32(rgb[0], rgb[1], rgb[2], 0); + col = colorBalanceFromKelvin(kelvin, col); + rgb[0] = R(col); + rgb[1] = G(col); + rgb[2] = B(col); } +byte correctionRGB[4] = {0,0,0,0}; +uint16_t lastKelvin = 0; + uint32_t colorBalanceFromKelvin(uint16_t kelvin, uint32_t rgb) { - byte rgbw[4] = {0,0,0,0}; - colorKtoRGB(kelvin, rgbw); // convert Kelvin to RGB - rgbw[0] = ((uint16_t) rgbw[0] * R(rgb)) / 255; // correct R - rgbw[1] = ((uint16_t) rgbw[1] * G(rgb)) / 255; // correct G - rgbw[2] = ((uint16_t) rgbw[2] * B(rgb)) / 255; // correct B - rgbw[3] = W(rgb); + //remember so that slow colorKtoRGB() doesn't have to run for every setPixelColor() + if (lastKelvin != kelvin) colorKtoRGB(kelvin, correctionRGB); // convert Kelvin to RGB + lastKelvin = kelvin; + byte rgbw[4]; + rgbw[0] = ((uint16_t) correctionRGB[0] * R(rgb)) /255; // correct R + rgbw[1] = ((uint16_t) correctionRGB[1] * G(rgb)) /255; // correct G + rgbw[2] = ((uint16_t) correctionRGB[2] * B(rgb)) /255; // correct B + rgbw[3] = W(rgb); return colorFromRgbw(rgbw); -} +} \ No newline at end of file diff --git a/wled00/led.cpp b/wled00/led.cpp index 172fabbc72..2c02d01acd 100644 --- a/wled00/led.cpp +++ b/wled00/led.cpp @@ -30,7 +30,6 @@ void toggleOnOff() { briLast = bri; bri = 0; - //unloadPlaylist(); // no longer necessary } } diff --git a/wled00/wled.h b/wled00/wled.h index dbe9d33875..ca3a68964f 100644 --- a/wled00/wled.h +++ b/wled00/wled.h @@ -31,7 +31,7 @@ #ifndef WLED_DISABLE_MQTT #define WLED_ENABLE_MQTT // saves 12kb #endif -//#define WLED_ENABLE_ADALIGHT // saves 500b only (uses GPIO3 (RX) for serial) +#define WLED_ENABLE_ADALIGHT // saves 500b only (uses GPIO3 (RX) for serial) //#define WLED_ENABLE_DMX // uses 3.5kb (use LEDPIN other than 2) #ifndef WLED_DISABLE_LOXONE #define WLED_ENABLE_LOXONE // uses 1.2kb From 80a657965e0726f3ec48b1dc47a8dba0876ed413 Mon Sep 17 00:00:00 2001 From: cschwinne Date: Mon, 22 Nov 2021 21:41:04 +0100 Subject: [PATCH 25/72] Fixed preset cycle not working from preset called by UI --- CHANGELOG.md | 5 +++++ wled00/json.cpp | 6 ++++-- wled00/set.cpp | 10 ++++------ wled00/wled.h | 4 +++- 4 files changed, 16 insertions(+), 9 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ff04e1de1f..ba305fbb86 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,11 @@ ### Builds after release 0.12.0 +#### Build 2111220 + +- Fixed preset cycle not working from preset called by UI +- Reintroduced permanent min. and max. cycle bounds + #### Build 2111190 - Changed default ESP32 LED pin from 16 to 2 diff --git a/wled00/json.cpp b/wled00/json.cpp index 4ac81fe77a..de40f5610f 100644 --- a/wled00/json.cpp +++ b/wled00/json.cpp @@ -339,9 +339,11 @@ bool deserializeState(JsonObject root, byte callMode, byte presetId) deletePreset(ps); } - if (getVal(root["ps"], &presetCycCurr, 1, 5)) { //load preset (clears state request!) + ps = presetCycCurr; + if (getVal(root["ps"], &ps, presetCycMin, presetCycMax)) { //load preset (clears state request!) if (!presetId) unloadPlaylist(); //stop playlist if preset changed manually - applyPreset(presetCycCurr, callMode); + if (ps >= presetCycMin && ps <= presetCycMax) presetCycCurr = ps; + applyPreset(ps, callMode); return stateResponse; } diff --git a/wled00/set.cpp b/wled00/set.cpp index cfd737b959..eda22508d0 100644 --- a/wled00/set.cpp +++ b/wled00/set.cpp @@ -547,6 +547,7 @@ void parseNumber(const char* str, byte* val, byte minv, byte maxv) const char* str2 = strchr(str,'~'); //min/max range (for preset cycle, e.g. "1~5~") if (str2) { byte p2 = atoi(str2+1); + presetCycMin = p1; presetCycMax = p2; while (isdigit((str2+1)[0])) str2++; parseNumber(str2+1, val, p1, p2); } else { @@ -655,17 +656,14 @@ bool handleSet(AsyncWebServerRequest *request, const String& req, bool apply) pos = req.indexOf(F("PS=")); //saves current in preset if (pos > 0) savePreset(getNumVal(&req, pos)); - byte presetCycleMin = 1; - byte presetCycleMax = 5; - pos = req.indexOf(F("P1=")); //sets first preset for cycle - if (pos > 0) presetCycleMin = getNumVal(&req, pos); + if (pos > 0) presetCycMin = getNumVal(&req, pos); pos = req.indexOf(F("P2=")); //sets last preset for cycle - if (pos > 0) presetCycleMax = getNumVal(&req, pos); + if (pos > 0) presetCycMax = getNumVal(&req, pos); //apply preset - if (updateVal(&req, "PL=", &presetCycCurr, presetCycleMin, presetCycleMax)) { + if (updateVal(&req, "PL=", &presetCycCurr, presetCycMin, presetCycMax)) { applyPreset(presetCycCurr); } diff --git a/wled00/wled.h b/wled00/wled.h index 0c53f56c13..54d9743eb3 100644 --- a/wled00/wled.h +++ b/wled00/wled.h @@ -8,7 +8,7 @@ */ // version code in format yymmddb (b = daily build) -#define VERSION 2111190 +#define VERSION 2111220 //uncomment this if you have a "my_config.h" file you'd like to use //#define WLED_USE_MY_CONFIG @@ -515,6 +515,8 @@ WLED_GLOBAL unsigned long presetCycledTime _INIT(0); WLED_GLOBAL int16_t currentPlaylist _INIT(-1); //still used for "PL=~" HTTP API command WLED_GLOBAL byte presetCycCurr _INIT(0); +WLED_GLOBAL byte presetCycMin _INIT(1); +WLED_GLOBAL byte presetCycMax _INIT(5); // realtime WLED_GLOBAL byte realtimeMode _INIT(REALTIME_MODE_INACTIVE); From c8d5218c65809b59d82050d48008bd27d0790277 Mon Sep 17 00:00:00 2001 From: cschwinne Date: Mon, 22 Nov 2021 22:23:51 +0100 Subject: [PATCH 26/72] Updated outdated wiki links in readme --- readme.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/readme.md b/readme.md index 506f7de737..e768210065 100644 --- a/readme.md +++ b/readme.md @@ -51,7 +51,7 @@ A fast and feature-rich implementation of an ESP8266/ESP32 webserver to control See the [documentation on our official site](https://kno.wled.ge)! -[On this page](https://github.com/Aircoookie/WLED/wiki/Learning-the-ropes) you can find excellent tutorials made by the community and helpful tools to help you get your new lamp up and running! +[On this page](https://kno.wled.ge/basics/tutorials/) you can find excellent tutorials made by the community and helpful tools to help you get your new lamp up and running! ## 🖼️ Images @@ -82,7 +82,7 @@ Any | 5v 3-pin ARGB for PC | Any PC RGB device that supports the 5v 3-pin ARGB m ## ✌️ Other Licensed under the MIT license -Credits [here](https://github.com/Aircoookie/WLED/wiki/Contributors-&-About)! +Credits [here](https://kno.wled.ge/about/contributors/)! Uses Linearicons by Perxis! From b97b6dc144207bce7d41392c0c198b25c59f3558 Mon Sep 17 00:00:00 2001 From: cschwinne Date: Tue, 23 Nov 2021 13:17:33 +0100 Subject: [PATCH 27/72] Remove F macro for "ps" --- wled00/cfg.cpp | 4 ++-- wled00/json.cpp | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/wled00/cfg.cpp b/wled00/cfg.cpp index 20bcdbd89c..a72af85bb2 100644 --- a/wled00/cfg.cpp +++ b/wled00/cfg.cpp @@ -220,7 +220,7 @@ bool deserializeConfig(JsonObject doc, bool fromFS) { CJSON(macroNl, light_nl["macro"]); JsonObject def = doc[F("def")]; - CJSON(bootPreset, def[F("ps")]); + CJSON(bootPreset, def["ps"]); CJSON(turnOnAtBoot, def["on"]); // true CJSON(briS, def["bri"]); // 128 @@ -593,7 +593,7 @@ void serializeConfig() { light_nl["macro"] = macroNl; JsonObject def = doc.createNestedObject("def"); - def[F("ps")] = bootPreset; + def["ps"] = bootPreset; def["on"] = turnOnAtBoot; def["bri"] = briS; diff --git a/wled00/json.cpp b/wled00/json.cpp index de40f5610f..1a546c7950 100644 --- a/wled00/json.cpp +++ b/wled00/json.cpp @@ -429,7 +429,7 @@ void serializeState(JsonObject root, bool forPreset, bool includeBri, bool segme if (!forPreset) { if (errorFlag) root[F("error")] = errorFlag; - root[F("ps")] = (currentPreset > 0) ? currentPreset : -1; + root["ps"] = (currentPreset > 0) ? currentPreset : -1; root[F("pl")] = currentPlaylist; usermods.addToJsonState(root); From 97b3c3db7b26b766cf4860336c2b9384876627e0 Mon Sep 17 00:00:00 2001 From: Blaz Kristan Date: Tue, 23 Nov 2021 20:05:51 +0100 Subject: [PATCH 28/72] Incrementing & random effects, palettes via JSON. --- wled00/json.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/wled00/json.cpp b/wled00/json.cpp index 1a546c7950..a30aa68b1f 100644 --- a/wled00/json.cpp +++ b/wled00/json.cpp @@ -148,21 +148,21 @@ void deserializeSegment(JsonObject elem, byte it, byte presetId) //temporary, strip object gets updated via colorUpdated() if (id == strip.getMainSegmentId()) { - byte effectPrev = effectCurrent; - effectCurrent = elem["fx"] | effectCurrent; - if (!presetId && effectCurrent != effectPrev) unloadPlaylist(); //stop playlist if active and FX changed manually + if (getVal(elem["fx"], &effectCurrent, 1, strip.getModeCount())) { //load effect ('r' random, '~' inc/dec, 1-255 exact value) + if (!presetId) unloadPlaylist(); //stop playlist if active and FX changed manually + } effectSpeed = elem[F("sx")] | effectSpeed; effectIntensity = elem[F("ix")] | effectIntensity; - effectPalette = elem["pal"] | effectPalette; + getVal(elem["pal"], &effectPalette, 1, strip.getPaletteCount()); } else { //permanent - byte fx = elem["fx"] | seg.mode; - if (fx != seg.mode && fx < strip.getModeCount()) { + byte fx = seg.mode; + if (getVal(elem["fx"], &fx, 1, strip.getModeCount())) { //load effect ('r' random, '~' inc/dec, 1-255 exact value) strip.setMode(id, fx); if (!presetId) unloadPlaylist(); //stop playlist if active and FX changed manually } seg.speed = elem[F("sx")] | seg.speed; seg.intensity = elem[F("ix")] | seg.intensity; - seg.palette = elem["pal"] | seg.palette; + getVal(elem["pal"], &seg.palette, 1, strip.getPaletteCount()); } JsonArray iarr = elem[F("i")]; //set individual LEDs From ea0f37f5b9c52a38f72c06c3db055c1832a5bc60 Mon Sep 17 00:00:00 2001 From: cschwinne Date: Wed, 24 Nov 2021 11:02:25 +0100 Subject: [PATCH 29/72] CCT bus manager logic simplification CCT from RGB if none set (-1) --- wled00/FX.h | 10 +- wled00/FX_fcn.cpp | 28 +- wled00/bus_manager.h | 84 +- wled00/cfg.cpp | 4 +- wled00/colors.cpp | 52 +- wled00/data/settings_leds.htm | 4 +- wled00/fcn_declare.h | 2 +- wled00/html_settings.h | 7 +- wled00/html_ui.h | 1576 ++++++++++++++++----------------- wled00/json.cpp | 2 +- wled00/set.cpp | 2 +- wled00/wled.h | 4 +- wled00/xml.cpp | 2 +- 13 files changed, 897 insertions(+), 880 deletions(-) diff --git a/wled00/FX.h b/wled00/FX.h index 2195f6e57f..669d03660b 100644 --- a/wled00/FX.h +++ b/wled00/FX.h @@ -165,12 +165,12 @@ #define FX_MODE_FIRE_FLICKER 45 #define FX_MODE_GRADIENT 46 #define FX_MODE_LOADING 47 -#define FX_MODE_POLICE 48 +#define FX_MODE_POLICE 48 // candidate for removal (after below three) #define FX_MODE_POLICE_ALL 49 // candidate for removal #define FX_MODE_TWO_DOTS 50 -#define FX_MODE_TWO_AREAS 51 +#define FX_MODE_TWO_AREAS 51 // candidate for removal #define FX_MODE_RUNNING_DUAL 52 -#define FX_MODE_HALLOWEEN 53 +#define FX_MODE_HALLOWEEN 53 // candidate for removal #define FX_MODE_TRICOLOR_CHASE 54 #define FX_MODE_TRICOLOR_WIPE 55 #define FX_MODE_TRICOLOR_FADE 56 @@ -247,7 +247,7 @@ class WS2812FX { // segment parameters public: - typedef struct Segment { // 30 (32 in memory) bytes + typedef struct Segment { // 31 (32 in memory) bytes uint16_t start; uint16_t stop; //segment invalid if stop == 0 uint16_t offset; @@ -259,7 +259,7 @@ class WS2812FX { uint8_t grouping, spacing; uint8_t opacity; uint32_t colors[NUM_COLORS]; - uint8_t cct; //0==2000K, 255==10160K + int16_t cct; //-1==auto (no RGB balance correction), 0==1900K, 255==10091K char *name; bool setColor(uint8_t slot, uint32_t c, uint8_t segn) { //returns true if changed if (slot >= NUM_COLORS || segn >= MAX_NUM_SEGMENTS) return false; diff --git a/wled00/FX_fcn.cpp b/wled00/FX_fcn.cpp index d516391569..eb7f7b5d5a 100644 --- a/wled00/FX_fcn.cpp +++ b/wled00/FX_fcn.cpp @@ -138,6 +138,7 @@ void WS2812FX::service() { if (!SEGMENT.getOption(SEG_OPTION_FREEZE)) { //only run effect function if not frozen _virtualSegmentLength = SEGMENT.virtualLength(); + busses.setSegmentCCT(SEGMENT.cct, correctWB); _bri_t = SEGMENT.opacity; _colors_t[0] = SEGMENT.colors[0]; _colors_t[1] = SEGMENT.colors[1]; _colors_t[2] = SEGMENT.colors[2]; if (!IS_SEGMENT_ON) _bri_t = 0; for (uint8_t t = 0; t < MAX_NUM_TRANSITIONS; t++) { @@ -156,6 +157,7 @@ void WS2812FX::service() { } } _virtualSegmentLength = 0; + busses.setSegmentCCT(-1); if(doShow) { yield(); show(); @@ -191,21 +193,6 @@ void WS2812FX::setPixelColor(uint16_t i, byte r, byte g, byte b, byte w) uint16_t realIndex = realPixelIndex(i); uint16_t len = SEGMENT.length(); - // determine if we can do white balance and accurate W calc - // NOTE & TODO: does not work correctly with custom mapping if map spans different strips - int16_t cct = -1; - for (uint8_t b = 0; b < busses.getNumBusses(); b++) { - Bus *bus = busses.getBus(b); - if (bus == nullptr || !bus->containsPixel(realIndex)) continue; - //if (bus == nullptr || bus->getStart()getStart()+bus->getLength()>realIndex) continue; - uint8_t busType = bus->getType(); - if (allowCCT - || busType == TYPE_ANALOG_2CH - || busType == TYPE_ANALOG_5CH) { - if (cct<0) cct = SEGMENT.cct; - } - } - //color_blend(getpixel, col, _bri_t); (pseudocode for future blending of segments) if (_bri_t < 255) { r = scale8(r, _bri_t); @@ -226,14 +213,14 @@ void WS2812FX::setPixelColor(uint16_t i, byte r, byte g, byte b, byte w) if (indexMir >= SEGMENT.stop) indexMir -= len; if (indexMir < customMappingSize) indexMir = customMappingTable[indexMir]; - busses.setPixelColor(indexMir, col, cct); + busses.setPixelColor(indexMir, col); } /* offset/phase */ indexSet += SEGMENT.offset; if (indexSet >= SEGMENT.stop) indexSet -= len; if (indexSet < customMappingSize) indexSet = customMappingTable[indexSet]; - busses.setPixelColor(indexSet, col, cct); + busses.setPixelColor(indexSet, col); } } } else { //live data, etc. @@ -618,7 +605,7 @@ void WS2812FX::resetSegments() { _segments[0].setOption(SEG_OPTION_SELECTED, 1); _segments[0].setOption(SEG_OPTION_ON, 1); _segments[0].opacity = 255; - _segments[0].cct = 128; + _segments[0].cct = -1; for (uint16_t i = 1; i < MAX_NUM_SEGMENTS; i++) { @@ -626,7 +613,7 @@ void WS2812FX::resetSegments() { _segments[i].grouping = 1; _segments[i].setOption(SEG_OPTION_ON, 1); _segments[i].opacity = 255; - _segments[i].cct = 128; + _segments[i].cct = -1; _segments[i].speed = DEFAULT_SPEED; _segments[i].intensity = DEFAULT_INTENSITY; _segment_runtimes[i].reset(); @@ -1162,4 +1149,5 @@ uint32_t WS2812FX::gamma32(uint32_t color) return RGBW32(r, g, b, w); } -WS2812FX* WS2812FX::instance = nullptr; \ No newline at end of file +WS2812FX* WS2812FX::instance = nullptr; +int16_t Bus::_cct = -1; \ No newline at end of file diff --git a/wled00/bus_manager.h b/wled00/bus_manager.h index cae4ac8622..af15d6953a 100644 --- a/wled00/bus_manager.h +++ b/wled00/bus_manager.h @@ -74,7 +74,7 @@ struct BusConfig { } }; -//parent class of BusDigital and BusPwm +//parent class of BusDigital, BusPwm, and BusNetwork class Bus { public: Bus(uint8_t type, uint16_t start, uint8_t aw) { @@ -88,7 +88,6 @@ class Bus { virtual void show() {} virtual bool canShow() { return true; } virtual void setPixelColor(uint16_t pix, uint32_t c) {}; - virtual void setPixelColor(uint16_t pix, uint32_t c, uint8_t cct) {}; virtual uint32_t getPixelColor(uint16_t pix) { return 0; }; virtual void setBrightness(uint8_t b) {}; virtual void cleanup() {}; @@ -111,6 +110,9 @@ class Bus { if (type > TYPE_ONOFF && type <= TYPE_ANALOG_5CH && type != TYPE_ANALOG_3CH) return true; return false; } + static void setCCT(uint16_t cct) { + _cct = cct; + } bool reversed = false; @@ -122,16 +124,18 @@ class Bus { bool _valid = false; bool _needsRefresh = false; uint8_t _autoWhiteMode = 0; + static int16_t _cct; uint32_t autoWhiteCalc(uint32_t c) { if (_autoWhiteMode == RGBW_MODE_MANUAL_ONLY) return c; + uint8_t w = W(c); + //ignore auto-white calculation if w>0 and mode DUAL (DUAL behaves as BRIGHTER if w==0) + if (w > 0 && _autoWhiteMode == RGBW_MODE_DUAL) return c; uint8_t r = R(c); uint8_t g = G(c); uint8_t b = B(c); - uint8_t w = W(c); - // ignore auto-white calculation if w>0 and mode DUAL (DUAL behaves as BRIGHTER if w==0) - if (!(w > 0 && _autoWhiteMode == RGBW_MODE_DUAL)) w = r < g ? (r < b ? r : b) : (g < b ? g : b); - if (_autoWhiteMode == RGBW_MODE_AUTO_ACCURATE) { r -= w; g -= w; b -= w; } // subtract w in ACCURATE mode + w = r < g ? (r < b ? r : b) : (g < b ? g : b); + if (_autoWhiteMode == RGBW_MODE_AUTO_ACCURATE) { r -= w; g -= w; b -= w; } //subtract w in ACCURATE mode return RGBW32(r, g, b, w); } }; @@ -181,17 +185,13 @@ class BusDigital : public Bus { } void setPixelColor(uint16_t pix, uint32_t c) { - if (getAutoWhiteMode() != RGBW_MODE_MANUAL_ONLY) c = autoWhiteCalc(c); + c = autoWhiteCalc(c); + if (_cct >= 1900) c = colorBalanceFromKelvin(_cct, c); //color correction from CCT if (reversed) pix = _len - pix -1; else pix += _skip; PolyBus::setPixelColor(_busPtr, _iType, pix, c, _colorOrder); } - void setPixelColor(uint16_t pix, uint32_t c, uint8_t cct) { - c = colorBalanceFromKelvin(2000+(cct<<5), c); // color correction from CCT - setPixelColor(pix, c); - } - uint32_t getPixelColor(uint16_t pix) { if (reversed) pix = _len - pix -1; else pix += _skip; @@ -282,22 +282,30 @@ class BusPwm : public Bus { _valid = true; }; - void setPixelColor(uint16_t pix, uint32_t c, uint8_t cct) { + void setPixelColor(uint16_t pix, uint32_t c) { if (pix != 0 || !_valid) return; //only react to first pixel - if (getAutoWhiteMode() != RGBW_MODE_MANUAL_ONLY) c = autoWhiteCalc(c); - c = colorBalanceFromKelvin(2000+(cct<<5), c); // color correction from CCT (w remains unchanged) + if (_type == TYPE_ANALOG_3CH && _cct >= 1900) { + c = colorBalanceFromKelvin(_cct, c); //color correction from CCT + } + c = autoWhiteCalc(c); uint8_t r = R(c); uint8_t g = G(c); uint8_t b = B(c); uint8_t w = W(c); + uint8_t cct = 0; //0 - full warm white, 255 - full cold white + if (_cct > -1) { + if (_cct >= 1900) cct = (_cct - 1900) >> 5; + else if (_cct < 256) cct = _cct; + } else { + cct = (approximateKelvinFromRGB(c) - 1900) >> 5; + } switch (_type) { case TYPE_ANALOG_1CH: //one channel (white), relies on auto white calculation - _data[0] = w; //max(r, max(g, max(b, w))); + _data[0] = w; break; case TYPE_ANALOG_2CH: //warm white + cold white // perhaps a non-linear adjustment would be in order. need to test - //w = max(r, max(g, max(b, w))); _data[1] = (w * cct) / 255; _data[0] = (w * (255-cct)) / 255; break; @@ -313,25 +321,6 @@ class BusPwm : public Bus { } } - void setPixelColor(uint16_t pix, uint32_t c) { - if (pix != 0 || !_valid) return; //only react to first pixel - if (getAutoWhiteMode() != RGBW_MODE_MANUAL_ONLY) c = autoWhiteCalc(c); - uint8_t r = R(c); - uint8_t g = G(c); - uint8_t b = B(c); - uint8_t w = W(c); - - switch (_type) { - case TYPE_ANALOG_1CH: //one channel (white), use highest RGBW value - _data[0] = max(r, max(g, max(b, w))); break; - case TYPE_ANALOG_2CH: //warm white + cold white - case TYPE_ANALOG_3CH: //standard dumb RGB - case TYPE_ANALOG_4CH: //standard dumb RGBW - case TYPE_ANALOG_5CH: //we'll want the white handling from 2CH here + RGB - _data[0] = r; _data[1] = g; _data[2] = b; _data[3] = w; _data[4] = w; break; - } - } - //does no index check uint32_t getPixelColor(uint16_t pix) { if (!_valid) return 0; @@ -432,7 +421,8 @@ class BusNetwork : public Bus { void setPixelColor(uint16_t pix, uint32_t c) { if (!_valid || pix >= _len) return; - if (getAutoWhiteMode() != RGBW_MODE_MANUAL_ONLY) c = autoWhiteCalc(c); + if (_cct >= 1900) c = colorBalanceFromKelvin(_cct, c); //color correction from CCT + c = autoWhiteCalc(c); uint16_t offset = pix * _UDPchannels; _data[offset] = R(c); _data[offset+1] = G(c); @@ -440,11 +430,6 @@ class BusNetwork : public Bus { if (_rgbw) _data[offset+3] = W(c); } - void setPixelColor(uint16_t pix, uint32_t c, uint8_t cct) { - c = colorBalanceFromKelvin(2000+(cct<<5), c); // color correction from CCT - setPixelColor(pix, c); - } - uint32_t getPixelColor(uint16_t pix) { if (!_valid || pix >= _len) return 0; uint16_t offset = pix * _UDPchannels; @@ -564,8 +549,7 @@ class BusManager { Bus* b = busses[i]; uint16_t bstart = b->getStart(); if (pix < bstart || pix >= bstart + b->getLength()) continue; - if (cct<0) busses[i]->setPixelColor(pix - bstart, c); // no white balance - else busses[i]->setPixelColor(pix - bstart, c, cct); // do white balance + busses[i]->setPixelColor(pix - bstart, c); } } @@ -575,6 +559,18 @@ class BusManager { } } + void setSegmentCCT(int16_t cct, bool allowWBCorrection = false) { + if (cct > 255) { //kelvin value, convert to 0-255 + if (cct < 1900) cct = 1900; + if (cct > 10091) cct = 10091; + if (!allowWBCorrection) cct = (cct - 1900) >> 5; + } else if (cct > -1) { + //if white balance correction allowed, save as kelvin value instead of 0-255 + if (allowWBCorrection) cct = 1900 + (cct << 5); + } else cct = -1; + Bus::setCCT(cct); + } + uint32_t getPixelColor(uint16_t pix) { for (uint8_t i = 0; i < numBusses; i++) { Bus* b = busses[i]; diff --git a/wled00/cfg.cpp b/wled00/cfg.cpp index a641d41793..8539a486fc 100644 --- a/wled00/cfg.cpp +++ b/wled00/cfg.cpp @@ -80,7 +80,7 @@ bool deserializeConfig(JsonObject doc, bool fromFS) { CJSON(strip.ablMilliampsMax, hw_led[F("maxpwr")]); CJSON(strip.milliampsPerLed, hw_led[F("ledma")]); uint8_t rgbwMode = hw_led[F("rgbwm")] | RGBW_MODE_DUAL; // use global setting (legacy) - CJSON(allowCCT, hw_led["cct"]); + CJSON(correctWB, hw_led["cct"]); JsonArray ins = hw_led["ins"]; @@ -520,7 +520,7 @@ void serializeConfig() { hw_led[F("total")] = strip.getLengthTotal(); //no longer read, but provided for compatibility on downgrade hw_led[F("maxpwr")] = strip.ablMilliampsMax; hw_led[F("ledma")] = strip.milliampsPerLed; - hw_led["cct"] = allowCCT; + hw_led["cct"] = correctWB; JsonArray hw_led_ins = hw_led.createNestedArray("ins"); diff --git a/wled00/colors.cpp b/wled00/colors.cpp index e7e2b7d19e..2677f61aa3 100644 --- a/wled00/colors.cpp +++ b/wled00/colors.cpp @@ -245,19 +245,10 @@ void colorRGBtoRGBW(byte* rgb) //rgb to rgbw (http://codewelt.com/rgbw). (RGBW_M } */ -// adjust RGB values based on color temperature in K (range [2800-10200]) (https://en.wikipedia.org/wiki/Color_balance) -void colorBalanceFromKelvin(uint16_t kelvin, byte *rgb) -{ - uint32_t col = RGBW32(rgb[0], rgb[1], rgb[2], 0); - col = colorBalanceFromKelvin(kelvin, col); - rgb[0] = R(col); - rgb[1] = G(col); - rgb[2] = B(col); -} - byte correctionRGB[4] = {0,0,0,0}; uint16_t lastKelvin = 0; +// adjust RGB values based on color temperature in K (range [2800-10200]) (https://en.wikipedia.org/wiki/Color_balance) uint32_t colorBalanceFromKelvin(uint16_t kelvin, uint32_t rgb) { //remember so that slow colorKtoRGB() doesn't have to run for every setPixelColor() @@ -267,6 +258,45 @@ uint32_t colorBalanceFromKelvin(uint16_t kelvin, uint32_t rgb) rgbw[0] = ((uint16_t) correctionRGB[0] * R(rgb)) /255; // correct R rgbw[1] = ((uint16_t) correctionRGB[1] * G(rgb)) /255; // correct G rgbw[2] = ((uint16_t) correctionRGB[2] * B(rgb)) /255; // correct B - rgbw[3] = W(rgb); + rgbw[3] = W(rgb); return colorFromRgbw(rgbw); +} + +//approximates a Kelvin color temperature from an RGB color. +//this does no check for the "whiteness" of the color, +//so should be used combined with a saturation check (as done by auto-white) +//values from http://www.vendian.org/mncharity/dir3/blackbody/UnstableURLs/bbr_color.html (10deg) +//equation spreadsheet at https://bit.ly/30RkHaN +//accuracy +-50K from 1900K up to 8000K +//minimum returned: 1900K, maximum returned: 10091K (range of 8192) +uint16_t approximateKelvinFromRGB(uint32_t rgb) { + //if not either red or blue is 255, color is dimmed. Scale up + uint8_t r = R(rgb), b = B(rgb); + if (r == b) return 6550; //red == blue at about 6600K (also can't go further if both R and B are 0) + + if (r > b) { + //scale blue up as if red was at 255 + uint16_t scale = 0xFFFF / r; //get scale factor (range 257-65535) + b = ((uint16_t)b * scale) >> 8; + //For all temps K<6600 R is bigger than B (for full bri colors R=255) + //-> Use 9 linear approximations for blackbody radiation blue values from 2000-6600K (blue is always 0 below 2000K) + if (b < 33) return 1900 + b *6; + if (b < 72) return 2100 + (b-33) *10; + if (b < 101) return 2492 + (b-72) *14; + if (b < 132) return 2900 + (b-101) *16; + if (b < 159) return 3398 + (b-132) *19; + if (b < 186) return 3906 + (b-159) *22; + if (b < 210) return 4500 + (b-186) *25; + if (b < 230) return 5100 + (b-210) *30; + return 5700 + (b-230) *34; + } else { + //scale red up as if blue was at 255 + uint16_t scale = 0xFFFF / b; //get scale factor (range 257-65535) + r = ((uint16_t)r * scale) >> 8; + //For all temps K>6600 B is bigger than R (for full bri colors B=255) + //-> Use 2 linear approximations for blackbody radiation red values from 6600-10091K (blue is always 0 below 2000K) + if (r > 225) return 6600 + (254-r) *50; + uint16_t k = 8080 + (225-r) *86; + return (k > 10091) ? 10091 : k; + } } \ No newline at end of file diff --git a/wled00/data/settings_leds.htm b/wled00/data/settings_leds.htm index 9e6d9277b5..b141b4d0fd 100644 --- a/wled00/data/settings_leds.htm +++ b/wled00/data/settings_leds.htm @@ -510,7 +510,9 @@

Hardware setup


Make a segment for each output:
Custom bus start indices:
- Allow WB correction:
+ White Balance correction:
+ Calculate CCT from RGB: TODO
+ CCT blending mode: TODO

Touch threshold:
diff --git a/wled00/fcn_declare.h b/wled00/fcn_declare.h index eda6bf34ae..1ba20b331f 100644 --- a/wled00/fcn_declare.h +++ b/wled00/fcn_declare.h @@ -68,8 +68,8 @@ void colorRGBtoXY(byte* rgb, float* xy); // only defined if huesync disabled TOD void colorFromDecOrHexString(byte* rgb, char* in); bool colorFromHexString(byte* rgb, const char* in); -void colorBalanceFromKelvin(uint16_t kelvin, byte *rgb); uint32_t colorBalanceFromKelvin(uint16_t kelvin, uint32_t rgb); +uint16_t approximateKelvinFromRGB(uint32_t rgb); //dmx.cpp void initDMX(); diff --git a/wled00/html_settings.h b/wled00/html_settings.h index 83bc10f7a4..9cd514e8a9 100644 --- a/wled00/html_settings.h +++ b/wled00/html_settings.h @@ -77,7 +77,7 @@ onclick="B()">Back // Autogenerated from wled00/data/settings_leds.htm, do not edit!! const char PAGE_settings_leds[] PROGMEM = R"=====(LED Settings - +
diff --git a/wled00/data/settings_leds.htm b/wled00/data/settings_leds.htm index 6060f7db81..34ccf90e1b 100644 --- a/wled00/data/settings_leds.htm +++ b/wled00/data/settings_leds.htm @@ -170,7 +170,6 @@ 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) ? "inline":"none"; // auto calculate white 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 } @@ -306,10 +305,11 @@ - + - + + @@ -334,8 +334,7 @@

Reversed:

Skip 1st LED:
-

Off Refresh:  
-

Auto-calculate white channel from RGB:
 
+

Off Refresh:
`; f.insertAdjacentHTML("beforeend", cn); } @@ -402,9 +401,9 @@ } if (!o.files) { - alert("This browser doesn't seem to support the `files` property of file inputs."); + alert("This browser doesn't support the `files` property of file inputs."); } else if (!o.files[0]) { - alert("Please select a JSON file before clicking 'Apply'"); + alert("Please select a JSON file first!"); } else { f = o.files[0]; fr = new FileReader(); @@ -510,9 +509,6 @@

Hardware setup


Make a segment for each output:
Custom bus start indices:
- White Balance correction:
- Calculate CCT from RGB:
- CCT additive blending: %%

Touch threshold:
@@ -554,6 +550,19 @@

Timed light

+

White management

+ White Balance correction:
+ + Auto-calculate white channel from RGB:
+ +
+ Calculate CCT from RGB:
+ CCT additive blending: %%

Advanced

Palette blending:

Reversed:

Skip 1st LED:
-

Off Refresh:  
+

Off Refresh:
`; f.insertAdjacentHTML("beforeend", cn); } @@ -408,10 +409,70 @@ d.Sf.data.value = ''; return false; } - function GetV() - { + // https://stackoverflow.com/questions/7346563/loading-local-json-file + function loadCfg(o) { + var f, fr; + + if (typeof window.FileReader !== 'function') { + alert("The file API isn't supported on this browser yet."); + return; + } + + if (!o.files) { + alert("This browser doesn't support the `files` property of file inputs."); + } else if (!o.files[0]) { + alert("Please select a JSON file first!"); + } else { + f = o.files[0]; + fr = new FileReader(); + fr.onload = receivedText; + fr.readAsText(f); + } + o.value = ''; + + function receivedText(e) { + let lines = e.target.result; + var 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; + l.ins.forEach((v,i,a)=>{ + addLEDs(1); + for (var j=0; j{ + 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; + } + if (c.hw.relay) { + d.getElementsByName("RL")[0].value = c.hw.relay.pin; + d.getElementsByName("RM")[0].checked = c.hw.relay.inv; + } + UI(); + } + } + } + function GetV() + { //values injected by server while sending HTML - //d.um_p=[6,7,8,9,10,11,1];bLimits(3,4096,4000,1664);d.Sf.MS.checked=1;addLEDs(1);d.Sf.L00.value=2;d.Sf.LC0.value=30;d.Sf.LT0.value=22;d.Sf.CO0.value=0;d.Sf.LS0.value=15;d.Sf.CV0.checked=1;d.Sf.SL0.checked=0;addLEDs(1);d.Sf.L01.value=10;d.Sf.L11.value=10;d.Sf.L21.value=1;d.Sf.L31.value=10;d.Sf.LC1.value=60;d.Sf.LT1.value=80;d.Sf.CO1.value=1;d.Sf.LS1.value=0;d.Sf.CV1.checked=0;d.Sf.SL1.checked=0;d.Sf.MA.value=850;d.Sf.LA.value=0;d.Sf.CA.value=56;d.Sf.AW.value=3;d.Sf.BO.checked=1;d.Sf.BP.value=80;d.Sf.GB.checked=0;d.Sf.GC.checked=1;d.Sf.TF.checked=1;d.Sf.TD.value=700;d.Sf.PF.checked=0;d.Sf.BF.value=100;d.Sf.TB.value=0;d.Sf.TL.value=60;d.Sf.TW.value=0;d.Sf.PB.selectedIndex=0;d.Sf.RL.value=12;d.Sf.RM.checked=1;addBtn(0,0,0);addBtn(1,-1,0);d.Sf.TT.value=32;d.Sf.IR.value=-1;d.Sf.IT.value=0; + //d.um_p=[6,7,8,9,10,11,14,15,13,1,21,19,22,25,26,27,5,23,18,17];bLimits(10,2048,64000,8192);d.Sf.MS.checked=1;d.Sf.CCT.checked=0;addLEDs(1);d.Sf.L00.value=192;d.Sf.L10.value=168;d.Sf.L20.value=0;d.Sf.L30.value=61;d.Sf.LC0.value=421;d.Sf.LT0.value=80;d.Sf.CO0.value=1;d.Sf.LS0.value=0;d.Sf.CV0.checked=0;d.Sf.SL0.checked=0;d.Sf.RF0.checked=0;d.Sf.MA.value=850;d.Sf.LA.value=0;d.Sf.CA.value=127;d.Sf.AW.value=3;d.Sf.BO.checked=0;d.Sf.BP.value=0;d.Sf.GB.checked=0;d.Sf.GC.checked=1;d.Sf.TF.checked=1;d.Sf.TD.value=700;d.Sf.PF.checked=1;d.Sf.BF.value=100;d.Sf.TB.value=0;d.Sf.TL.value=60;d.Sf.TW.value=1;d.Sf.PB.selectedIndex=0;d.Sf.RL.value=-1;d.Sf.RM.checked=1;addBtn(0,-1,0);addBtn(1,-1,0);addBtn(2,-1,0);addBtn(3,-1,0);d.Sf.TT.value=32;d.Sf.IR.value=-1;d.Sf.IT.value=8; }

WLED Software Update

-Installed version: 0.13.0-b4
Download the latest binary: Download the latest binary:

body{font-family:Verdana,sans-serif;text-align:center;background:#222;color:#fff;line-height:200%%;margin:0}hr{border-color:#666}.btn,button{background:#333;color:#fff;font-family:Verdana,sans-serif;border:.3ch solid #333;display:inline-block;font-size:20px;margin:12px 8px 8px;padding:1px 6px;cursor:pointer;text-decoration:none}.lnk{border:0}button.disabled,button[disabled]{color:#aaa}.helpB{text-align:left;position:absolute;width:60px}input{background:#333;color:#fff;font-family:Verdana,sans-serif;border:.5ch solid #333}input:disabled{color:#888}input[type=number]{width:4em;margin:2px}input[type=number].xxl{width:100px}input[type=number].xl{width:85px}input[type=number].l{width:63px}input[type=number].m{width:56px}input[type=number].s{width:49px}input[type=number].xs{width:42px}input[type=checkbox]{transform:scale(1.5);margin-right:10px}select{background:#333;color:#fff;font-family:Verdana,sans-serif;border:.5ch solid #333}td{padding:2px}.d5{width:4.5em!important}#toast{opacity:0;background-color:#444;border-radius:5px;bottom:64px;color:#fff;font-size:17px;padding:16px;pointer-events:none;position:fixed;text-align:center;z-index:5;transform:translateX(-50%%);max-width:90%%;left:50%%}#toast.show{opacity:1;background-color:#264;animation:fadein .5s,fadein .5s 2.5s reverse}#toast.error{opacity:1;background-color:#b21;animation:fadein .5s})====="; +const char PAGE_settingsCss[] PROGMEM = R"=====()====="; // Autogenerated from wled00/data/settings.htm, do not edit!! const char PAGE_settings[] PROGMEM = R"=====(WLED Settings
%DMXMENU%
-
-)====="; +body{text-align:center;background:#222;height:100px;margin:0}html{--h:8.9vh}button{background:#333;color:#fff;font-family:Verdana,Helvetica,sans-serif;border:1px solid #333;border-radius:.5em;font-size:6.5vmin;height:var(--h);width:calc(100%% - 40px);margin-top:2vh} +
+
+
%DMXMENU%
+
+
+
)====="; // Autogenerated from wled00/data/settings_wifi.htm, do not edit!! @@ -78,7 +75,7 @@ onclick="B()">Back // Autogenerated from wled00/data/settings_leds.htm, do not edit!! const char PAGE_settings_leds[] PROGMEM = R"=====(LED Settings