diff --git a/wled00/bus_manager.cpp b/wled00/bus_manager.cpp index 4fa5c40a57..5673f2db62 100644 --- a/wled00/bus_manager.cpp +++ b/wled00/bus_manager.cpp @@ -186,11 +186,17 @@ BusDigital::BusDigital(const BusConfig &bc, uint8_t nr) _hasWhite = hasWhite(bc.type); _hasCCT = hasCCT(bc.type); uint16_t lenToCreate = bc.count; + if (bc.type == TYPE_SM16703_DUAL) lenToCreate = (bc.count + _skip) * 2; // two SM16703 chips per logical pixel (including skip) if (bc.type == TYPE_WS2812_1CH_X3) lenToCreate = NUM_ICS_WS2812_1CH_3X(bc.count); // only needs a third of "RGB" LEDs for NeoPixelBus - _busPtr = PolyBus::create(_iType, _pins, lenToCreate + _skip, nr); + if (bc.type == TYPE_SM16703_DUAL) { + _busPtr = PolyBus::create(_iType, _pins, lenToCreate, nr); // lenToCreate already includes skip * 2 + } else { + _busPtr = PolyBus::create(_iType, _pins, lenToCreate + _skip, nr); + } _valid = (_busPtr != nullptr) && bc.count > 0; // fix for wled#4759 - if (_valid) for (unsigned i = 0; i < _skip; i++) { + unsigned skipHW = (bc.type == TYPE_SM16703_DUAL) ? _skip * 2 : _skip; + if (_valid) for (unsigned i = 0; i < skipHW; i++) { PolyBus::setPixelColor(_busPtr, _iType, i, 0, COL_ORDER_GRB); // set sacrificial pixels to black (CO does not matter here) } DEBUGBUS_PRINTF_P(PSTR("Bus: %successfully inited #%u (len:%u, type:%u (RGB:%d, W:%d, CCT:%d), pins:%u,%u [itype:%u] mA=%d/%d)\n"), @@ -293,6 +299,45 @@ void IRAM_ATTR BusDigital::setPixelColor(unsigned pix, uint32_t c) { if (Bus::_cct >= 1900) c = colorBalanceFromKelvin(Bus::_cct, c); //color correction from CCT c = color_fade(c, _bri, true); // apply brightness + if (_type == TYPE_SM16703_DUAL) { + // logical pixel uses two SM16703 chips: first RGB, second WW/CW (B unused) + uint8_t r = R(c), g = G(c), b = B(c); + uint8_t ww = 0, cw = 0; + if (hasCCT()) Bus::calculateCCT(c, ww, cw); + + if (BusManager::_useABL) { + if (_milliAmpsPerLed < 255) { + _colorSum += r + g + b + ww + cw; // include both chips' channels for ABL + } else { + // wacky model not expected; fall back to max RGB + uint8_t maxRgb = (r > g) ? ((r > b) ? r : b) : ((g > b) ? g : b); + _colorSum += maxRgb; + } + } + + unsigned logicalPix = pix; + if (_reversed) logicalPix = _len - pix - 1; + logicalPix += _skip; + unsigned firstIdx = logicalPix * 2; + unsigned secondIdx = firstIdx + 1; + + // bounds safeguard (should not trigger if lenToCreate set correctly) + unsigned hwLen = (_len + _skip) * 2; + if (secondIdx >= hwLen) return; + + const uint8_t coFirst = _colorOrderMap.getPixelColorOrder(logicalPix + _start, _colorOrder); + PolyBus::setPixelColor(_busPtr, _iType, firstIdx, c, coFirst, 0); + + // Second chip: R=WW, G=CW by default; respect WW/CW swap flag (upper nibble of color order) + const uint8_t coSecond = _colorOrderMap.getPixelColorOrder(logicalPix + _start, _colorOrder); + bool swapWhites = true; // hardware wiring has WW/CW reversed relative to CCT + if ((coSecond >> 4) & 0x01) swapWhites = !swapWhites; // UI swap toggles + if (swapWhites) { uint8_t tmp = ww; ww = cw; cw = tmp; } + uint32_t wwCwColor = RGBW32(ww, cw, 0, 0); + PolyBus::setPixelColor(_busPtr, _iType, secondIdx, wwCwColor, COL_ORDER_GRB, 0); + return; + } + if (BusManager::_useABL) { // if using ABL, sum all color channels to estimate current and limit brightness in show() uint8_t r = R(c), g = G(c), b = B(c); @@ -370,6 +415,7 @@ void BusDigital::setColorOrder(uint8_t colorOrder) { std::vector BusDigital::getLEDTypes() { return { {TYPE_WS2812_RGB, "D", PSTR("WS281x")}, + {TYPE_SM16703_DUAL, "D", PSTR("SM16703 RGB+CCT (2x)")}, {TYPE_SK6812_RGBW, "D", PSTR("SK6812/WS2814 RGBW")}, {TYPE_TM1814, "D", PSTR("TM1814")}, {TYPE_WS2811_400KHZ, "D", PSTR("400kHz")}, diff --git a/wled00/bus_manager.h b/wled00/bus_manager.h index 95772a443f..b5856cdc5a 100644 --- a/wled00/bus_manager.h +++ b/wled00/bus_manager.h @@ -174,7 +174,8 @@ class Bus { static constexpr bool hasWhite(uint8_t type) { return (type >= TYPE_WS2812_1CH && type <= TYPE_WS2812_WWA) || type == TYPE_SK6812_RGBW || type == TYPE_TM1814 || type == TYPE_UCS8904 || - type == TYPE_FW1906 || type == TYPE_WS2805 || type == TYPE_SM16825 || // digital types with white channel + type == TYPE_FW1906 || type == TYPE_WS2805 || type == TYPE_SM16825 || + type == TYPE_SM16703_DUAL || // digital types with white channel (type > TYPE_ONOFF && type <= TYPE_ANALOG_5CH && type != TYPE_ANALOG_3CH) || // analog types with white channel type == TYPE_NET_DDP_RGBW || type == TYPE_NET_ARTNET_RGBW; // network types with white channel } @@ -182,7 +183,7 @@ class Bus { return type == TYPE_WS2812_2CH_X3 || type == TYPE_WS2812_WWA || type == TYPE_ANALOG_2CH || type == TYPE_ANALOG_5CH || type == TYPE_FW1906 || type == TYPE_WS2805 || - type == TYPE_SM16825; + type == TYPE_SM16825 || type == TYPE_SM16703_DUAL; } static constexpr bool isTypeValid(uint8_t type) { return (type > 15 && type < 128); } static constexpr bool isDigital(uint8_t type) { return (type >= TYPE_DIGITAL_MIN && type <= TYPE_DIGITAL_MAX) || is2Pin(type); } diff --git a/wled00/bus_wrapper.h b/wled00/bus_wrapper.h index b2ff947418..88629ac0e6 100644 --- a/wled00/bus_wrapper.h +++ b/wled00/bus_wrapper.h @@ -1315,6 +1315,7 @@ class PolyBus { case TYPE_WS2812_2CH_X3: case TYPE_WS2812_RGB: case TYPE_WS2812_WWA: + case TYPE_SM16703_DUAL: return I_8266_U0_NEO_3 + offset; case TYPE_SK6812_RGBW: return I_8266_U0_NEO_4 + offset; @@ -1378,6 +1379,7 @@ class PolyBus { case TYPE_WS2812_2CH_X3: case TYPE_WS2812_RGB: case TYPE_WS2812_WWA: + case TYPE_SM16703_DUAL: return I_32_RN_NEO_3 + offset; case TYPE_SK6812_RGBW: return I_32_RN_NEO_4 + offset; diff --git a/wled00/const.h b/wled00/const.h index ac48838435..67ad07fbd3 100644 --- a/wled00/const.h +++ b/wled00/const.h @@ -305,6 +305,7 @@ static_assert(WLED_MAX_BUSSES <= 32, "WLED_MAX_BUSSES exceeds hard limit"); #define TYPE_WS2805 32 //RGB + WW + CW #define TYPE_TM1914 33 //RGB #define TYPE_SM16825 34 //RGB + WW + CW +#define TYPE_SM16703_DUAL 35 //dual SM16703: RGB chip + WW/CW chip (B unused) #define TYPE_DIGITAL_MAX 39 // last usable digital type //"Analog" types (40-47) #define TYPE_ONOFF 40 //binary output (relays etc.; NOT PWM)