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
|
||||
class Bus {
|
||||
public:
|
||||
@ -152,7 +208,7 @@ class Bus {
|
||||
|
||||
class BusDigital : public Bus {
|
||||
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 (!pinManager.allocatePin(bc.pins[0], true, PinOwner::BusDigital)) return;
|
||||
_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
|
||||
void setStatusPixel(uint32_t c) {
|
||||
if (_skip && canShow()) {
|
||||
PolyBus::setPixelColor(_busPtr, _iType, 0, c, _colorOrder);
|
||||
PolyBus::setPixelColor(_busPtr, _iType, 0, c, _colorOrderMap.getPixelColorOrder(_start, _colorOrder));
|
||||
PolyBus::show(_busPtr, _iType);
|
||||
}
|
||||
}
|
||||
@ -207,13 +263,13 @@ class BusDigital : public Bus {
|
||||
if (_cct >= 1900) c = colorBalanceFromKelvin(_cct, c); //color correction from CCT
|
||||
if (reversed) pix = _len - pix -1;
|
||||
else pix += _skip;
|
||||
PolyBus::setPixelColor(_busPtr, _iType, pix, c, _colorOrder);
|
||||
PolyBus::setPixelColor(_busPtr, _iType, pix, c, _colorOrderMap.getPixelColorOrder(pix+_start, _colorOrder));
|
||||
}
|
||||
|
||||
uint32_t getPixelColor(uint16_t pix) {
|
||||
if (reversed) pix = _len - pix -1;
|
||||
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() {
|
||||
@ -263,6 +319,7 @@ class BusDigital : public Bus {
|
||||
uint8_t _iType = I_NONE;
|
||||
uint8_t _skip = 0;
|
||||
void * _busPtr = nullptr;
|
||||
const ColorOrderMap &_colorOrderMap;
|
||||
};
|
||||
|
||||
|
||||
@ -555,7 +612,7 @@ class BusManager {
|
||||
if (bc.type >= TYPE_NET_DDP_RGB && bc.type < 96) {
|
||||
busses[numBusses] = new BusNetwork(bc);
|
||||
} else if (IS_DIGITAL(bc.type)) {
|
||||
busses[numBusses] = new BusDigital(bc, numBusses);
|
||||
busses[numBusses] = new BusDigital(bc, numBusses, colorOrderMap);
|
||||
} else {
|
||||
busses[numBusses] = new BusPwm(bc);
|
||||
}
|
||||
@ -640,8 +697,17 @@ class BusManager {
|
||||
return len;
|
||||
}
|
||||
|
||||
void updateColorOrderMap(const ColorOrderMap &com) {
|
||||
memcpy(&colorOrderMap, &com, sizeof(ColorOrderMap));
|
||||
}
|
||||
|
||||
const ColorOrderMap& getColorOrderMap() const {
|
||||
return colorOrderMap;
|
||||
}
|
||||
|
||||
private:
|
||||
uint8_t numBusses = 0;
|
||||
Bus* busses[WLED_MAX_BUSSES];
|
||||
ColorOrderMap colorOrderMap;
|
||||
};
|
||||
#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
|
||||
|
||||
// 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
|
||||
JsonObject btn_obj = hw["btn"];
|
||||
JsonArray hw_btn_ins = btn_obj[F("ins")];
|
||||
@ -574,6 +590,18 @@ void serializeConfig() {
|
||||
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)
|
||||
JsonObject hw_btn = hw.createNestedObject("btn");
|
||||
hw_btn["max"] = WLED_MAX_BUTTONS; // just information about max number of buttons (not actually used)
|
||||
|
@ -39,6 +39,12 @@
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef ESP8266
|
||||
#define WLED_MAX_COLOR_ORDER_MAPPINGS 5
|
||||
#else
|
||||
#define WLED_MAX_COLOR_ORDER_MAPPINGS 10
|
||||
#endif
|
||||
|
||||
//Usermod IDs
|
||||
#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
|
||||
@ -170,6 +176,7 @@
|
||||
#define COL_ORDER_RBG 3
|
||||
#define COL_ORDER_BGR 4
|
||||
#define COL_ORDER_GBR 5
|
||||
#define COL_ORDER_MAX 5
|
||||
|
||||
|
||||
//Button type
|
||||
|
@ -6,7 +6,7 @@
|
||||
<title>LED Settings</title>
|
||||
<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 customStarts=false,startsDirty=[];
|
||||
var customStarts=false,startsDirty=[],maxCOOverrides=5;
|
||||
function H()
|
||||
{
|
||||
window.open("https://kno.wled.ge/features/settings/#led-settings");
|
||||
@ -347,6 +347,53 @@ ${i+1}:
|
||||
|
||||
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) {
|
||||
var c = gId("btns").innerHTML;
|
||||
var bt = "BT" + String.fromCharCode((i<10?48:55)+i);;
|
||||
@ -431,6 +478,12 @@ ${i+1}:
|
||||
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) {
|
||||
var b = c.hw.btn;
|
||||
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>
|
||||
Custom bus start indices: <input type="checkbox" onchange="tglSi(this.checked)" id="si"> <br>
|
||||
<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>
|
||||
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()">
|
||||
|
File diff suppressed because one or more lines are too long
@ -136,6 +136,20 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
|
||||
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
|
||||
int hw_ir_pin = request->arg(F("IR")).toInt();
|
||||
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("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('c',SET_F("BO"),turnOnAtBoot);
|
||||
|
Loading…
Reference in New Issue
Block a user