Merge branch '0_15' into random-bg-img

This commit is contained in:
Blaz Kristan 2023-10-31 09:24:08 +01:00
commit bfd58b3cdf
13 changed files with 2269 additions and 2168 deletions

View File

@ -664,7 +664,7 @@ class AudioReactive : public Usermod {
void removeAudioPalettes(void); void removeAudioPalettes(void);
void createAudioPalettes(void); void createAudioPalettes(void);
CRGB getCRGBForBand(int x, int pal); CRGB getCRGBForBand(int x, int pal);
void fillAudioPalette(int pal); void fillAudioPalettes(void);
//////////////////// ////////////////////
// Debug support // // Debug support //
@ -1371,7 +1371,7 @@ class AudioReactive : public Usermod {
lastTime = millis(); lastTime = millis();
} }
for (int i=0; i<MAX_PALETTES; i++) fillAudioPalette(i); fillAudioPalettes();
} }
@ -1859,7 +1859,7 @@ class AudioReactive : public Usermod {
void AudioReactive::removeAudioPalettes(void) { void AudioReactive::removeAudioPalettes(void) {
DEBUG_PRINTLN(F("Removing audio palettes.")); DEBUG_PRINTLN(F("Removing audio palettes."));
while (palettes) { while (palettes>0) {
strip.customPalettes.pop_back(); strip.customPalettes.pop_back();
DEBUG_PRINTLN(palettes); DEBUG_PRINTLN(palettes);
palettes--; palettes--;
@ -1869,6 +1869,7 @@ void AudioReactive::removeAudioPalettes(void) {
void AudioReactive::createAudioPalettes(void) { void AudioReactive::createAudioPalettes(void) {
DEBUG_PRINT(F("Total # of palettes: ")); DEBUG_PRINTLN(strip.customPalettes.size()); DEBUG_PRINT(F("Total # of palettes: ")); DEBUG_PRINTLN(strip.customPalettes.size());
if (palettes) return;
DEBUG_PRINTLN(F("Adding audio palettes.")); DEBUG_PRINTLN(F("Adding audio palettes."));
for (int i=0; i<MAX_PALETTES; i++) for (int i=0; i<MAX_PALETTES; i++)
if (strip.customPalettes.size() < 10) { if (strip.customPalettes.size() < 10) {
@ -1907,9 +1908,11 @@ CRGB AudioReactive::getCRGBForBand(int x, int pal) {
return value; return value;
} }
void AudioReactive::fillAudioPalette(int pal) { void AudioReactive::fillAudioPalettes() {
if (pal>=palettes) return; // palette does not exist if (!palettes) return;
size_t lastCustPalette = strip.customPalettes.size();
if (lastCustPalette >= palettes) lastCustPalette -= palettes;
for (size_t pal=0; pal<palettes; pal++) {
uint8_t tcp[16]; // Needs to be 4 times however many colors are being used. uint8_t tcp[16]; // Needs to be 4 times however many colors are being used.
// 3 colors = 12, 4 colors = 16, etc. // 3 colors = 12, 4 colors = 16, etc.
@ -1936,7 +1939,8 @@ void AudioReactive::fillAudioPalette(int pal) {
tcp[14] = rgb.g; tcp[14] = rgb.g;
tcp[15] = rgb.b; tcp[15] = rgb.b;
strip.customPalettes[strip.customPalettes.size()-1-palettes+pal].loadDynamicGradientPalette(tcp); strip.customPalettes[lastCustPalette+pal].loadDynamicGradientPalette(tcp);
}
} }
// strings to reduce flash memory usage (used more than twice) // strings to reduce flash memory usage (used more than twice)

View File

@ -2590,14 +2590,14 @@ uint16_t mode_twinklefox()
{ {
return twinklefox_base(false); return twinklefox_base(false);
} }
static const char _data_FX_MODE_TWINKLEFOX[] PROGMEM = "Twinklefox@!,Twinkle rate,,,,Cool;;!"; static const char _data_FX_MODE_TWINKLEFOX[] PROGMEM = "Twinklefox@!,Twinkle rate,,,,Cool;!,!;!";
uint16_t mode_twinklecat() uint16_t mode_twinklecat()
{ {
return twinklefox_base(true); return twinklefox_base(true);
} }
static const char _data_FX_MODE_TWINKLECAT[] PROGMEM = "Twinklecat@!,Twinkle rate,,,,Cool;;!"; static const char _data_FX_MODE_TWINKLECAT[] PROGMEM = "Twinklecat@!,Twinkle rate,,,,Cool;!,!;!";
//inspired by https://www.tweaking4all.com/hardware/arduino/adruino-led-strip-effects/#LEDStripEffectBlinkingHalloweenEyes //inspired by https://www.tweaking4all.com/hardware/arduino/adruino-led-strip-effects/#LEDStripEffectBlinkingHalloweenEyes

View File

@ -90,18 +90,21 @@ Segment::Segment(const Segment &orig) {
//DEBUG_PRINTF("-- Copy segment constructor: %p -> %p\n", &orig, this); //DEBUG_PRINTF("-- Copy segment constructor: %p -> %p\n", &orig, this);
memcpy((void*)this, (void*)&orig, sizeof(Segment)); memcpy((void*)this, (void*)&orig, sizeof(Segment));
_t = nullptr; // copied segment cannot be in transition _t = nullptr; // copied segment cannot be in transition
if (orig.name) { name = new char[strlen(orig.name)+1]; if (name) strcpy(name, orig.name); } else { name = nullptr; } name = nullptr;
if (orig.data) { if (allocateData(orig._dataLen)) memcpy(data, orig.data, orig._dataLen); } else { data = nullptr; _dataLen = 0; } data = nullptr;
_dataLen = 0;
if (orig.name) { name = new char[strlen(orig.name)+1]; if (name) strcpy(name, orig.name); }
if (orig.data) { if (allocateData(orig._dataLen)) memcpy(data, orig.data, orig._dataLen); }
} }
// move constructor // move constructor
Segment::Segment(Segment &&orig) noexcept { Segment::Segment(Segment &&orig) noexcept {
//DEBUG_PRINTF("-- Move segment constructor: %p -> %p\n", &orig, this); //DEBUG_PRINTF("-- Move segment constructor: %p -> %p\n", &orig, this);
memcpy((void*)this, (void*)&orig, sizeof(Segment)); memcpy((void*)this, (void*)&orig, sizeof(Segment));
orig._t = nullptr; // old segment cannot be in transition any more
orig.name = nullptr; orig.name = nullptr;
orig.data = nullptr; orig.data = nullptr;
orig._dataLen = 0; orig._dataLen = 0;
orig._t = nullptr; // old segment cannot be in transition any more
} }
// copy assignment // copy assignment
@ -110,14 +113,7 @@ Segment& Segment::operator= (const Segment &orig) {
if (this != &orig) { if (this != &orig) {
// clean destination // clean destination
if (name) { delete[] name; name = nullptr; } if (name) { delete[] name; name = nullptr; }
if (orig.name) { name = new char[strlen(orig.name)+1]; if (name) strcpy(name, orig.name); } stopTransition();
if (_t) {
#ifndef WLED_DISABLE_MODE_BLEND
if (_t->_segT._dataT) free(_t->_segT._dataT);
#endif
delete _t;
_t = nullptr; // copied segment cannot be in transition
}
deallocateData(); deallocateData();
// copy source // copy source
memcpy((void*)this, (void*)&orig, sizeof(Segment)); memcpy((void*)this, (void*)&orig, sizeof(Segment));
@ -125,6 +121,7 @@ Segment& Segment::operator= (const Segment &orig) {
data = nullptr; data = nullptr;
_dataLen = 0; _dataLen = 0;
// copy source data // copy source data
if (orig.name) { name = new char[strlen(orig.name)+1]; if (name) strcpy(name, orig.name); }
if (orig.data) { if (allocateData(orig._dataLen)) memcpy(data, orig.data, orig._dataLen); } if (orig.data) { if (allocateData(orig._dataLen)) memcpy(data, orig.data, orig._dataLen); }
} }
return *this; return *this;
@ -135,13 +132,7 @@ Segment& Segment::operator= (Segment &&orig) noexcept {
//DEBUG_PRINTF("-- Moving segment: %p -> %p\n", &orig, this); //DEBUG_PRINTF("-- Moving segment: %p -> %p\n", &orig, this);
if (this != &orig) { if (this != &orig) {
if (name) { delete[] name; name = nullptr; } // free old name if (name) { delete[] name; name = nullptr; } // free old name
if (_t) { stopTransition();
#ifndef WLED_DISABLE_MODE_BLEND
if (_t->_segT._dataT) free(_t->_segT._dataT);
#endif
delete _t;
_t = nullptr;
}
deallocateData(); // free old runtime data deallocateData(); // free old runtime data
memcpy((void*)this, (void*)&orig, sizeof(Segment)); memcpy((void*)this, (void*)&orig, sizeof(Segment));
orig.name = nullptr; orig.name = nullptr;
@ -312,7 +303,7 @@ void Segment::startTransition(uint16_t dur) {
if (_dataLen > 0 && data) { if (_dataLen > 0 && data) {
_t->_segT._dataT = (byte *)malloc(_dataLen); _t->_segT._dataT = (byte *)malloc(_dataLen);
if (_t->_segT._dataT) { if (_t->_segT._dataT) {
//DEBUG_PRINTF("-- Allocated duplicate data (%d): %p\n", _dataLen, _t->_segT._dataT); //DEBUG_PRINTF("-- Allocated duplicate data (%d) for %p: %p\n", _dataLen, this, _t->_segT._dataT);
memcpy(_t->_segT._dataT, data, _dataLen); memcpy(_t->_segT._dataT, data, _dataLen);
_t->_segT._dataLenT = _dataLen; _t->_segT._dataLenT = _dataLen;
} }
@ -330,7 +321,7 @@ void Segment::stopTransition() {
if (isInTransition()) { if (isInTransition()) {
#ifndef WLED_DISABLE_MODE_BLEND #ifndef WLED_DISABLE_MODE_BLEND
if (_t->_segT._dataT && _t->_segT._dataLenT > 0) { if (_t->_segT._dataT && _t->_segT._dataLenT > 0) {
//DEBUG_PRINTF("-- Released duplicate data (%d): %p\n", _t->_segT._dataLenT, _t->_segT._dataT); //DEBUG_PRINTF("-- Released duplicate data (%d) for %p: %p\n", _t->_segT._dataLenT, this, _t->_segT._dataT);
free(_t->_segT._dataT); free(_t->_segT._dataT);
_t->_segT._dataT = nullptr; _t->_segT._dataT = nullptr;
_t->_segT._dataLenT = 0; _t->_segT._dataLenT = 0;
@ -588,7 +579,7 @@ void Segment::setMode(uint8_t fx, bool loadDefaults) {
sOpt = extractModeDefaults(fx, "o2"); check2 = (sOpt >= 0) ? (bool)sOpt : false; sOpt = extractModeDefaults(fx, "o2"); check2 = (sOpt >= 0) ? (bool)sOpt : false;
sOpt = extractModeDefaults(fx, "o3"); check3 = (sOpt >= 0) ? (bool)sOpt : false; sOpt = extractModeDefaults(fx, "o3"); check3 = (sOpt >= 0) ? (bool)sOpt : false;
sOpt = extractModeDefaults(fx, "m12"); if (sOpt >= 0) map1D2D = constrain(sOpt, 0, 7); sOpt = extractModeDefaults(fx, "m12"); if (sOpt >= 0) map1D2D = constrain(sOpt, 0, 7);
sOpt = extractModeDefaults(fx, "si"); if (sOpt >= 0) soundSim = constrain(sOpt, 0, 1); sOpt = extractModeDefaults(fx, "si"); if (sOpt >= 0) soundSim = constrain(sOpt, 0, 3);
sOpt = extractModeDefaults(fx, "rev"); if (sOpt >= 0) reverse = (bool)sOpt; sOpt = extractModeDefaults(fx, "rev"); if (sOpt >= 0) reverse = (bool)sOpt;
sOpt = extractModeDefaults(fx, "mi"); if (sOpt >= 0) mirror = (bool)sOpt; // NOTE: setting this option is a risky business sOpt = extractModeDefaults(fx, "mi"); if (sOpt >= 0) mirror = (bool)sOpt; // NOTE: setting this option is a risky business
sOpt = extractModeDefaults(fx, "rY"); if (sOpt >= 0) reverse_y = (bool)sOpt; sOpt = extractModeDefaults(fx, "rY"); if (sOpt >= 0) reverse_y = (bool)sOpt;

View File

@ -34,6 +34,7 @@
--bhd: none; --bhd: none;
--sgp: "block"; --sgp: "block";
--bmt: 0; --bmt: 0;
--sti: 42px;
} }
html { html {
@ -457,12 +458,17 @@ button {
padding: 4px 2px; padding: 4px 2px;
position: relative; position: relative;
opacity: 1; opacity: 1;
transition: opacity .5s linear, height .5s, transform .5s; transition: opacity .5s linear, height .25s, transform .25s;
} }
.filter { .filter {
z-index: 1; z-index: 1;
/*overflow: visible;*/ /*overflow: visible;*/
border-radius: 0 0 16px 16px;
max-width: 220px;
height: 54px;
line-height: 1.5;
padding-bottom: 8px;
} }
/* New tooltip */ /* New tooltip */
@ -470,7 +476,7 @@ button {
position: absolute; position: absolute;
opacity: 0; opacity: 0;
visibility: hidden; visibility: hidden;
transition: opacity .4s ease, visibility .4s ease; transition: opacity .25s ease, visibility .25s ease;
background-color: var(--c-5); background-color: var(--c-5);
box-shadow: 4px 4px 10px 4px var(--c-1); box-shadow: 4px 4px 10px 4px var(--c-1);
color: var(--c-f); color: var(--c-f);
@ -478,13 +484,13 @@ button {
padding: 8px 16px; padding: 8px 16px;
border-radius: 6px; border-radius: 6px;
z-index: 1; z-index: 1;
pointer-events: none;
} }
.tooltip::after { .tooltip::after {
content: ""; content: "";
position: absolute; position: absolute;
border: 8px; border: 8px solid;
border-style: solid;
border-color: var(--c-5) transparent transparent transparent; border-color: var(--c-5) transparent transparent transparent;
top: 100%; top: 100%;
left: calc(50% - 8px); left: calc(50% - 8px);
@ -1231,6 +1237,10 @@ TD .checkmark, TD .radiomark {
-webkit-filter: grayscale(100%); /* Safari 6.0 - 9.0 */ -webkit-filter: grayscale(100%); /* Safari 6.0 - 9.0 */
filter: grayscale(100%); filter: grayscale(100%);
} }
.filter .fchkl {
margin: 0 4px;
min-width: 20px;
}
.lbl-l { .lbl-l {
font-size: 13px; font-size: 13px;
@ -1307,20 +1317,14 @@ TD .checkmark, TD .radiomark {
top: 42px; top: 42px;
} }
#fxlist .lstI.selected { #fxlist .lstI.selected,
top: 84px;
}
#fxlist .lstI.sticky {
top: 42px;
}
#pallist .lstI.selected { #pallist .lstI.selected {
top: 84px; top: calc(var(--sti) + 42px);
} }
#fxlist .lstI.sticky,
#pallist .lstI.sticky { #pallist .lstI.sticky {
top: 42px; top: var(--sti);
} }
/* list item content */ /* list item content */

View File

@ -198,9 +198,35 @@
<div id="fx"> <div id="fx">
<p class="labels hd" id="modeLabel">Effect mode</p> <p class="labels hd" id="modeLabel">Effect mode</p>
<div class="staytop fnd" id="fxFind"> <div class="staytop fnd" id="fxFind">
<input type="text" placeholder="Search" oninput="search(this,'fxlist')" onfocus="search(this,'fxlist');gId('filters').classList.add('fade');" onblur="gId('filters').classList.remove('fade')"/> <input type="text" placeholder="Search" oninput="search(this,'fxlist')" onfocus="filterFocus(event);search(this,'fxlist');" onblur="filterFocus(event);" />
<i class="icons clear-icon" onclick="clean(this);">&#xe38f;</i> <i class="icons clear-icon" onclick="clean(this);">&#xe38f;</i>
<i class="icons search-icon" onclick="gId('filters').classList.toggle('hide');" style="cursor:pointer;">&#xe0a1;</i> <i class="icons search-icon" style="cursor:pointer;">&#xe0a1;</i>
<div id="filters" class="filter fade">
<label id="filterPal" tooltip="Uses palette" class="check fchkl">&#x1F3A8;
<input type="checkbox" data-flt="&#x1F3A8;" onchange="filterFx(this);">
<span class="checkmark"></span>
</label>
<label id="filter0D" tooltip="Single pixel" class="check fchkl">&#8226;
<input type="checkbox" data-flt="&#8226;" onchange="filterFx(this);">
<span class="checkmark"></span>
</label>
<label id="filter1D" tooltip="1D" class="check fchkl">&#8942;
<input type="checkbox" data-flt="&#8942;" onchange="filterFx(this);">
<span class="checkmark"></span>
</label>
<label id="filter2D" tooltip="2D" class="check fchkl">&#9638;
<input type="checkbox" data-flt="&#9638;" onchange="filterFx(this);">
<span class="checkmark"></span>
</label>
<label id="filterVol" tooltip="Volume" class="check fchkl">&#9834;
<input type="checkbox" data-flt="&#9834;" onchange="filterFx(this);">
<span class="checkmark"></span>
</label>
<label id="filterFreq" tooltip="Frequency" class="check fchkl">&#9835;
<input type="checkbox" data-flt="&#9835;" onchange="filterFx(this);">
<span class="checkmark"></span>
</label>
</div>
</div> </div>
<div id="fxlist" class="list"> <div id="fxlist" class="list">
<div class="lstI"> <div class="lstI">
@ -215,32 +241,6 @@
</div> </div>
</div> </div>
<div id="sliders"> <div id="sliders">
<div id="filters" class="filter">
<label id="filterPal" tooltip="Uses palette" class="check fchkl">&#x1F3A8;
<input type="checkbox" data-flt="&#x1F3A8;" onchange="filterFx(this)">
<span class="checkmark"></span>
</label>
<label id="filter0D" tooltip="Single pixel" class="check fchkl hide">&#8226;
<input type="checkbox" data-flt="&#8226;" onchange="filterFx(this)">
<span class="checkmark"></span>
</label>
<label id="filter1D" tooltip="1D" class="check fchkl">&#8942;
<input type="checkbox" data-flt="&#8942;" onchange="filterFx(this)">
<span class="checkmark"></span>
</label>
<label id="filter2D" tooltip="2D" class="check fchkl">&#9638;
<input type="checkbox" data-flt="&#9638;" onchange="filterFx(this)">
<span class="checkmark"></span>
</label>
<label id="filterVol" tooltip="Volume" class="check fchkl">&#9834;
<input type="checkbox" data-flt="&#9834;" onchange="filterFx(this)">
<span class="checkmark"></span>
</label>
<label id="filterFreq" tooltip="Frequency" class="check fchkl">&#9835;
<input type="checkbox" data-flt="&#9835;" onchange="filterFx(this)">
<span class="checkmark"></span>
</label>
</div>
<div id="slider0" class="slider"> <div id="slider0" class="slider">
<i class="icons slider-icon" onclick="tglFreeze()">&#xe325;</i> <i class="icons slider-icon" onclick="tglFreeze()">&#xe325;</i>
<div tooltip="Effect speed" class="sliderwrap il"> <div tooltip="Effect speed" class="sliderwrap il">

View File

@ -232,6 +232,7 @@ function onLoad()
var sett = localStorage.getItem('wledUiCfg'); var sett = localStorage.getItem('wledUiCfg');
if (sett) cfg = mergeDeep(cfg, JSON.parse(sett)); if (sett) cfg = mergeDeep(cfg, JSON.parse(sett));
tooltip();
resetPUtil(); resetPUtil();
if (localStorage.getItem('pcm') == "true" || (!/Mobi/.test(navigator.userAgent) && localStorage.getItem('pcm') == null)) togglePcMode(true); if (localStorage.getItem('pcm') == "true" || (!/Mobi/.test(navigator.userAgent) && localStorage.getItem('pcm') == null)) togglePcMode(true);
@ -637,12 +638,12 @@ function parseInfo(i) {
mh = i.leds.matrix ? i.leds.matrix.h : 0; mh = i.leds.matrix ? i.leds.matrix.h : 0;
isM = mw>0 && mh>0; isM = mw>0 && mh>0;
if (!isM) { if (!isM) {
gId("filter0D").classList.remove('hide'); //gId("filter0D").classList.remove('hide');
gId("filter1D").classList.add('hide'); //gId("filter1D").classList.add('hide');
gId("filter2D").classList.add('hide'); gId("filter2D").classList.add('hide');
} else { } else {
gId("filter0D").classList.add('hide'); //gId("filter0D").classList.add('hide');
gId("filter1D").classList.remove('hide'); //gId("filter1D").classList.remove('hide');
gId("filter2D").classList.remove('hide'); gId("filter2D").classList.remove('hide');
} }
// if (i.noaudio) { // if (i.noaudio) {
@ -1421,7 +1422,7 @@ function readState(s,command=false)
if (s.seg.length>2) d.querySelectorAll(".pop").forEach((e)=>{e.classList.remove("hide");}); if (s.seg.length>2) d.querySelectorAll(".pop").forEach((e)=>{e.classList.remove("hide");});
var cd = gId('csl').children; var cd = gId('csl').querySelectorAll("button");
for (let e = cd.length-1; e >= 0; e--) { for (let e = cd.length-1; e >= 0; e--) {
cd[e].dataset.r = i.col[e][0]; cd[e].dataset.r = i.col[e][0];
cd[e].dataset.g = i.col[e][1]; cd[e].dataset.g = i.col[e][1];
@ -1541,7 +1542,7 @@ function setEffectParameters(idx)
var cslLabel = ''; var cslLabel = '';
var sep = ''; var sep = '';
var cslCnt = 0, oCsel = csel; var cslCnt = 0, oCsel = csel;
for (let i=0; i<gId("csl").children.length; i++) { for (let i=0; i<gId("csl").querySelectorAll("button"); i++) {
var btn = gId("csl" + i); var btn = gId("csl" + i);
// if no controlDefined or coOnOff has a value // if no controlDefined or coOnOff has a value
if (coOnOff.length>i && coOnOff[i] != "") { if (coOnOff.length>i && coOnOff[i] != "") {
@ -2744,6 +2745,25 @@ function clean(c)
} }
} }
function filterFocus(e)
{
let t = e.explicitOriginalTarget;
let f = gId("filters");
if (e.type === "focus") f.classList.remove('fade'); // immediately show (still has transition)
// compute sticky top (with delay for transition)
setTimeout(()=>{
let sti = parseInt(getComputedStyle(d.documentElement).getPropertyValue('--sti')) + (e.type === "focus" ? 1 : -1) * f.offsetHeight;
sCol('--sti', sti+"px");
}, 252);
if (e.type === "blur") {
do {
if (t.id && (t.id === "fxFind")) { setTimeout(()=>{t.firstElementChild.focus();},150); return; }
t = t.parentElement;
} while (t.tagName !== "BODY");
setTimeout(()=>{f.classList.add('fade');},255); // wait with hiding
}
}
function filterFx(o) function filterFx(o)
{ {
if (!o) return; if (!o) return;
@ -2933,11 +2953,9 @@ function mergeDeep(target, ...sources)
function tooltip() function tooltip()
{ {
const elements = d.querySelectorAll("[tooltip]"); const elements = d.querySelectorAll("[tooltip]");
elements.forEach((element)=>{ elements.forEach((element)=>{
element.addEventListener("mouseover", ()=>{ element.addEventListener("mouseover", ()=>{
const tooltip = d.createElement("span"); const tooltip = d.createElement("span");
tooltip.className = "tooltip"; tooltip.className = "tooltip";
tooltip.textContent = element.getAttribute("tooltip"); tooltip.textContent = element.getAttribute("tooltip");
@ -2947,22 +2965,23 @@ function tooltip()
const { offsetHeight, offsetWidth } = tooltip; const { offsetHeight, offsetWidth } = tooltip;
const offset = element.classList.contains("sliderwrap") ? 6 : 12; const offset = element.classList.contains("sliderwrap") ? 4 : 10;
top -= offsetHeight + offset; top -= offsetHeight + offset;
left += (width - offsetWidth) / 2; left += (width - offsetWidth) / 2;
tooltip.style.top = top + "px"; tooltip.style.top = top + "px";
tooltip.style.left = left + "px"; tooltip.style.left = left + "px";
tooltip.classList.add("visible"); tooltip.classList.add("visible");
}); });
element.addEventListener("mouseout", ()=>{ element.addEventListener("mouseout", ()=>{
const tooltip = d.querySelector('.tooltip'); const tooltips = d.querySelectorAll('.tooltip');
tooltips.forEach((tooltip)=>{
tooltip.classList.remove("visible"); tooltip.classList.remove("visible");
d.body.removeChild(tooltip); d.body.removeChild(tooltip);
}); });
}); });
});
}; };
size(); size();
@ -2976,5 +2995,3 @@ _C.addEventListener('touchstart', lock, false);
_C.addEventListener('mouseout', move, false); _C.addEventListener('mouseout', move, false);
_C.addEventListener('mouseup', move, false); _C.addEventListener('mouseup', move, false);
_C.addEventListener('touchend', move, false); _C.addEventListener('touchend', move, false);
d.addEventListener('DOMContentLoaded', tooltip);

File diff suppressed because it is too large Load Diff

View File

@ -33,9 +33,9 @@ bool deserializeSegment(JsonObject elem, byte it, byte presetId)
//DEBUG_PRINTLN("-- JSON deserialize segment."); //DEBUG_PRINTLN("-- JSON deserialize segment.");
Segment& seg = strip.getSegment(id); Segment& seg = strip.getSegment(id);
//DEBUG_PRINTF("-- Original segment: %p\n", &seg); //DEBUG_PRINTF("-- Original segment: %p (%p)\n", &seg, seg.data);
Segment prev = seg; //make a backup so we can tell if something changed Segment prev = seg; //make a backup so we can tell if something changed (calling copy constructor)
//DEBUG_PRINTF("-- Duplicate segment: %p\n", &prev); //DEBUG_PRINTF("-- Duplicate segment: %p (%p)\n", &prev, prev.data);
uint16_t start = elem["start"] | seg.start; uint16_t start = elem["start"] | seg.start;
if (stop < 0) { if (stop < 0) {
@ -100,7 +100,7 @@ bool deserializeSegment(JsonObject elem, byte it, byte presetId)
if ((spc>0 && spc!=seg.spacing) || seg.map1D2D!=map1D2D) seg.fill(BLACK); // clear spacing gaps if ((spc>0 && spc!=seg.spacing) || seg.map1D2D!=map1D2D) seg.fill(BLACK); // clear spacing gaps
seg.map1D2D = constrain(map1D2D, 0, 7); seg.map1D2D = constrain(map1D2D, 0, 7);
seg.soundSim = constrain(soundSim, 0, 1); seg.soundSim = constrain(soundSim, 0, 3);
uint8_t set = elem[F("set")] | seg.set; uint8_t set = elem[F("set")] | seg.set;
seg.set = constrain(set, 0, 3); seg.set = constrain(set, 0, 3);
@ -1015,8 +1015,15 @@ void serializeModeNames(JsonArray arr)
} }
} }
static volatile bool servingClient = false;
void serveJson(AsyncWebServerRequest* request) void serveJson(AsyncWebServerRequest* request)
{ {
if (servingClient) {
request->send(503, "application/json", F("{\"error\":2}")); // ERR_CONCURENCY
return;
}
servingClient = true;
byte subJson = 0; byte subJson = 0;
const String& url = request->url(); const String& url = request->url();
if (url.indexOf("state") > 0) subJson = JSON_PATH_STATE; if (url.indexOf("state") > 0) subJson = JSON_PATH_STATE;
@ -1030,23 +1037,28 @@ void serveJson(AsyncWebServerRequest* request)
#ifdef WLED_ENABLE_JSONLIVE #ifdef WLED_ENABLE_JSONLIVE
else if (url.indexOf("live") > 0) { else if (url.indexOf("live") > 0) {
serveLiveLeds(request); serveLiveLeds(request);
servingClient = false;
return; return;
} }
#endif #endif
else if (url.indexOf("pal") > 0) { else if (url.indexOf("pal") > 0) {
request->send_P(200, "application/json", JSON_palette_names); request->send_P(200, "application/json", JSON_palette_names);
servingClient = false;
return; return;
} }
else if (url.indexOf("cfg") > 0 && handleFileRead(request, "/cfg.json")) { else if (url.indexOf("cfg") > 0 && handleFileRead(request, "/cfg.json")) {
servingClient = false;
return; return;
} }
else if (url.length() > 6) { //not just /json else if (url.length() > 6) { //not just /json
request->send(501, "application/json", F("{\"error\":\"Not implemented\"}")); request->send(501, "application/json", F("{\"error\":\"Not implemented\"}"));
servingClient = false;
return; return;
} }
if (!requestJSONBufferLock(17)) { if (!requestJSONBufferLock(17)) {
request->send(503, "application/json", F("{\"error\":3}")); request->send(503, "application/json", F("{\"error\":3}"));
servingClient = false;
return; return;
} }
AsyncJsonResponse *response = new AsyncJsonResponse(&doc, subJson==JSON_PATH_FXDATA || subJson==JSON_PATH_EFFECTS); // will clear and convert JsonDocument into JsonArray if necessary AsyncJsonResponse *response = new AsyncJsonResponse(&doc, subJson==JSON_PATH_FXDATA || subJson==JSON_PATH_EFFECTS); // will clear and convert JsonDocument into JsonArray if necessary
@ -1093,10 +1105,11 @@ void serveJson(AsyncWebServerRequest* request)
request->send(response); request->send(response);
releaseJSONBufferLock(); releaseJSONBufferLock();
servingClient = false;
} }
#ifdef WLED_ENABLE_JSONLIVE #ifdef WLED_ENABLE_JSONLIVE
#define MAX_LIVE_LEDS 180 #define MAX_LIVE_LEDS 256
bool serveLiveLeds(AsyncWebServerRequest* request, uint32_t wsClient) bool serveLiveLeds(AsyncWebServerRequest* request, uint32_t wsClient)
{ {
@ -1110,13 +1123,26 @@ bool serveLiveLeds(AsyncWebServerRequest* request, uint32_t wsClient)
uint16_t used = strip.getLengthTotal(); uint16_t used = strip.getLengthTotal();
uint16_t n = (used -1) /MAX_LIVE_LEDS +1; //only serve every n'th LED if count over MAX_LIVE_LEDS uint16_t n = (used -1) /MAX_LIVE_LEDS +1; //only serve every n'th LED if count over MAX_LIVE_LEDS
char buffer[2000]; #ifndef WLED_DISABLE_2D
if (strip.isMatrix) {
// ignore anything behid matrix (i.e. extra strip)
used = Segment::maxWidth*Segment::maxHeight; // always the size of matrix (more or less than strip.getLengthTotal())
n = 1;
if (used > MAX_LIVE_LEDS) n = 2;
if (used > MAX_LIVE_LEDS*4) n = 4;
}
#endif
char buffer[2048]; // shoud be enough for 256 LEDs [RRGGBB] + all other text (9+25)
strcpy_P(buffer, PSTR("{\"leds\":[")); strcpy_P(buffer, PSTR("{\"leds\":["));
obuf = buffer; obuf = buffer; // assign buffer for oappnd() functions
olen = 9; olen = 9;
for (size_t i= 0; i < used; i += n) for (size_t i = 0; i < used; i += n)
{ {
#ifndef WLED_DISABLE_2D
if (strip.isMatrix && n>1 && (i/Segment::maxWidth)%n) i += Segment::maxWidth * (n-1);
#endif
uint32_t c = strip.getPixelColor(i); uint32_t c = strip.getPixelColor(i);
uint8_t r = R(c); uint8_t r = R(c);
uint8_t g = G(c); uint8_t g = G(c);
@ -1125,11 +1151,19 @@ bool serveLiveLeds(AsyncWebServerRequest* request, uint32_t wsClient)
r = scale8(qadd8(w, r), strip.getBrightness()); //R, add white channel to RGB channels as a simple RGBW -> RGB map r = scale8(qadd8(w, r), strip.getBrightness()); //R, add white channel to RGB channels as a simple RGBW -> RGB map
g = scale8(qadd8(w, g), strip.getBrightness()); //G g = scale8(qadd8(w, g), strip.getBrightness()); //G
b = scale8(qadd8(w, b), strip.getBrightness()); //B b = scale8(qadd8(w, b), strip.getBrightness()); //B
olen += sprintf(obuf + olen, "\"%06X\",", RGBW32(r,g,b,0)); olen += sprintf_P(obuf + olen, PSTR("\"%06X\","), RGBW32(r,g,b,0));
} }
olen -= 1; olen -= 1;
oappend((const char*)F("],\"n\":")); oappend((const char*)F("],\"n\":"));
oappendi(n); oappendi(n);
#ifndef WLED_DISABLE_2D
if (strip.isMatrix) {
oappend((const char*)F(",\"w\":"));
oappendi(Segment::maxWidth/n);
oappend((const char*)F(",\"h\":"));
oappendi(Segment::maxHeight/n);
}
#endif
oappend("}"); oappend("}");
if (request) { if (request) {
request->send(200, "application/json", buffer); request->send(200, "application/json", buffer);
@ -1139,6 +1173,7 @@ bool serveLiveLeds(AsyncWebServerRequest* request, uint32_t wsClient)
wsc->text(obuf, olen); wsc->text(obuf, olen);
} }
#endif #endif
obuf = nullptr;
return true; return true;
} }
#endif #endif

View File

@ -296,8 +296,9 @@ void parseNotifyPacket(uint8_t *udpIn) {
} }
if (version > 11) { if (version > 11) {
// when applying synced options ignore selected as it may be used as indicator of which segments to sync // when applying synced options ignore selected as it may be used as indicator of which segments to sync
// freeze, reset & transitional should never be synced // freeze, reset should never be synced
selseg.options = (selseg.options & 0x0071U) | (udpIn[28+ofs]<<8) | (udpIn[9 +ofs] & 0x8E); // ignore selected, freeze, reset & transitional // LSB to MSB: select, reverse, on, mirror, freeze, reset, reverse_y, mirror_y, transpose, map1d2d (3), ssim (2), set (2)
selseg.options = (selseg.options & 0b0000000000110001U) | (udpIn[28+ofs]<<8) | (udpIn[9 +ofs] & 0b11001110U); // ignore selected, freeze, reset
if (applyEffects) { if (applyEffects) {
selseg.custom1 = udpIn[29+ofs]; selseg.custom1 = udpIn[29+ofs];
selseg.custom2 = udpIn[30+ofs]; selseg.custom2 = udpIn[30+ofs];
@ -532,6 +533,7 @@ void handleNotifications()
//wled notifier, ignore if realtime packets active //wled notifier, ignore if realtime packets active
if (udpIn[0] == 0 && !realtimeMode && receiveGroups) if (udpIn[0] == 0 && !realtimeMode && receiveGroups)
{ {
DEBUG_PRINT(F("UDP notification from: ")); DEBUG_PRINTLN(notifierUdp.remoteIP());
parseNotifyPacket(udpIn); parseNotifyPacket(udpIn);
return; return;
} }

View File

@ -61,11 +61,11 @@ void WLED::loop()
#ifdef WLED_ENABLE_DMX #ifdef WLED_ENABLE_DMX
handleDMX(); handleDMX();
#endif #endif
userLoop();
#ifdef WLED_DEBUG #ifdef WLED_DEBUG
unsigned long usermodMillis = millis(); unsigned long usermodMillis = millis();
#endif #endif
userLoop();
usermods.loop(); usermods.loop();
#ifdef WLED_DEBUG #ifdef WLED_DEBUG
usermodMillis = millis() - usermodMillis; usermodMillis = millis() - usermodMillis;
@ -186,7 +186,9 @@ void WLED::loop()
yield(); yield();
handleWs(); handleWs();
#if defined(STATUSLED)
handleStatusLED(); handleStatusLED();
#endif
toki.resetTick(); toki.resetTick();
@ -256,9 +258,9 @@ void WLED::loop()
#endif // WLED_DEBUG #endif // WLED_DEBUG
} }
void WLED::enableWatchdog() {
#if WLED_WATCHDOG_TIMEOUT > 0 #if WLED_WATCHDOG_TIMEOUT > 0
#ifdef ARDUINO_ARCH_ESP32 void WLED::enableWatchdog() {
#ifdef ARDUINO_ARCH_ESP32
esp_err_t watchdog = esp_task_wdt_init(WLED_WATCHDOG_TIMEOUT, true); esp_err_t watchdog = esp_task_wdt_init(WLED_WATCHDOG_TIMEOUT, true);
DEBUG_PRINT(F("Watchdog enabled: ")); DEBUG_PRINT(F("Watchdog enabled: "));
if (watchdog == ESP_OK) { if (watchdog == ESP_OK) {
@ -268,22 +270,20 @@ void WLED::enableWatchdog() {
return; return;
} }
esp_task_wdt_add(NULL); esp_task_wdt_add(NULL);
#else #else
ESP.wdtEnable(WLED_WATCHDOG_TIMEOUT * 1000); ESP.wdtEnable(WLED_WATCHDOG_TIMEOUT * 1000);
#endif #endif
#endif
} }
void WLED::disableWatchdog() { void WLED::disableWatchdog() {
#if WLED_WATCHDOG_TIMEOUT > 0 DEBUG_PRINTLN(F("Watchdog: disabled"));
DEBUG_PRINTLN(F("Watchdog: disabled")); #ifdef ARDUINO_ARCH_ESP32
#ifdef ARDUINO_ARCH_ESP32
esp_task_wdt_delete(NULL); esp_task_wdt_delete(NULL);
#else #else
ESP.wdtDisable(); ESP.wdtDisable();
#endif #endif
#endif
} }
#endif
void WLED::setup() void WLED::setup()
{ {
@ -464,15 +464,19 @@ void WLED::setup()
#ifndef WLED_DISABLE_OTA #ifndef WLED_DISABLE_OTA
if (aOtaEnabled) { if (aOtaEnabled) {
ArduinoOTA.onStart([]() { ArduinoOTA.onStart([]() {
#ifdef ESP8266 #ifdef ESP8266
wifi_set_sleep_type(NONE_SLEEP_T); wifi_set_sleep_type(NONE_SLEEP_T);
#endif #endif
#if WLED_WATCHDOG_TIMEOUT > 0
WLED::instance().disableWatchdog(); WLED::instance().disableWatchdog();
#endif
DEBUG_PRINTLN(F("Start ArduinoOTA")); DEBUG_PRINTLN(F("Start ArduinoOTA"));
}); });
ArduinoOTA.onError([](ota_error_t error) { ArduinoOTA.onError([](ota_error_t error) {
#if WLED_WATCHDOG_TIMEOUT > 0
// reenable watchdog on failed update // reenable watchdog on failed update
WLED::instance().enableWatchdog(); WLED::instance().enableWatchdog();
#endif
}); });
if (strlen(cmDNS) > 0) if (strlen(cmDNS) > 0)
ArduinoOTA.setHostname(cmDNS); ArduinoOTA.setHostname(cmDNS);
@ -491,7 +495,9 @@ void WLED::setup()
initServer(); initServer();
DEBUG_PRINT(F("heap ")); DEBUG_PRINTLN(ESP.getFreeHeap()); DEBUG_PRINT(F("heap ")); DEBUG_PRINTLN(ESP.getFreeHeap());
#if WLED_WATCHDOG_TIMEOUT > 0
enableWatchdog(); enableWatchdog();
#endif
#if defined(ARDUINO_ARCH_ESP32) && defined(WLED_DISABLE_BROWNOUT_DET) #if defined(ARDUINO_ARCH_ESP32) && defined(WLED_DISABLE_BROWNOUT_DET)
WRITE_PERI_REG(RTC_CNTL_BROWN_OUT_REG, 1); //enable brownout detector WRITE_PERI_REG(RTC_CNTL_BROWN_OUT_REG, 1); //enable brownout detector
@ -915,9 +921,9 @@ void WLED::handleConnection()
// else blink at 1Hz when WLED_CONNECTED is false (no WiFi, ?? no Ethernet ??) // else blink at 1Hz when WLED_CONNECTED is false (no WiFi, ?? no Ethernet ??)
// else blink at 2Hz when MQTT is enabled but not connected // else blink at 2Hz when MQTT is enabled but not connected
// else turn the status LED off // else turn the status LED off
#if defined(STATUSLED)
void WLED::handleStatusLED() void WLED::handleStatusLED()
{ {
#if defined(STATUSLED)
uint32_t c = 0; uint32_t c = 0;
#if STATUSLED>=0 #if STATUSLED>=0
@ -957,5 +963,5 @@ void WLED::handleStatusLED()
busses.setStatusPixel(0); busses.setStatusPixel(0);
#endif #endif
} }
#endif
} }
#endif

View File

@ -8,7 +8,7 @@
*/ */
// version code in format yymmddb (b = daily build) // version code in format yymmddb (b = daily build)
#define VERSION 2310180 #define VERSION 2310300
//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
@ -864,8 +864,12 @@ public:
void initAP(bool resetAP = false); void initAP(bool resetAP = false);
void initConnection(); void initConnection();
void initInterfaces(); void initInterfaces();
#if defined(STATUSLED)
void handleStatusLED(); void handleStatusLED();
#endif
#if WLED_WATCHDOG_TIMEOUT > 0
void enableWatchdog(); void enableWatchdog();
void disableWatchdog(); void disableWatchdog();
#endif
}; };
#endif // WLED_H #endif // WLED_H

View File

@ -308,10 +308,13 @@ void initServer()
if (!correctPIN || otaLock) return; if (!correctPIN || otaLock) return;
if(!index){ if(!index){
DEBUG_PRINTLN(F("OTA Update Start")); DEBUG_PRINTLN(F("OTA Update Start"));
#if WLED_WATCHDOG_TIMEOUT > 0
WLED::instance().disableWatchdog(); WLED::instance().disableWatchdog();
#endif
usermods.onUpdateBegin(true); // notify usermods that update is about to begin (some may require task de-init) usermods.onUpdateBegin(true); // notify usermods that update is about to begin (some may require task de-init)
lastEditTime = millis(); // make sure PIN does not lock during update lastEditTime = millis(); // make sure PIN does not lock during update
#ifdef ESP8266 #ifdef ESP8266
strip.purgeSegments(true); // free as much memory as you can
Update.runAsync(true); Update.runAsync(true);
#endif #endif
Update.begin((ESP.getFreeSketchSpace() - 0x1000) & 0xFFFFF000); Update.begin((ESP.getFreeSketchSpace() - 0x1000) & 0xFFFFF000);
@ -323,7 +326,9 @@ void initServer()
} else { } else {
DEBUG_PRINTLN(F("Update Failed")); DEBUG_PRINTLN(F("Update Failed"));
usermods.onUpdateBegin(false); // notify usermods that update has failed (some may require task init) usermods.onUpdateBegin(false); // notify usermods that update has failed (some may require task init)
#if WLED_WATCHDOG_TIMEOUT > 0
WLED::instance().enableWatchdog(); WLED::instance().enableWatchdog();
#endif
} }
} }
}); });

View File

@ -164,39 +164,39 @@ bool sendLiveLedsWs(uint32_t wsClient)
const size_t MAX_LIVE_LEDS_WS = 1024U; const size_t MAX_LIVE_LEDS_WS = 1024U;
#endif #endif
size_t n = ((used -1)/MAX_LIVE_LEDS_WS) +1; //only serve every n'th LED if count over MAX_LIVE_LEDS_WS size_t n = ((used -1)/MAX_LIVE_LEDS_WS) +1; //only serve every n'th LED if count over MAX_LIVE_LEDS_WS
size_t pos = (strip.isMatrix ? 4 : 2); // start of data size_t pos = 2; // start of data
#ifndef WLED_DISABLE_2D
if (strip.isMatrix) {
// ignore anything behid matrix (i.e. extra strip)
used = Segment::maxWidth*Segment::maxHeight; // always the size of matrix (more or less than strip.getLengthTotal())
n = 1;
if (used > MAX_LIVE_LEDS_WS) n = 2;
if (used > MAX_LIVE_LEDS_WS*4) n = 4;
pos = 4;
}
#endif
size_t bufSize = pos + (used/n)*3; size_t bufSize = pos + (used/n)*3;
AsyncWebSocketMessageBuffer * wsBuf = ws.makeBuffer(bufSize); AsyncWebSocketMessageBuffer * wsBuf = ws.makeBuffer(bufSize);
if (!wsBuf) return false; //out of memory if (!wsBuf) return false; //out of memory
uint8_t* buffer = wsBuf->get(); uint8_t* buffer = wsBuf->get();
if (!buffer) return false; //out of memory
wsBuf->lock(); // protect buffer from being cleaned by another WS instance
buffer[0] = 'L'; buffer[0] = 'L';
buffer[1] = 1; //version buffer[1] = 1; //version
#ifndef WLED_DISABLE_2D #ifndef WLED_DISABLE_2D
size_t skipLines = 0;
if (strip.isMatrix) { if (strip.isMatrix) {
buffer[1] = 2; //version buffer[1] = 2; //version
buffer[2] = Segment::maxWidth; buffer[2] = Segment::maxWidth/n;
buffer[3] = Segment::maxHeight; buffer[3] = Segment::maxHeight/n;
if (used > MAX_LIVE_LEDS_WS*4) {
buffer[2] = Segment::maxWidth/4;
buffer[3] = Segment::maxHeight/4;
skipLines = 3;
} else if (used > MAX_LIVE_LEDS_WS) {
buffer[2] = Segment::maxWidth/2;
buffer[3] = Segment::maxHeight/2;
skipLines = 1;
}
} }
#endif #endif
for (size_t i = 0; pos < bufSize -2; i += n) for (size_t i = 0; pos < bufSize -2; i += n)
{ {
#ifndef WLED_DISABLE_2D #ifndef WLED_DISABLE_2D
if (strip.isMatrix && skipLines) { if (strip.isMatrix && n>1 && (i/Segment::maxWidth)%n) i += Segment::maxWidth * (n-1);
if ((i/Segment::maxWidth)%(skipLines+1)) i += Segment::maxWidth * skipLines;
}
#endif #endif
uint32_t c = strip.getPixelColor(i); uint32_t c = strip.getPixelColor(i);
uint8_t r = R(c); uint8_t r = R(c);
@ -209,6 +209,8 @@ bool sendLiveLedsWs(uint32_t wsClient)
} }
wsc->binary(wsBuf); wsc->binary(wsBuf);
wsBuf->unlock(); // un-protect buffer
ws._cleanBuffers();
return true; return true;
} }