From af3f27feaec79a18979cae3043d359c2bda1fdab Mon Sep 17 00:00:00 2001 From: Robert Date: Mon, 17 Jul 2023 20:54:24 +0200 Subject: [PATCH 1/5] Move CTT calculation from BusPwm to Bus class. This enable it to other bus types. --- wled00/bus_manager.cpp | 29 +++++------------------------ wled00/bus_manager.h | 29 +++++++++++++++++++++++++++++ 2 files changed, 34 insertions(+), 24 deletions(-) diff --git a/wled00/bus_manager.cpp b/wled00/bus_manager.cpp index 3ac12c04e..dd88bf34f 100644 --- a/wled00/bus_manager.cpp +++ b/wled00/bus_manager.cpp @@ -11,7 +11,7 @@ //colors.cpp uint32_t colorBalanceFromKelvin(uint16_t kelvin, uint32_t rgb); -uint16_t approximateKelvinFromRGB(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); @@ -421,29 +421,10 @@ void BusPwm::setPixelColor(uint16_t pix, uint32_t 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; - } - + uint8_t ww, cw; - #ifdef WLED_USE_IC_CCT - ww = w; - cw = cct; - #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); - - if ((255-cct) < _cctBlend) cw = 255; - else cw = (cct * 255) / (255 - _cctBlend); - - ww = (w * ww) / 255; //brightness scaling - cw = (w * cw) / 255; - #endif + + calculateCCT(c, ww, cw); switch (_type) { case TYPE_ANALOG_1CH: //one channel (white), relies on auto white calculation @@ -784,4 +765,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; \ No newline at end of file +uint16_t BusManager::_milliAmpsMax = ABL_MILLIAMPS_DEFAULT; diff --git a/wled00/bus_manager.h b/wled00/bus_manager.h index 0b791adf3..d0d40f4f1 100644 --- a/wled00/bus_manager.h +++ b/wled00/bus_manager.h @@ -7,6 +7,9 @@ #include "const.h" +//colors.cpp +uint16_t approximateKelvinFromRGB(uint32_t rgb); + #define GET_BIT(var,bit) (((var)>>(bit))&0x01) #define SET_BIT(var,bit) ((var)|=(uint16_t)(0x0001<<(bit))) #define UNSET_BIT(var,bit) ((var)&=(~(uint16_t)(0x0001<<(bit)))) @@ -176,6 +179,32 @@ class Bus { if (_cctBlend > WLED_MAX_CCT_BLEND) _cctBlend = WLED_MAX_CCT_BLEND; #endif } + static void calculateCCT(uint32_t c, uint8_t &ww, uint8_t &cw) { + uint8_t cct = 0; //0 - full warm white, 255 - full cold white + uint8_t w = byte(c >> 24); + + if (_cct > -1) { + if (_cct >= 1900) cct = (_cct - 1900) >> 5; + else if (_cct < 256) cct = _cct; + } else { + cct = (approximateKelvinFromRGB(c) - 1900) >> 5; + } + + #ifdef WLED_USE_IC_CCT + ww = w; + cw = cct; + #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); + + if ((255-cct) < _cctBlend) cw = 255; + else cw = (cct * 255) / (255 - _cctBlend); + + ww = (w * ww) / 255; //brightness scaling + cw = (w * cw) / 255; + #endif + } inline void setAutoWhiteMode(uint8_t m) { if (m < 5) _autoWhiteMode = m; } inline uint8_t getAutoWhiteMode() { return _autoWhiteMode; } inline static void setGlobalAWMode(uint8_t m) { if (m < 5) _gAWM = m; else _gAWM = AW_GLOBAL_DISABLED; } From c74db95c1430e2998eae99996c01f8b2de5dca9a Mon Sep 17 00:00:00 2001 From: Robert Date: Wed, 19 Jul 2023 09:22:29 +0200 Subject: [PATCH 2/5] Add FW1906 support --- platformio.ini | 2 +- wled00/bus_manager.cpp | 5 +- wled00/bus_manager.h | 4 +- wled00/bus_wrapper.h | 128 +++++++++++++++++++++++++++++++++- wled00/const.h | 1 + wled00/data/settings_leds.htm | 4 +- 6 files changed, 137 insertions(+), 7 deletions(-) diff --git a/platformio.ini b/platformio.ini index 6306595a2..40dceabc5 100644 --- a/platformio.ini +++ b/platformio.ini @@ -143,7 +143,7 @@ lib_compat_mode = strict lib_deps = fastled/FastLED @ 3.6.0 IRremoteESP8266 @ 2.8.2 - makuna/NeoPixelBus @ 2.7.5 + makuna/NeoPixelBus @ 2.7.8 https://github.com/Aircoookie/ESPAsyncWebServer.git @ ^2.1.0 # for I2C interface ;Wire diff --git a/wled00/bus_manager.cpp b/wled00/bus_manager.cpp index dd88bf34f..35538a78a 100644 --- a/wled00/bus_manager.cpp +++ b/wled00/bus_manager.cpp @@ -280,6 +280,7 @@ void IRAM_ATTR BusDigital::setPixelColor(uint16_t pix, uint32_t c) { if (!_valid) return; if (Bus::hasWhite(_type)) c = autoWhiteCalc(c); if (_cct >= 1900) c = colorBalanceFromKelvin(_cct, c); //color correction from CCT + if (_type == TYPE_FW1906) calculateCCT(c, PolyBus::cctWW, PolyBus::cctCW); // FW1906 ignores W component in c if (_data) { // use _buffering this causes ~20% FPS drop size_t channels = Bus::hasWhite(_type) + 3*Bus::hasRGB(_type); size_t offset = pix*channels; @@ -645,13 +646,13 @@ uint32_t BusManager::memUsage(BusConfig &bc) { uint16_t multiplier = 1; if (IS_DIGITAL(bc.type)) { // digital types if (IS_16BIT(bc.type)) len *= 2; // 16-bit LEDs - #ifdef ESP8266 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; } #else //ESP32 RMT uses double buffer, I2S uses 5x buffer - if (bc.type > 28) channels = 4; //RGBW multiplier = 2; #endif } diff --git a/wled00/bus_manager.h b/wled00/bus_manager.h index d0d40f4f1..6b8e9d7eb 100644 --- a/wled00/bus_manager.h +++ b/wled00/bus_manager.h @@ -157,7 +157,7 @@ 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) 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 return false; @@ -165,7 +165,7 @@ class Bus { virtual bool hasCCT(void) { return Bus::hasCCT(_type); } static bool hasCCT(uint8_t type) { if (type == TYPE_WS2812_2CH_X3 || type == TYPE_WS2812_WWA || - type == TYPE_ANALOG_2CH || type == TYPE_ANALOG_5CH) return true; + type == TYPE_ANALOG_2CH || type == TYPE_ANALOG_5CH || type == TYPE_FW1906) return true; return false; } static void setCCT(uint16_t cct) { diff --git a/wled00/bus_wrapper.h b/wled00/bus_wrapper.h index c63e055a8..e329246f1 100644 --- a/wled00/bus_wrapper.h +++ b/wled00/bus_wrapper.h @@ -2,6 +2,7 @@ #define BusWrapper_h #include "NeoPixelBusLg.h" +#include "bus_manager.h" // temporary - these defines should actually be set in platformio.ini // C3: I2S0 and I2S1 methods not supported (has one I2S bus) @@ -63,6 +64,11 @@ #define I_8266_U1_UCS_4 54 #define I_8266_DM_UCS_4 55 #define I_8266_BB_UCS_4 56 +//FW1906 GRBCW +#define I_8266_U0_FW6_5 66 +#define I_8266_U1_FW6_5 67 +#define I_8266_DM_FW6_5 68 +#define I_8266_BB_FW6_5 69 //ESP8266 APA106 #define I_8266_U0_APA106_3 81 #define I_8266_U1_APA106_3 82 @@ -104,12 +110,17 @@ #define I_32_RN_UCS_4 60 #define I_32_I0_UCS_4 61 #define I_32_I1_UCS_4 62 +//FW1906 GRBCW +#define I_32_RN_FW6_5 63 +#define I_32_I0_FW6_5 64 +#define I_32_I1_FW6_5 65 //Bit Bang theoratically possible, but very undesirable and not needed (no pin restrictions on RMT and I2S) #define I_32_RN_APA106_3 85 #define I_32_I0_APA106_3 86 #define I_32_I1_APA106_3 87 #define I_32_BB_APA106_3 88 // bitbangging on ESP32 not recommended + //APA102 #define I_HS_DOT_3 39 //hardware SPI #define I_SS_DOT_3 40 //soft SPI @@ -176,6 +187,11 @@ #define B_8266_U1_APA106_3 NeoPixelBusLg //3 chan, esp8266, gpio2 #define B_8266_DM_APA106_3 NeoPixelBusLg //3 chan, esp8266, gpio3 #define B_8266_BB_APA106_3 NeoPixelBusLg //3 chan, esp8266, bb (any pin but 16) +//FW1906 GRBCW +#define B_8266_U0_FW6_5 NeoPixelBusLg //esp8266, gpio1 +#define B_8266_U1_FW6_5 NeoPixelBusLg //esp8266, gpio2 +#define B_8266_DM_FW6_5 NeoPixelBusLg //esp8266, gpio3 +#define B_8266_BB_FW6_5 NeoPixelBusLg //esp8266, bb #endif /*** ESP32 Neopixel methods ***/ @@ -251,6 +267,14 @@ #define B_32_I1_APA106_3 NeoPixelBusLg #endif //#define B_32_BB_APA106_3 NeoPixelBusLg // NeoEsp8266BitBang800KbpsMethod +//FW1906 GRBCW +#define B_32_RN_FW6_5 NeoPixelBusLg +#ifndef WLED_NO_I2S0_PIXELBUS +#define B_32_I0_FW6_5 NeoPixelBusLg +#endif +#ifndef WLED_NO_I2S1_PIXELBUS +#define B_32_I1_FW6_5 NeoPixelBusLg +#endif #endif @@ -290,6 +314,12 @@ //handles pointer type conversion for all possible bus types class PolyBus { public: + // WW and CW components for chips that support them (FW1906). They have to be set before calling + // PolyBus::setPixelColor(). In such case W component has no meaning in setPixelColor() + // as there is no support for WW/CW API yet + // if both values are 0, W component *may* be used instead (for WW & CW) + static uint8_t cctWW, cctCW; + // initialize SPI bus speed for DotStar methods template static void beginDotStar(void* busPtr, int8_t sck, int8_t miso, int8_t mosi, int8_t ss, uint16_t clock_kHz = 0U) { @@ -353,6 +383,10 @@ class PolyBus { case I_8266_U1_APA106_3: (static_cast(busPtr))->Begin(); break; case I_8266_DM_APA106_3: (static_cast(busPtr))->Begin(); break; case I_8266_BB_APA106_3: (static_cast(busPtr))->Begin(); break; + case I_8266_U0_FW6_5: (static_cast(busPtr))->Begin(); break; + case I_8266_U1_FW6_5: (static_cast(busPtr))->Begin(); break; + case I_8266_DM_FW6_5: (static_cast(busPtr))->Begin(); break; + case I_8266_BB_FW6_5: (static_cast(busPtr))->Begin(); break; #endif #ifdef ARDUINO_ARCH_ESP32 case I_32_RN_NEO_3: (static_cast(busPtr))->Begin(); break; @@ -404,6 +438,14 @@ class PolyBus { #ifndef WLED_NO_I2S1_PIXELBUS case I_32_I1_UCS_4: (static_cast(busPtr))->Begin(); break; #endif + case I_32_RN_FW6_5: (static_cast(busPtr))->Begin(); break; + #ifndef WLED_NO_I2S0_PIXELBUS + case I_32_I0_FW6_5: (static_cast(busPtr))->Begin(); break; + #endif + #ifndef WLED_NO_I2S1_PIXELBUS + case I_32_I1_FW6_5: (static_cast(busPtr))->Begin(); break; + #endif + // case I_32_BB_UCS_4: (static_cast(busPtr))->Begin(); break; case I_32_RN_APA106_3: (static_cast(busPtr))->Begin(); break; #ifndef WLED_NO_I2S0_PIXELBUS @@ -465,6 +507,10 @@ class PolyBus { case I_8266_U1_APA106_3: busPtr = new B_8266_U1_APA106_3(len, pins[0]); break; case I_8266_DM_APA106_3: busPtr = new B_8266_DM_APA106_3(len, pins[0]); break; case I_8266_BB_APA106_3: busPtr = new B_8266_BB_APA106_3(len, pins[0]); break; + case I_8266_U0_FW6_5: busPtr = new B_8266_U0_FW6_5(len, pins[0]); break; + case I_8266_U1_FW6_5: busPtr = new B_8266_U1_FW6_5(len, pins[0]); break; + case I_8266_DM_FW6_5: busPtr = new B_8266_DM_FW6_5(len, pins[0]); break; + case I_8266_BB_FW6_5: busPtr = new B_8266_BB_FW6_5(len, pins[0]); break; #endif #ifdef ARDUINO_ARCH_ESP32 case I_32_RN_NEO_3: busPtr = new B_32_RN_NEO_3(len, pins[0], (NeoBusChannel)channel); break; @@ -525,6 +571,13 @@ class PolyBus { case I_32_I1_APA106_3: busPtr = new B_32_I1_APA106_3(len, pins[0]); break; #endif // case I_32_BB_APA106_3: busPtr = new B_32_BB_APA106_3(len, pins[0], (NeoBusChannel)channel); break; + case I_32_RN_FW6_5: busPtr = new B_32_RN_FW6_5(len, pins[0], (NeoBusChannel)channel); break; + #ifndef WLED_NO_I2S0_PIXELBUS + case I_32_I0_FW6_5: busPtr = new B_32_I0_FW6_5(len, pins[0]); break; + #endif + #ifndef WLED_NO_I2S1_PIXELBUS + case I_32_I1_FW6_5: busPtr = new B_32_I1_FW6_5(len, pins[0]); break; + #endif #endif // for 2-wire: pins[1] is clk, pins[0] is dat. begin expects (len, clk, dat) case I_HS_DOT_3: busPtr = new B_HS_DOT_3(len, pins[1], pins[0]); break; @@ -578,6 +631,10 @@ class PolyBus { case I_8266_U1_APA106_3: (static_cast(busPtr))->Show(consistent); break; case I_8266_DM_APA106_3: (static_cast(busPtr))->Show(consistent); break; case I_8266_BB_APA106_3: (static_cast(busPtr))->Show(consistent); break; + case I_8266_U0_FW6_5: (static_cast(busPtr))->Show(consistent); break; + case I_8266_U1_FW6_5: (static_cast(busPtr))->Show(consistent); break; + case I_8266_DM_FW6_5: (static_cast(busPtr))->Show(consistent); break; + case I_8266_BB_FW6_5: (static_cast(busPtr))->Show(consistent); break; #endif #ifdef ARDUINO_ARCH_ESP32 case I_32_RN_NEO_3: (static_cast(busPtr))->Show(consistent); break; @@ -638,6 +695,13 @@ class PolyBus { case I_32_I1_APA106_3: (static_cast(busPtr))->Show(consistent); break; #endif // case I_32_BB_APA106_3: (static_cast(busPtr))->Show(consistent); break; + case I_32_RN_FW6_5: (static_cast(busPtr))->Show(consistent); break; + #ifndef WLED_NO_I2S0_PIXELBUS + case I_32_I0_FW6_5: (static_cast(busPtr))->Show(consistent); break; + #endif + #ifndef WLED_NO_I2S1_PIXELBUS + case I_32_I1_FW6_5: (static_cast(busPtr))->Show(consistent); break; + #endif #endif case I_HS_DOT_3: (static_cast(busPtr))->Show(consistent); break; case I_SS_DOT_3: (static_cast(busPtr))->Show(consistent); break; @@ -687,6 +751,10 @@ class PolyBus { case I_8266_U1_APA106_3: return (static_cast(busPtr))->CanShow(); break; case I_8266_DM_APA106_3: return (static_cast(busPtr))->CanShow(); break; case I_8266_BB_APA106_3: return (static_cast(busPtr))->CanShow(); break; + case I_8266_U0_FW6_5: return (static_cast(busPtr))->CanShow(); break; + case I_8266_U1_FW6_5: return (static_cast(busPtr))->CanShow(); break; + case I_8266_DM_FW6_5: return (static_cast(busPtr))->CanShow(); break; + case I_8266_BB_FW6_5: return (static_cast(busPtr))->CanShow(); break; #endif #ifdef ARDUINO_ARCH_ESP32 case I_32_RN_NEO_3: return (static_cast(busPtr))->CanShow(); break; @@ -747,6 +815,13 @@ class PolyBus { case I_32_I1_APA106_3: return (static_cast(busPtr))->CanShow(); break; #endif // case I_32_BB_APA106_3: return (static_cast(busPtr))->CanShow(); break; + case I_32_RN_FW6_5: return (static_cast(busPtr))->CanShow(); break; + #ifndef WLED_NO_I2S0_PIXELBUS + case I_32_I0_FW6_5: return (static_cast(busPtr))->CanShow(); break; + #endif + #ifndef WLED_NO_I2S1_PIXELBUS + case I_32_I1_FW6_5: return (static_cast(busPtr))->CanShow(); break; + #endif #endif case I_HS_DOT_3: return (static_cast(busPtr))->CanShow(); break; case I_SS_DOT_3: return (static_cast(busPtr))->CanShow(); break; @@ -821,6 +896,10 @@ class PolyBus { case I_8266_U1_APA106_3: (static_cast(busPtr))->SetPixelColor(pix, RgbColor(col)); break; case I_8266_DM_APA106_3: (static_cast(busPtr))->SetPixelColor(pix, RgbColor(col)); break; case I_8266_BB_APA106_3: (static_cast(busPtr))->SetPixelColor(pix, RgbColor(col)); break; + case I_8266_U0_FW6_5: (static_cast(busPtr))->SetPixelColor(pix, RgbwwColor(col.R, col.G, col.B, cctWW, cctCW)); break; + case I_8266_U1_FW6_5: (static_cast(busPtr))->SetPixelColor(pix, RgbwwColor(col.R, col.G, col.B, cctWW, cctCW)); break; + case I_8266_DM_FW6_5: (static_cast(busPtr))->SetPixelColor(pix, RgbwwColor(col.R, col.G, col.B, cctWW, cctCW)); break; + case I_8266_BB_FW6_5: (static_cast(busPtr))->SetPixelColor(pix, RgbwwColor(col.R, col.G, col.B, cctWW, cctCW)); break; #endif #ifdef ARDUINO_ARCH_ESP32 case I_32_RN_NEO_3: (static_cast(busPtr))->SetPixelColor(pix, RgbColor(col)); break; @@ -881,6 +960,13 @@ class PolyBus { case I_32_I1_APA106_3: (static_cast(busPtr))->SetPixelColor(pix, RgbColor(col)); break; #endif // case I_32_BB_APA106_3: (static_cast(busPtr))->SetPixelColor(pix, RgbColor(col)); break; + case I_32_RN_FW6_5: (static_cast(busPtr))->SetPixelColor(pix, RgbwwColor(col.R, col.G, col.B, cctWW, cctCW)); break; + #ifndef WLED_NO_I2S0_PIXELBUS + case I_32_I0_FW6_5: (static_cast(busPtr))->SetPixelColor(pix, RgbwwColor(col.R, col.G, col.B, cctWW, cctCW)); break; + #endif + #ifndef WLED_NO_I2S1_PIXELBUS + case I_32_I1_FW6_5: (static_cast(busPtr))->SetPixelColor(pix, RgbwwColor(col.R, col.G, col.B, cctWW, cctCW)); break; + #endif #endif case I_HS_DOT_3: (static_cast(busPtr))->SetPixelColor(pix, RgbColor(col)); break; case I_SS_DOT_3: (static_cast(busPtr))->SetPixelColor(pix, RgbColor(col)); break; @@ -931,6 +1017,10 @@ class PolyBus { case I_8266_U1_APA106_3: (static_cast(busPtr))->SetLuminance(b); break; case I_8266_DM_APA106_3: (static_cast(busPtr))->SetLuminance(b); break; case I_8266_BB_APA106_3: (static_cast(busPtr))->SetLuminance(b); break; + case I_8266_U0_FW6_5: (static_cast(busPtr))->SetLuminance(b); break; + case I_8266_U1_FW6_5: (static_cast(busPtr))->SetLuminance(b); break; + case I_8266_DM_FW6_5: (static_cast(busPtr))->SetLuminance(b); break; + case I_8266_BB_FW6_5: (static_cast(busPtr))->SetLuminance(b); break; #endif #ifdef ARDUINO_ARCH_ESP32 case I_32_RN_NEO_3: (static_cast(busPtr))->SetLuminance(b); break; @@ -991,6 +1081,14 @@ class PolyBus { case I_32_I1_APA106_3: (static_cast(busPtr))->SetLuminance(b); break; #endif // case I_32_BB_APA106_3: (static_cast(busPtr))->SetLuminance(b); break; + case I_32_RN_FW6_5: (static_cast(busPtr))->SetLuminance(b); break; + #ifndef WLED_NO_I2S0_PIXELBUS + case I_32_I0_FW6_5: (static_cast(busPtr))->SetLuminance(b); break; + #endif + #ifndef WLED_NO_I2S1_PIXELBUS + case I_32_I1_FW6_5: (static_cast(busPtr))->SetLuminance(b); break; + #endif + #endif case I_HS_DOT_3: (static_cast(busPtr))->SetLuminance(b); break; case I_SS_DOT_3: (static_cast(busPtr))->SetLuminance(b); break; @@ -1042,6 +1140,10 @@ class PolyBus { case I_8266_U1_APA106_3: col = (static_cast(busPtr))->GetPixelColor(pix); break; case I_8266_DM_APA106_3: col = (static_cast(busPtr))->GetPixelColor(pix); break; case I_8266_BB_APA106_3: col = (static_cast(busPtr))->GetPixelColor(pix); break; + case I_8266_U0_FW6_5: { RgbwwColor c = (static_cast(busPtr))->GetPixelColor(pix); col = RGBW32(c.R>>8,c.G>>8,c.B>>8,c.WW>>8); } break; + case I_8266_U1_FW6_5: { RgbwwColor c = (static_cast(busPtr))->GetPixelColor(pix); col = RGBW32(c.R>>8,c.G>>8,c.B>>8,c.WW>>8); } break; + case I_8266_DM_FW6_5: { RgbwwColor c = (static_cast(busPtr))->GetPixelColor(pix); col = RGBW32(c.R>>8,c.G>>8,c.B>>8,c.WW>>8); } break; + case I_8266_BB_FW6_5: { RgbwwColor c = (static_cast(busPtr))->GetPixelColor(pix); col = RGBW32(c.R>>8,c.G>>8,c.B>>8,c.WW>>8); } break; #endif #ifdef ARDUINO_ARCH_ESP32 case I_32_RN_NEO_3: col = (static_cast(busPtr))->GetPixelColor(pix); break; @@ -1102,6 +1204,13 @@ class PolyBus { case I_32_I1_APA106_3: col = (static_cast(busPtr))->GetPixelColor(pix); break; #endif // case I_32_BB_APA106_3: col = (static_cast(busPtr))->GetPixelColor(pix); break; + case I_32_RN_FW6_5: { RgbwwColor c = (static_cast(busPtr))->GetPixelColor(pix); col = RGBW32(c.R>>8,c.G>>8,c.B>>8,c.WW>>8); } break; + #ifndef WLED_NO_I2S0_PIXELBUS + case I_32_I0_FW6_5: { RgbwwColor c = (static_cast(busPtr))->GetPixelColor(pix); col = RGBW32(c.R>>8,c.G>>8,c.B>>8,c.WW>>8); } break; + #endif + #ifndef WLED_NO_I2S1_PIXELBUS + case I_32_I1_FW6_5: { RgbwwColor c = (static_cast(busPtr))->GetPixelColor(pix); col = RGBW32(c.R>>8,c.G>>8,c.B>>8,c.WW>>8); } break; + #endif #endif case I_HS_DOT_3: col = (static_cast(busPtr))->GetPixelColor(pix); break; case I_SS_DOT_3: col = (static_cast(busPtr))->GetPixelColor(pix); break; @@ -1171,6 +1280,10 @@ class PolyBus { case I_8266_U1_APA106_3: delete (static_cast(busPtr)); break; case I_8266_DM_APA106_3: delete (static_cast(busPtr)); break; case I_8266_BB_APA106_3: delete (static_cast(busPtr)); break; + case I_8266_U0_FW6_5: delete (static_cast(busPtr)); break; + case I_8266_U1_FW6_5: delete (static_cast(busPtr)); break; + case I_8266_DM_FW6_5: delete (static_cast(busPtr)); break; + case I_8266_BB_FW6_5: delete (static_cast(busPtr)); break; #endif #ifdef ARDUINO_ARCH_ESP32 case I_32_RN_NEO_3: delete (static_cast(busPtr)); break; @@ -1231,6 +1344,13 @@ class PolyBus { case I_32_I1_APA106_3: delete (static_cast(busPtr)); break; #endif // case I_32_BB_APA106_3: delete (static_cast(busPtr)); break; + case I_32_RN_FW6_5: delete (static_cast(busPtr)); break; + #ifndef WLED_NO_I2S0_PIXELBUS + case I_32_I0_FW6_5: delete (static_cast(busPtr)); break; + #endif + #ifndef WLED_NO_I2S1_PIXELBUS + case I_32_I1_FW6_5: delete (static_cast(busPtr)); break; + #endif #endif case I_HS_DOT_3: delete (static_cast(busPtr)); break; case I_SS_DOT_3: delete (static_cast(busPtr)); break; @@ -1292,6 +1412,8 @@ class PolyBus { return I_8266_U0_UCS_4 + offset; case TYPE_APA106: return I_8266_U0_APA106_3 + offset; + case TYPE_FW1906: + return I_8266_U0_FW6_5 + offset; } #else //ESP32 uint8_t offset = 0; //0 = RMT (num 0-7) 8 = I2S0 9 = I2S1 @@ -1332,6 +1454,8 @@ class PolyBus { return I_32_RN_UCS_4 + offset; case TYPE_APA106: return I_32_RN_APA106_3 + offset; + case TYPE_FW1906: + return I_32_RN_FW6_5 + offset; } #endif } @@ -1339,4 +1463,6 @@ class PolyBus { } }; -#endif \ No newline at end of file +uint8_t PolyBus::cctWW; +uint8_t PolyBus::cctCW; +#endif diff --git a/wled00/const.h b/wled00/const.h index dd965bc40..6f4c2649e 100644 --- a/wled00/const.h +++ b/wled00/const.h @@ -269,6 +269,7 @@ #define TYPE_TM1829 25 #define TYPE_UCS8903 26 #define TYPE_APA106 27 +#define TYPE_FW1906 28 //RGB + CW + WW + unused channel (6 channels per IC) #define TYPE_UCS8904 29 //first RGBW digital type (hardcoded in busmanager.cpp, memUsage()) #define TYPE_SK6812_RGBW 30 #define TYPE_TM1814 31 diff --git a/wled00/data/settings_leds.htm b/wled00/data/settings_leds.htm index 611653a64..061d5a9ac 100644 --- a/wled00/data/settings_leds.htm +++ b/wled00/data/settings_leds.htm @@ -188,6 +188,7 @@ if (isDig(t)) { if (is16b(t)) len *= 2; // 16 bit LEDs if (t > 28 && t < 40) ch = 4; //RGBW + if (t == 28) ch = 5; //GRBCW if (maxM < 10000 && d.getElementsByName("L0"+n)[0].value == 3) { //8266 DMA uses 5x the mem mul = 5; } @@ -242,7 +243,7 @@ d.Sf["MA"+n].min = (isVir(t) || isAna(t)) ? 0 : 250; } gId("rf"+n).onclick = (t == 31) ? (()=>{return false}) : (()=>{}); // prevent change for TM1814 - gRGBW |= isRGBW = ((t > 17 && t < 22) || (t > 28 && t < 32) || (t > 40 && t < 46 && t != 43) || t == 88); // RGBW checkbox, TYPE_xxxx values from const.h + gRGBW |= isRGBW = ((t > 17 && t < 22) || (t > 27 && t < 32) || (t > 40 && t < 46 && t != 43) || t == 88); // RGBW checkbox, TYPE_xxxx values from const.h gId("co"+n).style.display = (isVir(t) || isAna(t)) ? "none":"inline"; // hide color order for PWM gId("dig"+n+"w").style.display = (isDig(t) && isRGBW) ? "inline":"none"; // show swap channels dropdown if (!(isDig(t) && isRGBW)) d.Sf["WO"+n].value = 0; // reset swapping @@ -383,6 +384,7 @@ ${i+1}: \ \ \ +\ \ \ \ From 505768db0469ccbb2c9a1cf786cc7de52e6a4130 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bla=C5=BE=20Kristan?= Date: Mon, 11 Mar 2024 11:17:45 +0100 Subject: [PATCH 3/5] Some fixes & implement recommendation from @Aircoookie https://github.com/Aircoookie/WLED/pull/3298#issuecomment-1651910418 --- wled00/bus_manager.cpp | 28 +++++++++++++--------------- wled00/bus_wrapper.h | 25 +++++++++---------------- 2 files changed, 22 insertions(+), 31 deletions(-) diff --git a/wled00/bus_manager.cpp b/wled00/bus_manager.cpp index 35538a78a..6fd55bb5d 100644 --- a/wled00/bus_manager.cpp +++ b/wled00/bus_manager.cpp @@ -205,10 +205,11 @@ void BusDigital::show() { _milliAmpsTotal = 0; if (!_valid) return; + uint8_t cctWW = 0, cctCW = 0; uint8_t newBri = estimateCurrentAndLimitBri(); // will fill _milliAmpsTotal if (newBri < _bri) PolyBus::setBrightness(_busPtr, _iType, newBri); // limit brightness to stay within current limits - if (_data) { // use _buffering this causes ~20% FPS drop + if (_data) { size_t channels = Bus::hasWhite(_type) + 3*Bus::hasRGB(_type); for (size_t i=0; i<_len; i++) { size_t offset = i*channels; @@ -226,7 +227,8 @@ void BusDigital::show() { uint16_t pix = i; if (_reversed) pix = _len - pix -1; pix += _skip; - PolyBus::setPixelColor(_busPtr, _iType, pix, c, co); + 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 @@ -239,7 +241,8 @@ 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); - PolyBus::setPixelColor(_busPtr, _iType, i, c, 0); // repaint all pixels with new brightness + if (_type == TYPE_FW1906) Bus::calculateCCT(c, cctWW, cctCW); + PolyBus::setPixelColor(_busPtr, _iType, i, c, 0, (cctCW<<8) | cctWW); // repaint all pixels with new brightness } } } @@ -278,10 +281,10 @@ 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 (_cct >= 1900) c = colorBalanceFromKelvin(_cct, c); //color correction from CCT - if (_type == TYPE_FW1906) calculateCCT(c, PolyBus::cctWW, PolyBus::cctCW); // FW1906 ignores W component in c - if (_data) { // use _buffering this causes ~20% FPS drop + if (_data) { size_t channels = Bus::hasWhite(_type) + 3*Bus::hasRGB(_type); size_t offset = pix*channels; if (Bus::hasRGB(_type)) { @@ -304,14 +307,15 @@ void IRAM_ATTR BusDigital::setPixelColor(uint16_t pix, uint32_t c) { case 2: c = RGBW32(R(cOld), G(cOld), W(c) , 0); break; } } - PolyBus::setPixelColor(_busPtr, _iType, pix, c, co); + if (_type == TYPE_FW1906) Bus::calculateCCT(c, cctWW, cctCW); + PolyBus::setPixelColor(_busPtr, _iType, pix, c, co, (cctCW<<8) | cctWW); } } // returns original color if global buffering is enabled, else returns lossly restored color from bus uint32_t IRAM_ATTR BusDigital::getPixelColor(uint16_t pix) { if (!_valid) return 0; - if (_data) { // use _buffering this causes ~20% FPS drop + if (_data) { size_t channels = Bus::hasWhite(_type) + 3*Bus::hasRGB(_type); size_t offset = pix*channels; uint32_t c; @@ -422,22 +426,16 @@ void BusPwm::setPixelColor(uint16_t pix, uint32_t c) { uint8_t g = G(c); uint8_t b = B(c); uint8_t w = W(c); - - uint8_t ww, cw; - - calculateCCT(c, ww, cw); switch (_type) { case TYPE_ANALOG_1CH: //one channel (white), relies on auto white calculation _data[0] = w; break; case TYPE_ANALOG_2CH: //warm white + cold white - _data[1] = cw; - _data[0] = ww; + Bus::calculateCCT(c, _data[0], _data[1]); break; case TYPE_ANALOG_5CH: //RGB + warm white + cold white - _data[4] = cw; - w = ww; + Bus::calculateCCT(c, w, _data[4]); case TYPE_ANALOG_4CH: //RGBW _data[3] = w; case TYPE_ANALOG_3CH: //standard dumb RGB diff --git a/wled00/bus_wrapper.h b/wled00/bus_wrapper.h index e329246f1..fafe3a460 100644 --- a/wled00/bus_wrapper.h +++ b/wled00/bus_wrapper.h @@ -314,11 +314,6 @@ //handles pointer type conversion for all possible bus types class PolyBus { public: - // WW and CW components for chips that support them (FW1906). They have to be set before calling - // PolyBus::setPixelColor(). In such case W component has no meaning in setPixelColor() - // as there is no support for WW/CW API yet - // if both values are 0, W component *may* be used instead (for WW & CW) - static uint8_t cctWW, cctCW; // initialize SPI bus speed for DotStar methods template @@ -837,12 +832,13 @@ class PolyBus { return true; } - static void setPixelColor(void* busPtr, uint8_t busType, uint16_t pix, uint32_t c, uint8_t co) { + static void setPixelColor(void* busPtr, uint8_t busType, uint16_t pix, uint32_t c, uint8_t co, uint16_t wwcw = 0) { uint8_t r = c >> 16; uint8_t g = c >> 8; uint8_t b = c >> 0; uint8_t w = c >> 24; RgbwColor col; + uint8_t cctWW = wwcw & 0xFF, cctCW = (wwcw>>8) & 0xFF; // reorder channels to selected order switch (co & 0x0F) { @@ -1140,10 +1136,10 @@ class PolyBus { case I_8266_U1_APA106_3: col = (static_cast(busPtr))->GetPixelColor(pix); break; case I_8266_DM_APA106_3: col = (static_cast(busPtr))->GetPixelColor(pix); break; case I_8266_BB_APA106_3: col = (static_cast(busPtr))->GetPixelColor(pix); break; - case I_8266_U0_FW6_5: { RgbwwColor c = (static_cast(busPtr))->GetPixelColor(pix); col = RGBW32(c.R>>8,c.G>>8,c.B>>8,c.WW>>8); } break; - case I_8266_U1_FW6_5: { RgbwwColor c = (static_cast(busPtr))->GetPixelColor(pix); col = RGBW32(c.R>>8,c.G>>8,c.B>>8,c.WW>>8); } break; - case I_8266_DM_FW6_5: { RgbwwColor c = (static_cast(busPtr))->GetPixelColor(pix); col = RGBW32(c.R>>8,c.G>>8,c.B>>8,c.WW>>8); } break; - case I_8266_BB_FW6_5: { RgbwwColor c = (static_cast(busPtr))->GetPixelColor(pix); col = RGBW32(c.R>>8,c.G>>8,c.B>>8,c.WW>>8); } break; + case I_8266_U0_FW6_5: { RgbwwColor c = (static_cast(busPtr))->GetPixelColor(pix); col = RGBW32(c.R,c.G,c.B,max(c.WW,c.CW)); } break; // will not return original W + case I_8266_U1_FW6_5: { RgbwwColor c = (static_cast(busPtr))->GetPixelColor(pix); col = RGBW32(c.R,c.G,c.B,max(c.WW,c.CW)); } break; // will not return original W + case I_8266_DM_FW6_5: { RgbwwColor c = (static_cast(busPtr))->GetPixelColor(pix); col = RGBW32(c.R,c.G,c.B,max(c.WW,c.CW)); } break; // will not return original W + case I_8266_BB_FW6_5: { RgbwwColor c = (static_cast(busPtr))->GetPixelColor(pix); col = RGBW32(c.R,c.G,c.B,max(c.WW,c.CW)); } break; // will not return original W #endif #ifdef ARDUINO_ARCH_ESP32 case I_32_RN_NEO_3: col = (static_cast(busPtr))->GetPixelColor(pix); break; @@ -1204,12 +1200,12 @@ class PolyBus { case I_32_I1_APA106_3: col = (static_cast(busPtr))->GetPixelColor(pix); break; #endif // case I_32_BB_APA106_3: col = (static_cast(busPtr))->GetPixelColor(pix); break; - case I_32_RN_FW6_5: { RgbwwColor c = (static_cast(busPtr))->GetPixelColor(pix); col = RGBW32(c.R>>8,c.G>>8,c.B>>8,c.WW>>8); } break; + case I_32_RN_FW6_5: { RgbwwColor c = (static_cast(busPtr))->GetPixelColor(pix); col = RGBW32(c.R,c.G,c.B,max(c.WW,c.CW)); } break; // will not return original W #ifndef WLED_NO_I2S0_PIXELBUS - case I_32_I0_FW6_5: { RgbwwColor c = (static_cast(busPtr))->GetPixelColor(pix); col = RGBW32(c.R>>8,c.G>>8,c.B>>8,c.WW>>8); } break; + case I_32_I0_FW6_5: { RgbwwColor c = (static_cast(busPtr))->GetPixelColor(pix); col = RGBW32(c.R,c.G,c.B,max(c.WW,c.CW)); } break; // will not return original W #endif #ifndef WLED_NO_I2S1_PIXELBUS - case I_32_I1_FW6_5: { RgbwwColor c = (static_cast(busPtr))->GetPixelColor(pix); col = RGBW32(c.R>>8,c.G>>8,c.B>>8,c.WW>>8); } break; + case I_32_I1_FW6_5: { RgbwwColor c = (static_cast(busPtr))->GetPixelColor(pix); col = RGBW32(c.R,c.G,c.B,max(c.WW,c.CW)); } break; // will not return original W #endif #endif case I_HS_DOT_3: col = (static_cast(busPtr))->GetPixelColor(pix); break; @@ -1462,7 +1458,4 @@ class PolyBus { return I_NONE; } }; - -uint8_t PolyBus::cctWW; -uint8_t PolyBus::cctCW; #endif From 0dcb56eab50d41bb015eeba6e709ed3330507ae7 Mon Sep 17 00:00:00 2001 From: Blaz Kristan Date: Sat, 16 Mar 2024 12:36:05 +0100 Subject: [PATCH 4/5] Modify Bus & BusManager to accommodate digital CCT - additional fix in hasWhite() & setCCT() & hasCCT() --- wled00/FX_fcn.cpp | 15 +++++------ wled00/bus_manager.cpp | 56 ++++++++++++++++++++++-------------------- wled00/bus_manager.h | 25 +++++++++++++++---- 3 files changed, 57 insertions(+), 39 deletions(-) diff --git a/wled00/FX_fcn.cpp b/wled00/FX_fcn.cpp index 42e98452f..edd3a068d 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 6fd55bb5d..a9c3ac44d 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 6b8e9d7eb..233c2a668 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); From 7a9eff7f35a51ceb0cae3f80fa0491e3497e7dc7 Mon Sep 17 00:00:00 2001 From: Blaz Kristan Date: Mon, 18 Mar 2024 20:23:30 +0100 Subject: [PATCH 5/5] Fix. --- wled00/FX_fcn.cpp | 1 - wled00/bus_manager.cpp | 9 +++++++++ wled00/bus_manager.h | 5 ----- 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/wled00/FX_fcn.cpp b/wled00/FX_fcn.cpp index edd3a068d..3566755f0 100644 --- a/wled00/FX_fcn.cpp +++ b/wled00/FX_fcn.cpp @@ -538,7 +538,6 @@ 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; diff --git a/wled00/bus_manager.cpp b/wled00/bus_manager.cpp index a9c3ac44d..eeb9a15e4 100644 --- a/wled00/bus_manager.cpp +++ b/wled00/bus_manager.cpp @@ -441,10 +441,19 @@ void BusPwm::setPixelColor(uint16_t pix, uint32_t c) { _data[0] = w; break; case TYPE_ANALOG_2CH: //warm white + cold white + #ifdef WLED_USE_IC_CCT + _data[0] = w; + _data[1] = cct; + #else Bus::calculateCCT(c, _data[0], _data[1]); + #endif break; case TYPE_ANALOG_5CH: //RGB + warm white + cold white + #ifdef WLED_USE_IC_CCT + _data[4] = cct; + #else Bus::calculateCCT(c, w, _data[4]); + #endif case TYPE_ANALOG_4CH: //RGBW _data[3] = w; case TYPE_ANALOG_3CH: //standard dumb RGB diff --git a/wled00/bus_manager.h b/wled00/bus_manager.h index 233c2a668..d4facb33b 100644 --- a/wled00/bus_manager.h +++ b/wled00/bus_manager.h @@ -194,10 +194,6 @@ class Bus { cct = (approximateKelvinFromRGB(c) - 1900) >> 5; } - #ifdef WLED_USE_IC_CCT - ww = w; - cw = cct; - #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); @@ -206,7 +202,6 @@ class Bus { ww = (w * ww) / 255; //brightness scaling cw = (w * cw) / 255; - #endif } inline void setAutoWhiteMode(uint8_t m) { if (m < 5) _autoWhiteMode = m; } inline uint8_t getAutoWhiteMode() { return _autoWhiteMode; }