diff --git a/wled00/bus_manager.cpp b/wled00/bus_manager.cpp index 77e60ba4f..47a4aab0f 100644 --- a/wled00/bus_manager.cpp +++ b/wled00/bus_manager.cpp @@ -6,6 +6,15 @@ #include #ifdef ARDUINO_ARCH_ESP32 #include "driver/ledc.h" +#include "soc/ledc_struct.h" + #if !(defined(CONFIG_IDF_TARGET_ESP32C3) || defined(CONFIG_IDF_TARGET_ESP32S2) || defined(CONFIG_IDF_TARGET_ESP32S3)) + #define LEDC_MUTEX_LOCK() do {} while (xSemaphoreTake(_ledc_sys_lock, portMAX_DELAY) != pdPASS) + #define LEDC_MUTEX_UNLOCK() xSemaphoreGive(_ledc_sys_lock) + extern xSemaphoreHandle _ledc_sys_lock; + #else + #define LEDC_MUTEX_LOCK() + #define LEDC_MUTEX_UNLOCK() + #endif #endif #include "const.h" #include "pin_manager.h" @@ -69,6 +78,27 @@ uint8_t IRAM_ATTR ColorOrderMap::getPixelColorOrder(uint16_t pix, uint8_t defaul } +void Bus::calculateCCT(uint32_t c, uint8_t &ww, uint8_t &cw) { + unsigned cct = 0; //0 - full warm white, 255 - full cold white + unsigned w = W(c); + + if (_cct > -1) { // using RGB? + if (_cct >= 1900) cct = (_cct - 1900) >> 5; // convert K in relative format + else if (_cct < 256) cct = _cct; // already relative + } else { + cct = (approximateKelvinFromRGB(c) - 1900) >> 5; // convert K (from RGB value) to relative format + } + + //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; +} + uint32_t Bus::autoWhiteCalc(uint32_t c) const { unsigned aWM = _autoWhiteMode; if (_gAWM < AW_GLOBAL_DISABLED) aWM = _gAWM; @@ -354,6 +384,32 @@ void BusDigital::setColorOrder(uint8_t colorOrder) { _colorOrder = colorOrder; } +// credit @willmmiles & @netmindz https://github.com/Aircoookie/WLED/pull/4056 +std::vector BusDigital::getLEDTypes() { + return { + {TYPE_WS2812_RGB, "D", PSTR("WS281x")}, + {TYPE_SK6812_RGBW, "D", PSTR("SK6812/WS2814 RGBW")}, + {TYPE_TM1814, "D", PSTR("TM1814")}, + {TYPE_WS2811_400KHZ, "D", PSTR("400kHz")}, + {TYPE_TM1829, "D", PSTR("TM1829")}, + {TYPE_UCS8903, "D", PSTR("UCS8903")}, + {TYPE_APA106, "D", PSTR("APA106/PL9823")}, + {TYPE_TM1914, "D", PSTR("TM1914")}, + {TYPE_FW1906, "D", PSTR("FW1906 GRBCW")}, + {TYPE_UCS8904, "D", PSTR("UCS8904 RGBW")}, + {TYPE_WS2805, "D", PSTR("WS2805 RGBCW")}, + {TYPE_SM16825, "D", PSTR("SM16825 RGBCW")}, + {TYPE_WS2812_1CH_X3, "D", PSTR("WS2811 White")}, + //{TYPE_WS2812_2CH_X3, "D", PSTR("WS2811 CCT")}, // not implemented + //{TYPE_WS2812_WWA, "D", PSTR("WS2811 WWA")}, // not implemented + {TYPE_WS2801, "2P", PSTR("WS2801")}, + {TYPE_APA102, "2P", PSTR("APA102")}, + {TYPE_LPD8806, "2P", PSTR("LPD8806")}, + {TYPE_LPD6803, "2P", PSTR("LPD6803")}, + {TYPE_P9813, "2P", PSTR("PP9813")}, + }; +} + void BusDigital::reinit(void) { if (!_valid) return; PolyBus::begin(_busPtr, _iType, _pins); @@ -392,15 +448,16 @@ void BusDigital::cleanup(void) { #define MAX_BIT_WIDTH SOC_LEDC_TIMER_BIT_WIDE_NUM #else // ESP32: 20 bit (but in reality we would never go beyond 16 bit as the frequency would be to low) - #define MAX_BIT_WIDTH 20 + #define MAX_BIT_WIDTH 14 #endif #endif BusPwm::BusPwm(BusConfig &bc) -: Bus(bc.type, bc.start, bc.autoWhite, 1, bc.reversed, bc.refreshReq) // hijack Off refresh flag to indicate usage of phase shifting +: Bus(bc.type, bc.start, bc.autoWhite, 1, bc.reversed, bc.refreshReq) // hijack Off refresh flag to indicate usage of dithering { if (!isPWM(bc.type)) return; unsigned numPins = numPWMPins(bc.type); + [[maybe_unused]] const bool dithering = _needsRefresh; _frequency = bc.frequency ? bc.frequency : WLED_PWM_FREQ; // duty cycle resolution (_depth) can be extracted from this formula: CLOCK_FREQUENCY > _frequency * 2^_depth for (_depth = MAX_BIT_WIDTH; _depth > 8; _depth--) if (((CLOCK_FREQUENCY/_frequency) >> _depth) > 0) break; @@ -413,11 +470,14 @@ BusPwm::BusPwm(BusConfig &bc) analogWriteRange((1<<_depth)-1); analogWriteFreq(_frequency); #else + // for 2 pin PWM CCT strip pinManager will make sure both LEDC channels are in the same speed group and sharing the same timer _ledcStart = pinManager.allocateLedc(numPins); if (_ledcStart == 255) { //no more free LEDC channels pinManager.deallocateMultiplePins(pins, numPins, PinOwner::BusPwm); return; } + // if _needsRefresh is true (UI hack) we are using dithering (credit @dedehai & @zalatnaicsongor) + if (dithering) _depth = 12; // fixed 8 bit depth PWM with 4 bit dithering (ESP8266 has no hardware to support dithering) #endif for (unsigned i = 0; i < numPins; i++) { @@ -426,7 +486,7 @@ BusPwm::BusPwm(BusConfig &bc) pinMode(_pins[i], OUTPUT); #else unsigned channel = _ledcStart + i; - ledcSetup(channel, _frequency, _depth); + ledcSetup(channel, _frequency, _depth - (dithering*4)); // with dithering _frequency doesn't really matter as resolution is 8 bit ledcAttachPin(_pins[i], channel); // LEDC timer reset credit @dedehai uint8_t group = (channel / 8), timer = ((channel / 2) % 4); // same fromula as in ledcSetup() @@ -500,39 +560,60 @@ uint32_t BusPwm::getPixelColor(uint16_t pix) const { void BusPwm::show() { if (!_valid) return; + // if _needsRefresh is true (UI hack) we are using dithering (credit @dedehai & @zalatnaicsongor) + // https://github.com/Aircoookie/WLED/pull/4115 and https://github.com/zalatnaicsongor/WLED/pull/1) + const bool dithering = _needsRefresh; // avoid working with bitfield const unsigned numPins = getPins(); - const unsigned maxBri = (1<<_depth); + const unsigned maxBri = (1<<_depth); // possible values: 16384 (14), 8192 (13), 4096 (12), 2048 (11), 1024 (10), 512 (9) and 256 (8) + [[maybe_unused]] const unsigned bitShift = dithering * 4; // if dithering, _depth is 12 bit but LEDC channel is set to 8 bit (using 4 fractional bits) - // use CIE brightness formula - unsigned pwmBri = (unsigned)_bri * 100; - if (pwmBri < 2040) - pwmBri = ((pwmBri << _depth) + 115043) / 230087; //adding '0.5' before division for correct rounding - else { + // use CIE brightness formula (cubic) to fit (or approximate linearity of) human eye perceived brightness + // the formula is based on 12 bit resolution as there is no need for greater precision + // see: https://en.wikipedia.org/wiki/Lightness + unsigned pwmBri = (unsigned)_bri * 100; // enlarge to use integer math for linear response + if (pwmBri < 2040) { + // linear response for values [0-20] + pwmBri = ((pwmBri << 12) + 115043) / 230087; //adding '0.5' before division for correct rounding + } else { + // cubic response for values [21-255] pwmBri += 4080; float temp = (float)pwmBri / 29580.0f; - temp = temp * temp * temp * maxBri; - pwmBri = (unsigned)temp; + temp = temp * temp * temp * (float)maxBri; + pwmBri = (unsigned)temp; // pwmBri is in range [0-maxBri] } + [[maybe_unused]] unsigned hPoint = 0; // phase shift (0 - maxBri) + // we will be phase shifting every channel by previous pulse length (plus dead time if required) + // phase shifting is only mandatory when using H-bridge to drive reverse-polarity PWM CCT (2 wire) LED type + // CCT additive blending must be 0 (WW & CW will not overlap) otherwise signals *will* overlap + // for all other cases it will just try to "spread" the load on PSU + // Phase shifting requires that LEDC timers are synchronised (see setup()). For PWM CCT (and H-bridge) it is + // also mandatory that both channels use the same timer (pinManager takes care of that). for (unsigned i = 0; i < numPins; i++) { - unsigned scaled = (_data[i] * pwmBri) / 255; - if (_reversed) scaled = maxBri - scaled; + unsigned duty = (_data[i] * pwmBri) / 255; #ifdef ESP8266 - analogWrite(_pins[i], scaled); + if (_reversed) duty = maxBri - duty; + analogWrite(_pins[i], duty); #else + int deadTime = 0; + if (_type == TYPE_ANALOG_2CH && Bus::getCCTBlend() == 0) { + // add dead time between signals (when using dithering, two full 8bit pulses are required) + deadTime = (1+dithering) << bitShift; + // we only need to take care of shortening the signal at (almost) full brightness otherwise pulses may overlap + if (_bri >= 254 && duty >= maxBri / 2 && duty < maxBri) duty -= deadTime << 1; // shorten duty of larger signal except if full on + if (_reversed) deadTime = -deadTime; // need to invert dead time to make phaseshift go the opposite way so low signals dont overlap + } + if (_reversed) duty = maxBri - duty; unsigned channel = _ledcStart + i; - // determine phase shift POC for PWM CCT (credit @dedehai) - // phase shifting (180°) is only available for PWM CCT LED type if _needsRefresh is true (UI hack) - // and CCT blending is 0 (WW & CW must not overlap) - // this will allow using H-bridge to drive reverse-polarity CCT LED strip (2 wires) - // NOTE/TODO: if this has no side effects we may forego UI hack and the need for _needsRefresh - // we may even use phase shift to evenly distribute power across different pins - if (_type == TYPE_ANALOG_2CH && _needsRefresh && Bus::getCCTBlend() == 0) { // hacked to determine if phase shifted PWM is requested - unsigned maxDuty = (maxBri / numPins); // numPins is 2 - if (scaled >= maxDuty) scaled = maxDuty - 1; // safety check & add dead time of 1 pulse when brightness is at 50% - ledc_set_duty_and_update((ledc_mode_t)(channel / 8), (ledc_channel_t)(channel % 8), scaled, maxDuty*i); - } else - ledcWrite(channel, scaled); + unsigned gr = channel/8; // high/low speed group + unsigned ch = channel%8; // group channel + // directly write to LEDC struct as there is no HAL exposed function for dithering + // duty has 20 bit resolution with 4 fractional bits (24 bits in total) + LEDC.channel_group[gr].channel[ch].duty.duty = duty << ((!dithering)*4); // lowest 4 bits are used for dithering, shift by 4 bits if not using dithering + LEDC.channel_group[gr].channel[ch].hpoint.hpoint = hPoint >> bitShift; // hPoint is at _depth resolution (needs shifting if dithering) + ledc_update_duty((ledc_mode_t)gr, (ledc_channel_t)ch); + hPoint += duty + deadTime; // offset to cascade the signals + if (hPoint >= maxBri) hPoint = 0; // offset it out of bounds, reset #endif } } @@ -544,6 +625,18 @@ uint8_t BusPwm::getPins(uint8_t* pinArray) const { return numPins; } +// credit @willmmiles & @netmindz https://github.com/Aircoookie/WLED/pull/4056 +std::vector BusPwm::getLEDTypes() { + return { + {TYPE_ANALOG_1CH, "A", PSTR("PWM White")}, + {TYPE_ANALOG_2CH, "AA", PSTR("PWM CCT")}, + {TYPE_ANALOG_3CH, "AAA", PSTR("PWM RGB")}, + {TYPE_ANALOG_4CH, "AAAA", PSTR("PWM RGBW")}, + {TYPE_ANALOG_5CH, "AAAAA", PSTR("PWM RGB+CCT")}, + //{TYPE_ANALOG_6CH, "AAAAAA", PSTR("PWM RGB+DCCT")}, // unimplementable ATM + }; +} + void BusPwm::deallocatePins(void) { unsigned numPins = getPins(); for (unsigned i = 0; i < numPins; i++) { @@ -565,7 +658,7 @@ BusOnOff::BusOnOff(BusConfig &bc) : Bus(bc.type, bc.start, bc.autoWhite, 1, bc.reversed) , _onoffdata(0) { - if (bc.type != TYPE_ONOFF) return; + if (!Bus::isOnOff(bc.type)) return; uint8_t currentPin = bc.pins[0]; if (!pinManager.allocatePin(currentPin, true, PinOwner::BusOnOff)) { @@ -607,6 +700,12 @@ uint8_t BusOnOff::getPins(uint8_t* pinArray) const { return 1; } +// credit @willmmiles & @netmindz https://github.com/Aircoookie/WLED/pull/4056 +std::vector BusOnOff::getLEDTypes() { + return { + {TYPE_ONOFF, "", PSTR("On/Off")}, + }; +} BusNetwork::BusNetwork(BusConfig &bc) : Bus(bc.type, bc.start, bc.autoWhite, bc.count) @@ -664,6 +763,21 @@ uint8_t BusNetwork::getPins(uint8_t* pinArray) const { return 4; } +// credit @willmmiles & @netmindz https://github.com/Aircoookie/WLED/pull/4056 +std::vector BusNetwork::getLEDTypes() { + return { + {TYPE_NET_DDP_RGB, "N", PSTR("DDP RGB (network)")}, // should be "NNNN" to determine 4 "pin" fields + {TYPE_NET_ARTNET_RGB, "N", PSTR("Art-Net RGB (network)")}, + {TYPE_NET_DDP_RGBW, "N", PSTR("DDP RGBW (network)")}, + {TYPE_NET_ARTNET_RGBW, "N", PSTR("Art-Net RGBW (network)")}, + // hypothetical extensions + //{TYPE_VIRTUAL_I2C_W, "V", PSTR("I2C White (virtual)")}, // allows setting I2C address in _pin[0] + //{TYPE_VIRTUAL_I2C_CCT, "V", PSTR("I2C CCT (virtual)")}, // allows setting I2C address in _pin[0] + //{TYPE_VIRTUAL_I2C_RGB, "VVV", PSTR("I2C RGB (virtual)")}, // allows setting I2C address in _pin[0] and 2 additional values in _pin[1] & _pin[2] + //{TYPE_USERMOD, "VVVVV", PSTR("Usermod (virtual)")}, // 5 data fields (see https://github.com/Aircoookie/WLED/pull/4123) + }; +} + void BusNetwork::cleanup(void) { _type = I_NONE; _valid = false; @@ -711,63 +825,30 @@ int BusManager::add(BusConfig &bc) { return numBusses++; } -// idea by @netmindz https://github.com/Aircoookie/WLED/pull/4056 -String BusManager::getLEDTypesJSONString(void) { - struct LEDType { - uint8_t id; - const char *type; - const char *name; - } types[] = { - {TYPE_WS2812_RGB, "D", PSTR("WS281x")}, - {TYPE_SK6812_RGBW, "D", PSTR("SK6812/WS2814 RGBW")}, - {TYPE_TM1814, "D", PSTR("TM1814")}, - {TYPE_WS2811_400KHZ, "D", PSTR("400kHz")}, - {TYPE_TM1829, "D", PSTR("TM1829")}, - {TYPE_UCS8903, "D", PSTR("UCS8903")}, - {TYPE_APA106, "D", PSTR("APA106/PL9823")}, - {TYPE_TM1914, "D", PSTR("TM1914")}, - {TYPE_FW1906, "D", PSTR("FW1906 GRBCW")}, - {TYPE_UCS8904, "D", PSTR("UCS8904 RGBW")}, - {TYPE_WS2805, "D", PSTR("WS2805 RGBCW")}, - {TYPE_SM16825, "D", PSTR("SM16825 RGBCW")}, - {TYPE_WS2812_1CH_X3, "D", PSTR("WS2811 White")}, - //{TYPE_WS2812_2CH_X3, "D", PSTR("WS2811 CCT")}, // not implemented - //{TYPE_WS2812_WWA, "D", PSTR("WS2811 WWA")}, // not implemented - {TYPE_WS2801, "2P", PSTR("WS2801")}, - {TYPE_APA102, "2P", PSTR("APA102")}, - {TYPE_LPD8806, "2P", PSTR("LPD8806")}, - {TYPE_LPD6803, "2P", PSTR("LPD6803")}, - {TYPE_P9813, "2P", PSTR("PP9813")}, - {TYPE_ONOFF, "", PSTR("On/Off")}, - {TYPE_ANALOG_1CH, "A", PSTR("PWM White")}, - {TYPE_ANALOG_2CH, "AA", PSTR("PWM CCT")}, - {TYPE_ANALOG_3CH, "AAA", PSTR("PWM RGB")}, - {TYPE_ANALOG_4CH, "AAAA", PSTR("PWM RGBW")}, - {TYPE_ANALOG_5CH, "AAAAA", PSTR("PWM RGB+CCT")}, - //{TYPE_ANALOG_6CH, "AAAAAA", PSTR("PWM RGB+DCCT")}, // unimplementable ATM - {TYPE_NET_DDP_RGB, "N", PSTR("DDP RGB (network)")}, - {TYPE_NET_ARTNET_RGB, "N", PSTR("Art-Net RGB (network)")}, - {TYPE_NET_DDP_RGBW, "N", PSTR("DDP RGBW (network)")}, - {TYPE_NET_ARTNET_RGBW, "N", PSTR("Art-Net RGBW (network)")}, - // hypothetical extensions - //{TYPE_VIRTUAL_I2C_W, "V", PSTR("I2C White (virtual)")}, // allows setting I2C address in _pin[0] - //{TYPE_VIRTUAL_I2C_CCT, "V", PSTR("I2C CCT (virtual)")}, // allows setting I2C address in _pin[0] - //{TYPE_VIRTUAL_I2C_RGB, "V", PSTR("I2C RGB (virtual)")}, // allows setting I2C address in _pin[0] - }; - String json = "["; +// credit @willmmiles +static String LEDTypesToJson(const std::vector& types) { + String json; for (const auto &type : types) { - String id = String(type.id); - // capabilities follows similar pattern as JSON API + // capabilities follows similar pattern as JSON API int capabilities = Bus::hasRGB(type.id) | Bus::hasWhite(type.id)<<1 | Bus::hasCCT(type.id)<<2 | Bus::is16bit(type.id)<<4; - json += "{i:" + id - + F(",c:") + String(capabilities) - + F(",t:\"") + FPSTR(type.type) - + F("\",n:\"") + FPSTR(type.name) + F("\"},"); + char str[256]; + sprintf_P(str, PSTR("{i:%d,c:%d,t:\"%s\",n:\"%s\"},"), type.id, capabilities, type.type, type.name); + json += str; } - json.setCharAt(json.length()-1, ']'); // replace last comma with bracket return json; } +// credit @willmmiles & @netmindz https://github.com/Aircoookie/WLED/pull/4056 +String BusManager::getLEDTypesJSONString(void) { + String json = "["; + json += LEDTypesToJson(BusDigital::getLEDTypes()); + json += LEDTypesToJson(BusOnOff::getLEDTypes()); + json += LEDTypesToJson(BusPwm::getLEDTypes()); + json += LEDTypesToJson(BusNetwork::getLEDTypes()); + //json += LEDTypesToJson(BusVirtual::getLEDTypes()); + json.setCharAt(json.length()-1, ']'); // replace last comma with bracket + return json; +} void BusManager::useParallelOutput(void) { _parallelOutputs = 8; // hardcoded since we use NPB I2S x8 methods @@ -807,7 +888,7 @@ void BusManager::esp32RMTInvertIdle(void) { if (u >= _parallelOutputs + 8) return; // only 8 RMT channels rmt = u - _parallelOutputs; #endif - if (busses[u]->getLength()==0 || !Bus::isDigital(busses[u]->getType()) || IS_2PIN(busses[u]->getType())) continue; + if (busses[u]->getLength()==0 || !busses[u]->isDigital() || busses[u]->is2Pin()) continue; //assumes that bus number to rmt channel mapping stays 1:1 rmt_channel_t ch = static_cast(rmt); rmt_idle_level_t lvl; @@ -826,7 +907,7 @@ void BusManager::on(void) { if (pinManager.getPinOwner(LED_BUILTIN) == PinOwner::BusDigital) { for (unsigned i = 0; i < numBusses; i++) { uint8_t pins[2] = {255,255}; - if (Bus::isDigital(busses[i]->getType()) && busses[i]->getPins(pins)) { + if (busses[i]->isDigital() && busses[i]->getPins(pins)) { if (pins[0] == LED_BUILTIN || pins[1] == LED_BUILTIN) { BusDigital *bus = static_cast(busses[i]); bus->reinit(); diff --git a/wled00/bus_manager.h b/wled00/bus_manager.h index f6aa4b579..c9bb5dd8e 100644 --- a/wled00/bus_manager.h +++ b/wled00/bus_manager.h @@ -6,7 +6,6 @@ */ #include "const.h" -#include #include //colors.cpp @@ -36,6 +35,7 @@ struct ColorOrderMap { bool add(uint16_t start, uint16_t len, uint8_t colorOrder); inline uint8_t count() const { return _mappings.size(); } + inline void reserve(size_t num) { _mappings.reserve(num); } void reset() { _mappings.clear(); @@ -54,6 +54,13 @@ struct ColorOrderMap { }; +typedef struct { + uint8_t id; + const char *type; + const char *name; +} LEDType; + + //parent class of BusDigital, BusPwm, and BusNetwork class Bus { public: @@ -109,6 +116,7 @@ class Bus { inline bool isOffRefreshRequired(void) const { return _needsRefresh; } inline bool containsPixel(uint16_t pix) const { return pix >= _start && pix < _start + _len; } + static inline std::vector getLEDTypes(void) { return {{TYPE_NONE, "", PSTR("None")}}; } // not used. just for reference for derived classes static constexpr uint8_t getNumberOfPins(uint8_t type) { return isVirtual(type) ? 4 : isPWM(type) ? numPWMPins(type) : is2Pin(type) + 1; } // credit @PaoloTK static constexpr uint8_t getNumberOfChannels(uint8_t type) { return hasWhite(type) + 3*hasRGB(type) + hasCCT(type); } static constexpr bool hasRGB(uint8_t type) { @@ -141,34 +149,14 @@ class Bus { static inline uint8_t getGlobalAWMode(void) { return _gAWM; } static inline void setCCT(int16_t cct) { _cct = cct; } static inline uint8_t getCCTBlend(void) { return _cctBlend; } - static void setCCTBlend(uint8_t b) { - if (b > 100) b = 100; - _cctBlend = (b * 127) / 100; + static inline void setCCTBlend(uint8_t b) { + _cctBlend = (std::min((int)b,100) * 127) / 100; //compile-time limiter for hardware that can't power both white channels at max #ifdef WLED_MAX_CCT_BLEND 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; - } - - //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; - } + static void calculateCCT(uint32_t c, uint8_t &ww, uint8_t &cw); protected: uint8_t _type; @@ -226,6 +214,8 @@ class BusDigital : public Bus { void reinit(void); void cleanup(void); + static std::vector getLEDTypes(void); + private: uint8_t _skip; uint8_t _colorOrder; @@ -266,6 +256,8 @@ class BusPwm : public Bus { void show(void) override; void cleanup(void) { deallocatePins(); } + static std::vector getLEDTypes(void); + private: uint8_t _pins[OUTPUT_MAX_PINS]; uint8_t _pwmdata[OUTPUT_MAX_PINS]; @@ -290,6 +282,8 @@ class BusOnOff : public Bus { void show(void) override; void cleanup(void) { pinManager.deallocatePin(_pin, PinOwner::BusOnOff); } + static std::vector getLEDTypes(void); + private: uint8_t _pin; uint8_t _onoffdata; @@ -308,6 +302,8 @@ class BusNetwork : public Bus { void show(void) override; void cleanup(void); + static std::vector getLEDTypes(void); + private: IPAddress _client; uint8_t _UDPtype; @@ -417,7 +413,7 @@ class BusManager { #endif static uint8_t getNumVirtualBusses(void) { int j = 0; - for (int i=0; igetType() >= TYPE_NET_DDP_RGB && busses[i]->getType() < 96) j++; + for (int i=0; iisVirtual()) j++; return j; } }; diff --git a/wled00/cfg.cpp b/wled00/cfg.cpp index 5f0c8593f..bcb6f7aad 100644 --- a/wled00/cfg.cpp +++ b/wled00/cfg.cpp @@ -244,6 +244,7 @@ bool deserializeConfig(JsonObject doc, bool fromFS) { // read color order map configuration JsonArray hw_com = hw[F("com")]; if (!hw_com.isNull()) { + BusManager::getColorOrderMap().reserve(std::min(hw_com.size(), (size_t)WLED_MAX_COLOR_ORDER_MAPPINGS)); for (JsonObject entry : hw_com) { uint16_t start = entry["start"] | 0; uint16_t len = entry["len"] | 0; diff --git a/wled00/const.h b/wled00/const.h index 1bfca0758..c9f62bda4 100644 --- a/wled00/const.h +++ b/wled00/const.h @@ -496,7 +496,7 @@ // string temp buffer (now stored in stack locally) #ifdef ESP8266 -#define SETTINGS_STACK_BUF_SIZE 2048 +#define SETTINGS_STACK_BUF_SIZE 2560 #else #define SETTINGS_STACK_BUF_SIZE 3840 // warning: quite a large value for stack (640 * WLED_MAX_USERMODS) #endif @@ -536,7 +536,11 @@ #ifdef ESP8266 #define WLED_PWM_FREQ 880 //PWM frequency proven as good for LEDs #else - #define WLED_PWM_FREQ 19531 + #ifdef SOC_LEDC_SUPPORT_XTAL_CLOCK + #define WLED_PWM_FREQ 9765 // XTAL clock is 40MHz (this will allow 12 bit resolution) + #else + #define WLED_PWM_FREQ 19531 // APB clock is 80MHz + #endif #endif #endif diff --git a/wled00/data/settings_leds.htm b/wled00/data/settings_leds.htm index 30709e12d..f9bfd3dca 100644 --- a/wled00/data/settings_leds.htm +++ b/wled00/data/settings_leds.htm @@ -5,9 +5,9 @@ LED Settings