Skip to content
Open
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 25 additions & 2 deletions wled00/bus_manager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -1431,10 +1435,27 @@ void BusManager::applyABL() {
}
_gMilliAmpsUsed = milliAmpsSum;
}
else if (_usePowerMonitoring) {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

there is no need for code duplication, just adjust the logic above:
if (_useABL || _usePowerMonitoring)
and
if (_gMilliAmpsMax > 0 && _useABL)

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

changed, but still needed to do this:

} else if (_usePowerMonitoring) {
      for (auto &bus : busses) {
        if (bus->isDigital() && bus->isOk()) {
          BusDigital &busd = static_cast<BusDigital&>(*bus);
          busd.applyBriLimit(255);
        }
      }
    }

because we need to reset _colorSum independently from abl process (it will accumulate over time making wrong calculations)

// Calculate current for power monitoring without applying brightness limits
unsigned milliAmpsSum = 0;
for (auto &bus : busses) {
if (bus->isDigital() && bus->isOk()) {
BusDigital &busd = static_cast<BusDigital&>(*bus);
busd.estimateCurrent();
busd.applyBriLimit(255); // reset _colorSum without limiting brightness
milliAmpsSum += busd.getUsedCurrent();
}
}
_gMilliAmpsUsed = milliAmpsSum;
}
else
_gMilliAmpsUsed = 0; // reset, we have no current estimation without ABL
}

float BusManager::currentWatts() {
return (currentMilliamps() * _gVoltage) / 1000.0;
}

ColorOrderMap& BusManager::getColorOrderMap() { return _colorOrderMap; }


Expand All @@ -1450,4 +1471,6 @@ uint16_t BusDigital::_milliAmpsTotal = 0;
std::vector<std::unique_ptr<Bus>> 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;
9 changes: 9 additions & 0 deletions wled00/bus_manager.h
Original file line number Diff line number Diff line change
Expand Up @@ -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(); }

Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -480,7 +482,9 @@ namespace BusManager {
//extern std::vector<Bus*> 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() ;
Expand All @@ -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

Expand Down
6 changes: 6 additions & 0 deletions wled00/cfg.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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")]);
Expand Down Expand Up @@ -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;
Expand Down
4 changes: 4 additions & 0 deletions wled00/const.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
28 changes: 22 additions & 6 deletions wled00/data/settings_leds.htm
Original file line number Diff line number Diff line change
Expand Up @@ -133,11 +133,19 @@
}
}
}
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';
}
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;
Expand Down Expand Up @@ -172,11 +180,10 @@
// enable and update LED Amps
function enLA(s,n)
{
const abl = d.Sf.ABL.checked;
const t = parseInt(d.Sf["LT"+n].value); // LED type SELECT
gId('LAdis'+n).style.display = s.selectedIndex==5 ? "inline" : "none"; // show/hide custom mA field
if (s.value!=="0") d.Sf["LA"+n].value = s.value; // set value from select object
d.Sf["LA"+n].min = (!isDig(t) || !abl) ? 0 : 1; // set minimum value for validation
d.Sf["LA"+n].min = (!isDig(t)) ? 0 : 1; // set minimum value for validation (required for power monitoring)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

there is a reason the field is hidden if its not used - to not confuse unexperienced users (which there are a lot of)

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

made it appear only when the abl/power fields are selected

}
function setABL()
{
Expand All @@ -202,6 +209,7 @@
else sel.value = 0;
enLA(sel,n); // configure individual limiter
});
enPM();
enABL();
gId('m1').innerHTML = maxM;
}
Expand Down Expand Up @@ -287,11 +295,11 @@
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) ? "inline" : "none"; // show mA/LED for digital LEDs (for power monitoring)
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
d.Sf["LA"+n].min = (!isDig(t) || !abl) ? 0 : 1; // set minimum value for LED mA
d.Sf["LA"+n].min = (!isDig(t)) ? 0 : 1; // set minimum value for LED mA (always required for power monitoring)
d.Sf["MA"+n].min = (!isDig(t)) ? 0 : 250; // set minimum value for PSU mA
}
gId("rf"+n).onclick = mustR(t) ? (()=>{return false}) : (()=>{}); // prevent change change of "Refresh" checkmark when mandatory
Expand Down Expand Up @@ -469,7 +477,7 @@
<hr class="sml">
${i+1}:
<select name="LT${s}" onchange="updateTypeDropdowns();UI(true)"></select><br>
<div id="abl${s}">
<div id="dig${s}ma" style="display:none">
mA/LED: <select name="LAsel${s}" onchange="enLA(this,'${s}');UI();">
<option value="55" selected>55mA (typ. 5V WS281x)</option>
<option value="35">35mA (eco WS2812)</option>
Expand All @@ -479,8 +487,8 @@
<option value="0">Custom</option>
</select><br>
<div id="LAdis${s}" style="display: none;">max. mA/LED: <input name="LA${s}" type="number" min="1" max="255" oninput="UI()"> mA<br></div>
<div id="PSU${s}">PSU: <input name="MA${s}" type="number" class="xl" min="250" max="65000" oninput="UI()" value="250"> mA<br></div>
</div>
<div id="PSU${s}">PSU: <input name="MA${s}" type="number" class="xl" min="250" max="65000" oninput="UI()" value="250"> mA<br></div>
<div id="co${s}" style="display:inline">Color Order:
<select name="CO${s}">
<option value="0">GRB</option>
Expand Down Expand Up @@ -695,6 +703,7 @@
});
d.getElementsByName("PR")[0].checked = l.prl | 0;
d.getElementsByName("MA")[0].value = l.maxpwr;
if (l.voltage) d.getElementsByName("LV")[0].value = l.voltage;
d.getElementsByName("ABL")[0].checked = l.maxpwr > 0;
}
if(c.hw.com) {
Expand Down Expand Up @@ -868,6 +877,12 @@ <h2>LED &amp; Hardware setup</h2>
<b><span id="psu">?</span></b><br>
<span id="psu2"><br></span>
<br>
Enable power monitoring: <input type="checkbox" name="PM" onchange="enPM()"><br>
<div id="pwrmon" style="display:none">
LED Strip Voltage: <input name="LV" type="number" class="m" step="1" min="1" max="50" value="5" oninput="UI()"> V<br>
<i>Exposes power in API. Typical: 5V (WS281x), 12V/24V (others)</i><br>
<i id="pmwarn" class="warn">⚠ CPU intensive, may reduce FPS</i><br>
</div>
Enable automatic brightness limiter: <input type="checkbox" name="ABL" onchange="enABL()"><br>
<div id="abl">
<i>Automatically limits brightness to stay close to the limit.<br>
Expand All @@ -880,6 +895,7 @@ <h2>LED &amp; Hardware setup</h2>
<i>Make sure you enter correct value for each LED output.<br>
If using multiple outputs with only one PSU, distribute its power proportionally amongst outputs.</i><br>
</div>
<i class="warn">⚠ CPU intensive, may reduce FPS</i><br>
<div id="ampwarning" class="warn" style="display: none;">
&#9888; Your power supply provides high current.<br>
To improve the safety of your setup,<br>
Expand Down
2 changes: 2 additions & 0 deletions wled00/json.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -704,8 +704,10 @@ void serializeInfo(JsonObject root)
JsonObject leds = root.createNestedObject(F("leds"));
leds[F("count")] = strip.getLengthTotal();
leds[F("pwr")] = BusManager::currentMilliamps();
leds[F("watts")] = BusManager::currentWatts();
leds["fps"] = strip.getFps();
leds[F("maxpwr")] = BusManager::currentMilliamps()>0 ? BusManager::ablMilliampsMax() : 0;
leds[F("voltage")] = BusManager::getVoltage();
leds[F("maxseg")] = WS2812FX::getMaxSegments();
//leds[F("actseg")] = strip.getActiveSegmentsNum();
//leds[F("seglock")] = false; //might be used in the future to prevent modifications to segment config
Expand Down
5 changes: 5 additions & 0 deletions wled00/set.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,11 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
unsigned ablMilliampsMax = request->arg(F("MA")).toInt();
BusManager::setMilliampsMax(ablMilliampsMax);

uint8_t ledVoltage = request->arg(F("LV")).toInt();
if (ledVoltage > 0 && ledVoltage <= 50) BusManager::setVoltage(ledVoltage);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why restrict to 50?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I could only find 48v leds (UCS2903). So 50V seemed like an appropriate choice.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if there is no technical reason, leave it to the user. there may be 120V strips one day, who knows.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

removed this check. In html bumped up to 255.


BusManager::enablePowerMonitoring(request->hasArg(F("PM")));

strip.autoSegments = request->hasArg(F("MS"));
strip.correctWB = request->hasArg(F("CCT"));
strip.cctFromRgb = request->hasArg(F("CR"));
Expand Down
2 changes: 2 additions & 0 deletions wled00/xml.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -374,6 +374,8 @@ void getSettingsJS(byte subPage, Print& settingsScript)
sumMa += bus->getMaxCurrent();
}
printSetFormValue(settingsScript,PSTR("MA"),BusManager::ablMilliampsMax() ? BusManager::ablMilliampsMax() : sumMa);
printSetFormValue(settingsScript,PSTR("LV"),BusManager::getVoltage());
printSetFormCheckbox(settingsScript,PSTR("PM"),BusManager::powerMonitoring());
printSetFormCheckbox(settingsScript,PSTR("ABL"),BusManager::ablMilliampsMax() || sumMa > 0);
printSetFormCheckbox(settingsScript,PSTR("PPL"),!BusManager::ablMilliampsMax() && sumMa > 0);

Expand Down