diff --git a/wled00/FX_fcn.cpp b/wled00/FX_fcn.cpp index 42e98452..edd3a068 100644 --- a/wled00/FX_fcn.cpp +++ b/wled00/FX_fcn.cpp @@ -538,6 +538,7 @@ bool Segment::setColor(uint8_t slot, uint32_t c) { //returns true if changed } void Segment::setCCT(uint16_t k) { + if (!isCCT() || !correctWB) return; if (k > 255) { //kelvin value, convert to 0-255 if (k < 1900) k = 1900; if (k > 10091) k = 10091; @@ -1162,12 +1163,16 @@ void WS2812FX::service() { uint16_t delay = FRAMETIME; if (!seg.freeze) { //only run effect function if not frozen + int16_t oldCCT = BusManager::getSegmentCCT(); // store original CCT value (actually it is not Segment based) _virtualSegmentLength = seg.virtualLength(); //SEGLEN _colors_t[0] = gamma32(seg.currentColor(0)); _colors_t[1] = gamma32(seg.currentColor(1)); _colors_t[2] = gamma32(seg.currentColor(2)); seg.currentPalette(_currentPalette, seg.palette); // we need to pass reference - if (!cctFromRgb || correctWB) BusManager::setSegmentCCT(seg.currentBri(true), correctWB); + // when correctWB is true we need to correct/adjust RGB value according to desired CCT value, but it will also affect actual WW/CW ratio + // when cctFromRgb is true we implicitly calculate WW and CW from RGB values + if (cctFromRgb) BusManager::setSegmentCCT(-1); + else BusManager::setSegmentCCT(seg.currentBri(true), correctWB); // Effect blending // When two effects are being blended, each may have different segment data, this // data needs to be saved first and then restored before running previous mode. @@ -1190,6 +1195,7 @@ void WS2812FX::service() { #endif seg.call++; if (seg.isInTransition() && delay > FRAMETIME) delay = FRAMETIME; // force faster updates during transition + BusManager::setSegmentCCT(oldCCT); // restore old CCT for ABL adjustments } seg.next_time = nowUp + delay; @@ -1198,7 +1204,6 @@ void WS2812FX::service() { _segment_index++; } _virtualSegmentLength = 0; - BusManager::setSegmentCCT(-1); _isServicing = false; _triggered = false; @@ -1390,11 +1395,7 @@ bool WS2812FX::hasCCTBus(void) { for (size_t b = 0; b < BusManager::getNumBusses(); b++) { Bus *bus = BusManager::getBus(b); if (bus == nullptr || bus->getLength()==0) break; - switch (bus->getType()) { - case TYPE_ANALOG_5CH: - case TYPE_ANALOG_2CH: - return true; - } + if (bus->hasCCT()) return true; } return false; } diff --git a/wled00/bus_manager.cpp b/wled00/bus_manager.cpp index 6fd55bb5..a9c3ac44 100644 --- a/wled00/bus_manager.cpp +++ b/wled00/bus_manager.cpp @@ -11,7 +11,6 @@ //colors.cpp uint32_t colorBalanceFromKelvin(uint16_t kelvin, uint32_t rgb); -void colorRGBtoRGBW(byte* rgb); //udp.cpp uint8_t realtimeBroadcast(uint8_t type, IPAddress client, uint16_t length, byte *buffer, uint8_t bri=255, bool isRGBW=false); @@ -122,7 +121,7 @@ BusDigital::BusDigital(BusConfig &bc, uint8_t nr, const ColorOrderMap &com) } _iType = PolyBus::getI(bc.type, _pins, nr); if (_iType == I_NONE) return; - if (bc.doubleBuffer && !allocData(bc.count * (Bus::hasWhite(_type) + 3*Bus::hasRGB(_type)))) return; //warning: hardcoded channel count + if (bc.doubleBuffer && !allocData(bc.count * Bus::getNumberOfChannels(bc.type))) return; //_buffering = bc.doubleBuffer; uint16_t lenToCreate = bc.count; if (bc.type == TYPE_WS2812_1CH_X3) lenToCreate = NUM_ICS_WS2812_1CH_3X(bc.count); // only needs a third of "RGB" LEDs for NeoPixelBus @@ -210,9 +209,10 @@ void BusDigital::show() { if (newBri < _bri) PolyBus::setBrightness(_busPtr, _iType, newBri); // limit brightness to stay within current limits if (_data) { - size_t channels = Bus::hasWhite(_type) + 3*Bus::hasRGB(_type); + size_t channels = getNumberOfChannels(); + int16_t oldCCT = _cct; // temporarily save bus CCT for (size_t i=0; i<_len; i++) { - size_t offset = i*channels; + size_t offset = i * channels; uint8_t co = _colorOrderMap.getPixelColorOrder(i+_start, _colorOrder); uint32_t c; if (_type == TYPE_WS2812_1CH_X3) { // map to correct IC, each controls 3 LEDs (_len is always a multiple of 3) @@ -222,18 +222,26 @@ void BusDigital::show() { case 2: c = RGBW32(_data[offset-2], _data[offset-1], _data[offset] , 0); break; } } else { - c = RGBW32(_data[offset],_data[offset+1],_data[offset+2],(Bus::hasWhite(_type)?_data[offset+3]:0)); + if (hasRGB()) c = RGBW32(_data[offset], _data[offset+1], _data[offset+2], hasWhite() ? _data[offset+3] : 0); + else c = RGBW32(0, 0, 0, _data[offset]); + } + if (hasCCT()) { + // unfortunately as a segment may span multiple buses or a bus may contain multiple segments and each segment may have different CCT + // we need to extract and appy CCT value for each pixel individually even though all buses share the same _cct variable + // TODO: there is an issue if CCT is calculated from RGB value (_cct==-1), we cannot do that with double buffer + _cct = _data[offset+channels-1]; + Bus::calculateCCT(c, cctWW, cctCW); } uint16_t pix = i; if (_reversed) pix = _len - pix -1; pix += _skip; - if (_type == TYPE_FW1906) Bus::calculateCCT(c, cctWW, cctCW); PolyBus::setPixelColor(_busPtr, _iType, pix, c, co, (cctCW<<8) | cctWW); } #if !defined(STATUSLED) || STATUSLED>=0 if (_skip) PolyBus::setPixelColor(_busPtr, _iType, 0, 0, _colorOrderMap.getPixelColorOrder(_start, _colorOrder)); // paint skipped pixels black #endif for (int i=1; i<_skip; i++) PolyBus::setPixelColor(_busPtr, _iType, i, 0, _colorOrderMap.getPixelColorOrder(_start, _colorOrder)); // paint skipped pixels black + _cct = oldCCT; } else { if (newBri < _bri) { uint16_t hwLen = _len; @@ -241,7 +249,7 @@ void BusDigital::show() { for (unsigned i = 0; i < hwLen; i++) { // use 0 as color order, actual order does not matter here as we just update the channel values as-is uint32_t c = restoreColorLossy(PolyBus::getPixelColor(_busPtr, _iType, i, 0), _bri); - if (_type == TYPE_FW1906) Bus::calculateCCT(c, cctWW, cctCW); + if (hasCCT()) Bus::calculateCCT(c, cctWW, cctCW); // this will unfortunately corrupt (segment) CCT data on every bus PolyBus::setPixelColor(_busPtr, _iType, i, c, 0, (cctCW<<8) | cctWW); // repaint all pixels with new brightness } } @@ -282,17 +290,19 @@ void BusDigital::setStatusPixel(uint32_t c) { void IRAM_ATTR BusDigital::setPixelColor(uint16_t pix, uint32_t c) { if (!_valid) return; uint8_t cctWW = 0, cctCW = 0; - if (Bus::hasWhite(_type)) c = autoWhiteCalc(c); + if (hasWhite()) c = autoWhiteCalc(c); if (_cct >= 1900) c = colorBalanceFromKelvin(_cct, c); //color correction from CCT if (_data) { - size_t channels = Bus::hasWhite(_type) + 3*Bus::hasRGB(_type); - size_t offset = pix*channels; - if (Bus::hasRGB(_type)) { + size_t offset = pix * getNumberOfChannels(); + if (hasRGB()) { _data[offset++] = R(c); _data[offset++] = G(c); _data[offset++] = B(c); } - if (Bus::hasWhite(_type)) _data[offset] = W(c); + if (hasWhite()) _data[offset++] = W(c); + // unfortunately as a segment may span multiple buses or a bus may contain multiple segments and each segment may have different CCT + // we need to store CCT value for each pixel (if there is a color correction in play, convert K in CCT ratio) + if (hasCCT()) _data[offset] = _cct >= 1900 ? (_cct - 1900) >> 5 : (_cct < 0 ? 127 : _cct); // TODO: if _cct == -1 we simply ignore it } else { if (_reversed) pix = _len - pix -1; pix += _skip; @@ -307,7 +317,7 @@ void IRAM_ATTR BusDigital::setPixelColor(uint16_t pix, uint32_t c) { case 2: c = RGBW32(R(cOld), G(cOld), W(c) , 0); break; } } - if (_type == TYPE_FW1906) Bus::calculateCCT(c, cctWW, cctCW); + if (hasCCT()) Bus::calculateCCT(c, cctWW, cctCW); PolyBus::setPixelColor(_busPtr, _iType, pix, c, co, (cctCW<<8) | cctWW); } } @@ -316,13 +326,12 @@ void IRAM_ATTR BusDigital::setPixelColor(uint16_t pix, uint32_t c) { uint32_t IRAM_ATTR BusDigital::getPixelColor(uint16_t pix) { if (!_valid) return 0; if (_data) { - size_t channels = Bus::hasWhite(_type) + 3*Bus::hasRGB(_type); - size_t offset = pix*channels; + size_t offset = pix * getNumberOfChannels(); uint32_t c; - if (!Bus::hasRGB(_type)) { + if (!hasRGB()) { c = RGBW32(_data[offset], _data[offset], _data[offset], _data[offset]); } else { - c = RGBW32(_data[offset], _data[offset+1], _data[offset+2], Bus::hasWhite(_type) ? _data[offset+3] : 0); + c = RGBW32(_data[offset], _data[offset+1], _data[offset+2], hasWhite() ? _data[offset+3] : 0); } return c; } else { @@ -640,12 +649,10 @@ uint32_t BusManager::memUsage(BusConfig &bc) { if (bc.type == TYPE_ONOFF || IS_PWM(bc.type)) return 5; uint16_t len = bc.count + bc.skipAmount; - uint16_t channels = 3; + uint16_t channels = Bus::getNumberOfChannels(bc.type); uint16_t multiplier = 1; if (IS_DIGITAL(bc.type)) { // digital types if (IS_16BIT(bc.type)) len *= 2; // 16-bit LEDs - if (bc.type > 28) channels = 4; //RGBW - if (bc.type == TYPE_FW1906) channels = 5; //GRBCW #ifdef ESP8266 if (bc.pins[0] == 3) { //8266 DMA uses 5x the mem multiplier = 5; @@ -654,11 +661,6 @@ uint32_t BusManager::memUsage(BusConfig &bc) { multiplier = 2; #endif } - if (IS_VIRTUAL(bc.type)) { - switch (bc.type) { - case TYPE_NET_DDP_RGBW: channels = 4; break; - } - } return len * channels * multiplier; //RGB } @@ -720,7 +722,7 @@ void BusManager::setSegmentCCT(int16_t cct, bool allowWBCorrection) { if (cct >= 0) { //if white balance correction allowed, save as kelvin value instead of 0-255 if (allowWBCorrection) cct = 1900 + (cct << 5); - } else cct = -1; + } else cct = -1; // will use kelvin approximation from RGB Bus::setCCT(cct); } @@ -764,4 +766,4 @@ uint8_t BusManager::numBusses = 0; Bus* BusManager::busses[WLED_MAX_BUSSES+WLED_MIN_VIRTUAL_BUSSES]; ColorOrderMap BusManager::colorOrderMap = {}; uint16_t BusManager::_milliAmpsUsed = 0; -uint16_t BusManager::_milliAmpsMax = ABL_MILLIAMPS_DEFAULT; +uint16_t BusManager::_milliAmpsMax = ABL_MILLIAMPS_DEFAULT; \ No newline at end of file diff --git a/wled00/bus_manager.h b/wled00/bus_manager.h index 6b8e9d7e..233c2a66 100644 --- a/wled00/bus_manager.h +++ b/wled00/bus_manager.h @@ -141,6 +141,8 @@ class Bus { virtual uint16_t getLEDCurrent() { return 0; } virtual uint16_t getUsedCurrent() { return 0; } virtual uint16_t getMaxCurrent() { return 0; } + virtual uint8_t getNumberOfChannels() { return hasWhite(_type) + 3*hasRGB(_type) + hasCCT(_type); } + static inline uint8_t getNumberOfChannels(uint8_t type) { return hasWhite(type) + 3*hasRGB(type) + hasCCT(type); } inline void setReversed(bool reversed) { _reversed = reversed; } inline uint16_t getStart() { return _start; } inline void setStart(uint16_t start) { _start = start; } @@ -157,9 +159,10 @@ class Bus { } virtual bool hasWhite(void) { return Bus::hasWhite(_type); } static bool hasWhite(uint8_t type) { - if ((type >= TYPE_WS2812_1CH && type <= TYPE_WS2812_WWA) || type == TYPE_SK6812_RGBW || type == TYPE_TM1814 || type == TYPE_UCS8904 || type == TYPE_FW1906) return true; // digital types with white channel + if ((type >= TYPE_WS2812_1CH && type <= TYPE_WS2812_WWA) || + type == TYPE_SK6812_RGBW || type == TYPE_TM1814 || type == TYPE_UCS8904 || type == TYPE_FW1906) return true; // digital types with white channel if (type > TYPE_ONOFF && type <= TYPE_ANALOG_5CH && type != TYPE_ANALOG_3CH) return true; // analog types with white channel - if (type == TYPE_NET_DDP_RGBW) return true; // network types with white channel + if (type == TYPE_NET_DDP_RGBW || type == TYPE_NET_ARTNET_RGBW) return true; // network types with white channel return false; } virtual bool hasCCT(void) { return Bus::hasCCT(_type); } @@ -168,7 +171,8 @@ class Bus { type == TYPE_ANALOG_2CH || type == TYPE_ANALOG_5CH || type == TYPE_FW1906) return true; return false; } - static void setCCT(uint16_t cct) { + static int16_t getCCT() { return _cct; } + static void setCCT(int16_t cct) { _cct = cct; } static void setCCTBlend(uint8_t b) { @@ -196,8 +200,7 @@ class Bus { #else //0 - linear (CCT 127 = 50% warm, 50% cold), 127 - additive CCT blending (CCT 127 = 100% warm, 100% cold) if (cct < _cctBlend) ww = 255; - else ww = ((255-cct) * 255) / (255 - _cctBlend); - + else ww = ((255-cct) * 255) / (255 - _cctBlend); if ((255-cct) < _cctBlend) cw = 255; else cw = (cct * 255) / (255 - _cctBlend); @@ -220,8 +223,17 @@ class Bus { bool _needsRefresh; uint8_t _autoWhiteMode; uint8_t *_data; + // global Auto White Calculation override static uint8_t _gAWM; + // _cct has the following menaings (see calculateCCT() & BusManager::setSegmentCCT()): + // -1 means to extract approximate CCT value in K from RGB (in calcualteCCT()) + // [0,255] is the exact CCT value where 0 means warm and 255 cold + // [1900,10060] only for color correction expressed in K (colorBalanceFromKelvin()) static int16_t _cct; + // _cctBlend determines WW/CW blending: + // 0 - linear (CCT 127 => 50% warm, 50% cold) + // 63 - semi additive/nonlinear (CCT 127 => 66% warm, 66% cold) + // 127 - additive CCT blending (CCT 127 => 100% warm, 100% cold) static uint8_t _cctBlend; uint32_t autoWhiteCalc(uint32_t c); @@ -363,9 +375,12 @@ class BusManager { static void setStatusPixel(uint32_t c); static void setPixelColor(uint16_t pix, uint32_t c); static void setBrightness(uint8_t b); + // for setSegmentCCT(), cct can only be in [-1,255] range; allowWBCorrection will convert it to K + // WARNING: setSegmentCCT() is a misleading name!!! much better would be setGlobalCCT() or just setCCT() static void setSegmentCCT(int16_t cct, bool allowWBCorrection = false); static void setMilliampsMax(uint16_t max) { _milliAmpsMax = max;} static uint32_t getPixelColor(uint16_t pix); + static inline int16_t getSegmentCCT() { return Bus::getCCT(); } static Bus* getBus(uint8_t busNr);