Allow overriding of color order by LED pixel range. (#2463)
* Overridable color order - Use `ColorOrderMap` to hold optional color order overrides for ranges of LEDs. - Serialization of config to/from filesystem is complete. - Back-end configuration is complete. - TODO: front-end changes to the LED settings page. * Add Color order override settings - Adds color order override section to settings page. * PR Feedback - Limit max number of color order overrides to 5 on ESP8266 - Only append color overrides if they were provided in the POST of LED settings.
This commit is contained in:
parent
e879fe5843
commit
fb19f1ecbc
@ -73,6 +73,62 @@ struct BusConfig {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Defines an LED Strip and its color ordering.
|
||||||
|
struct ColorOrderMapEntry {
|
||||||
|
uint16_t start;
|
||||||
|
uint16_t len;
|
||||||
|
uint8_t colorOrder;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ColorOrderMap {
|
||||||
|
void add(uint16_t start, uint16_t len, uint8_t colorOrder) {
|
||||||
|
if (_count >= WLED_MAX_COLOR_ORDER_MAPPINGS) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (len == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (colorOrder > COL_ORDER_MAX) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_mappings[_count].start = start;
|
||||||
|
_mappings[_count].len = len;
|
||||||
|
_mappings[_count].colorOrder = colorOrder;
|
||||||
|
_count++;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t count() const {
|
||||||
|
return _count;
|
||||||
|
}
|
||||||
|
|
||||||
|
void reset() {
|
||||||
|
_count = 0;
|
||||||
|
memset(_mappings, 0, sizeof(_mappings));
|
||||||
|
}
|
||||||
|
|
||||||
|
const ColorOrderMapEntry* get(uint8_t n) const {
|
||||||
|
if (n > _count) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
return &(_mappings[n]);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline uint8_t getPixelColorOrder(uint16_t pix, uint8_t defaultColorOrder) const {
|
||||||
|
if (_count == 0) return defaultColorOrder;
|
||||||
|
|
||||||
|
for (uint8_t i = 0; i < _count; i++) {
|
||||||
|
if (pix >= _mappings[i].start && pix < (_mappings[i].start + _mappings[i].len)) {
|
||||||
|
return _mappings[i].colorOrder;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return defaultColorOrder;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
uint8_t _count;
|
||||||
|
ColorOrderMapEntry _mappings[WLED_MAX_COLOR_ORDER_MAPPINGS];
|
||||||
|
};
|
||||||
|
|
||||||
//parent class of BusDigital, BusPwm, and BusNetwork
|
//parent class of BusDigital, BusPwm, and BusNetwork
|
||||||
class Bus {
|
class Bus {
|
||||||
public:
|
public:
|
||||||
@ -152,7 +208,7 @@ class Bus {
|
|||||||
|
|
||||||
class BusDigital : public Bus {
|
class BusDigital : public Bus {
|
||||||
public:
|
public:
|
||||||
BusDigital(BusConfig &bc, uint8_t nr) : Bus(bc.type, bc.start) {
|
BusDigital(BusConfig &bc, uint8_t nr, const ColorOrderMap &com) : Bus(bc.type, bc.start), _colorOrderMap(com) {
|
||||||
if (!IS_DIGITAL(bc.type) || !bc.count) return;
|
if (!IS_DIGITAL(bc.type) || !bc.count) return;
|
||||||
if (!pinManager.allocatePin(bc.pins[0], true, PinOwner::BusDigital)) return;
|
if (!pinManager.allocatePin(bc.pins[0], true, PinOwner::BusDigital)) return;
|
||||||
_pins[0] = bc.pins[0];
|
_pins[0] = bc.pins[0];
|
||||||
@ -197,7 +253,7 @@ class BusDigital : public Bus {
|
|||||||
//TODO only show if no new show due in the next 50ms
|
//TODO only show if no new show due in the next 50ms
|
||||||
void setStatusPixel(uint32_t c) {
|
void setStatusPixel(uint32_t c) {
|
||||||
if (_skip && canShow()) {
|
if (_skip && canShow()) {
|
||||||
PolyBus::setPixelColor(_busPtr, _iType, 0, c, _colorOrder);
|
PolyBus::setPixelColor(_busPtr, _iType, 0, c, _colorOrderMap.getPixelColorOrder(_start, _colorOrder));
|
||||||
PolyBus::show(_busPtr, _iType);
|
PolyBus::show(_busPtr, _iType);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -207,13 +263,13 @@ class BusDigital : public Bus {
|
|||||||
if (_cct >= 1900) c = colorBalanceFromKelvin(_cct, c); //color correction from CCT
|
if (_cct >= 1900) c = colorBalanceFromKelvin(_cct, c); //color correction from CCT
|
||||||
if (reversed) pix = _len - pix -1;
|
if (reversed) pix = _len - pix -1;
|
||||||
else pix += _skip;
|
else pix += _skip;
|
||||||
PolyBus::setPixelColor(_busPtr, _iType, pix, c, _colorOrder);
|
PolyBus::setPixelColor(_busPtr, _iType, pix, c, _colorOrderMap.getPixelColorOrder(pix+_start, _colorOrder));
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t getPixelColor(uint16_t pix) {
|
uint32_t getPixelColor(uint16_t pix) {
|
||||||
if (reversed) pix = _len - pix -1;
|
if (reversed) pix = _len - pix -1;
|
||||||
else pix += _skip;
|
else pix += _skip;
|
||||||
return PolyBus::getPixelColor(_busPtr, _iType, pix, _colorOrder);
|
return PolyBus::getPixelColor(_busPtr, _iType, pix, _colorOrderMap.getPixelColorOrder(pix+_start, _colorOrder));
|
||||||
}
|
}
|
||||||
|
|
||||||
inline uint8_t getColorOrder() {
|
inline uint8_t getColorOrder() {
|
||||||
@ -263,6 +319,7 @@ class BusDigital : public Bus {
|
|||||||
uint8_t _iType = I_NONE;
|
uint8_t _iType = I_NONE;
|
||||||
uint8_t _skip = 0;
|
uint8_t _skip = 0;
|
||||||
void * _busPtr = nullptr;
|
void * _busPtr = nullptr;
|
||||||
|
const ColorOrderMap &_colorOrderMap;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@ -555,7 +612,7 @@ class BusManager {
|
|||||||
if (bc.type >= TYPE_NET_DDP_RGB && bc.type < 96) {
|
if (bc.type >= TYPE_NET_DDP_RGB && bc.type < 96) {
|
||||||
busses[numBusses] = new BusNetwork(bc);
|
busses[numBusses] = new BusNetwork(bc);
|
||||||
} else if (IS_DIGITAL(bc.type)) {
|
} else if (IS_DIGITAL(bc.type)) {
|
||||||
busses[numBusses] = new BusDigital(bc, numBusses);
|
busses[numBusses] = new BusDigital(bc, numBusses, colorOrderMap);
|
||||||
} else {
|
} else {
|
||||||
busses[numBusses] = new BusPwm(bc);
|
busses[numBusses] = new BusPwm(bc);
|
||||||
}
|
}
|
||||||
@ -640,8 +697,17 @@ class BusManager {
|
|||||||
return len;
|
return len;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void updateColorOrderMap(const ColorOrderMap &com) {
|
||||||
|
memcpy(&colorOrderMap, &com, sizeof(ColorOrderMap));
|
||||||
|
}
|
||||||
|
|
||||||
|
const ColorOrderMap& getColorOrderMap() const {
|
||||||
|
return colorOrderMap;
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
uint8_t numBusses = 0;
|
uint8_t numBusses = 0;
|
||||||
Bus* busses[WLED_MAX_BUSSES];
|
Bus* busses[WLED_MAX_BUSSES];
|
||||||
|
ColorOrderMap colorOrderMap;
|
||||||
};
|
};
|
||||||
#endif
|
#endif
|
||||||
|
@ -123,6 +123,22 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
|
|||||||
}
|
}
|
||||||
if (hw_led["rev"]) busses.getBus(0)->reversed = true; //set 0.11 global reversed setting for first bus
|
if (hw_led["rev"]) busses.getBus(0)->reversed = true; //set 0.11 global reversed setting for first bus
|
||||||
|
|
||||||
|
// read color order map configuration
|
||||||
|
JsonArray hw_com = hw[F("com")];
|
||||||
|
if (!hw_com.isNull()) {
|
||||||
|
ColorOrderMap com = {};
|
||||||
|
uint8_t s = 0;
|
||||||
|
for (JsonObject entry : hw_com) {
|
||||||
|
if (s > WLED_MAX_COLOR_ORDER_MAPPINGS) break;
|
||||||
|
uint16_t start = entry[F("start")] | 0;
|
||||||
|
uint16_t len = entry[F("len")] | 0;
|
||||||
|
uint8_t colorOrder = (int)entry[F("order")];
|
||||||
|
com.add(start, len, colorOrder);
|
||||||
|
s++;
|
||||||
|
}
|
||||||
|
busses.updateColorOrderMap(com);
|
||||||
|
}
|
||||||
|
|
||||||
// read multiple button configuration
|
// read multiple button configuration
|
||||||
JsonObject btn_obj = hw["btn"];
|
JsonObject btn_obj = hw["btn"];
|
||||||
JsonArray hw_btn_ins = btn_obj[F("ins")];
|
JsonArray hw_btn_ins = btn_obj[F("ins")];
|
||||||
@ -574,6 +590,18 @@ void serializeConfig() {
|
|||||||
ins[F("rgbw")] = bus->isRgbw();
|
ins[F("rgbw")] = bus->isRgbw();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
JsonArray hw_com = hw.createNestedArray(F("com"));
|
||||||
|
const ColorOrderMap& com = busses.getColorOrderMap();
|
||||||
|
for (uint8_t s = 0; s < com.count(); s++) {
|
||||||
|
const ColorOrderMapEntry *entry = com.get(s);
|
||||||
|
if (!entry) break;
|
||||||
|
|
||||||
|
JsonObject co = hw_com.createNestedObject();
|
||||||
|
co[F("start")] = entry->start;
|
||||||
|
co[F("len")] = entry->len;
|
||||||
|
co[F("order")] = entry->colorOrder;
|
||||||
|
}
|
||||||
|
|
||||||
// button(s)
|
// button(s)
|
||||||
JsonObject hw_btn = hw.createNestedObject("btn");
|
JsonObject hw_btn = hw.createNestedObject("btn");
|
||||||
hw_btn["max"] = WLED_MAX_BUTTONS; // just information about max number of buttons (not actually used)
|
hw_btn["max"] = WLED_MAX_BUTTONS; // just information about max number of buttons (not actually used)
|
||||||
|
@ -39,6 +39,12 @@
|
|||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef ESP8266
|
||||||
|
#define WLED_MAX_COLOR_ORDER_MAPPINGS 5
|
||||||
|
#else
|
||||||
|
#define WLED_MAX_COLOR_ORDER_MAPPINGS 10
|
||||||
|
#endif
|
||||||
|
|
||||||
//Usermod IDs
|
//Usermod IDs
|
||||||
#define USERMOD_ID_RESERVED 0 //Unused. Might indicate no usermod present
|
#define USERMOD_ID_RESERVED 0 //Unused. Might indicate no usermod present
|
||||||
#define USERMOD_ID_UNSPECIFIED 1 //Default value for a general user mod that does not specify a custom ID
|
#define USERMOD_ID_UNSPECIFIED 1 //Default value for a general user mod that does not specify a custom ID
|
||||||
@ -170,6 +176,7 @@
|
|||||||
#define COL_ORDER_RBG 3
|
#define COL_ORDER_RBG 3
|
||||||
#define COL_ORDER_BGR 4
|
#define COL_ORDER_BGR 4
|
||||||
#define COL_ORDER_GBR 5
|
#define COL_ORDER_GBR 5
|
||||||
|
#define COL_ORDER_MAX 5
|
||||||
|
|
||||||
|
|
||||||
//Button type
|
//Button type
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
<title>LED Settings</title>
|
<title>LED Settings</title>
|
||||||
<script>
|
<script>
|
||||||
var d=document,laprev=55,maxB=1,maxM=4000,maxPB=4096,maxL=1333,maxLbquot=0; //maximum bytes for LED allocation: 4kB for 8266, 32kB for 32
|
var d=document,laprev=55,maxB=1,maxM=4000,maxPB=4096,maxL=1333,maxLbquot=0; //maximum bytes for LED allocation: 4kB for 8266, 32kB for 32
|
||||||
var customStarts=false,startsDirty=[];
|
var customStarts=false,startsDirty=[],maxCOOverrides=5;
|
||||||
function H()
|
function H()
|
||||||
{
|
{
|
||||||
window.open("https://kno.wled.ge/features/settings/#led-settings");
|
window.open("https://kno.wled.ge/features/settings/#led-settings");
|
||||||
@ -347,6 +347,53 @@ ${i+1}:
|
|||||||
|
|
||||||
if (!init) UI();
|
if (!init) UI();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function addCOM(start=0,len=1,co=0) {
|
||||||
|
var i = d.getElementsByClassName("com_entry").length;
|
||||||
|
if (i >= 10) return;
|
||||||
|
|
||||||
|
var b = `<div class="com_entry">
|
||||||
|
<hr style="width:260px">
|
||||||
|
${i+1}: Start: <input type="number" name="XS${i}" id="xs${i}" class="l starts" min="0" max="65535" value="${start}" oninput="UI();" required="">
|
||||||
|
Length: <input type="number" name="XC${i}" id="xc${i}" class="l" min="1" max="65535" value="${len}" required="" oninput="UI()">
|
||||||
|
<div style="display:inline">Color Order:
|
||||||
|
<select id="xo${i}" name="XO${i}">
|
||||||
|
<option value="0">GRB</option>
|
||||||
|
<option value="1">RGB</option>
|
||||||
|
<option value="2">BRG</option>
|
||||||
|
<option value="3">RBG</option>
|
||||||
|
<option value="4">BGR</option>
|
||||||
|
<option value="5">GBR</option>
|
||||||
|
</select>
|
||||||
|
</div><br></div>`;
|
||||||
|
gId("com_entries").insertAdjacentHTML("beforeend", b);
|
||||||
|
gId("xo"+i).value = co;
|
||||||
|
btnCOM(i+1);
|
||||||
|
}
|
||||||
|
|
||||||
|
function remCOM() {
|
||||||
|
var entries = d.getElementsByClassName("com_entry");
|
||||||
|
var i = entries.length;
|
||||||
|
if (i === 0) return;
|
||||||
|
entries[i-1].remove();
|
||||||
|
btnCOM(i-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
function resetCOM(_newMaxCOOverrides=undefined) {
|
||||||
|
if (_newMaxCOOverrides) {
|
||||||
|
maxCOOverrides = _newMaxCOOverrides;
|
||||||
|
}
|
||||||
|
for (let e of d.getElementsByClassName("com_entry")) {
|
||||||
|
e.remove();
|
||||||
|
}
|
||||||
|
btnCOM(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
function btnCOM(i) {
|
||||||
|
gId("com_add").style.display = (i<maxCOOverrides) ? "inline":"none";
|
||||||
|
gId("com_rem").style.display = (i>0) ? "inline":"none";
|
||||||
|
}
|
||||||
|
|
||||||
function addBtn(i,p,t) {
|
function addBtn(i,p,t) {
|
||||||
var c = gId("btns").innerHTML;
|
var c = gId("btns").innerHTML;
|
||||||
var bt = "BT" + String.fromCharCode((i<10?48:55)+i);;
|
var bt = "BT" + String.fromCharCode((i<10?48:55)+i);;
|
||||||
@ -431,6 +478,12 @@ ${i+1}:
|
|||||||
d.getElementsByName("CV"+i)[0].checked = v.rev;
|
d.getElementsByName("CV"+i)[0].checked = v.rev;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
if(c.hw.com) {
|
||||||
|
resetCOM();
|
||||||
|
c.hw.com.forEach(e => {
|
||||||
|
addCOM(e.start, e.len, e.order);
|
||||||
|
});
|
||||||
|
}
|
||||||
if (c.hw.btn) {
|
if (c.hw.btn) {
|
||||||
var b = c.hw.btn;
|
var b = c.hw.btn;
|
||||||
if (Array.isArray(b.ins)) gId("btns").innerHTML = "";
|
if (Array.isArray(b.ins)) gId("btns").innerHTML = "";
|
||||||
@ -510,6 +563,14 @@ ${i+1}:
|
|||||||
Make a segment for each output: <input type="checkbox" name="MS"> <br>
|
Make a segment for each output: <input type="checkbox" name="MS"> <br>
|
||||||
Custom bus start indices: <input type="checkbox" onchange="tglSi(this.checked)" id="si"> <br>
|
Custom bus start indices: <input type="checkbox" onchange="tglSi(this.checked)" id="si"> <br>
|
||||||
<hr style="width:260px">
|
<hr style="width:260px">
|
||||||
|
<div id="color_order_mapping">
|
||||||
|
Color Order Override:
|
||||||
|
<div id="com_entries"></div>
|
||||||
|
<hr style="width:260px">
|
||||||
|
<button type="button" id="com_add" onclick="addCOM();UI()" style="display:none;border-radius:20px;height:36px;">+</button>
|
||||||
|
<button type="button" id="com_rem" onclick="remCOM();UI()" style="display:none;border-radius:20px;width:36px;height:36px;">-</button><br>
|
||||||
|
</div>
|
||||||
|
<hr style="width:260px">
|
||||||
<div id="btns"></div>
|
<div id="btns"></div>
|
||||||
Touch threshold: <input type="number" class="s" min="0" max="100" name="TT" required><br>
|
Touch threshold: <input type="number" class="s" min="0" max="100" name="TT" required><br>
|
||||||
IR GPIO: <input type="number" min="-1" max="40" name="IR" onchange="UI()" class="xs"><select name="IT" onchange="UI()">
|
IR GPIO: <input type="number" min="-1" max="40" name="IR" onchange="UI()" class="xs"><select name="IT" onchange="UI()">
|
||||||
|
File diff suppressed because one or more lines are too long
@ -136,6 +136,20 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
|
|||||||
doInitBusses = true;
|
doInitBusses = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ColorOrderMap com = {};
|
||||||
|
for (uint8_t s = 0; s < WLED_MAX_COLOR_ORDER_MAPPINGS; s++) {
|
||||||
|
char xs[4] = "XS"; xs[2] = 48+s; xs[3] = 0; //start LED
|
||||||
|
char xc[4] = "XC"; xc[2] = 48+s; xc[3] = 0; //strip length
|
||||||
|
char xo[4] = "XO"; xo[2] = 48+s; xo[3] = 0; //color order
|
||||||
|
if (request->hasArg(xs)) {
|
||||||
|
start = request->arg(xs).toInt();
|
||||||
|
length = request->arg(xc).toInt();
|
||||||
|
colorOrder = request->arg(xo).toInt();
|
||||||
|
com.add(start, length, colorOrder);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
busses.updateColorOrderMap(com);
|
||||||
|
|
||||||
// upate other pins
|
// upate other pins
|
||||||
int hw_ir_pin = request->arg(F("IR")).toInt();
|
int hw_ir_pin = request->arg(F("IR")).toInt();
|
||||||
if (pinManager.allocatePin(hw_ir_pin,false, PinOwner::IR)) {
|
if (pinManager.allocatePin(hw_ir_pin,false, PinOwner::IR)) {
|
||||||
|
@ -421,6 +421,19 @@ void getSettingsJS(byte subPage, char* dest)
|
|||||||
oappend(SET_F("mA\";"));
|
oappend(SET_F("mA\";"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
oappend(SET_F("resetCOM("));
|
||||||
|
oappend(itoa(WLED_MAX_COLOR_ORDER_MAPPINGS,nS,10));
|
||||||
|
oappend(SET_F(");"));
|
||||||
|
const ColorOrderMap& com = busses.getColorOrderMap();
|
||||||
|
for (uint8_t s=0; s < com.count(); s++) {
|
||||||
|
const ColorOrderMapEntry* entry = com.get(s);
|
||||||
|
if (entry == nullptr) break;
|
||||||
|
oappend(SET_F("addCOM("));
|
||||||
|
oappend(itoa(entry->start,nS,10)); oappend(",");
|
||||||
|
oappend(itoa(entry->len,nS,10)); oappend(",");
|
||||||
|
oappend(itoa(entry->colorOrder,nS,10)); oappend(");");
|
||||||
|
}
|
||||||
|
|
||||||
sappend('v',SET_F("CA"),briS);
|
sappend('v',SET_F("CA"),briS);
|
||||||
|
|
||||||
sappend('c',SET_F("BO"),turnOnAtBoot);
|
sappend('c',SET_F("BO"),turnOnAtBoot);
|
||||||
|
Loading…
Reference in New Issue
Block a user