diff --git a/wled00/bus_manager.cpp b/wled00/bus_manager.cpp index 4fa5c40a57..b0679db179 100644 --- a/wled00/bus_manager.cpp +++ b/wled00/bus_manager.cpp @@ -293,8 +293,8 @@ 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 (BusManager::_useABL) { - // if using ABL, sum all color channels to estimate current and limit brightness in show() + if (BusManager::_useABL || BusManager::_usePowerMonitoring) { + // if using ABL or power monitoring, sum all color channels to estimate current uint8_t r = R(c), g = G(c), b = B(c); if (_milliAmpsPerLed < 255) { // normal ABL _colorSum += r + g + b + W(c); @@ -360,6 +360,10 @@ size_t BusDigital::getBusSize() const { return sizeof(BusDigital) + (isOk() ? PolyBus::getDataSize(_busPtr, _iType) : 0); // does not include common I2S DMA buffer } +float BusDigital::getVoltage() const { + return BusManager::getVoltage(); +} + void BusDigital::setColorOrder(uint8_t colorOrder) { // upper nibble contains W swap information if ((colorOrder & 0x0F) > 5) return; @@ -1393,7 +1397,7 @@ void BusManager::initializeABL() { } void BusManager::applyABL() { - if (_useABL) { + if (_useABL || _usePowerMonitoring) { unsigned milliAmpsSum = 0; // use temporary variable to always return a valid _gMilliAmpsUsed to UI unsigned totalLEDs = 0; for (auto &bus : busses) { @@ -1407,7 +1411,7 @@ void BusManager::applyABL() { } } // check global current limit and apply global ABL limit, total current is summed above - if (_gMilliAmpsMax > 0) { + if (_gMilliAmpsMax > 0 && _useABL) { uint8_t newBri = 255; uint32_t globalMax = _gMilliAmpsMax > MA_FOR_ESP ? _gMilliAmpsMax - MA_FOR_ESP : 1; // subtract ESP current consumption, fully limit if too low if (globalMax > totalLEDs) { // check if budget is larger than standby current @@ -1428,11 +1432,23 @@ void BusManager::applyABL() { busd.applyBriLimit(newBri); } } + } else if (_usePowerMonitoring) { + // reset _colorSum for power monitoring without applying brightness limits + for (auto &bus : busses) { + if (bus->isDigital() && bus->isOk()) { + BusDigital &busd = static_cast(*bus); + busd.applyBriLimit(255); + } + } } _gMilliAmpsUsed = milliAmpsSum; } else - _gMilliAmpsUsed = 0; // reset, we have no current estimation without ABL + _gMilliAmpsUsed = 0; // reset, we have no current estimation without ABL or power monitoring +} + +float BusManager::currentWatts() { + return (currentMilliamps() * _gVoltage) / 1000.0; } ColorOrderMap& BusManager::getColorOrderMap() { return _colorOrderMap; } @@ -1450,4 +1466,6 @@ uint16_t BusDigital::_milliAmpsTotal = 0; std::vector> BusManager::busses; uint16_t BusManager::_gMilliAmpsUsed = 0; uint16_t BusManager::_gMilliAmpsMax = ABL_MILLIAMPS_DEFAULT; +uint8_t BusManager::_gVoltage = LED_VOLTAGE_DEFAULT; bool BusManager::_useABL = false; +bool BusManager::_usePowerMonitoring = false; diff --git a/wled00/bus_manager.h b/wled00/bus_manager.h index 95772a443f..94965317bf 100644 --- a/wled00/bus_manager.h +++ b/wled00/bus_manager.h @@ -140,6 +140,7 @@ class Bus { virtual uint16_t getLEDCurrent() const { return 0; } virtual uint16_t getUsedCurrent() const { return 0; } virtual uint16_t getMaxCurrent() const { return 0; } + virtual float getVoltage() const { return LED_VOLTAGE_DEFAULT; } virtual size_t getBusSize() const { return sizeof(Bus); } virtual const String getCustomText() const { return String(); } @@ -258,6 +259,7 @@ class BusDigital : public Bus { uint16_t getLEDCurrent() const override { return _milliAmpsPerLed; } uint16_t getUsedCurrent() const override { return _milliAmpsTotal; } uint16_t getMaxCurrent() const override { return _milliAmpsMax; } + float getVoltage() const override; void setCurrentLimit(uint16_t milliAmps) { _milliAmpsLimit = milliAmps; } void estimateCurrent(); // estimate used current from summed colors void applyBriLimit(uint8_t newBri); @@ -480,7 +482,9 @@ namespace BusManager { //extern std::vector busses; extern uint16_t _gMilliAmpsUsed; extern uint16_t _gMilliAmpsMax; + extern uint8_t _gVoltage; extern bool _useABL; + extern bool _usePowerMonitoring; #ifdef ESP32_DATA_IDLE_HIGH void esp32RMTInvertIdle() ; @@ -496,6 +500,11 @@ namespace BusManager { //inline uint16_t ablMilliampsMax() { unsigned sum = 0; for (auto &bus : busses) sum += bus->getMaxCurrent(); return sum; } inline uint16_t ablMilliampsMax() { return _gMilliAmpsMax; } // used for compatibility reasons (and enabling virtual global ABL) inline void setMilliampsMax(uint16_t max) { _gMilliAmpsMax = max;} + inline uint8_t getVoltage() { return _gVoltage; } + inline void setVoltage(uint8_t v) { _gVoltage = v; } + inline bool powerMonitoring() { return _usePowerMonitoring; } + inline void enablePowerMonitoring(bool pm){ _usePowerMonitoring = pm; } + float currentWatts(); // calculate total power consumption in Watts void initializeABL(); // setup automatic brightness limiter parameters, call once after buses are initialized void applyABL(); // apply automatic brightness limiter, global or per bus diff --git a/wled00/cfg.cpp b/wled00/cfg.cpp index e30be759b6..59f1589ae6 100644 --- a/wled00/cfg.cpp +++ b/wled00/cfg.cpp @@ -158,6 +158,10 @@ bool deserializeConfig(JsonObject doc, bool fromFS) { uint16_t total = hw_led[F("total")] | strip.getLengthTotal(); uint16_t ablMilliampsMax = hw_led[F("maxpwr")] | BusManager::ablMilliampsMax(); BusManager::setMilliampsMax(ablMilliampsMax); + uint8_t ledVoltage = hw_led[F("voltage")] | LED_VOLTAGE_DEFAULT; + BusManager::setVoltage(ledVoltage); + bool powerMonitoring = hw_led[F("pwrmon")] | false; + BusManager::enablePowerMonitoring(powerMonitoring); Bus::setGlobalAWMode(hw_led[F("rgbwm")] | AW_GLOBAL_DISABLED); CJSON(strip.correctWB, hw_led["cct"]); CJSON(strip.cctFromRgb, hw_led[F("cr")]); @@ -921,6 +925,8 @@ void serializeConfig(JsonObject root) { JsonObject hw_led = hw.createNestedObject("led"); hw_led[F("total")] = strip.getLengthTotal(); //provided for compatibility on downgrade and per-output ABL hw_led[F("maxpwr")] = BusManager::ablMilliampsMax(); + hw_led[F("voltage")] = BusManager::getVoltage(); + hw_led[F("pwrmon")] = BusManager::powerMonitoring(); // hw_led[F("ledma")] = 0; // no longer used hw_led["cct"] = strip.correctWB; hw_led[F("cr")] = strip.cctFromRgb; diff --git a/wled00/const.h b/wled00/const.h index 9067d9b16c..9226ac34d5 100644 --- a/wled00/const.h +++ b/wled00/const.h @@ -536,6 +536,10 @@ static_assert(WLED_MAX_BUSSES <= 32, "WLED_MAX_BUSSES exceeds hard limit"); #endif #endif +#ifndef LED_VOLTAGE_DEFAULT + #define LED_VOLTAGE_DEFAULT 5 // 5V is most common for WS281x +#endif + // PWM settings #ifndef WLED_PWM_FREQ #ifdef ESP8266 diff --git a/wled00/data/settings_leds.htm b/wled00/data/settings_leds.htm index 2cd5e28393..24a7078196 100644 --- a/wled00/data/settings_leds.htm +++ b/wled00/data/settings_leds.htm @@ -133,11 +133,20 @@ } } } + function enPM() + { + var en = d.Sf.PM.checked; + gId('pwrmon').style.display = (en) ? 'inline':'none'; + gId('pmwarn').style.display = (en && !d.Sf.ABL.checked) ? 'inline':'none'; + UI(); + } function enABL() { var en = d.Sf.ABL.checked; gId('abl').style.display = (en) ? 'inline':'none'; gId('psu2').style.display = (en) ? 'inline':'none'; + // Change power monitoring warning visibility (hide if ABL is enabled - calculations already running) + if (d.Sf.PM.checked) gId('pmwarn').style.display = (!en) ? 'inline':'none'; if (!en) { // limiter disabled d.Sf.PPL.checked = false; @@ -202,6 +211,7 @@ else sel.value = 0; enLA(sel,n); // configure individual limiter }); + enPM(); enABL(); gId('m1').innerHTML = maxM; } @@ -287,7 +297,7 @@ memu += getMem(t, n); // calc memory dC += (isDig(t) && !isD2P(t)); setPinConfig(n,t); - gId("abl"+n).style.display = (!abl || !isDig(t)) ? "none" : "inline"; // show/hide individual ABL settings + gId("dig"+n+"ma").style.display = (isDig(t) && (d.Sf.ABL.checked || d.Sf.PM.checked)) ? "inline" : "none"; // show mA/LED only when ABL or power monitoring enabled if (change) { // did we change LED type? gId("rf"+n).checked = (gId("rf"+n).checked || t == 31); // LEDs require data in off state (mandatory for TM1814) if (isAna(t)) d.Sf["LC"+n].value = 1; // for sanity change analog count just to 1 LED @@ -469,7 +479,7 @@
${i+1}:
-
+ +
PSU: mA
Color Order:
+ Enable automatic brightness limiter:
Automatically limits brightness to stay close to the limit.
@@ -880,6 +897,7 @@

LED & Hardware setup

Make sure you enter correct value for each LED output.
If using multiple outputs with only one PSU, distribute its power proportionally amongst outputs.

+ ⚠ CPU intensive, may reduce FPS