Dual mode 2D + 1D with auto segment creation. (#3060)

* Dual mode 2D + 1D with auto segment creation.

* Bugfix.
- stop when seglen
This commit is contained in:
Blaž Kristan 2023-02-01 19:30:56 +01:00 committed by GitHub
parent 6be9317fd7
commit dca8a47da8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 2030 additions and 1980 deletions

View File

@ -393,8 +393,8 @@ void Segment::set(uint16_t i1, uint16_t i2, uint8_t grp, uint8_t spc, uint16_t o
markForReset(); markForReset();
return; return;
} }
if (i1 < Segment::maxWidth) start = i1; // Segment::maxWidth equals strip.getLengthTotal() for 1D if (i1 < Segment::maxWidth || (i1 >= Segment::maxWidth*Segment::maxHeight && i1 < strip.getLengthTotal())) start = i1; // Segment::maxWidth equals strip.getLengthTotal() for 1D
stop = i2 > Segment::maxWidth ? Segment::maxWidth : MAX(1,i2); stop = i2 > Segment::maxWidth*Segment::maxHeight ? MIN(i2,strip.getLengthTotal()) : (i2 > Segment::maxWidth ? Segment::maxWidth : MAX(1,i2));
startY = 0; startY = 0;
stopY = 1; stopY = 1;
#ifndef WLED_DISABLE_2D #ifndef WLED_DISABLE_2D
@ -600,12 +600,14 @@ void IRAM_ATTR Segment::setPixelColor(int i, uint32_t col)
} }
return; return;
} else if (Segment::maxHeight!=1 && (width()==1 || height()==1)) { } else if (Segment::maxHeight!=1 && (width()==1 || height()==1)) {
// we have a vertical or horizontal 1D segment (WARNING: virtual...() may be transposed) if (start < Segment::maxWidth*Segment::maxHeight) {
int x = 0, y = 0; // we have a vertical or horizontal 1D segment (WARNING: virtual...() may be transposed)
if (virtualHeight()>1) y = i; int x = 0, y = 0;
if (virtualWidth() >1) x = i; if (virtualHeight()>1) y = i;
setPixelColorXY(x, y, col); if (virtualWidth() >1) x = i;
return; setPixelColorXY(x, y, col);
return;
}
} }
#endif #endif
@ -1412,6 +1414,20 @@ void WS2812FX::makeAutoSegments(bool forceReset) {
_segments[i].spacing = 0; _segments[i].spacing = 0;
_mainSegment = i; _mainSegment = i;
} }
// do we have LEDs after the matrix? (ignore buses)
if (autoSegments && _length > Segment::maxWidth*Segment::maxHeight /*&& getActiveSegmentsNum() == 2*/) {
if (_segments.size() == getLastActiveSegmentId()+1)
_segments.push_back(Segment(Segment::maxWidth*Segment::maxHeight, _length));
else {
size_t i = getLastActiveSegmentId() + 1;
_segments[i].start = Segment::maxWidth*Segment::maxHeight;
_segments[i].stop = _length;
_segments[i].startY = 0;
_segments[i].stopY = 1;
_segments[i].grouping = 1;
_segments[i].spacing = 0;
}
}
#endif #endif
} else if (autoSegments) { //make one segment per bus } else if (autoSegments) { //make one segment per bus
uint16_t segStarts[MAX_NUM_SEGMENTS] = {0}; uint16_t segStarts[MAX_NUM_SEGMENTS] = {0};
@ -1435,6 +1451,7 @@ void WS2812FX::makeAutoSegments(bool forceReset) {
s++; s++;
} }
_segments.clear(); _segments.clear();
_segments.reserve(s); // prevent reallocations
for (size_t i = 0; i < s; i++) { for (size_t i = 0; i < s; i++) {
Segment seg = Segment(segStarts[i], segStops[i]); Segment seg = Segment(segStarts[i], segStops[i]);
seg.selected = true; seg.selected = true;
@ -1482,8 +1499,7 @@ bool WS2812FX::checkSegmentAlignment() {
//After this function is called, setPixelColor() will use that segment (offsets, grouping, ... will apply) //After this function is called, setPixelColor() will use that segment (offsets, grouping, ... will apply)
//Note: If called in an interrupt (e.g. JSON API), original segment must be restored, //Note: If called in an interrupt (e.g. JSON API), original segment must be restored,
//otherwise it can lead to a crash on ESP32 because _segment_index is modified while in use by the main thread //otherwise it can lead to a crash on ESP32 because _segment_index is modified while in use by the main thread
uint8_t WS2812FX::setPixelSegment(uint8_t n) uint8_t WS2812FX::setPixelSegment(uint8_t n) {
{
uint8_t prevSegId = _segment_index; uint8_t prevSegId = _segment_index;
if (n < _segments.size()) { if (n < _segments.size()) {
_segment_index = n; _segment_index = n;
@ -1492,8 +1508,7 @@ uint8_t WS2812FX::setPixelSegment(uint8_t n)
return prevSegId; return prevSegId;
} }
void WS2812FX::setRange(uint16_t i, uint16_t i2, uint32_t col) void WS2812FX::setRange(uint16_t i, uint16_t i2, uint32_t col) {
{
if (i2 >= i) if (i2 >= i)
{ {
for (uint16_t x = i; x <= i2; x++) setPixelColor(x, col); for (uint16_t x = i; x <= i2; x++) setPixelColor(x, col);
@ -1503,14 +1518,12 @@ void WS2812FX::setRange(uint16_t i, uint16_t i2, uint32_t col)
} }
} }
void WS2812FX::setTransitionMode(bool t) void WS2812FX::setTransitionMode(bool t) {
{
for (segment &seg : _segments) if (!seg.transitional) seg.startTransition(t ? _transitionDur : 0); for (segment &seg : _segments) if (!seg.transitional) seg.startTransition(t ? _transitionDur : 0);
} }
#ifdef WLED_DEBUG #ifdef WLED_DEBUG
void WS2812FX::printSize() void WS2812FX::printSize() {
{
size_t size = 0; size_t size = 0;
for (const Segment &seg : _segments) size += seg.getSize(); for (const Segment &seg : _segments) size += seg.getSize();
DEBUG_PRINTF("Segments: %d -> %uB\n", _segments.size(), size); DEBUG_PRINTF("Segments: %d -> %uB\n", _segments.size(), size);
@ -1521,8 +1534,7 @@ void WS2812FX::printSize()
} }
#endif #endif
void WS2812FX::loadCustomPalettes() void WS2812FX::loadCustomPalettes() {
{
byte tcp[72]; //support gradient palettes with up to 18 entries byte tcp[72]; //support gradient palettes with up to 18 entries
CRGBPalette16 targetPalette; CRGBPalette16 targetPalette;
customPalettes.clear(); // start fresh customPalettes.clear(); // start fresh

View File

@ -713,7 +713,7 @@ function populateSegments(s)
let rvXck = `<label class="check revchkl">Reverse ${isM?'':'direction'}<input type="checkbox" id="seg${i}rev" onchange="setRev(${i})" ${inst.rev?"checked":""}><span class="checkmark"></span></label>`; let rvXck = `<label class="check revchkl">Reverse ${isM?'':'direction'}<input type="checkbox" id="seg${i}rev" onchange="setRev(${i})" ${inst.rev?"checked":""}><span class="checkmark"></span></label>`;
let miXck = `<label class="check revchkl">Mirror<input type="checkbox" id="seg${i}mi" onchange="setMi(${i})" ${inst.mi?"checked":""}><span class="checkmark"></span></label>`; let miXck = `<label class="check revchkl">Mirror<input type="checkbox" id="seg${i}mi" onchange="setMi(${i})" ${inst.mi?"checked":""}><span class="checkmark"></span></label>`;
let rvYck = "", miYck =""; let rvYck = "", miYck ="";
if (isM) { if (isM && staX<mw*mh) {
rvYck = `<label class="check revchkl">Reverse<input type="checkbox" id="seg${i}rY" onchange="setRevY(${i})" ${inst.rY?"checked":""}><span class="checkmark"></span></label>`; rvYck = `<label class="check revchkl">Reverse<input type="checkbox" id="seg${i}rY" onchange="setRevY(${i})" ${inst.rY?"checked":""}><span class="checkmark"></span></label>`;
miYck = `<label class="check revchkl">Mirror<input type="checkbox" id="seg${i}mY" onchange="setMiY(${i})" ${inst.mY?"checked":""}><span class="checkmark"></span></label>`; miYck = `<label class="check revchkl">Mirror<input type="checkbox" id="seg${i}mY" onchange="setMiY(${i})" ${inst.mY?"checked":""}><span class="checkmark"></span></label>`;
} }
@ -749,19 +749,19 @@ function populateSegments(s)
<input type="text" class="ptxt noslide" id="seg${i}t" autocomplete="off" maxlength=32 value="${inst.n?inst.n:""}" placeholder="Enter name..."/> <input type="text" class="ptxt noslide" id="seg${i}t" autocomplete="off" maxlength=32 value="${inst.n?inst.n:""}" placeholder="Enter name..."/>
<table class="infot segt"> <table class="infot segt">
<tr> <tr>
<td>${isM?'Start X':'Start LED'}</td> <td>${isM&&staX<mw*mh?'Start X':'Start LED'}</td>
<td>${isM?(cfg.comp.seglen?"Width":"Stop X"):(cfg.comp.seglen?"LED count":"Stop LED")}</td> <td>${isM&&staX<mw*mh?(cfg.comp.seglen?"Width":"Stop X"):(cfg.comp.seglen?"LED count":"Stop LED")}</td>
<td>${isM?'':'Offset'}</td> <td>${isM&&staX<mw*mh?'':'Offset'}</td>
</tr> </tr>
<tr> <tr>
<td><input class="noslide segn" id="seg${i}s" type="number" min="0" max="${(isM?mw:ledCount)-1}" value="${staX}" oninput="updateLen(${i})" onkeydown="segEnter(${i})"></td> <td><input class="noslide segn" id="seg${i}s" type="number" min="0" max="${(isM&&staX<mw*mh?mw:ledCount)-1}" value="${staX}" oninput="updateLen(${i})" onkeydown="segEnter(${i})"></td>
<td><input class="noslide segn" id="seg${i}e" type="number" min="0" max="${(isM?mw:ledCount)-(cfg.comp.seglen?staX:0)}" value="${stoX-(cfg.comp.seglen?staX:0)}" oninput="updateLen(${i})" onkeydown="segEnter(${i})"></td> <td><input class="noslide segn" id="seg${i}e" type="number" min="0" max="${(isM&&staX<mw*mh?mw:ledCount)}" value="${stoX-(cfg.comp.seglen?staX:0)}" oninput="updateLen(${i})" onkeydown="segEnter(${i})"></td>
<td style="text-align:revert;">${isM?miXck+'<br>'+rvXck:''}<input class="noslide segn ${isM?'hide':''}" id="seg${i}of" type="number" value="${inst.of}" oninput="updateLen(${i})"></td> <td style="text-align:revert;">${isM&&staX<mw*mh?miXck+'<br>'+rvXck:''}<input class="noslide segn ${isM&&staX<mw*mh?'hide':''}" id="seg${i}of" type="number" value="${inst.of}" oninput="updateLen(${i})"></td>
</tr> </tr>
${isM ? '<tr><td>Start Y</td><td>'+(cfg.comp.seglen?'Height':'Stop Y')+'</td><td></td></tr>'+ ${isM&&staX<mw*mh ? '<tr><td>Start Y</td><td>'+(cfg.comp.seglen?'Height':'Stop Y')+'</td><td></td></tr>'+
'<tr>'+ '<tr>'+
'<td><input class="noslide segn" id="seg'+i+'sY" type="number" min="0" max="'+(mh-1)+'" value="'+staY+'" oninput="updateLen('+i+')" onkeydown="segEnter('+i+')"></td>'+ '<td><input class="noslide segn" id="seg'+i+'sY" type="number" min="0" max="'+(mh-1)+'" value="'+staY+'" oninput="updateLen('+i+')" onkeydown="segEnter('+i+')"></td>'+
'<td><input class="noslide segn" id="seg'+i+'eY" type="number" min="0" max="'+(mh-(cfg.comp.seglen?staY:0))+'" value="'+(stoY-(cfg.comp.seglen?staY:0))+'" oninput="updateLen('+i+')" onkeydown="segEnter('+i+')"></td>'+ '<td><input class="noslide segn" id="seg'+i+'eY" type="number" min="0" max="'+mh+'" value="'+(stoY-(cfg.comp.seglen?staY:0))+'" oninput="updateLen('+i+')" onkeydown="segEnter('+i+')"></td>'+
'<td style="text-align:revert;">'+miYck+'<br>'+rvYck+'</td>'+ '<td style="text-align:revert;">'+miYck+'<br>'+rvYck+'</td>'+
'</tr>':''} '</tr>':''}
<tr> <tr>
@ -772,16 +772,17 @@ function populateSegments(s)
<tr> <tr>
<td><input class="noslide segn" id="seg${i}grp" type="number" min="1" max="255" value="${inst.grp}" oninput="updateLen(${i})" onkeydown="segEnter(${i})"></td> <td><input class="noslide segn" id="seg${i}grp" type="number" min="1" max="255" value="${inst.grp}" oninput="updateLen(${i})" onkeydown="segEnter(${i})"></td>
<td><input class="noslide segn" id="seg${i}spc" type="number" min="0" max="255" value="${inst.spc}" oninput="updateLen(${i})" onkeydown="segEnter(${i})"></td> <td><input class="noslide segn" id="seg${i}spc" type="number" min="0" max="255" value="${inst.spc}" oninput="updateLen(${i})" onkeydown="segEnter(${i})"></td>
<td style="text-align:left;"><button class="btn btn-xs" onclick="setSeg(${i})"><i class="icons btn-icon" id="segc${i}">&#xe390;</i></button></td> <td style="text-align:revert;"><button class="btn btn-xs" onclick="setSeg(${i})"><i class="icons btn-icon" id="segc${i}">&#xe390;</i></button></td>
</tr> </tr>
</table> </table>
<div class="h bp" id="seg${i}len"></div> <div class="h bp" id="seg${i}len"></div>
${!isM?rvXck:''} ${!(isM&&staX<mw*mh)?rvXck:''}
${isM&&stoY-staY>1&&stoX-staX>1?map2D:''} ${isM&&staX<mw*mh&&stoY-staY>1&&stoX-staX>1?map2D:''}
${s.AudioReactive && s.AudioReactive.on ? "" : sndSim} ${s.AudioReactive && s.AudioReactive.on ? "" : sndSim}
<label class="check revchkl" id="seg${i}lbtm"> <label class="check revchkl" id="seg${i}lbtm">
${isM?'Transpose':'Mirror effect'} ${isM&&staX<mw*mh?'Transpose':'Mirror effect'}${isM&&staX<mw*mh?
<input type="checkbox" id="seg${i}${isM?'tp':'mi'}" onchange="${(isM?'setTp(':'setMi(')+i})" ${isM?(inst.tp?"checked":""):(inst.mi?"checked":"")}> '<input type="checkbox" id="seg'+i+'tp" onchange="setTp('+i+')" '+(inst.tp?"checked":"")+'>':
'<input type="checkbox" id="seg'+i+'mi" onchange="setMi('+i+')" '+(inst.mi?"checked":"")+'>'}
<span class="checkmark"></span> <span class="checkmark"></span>
</label> </label>
<div class="del"> <div class="del">
@ -1049,29 +1050,50 @@ function updateLen(s)
var start = parseInt(gId(`seg${s}s`).value); var start = parseInt(gId(`seg${s}s`).value);
var stop = parseInt(gId(`seg${s}e`).value) + (cfg.comp.seglen?start:0); var stop = parseInt(gId(`seg${s}e`).value) + (cfg.comp.seglen?start:0);
var len = stop - start; var len = stop - start;
let sY = gId(`seg${s}sY`);
let eY = gId(`seg${s}eY`);
let sX = gId(`seg${s}s`);
let eX = gId(`seg${s}e`);
let of = gId(`seg${s}of`);
let mySH = gId("mkSYH");
let mySD = gId("mkSYD");
if (isM) { if (isM) {
// matrix setup // do we have 1D segment *after* the matrix?
let startY = parseInt(gId(`seg${s}sY`).value); if (start >= mw*mh) {
let stopY = parseInt(gId(`seg${s}eY`).value) + (cfg.comp.seglen?startY:0); if (sY) { sY.value = 0; sY.max = 0; sY.min = 0; }
len *= (stopY-startY); if (eY) { eY.value = 1; eY.max = 1; eY.min = 0; }
let tPL = gId(`seg${s}lbtm`); sX.min = mw*mh; sX.max = ledCount-1;
if (stop-start>1 && stopY-startY>1) { eX.min = mw*mh+1; eX.max = ledCount;
// 2D segment if (mySH) mySH.classList.add("hide");
if (tPL) tPL.classList.remove('hide'); // unhide transpose checkbox if (mySD) mySD.classList.add("hide");
let sE = gId('fxlist').querySelector(`.lstI[data-id="${selectedFx}"]`); if (of) of.classList.remove("hide");
if (sE) {
let sN = sE.querySelector(".lstIname").innerText;
let seg = gId(`seg${s}map2D`);
if (seg) {
if(sN.indexOf("\u25A6")<0) seg.classList.remove('hide'); // unhide mapping for 1D effects (| in name)
else seg.classList.add('hide'); // hide mapping otherwise
}
}
} else { } else {
// 1D segment in 2D set-up // matrix setup
if (tPL) { if (mySH) mySH.classList.remove("hide");
tPL.classList.add('hide'); // hide transpose checkbox if (mySD) mySD.classList.remove("hide");
gId(`seg${s}tp`).checked = false; // and uncheck it if (of) of.classList.add("hide");
let startY = parseInt(sY.value);
let stopY = parseInt(eY.value) + (cfg.comp.seglen?startY:0);
len *= (stopY-startY);
let tPL = gId(`seg${s}lbtm`);
if (stop-start>1 && stopY-startY>1) {
// 2D segment
if (tPL) tPL.classList.remove('hide'); // unhide transpose checkbox
let sE = gId('fxlist').querySelector(`.lstI[data-id="${selectedFx}"]`);
if (sE) {
let sN = sE.querySelector(".lstIname").innerText;
let seg = gId(`seg${s}map2D`);
if (seg) {
if(sN.indexOf("\u25A6")<0) seg.classList.remove('hide'); // unhide mapping for 1D effects (| in name)
else seg.classList.add('hide'); // hide mapping otherwise
}
}
} else {
// 1D segment in 2D set-up
if (tPL) {
tPL.classList.add('hide'); // hide transpose checkbox
gId(`seg${s}tp`).checked = false; // and uncheck it
}
} }
} }
} }
@ -1090,6 +1112,7 @@ function updateLen(s)
var virt = Math.ceil(len/(grp + spc)); var virt = Math.ceil(len/(grp + spc));
if (!isNaN(virt) && (grp > 1 || spc > 0)) out += ` (${virt} virtual)`; if (!isNaN(virt) && (grp > 1 || spc > 0)) out += ` (${virt} virtual)`;
} }
if (isM && start >= mw*mh) out += " [strip]";
gId(`seg${s}len`).innerHTML = out; gId(`seg${s}len`).innerHTML = out;
} }
@ -1692,11 +1715,11 @@ function makeSeg()
<td><input class="noslide segn" id="seg${lu}e" type="number" min="0" max="${ct}" value="${ct}" oninput="updateLen(${lu})" onkeydown="segEnter(${lu})"></td> <td><input class="noslide segn" id="seg${lu}e" type="number" min="0" max="${ct}" value="${ct}" oninput="updateLen(${lu})" onkeydown="segEnter(${lu})"></td>
<td><button class="btn btn-xs" onclick="setSeg(${lu});"><i class="icons bth-icon" id="segc${lu}">&#xe390;</i></button></td> <td><button class="btn btn-xs" onclick="setSeg(${lu});"><i class="icons bth-icon" id="segc${lu}">&#xe390;</i></button></td>
</tr> </tr>
${isM ? '<tr><td>Start Y</td><td>'+(cfg.comp.seglen?'Height':'Stop Y')+'</td></tr>'+ <tr id="mkSYH" class="${isM?"":"hide"}"><td>Start Y</td><td>${cfg.comp.seglen?'Height':'Stop Y'}</td></tr>
'<tr>'+ <tr id="mkSYD" class="${isM?"":"hide"}">
'<td><input class="noslide segn" id="seg'+lu+'sY" type="number" min="0" max="'+(mh-1)+'" value="'+0+'" oninput="updateLen('+lu+')" onkeydown="segEnter('+lu+')"></td>'+ <td><input class="noslide segn" id="seg${lu}sY" type="number" min="0" max="${mh-1}" value="0" oninput="updateLen(${lu})" onkeydown="segEnter(${lu})"></td>
'<td><input class="noslide segn" id="seg'+lu+'eY" type="number" min="0" max="'+mh+'" value="'+mh+'" oninput="updateLen('+lu+')" onkeydown="segEnter('+lu+')"></td>'+ <td><input class="noslide segn" id="seg${lu}eY" type="number" min="0" max="${mh}" value="${isM?mh:1}" oninput="updateLen(${lu})" onkeydown="segEnter(${lu})"></td>
'</tr>':''} </tr>
</table> </table>
<div class="h" id="seg${lu}len">${ledCount - ns} LEDs</div> <div class="h" id="seg${lu}len">${ledCount - ns} LEDs</div>
<div class="c"><button class="btn btn-p" onclick="resetUtil()">Cancel</button></div> <div class="c"><button class="btn btn-p" onclick="resetUtil()">Cancel</button></div>
@ -2026,25 +2049,26 @@ function setSeg(s)
let sX = gId(`seg${s}s`); let sX = gId(`seg${s}s`);
let eX = gId(`seg${s}e`); let eX = gId(`seg${s}e`);
var start = parseInt(sX.value); var start = parseInt(sX.value);
var stop = parseInt(eX.value); var stop = parseInt(eX.value) + (cfg.comp.seglen?start:0);
if (start<sX.min || start>sX.max) {sX.value=sX.min; return;} // prevent out of bounds if (start<sX.min || start>sX.max) {sX.value=sX.min; return;} // prevent out of bounds
if (stop<eX.min || stop>eX.max) {eX.value=eX.max; return;} // prevent out of bounds if (stop<eX.min || stop-(cfg.comp.seglen?start:0)>eX.max) {eX.value=eX.max; return;} // prevent out of bounds
if ((cfg.comp.seglen && stop == 0) || (!cfg.comp.seglen && stop <= start)) {delSeg(s); return;} if ((cfg.comp.seglen && stop == 0) || (!cfg.comp.seglen && stop <= start)) {delSeg(s); return;}
var obj = {"seg": {"id": s, "n": name, "start": start, "stop": (cfg.comp.seglen?start:0)+stop}}; var obj = {"seg": {"id": s, "n": name, "start": start, "stop": stop}};
if (isM) { if (isM && start<mw*mh) {
let sY = gId(`seg${s}sY`); let sY = gId(`seg${s}sY`);
let eY = gId(`seg${s}eY`); let eY = gId(`seg${s}eY`);
var startY = parseInt(sY.value); var startY = parseInt(sY.value);
var stopY = parseInt(eY.value); var stopY = parseInt(eY.value) + (cfg.comp.seglen?startY:0);
if (startY<sY.min || startY>sY.max) {sY.value=sY.min; return;} // prevent out of bounds if (startY<sY.min || startY>sY.max) {sY.value=sY.min; return;} // prevent out of bounds
if (stopY<eY.min || stopY>eY.max) {eY.value=eY.max; return;} // prevent out of bounds if (stopY<eY.min || stopY>eY.max) {eY.value=eY.max; return;} // prevent out of bounds
obj.seg.startY = startY; obj.seg.startY = startY;
obj.seg.stopY = (cfg.comp.seglen?startY:0)+stopY; obj.seg.stopY = stopY;
} }
if (gId(`seg${s}grp`)) { // advanced options, not present in new segment dialog (makeSeg()) let g = gId(`seg${s}grp`);
var grp = parseInt(gId(`seg${s}grp`).value); if (g) { // advanced options, not present in new segment dialog (makeSeg())
var spc = parseInt(gId(`seg${s}spc`).value); let grp = parseInt(g.value);
var ofs = parseInt(gId(`seg${s}of` ).value); let spc = parseInt(gId(`seg${s}spc`).value);
let ofs = parseInt(gId(`seg${s}of` ).value);
obj.seg.grp = grp; obj.seg.grp = grp;
obj.seg.spc = spc; obj.seg.spc = spc;
obj.seg.of = ofs; obj.seg.of = ofs;

File diff suppressed because it is too large Load Diff

View File

@ -8,7 +8,7 @@
*/ */
// version code in format yymmddb (b = daily build) // version code in format yymmddb (b = daily build)
#define VERSION 2301240 #define VERSION 2301290
//uncomment this if you have a "my_config.h" file you'd like to use //uncomment this if you have a "my_config.h" file you'd like to use
//#define WLED_USE_MY_CONFIG //#define WLED_USE_MY_CONFIG