Skip to content
Draft
Show file tree
Hide file tree
Changes from 16 commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
d7e45b4
Add per-bus W-channel CCT for accurate auto-white calculation
NerdyGriffin May 4, 2026
01fe430
Show W-LED CCT controls in DUAL auto-white mode too
NerdyGriffin May 25, 2026
113b3dc
Add fast path to autoWhiteCalc when per-bus CCT feature is off
NerdyGriffin May 25, 2026
cd00e56
Refine W-channel CCT UI label and split onto separate lines
NerdyGriffin May 28, 2026
cd6bf0e
Lower W-channel CCT minimum from 1900 K to 1000 K
NerdyGriffin May 28, 2026
38d19b0
Merge branch 'main' into claude/wled-cct-conversion-research-qmgU7
softhack007 May 29, 2026
5f61870
Seed W-channel CCT field when re-enabling from a blank value
NerdyGriffin May 29, 2026
b42e4e1
Merge updated PR branch (main sync) into local wkChk fix
NerdyGriffin May 29, 2026
5312bd0
Relabel W-channel CCT checkbox to reflect what it actually does
NerdyGriffin May 29, 2026
15016d6
Merge branch 'main' into claude/wled-cct-conversion-research-qmgU7
NerdyGriffin May 29, 2026
adefb08
Wrap loadCfg W-channel CCT block in standard AI markers
NerdyGriffin May 29, 2026
7f7e78a
Merge updated PR branch (main sync) into local AI-marker fix
NerdyGriffin May 29, 2026
9965ca6
Restrict W-channel CCT correction to true RGBW bus types
NerdyGriffin May 30, 2026
c26fc17
Disambiguate W-channel CCT gating comment
NerdyGriffin May 30, 2026
18bdf30
Align W-channel feature comments to "W channel color temperature"
NerdyGriffin May 30, 2026
988d833
Optimize autoWhiteCalc hot path with precomputed reciprocals
NerdyGriffin May 30, 2026
d9881b8
Address review: init reciprocal cache, honor global auto-white override
NerdyGriffin Jun 9, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
65 changes: 63 additions & 2 deletions wled00/bus_manager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,32 @@ void Bus::calculateCCT(uint32_t c, uint8_t &ww, uint8_t &cw) {
cw = (w * cw) / 255;
}

// AI: below section was generated by an AI
// recompute cached W-LED RGB equivalent when the configured Kelvin changes;
// 0 means "treat the W LED as neutral white" which preserves legacy behavior
// where autoWhiteCalc subtracted the same value from R, G, B.
void Bus::setWhiteKelvin(uint16_t k) {
_whiteKelvin = k;
if (k == 0) {
_wR = _wG = _wB = 255; // legacy: treat W as neutral
} else {
byte rgb[4];
colorKtoRGB(k, rgb);
_wR = rgb[0]; _wG = rgb[1]; _wB = rgb[2];
}
Comment thread
coderabbitai[bot] marked this conversation as resolved.
// Precompute fixed-point reciprocals (Q15) so autoWhiteCalc's hot path can
// replace the per-pixel division (channel*255)/_wX with (channel*_rwX)>>15.
// floor() here under-estimates the reciprocal, so the derived w cap can only
// come out <= the true floor value — never larger — keeping the subtraction
// underflow-safe (verified exact across all channel/_wX combinations: max
// error 1, no over-estimate; max product 255*_rwX fits uint32). _rwX==0 is the
// "channel does not constrain w" sentinel, matching the _wX==0 guard below.
_rwR = _wR ? ((255U << 15) / _wR) : 0;
_rwG = _wG ? ((255U << 15) / _wG) : 0;
_rwB = _wB ? ((255U << 15) / _wB) : 0;
}
// AI: end

// calculates white channel and CCT values based on given settings
uint32_t Bus::autoWhiteCalc(uint32_t c, uint8_t &ww, uint8_t &cw) const {
unsigned aWM = _autoWhiteMode;
Expand All @@ -112,9 +138,43 @@ uint32_t Bus::autoWhiteCalc(uint32_t c, uint8_t &ww, uint8_t &cw) const {
//ignore auto-white calculation if w>0 and mode DUAL (DUAL behaves as BRIGHTER if w==0)
} else if (aWM == RGBW_MODE_MAX) {
w = r > g ? (r > b ? r : b) : (g > b ? g : b); // brightest RGB channel
} else if (_whiteKelvin == 0 || _hasCCT || !_hasRgb) {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this should be done in the UI, avoid conditionals in the hot path if possible

// Fast path: per-bus W channel color temperature feature is off, OR the bus type can't
// use it — dual-white CCT buses have a variable white point set via
// the CCT control (not a single fixed Kelvin), and non-RGB buses have
// nothing to derive the correction from. Identical to the pre-feature
// behavior: pick darkest RGB channel as W and (for ACCURATE) subtract
// it equally. Also avoids three divisions per pixel in the common
// default case, since most strips never enable the feature.
w = r < g ? (r < b ? r : b) : (g < b ? g : b);
if (aWM == RGBW_MODE_AUTO_ACCURATE) { r -= w; g -= w; b -= w; }
} else {
w = r < g ? (r < b ? r : b) : (g < b ? g : b); // darkest RGB channel
if (aWM == RGBW_MODE_AUTO_ACCURATE) { r -= w; g -= w; b -= w; } //subtract w in ACCURATE mode
// AI: below section was generated by an AI
// Per-channel cap path (feature on): pick the largest w such that
// (w * _wX)/255 <= channel for every X in {R,G,B}, preventing
// underflow when subtracting the W LED's RGB contribution. Floor
// division composes back through the subtract — i.e.
// floor((r*255)/_wR) * _wR <= r*255 — so the subtraction is safe.
// Hot path: the (channel*255)/_wX divisions are replaced by the Q15
// reciprocals precomputed in setWhiteKelvin (multiply + shift, no
// per-pixel divide). _rwX is floor-biased so wMax never over-estimates,
// keeping the cap underflow-safe. _rwX==0 means _wX==0 (channel doesn't
// constrain w — _wB is 0 at/below 1900 K, _wG only at extreme lows),
// matching the previous per-channel zero guards. The /255 in the
// subtraction stays: 255 is a compile-time constant the compiler already
// strength-reduces, so it isn't an actual division.
unsigned wMaxR = _rwR ? (r * _rwR) >> 15 : 255U;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove conditionals, _rwR is never zero, _rwG is also never zero and the multiplication + shift is usually faster than a branch as it lets the compiler optimize more.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Last time I worked through the math, I thought there was a case where _rwR can be zero, but I also want to simply this. I will look into this some more

unsigned wMaxG = _rwG ? (g * _rwG) >> 15 : 255U;
unsigned wMaxB = _rwB ? (b * _rwB) >> 15 : 255U;
unsigned wCap = wMaxR < wMaxG ? (wMaxR < wMaxB ? wMaxR : wMaxB) : (wMaxG < wMaxB ? wMaxG : wMaxB);
if (wCap > 255U) wCap = 255U;
w = wCap;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this changes the MODE_MIN behaviour. intentional? if so why only min and not MODE_MAX?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It was primarily intended for MODE_AUTO_ACCURATE and DUAL because they advertise themselves as an "accurate" translation of RGB into RGBW (and ACCURATE is the mode I use on my daily-driver WLED controllers). But it may be extended to include MODE_MAX if desired, although the RGB + whiteKelvin -> RGBW math might need to be different to match the intent of MODE_MAX.
I originally assumed MODE_MAX didn't need it because it isn't concerned about color accuracy, but I am open to corrections/suggestions.

Please let me know if I am misunderstanding what you are referring to as MODE_MIN and MODE_MAX

if (aWM == RGBW_MODE_AUTO_ACCURATE) {
r -= (w * _wR) / 255; // subtract W LED's R contribution

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

still uses division, why not right shift?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since it divided by a constant, the compiler already converts that to a shift at compile time. But it can just as easily be explicitly written as a right shift

g -= (w * _wG) / 255; // subtract W LED's G contribution
b -= (w * _wB) / 255; // subtract W LED's B contribution
}
// AI: end
}
c = RGBW32(r, g, b, w);
}
Expand Down Expand Up @@ -1226,6 +1286,7 @@ int BusManager::add(const BusConfig &bc, bool placeholder) {
} else {
busses.push_back(make_unique<BusPwm>(bc));
}
if (!busses.empty()) busses.back()->setWhiteKelvin(bc.whiteKelvin);
return busses.size();
}

Expand Down
18 changes: 17 additions & 1 deletion wled00/bus_manager.h
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ make_unique(Args&&... args)

//colors.cpp
uint16_t approximateKelvinFromRGB(uint32_t rgb);
void colorKtoRGB(uint16_t kelvin, byte* rgb);

#define GET_BIT(var,bit) (((var)>>(bit))&0x01)
#define SET_BIT(var,bit) ((var)|=(uint16_t)(0x0001<<(bit)))
Expand Down Expand Up @@ -121,6 +122,10 @@ class Bus {
, _reversed(reversed)
, _valid(false)
, _needsRefresh(refresh)
, _whiteKelvin(0)
, _wR(255)
, _wG(255)
, _wB(255)
{
_autoWhiteMode = Bus::hasWhite(type) ? aw : RGBW_MODE_MANUAL_ONLY;
};
Expand Down Expand Up @@ -162,6 +167,8 @@ class Bus {
inline void setStart(uint16_t start) { _start = start; }
inline void setAutoWhiteMode(uint8_t m) { if (m < 5) _autoWhiteMode = m; }
inline uint8_t getAutoWhiteMode() const { return _autoWhiteMode; }
inline uint16_t getWhiteKelvin() const { return _whiteKelvin; }
void setWhiteKelvin(uint16_t k);
inline size_t getNumberOfChannels() const { return hasWhite() + 3*hasRGB() + hasCCT(); }
inline uint16_t getStart() const { return _start; }
inline uint8_t getType() const { return _type; }
Expand Down Expand Up @@ -221,6 +228,13 @@ class Bus {
uint8_t _autoWhiteMode; // global Auto White Calculation override
uint16_t _start;
uint16_t _len;
uint16_t _whiteKelvin; // physical W channel color temperature in Kelvin (0 = neutral/legacy behavior)
uint8_t _wR; // cached W LED RGB equivalent (255,255,255 when _whiteKelvin==0)
uint8_t _wG;
uint8_t _wB;
uint32_t _rwR; // Q15 reciprocal of _wR (floor((255<<15)/_wR), 0 if _wR==0) for autoWhiteCalc hot path

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

uint16_t should be accurate enough I think, no need to waste RAM
also I am not sure how the compiler packs this, it's best practice to try and align values to 4byte boundaries to avoid padding.

uint32_t _rwG;
uint32_t _rwB;
Comment on lines +238 to +240

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Initialize reciprocal cache members in Bus constructor.

_rwR/_rwG/_rwB are newly added but not initialized in the constructor, which risks undefined values being consumed before setWhiteKelvin() runs.

Proposed fix
     , _whiteKelvin(0)
     , _wR(255)
     , _wG(255)
     , _wB(255)
+    , _rwR(0)
+    , _rwG(0)
+    , _rwB(0)
     {
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@wled00/bus_manager.h` around lines 235 - 237, The reciprocal cache fields
_rwR, _rwG, and _rwB in class Bus are added but not initialized; update the Bus
constructor (the Bus::Bus initializer list or body) to initialize _rwR, _rwG,
and _rwB to 0 so they have defined values before any call to setWhiteKelvin(),
ensuring the autoWhiteCalc hot path won't read uninitialized reciprocals.

//struct { //using bitfield struct adds abour 250 bytes to binary size
bool _reversed;// : 1;
bool _valid;// : 1;
Expand Down Expand Up @@ -461,6 +475,7 @@ struct BusConfig {
uint8_t skipAmount;
bool refreshReq;
uint8_t autoWhite;
uint16_t whiteKelvin; // physical W channel color temperature in Kelvin (0 = neutral/legacy behavior)
uint8_t pins[OUTPUT_MAX_PINS] = {255, 255, 255, 255, 255};
uint16_t frequency;
uint8_t milliAmpsPerLed;
Expand All @@ -469,13 +484,14 @@ struct BusConfig {
uint8_t iType; // internal bus type (I_*) determined during memory estimation, used for bus creation
String text;

BusConfig(uint8_t busType, uint8_t* ppins, uint16_t pstart, uint16_t len = 1, uint8_t pcolorOrder = COL_ORDER_GRB, bool rev = false, uint8_t skip = 0, byte aw=RGBW_MODE_MANUAL_ONLY, uint16_t clock_kHz=0U, uint8_t maPerLed=LED_MILLIAMPS_DEFAULT, uint16_t maMax=ABL_MILLIAMPS_DEFAULT, uint8_t driver=0, String sometext = "")
BusConfig(uint8_t busType, uint8_t* ppins, uint16_t pstart, uint16_t len = 1, uint8_t pcolorOrder = COL_ORDER_GRB, bool rev = false, uint8_t skip = 0, byte aw=RGBW_MODE_MANUAL_ONLY, uint16_t clock_kHz=0U, uint8_t maPerLed=LED_MILLIAMPS_DEFAULT, uint16_t maMax=ABL_MILLIAMPS_DEFAULT, uint8_t driver=0, String sometext = "", uint16_t whiteK=0)
: count(std::max(len,(uint16_t)1))
, start(pstart)
, colorOrder(pcolorOrder)
, reversed(rev)
, skipAmount(skip)
, autoWhite(aw)
, whiteKelvin(whiteK)
, frequency(clock_kHz)
, milliAmpsPerLed(maPerLed)
, milliAmpsMax(maMax)
Expand Down
4 changes: 3 additions & 1 deletion wled00/cfg.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,7 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
bool refresh = elm["ref"] | false;
uint16_t freqkHz = elm[F("freq")] | 0; // will be in kHz for DotStar and Hz for PWM
uint8_t AWmode = elm[F("rgbwm")] | RGBW_MODE_MANUAL_ONLY;
uint16_t whiteK = elm[F("wk")] | 0; // physical W channel color temperature in K (0 = neutral/legacy)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Clamp wk at config ingress to the supported domain.

wk is accepted as-is here. Please enforce 0 (disabled) or [1000, 10000] before constructing BusConfig, so invalid JSON values don’t leak into runtime state.

Proposed fix
-      uint16_t whiteK = elm[F("wk")] | 0; // physical W channel color temperature in K (0 = neutral/legacy)
+      uint16_t whiteK = elm[F("wk")] | 0; // physical W channel color temperature in K (0 = neutral/legacy)
+      if (whiteK != 0 && (whiteK < 1000 || whiteK > 10000)) whiteK = 0;

As per coding guidelines, enforce input-validation at the first untrusted ingress point (HTTP/JSON request bodies and query parameters).

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@wled00/cfg.cpp` at line 241, The code reads whiteK from JSON as uint16_t
without validation (uint16_t whiteK = elm[F("wk")] | 0), allowing invalid values
into BusConfig; update the ingress parsing for wk so that you accept 0
(disabled) or clamp values into the supported range [1000,10000] before
constructing BusConfig—i.e., parse elm[F("wk")] into a temporary integer/default
0, if value != 0 then if value < 1000 set to 1000, if value > 10000 set to
10000, and only then assign to whiteK (or reject non-numeric inputs by treating
as 0). Ensure you apply this change where whiteK is read/used (reference
variable whiteK and the JSON key elm[F("wk")]) so runtime state cannot contain
out-of-range temps.

uint8_t maPerLed = elm[F("ledma")] | LED_MILLIAMPS_DEFAULT;
uint16_t maMax = elm[F("maxpwr")] | (ablMilliampsMax * length) / total; // rough (incorrect?) per strip ABL calculation when no config exists
// To disable brightness limiter we either set output max current to 0 or single LED current to 0 (we choose output max current)
Expand All @@ -249,7 +250,7 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
uint8_t driverType = elm[F("drv")] | 0; // 0=RMT (default), 1=I2S note: polybus may override this if driver is not available

String host = elm[F("text")] | String();
busConfigs.emplace_back(ledType, pins, start, length, colorOrder, reversed, skipFirst, AWmode, freqkHz, maPerLed, maMax, driverType, host);
busConfigs.emplace_back(ledType, pins, start, length, colorOrder, reversed, skipFirst, AWmode, freqkHz, maPerLed, maMax, driverType, host, whiteK);
doInitBusses = true; // finalization done in beginStrip()
if (!Bus::isVirtual(ledType)) s++; // have as many virtual buses as you want
}
Expand Down Expand Up @@ -999,6 +1000,7 @@ void serializeConfig(JsonObject root) {
ins["type"] = bus->getType() & 0x7F;
ins["ref"] = bus->isOffRefreshRequired();
ins[F("rgbwm")] = bus->getAutoWhiteMode();
ins[F("wk")] = bus->getWhiteKelvin();
ins[F("freq")] = bus->getFrequency();
ins[F("maxpwr")] = bus->getMaxCurrent();
ins[F("ledma")] = bus->getLEDCurrent();
Expand Down
53 changes: 52 additions & 1 deletion wled00/data/settings_leds.htm
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,22 @@
});
if (ppl) d.Sf.MA.value = sumMA; // populate UI ABL value if PPL used
}
// AI: below section was generated by an AI
// Per-bus W-LED color temperature toggle. The Kelvin input lives in a
// wrapper div (dig<n>wkv) that UI() shows/hides based on the checkbox;
// the input itself is also disabled when off, so it isn't submitted
// with the form — backend then sees no WK<n> arg and stores wk=0
// (legacy fast path). Seed the field to 6500 K when re-enabling from
// a blank or sub-min value so the UI default matches the sRGB white
// point.
function wkChk(n)
{
const wke = d.Sf["WKE"+n], wk = d.Sf["WK"+n];
if (!wke || !wk) return;
if (wke.checked && !(parseInt(wk.value, 10) >= 1000)) wk.value = 6500;
UI();
}
Comment thread
coderabbitai[bot] marked this conversation as resolved.
// AI: end
// enable and update LED Amps
function enLA(s,n)
{
Expand Down Expand Up @@ -369,6 +385,31 @@
gId("dig"+n+"s").style.display = (isVir(t) || isAna(t) || isHub75(t)) ? "none":"inline"; // hide skip 1st for virtual & analog
gId("dig"+n+"f").style.display = (isDig(t) || (isPWM(t) && maxL>2048)) ? "inline":"none"; // hide refresh (PWM hijacks reffresh for dithering on ESP32)
gId("dig"+n+"a").style.display = (hasW(t)) ? "inline":"none"; // auto calculate white
// AI: below section was generated by an AI
// The "Correct auto-white for W channel color temperature" control is
// only meaningful for true single-white RGBW buses (hasW && hasRGB &&
// !hasCCT) AND when autoWhiteCalc uses the per-channel-cap path that
// consumes _wR/_wG/_wB — i.e. AW mode is Brighter (1), Accurate (2), or
// Dual (3, where manual w==0 falls through to the Brighter path). Hide
// the whole toggle otherwise. The Kelvin input lives in a child block
// that's shown only when the checkbox is on; the input is disabled (and
// so not submitted) when off, so the backend stores wk=0 and the legacy
// autoWhite path is used.
{
const awEl = d.Sf["AW"+n];
const awv = awEl ? parseInt(awEl.value) : 0;
const wkBox = gId("dig"+n+"wk");
// only true single-white RGBW types: a fixed W-LED color temperature is
// meaningless for dual-white CCT buses (variable white point) and for
// non-RGB buses (nothing to derive the correction from)
if (wkBox) wkBox.style.display = (hasW(t) && hasRGB(t) && !hasCCT(t) && (awv === 1 || awv === 2 || awv === 3)) ? "inline" : "none";
const wke = d.Sf["WKE"+n], wk = d.Sf["WK"+n], wkv = gId("dig"+n+"wkv");
if (wke && wk) {
wk.disabled = !wke.checked;
if (wkv) wkv.style.display = wke.checked ? "inline" : "none";
}
}
// AI: end
gId("dig"+n+"l").style.display = (isD2P(t) || isPWM(t)) ? "inline":"none"; // bus clock speed / PWM speed (relative) (not On/Off)
gId("rev"+n).innerHTML = isAna(t) ? "Inverted output":"Reversed"; // change reverse text for analog else (rotated 180°)
//gId("psd"+n).innerHTML = isAna(t) ? "Index:":"Start:"; // change analog start description
Expand Down Expand Up @@ -593,7 +634,7 @@
<div id="dig${s}r" style="display:inline"><br><span id="rev${s}">Reversed</span>: <input type="checkbox" name="CV${s}"></div>
<div id="dig${s}s" style="display:inline"><br>Skip first LEDs: <input type="number" name="SL${s}" min="0" max="255" value="0" oninput="UI()"></div>
<div id="dig${s}f" style="display:inline"><br><span id="off${s}">Off Refresh</span>: <input id="rf${s}" type="checkbox" name="RF${s}"></div>
<div id="dig${s}a" style="display:inline"><br>Auto-calculate W channel from RGB:<br><select name="AW${s}"><option value=0>None</option><option value=1>Brighter</option><option value=2>Accurate</option><option value=3>Dual</option><option value=4>Max</option></select>&nbsp;</div>
<div id="dig${s}a" style="display:inline"><br>Auto-calculate W channel from RGB:<br><select name="AW${s}" onchange="UI()"><option value=0>None</option><option value=1>Brighter</option><option value=2>Accurate</option><option value=3>Dual</option><option value=4>Max</option></select><div id="dig${s}wk" style="display:none"><br>Correct auto-white for W channel color temperature: <input type="checkbox" name="WKE${s}" onchange="wkChk('${s}')"><div id="dig${s}wkv" style="display:none"><br>W channel color temperature: <input type="number" name="WK${s}" min="1000" max="10000" step="50" class="l" value="6500" disabled> K</div></div></div>
</div>`;
f.insertAdjacentHTML("beforeend", cn);
// fill led types (credit @netmindz)
Expand Down Expand Up @@ -784,6 +825,16 @@
d.getElementsByName("RF"+i)[0].checked = v.ref;
d.getElementsByName("CV"+i)[0].checked = v.rev;
d.getElementsByName("AW"+i)[0].value = v.rgbwm;
// AI: below section was generated by an AI
// derive WKE checkbox + WK seed from stored wk (0 = feature off)
{
const wkChkEl = d.getElementsByName("WKE"+i)[0];
const wkEl = d.getElementsByName("WK"+i)[0];
const wkv = parseInt(v.wk) | 0;
if (wkChkEl) wkChkEl.checked = wkv > 0;
if (wkEl) wkEl.value = wkv > 0 ? wkv : 6500;
}
// AI: end
d.getElementsByName("WO"+i)[0].value = (v.order>>4) & 0x0F;
d.getElementsByName("SP"+i)[0].value = v.freq;
d.getElementsByName("LA"+i)[0].value = v.ledma;
Expand Down
6 changes: 5 additions & 1 deletion wled00/set.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,7 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
char sl[4] = "SL"; sl[2] = offset+s; sl[3] = 0; //skip first N LEDs
char rf[4] = "RF"; rf[2] = offset+s; rf[3] = 0; //refresh required
char aw[4] = "AW"; aw[2] = offset+s; aw[3] = 0; //auto white mode
char wk[4] = "WK"; wk[2] = offset+s; wk[3] = 0; //W channel color temperature (Kelvin)
char wo[4] = "WO"; wo[2] = offset+s; wo[3] = 0; //channel swap
char sp[4] = "SP"; sp[2] = offset+s; sp[3] = 0; //bus clock speed (DotStar & PWM)
char la[4] = "LA"; la[2] = offset+s; la[3] = 0; //LED mA
Expand All @@ -230,6 +231,9 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
break; // no parameter
}
awmode = request->arg(aw).toInt();
uint16_t whiteK = request->hasArg(wk) ? (uint16_t)request->arg(wk).toInt() : 0;
// Reject out-of-range or sub-1000K Kelvin values; 0 means "neutral/legacy"
if (whiteK != 0 && (whiteK < 1000 || whiteK > 10000)) whiteK = 0;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the first condition is redundant

uint16_t freq = request->arg(sp).toInt();
if (Bus::isPWM(type)) {
switch (freq) {
Expand Down Expand Up @@ -265,7 +269,7 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
text = request->arg(hs).substring(0,31);
// actual finalization is done in WLED::loop() (removing old busses and adding new)
// this may happen even before this loop is finished so we do "doInitBusses" after the loop
busConfigs.emplace_back(type, pins, start, length, colorOrder | (channelSwap<<4), request->hasArg(cv), skip, awmode, freq, maPerLed, maMax, driverType, text);
busConfigs.emplace_back(type, pins, start, length, colorOrder | (channelSwap<<4), request->hasArg(cv), skip, awmode, freq, maPerLed, maMax, driverType, text, whiteK);
busesChanged = true;
}
//doInitBusses = busesChanged; // we will do that below to ensure all input data is processed
Expand Down
4 changes: 4 additions & 0 deletions wled00/xml.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -371,6 +371,8 @@ void getSettingsJS(byte subPage, Print& settingsScript)
char sl[4] = "SL"; sl[2] = offset+s; sl[3] = 0; //skip 1st LED
char rf[4] = "RF"; rf[2] = offset+s; rf[3] = 0; //off refresh
char aw[4] = "AW"; aw[2] = offset+s; aw[3] = 0; //auto white mode
char wke[5] = "WKE"; wke[3] = offset+s; wke[4] = 0; //W channel color temperature enabled (UI checkbox)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't like the use of an enabled flag, could use 0 (or out of range) means disabled (the code in set.cpp already treats it that way)

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed, I will change that

char wk[4] = "WK"; wk[2] = offset+s; wk[3] = 0; //W channel color temperature (Kelvin)
char wo[4] = "WO"; wo[2] = offset+s; wo[3] = 0; //swap channels
char sp[4] = "SP"; sp[2] = offset+s; sp[3] = 0; //bus clock speed
char la[4] = "LA"; la[2] = offset+s; la[3] = 0; //LED current
Expand All @@ -392,6 +394,8 @@ void getSettingsJS(byte subPage, Print& settingsScript)
printSetFormValue(settingsScript,sl,bus->skippedLeds());
printSetFormCheckbox(settingsScript,rf,bus->isOffRefreshRequired());
printSetFormValue(settingsScript,aw,bus->getAutoWhiteMode());
printSetFormCheckbox(settingsScript,wke,bus->getWhiteKelvin() > 0);
printSetFormValue(settingsScript,wk,bus->getWhiteKelvin() > 0 ? bus->getWhiteKelvin() : 6500);
printSetFormValue(settingsScript,wo,bus->getColorOrder() >> 4);
unsigned speed = bus->getFrequency();
if (bus->isPWM()) {
Expand Down
Loading