Reducing JSON buffer size requirements.

Increasing maximum number of segments.
This commit is contained in:
Blaz Kristan 2021-03-24 23:55:39 +01:00
parent 0b75a7d0d3
commit 77d8a8e43d
13 changed files with 2914 additions and 2808 deletions

View File

@ -56,14 +56,14 @@
/* each segment uses 52 bytes of SRAM memory, so if you're application fails because of /* each segment uses 52 bytes of SRAM memory, so if you're application fails because of
insufficient memory, decreasing MAX_NUM_SEGMENTS may help */ insufficient memory, decreasing MAX_NUM_SEGMENTS may help */
#ifdef ESP8266 #ifdef ESP8266
#define MAX_NUM_SEGMENTS 13 #define MAX_NUM_SEGMENTS 18
/* How many color transitions can run at once */ /* How many color transitions can run at once */
#define MAX_NUM_TRANSITIONS 8 #define MAX_NUM_TRANSITIONS 8
/* How much data bytes all segments combined may allocate */ /* How much data bytes all segments combined may allocate */
#define MAX_SEGMENT_DATA 3072 #define MAX_SEGMENT_DATA 4096
#else #else
#define MAX_NUM_SEGMENTS 18 #define MAX_NUM_SEGMENTS 24
#define MAX_NUM_TRANSITIONS 18 #define MAX_NUM_TRANSITIONS 24
#define MAX_SEGMENT_DATA 20480 #define MAX_SEGMENT_DATA 20480
#endif #endif

View File

@ -228,7 +228,7 @@
// Size of buffer for API JSON object (increase for more segments) // Size of buffer for API JSON object (increase for more segments)
#ifdef ESP8266 #ifdef ESP8266
#define JSON_BUFFER_SIZE 9120 #define JSON_BUFFER_SIZE 8192
#else #else
#define JSON_BUFFER_SIZE 16384 #define JSON_BUFFER_SIZE 16384
#endif #endif

View File

@ -210,7 +210,7 @@
<img alt="" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAggAAACMCAYAAAAZQlGEAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAKLSURBVHhe7dgxjtwwEADBpf//5zUDwklnpzFAnKoSTigNFTT0AQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGDcOieX+G5nvNLaznil6f1Nv+/tz3c7+3tmen/Tpu/jbe877c85AQD+EQgAQAgEACAEAgAQAgEACIEAAIRAAABCIAAAIRAAgBAIAEAIBAAgBAIAEAIBAAiBAACEQAAAQiAAACEQAIAQCABACAQAINY5+aHvdsYRazvjK9jfM7fvz/0+Y3+/2+336w8CABACAQAIgQAAhEAAAEIgAAAhEACAEAgAQAgEACAEAgAQAgEACIEAAIRAAABCIAAAIRAAgBAIAEAIBAAgBAIAEAIBAAiBAADEOudrfLczXmltZ3yF6fuwv2em9+d+n5ne3zT3cZfp+/AHAQAIgQAAhEAAAEIgAAAhEACAEAgAQAgEACAEAgAQAgEACIEAAIRAAABCIAAAIRAAgBAIAEAIBAAgBAIAEAIBAAiBAACEQAAAYp3zNb7bGUes7Yz8wO334fmeuf35bmd/z9y+v9ufzx8EACAEAgAQAgEACIEAAIRAAABCIAAAIRAAgBAIAEAIBAAgBAIAEAIBAAiBAACEQAAAQiAAACEQAIAQCABACAQAIAQCABACAQCIdc4x3+2MV1rbGfmFpr+/6e/l9ue73fT+pt1+H2/bn+/lGX8QAIAQCABACAQAIAQCABACAQAIgQAAhEAAAEIgAAAhEACAEAgAQAgEACAEAgAQAgEACIEAAIRAAABCIAAAIRAAgBAIAEAIBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgP/u8/kLYCqAxINTyZkAAAAASUVORK5CYII="> <img alt="" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAggAAACMCAYAAAAZQlGEAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAKLSURBVHhe7dgxjtwwEADBpf//5zUDwklnpzFAnKoSTigNFTT0AQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGDcOieX+G5nvNLaznil6f1Nv+/tz3c7+3tmen/Tpu/jbe877c85AQD+EQgAQAgEACAEAgAQAgEACIEAAIRAAABCIAAAIRAAgBAIAEAIBAAgBAIAEAIBAAiBAACEQAAAQiAAACEQAIAQCABACAQAINY5+aHvdsYRazvjK9jfM7fvz/0+Y3+/2+336w8CABACAQAIgQAAhEAAAEIgAAAhEACAEAgAQAgEACAEAgAQAgEACIEAAIRAAABCIAAAIRAAgBAIAEAIBAAgBAIAEAIBAAiBAADEOudrfLczXmltZ3yF6fuwv2em9+d+n5ne3zT3cZfp+/AHAQAIgQAAhEAAAEIgAAAhEACAEAgAQAgEACAEAgAQAgEACIEAAIRAAABCIAAAIRAAgBAIAEAIBAAgBAIAEAIBAAiBAACEQAAAYp3zNb7bGUes7Yz8wO334fmeuf35bmd/z9y+v9ufzx8EACAEAgAQAgEACIEAAIRAAABCIAAAIRAAgBAIAEAIBAAgBAIAEAIBAAiBAACEQAAAQiAAACEQAIAQCABACAQAIAQCABACAQCIdc4x3+2MV1rbGfmFpr+/6e/l9ue73fT+pt1+H2/bn+/lGX8QAIAQCABACAQAIAQCABACAQAIgQAAhEAAAEIgAAAhEACAEAgAQAgEACAEAgAQAgEACIEAAIRAAABCIAAAIRAAgBAIAEAIBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgP/u8/kLYCqAxINTyZkAAAAASUVORK5CYII=">
</div><br> </div><br>
<div id="kv">Loading...</div><br> <div id="kv">Loading...</div><br>
<button class="btn infobtn" onclick="requestJson(null)">Refresh</button> <button class="btn infobtn" onclick="loadInfo()">Refresh</button>
<button class="btn infobtn" onclick="toggleInfo()">Close Info</button><br> <button class="btn infobtn" onclick="toggleInfo()">Close Info</button><br>
<button class="btn infobtn" onclick="toggleNodes()">Instance List</button> <button class="btn infobtn" onclick="toggleNodes()">Instance List</button>
<button class="btn infobtn" id="resetbtn" onclick="cnfReset()">Reboot WLED</button><br> <button class="btn infobtn" id="resetbtn" onclick="cnfReset()">Reboot WLED</button><br>

View File

@ -26,6 +26,7 @@ var cfg = {
theme:{base:"dark", bg:{url:""}, alpha:{bg:0.6,tab:0.8}, color:{bg:""}}, theme:{base:"dark", bg:{url:""}, alpha:{bg:0.6,tab:0.8}, color:{bg:""}},
comp :{colors:{picker: true, rgb: false, quick: true, hex: false}, labels:true, pcmbot:false, pid:true} comp :{colors:{picker: true, rgb: false, quick: true, hex: false}, labels:true, pcmbot:false, pid:true}
}; };
var myWS, noWS = false;
var cpick = new iro.ColorPicker("#picker", { var cpick = new iro.ColorPicker("#picker", {
width: 260, width: 260,
@ -51,7 +52,7 @@ var cpick = new iro.ColorPicker("#picker", {
] ]
}); });
function handleVisibilityChange() {if (!d.hidden && new Date () - lastUpdate > 3000) requestJson(null);} function handleVisibilityChange() {if (!d.hidden && new Date () - lastUpdate > 3000) requestJson({'v':true,'rev':2},false);}
function sCol(na, col) {d.documentElement.style.setProperty(na, col);} function sCol(na, col) {d.documentElement.style.setProperty(na, col);}
function applyCfg() function applyCfg()
@ -191,12 +192,14 @@ function onLoad()
loadPalettes(function() { loadPalettes(function() {
loadFX(function() { loadFX(function() {
loadPresets(function() { loadPresets(function() {
requestJson(null, false, true, function() { loadInfo(function() {
requestJson({'v':true,'rev':2}, false, true, function() {
loadPalettesData(); loadPalettesData();
}); });
}); });
}); });
}); });
});
d.addEventListener("visibilitychange", handleVisibilityChange, false); d.addEventListener("visibilitychange", handleVisibilityChange, false);
size(); size();
@ -209,11 +212,13 @@ function onLoad()
sl.addEventListener('touchend', toggleBubble); sl.addEventListener('touchend', toggleBubble);
} }
// Creatte UI update WS handler // Create UI update WS handler
var mySocket = new WebSocket('ws://'+(loc?locip:window.location.hostname)+'/ws'); myWS = new WebSocket('ws://'+(loc?locip:window.location.hostname)+'/ws');
mySocket.onmessage = function(event) { myWS.onopen = function () {
myWS.send("{'v':true,'rev':2}");
}
myWS.onmessage = function(event) {
var json = JSON.parse(event.data); var json = JSON.parse(event.data);
//console.log(json);
if (handleJson(json.state)) updateUI(true); if (handleJson(json.state)) updateUI(true);
} }
} }
@ -493,6 +498,40 @@ function populatePresets(fromls)
populateQL(); populateQL();
} }
function loadInfo(callback=null)
{
var url = (loc?`http://${locip}`:'') + '/json/info';
fetch(url, {
method: 'get'
})
.then(res => {
if (!res.ok) showToast('Could not load Info!', true);
return res.json();
})
.then(json => {
lastinfo = json;
var name = json.name;
d.getElementById('namelabel').innerHTML = name;
if (name === "Dinnerbone") d.documentElement.style.transform = "rotate(180deg)";
if (json.live) name = "(Live) " + name;
if (loc) name = "(L) " + name;
d.title = name;
isRgbw = json.leds.wv;
ledCount = json.leds.count;
syncTglRecv = json.str;
maxSeg = json.leds.maxseg;
pmt = json.fs.pmt;
// d.getElementById('buttonNodes').style.display = (json.ndc > 0 && window.innerWidth > 770) ? "block":"none";
populateInfo(json);
if (callback) callback();
})
.catch(function (error) {
showToast(error, true);
console.log(error);
if (callback) callback();
});
}
function populateInfo(i) function populateInfo(i)
{ {
var cn=""; var cn="";
@ -780,10 +819,16 @@ function genPalPrevCss(id)
b = Math.random() * 255; b = Math.random() * 255;
} else { } else {
if (selColors) { if (selColors) {
let pos = element[1] - 1; let e = element[1] - 1;
r = selColors[pos][0]; if (Array.isArray(selColors[e])) {
g = selColors[pos][1]; r = selColors[e][0];
b = selColors[pos][2]; g = selColors[e][1];
b = selColors[e][2];
} else {
r = (selColors[e]>>16) & 0xFF;
g = (selColors[e]>> 8) & 0xFF;
b = (selColors[e] ) & 0xFF;
}
} }
} }
if (index === false) { if (index === false) {
@ -892,9 +937,12 @@ function updatePA(scrollto=false)
function updateUI(scrollto=false) function updateUI(scrollto=false)
{ {
noWS = (myWS.readyState === WebSocket.CLOSED);
d.getElementById('buttonPower').className = (isOn) ? "active":""; d.getElementById('buttonPower').className = (isOn) ? "active":"";
d.getElementById('buttonNl').className = (nlA) ? "active":""; d.getElementById('buttonNl').className = (nlA) ? "active":"";
d.getElementById('buttonSync').className = (syncSend) ? "active":""; d.getElementById('buttonSync').className = (syncSend) ? "active":"";
d.getElementById('buttonNodes').style.display = (lastinfo.ndc > 0 && window.innerWidth > 770) ? "block":"none";
updateSelectedPalette(scrollto); updateSelectedPalette(scrollto);
updateSelectedFx(scrollto); updateSelectedFx(scrollto);
@ -944,13 +992,14 @@ function updateSelectedFx(scrollto=false)
var selectedEffect = parent.querySelector(`.lstI[data-id="${selectedFx}"]`); var selectedEffect = parent.querySelector(`.lstI[data-id="${selectedFx}"]`);
if (selectedEffect) selectedEffect.classList.add('selected'); if (selectedEffect) selectedEffect.classList.add('selected');
/*
if (scrollto && selectedEffect) { if (scrollto && selectedEffect) {
selectedEffect.scrollIntoView({ selectedEffect.scrollIntoView({
behavior: 'smooth', behavior: 'smooth',
block: 'center', block: 'center',
}); });
} }
*/
} }
function displayRover(i,s) function displayRover(i,s)
@ -998,8 +1047,20 @@ function handleJson(s)
var cd = d.getElementById('csl').children; var cd = d.getElementById('csl').children;
for (let e = cd.length-1; e >= 0; e--) for (let e = cd.length-1; e >= 0; e--)
{ {
cd[e].style.backgroundColor = "rgb(" + i.col[e][0] + "," + i.col[e][1] + "," + i.col[e][2] + ")"; var r,g,b,w;
if (isRgbw) whites[e] = parseInt(i.col[e][3]); if (Array.isArray(i.col[e])) {
r = i.col[e][0];
g = i.col[e][1];
b = i.col[e][2];
if (isRgbw) w = i.col[e][3];
} else {
r = (i.col[e]>>16) & 0xFF;
g = (i.col[e]>> 8) & 0xFF;
b = (i.col[e] ) & 0xFF;
if (isRgbw) w = (i.col[e] >> 24) & 0xFF;
}
cd[e].style.backgroundColor = "rgb(" + r + "," + g + "," + b + ")";
if (isRgbw) whites[e] = parseInt(w);
selectSlot(csel); selectSlot(csel);
} }
d.getElementById('sliderSpeed').value = whites[csel]; d.getElementById('sliderSpeed').value = whites[csel];
@ -1010,6 +1071,8 @@ function handleJson(s)
selectedPal = i.pal; selectedPal = i.pal;
selectedFx = i.fx; selectedFx = i.fx;
displayRover(lastinfo, s);
return true; return true;
} }
@ -1029,6 +1092,7 @@ function requestJson(command, rinfo = true, verbose = true, callback = null)
if (command) if (command)
{ {
command.v = verbose; command.v = verbose;
command.rev = 2;
command.time = Math.floor(Date.now() / 1000); command.time = Math.floor(Date.now() / 1000);
req = JSON.stringify(command); req = JSON.stringify(command);
} }
@ -1061,8 +1125,8 @@ function requestJson(command, rinfo = true, verbose = true, callback = null)
populatePresets(true); populatePresets(true);
pmtLast = pmt; pmtLast = pmt;
} }
var info = json.info; var info = json.info;
/*
var name = info.name; var name = info.name;
d.getElementById('namelabel').innerHTML = name; d.getElementById('namelabel').innerHTML = name;
if (name === "Dinnerbone") d.documentElement.style.transform = "rotate(180deg)"; if (name === "Dinnerbone") d.documentElement.style.transform = "rotate(180deg)";
@ -1074,10 +1138,10 @@ function requestJson(command, rinfo = true, verbose = true, callback = null)
ledCount = info.leds.count; ledCount = info.leds.count;
syncTglRecv = info.str; syncTglRecv = info.str;
maxSeg = info.leds.maxseg; maxSeg = info.leds.maxseg;
*/
pmt = info.fs.pmt; pmt = info.fs.pmt;
if (!command && pmt != pmtLast) setTimeout(loadPresets,99); if (!command && pmt != pmtLast) setTimeout(loadPresets,99);
pmtLast = pmt; pmtLast = pmt;
d.getElementById('buttonNodes').style.display = (info.ndc > 0 && window.innerWidth > 770) ? "block":"none";
lastinfo = info; lastinfo = info;
if (isInfo) populateInfo(info); if (isInfo) populateInfo(info);
displayRover(info, s); displayRover(info, s);
@ -1117,7 +1181,7 @@ function togglePower()
isOn = !isOn; isOn = !isOn;
var obj = {"on": isOn}; var obj = {"on": isOn};
obj.transition = parseInt(d.getElementById('cyctt').value*10); obj.transition = parseInt(d.getElementById('cyctt').value*10);
requestJson(obj, false); requestJson(obj, false, noWS);
} }
function toggleNl() function toggleNl()
@ -1126,7 +1190,7 @@ function toggleNl()
if (nlA) showToast(`Timer active. Your light will turn ${nlTar > 0 ? "on":"off"} ${nlFade ? "over":"after"} ${nlDur} minutes.`); if (nlA) showToast(`Timer active. Your light will turn ${nlTar > 0 ? "on":"off"} ${nlFade ? "over":"after"} ${nlDur} minutes.`);
else showToast('Timer deactivated.'); else showToast('Timer deactivated.');
var obj = {"nl": {"on": nlA}}; var obj = {"nl": {"on": nlA}};
requestJson(obj, false); requestJson(obj, false, noWS);
} }
function toggleSync() function toggleSync()
@ -1136,7 +1200,7 @@ function toggleSync()
else showToast('This light and other lights in the network will no longer sync.'); else showToast('This light and other lights in the network will no longer sync.');
var obj = {"udpn": {"send": syncSend}}; var obj = {"udpn": {"send": syncSend}};
if (syncTglRecv) obj.udpn.recv = syncSend; if (syncTglRecv) obj.udpn.recv = syncSend;
requestJson(obj, false); requestJson(obj, false, noWS);
} }
function toggleLiveview() function toggleLiveview()
@ -1153,7 +1217,7 @@ function toggleInfo()
{ {
if (isNodes) toggleNodes(); if (isNodes) toggleNodes();
isInfo = !isInfo; isInfo = !isInfo;
if (isInfo) populateInfo(lastinfo); if (isInfo) loadInfo();
d.getElementById('info').style.transform = (isInfo) ? "translateY(0px)":"translateY(100%)"; d.getElementById('info').style.transform = (isInfo) ? "translateY(0px)":"translateY(100%)";
d.getElementById('buttonI').className = (isInfo) ? "active":""; d.getElementById('buttonI').className = (isInfo) ? "active":"";
} }
@ -1266,14 +1330,14 @@ function selSegEx(s)
{ {
var obj = {"seg":[]}; var obj = {"seg":[]};
for (let i=0; i<=lSeg; i++) obj.seg.push({"sel":(i==s)?true:false}); for (let i=0; i<=lSeg; i++) obj.seg.push({"sel":(i==s)?true:false});
requestJson(obj, false); requestJson(obj, false, noWS);
} }
function selSeg(s) function selSeg(s)
{ {
var sel = d.getElementById(`seg${s}sel`).checked; var sel = d.getElementById(`seg${s}sel`).checked;
var obj = {"seg": {"id": s, "sel": sel}}; var obj = {"seg": {"id": s, "sel": sel}};
requestJson(obj, false); requestJson(obj, false, noWS);
} }
function setSeg(s) function setSeg(s)
@ -1289,7 +1353,7 @@ function setSeg(s)
obj.seg.grp = grp; obj.seg.grp = grp;
obj.seg.spc = spc; obj.seg.spc = spc;
} }
requestJson(obj, false); requestJson(obj, false, noWS);
} }
function delSeg(s) function delSeg(s)
@ -1301,33 +1365,33 @@ function delSeg(s)
expanded[s] = false; expanded[s] = false;
segCount--; segCount--;
var obj = {"seg": {"id": s, "stop": 0}}; var obj = {"seg": {"id": s, "stop": 0}};
requestJson(obj, false); requestJson(obj, false, noWS);
} }
function setRev(s) function setRev(s)
{ {
var rev = d.getElementById(`seg${s}rev`).checked; var rev = d.getElementById(`seg${s}rev`).checked;
var obj = {"seg": {"id": s, "rev": rev}}; var obj = {"seg": {"id": s, "rev": rev}};
requestJson(obj, false); requestJson(obj, false, noWS);
} }
function setMi(s) function setMi(s)
{ {
var mi = d.getElementById(`seg${s}mi`).checked; var mi = d.getElementById(`seg${s}mi`).checked;
var obj = {"seg": {"id": s, "mi": mi}}; var obj = {"seg": {"id": s, "mi": mi}};
requestJson(obj, false); requestJson(obj, false, noWS);
} }
function setSegPwr(s) function setSegPwr(s)
{ {
var obj = {"seg": {"id": s, "on": !powered[s]}}; var obj = {"seg": {"id": s, "on": !powered[s]}};
requestJson(obj, false); requestJson(obj, false, noWS);
} }
function setSegBri(s) function setSegBri(s)
{ {
var obj = {"seg": {"id": s, "bri": parseInt(d.getElementById(`seg${s}bri`).value)}}; var obj = {"seg": {"id": s, "bri": parseInt(d.getElementById(`seg${s}bri`).value)}};
requestJson(obj, false); requestJson(obj, false, noWS);
} }
function setX(ind = null) function setX(ind = null)
@ -1343,7 +1407,7 @@ function setX(ind = null)
d.querySelector(`#fxlist .lstI[data-id="${ind}"]`).classList.add('selected'); d.querySelector(`#fxlist .lstI[data-id="${ind}"]`).classList.add('selected');
var obj = {"seg": {"fx": parseInt(ind)}}; var obj = {"seg": {"fx": parseInt(ind)}};
requestJson(obj); requestJson(obj, false, noWS);
} }
function setPalette(paletteId = null) function setPalette(paletteId = null)
@ -1358,32 +1422,32 @@ function setPalette(paletteId = null)
d.querySelector(`#selectPalette .lstI[data-id="${paletteId}"]`).classList.add('selected'); d.querySelector(`#selectPalette .lstI[data-id="${paletteId}"]`).classList.add('selected');
var obj = {"seg": {"pal": paletteId}}; var obj = {"seg": {"pal": paletteId}};
requestJson(obj, false); requestJson(obj, false, noWS);
} }
function setBri() function setBri()
{ {
var obj = {"bri": parseInt(d.getElementById('sliderBri').value)}; var obj = {"bri": parseInt(d.getElementById('sliderBri').value)};
obj.transition = parseInt(d.getElementById('cyctt').value*10); obj.transition = parseInt(d.getElementById('cyctt').value*10);
requestJson(obj, false); requestJson(obj, false, noWS);
} }
function setSpeed() function setSpeed()
{ {
var obj = {"seg": {"sx": parseInt(d.getElementById('sliderSpeed').value)}}; var obj = {"seg": {"sx": parseInt(d.getElementById('sliderSpeed').value)}};
requestJson(obj, false); requestJson(obj, false, noWS);
} }
function setIntensity() function setIntensity()
{ {
var obj = {"seg": {"ix": parseInt(d.getElementById('sliderIntensity').value)}}; var obj = {"seg": {"ix": parseInt(d.getElementById('sliderIntensity').value)}};
requestJson(obj, false); requestJson(obj, false, noWS);
} }
function setLor(i) function setLor(i)
{ {
var obj = {"lor": i}; var obj = {"lor": i};
requestJson(obj, false); requestJson(obj, false, noWS);
} }
function toggleCY() function toggleCY()
@ -1395,14 +1459,14 @@ function toggleCY()
obj.transition = parseInt(d.getElementById('cyctt').value*10); obj.transition = parseInt(d.getElementById('cyctt').value*10);
} }
requestJson(obj, false); requestJson(obj, false, noWS);
} }
function setPreset(i) function setPreset(i)
{ {
var obj = {"ps": i}; var obj = {"ps": i};
showToast("Loading preset " + pName(i) +" (" + i + ")"); showToast("Loading preset " + pName(i) +" (" + i + ")");
requestJson(obj, false); requestJson(obj, false, noWS);
} }
function saveP(i) function saveP(i)
@ -1439,7 +1503,7 @@ function saveP(i)
if (pQN.length > 0) obj.ql = pQN; if (pQN.length > 0) obj.ql = pQN;
showToast("Saving " + pN +" (" + pI + ")"); showToast("Saving " + pN +" (" + pI + ")");
requestJson(obj, false); requestJson(obj, false, noWS);
if (obj.o) { if (obj.o) {
pJson[pI] = obj; pJson[pI] = obj;
delete pJson[pI].psave; delete pJson[pI].psave;
@ -1458,7 +1522,7 @@ function saveP(i)
function delP(i) function delP(i)
{ {
var obj = {"pdel": i}; var obj = {"pdel": i};
requestJson(obj, false); requestJson(obj, false, noWS);
delete pJson[i]; delete pJson[i];
populatePresets(); populatePresets();
} }
@ -1563,7 +1627,7 @@ function setColor(sr)
updateHex(); updateHex();
updateRgb(); updateRgb();
obj.transition = parseInt(d.getElementById('cyctt').value*10); obj.transition = parseInt(d.getElementById('cyctt').value*10);
requestJson(obj, false); requestJson(obj, false, noWS);
} }
var hc = 0; var hc = 0;
@ -1598,7 +1662,7 @@ function rSegs()
bt.innerHTML = "Reset segments"; bt.innerHTML = "Reset segments";
var obj = {"seg":[{"start":0,"stop":ledCount,"sel":true}]}; var obj = {"seg":[{"start":0,"stop":ledCount,"sel":true}]};
for (let i=1; i<=lSeg; i++) obj.seg.push({"stop":0}); for (let i=1; i<=lSeg; i++) obj.seg.push({"stop":0});
requestJson(obj, false); requestJson(obj, false, noWS);
} }
function loadPalettesData() function loadPalettesData()

View File

@ -26,7 +26,7 @@
socket.onopen = function () { socket.onopen = function () {
console.info("Live-Preview websocket is opened"); console.info("Live-Preview websocket is opened");
socket.send("{'lv':true}"); socket.send("{'lv':true,'rev':2}");
} }
socket.onclose = function () { console.info("Live-Preview websocket is closing"); } socket.onclose = function () { console.info("Live-Preview websocket is closing"); }

View File

@ -97,11 +97,11 @@ void handleIR();
#include "FX.h" #include "FX.h"
void deserializeSegment(JsonObject elem, byte it); void deserializeSegment(JsonObject elem, byte it);
bool deserializeState(JsonObject root); uint8_t deserializeState(JsonObject root);
void serializeSegment(JsonObject& root, WS2812FX::Segment& seg, byte id, bool forPreset = false, bool segmentBounds = true); void serializeSegment(JsonObject& root, WS2812FX::Segment& seg, byte id, bool forPreset = false, bool segmentBounds = true, uint8_t versionAPI = 1);
void serializeState(JsonObject root, bool forPreset = false, bool includeBri = true, bool segmentBounds = true); void serializeState(JsonObject root, bool forPreset = false, bool includeBri = true, bool segmentBounds = true);
void serializeInfo(JsonObject root); void serializeInfo(JsonObject root);
void serveJson(AsyncWebServerRequest* request); void serveJson(AsyncWebServerRequest* request, uint8_t versionAPI = 1);
bool serveLiveLeds(AsyncWebServerRequest* request, uint32_t wsClient = 0); bool serveLiveLeds(AsyncWebServerRequest* request, uint32_t wsClient = 0);
//led.cpp //led.cpp

View File

@ -85,7 +85,7 @@ charset="utf-8"><meta name="theme-color" content="#222222"><title>
WLED Live Preview</title><style> WLED Live Preview</title><style>
body{margin:0}#canv{background:#000;filter:brightness(175%);width:100%;height:100%;position:absolute} body{margin:0}#canv{background:#000;filter:brightness(175%);width:100%;height:100%;position:absolute}
</style></head><body><div id="canv"><script> </style></head><body><div id="canv"><script>
console.info("Live-Preview websocket opening");var socket=new WebSocket("ws://"+document.location.host+"/ws");function updatePreview(e){var o="linear-gradient(90deg,",n=e.length;for(i=0;i<n;i++){var t=e[i];t.length>6&&(t=t.substring(2)),o+="#"+t,i<n-1&&(o+=",")}o+=")",document.getElementById("canv").style.background=o}socket.onopen=function(){console.info("Live-Preview websocket is opened"),socket.send("{'lv':true}")},socket.onclose=function(){console.info("Live-Preview websocket is closing")},socket.onerror=function(e){console.error("Live-Preview websocket error:",e)},socket.onmessage=function(e){try{var o=JSON.parse(e.data);o&&o.leds&&requestAnimationFrame((function(){updatePreview(o.leds)}))}catch(e){console.error("Live-Preview websocket error:",e)}} console.info("Live-Preview websocket opening");var socket=new WebSocket("ws://"+document.location.host+"/ws");function updatePreview(e){var o="linear-gradient(90deg,",n=e.length;for(i=0;i<n;i++){var r=e[i];r.length>6&&(r=r.substring(2)),o+="#"+r,i<n-1&&(o+=",")}o+=")",document.getElementById("canv").style.background=o}socket.onopen=function(){console.info("Live-Preview websocket is opened"),socket.send("{'lv':true,'rev':2}")},socket.onclose=function(){console.info("Live-Preview websocket is closing")},socket.onerror=function(e){console.error("Live-Preview websocket error:",e)},socket.onmessage=function(e){try{var o=JSON.parse(e.data);o&&o.leds&&requestAnimationFrame((function(){updatePreview(o.leds)}))}catch(e){console.error("Live-Preview websocket error:",e)}}
</script></body></html>)====="; </script></body></html>)=====";

File diff suppressed because it is too large Load Diff

View File

@ -40,6 +40,15 @@ void deserializeSegment(JsonObject elem, byte it)
{ {
int rgbw[] = {0,0,0,0}; int rgbw[] = {0,0,0,0};
bool colValid = false; bool colValid = false;
if (colarr[i].is<unsigned long>()) {
// unsigned long RGBW
uint32_t colX = colarr[i];
rgbw[0] = (colX >> 16) & 0xFF;
rgbw[1] = (colX >> 8) & 0xFF;
rgbw[2] = (colX ) & 0xFF;
rgbw[3] = (colX >> 24) & 0xFF;
colValid = true;
} else {
JsonArray colX = colarr[i]; JsonArray colX = colarr[i];
if (colX.isNull()) { if (colX.isNull()) {
byte brgbw[] = {0,0,0,0}; byte brgbw[] = {0,0,0,0};
@ -57,12 +66,12 @@ void deserializeSegment(JsonObject elem, byte it)
} else { //Array of ints (RGB or RGBW color), e.g. [255,160,0] } else { //Array of ints (RGB or RGBW color), e.g. [255,160,0]
byte sz = colX.size(); byte sz = colX.size();
if (sz == 0) continue; //do nothing on empty array if (sz == 0) continue; //do nothing on empty array
byte cp = copyArray(colX, rgbw, 4); byte cp = copyArray(colX, rgbw, 4);
if (cp == 1 && rgbw[0] == 0) if (cp == 1 && rgbw[0] == 0)
seg.setColor(i, 0, id); seg.setColor(i, 0, id);
colValid = true; colValid = true;
} }
}
if (!colValid) continue; if (!colValid) continue;
if (id == strip.getMainSegmentId() && i < 2) //temporary, to make transition work on main segment if (id == strip.getMainSegmentId() && i < 2) //temporary, to make transition work on main segment
@ -156,10 +165,11 @@ void deserializeSegment(JsonObject elem, byte it)
} }
} }
bool deserializeState(JsonObject root) uint8_t deserializeState(JsonObject root)
{ {
strip.applyToAllSelected = false; strip.applyToAllSelected = false;
bool stateResponse = root[F("v")] | false; bool stateResponse = root[F("v")] | false;
uint8_t versionAPI = root["rev"] | 1;
bri = root["bri"] | bri; bri = root["bri"] | bri;
@ -269,7 +279,7 @@ bool deserializeState(JsonObject root)
deletePreset(ps); deletePreset(ps);
} }
ps = root["ps"] | -1; //load preset (clears state request!) ps = root["ps"] | -1; //load preset (clears state request!)
if (ps >= 0) {applyPreset(ps); return stateResponse;} if (ps >= 0) {applyPreset(ps); return stateResponse ? versionAPI : 0;}
//HTTP API commands //HTTP API commands
const char* httpwin = root["win"]; const char* httpwin = root["win"];
@ -288,10 +298,10 @@ bool deserializeState(JsonObject root)
colorUpdated(noNotification ? NOTIFIER_CALL_MODE_NO_NOTIFY : NOTIFIER_CALL_MODE_DIRECT_CHANGE); colorUpdated(noNotification ? NOTIFIER_CALL_MODE_NO_NOTIFY : NOTIFIER_CALL_MODE_DIRECT_CHANGE);
return stateResponse; return stateResponse ? versionAPI : 0;
} }
void serializeSegment(JsonObject& root, WS2812FX::Segment& seg, byte id, bool forPreset, bool segmentBounds) void serializeSegment(JsonObject& root, WS2812FX::Segment& seg, byte id, bool forPreset, bool segmentBounds, uint8_t versionAPI)
{ {
root["id"] = id; root["id"] = id;
if (segmentBounds) { if (segmentBounds) {
@ -307,6 +317,16 @@ void serializeSegment(JsonObject& root, WS2812FX::Segment& seg, byte id, bool fo
JsonArray colarr = root.createNestedArray("col"); JsonArray colarr = root.createNestedArray("col");
if (versionAPI>1) {
for (uint8_t i = 0; i < 3; i++)
{
if (id==strip.getMainSegmentId() && i < 2) //temporary, to make transition work on main segment
if (i==0) colarr.add((unsigned long)((col[0]<<16) | (col[1]<<8) | col[2] | (useRGBW?col[3]<<24:0)));
else colarr.add((unsigned long)((colSec[0]<<16) | (colSec[1]<<8) | colSec[2] | (useRGBW?colSec[3]<<24:0)));
else
colarr.add((unsigned long)seg.colors[i]);
}
} else {
for (uint8_t i = 0; i < 3; i++) for (uint8_t i = 0; i < 3; i++)
{ {
JsonArray colX = colarr.createNestedArray(); JsonArray colX = colarr.createNestedArray();
@ -325,6 +345,7 @@ void serializeSegment(JsonObject& root, WS2812FX::Segment& seg, byte id, bool fo
colX.add((seg.colors[i] >> 24) & 0xFF); colX.add((seg.colors[i] >> 24) & 0xFF);
} }
} }
}
root[F("fx")] = seg.mode; root[F("fx")] = seg.mode;
root[F("sx")] = seg.speed; root[F("sx")] = seg.speed;
@ -377,14 +398,17 @@ void serializeState(JsonObject root, bool forPreset, bool includeBri, bool segme
root[F("mainseg")] = strip.getMainSegmentId(); root[F("mainseg")] = strip.getMainSegmentId();
uint8_t versionAPI = root["rev"] | 1;
JsonArray seg = root.createNestedArray("seg"); JsonArray seg = root.createNestedArray("seg");
for (byte s = 0; s < strip.getMaxSegments(); s++) for (byte s = 0; s < strip.getMaxSegments(); s++)
{ {
WS2812FX::Segment sg = strip.getSegment(s); WS2812FX::Segment sg = strip.getSegment(s);
// TODO: add logic to stop at 12 segments if using versionAPI==1 on ESP8266
if (sg.isActive()) if (sg.isActive())
{ {
JsonObject seg0 = seg.createNestedObject(); JsonObject seg0 = seg.createNestedObject();
serializeSegment(seg0, sg, s, forPreset, segmentBounds); serializeSegment(seg0, sg, s, forPreset, segmentBounds, versionAPI);
} else if (forPreset && segmentBounds) { //disable segments not part of preset } else if (forPreset && segmentBounds) { //disable segments not part of preset
JsonObject seg0 = seg.createNestedObject(); JsonObject seg0 = seg.createNestedObject();
seg0["stop"] = 0; seg0["stop"] = 0;
@ -711,7 +735,7 @@ void serializeNodes(JsonObject root)
} }
} }
void serveJson(AsyncWebServerRequest* request) void serveJson(AsyncWebServerRequest* request, uint8_t versionAPI)
{ {
byte subJson = 0; byte subJson = 0;
const String& url = request->url(); const String& url = request->url();
@ -743,6 +767,7 @@ void serveJson(AsyncWebServerRequest* request)
switch (subJson) switch (subJson)
{ {
case 1: //state case 1: //state
if (versionAPI>1) doc["rev"] = (int)versionAPI;
serializeState(doc); break; serializeState(doc); break;
case 2: //info case 2: //info
serializeInfo(doc); break; serializeInfo(doc); break;
@ -752,6 +777,7 @@ void serveJson(AsyncWebServerRequest* request)
serializePalettes(doc, request); break; serializePalettes(doc, request); break;
default: //all default: //all
JsonObject state = doc.createNestedObject("state"); JsonObject state = doc.createNestedObject("state");
if (versionAPI>1) state["rev"] = (int)versionAPI;
serializeState(state); serializeState(state);
JsonObject info = doc.createNestedObject("info"); JsonObject info = doc.createNestedObject("info");
serializeInfo(info); serializeInfo(info);

View File

@ -7,7 +7,7 @@
bool applyPreset(byte index) bool applyPreset(byte index)
{ {
if (index == 0) return false; if (index == 0) return false;
if (fileDoc) { if (fileDoc) { // from POST "/json" handler (wled_server.cpp)
errorFlag = readObjectFromFileUsingId("/presets.json", index, fileDoc) ? ERR_NONE : ERR_FS_PLOAD; errorFlag = readObjectFromFileUsingId("/presets.json", index, fileDoc) ? ERR_NONE : ERR_FS_PLOAD;
JsonObject fdo = fileDoc->as<JsonObject>(); JsonObject fdo = fileDoc->as<JsonObject>();
if (fdo["ps"] == index) fdo.remove("ps"); //remove load request for same presets to prevent recursive crash if (fdo["ps"] == index) fdo.remove("ps"); //remove load request for same presets to prevent recursive crash

View File

@ -8,7 +8,7 @@
*/ */
// version code in format yymmddb (b = daily build) // version code in format yymmddb (b = daily build)
#define VERSION 2103231 #define VERSION 2103250
//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

View File

@ -83,7 +83,7 @@ void initServer()
}); });
AsyncCallbackJsonWebHandler* handler = new AsyncCallbackJsonWebHandler("/json", [](AsyncWebServerRequest *request) { AsyncCallbackJsonWebHandler* handler = new AsyncCallbackJsonWebHandler("/json", [](AsyncWebServerRequest *request) {
bool verboseResponse = false; uint8_t vAPI = 0;
{ //scope JsonDocument so it releases its buffer { //scope JsonDocument so it releases its buffer
DynamicJsonDocument jsonBuffer(JSON_BUFFER_SIZE); DynamicJsonDocument jsonBuffer(JSON_BUFFER_SIZE);
DeserializationError error = deserializeJson(jsonBuffer, (uint8_t*)(request->_tempObject)); DeserializationError error = deserializeJson(jsonBuffer, (uint8_t*)(request->_tempObject));
@ -91,12 +91,12 @@ void initServer()
if (error || root.isNull()) { if (error || root.isNull()) {
request->send(400, "application/json", F("{\"error\":9}")); return; request->send(400, "application/json", F("{\"error\":9}")); return;
} }
fileDoc = &jsonBuffer; fileDoc = &jsonBuffer; // used for applying presets (presets.cpp)
verboseResponse = deserializeState(root); vAPI = deserializeState(root);
fileDoc = nullptr; fileDoc = nullptr;
} }
if (verboseResponse) { //if JSON contains "v" if (vAPI>0) { //if JSON contains "v"
serveJson(request); return; serveJson(request,vAPI); return;
} }
request->send(200, "application/json", F("{\"success\":true}")); request->send(200, "application/json", F("{\"success\":true}"));
}); });

View File

@ -8,6 +8,7 @@
uint16_t wsLiveClientId = 0; uint16_t wsLiveClientId = 0;
unsigned long wsLastLiveTime = 0; unsigned long wsLastLiveTime = 0;
//uint8_t* wsFrameBuffer = nullptr; //uint8_t* wsFrameBuffer = nullptr;
uint8_t vAPI = 2;
#define WS_LIVE_INTERVAL 40 #define WS_LIVE_INTERVAL 40
@ -27,7 +28,7 @@ void wsEvent(AsyncWebSocket * server, AsyncWebSocketClient * client, AwsEventTyp
//the whole message is in a single frame and we got all of it's data (max. 1450byte) //the whole message is in a single frame and we got all of it's data (max. 1450byte)
if(info->opcode == WS_TEXT) if(info->opcode == WS_TEXT)
{ {
bool verboseResponse = false; uint8_t verboseResponse = 0;
{ //scope JsonDocument so it releases its buffer { //scope JsonDocument so it releases its buffer
DynamicJsonDocument jsonBuffer(JSON_BUFFER_SIZE); DynamicJsonDocument jsonBuffer(JSON_BUFFER_SIZE);
DeserializationError error = deserializeJson(jsonBuffer, data, len); DeserializationError error = deserializeJson(jsonBuffer, data, len);
@ -40,6 +41,7 @@ void wsEvent(AsyncWebSocket * server, AsyncWebSocketClient * client, AwsEventTyp
} }
verboseResponse = deserializeState(root); verboseResponse = deserializeState(root);
if (verboseResponse) vAPI = verboseResponse;
} }
if (verboseResponse || millis() - lastInterfaceUpdate < 1900) sendDataWs(client); //update if it takes longer than 100ms until next "broadcast" if (verboseResponse || millis() - lastInterfaceUpdate < 1900) sendDataWs(client); //update if it takes longer than 100ms until next "broadcast"
} }
@ -79,6 +81,7 @@ void sendDataWs(AsyncWebSocketClient * client)
{ //scope JsonDocument so it releases its buffer { //scope JsonDocument so it releases its buffer
DynamicJsonDocument doc(JSON_BUFFER_SIZE); DynamicJsonDocument doc(JSON_BUFFER_SIZE);
JsonObject state = doc.createNestedObject("state"); JsonObject state = doc.createNestedObject("state");
if (vAPI>1) state["rev"] = 2;
serializeState(state); serializeState(state);
JsonObject info = doc.createNestedObject("info"); JsonObject info = doc.createNestedObject("info");
serializeInfo(info); serializeInfo(info);