CCT bus manager logic simplification

CCT from RGB if none set (-1)
This commit is contained in:
cschwinne 2021-11-24 11:02:25 +01:00
parent b3324d22f5
commit ea0f37f5b9
13 changed files with 897 additions and 880 deletions

View File

@ -165,12 +165,12 @@
#define FX_MODE_FIRE_FLICKER 45
#define FX_MODE_GRADIENT 46
#define FX_MODE_LOADING 47
#define FX_MODE_POLICE 48
#define FX_MODE_POLICE 48 // candidate for removal (after below three)
#define FX_MODE_POLICE_ALL 49 // candidate for removal
#define FX_MODE_TWO_DOTS 50
#define FX_MODE_TWO_AREAS 51
#define FX_MODE_TWO_AREAS 51 // candidate for removal
#define FX_MODE_RUNNING_DUAL 52
#define FX_MODE_HALLOWEEN 53
#define FX_MODE_HALLOWEEN 53 // candidate for removal
#define FX_MODE_TRICOLOR_CHASE 54
#define FX_MODE_TRICOLOR_WIPE 55
#define FX_MODE_TRICOLOR_FADE 56
@ -247,7 +247,7 @@ class WS2812FX {
// segment parameters
public:
typedef struct Segment { // 30 (32 in memory) bytes
typedef struct Segment { // 31 (32 in memory) bytes
uint16_t start;
uint16_t stop; //segment invalid if stop == 0
uint16_t offset;
@ -259,7 +259,7 @@ class WS2812FX {
uint8_t grouping, spacing;
uint8_t opacity;
uint32_t colors[NUM_COLORS];
uint8_t cct; //0==2000K, 255==10160K
int16_t cct; //-1==auto (no RGB balance correction), 0==1900K, 255==10091K
char *name;
bool setColor(uint8_t slot, uint32_t c, uint8_t segn) { //returns true if changed
if (slot >= NUM_COLORS || segn >= MAX_NUM_SEGMENTS) return false;

View File

@ -138,6 +138,7 @@ void WS2812FX::service() {
if (!SEGMENT.getOption(SEG_OPTION_FREEZE)) { //only run effect function if not frozen
_virtualSegmentLength = SEGMENT.virtualLength();
busses.setSegmentCCT(SEGMENT.cct, correctWB);
_bri_t = SEGMENT.opacity; _colors_t[0] = SEGMENT.colors[0]; _colors_t[1] = SEGMENT.colors[1]; _colors_t[2] = SEGMENT.colors[2];
if (!IS_SEGMENT_ON) _bri_t = 0;
for (uint8_t t = 0; t < MAX_NUM_TRANSITIONS; t++) {
@ -156,6 +157,7 @@ void WS2812FX::service() {
}
}
_virtualSegmentLength = 0;
busses.setSegmentCCT(-1);
if(doShow) {
yield();
show();
@ -191,21 +193,6 @@ void WS2812FX::setPixelColor(uint16_t i, byte r, byte g, byte b, byte w)
uint16_t realIndex = realPixelIndex(i);
uint16_t len = SEGMENT.length();
// determine if we can do white balance and accurate W calc
// NOTE & TODO: does not work correctly with custom mapping if map spans different strips
int16_t cct = -1;
for (uint8_t b = 0; b < busses.getNumBusses(); b++) {
Bus *bus = busses.getBus(b);
if (bus == nullptr || !bus->containsPixel(realIndex)) continue;
//if (bus == nullptr || bus->getStart()<realIndex || bus->getStart()+bus->getLength()>realIndex) continue;
uint8_t busType = bus->getType();
if (allowCCT
|| busType == TYPE_ANALOG_2CH
|| busType == TYPE_ANALOG_5CH) {
if (cct<0) cct = SEGMENT.cct;
}
}
//color_blend(getpixel, col, _bri_t); (pseudocode for future blending of segments)
if (_bri_t < 255) {
r = scale8(r, _bri_t);
@ -226,14 +213,14 @@ void WS2812FX::setPixelColor(uint16_t i, byte r, byte g, byte b, byte w)
if (indexMir >= SEGMENT.stop) indexMir -= len;
if (indexMir < customMappingSize) indexMir = customMappingTable[indexMir];
busses.setPixelColor(indexMir, col, cct);
busses.setPixelColor(indexMir, col);
}
/* offset/phase */
indexSet += SEGMENT.offset;
if (indexSet >= SEGMENT.stop) indexSet -= len;
if (indexSet < customMappingSize) indexSet = customMappingTable[indexSet];
busses.setPixelColor(indexSet, col, cct);
busses.setPixelColor(indexSet, col);
}
}
} else { //live data, etc.
@ -618,7 +605,7 @@ void WS2812FX::resetSegments() {
_segments[0].setOption(SEG_OPTION_SELECTED, 1);
_segments[0].setOption(SEG_OPTION_ON, 1);
_segments[0].opacity = 255;
_segments[0].cct = 128;
_segments[0].cct = -1;
for (uint16_t i = 1; i < MAX_NUM_SEGMENTS; i++)
{
@ -626,7 +613,7 @@ void WS2812FX::resetSegments() {
_segments[i].grouping = 1;
_segments[i].setOption(SEG_OPTION_ON, 1);
_segments[i].opacity = 255;
_segments[i].cct = 128;
_segments[i].cct = -1;
_segments[i].speed = DEFAULT_SPEED;
_segments[i].intensity = DEFAULT_INTENSITY;
_segment_runtimes[i].reset();
@ -1162,4 +1149,5 @@ uint32_t WS2812FX::gamma32(uint32_t color)
return RGBW32(r, g, b, w);
}
WS2812FX* WS2812FX::instance = nullptr;
WS2812FX* WS2812FX::instance = nullptr;
int16_t Bus::_cct = -1;

View File

@ -74,7 +74,7 @@ struct BusConfig {
}
};
//parent class of BusDigital and BusPwm
//parent class of BusDigital, BusPwm, and BusNetwork
class Bus {
public:
Bus(uint8_t type, uint16_t start, uint8_t aw) {
@ -88,7 +88,6 @@ class Bus {
virtual void show() {}
virtual bool canShow() { return true; }
virtual void setPixelColor(uint16_t pix, uint32_t c) {};
virtual void setPixelColor(uint16_t pix, uint32_t c, uint8_t cct) {};
virtual uint32_t getPixelColor(uint16_t pix) { return 0; };
virtual void setBrightness(uint8_t b) {};
virtual void cleanup() {};
@ -111,6 +110,9 @@ class Bus {
if (type > TYPE_ONOFF && type <= TYPE_ANALOG_5CH && type != TYPE_ANALOG_3CH) return true;
return false;
}
static void setCCT(uint16_t cct) {
_cct = cct;
}
bool reversed = false;
@ -122,16 +124,18 @@ class Bus {
bool _valid = false;
bool _needsRefresh = false;
uint8_t _autoWhiteMode = 0;
static int16_t _cct;
uint32_t autoWhiteCalc(uint32_t c) {
if (_autoWhiteMode == RGBW_MODE_MANUAL_ONLY) return c;
uint8_t w = W(c);
//ignore auto-white calculation if w>0 and mode DUAL (DUAL behaves as BRIGHTER if w==0)
if (w > 0 && _autoWhiteMode == RGBW_MODE_DUAL) return c;
uint8_t r = R(c);
uint8_t g = G(c);
uint8_t b = B(c);
uint8_t w = W(c);
// ignore auto-white calculation if w>0 and mode DUAL (DUAL behaves as BRIGHTER if w==0)
if (!(w > 0 && _autoWhiteMode == RGBW_MODE_DUAL)) w = r < g ? (r < b ? r : b) : (g < b ? g : b);
if (_autoWhiteMode == RGBW_MODE_AUTO_ACCURATE) { r -= w; g -= w; b -= w; } // subtract w in ACCURATE mode
w = r < g ? (r < b ? r : b) : (g < b ? g : b);
if (_autoWhiteMode == RGBW_MODE_AUTO_ACCURATE) { r -= w; g -= w; b -= w; } //subtract w in ACCURATE mode
return RGBW32(r, g, b, w);
}
};
@ -181,17 +185,13 @@ class BusDigital : public Bus {
}
void setPixelColor(uint16_t pix, uint32_t c) {
if (getAutoWhiteMode() != RGBW_MODE_MANUAL_ONLY) c = autoWhiteCalc(c);
c = autoWhiteCalc(c);
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);
}
void setPixelColor(uint16_t pix, uint32_t c, uint8_t cct) {
c = colorBalanceFromKelvin(2000+(cct<<5), c); // color correction from CCT
setPixelColor(pix, c);
}
uint32_t getPixelColor(uint16_t pix) {
if (reversed) pix = _len - pix -1;
else pix += _skip;
@ -282,22 +282,30 @@ class BusPwm : public Bus {
_valid = true;
};
void setPixelColor(uint16_t pix, uint32_t c, uint8_t cct) {
void setPixelColor(uint16_t pix, uint32_t c) {
if (pix != 0 || !_valid) return; //only react to first pixel
if (getAutoWhiteMode() != RGBW_MODE_MANUAL_ONLY) c = autoWhiteCalc(c);
c = colorBalanceFromKelvin(2000+(cct<<5), c); // color correction from CCT (w remains unchanged)
if (_type == TYPE_ANALOG_3CH && _cct >= 1900) {
c = colorBalanceFromKelvin(_cct, c); //color correction from CCT
}
c = autoWhiteCalc(c);
uint8_t r = R(c);
uint8_t g = G(c);
uint8_t b = B(c);
uint8_t w = W(c);
uint8_t cct = 0; //0 - full warm white, 255 - full cold white
if (_cct > -1) {
if (_cct >= 1900) cct = (_cct - 1900) >> 5;
else if (_cct < 256) cct = _cct;
} else {
cct = (approximateKelvinFromRGB(c) - 1900) >> 5;
}
switch (_type) {
case TYPE_ANALOG_1CH: //one channel (white), relies on auto white calculation
_data[0] = w; //max(r, max(g, max(b, w)));
_data[0] = w;
break;
case TYPE_ANALOG_2CH: //warm white + cold white
// perhaps a non-linear adjustment would be in order. need to test
//w = max(r, max(g, max(b, w)));
_data[1] = (w * cct) / 255;
_data[0] = (w * (255-cct)) / 255;
break;
@ -313,25 +321,6 @@ class BusPwm : public Bus {
}
}
void setPixelColor(uint16_t pix, uint32_t c) {
if (pix != 0 || !_valid) return; //only react to first pixel
if (getAutoWhiteMode() != RGBW_MODE_MANUAL_ONLY) c = autoWhiteCalc(c);
uint8_t r = R(c);
uint8_t g = G(c);
uint8_t b = B(c);
uint8_t w = W(c);
switch (_type) {
case TYPE_ANALOG_1CH: //one channel (white), use highest RGBW value
_data[0] = max(r, max(g, max(b, w))); break;
case TYPE_ANALOG_2CH: //warm white + cold white
case TYPE_ANALOG_3CH: //standard dumb RGB
case TYPE_ANALOG_4CH: //standard dumb RGBW
case TYPE_ANALOG_5CH: //we'll want the white handling from 2CH here + RGB
_data[0] = r; _data[1] = g; _data[2] = b; _data[3] = w; _data[4] = w; break;
}
}
//does no index check
uint32_t getPixelColor(uint16_t pix) {
if (!_valid) return 0;
@ -432,7 +421,8 @@ class BusNetwork : public Bus {
void setPixelColor(uint16_t pix, uint32_t c) {
if (!_valid || pix >= _len) return;
if (getAutoWhiteMode() != RGBW_MODE_MANUAL_ONLY) c = autoWhiteCalc(c);
if (_cct >= 1900) c = colorBalanceFromKelvin(_cct, c); //color correction from CCT
c = autoWhiteCalc(c);
uint16_t offset = pix * _UDPchannels;
_data[offset] = R(c);
_data[offset+1] = G(c);
@ -440,11 +430,6 @@ class BusNetwork : public Bus {
if (_rgbw) _data[offset+3] = W(c);
}
void setPixelColor(uint16_t pix, uint32_t c, uint8_t cct) {
c = colorBalanceFromKelvin(2000+(cct<<5), c); // color correction from CCT
setPixelColor(pix, c);
}
uint32_t getPixelColor(uint16_t pix) {
if (!_valid || pix >= _len) return 0;
uint16_t offset = pix * _UDPchannels;
@ -564,8 +549,7 @@ class BusManager {
Bus* b = busses[i];
uint16_t bstart = b->getStart();
if (pix < bstart || pix >= bstart + b->getLength()) continue;
if (cct<0) busses[i]->setPixelColor(pix - bstart, c); // no white balance
else busses[i]->setPixelColor(pix - bstart, c, cct); // do white balance
busses[i]->setPixelColor(pix - bstart, c);
}
}
@ -575,6 +559,18 @@ class BusManager {
}
}
void setSegmentCCT(int16_t cct, bool allowWBCorrection = false) {
if (cct > 255) { //kelvin value, convert to 0-255
if (cct < 1900) cct = 1900;
if (cct > 10091) cct = 10091;
if (!allowWBCorrection) cct = (cct - 1900) >> 5;
} else if (cct > -1) {
//if white balance correction allowed, save as kelvin value instead of 0-255
if (allowWBCorrection) cct = 1900 + (cct << 5);
} else cct = -1;
Bus::setCCT(cct);
}
uint32_t getPixelColor(uint16_t pix) {
for (uint8_t i = 0; i < numBusses; i++) {
Bus* b = busses[i];

View File

@ -80,7 +80,7 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
CJSON(strip.ablMilliampsMax, hw_led[F("maxpwr")]);
CJSON(strip.milliampsPerLed, hw_led[F("ledma")]);
uint8_t rgbwMode = hw_led[F("rgbwm")] | RGBW_MODE_DUAL; // use global setting (legacy)
CJSON(allowCCT, hw_led["cct"]);
CJSON(correctWB, hw_led["cct"]);
JsonArray ins = hw_led["ins"];
@ -520,7 +520,7 @@ void serializeConfig() {
hw_led[F("total")] = strip.getLengthTotal(); //no longer read, but provided for compatibility on downgrade
hw_led[F("maxpwr")] = strip.ablMilliampsMax;
hw_led[F("ledma")] = strip.milliampsPerLed;
hw_led["cct"] = allowCCT;
hw_led["cct"] = correctWB;
JsonArray hw_led_ins = hw_led.createNestedArray("ins");

View File

@ -245,19 +245,10 @@ void colorRGBtoRGBW(byte* rgb) //rgb to rgbw (http://codewelt.com/rgbw). (RGBW_M
}
*/
// adjust RGB values based on color temperature in K (range [2800-10200]) (https://en.wikipedia.org/wiki/Color_balance)
void colorBalanceFromKelvin(uint16_t kelvin, byte *rgb)
{
uint32_t col = RGBW32(rgb[0], rgb[1], rgb[2], 0);
col = colorBalanceFromKelvin(kelvin, col);
rgb[0] = R(col);
rgb[1] = G(col);
rgb[2] = B(col);
}
byte correctionRGB[4] = {0,0,0,0};
uint16_t lastKelvin = 0;
// adjust RGB values based on color temperature in K (range [2800-10200]) (https://en.wikipedia.org/wiki/Color_balance)
uint32_t colorBalanceFromKelvin(uint16_t kelvin, uint32_t rgb)
{
//remember so that slow colorKtoRGB() doesn't have to run for every setPixelColor()
@ -267,6 +258,45 @@ uint32_t colorBalanceFromKelvin(uint16_t kelvin, uint32_t rgb)
rgbw[0] = ((uint16_t) correctionRGB[0] * R(rgb)) /255; // correct R
rgbw[1] = ((uint16_t) correctionRGB[1] * G(rgb)) /255; // correct G
rgbw[2] = ((uint16_t) correctionRGB[2] * B(rgb)) /255; // correct B
rgbw[3] = W(rgb);
rgbw[3] = W(rgb);
return colorFromRgbw(rgbw);
}
//approximates a Kelvin color temperature from an RGB color.
//this does no check for the "whiteness" of the color,
//so should be used combined with a saturation check (as done by auto-white)
//values from http://www.vendian.org/mncharity/dir3/blackbody/UnstableURLs/bbr_color.html (10deg)
//equation spreadsheet at https://bit.ly/30RkHaN
//accuracy +-50K from 1900K up to 8000K
//minimum returned: 1900K, maximum returned: 10091K (range of 8192)
uint16_t approximateKelvinFromRGB(uint32_t rgb) {
//if not either red or blue is 255, color is dimmed. Scale up
uint8_t r = R(rgb), b = B(rgb);
if (r == b) return 6550; //red == blue at about 6600K (also can't go further if both R and B are 0)
if (r > b) {
//scale blue up as if red was at 255
uint16_t scale = 0xFFFF / r; //get scale factor (range 257-65535)
b = ((uint16_t)b * scale) >> 8;
//For all temps K<6600 R is bigger than B (for full bri colors R=255)
//-> Use 9 linear approximations for blackbody radiation blue values from 2000-6600K (blue is always 0 below 2000K)
if (b < 33) return 1900 + b *6;
if (b < 72) return 2100 + (b-33) *10;
if (b < 101) return 2492 + (b-72) *14;
if (b < 132) return 2900 + (b-101) *16;
if (b < 159) return 3398 + (b-132) *19;
if (b < 186) return 3906 + (b-159) *22;
if (b < 210) return 4500 + (b-186) *25;
if (b < 230) return 5100 + (b-210) *30;
return 5700 + (b-230) *34;
} else {
//scale red up as if blue was at 255
uint16_t scale = 0xFFFF / b; //get scale factor (range 257-65535)
r = ((uint16_t)r * scale) >> 8;
//For all temps K>6600 B is bigger than R (for full bri colors B=255)
//-> Use 2 linear approximations for blackbody radiation red values from 6600-10091K (blue is always 0 below 2000K)
if (r > 225) return 6600 + (254-r) *50;
uint16_t k = 8080 + (225-r) *86;
return (k > 10091) ? 10091 : k;
}
}

View File

@ -510,7 +510,9 @@ ${i+1}:
<hr style="width:260px">
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>
Allow WB correction: <input type="checkbox" name="CCT"> <br>
White Balance correction: <input type="checkbox" name="CCT"> <br>
Calculate CCT from RGB: TODO<br>
CCT blending mode: TODO<br>
<hr style="width:260px">
<div id="btns"></div>
Touch threshold: <input type="number" class="s" min="0" max="100" name="TT" required><br>

View File

@ -68,8 +68,8 @@ void colorRGBtoXY(byte* rgb, float* xy); // only defined if huesync disabled TOD
void colorFromDecOrHexString(byte* rgb, char* in);
bool colorFromHexString(byte* rgb, const char* in);
void colorBalanceFromKelvin(uint16_t kelvin, byte *rgb);
uint32_t colorBalanceFromKelvin(uint16_t kelvin, uint32_t rgb);
uint16_t approximateKelvinFromRGB(uint32_t rgb);
//dmx.cpp
void initDMX();

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

View File

@ -505,7 +505,7 @@ void serializeInfo(JsonObject root)
leds[F("count")] = strip.getLengthTotal();
leds[F("rgbw")] = strip.isRgbw;
leds[F("wv")] = false;
leds["cct"] = allowCCT;
leds["cct"] = correctWB;
for (uint8_t s = 0; s < busses.getNumBusses(); s++) {
Bus *bus = busses.getBus(s);
if (bus == nullptr || bus->getLength()==0) break;

View File

@ -95,7 +95,7 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
uint8_t pins[5] = {255, 255, 255, 255, 255};
autoSegments = request->hasArg(F("MS"));
allowCCT = request->hasArg(F("CCT"));
correctWB = request->hasArg(F("CCT"));
for (uint8_t s = 0; s < WLED_MAX_BUSSES; s++) {
char lp[4] = "L0"; lp[2] = 48+s; lp[3] = 0; //ascii 0-9 //strip data pin

View File

@ -8,7 +8,7 @@
*/
// version code in format yymmddb (b = daily build)
#define VERSION 2111170
#define VERSION 2111240
//uncomment this if you have a "my_config.h" file you'd like to use
//#define WLED_USE_MY_CONFIG
@ -269,7 +269,7 @@ WLED_GLOBAL byte bootPreset _INIT(0); // save preset to load
//if false, only one segment spanning the total LEDs is created,
//but not on LED settings save if there is more than one segment currently
WLED_GLOBAL bool autoSegments _INIT(false);
WLED_GLOBAL bool allowCCT _INIT(false); //CCT color correction
WLED_GLOBAL bool correctWB _INIT(false); //CCT color correction
WLED_GLOBAL byte col[] _INIT_N(({ 255, 160, 0, 0 })); // current RGB(W) primary color. col[] should be updated if you want to change the color.
WLED_GLOBAL byte colSec[] _INIT_N(({ 0, 0, 0, 0 })); // current RGB(W) secondary color

View File

@ -370,7 +370,7 @@ void getSettingsJS(byte subPage, char* dest)
oappend(SET_F(");"));
sappend('c',SET_F("MS"),autoSegments);
sappend('c',SET_F("CCT"),allowCCT);
sappend('c',SET_F("CCT"),correctWB);
for (uint8_t s=0; s < busses.getNumBusses(); s++) {
Bus* bus = busses.getBus(s);