Skip to content
Open
Show file tree
Hide file tree
Changes from all 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
28 changes: 23 additions & 5 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 @@ -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) {
Expand All @@ -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
Expand All @@ -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<BusDigital&>(*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; }
Expand All @@ -1450,4 +1466,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
24 changes: 21 additions & 3 deletions wled00/data/settings_leds.htm
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -202,6 +211,7 @@
else sel.value = 0;
enLA(sel,n); // configure individual limiter
});
enPM();
enABL();
gId('m1').innerHTML = maxM;
}
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -469,7 +479,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 +489,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 +705,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 +879,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="255" 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 +897,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) BusManager::setVoltage(ledVoltage);

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