@@ -1194,8 +1194,9 @@ void WS2812FX::finalizeInit() {
11941194 if (busEnd > _length) _length = busEnd;
11951195 // This must be done after all buses have been created, as some kinds (parallel I2S) interact
11961196 bus->begin ();
1197- bus->setBrightness (bri);
1197+ bus->setBrightness (scaledBri ( bri) );
11981198 }
1199+ BusManager::initializeABL (); // init brightness limiter
11991200 DEBUG_PRINTF_P (PSTR (" Heap after buses: %d\n " ), ESP.getFreeHeap ());
12001201
12011202 Segment::maxWidth = _length;
@@ -1297,7 +1298,7 @@ static uint8_t _add (uint8_t a, uint8_t b) { unsigned t = a + b; return t
12971298static uint8_t _subtract (uint8_t a, uint8_t b) { return b > a ? (b - a) : 0 ; }
12981299static uint8_t _difference (uint8_t a, uint8_t b) { return b > a ? (b - a) : (a - b); }
12991300static uint8_t _average (uint8_t a, uint8_t b) { return (a + b) >> 1 ; }
1300- #ifdef CONFIG_IDF_TARGET_ESP32C3
1301+ #if defined(ESP8266) || defined( CONFIG_IDF_TARGET_ESP32C3)
13011302static uint8_t _multiply (uint8_t a, uint8_t b) { return ((a * b) + 255 ) >> 8 ; } // faster than division on C3 but slightly less accurate
13021303#else
13031304static uint8_t _multiply (uint8_t a, uint8_t b) { return (a * b) / 255 ; } // origianl uses a & b in range [0,1]
@@ -1308,10 +1309,10 @@ static uint8_t _darken (uint8_t a, uint8_t b) { return a < b ? a : b; }
13081309static uint8_t _screen (uint8_t a, uint8_t b) { return 255 - _multiply (~a,~b); } // 255 - (255-a)*(255-b)/255
13091310static uint8_t _overlay (uint8_t a, uint8_t b) { return b < 128 ? 2 * _multiply (a,b) : (255 - 2 * _multiply (~a,~b)); }
13101311static uint8_t _hardlight (uint8_t a, uint8_t b) { return a < 128 ? 2 * _multiply (a,b) : (255 - 2 * _multiply (~a,~b)); }
1311- #ifdef CONFIG_IDF_TARGET_ESP32C3
1312- static uint8_t _softlight (uint8_t a, uint8_t b) { return (((b * b * (255 - 2 * a) + 255 ) >> 8 ) + 2 * a * b + 255 ) >> 8 ; } // Pegtop's formula (1 - 2a)b^2 + 2ab
1312+ #if defined(ESP8266) || defined( CONFIG_IDF_TARGET_ESP32C3)
1313+ static uint8_t _softlight (uint8_t a, uint8_t b) { return (((b * b * (255 - 2 * a)) ) + (( 2 * a * b + 256 ) << 8 )) >> 16 ; } // Pegtop's formula (1 - 2a)b^2
13131314#else
1314- static uint8_t _softlight (uint8_t a, uint8_t b) { return (b * b * (255 - 2 * a) / 255 + 2 * a * b) / 255 ; } // Pegtop's formula (1 - 2a)b^2 + 2ab
1315+ static uint8_t _softlight (uint8_t a, uint8_t b) { return (b * b * (255 - 2 * a) + 255 * 2 * a * b) / ( 255 * 255 ) ; } // Pegtop's formula (1 - 2a)b^2 + 2ab
13151316#endif
13161317static uint8_t _dodge (uint8_t a, uint8_t b) { return _divide (~a,b); }
13171318static uint8_t _burn (uint8_t a, uint8_t b) { return ~_divide (a,~b); }
@@ -1550,66 +1551,6 @@ void WS2812FX::blendSegment(const Segment &topSegment) const {
15501551 Segment::setClippingRect (0 , 0 ); // disable clipping for overlays
15511552}
15521553
1553- // To disable brightness limiter we either set output max current to 0 or single LED current to 0
1554- static uint8_t estimateCurrentAndLimitBri (uint8_t brightness, uint32_t *pixels) {
1555- unsigned milliAmpsMax = BusManager::ablMilliampsMax ();
1556- if (milliAmpsMax > 0 ) {
1557- unsigned milliAmpsTotal = 0 ;
1558- unsigned avgMilliAmpsPerLED = 0 ;
1559- unsigned lengthDigital = 0 ;
1560- bool useWackyWS2815PowerModel = false ;
1561-
1562- for (size_t i = 0 ; i < BusManager::getNumBusses (); i++) {
1563- const Bus *bus = BusManager::getBus (i);
1564- if (!(bus && bus->isDigital () && bus->isOk ())) continue ;
1565- unsigned maPL = bus->getLEDCurrent ();
1566- if (maPL == 0 || bus->getMaxCurrent () > 0 ) continue ; // skip buses with 0 mA per LED or max current per bus defined (PP-ABL)
1567- if (maPL == 255 ) {
1568- useWackyWS2815PowerModel = true ;
1569- maPL = 12 ; // WS2815 uses 12mA per channel
1570- }
1571- avgMilliAmpsPerLED += maPL * bus->getLength ();
1572- lengthDigital += bus->getLength ();
1573- // sum up the usage of each LED on digital bus
1574- uint32_t busPowerSum = 0 ;
1575- for (unsigned j = 0 ; j < bus->getLength (); j++) {
1576- uint32_t c = pixels[j + bus->getStart ()];
1577- byte r = R (c), g = G (c), b = B (c), w = W (c);
1578- if (useWackyWS2815PowerModel) { // ignore white component on WS2815 power calculation
1579- busPowerSum += (max (max (r,g),b)) * 3 ;
1580- } else {
1581- busPowerSum += (r + g + b + w);
1582- }
1583- }
1584- // RGBW led total output with white LEDs enabled is still 50mA, so each channel uses less
1585- if (bus->hasWhite ()) {
1586- busPowerSum *= 3 ;
1587- busPowerSum >>= 2 ; // same as /= 4
1588- }
1589- // powerSum has all the values of channels summed (max would be getLength()*765 as white is excluded) so convert to milliAmps
1590- milliAmpsTotal += (busPowerSum * maPL * brightness) / (765 *255 );
1591- }
1592- if (lengthDigital > 0 ) {
1593- avgMilliAmpsPerLED /= lengthDigital;
1594-
1595- if (milliAmpsMax > MA_FOR_ESP && avgMilliAmpsPerLED > 0 ) { // 0 mA per LED and too low numbers turn off calculation
1596- unsigned powerBudget = (milliAmpsMax - MA_FOR_ESP); // 80/120mA for ESP power
1597- if (powerBudget > lengthDigital) { // each LED uses about 1mA in standby, exclude that from power budget
1598- powerBudget -= lengthDigital;
1599- } else {
1600- powerBudget = 0 ;
1601- }
1602- if (milliAmpsTotal > powerBudget) {
1603- // scale brightness down to stay in current limit
1604- unsigned scaleB = powerBudget * 255 / milliAmpsTotal;
1605- brightness = ((brightness * scaleB) >> 8 ) + 1 ;
1606- }
1607- }
1608- }
1609- }
1610- return brightness;
1611- }
1612-
16131554void WS2812FX::show () {
16141555 if (!_pixels) return ; // no pixels allocated, nothing to show
16151556
@@ -1637,10 +1578,6 @@ void WS2812FX::show() {
16371578 show_callback callback = _callback;
16381579 if (callback) callback (); // will call setPixelColor or setRealtimePixelColor
16391580
1640- // determine ABL brightness
1641- uint8_t newBri = estimateCurrentAndLimitBri (_brightness, _pixels);
1642- if (newBri != _brightness) BusManager::setBrightness (newBri);
1643-
16441581 // paint actual pixels
16451582 int oldCCT = Bus::getCCT (); // store original CCT value (since it is global)
16461583 // when cctFromRgb is true we implicitly calculate WW and CW from RGB values (cct==-1)
@@ -1651,7 +1588,11 @@ void WS2812FX::show() {
16511588 if (_pixelCCT) { // cctFromRgb already exluded at allocation
16521589 if (i == 0 || _pixelCCT[i-1 ] != _pixelCCT[i]) BusManager::setSegmentCCT (_pixelCCT[i], correctWB);
16531590 }
1654- BusManager::setPixelColor (getMappedPixelIndex (i), realtimeMode && arlsDisableGammaCorrection ? _pixels[i] : gamma32 (_pixels[i]));
1591+
1592+ uint32_t c = _pixels[i]; // need a copy, do not modify _pixels directly (no byte access allowed on ESP32)
1593+ if (c > 0 && !(realtimeMode && arlsDisableGammaCorrection))
1594+ c = gamma32 (c); // apply gamma correction if enabled note: applying gamma after brightness has too much color loss
1595+ BusManager::setPixelColor (getMappedPixelIndex (i), c);
16551596 }
16561597 Bus::setCCT (oldCCT); // restore old CCT for ABL adjustments
16571598
@@ -1663,9 +1604,6 @@ void WS2812FX::show() {
16631604 // See https://github.com/Makuna/NeoPixelBus/wiki/ESP32-NeoMethods#neoesp32rmt-methods
16641605 BusManager::show ();
16651606
1666- // restore brightness for next frame
1667- if (newBri != _brightness) BusManager::setBrightness (_brightness);
1668-
16691607 if (diff > 0 ) { // skip calculation if no time has passed
16701608 size_t fpsCurr = (1000 << FPS_CALC_SHIFT) / diff; // fixed point math
16711609 _cumulativeFps = (FPS_CALC_AVG * _cumulativeFps + fpsCurr + FPS_CALC_AVG / 2 ) / (FPS_CALC_AVG + 1 ); // "+FPS_CALC_AVG/2" for proper rounding
@@ -1730,7 +1668,7 @@ void WS2812FX::setBrightness(uint8_t b, bool direct) {
17301668 if (_brightness == 0 ) { // unfreeze all segments on power off
17311669 for (const Segment &seg : _segments) seg.freeze = false ; // freeze is mutable
17321670 }
1733- BusManager::setBrightness (b );
1671+ BusManager::setBrightness (scaledBri (b) );
17341672 if (!direct) {
17351673 unsigned long t = millis ();
17361674 if (_segments[0 ].next_time > t + 22 && t - _lastShow > MIN_SHOW_DELAY) trigger (); // apply brightness change immediately if no refresh soon
0 commit comments