Feature: Per-port ABL
This commit is contained in:
parent
21de8f2284
commit
8f7f9ec367
@ -933,9 +933,6 @@ class WS2812FX { // 96 bytes
|
||||
uint8_t _qGrouping, _qSpacing;
|
||||
uint16_t _qOffset;
|
||||
|
||||
uint8_t
|
||||
estimateCurrentAndLimitBri(void);
|
||||
|
||||
void
|
||||
setUpSegmentFromQueuedChanges(void);
|
||||
};
|
||||
|
@ -1225,104 +1225,16 @@ uint32_t IRAM_ATTR WS2812FX::getPixelColor(uint16_t i) {
|
||||
return busses.getPixelColor(i);
|
||||
}
|
||||
|
||||
|
||||
//DISCLAIMER
|
||||
//The following function attemps to calculate the current LED power usage,
|
||||
//and will limit the brightness to stay below a set amperage threshold.
|
||||
//It is NOT a measurement and NOT guaranteed to stay within the ablMilliampsMax margin.
|
||||
//Stay safe with high amperage and have a reasonable safety margin!
|
||||
//I am NOT to be held liable for burned down garages!
|
||||
|
||||
//fine tune power estimation constants for your setup
|
||||
#define MA_FOR_ESP 100 //how much mA does the ESP use (Wemos D1 about 80mA, ESP32 about 120mA)
|
||||
//you can set it to 0 if the ESP is powered by USB and the LEDs by external
|
||||
|
||||
uint8_t WS2812FX::estimateCurrentAndLimitBri() {
|
||||
//power limit calculation
|
||||
//each LED can draw up 195075 "power units" (approx. 53mA)
|
||||
//one PU is the power it takes to have 1 channel 1 step brighter per brightness step
|
||||
//so A=2,R=255,G=0,B=0 would use 510 PU per LED (1mA is about 3700 PU)
|
||||
bool useWackyWS2815PowerModel = false;
|
||||
byte actualMilliampsPerLed = milliampsPerLed;
|
||||
|
||||
if (ablMilliampsMax < 150 || actualMilliampsPerLed == 0) { //0 mA per LED and too low numbers turn off calculation
|
||||
currentMilliamps = 0;
|
||||
return _brightness;
|
||||
}
|
||||
|
||||
if (milliampsPerLed == 255) {
|
||||
useWackyWS2815PowerModel = true;
|
||||
actualMilliampsPerLed = 12; // from testing an actual strip
|
||||
}
|
||||
|
||||
size_t powerBudget = (ablMilliampsMax - MA_FOR_ESP); //100mA for ESP power
|
||||
|
||||
size_t pLen = 0; //getLengthPhysical();
|
||||
size_t powerSum = 0;
|
||||
for (unsigned bNum = 0; bNum < busses.getNumBusses(); bNum++) {
|
||||
Bus *bus = busses.getBus(bNum);
|
||||
if (!IS_DIGITAL(bus->getType())) continue; //exclude non-digital network busses
|
||||
uint16_t len = bus->getLength();
|
||||
pLen += len;
|
||||
uint32_t busPowerSum = 0;
|
||||
for (unsigned i = 0; i < len; i++) { //sum up the usage of each LED
|
||||
uint32_t c = bus->getPixelColor(i); // always returns original or restored color without brightness scaling
|
||||
byte r = R(c), g = G(c), b = B(c), w = W(c);
|
||||
|
||||
if(useWackyWS2815PowerModel) { //ignore white component on WS2815 power calculation
|
||||
busPowerSum += (MAX(MAX(r,g),b)) * 3;
|
||||
} else {
|
||||
busPowerSum += (r + g + b + w);
|
||||
}
|
||||
}
|
||||
|
||||
if (bus->hasWhite()) { //RGBW led total output with white LEDs enabled is still 50mA, so each channel uses less
|
||||
busPowerSum *= 3;
|
||||
busPowerSum >>= 2; //same as /= 4
|
||||
}
|
||||
powerSum += busPowerSum;
|
||||
}
|
||||
|
||||
if (powerBudget > pLen) { //each LED uses about 1mA in standby, exclude that from power budget
|
||||
powerBudget -= pLen;
|
||||
} else {
|
||||
powerBudget = 0;
|
||||
}
|
||||
|
||||
// powerSum has all the values of channels summed (max would be pLen*765 as white is excluded) so convert to milliAmps
|
||||
powerSum = (powerSum * actualMilliampsPerLed) / 765;
|
||||
|
||||
uint8_t newBri = _brightness;
|
||||
if (powerSum * _brightness / 255 > powerBudget) { //scale brightness down to stay in current limit
|
||||
float scale = (float)(powerBudget * 255) / (float)(powerSum * _brightness);
|
||||
uint16_t scaleI = scale * 255;
|
||||
uint8_t scaleB = (scaleI > 255) ? 255 : scaleI;
|
||||
newBri = scale8(_brightness, scaleB) + 1;
|
||||
}
|
||||
currentMilliamps = (powerSum * newBri) / 255;
|
||||
currentMilliamps += MA_FOR_ESP; //add power of ESP back to estimate
|
||||
currentMilliamps += pLen; //add standby power (1mA/LED) back to estimate
|
||||
return newBri;
|
||||
}
|
||||
|
||||
void WS2812FX::show(void) {
|
||||
// avoid race condition, caputre _callback value
|
||||
show_callback callback = _callback;
|
||||
if (callback) callback();
|
||||
|
||||
uint8_t newBri = estimateCurrentAndLimitBri();
|
||||
busses.setBrightness(newBri); // "repaints" all pixels if brightness changed
|
||||
|
||||
// some buses send asynchronously and this method will return before
|
||||
// all of the data has been sent.
|
||||
// See https://github.com/Makuna/NeoPixelBus/wiki/ESP32-NeoMethods#neoesp32rmt-methods
|
||||
busses.show();
|
||||
|
||||
// restore bus brightness to its original value
|
||||
// this is done right after show, so this is only OK if LED updates are completed before show() returns
|
||||
// or async show has a separate buffer (ESP32 RMT and I2S are ok)
|
||||
if (newBri < _brightness) busses.setBrightness(_brightness);
|
||||
|
||||
unsigned long showNow = millis();
|
||||
size_t diff = showNow - _lastShow;
|
||||
size_t fpsCurr = 200;
|
||||
|
@ -101,6 +101,8 @@ BusDigital::BusDigital(BusConfig &bc, uint8_t nr, const ColorOrderMap &com)
|
||||
: Bus(bc.type, bc.start, bc.autoWhite, bc.count, bc.reversed, (bc.refreshReq || bc.type == TYPE_TM1814))
|
||||
, _skip(bc.skipAmount) //sacrificial pixels
|
||||
, _colorOrder(bc.colorOrder)
|
||||
, _milliAmpsPerLed(bc.milliAmpsPerLed)
|
||||
, _milliAmpsMax(bc.milliAmpsMax)
|
||||
, _colorOrderMap(com)
|
||||
{
|
||||
if (!IS_DIGITAL(bc.type) || !bc.count) return;
|
||||
@ -126,8 +128,81 @@ BusDigital::BusDigital(BusConfig &bc, uint8_t nr, const ColorOrderMap &com)
|
||||
DEBUG_PRINTF("%successfully inited strip %u (len %u) with type %u and pins %u,%u (itype %u)\n", _valid?"S":"Uns", nr, bc.count, bc.type, _pins[0], _pins[1], _iType);
|
||||
}
|
||||
|
||||
//fine tune power estimation constants for your setup
|
||||
//you can set it to 0 if the ESP is powered by USB and the LEDs by external
|
||||
#ifndef MA_FOR_ESP
|
||||
#ifdef ESP8266
|
||||
#define MA_FOR_ESP 80 //how much mA does the ESP use (Wemos D1 about 80mA)
|
||||
#else
|
||||
#define MA_FOR_ESP 120 //how much mA does the ESP use (ESP32 about 120mA)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
//DISCLAIMER
|
||||
//The following function attemps to calculate the current LED power usage,
|
||||
//and will limit the brightness to stay below a set amperage threshold.
|
||||
//It is NOT a measurement and NOT guaranteed to stay within the ablMilliampsMax margin.
|
||||
//Stay safe with high amperage and have a reasonable safety margin!
|
||||
//I am NOT to be held liable for burned down garages or houses!
|
||||
|
||||
// To disable brightness limiter we either set output max current to 0 or single LED current to 0
|
||||
uint8_t BusDigital::estimateCurrentAndLimitBri() {
|
||||
bool useWackyWS2815PowerModel = false;
|
||||
byte actualMilliampsPerLed = _milliAmpsPerLed;
|
||||
|
||||
if (_milliAmpsMax < MA_FOR_ESP || actualMilliampsPerLed == 0) { //0 mA per LED and too low numbers turn off calculation
|
||||
return _bri;
|
||||
}
|
||||
|
||||
if (_milliAmpsPerLed == 255) {
|
||||
useWackyWS2815PowerModel = true;
|
||||
actualMilliampsPerLed = 12; // from testing an actual strip
|
||||
}
|
||||
|
||||
size_t powerBudget = (_milliAmpsMax - MA_FOR_ESP); //100mA for ESP power
|
||||
|
||||
uint32_t busPowerSum = 0;
|
||||
for (unsigned i = 0; i < getLength(); i++) { //sum up the usage of each LED
|
||||
uint32_t c = getPixelColor(i); // always returns original or restored color without brightness scaling
|
||||
byte r = R(c), g = G(c), b = B(c), w = W(c);
|
||||
|
||||
if (useWackyWS2815PowerModel) { //ignore white component on WS2815 power calculation
|
||||
busPowerSum += (max(max(r,g),b)) * 3;
|
||||
} else {
|
||||
busPowerSum += (r + g + b + w);
|
||||
}
|
||||
}
|
||||
|
||||
if (hasWhite()) { //RGBW led total output with white LEDs enabled is still 50mA, so each channel uses less
|
||||
busPowerSum *= 3;
|
||||
busPowerSum >>= 2; //same as /= 4
|
||||
}
|
||||
|
||||
if (powerBudget > getLength()) { //each LED uses about 1mA in standby, exclude that from power budget
|
||||
powerBudget -= getLength();
|
||||
} else {
|
||||
powerBudget = 0;
|
||||
}
|
||||
|
||||
// powerSum has all the values of channels summed (max would be getLength()*765 as white is excluded) so convert to milliAmps
|
||||
busPowerSum = (busPowerSum * actualMilliampsPerLed) / 765;
|
||||
|
||||
uint8_t newBri = _bri;
|
||||
if (busPowerSum * _bri / 255 > powerBudget) { //scale brightness down to stay in current limit
|
||||
float scale = (float)(powerBudget * 255) / (float)(busPowerSum * _bri);
|
||||
uint16_t scaleI = scale * 255;
|
||||
uint8_t scaleB = (scaleI > 255) ? 255 : scaleI;
|
||||
newBri = unsigned(_bri * scaleB) / 256 + 1;
|
||||
}
|
||||
return newBri;
|
||||
}
|
||||
|
||||
void BusDigital::show() {
|
||||
if (!_valid) return;
|
||||
|
||||
uint8_t newBri = estimateCurrentAndLimitBri();
|
||||
if (newBri < _bri) PolyBus::setBrightness(_busPtr, _iType, newBri); // limit brightness to stay within current limits
|
||||
|
||||
if (_data) { // use _buffering this causes ~20% FPS drop
|
||||
size_t channels = Bus::hasWhite(_type) + 3*Bus::hasRGB(_type);
|
||||
for (size_t i=0; i<_len; i++) {
|
||||
@ -152,8 +227,22 @@ void BusDigital::show() {
|
||||
if (_skip) PolyBus::setPixelColor(_busPtr, _iType, 0, 0, _colorOrderMap.getPixelColorOrder(_start, _colorOrder)); // paint skipped pixels black
|
||||
#endif
|
||||
for (int i=1; i<_skip; i++) PolyBus::setPixelColor(_busPtr, _iType, i, 0, _colorOrderMap.getPixelColorOrder(_start, _colorOrder)); // paint skipped pixels black
|
||||
} else {
|
||||
if (newBri < _bri) {
|
||||
uint16_t hwLen = _len;
|
||||
if (_type == TYPE_WS2812_1CH_X3) hwLen = NUM_ICS_WS2812_1CH_3X(_len); // only needs a third of "RGB" LEDs for NeoPixelBus
|
||||
for (unsigned i = 0; i < hwLen; i++) {
|
||||
// use 0 as color order, actual order does not matter here as we just update the channel values as-is
|
||||
uint32_t c = restoreColorLossy(PolyBus::getPixelColor(_busPtr, _iType, i, 0), _bri);
|
||||
PolyBus::setPixelColor(_busPtr, _iType, i, c, 0); // repaint all pixels with new brightness
|
||||
}
|
||||
}
|
||||
}
|
||||
PolyBus::show(_busPtr, _iType, !_data); // faster if buffer consistency is not important (use !_buffering this causes 20% FPS drop)
|
||||
// restore bus brightness to its original value
|
||||
// this is done right after show, so this is only OK if LED updates are completed before show() returns
|
||||
// or async show has a separate buffer (ESP32 RMT and I2S are ok)
|
||||
if (newBri < _bri) PolyBus::setBrightness(_busPtr, _iType, _bri);
|
||||
}
|
||||
|
||||
bool BusDigital::canShow() {
|
||||
@ -172,7 +261,7 @@ void BusDigital::setBrightness(uint8_t b) {
|
||||
uint8_t prevBri = _bri;
|
||||
Bus::setBrightness(b);
|
||||
PolyBus::setBrightness(_busPtr, _iType, b);
|
||||
|
||||
/*
|
||||
if (_data) return; // use _buffering this causes ~20% FPS drop
|
||||
|
||||
// must update/repaint every LED in the NeoPixelBus buffer to the new brightness
|
||||
@ -185,6 +274,7 @@ void BusDigital::setBrightness(uint8_t b) {
|
||||
uint32_t c = restoreColorLossy(PolyBus::getPixelColor(_busPtr, _iType, i, 0), prevBri);
|
||||
PolyBus::setPixelColor(_busPtr, _iType, i, c, 0);
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
//If LEDs are skipped, it is possible to use the first as a status LED.
|
||||
|
@ -35,8 +35,10 @@ struct BusConfig {
|
||||
uint8_t pins[5] = {LEDPIN, 255, 255, 255, 255};
|
||||
uint16_t frequency;
|
||||
bool doubleBuffer;
|
||||
uint8_t milliAmpsPerLed;
|
||||
uint16_t milliAmpsMax;
|
||||
|
||||
BusConfig(uint8_t busType, uint8_t* ppins, uint16_t pstart, uint16_t len = 1, uint8_t pcolorOrder = COL_ORDER_GRB, bool rev = false, uint8_t skip = 0, byte aw=RGBW_MODE_MANUAL_ONLY, uint16_t clock_kHz=0U, bool dblBfr=false)
|
||||
BusConfig(uint8_t busType, uint8_t* ppins, uint16_t pstart, uint16_t len = 1, uint8_t pcolorOrder = COL_ORDER_GRB, bool rev = false, uint8_t skip = 0, byte aw=RGBW_MODE_MANUAL_ONLY, uint16_t clock_kHz=0U, bool dblBfr=false, uint8_t maPerLed=55, uint16_t maMax=ABL_MILLIAMPS_DEFAULT)
|
||||
: count(len)
|
||||
, start(pstart)
|
||||
, colorOrder(pcolorOrder)
|
||||
@ -45,6 +47,8 @@ struct BusConfig {
|
||||
, autoWhite(aw)
|
||||
, frequency(clock_kHz)
|
||||
, doubleBuffer(dblBfr)
|
||||
, milliAmpsPerLed(maPerLed)
|
||||
, milliAmpsMax(maMax)
|
||||
{
|
||||
refreshReq = (bool) GET_BIT(busType,7);
|
||||
type = busType & 0x7F; // bit 7 may be/is hacked to include refresh info (1=refresh in off state, 0=no refresh)
|
||||
@ -132,6 +136,8 @@ class Bus {
|
||||
virtual uint8_t getColorOrder() { return COL_ORDER_RGB; }
|
||||
virtual uint8_t skippedLeds() { return 0; }
|
||||
virtual uint16_t getFrequency() { return 0U; }
|
||||
virtual uint16_t getLEDCurrent() { return 0; }
|
||||
virtual uint16_t getMaxCurrent() { return 0; }
|
||||
inline void setReversed(bool reversed) { _reversed = reversed; }
|
||||
inline uint16_t getStart() { return _start; }
|
||||
inline void setStart(uint16_t start) { _start = start; }
|
||||
@ -211,6 +217,9 @@ class BusDigital : public Bus {
|
||||
uint8_t getPins(uint8_t* pinArray);
|
||||
uint8_t skippedLeds() { return _skip; }
|
||||
uint16_t getFrequency() { return _frequencykHz; }
|
||||
uint8_t estimateCurrentAndLimitBri();
|
||||
uint16_t getLEDCurrent() { return _milliAmpsPerLed; }
|
||||
uint16_t getMaxCurrent() { return _milliAmpsMax; }
|
||||
void reinit();
|
||||
void cleanup();
|
||||
|
||||
@ -220,6 +229,8 @@ class BusDigital : public Bus {
|
||||
uint8_t _pins[2];
|
||||
uint8_t _iType;
|
||||
uint16_t _frequencykHz;
|
||||
uint8_t _milliAmpsPerLed;
|
||||
uint16_t _milliAmpsMax;
|
||||
void * _busPtr;
|
||||
const ColorOrderMap &_colorOrderMap;
|
||||
//bool _buffering; // temporary until we figure out why comparison "_data" causes severe FPS drop
|
||||
|
@ -89,8 +89,9 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
|
||||
JsonObject hw_led = hw["led"];
|
||||
|
||||
uint8_t autoWhiteMode = RGBW_MODE_MANUAL_ONLY;
|
||||
uint16_t total = hw_led[F("total")] | strip.getLengthTotal();
|
||||
CJSON(strip.ablMilliampsMax, hw_led[F("maxpwr")]);
|
||||
CJSON(strip.milliampsPerLed, hw_led[F("ledma")]);
|
||||
CJSON(strip.milliampsPerLed, hw_led[F("ledma")]); // no longer used
|
||||
Bus::setGlobalAWMode(hw_led[F("rgbwm")] | 255);
|
||||
CJSON(correctWB, hw_led["cct"]);
|
||||
CJSON(cctFromRgb, hw_led[F("cr")]);
|
||||
@ -167,8 +168,15 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
|
||||
uint16_t freqkHz = elm[F("freq")] | 0; // will be in kHz for DotStar and Hz for PWM (not yet implemented fully)
|
||||
ledType |= refresh << 7; // hack bit 7 to indicate strip requires off refresh
|
||||
uint8_t AWmode = elm[F("rgbwm")] | autoWhiteMode;
|
||||
uint8_t maPerLed = elm[F("ledma")] | strip.milliampsPerLed; // replace with 55 when removing strip.milliampsPerLed
|
||||
uint16_t maMax = elm[F("maxpwr")] | (strip.ablMilliampsMax * length) / total; // rough (incorrect?) per strip ABL calculation when no config exists
|
||||
// To disable brightness limiter we either set output max current to 0 or single LED current to 0 (we choose output max current)
|
||||
if ((ledType > TYPE_TM1814 && ledType < TYPE_WS2801) || ledType >= TYPE_NET_DDP_RGB) { // analog and virtual
|
||||
maPerLed = 0;
|
||||
maMax = 0;
|
||||
}
|
||||
if (fromFS) {
|
||||
BusConfig bc = BusConfig(ledType, pins, start, length, colorOrder, reversed, skipFirst, AWmode, freqkHz, useGlobalLedBuffer);
|
||||
BusConfig bc = BusConfig(ledType, pins, start, length, colorOrder, reversed, skipFirst, AWmode, freqkHz, useGlobalLedBuffer, maPerLed, maMax);
|
||||
mem += BusManager::memUsage(bc);
|
||||
if (useGlobalLedBuffer && start + length > maxlen) {
|
||||
maxlen = start + length;
|
||||
@ -177,7 +185,7 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
|
||||
if (mem + globalBufMem <= MAX_LED_MEMORY) if (busses.add(bc) == -1) break; // finalization will be done in WLED::beginStrip()
|
||||
} else {
|
||||
if (busConfigs[s] != nullptr) delete busConfigs[s];
|
||||
busConfigs[s] = new BusConfig(ledType, pins, start, length, colorOrder, reversed, skipFirst, AWmode, freqkHz, useGlobalLedBuffer);
|
||||
busConfigs[s] = new BusConfig(ledType, pins, start, length, colorOrder, reversed, skipFirst, AWmode, freqkHz, useGlobalLedBuffer, maPerLed, maMax);
|
||||
busesChanged = true;
|
||||
}
|
||||
s++;
|
||||
@ -717,9 +725,9 @@ void serializeConfig() {
|
||||
JsonObject hw = doc.createNestedObject("hw");
|
||||
|
||||
JsonObject hw_led = hw.createNestedObject("led");
|
||||
hw_led[F("total")] = strip.getLengthTotal(); //no longer read, but provided for compatibility on downgrade
|
||||
hw_led[F("total")] = strip.getLengthTotal(); //provided for compatibility on downgrade and per-output ABL
|
||||
hw_led[F("maxpwr")] = strip.ablMilliampsMax;
|
||||
hw_led[F("ledma")] = strip.milliampsPerLed;
|
||||
hw_led[F("ledma")] = strip.milliampsPerLed; // no longer used
|
||||
hw_led["cct"] = correctWB;
|
||||
hw_led[F("cr")] = cctFromRgb;
|
||||
hw_led[F("cb")] = strip.cctBlending;
|
||||
@ -766,6 +774,8 @@ void serializeConfig() {
|
||||
ins["ref"] = bus->isOffRefreshRequired();
|
||||
ins[F("rgbwm")] = bus->getAutoWhiteMode();
|
||||
ins[F("freq")] = bus->getFrequency();
|
||||
ins[F("maxpwr")] = bus->getMaxCurrent();
|
||||
ins[F("ledma")] = bus->getLEDCurrent();
|
||||
}
|
||||
|
||||
JsonArray hw_com = hw.createNestedArray(F("com"));
|
||||
|
@ -107,32 +107,55 @@
|
||||
}
|
||||
function enABL()
|
||||
{
|
||||
var en = gId('able').checked;
|
||||
d.Sf.LA.value = (en) ? laprev:0;
|
||||
var en = d.Sf["ABL"].checked;
|
||||
d.Sf["MA"].min = en ? 250 : 0;
|
||||
gId('abl').style.display = (en) ? 'inline':'none';
|
||||
gId('psu2').style.display = (en) ? 'inline':'none';
|
||||
if (d.Sf.LA.value > 0) setABL();
|
||||
if (!en) d.Sf["PPL"].checked = false;
|
||||
enPPL();
|
||||
UI();
|
||||
}
|
||||
function enLA()
|
||||
function enPPL()
|
||||
{
|
||||
var val = d.Sf.LAsel.value;
|
||||
d.Sf.LA.value = val;
|
||||
gId('LAdis').style.display = (val == 50) ? 'inline':'none';
|
||||
UI();
|
||||
const abl = d.Sf["ABL"].checked;
|
||||
const en = d.Sf["PPL"].checked;
|
||||
d.Sf["MA"].readonly = en;
|
||||
gId("ppldis").style.display = en ? 'inline' : 'none';
|
||||
d.Sf.querySelectorAll("#mLC input[name^=MA]").forEach((i,n)=>{
|
||||
gId("PSU"+n).style.display = en ? "inline" : "none";
|
||||
const t = parseInt(d.Sf["LT"+n].value); // LED type SELECT
|
||||
i.min = en && !((t >= 80 && t < 96) || (t >= 40 && t < 48)) ? 250 : 0;
|
||||
if (!abl) i.value = 0;
|
||||
});
|
||||
}
|
||||
function enLA(s,n)
|
||||
{
|
||||
const t = parseInt(d.Sf["LT"+n].value); // LED type SELECT
|
||||
gId('LAdis'+n).style.display = s.selectedIndex==5 ? "inline" : "none";
|
||||
d.Sf["LA"+n].value = s.value==="0" ? 55 : s.value;
|
||||
d.Sf["LA"+n].min = ((t >= 80 && t < 96) || (t >= 40 && t < 48)) ? 0 : 1;
|
||||
}
|
||||
function setABL()
|
||||
{
|
||||
gId('able').checked = true;
|
||||
d.Sf.LAsel.value = 50;
|
||||
switch (parseInt(d.Sf.LA.value)) {
|
||||
case 0: gId('able').checked = false; enABL(); break;
|
||||
case 30: d.Sf.LAsel.value = 30; break;
|
||||
case 35: d.Sf.LAsel.value = 35; break;
|
||||
case 55: d.Sf.LAsel.value = 55; break;
|
||||
case 255: d.Sf.LAsel.value = 255; break;
|
||||
default: gId('LAdis').style.display = 'inline';
|
||||
d.Sf["ABL"].checked = false;
|
||||
// check if ABL is enabled (max mA entered per output)
|
||||
d.Sf.querySelectorAll("#mLC input[name^=MA]").forEach((i,n)=>{
|
||||
if (parseInt(i.value) > 0) d.Sf["ABL"].checked = true;
|
||||
});
|
||||
// select appropriate LED current
|
||||
d.Sf.querySelectorAll("#mLC select[name^=LAsel]").forEach((sel,n)=>{
|
||||
sel.value = 0; // set custom
|
||||
switch (parseInt(d.Sf["LA"+n].value)) {
|
||||
case 0: break; // disable ABL
|
||||
case 15: sel.value = 15; break;
|
||||
case 30: sel.value = 30; break;
|
||||
case 35: sel.value = 35; break;
|
||||
case 55: sel.value = 55; break;
|
||||
case 255: sel.value = 255; break;
|
||||
}
|
||||
enLA(sel,n);
|
||||
});
|
||||
enABL();
|
||||
gId('m1').innerHTML = maxM;
|
||||
}
|
||||
//returns mem usage
|
||||
@ -161,19 +184,22 @@
|
||||
function UI(change=false)
|
||||
{
|
||||
let isRGBW = false, gRGBW = false, memu = 0;
|
||||
|
||||
gId('ampwarning').style.display = (d.Sf.MA.value > 7200) ? 'inline':'none';
|
||||
|
||||
if (d.Sf.LA.value == 255) laprev = 12;
|
||||
else if (d.Sf.LA.value > 0) laprev = d.Sf.LA.value;
|
||||
let sumMA = 0, busMA = 0;
|
||||
let sLC = 0, sPC = 0, sDI = 0, maxLC = 0;
|
||||
const ablEN = d.Sf["ABL"].checked;
|
||||
const pplEN = d.Sf["PPL"].checked;
|
||||
|
||||
// enable/disable LED fields
|
||||
d.Sf.querySelectorAll("#mLC select[name^=LT]").forEach((s)=>{
|
||||
// is the field a LED type?
|
||||
var n = s.name.substring(2);
|
||||
var t = parseInt(s.value);
|
||||
let isDig = ((t >= 16 && t < 32) || (t >= 50 && t < 64));
|
||||
let isVir = (t >= 80 && t < 96);
|
||||
let isPwm = (t >= 40 && t < 48);
|
||||
gId("p0d"+n).innerHTML = (t>=80 && t<96) ? "IP address:" : (t > 49) ? "Data GPIO:" : (t > 41) ? "GPIOs:" : "GPIO:";
|
||||
gId("p1d"+n).innerHTML = (t> 49 && t<64) ? "Clk GPIO:" : "";
|
||||
gId("abl"+n).style.display = ((t >= 80 && t < 96) || (t >= 40 && t < 48)) ? "none" : "inline";
|
||||
//var LK = d.getElementsByName("L1"+n)[0]; // clock pin
|
||||
|
||||
memu += getMem(t, n); // calc memory
|
||||
@ -197,6 +223,8 @@
|
||||
if (change) {
|
||||
gId("rf"+n).checked = (gId("rf"+n).checked || t == 31); // LEDs require data in off state
|
||||
if (t > 31 && t < 48) d.getElementsByName("LC"+n)[0].value = 1; // for sanity change analog count just to 1 LED
|
||||
d.Sf["LA"+n].min = ((t >= 80 && t < 96) || (t >= 40 && t < 48)) ? 0 : 1;
|
||||
d.Sf["MA"+n].min = ((t >= 80 && t < 96) || (t >= 40 && t < 48)) ? 0 : 250;
|
||||
}
|
||||
gId("rf"+n).onclick = (t == 31) ? (()=>{return false}) : (()=>{}); // prevent change for TM1814
|
||||
gRGBW |= isRGBW = ((t > 17 && t < 22) || (t > 28 && t < 32) || (t > 40 && t < 46 && t != 43) || t == 88); // RGBW checkbox, TYPE_xxxx values from const.h
|
||||
@ -211,6 +239,9 @@
|
||||
gId("dig"+n+"l").style.display = (t > 48 && t < 64) ? "inline":"none"; // bus clock speed
|
||||
gId("rev"+n).innerHTML = (t >= 40 && t < 48) ? "Inverted output":"Reversed (rotated 180°)"; // change reverse text for analog
|
||||
gId("psd"+n).innerHTML = (t >= 40 && t < 48) ? "Index:":"Start:"; // change analog start description
|
||||
if (ablEN && pplEN && !((t >= 80 && t < 96) || (t >= 40 && t < 48))) {
|
||||
sumMA += parseInt(d.Sf["MA"+n].value);
|
||||
}
|
||||
});
|
||||
// display global white channel overrides
|
||||
gId("wc").style.display = (gRGBW) ? 'inline':'none';
|
||||
@ -220,7 +251,6 @@
|
||||
}
|
||||
// check for pin conflicts
|
||||
var LCs = d.Sf.querySelectorAll("#mLC input[name^=L]"); // input fields
|
||||
var sLC = 0, sPC = 0, maxLC = 0;
|
||||
for (i=0; i<LCs.length; i++) {
|
||||
var nm = LCs[i].name.substring(0,2); // field name
|
||||
var n = LCs[i].name.substring(2); // bus number
|
||||
@ -233,19 +263,25 @@
|
||||
var s = parseInt(gId("ls"+n).value); //start value
|
||||
if (s+c > sLC) sLC = s+c; //update total count
|
||||
if(c>maxLC)maxLC=c; //max per output
|
||||
var t = parseInt(d.getElementsByName("LT"+n)[0].value); // LED type SELECT
|
||||
var t = parseInt(d.Sf["LT"+n].value); // LED type SELECT
|
||||
if (t<80) sPC+=c; //virtual out busses do not count towards physical LEDs
|
||||
if (!((t >= 80 && t < 96) || (t >= 40 && t < 48))) sDI+=c;
|
||||
if (!((t >= 80 && t < 96) || (t >= 40 && t < 48))) {
|
||||
let maPL = parseInt(d.Sf["LA"+n].value);
|
||||
if (maPL==255) maPL = 12;
|
||||
busMA += maPL*c;
|
||||
}
|
||||
} // increase led count
|
||||
continue;
|
||||
}
|
||||
// do we have led pins for digital leds
|
||||
if (nm=="L0" || nm=="L1") {
|
||||
var lc=d.getElementsByName("LC"+n)[0];
|
||||
var lc=d.Sf["LC"+n];
|
||||
lc.max=maxPB; // update max led count value
|
||||
}
|
||||
// ignore IP address (stored in pins for virtual busses)
|
||||
if (nm=="L0" || nm=="L1" || nm=="L2" || nm=="L3") {
|
||||
var t = parseInt(d.getElementsByName("LT"+n)[0].value); // LED type SELECT
|
||||
var t = parseInt(d.Sf["LT"+n].value); // LED type SELECT
|
||||
if (t>=80) {
|
||||
LCs[i].max = 255;
|
||||
LCs[i].min = 0;
|
||||
@ -267,7 +303,7 @@
|
||||
if (n2=="L0" || n2=="L1" || n2=="L2" || n2=="L3" || n2=="L4"/* || n2=="RL" || n2=="BT" || n2=="IR"*/) {
|
||||
if (n2.substring(0,1)==="L") {
|
||||
var m = LCs[j].name.substring(2);
|
||||
var t2 = parseInt(d.getElementsByName("LT"+m)[0].value, 10);
|
||||
var t2 = parseInt(d.Sf["LT"+m].value, 10);
|
||||
if (t2>=80) continue;
|
||||
}
|
||||
if (LCs[j].value!="" && LCs[j].value!="-1") p.push(parseInt(LCs[j].value,10)); // add current pin
|
||||
@ -282,6 +318,14 @@
|
||||
// LCs[i].min = -1;
|
||||
//}
|
||||
}
|
||||
d.Sf.querySelectorAll("#mLC input[name^=LC]").forEach((s,n)=>{
|
||||
let c = parseInt(s.value,10); //get LED count
|
||||
let t = parseInt(d.Sf["LT"+n].value);
|
||||
if (ablEN) {
|
||||
let v = Math.round(parseInt(d.Sf["MA"].value,10)*c/sDI);
|
||||
if (!pplEN && !((t >= 80 && t < 96) || (t >= 40 && t < 48))) d.Sf["MA"+n].value = v;
|
||||
} else d.Sf["MA"+n].value = 0;
|
||||
});
|
||||
// update total led count
|
||||
gId("lc").textContent = sLC;
|
||||
gId("pc").textContent = (sLC == sPC) ? "":"(" + sPC + " physical)";
|
||||
@ -294,27 +338,20 @@
|
||||
gId('ledwarning').style.color = (maxLC > Math.max(maxPB,800) || bquot > 100) ? 'red':'orange';
|
||||
gId('wreason').innerHTML = (bquot > 80) ? "80% of max. LED memory" +(bquot>100 ? ` (<b>ERROR: Using over ${maxM}B!</b>)` : "") : "800 LEDs per output";
|
||||
// calculate power
|
||||
var val = Math.ceil((100 + sPC * laprev)/500)/2;
|
||||
if (pplEN) d.Sf.MA.value = sumMA;
|
||||
gId('ampwarning').style.display = (parseInt(d.Sf.MA.value,10) > 7200) ? 'inline':'none';
|
||||
var val = Math.ceil((100 + busMA)/500)/2;
|
||||
val = (val > 5) ? Math.ceil(val) : val;
|
||||
var s = "";
|
||||
var is12V = (d.Sf.LAsel.value == 30);
|
||||
var isWS2815 = (d.Sf.LAsel.value == 255);
|
||||
if (val < 1.02 && !is12V && !isWS2815)
|
||||
{
|
||||
s = "ESP 5V pin with 1A USB supply";
|
||||
} else
|
||||
{
|
||||
s += is12V ? "12V ": isWS2815 ? "WS2815 12V " : "5V ";
|
||||
var s = "A power supply with total of ";
|
||||
s += val;
|
||||
s += "A supply connected to LEDs";
|
||||
}
|
||||
var val2 = Math.ceil((100 + sPC * laprev)/1500)/2;
|
||||
s += "A is required.";
|
||||
var val2 = Math.ceil((100 + busMA)/1500)/2;
|
||||
val2 = (val2 > 5) ? Math.ceil(val2) : val2;
|
||||
var s2 = "(for most effects, ~";
|
||||
s2 += val2;
|
||||
s2 += "A is enough)<br>";
|
||||
gId('psu').innerHTML = s;
|
||||
gId('psu2').innerHTML = isWS2815 ? "" : s2;
|
||||
gId('psu2').innerHTML = s2;
|
||||
gId("json").style.display = d.Sf.IT.value==8 ? "" : "none";
|
||||
}
|
||||
function lastEnd(i) {
|
||||
@ -364,6 +401,18 @@ ${i+1}:
|
||||
<option value="82">Art-Net RGB (network)</option>
|
||||
<option value="88">DDP RGBW (network)</option>
|
||||
</select><br>
|
||||
<div id="abl${i}">
|
||||
mA/LED: <select name="LAsel${i}" onchange="enLA(this,${i});UI();">
|
||||
<option value="55" selected>55mA (typ. 5V WS281x)</option>
|
||||
<option value="35">35mA (eco WS2812)</option>
|
||||
<option value="30">30mA (typ. 12V)</option>
|
||||
<option value="255">12mA (WS2815)</option>
|
||||
<option value="15">15mA (seed/fairy pixels)</option>
|
||||
<option value="0">Custom</option>
|
||||
</select><br>
|
||||
<div id="LAdis${i}" style="display: none;">max. mA/LED: <input name="LA${i}" type="number" min="1" max="254" oninput="UI()"> mA<br></div>
|
||||
<div id="PSU${i}">PSU: <input name="MA${i}" type="number" class="xl" min="250" max="65000" oninput="UI()"> mA<br></div>
|
||||
</div>
|
||||
<div id="co${i}" style="display:inline">Color Order:
|
||||
<select name="CO${i}">
|
||||
<option value="0">GRB</option>
|
||||
@ -697,29 +746,23 @@ Length: <input type="number" name="XC${i}" id="xc${i}" class="l" min="1" max="65
|
||||
<b><span id="psu">?</span></b><br>
|
||||
<span id="psu2"><br></span>
|
||||
<br>
|
||||
Enable automatic brightness limiter: <input type="checkbox" name="ABen" onchange="enABL()" id="able"><br>
|
||||
Enable automatic brightness limiter: <input type="checkbox" name="ABL" onchange="enABL()"><br>
|
||||
<div id="abl">
|
||||
Maximum Current: <input name="MA" type="number" class="l" min="250" max="65000" oninput="UI()" required> mA<br>
|
||||
<i>Automatically limits brightness to stay close to the limit.<br>
|
||||
Keep at <1A if poweing LEDs directly from the ESP 5V pin!<br>
|
||||
Analog (PWM) and virtual LEDs cannot use automatic brightness limiter.<br></i>
|
||||
Maximum PSU Current: <input name="MA" type="number" class="xl" min="250" max="65000" oninput="UI()" required> mA<br>
|
||||
Use per-output limiter: <input type="checkbox" name="PPL" onchange="enPPL()"><br>
|
||||
<div id="ppldis" style="display:none;">
|
||||
<i>Make sure you enter correct values in each LED output.<br>
|
||||
If using multiple outputs with only one PSU, distribute its power proportionally amongst ouputs.</i><br>
|
||||
</div>
|
||||
<div id="ampwarning" class="warn" style="display: none;">
|
||||
⚠ Your power supply provides high current.<br>
|
||||
To improve the safety of your setup,<br>
|
||||
please use thick cables,<br>
|
||||
multiple power injection points and a fuse!<br>
|
||||
</div>
|
||||
<i>Automatically limits brightness to stay close to the limit.<br>
|
||||
Keep at <1A if powering LEDs directly from the ESP 5V pin!<br>
|
||||
If you are using an external power supply, enter its rating.<br>
|
||||
(Current estimated usage: <span class="pow">unknown</span>)</i><br><br>
|
||||
LED voltage (Max. current for a single LED):<br>
|
||||
<select name="LAsel" onchange="enLA()">
|
||||
<option value="55" selected>5V default (55mA)</option>
|
||||
<option value="35">5V efficient (35mA)</option>
|
||||
<option value="30">12V (30mA)</option>
|
||||
<option value="255">WS2815 (12mA)</option>
|
||||
<option value="50">Custom</option>
|
||||
</select><br>
|
||||
<span id="LAdis" style="display: none;">Custom max. current per LED: <input name="LA" type="number" min="0" max="255" id="la" oninput="UI()" required> mA<br></span>
|
||||
<i>Keep at default if you are unsure about your type of LEDs.</i><br>
|
||||
</div>
|
||||
<h3>Hardware setup</h3>
|
||||
<div id="mLC">LED outputs:</div>
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -94,10 +94,13 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t colorOrder, type, skip, awmode, channelSwap;
|
||||
uint16_t length, start;
|
||||
uint8_t colorOrder, type, skip, awmode, channelSwap, maPerLed;
|
||||
uint16_t length, start, maMax;
|
||||
uint8_t pins[5] = {255, 255, 255, 255, 255};
|
||||
|
||||
strip.ablMilliampsMax = request->arg(F("MA")).toInt();
|
||||
//strip.milliampsPerLed = request->arg(F("LA")).toInt();
|
||||
|
||||
autoSegments = request->hasArg(F("MS"));
|
||||
correctWB = request->hasArg(F("CCT"));
|
||||
cctFromRgb = request->hasArg(F("CR"));
|
||||
@ -120,6 +123,8 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
|
||||
char aw[4] = "AW"; aw[2] = 48+s; aw[3] = 0; //auto white mode
|
||||
char wo[4] = "WO"; wo[2] = 48+s; wo[3] = 0; //channel swap
|
||||
char sp[4] = "SP"; sp[2] = 48+s; sp[3] = 0; //bus clock speed (DotStar & PWM)
|
||||
char la[4] = "LA"; la[2] = 48+s; la[3] = 0; //LED mA
|
||||
char ma[4] = "MA"; ma[2] = 48+s; ma[3] = 0; //max mA
|
||||
if (!request->hasArg(lp)) {
|
||||
DEBUG_PRINT(F("No data for "));
|
||||
DEBUG_PRINTLN(s);
|
||||
@ -164,10 +169,17 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
|
||||
freqHz = 0;
|
||||
}
|
||||
channelSwap = (type == TYPE_SK6812_RGBW || type == TYPE_TM1814) ? request->arg(wo).toInt() : 0;
|
||||
if ((type > TYPE_TM1814 && type < TYPE_WS2801) || type >= TYPE_NET_DDP_RGB) { // analog and virtual
|
||||
maPerLed = 0;
|
||||
maMax = 0;
|
||||
} else {
|
||||
maPerLed = request->arg(la).toInt();
|
||||
maMax = request->arg(ma).toInt(); // if ABL is disabled this will be 0
|
||||
}
|
||||
// actual finalization is done in WLED::loop() (removing old busses and adding new)
|
||||
// this may happen even before this loop is finished so we do "doInitBusses" after the loop
|
||||
if (busConfigs[s] != nullptr) delete busConfigs[s];
|
||||
busConfigs[s] = new BusConfig(type, pins, start, length, colorOrder | (channelSwap<<4), request->hasArg(cv), skip, awmode, freqHz, useGlobalLedBuffer);
|
||||
busConfigs[s] = new BusConfig(type, pins, start, length, colorOrder | (channelSwap<<4), request->hasArg(cv), skip, awmode, freqHz, useGlobalLedBuffer, maPerLed, maMax);
|
||||
busesChanged = true;
|
||||
}
|
||||
//doInitBusses = busesChanged; // we will do that below to ensure all input data is processed
|
||||
@ -241,9 +253,6 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
|
||||
}
|
||||
touchThreshold = request->arg(F("TT")).toInt();
|
||||
|
||||
strip.ablMilliampsMax = request->arg(F("MA")).toInt();
|
||||
strip.milliampsPerLed = request->arg(F("LA")).toInt();
|
||||
|
||||
briS = request->arg(F("CA")).toInt();
|
||||
|
||||
turnOnAtBoot = request->hasArg(F("BO"));
|
||||
|
@ -8,7 +8,7 @@
|
||||
*/
|
||||
|
||||
// version code in format yymmddb (b = daily build)
|
||||
#define VERSION 2311120
|
||||
#define VERSION 2311150
|
||||
|
||||
//uncomment this if you have a "my_config.h" file you'd like to use
|
||||
//#define WLED_USE_MY_CONFIG
|
||||
|
@ -354,6 +354,7 @@ void getSettingsJS(byte subPage, char* dest)
|
||||
sappend('v',SET_F("AW"),Bus::getGlobalAWMode());
|
||||
sappend('c',SET_F("LD"),useGlobalLedBuffer);
|
||||
|
||||
uint16_t sumMa = 0;
|
||||
for (uint8_t s=0; s < busses.getNumBusses(); s++) {
|
||||
Bus* bus = busses.getBus(s);
|
||||
if (bus == nullptr) continue;
|
||||
@ -368,6 +369,8 @@ void getSettingsJS(byte subPage, char* dest)
|
||||
char aw[4] = "AW"; aw[2] = 48+s; aw[3] = 0; //auto white mode
|
||||
char wo[4] = "WO"; wo[2] = 48+s; wo[3] = 0; //swap channels
|
||||
char sp[4] = "SP"; sp[2] = 48+s; sp[3] = 0; //bus clock speed
|
||||
char la[4] = "LA"; la[2] = 48+s; la[3] = 0; //LED current
|
||||
char ma[4] = "MA"; ma[2] = 48+s; ma[3] = 0; //max per-port PSU current
|
||||
oappend(SET_F("addLEDs(1);"));
|
||||
uint8_t pins[5];
|
||||
uint8_t nPins = bus->getPins(pins);
|
||||
@ -405,8 +408,13 @@ void getSettingsJS(byte subPage, char* dest)
|
||||
}
|
||||
}
|
||||
sappend('v',sp,speed);
|
||||
sappend('v',la,bus->getLEDCurrent());
|
||||
sappend('v',ma,bus->getMaxCurrent());
|
||||
sumMa += bus->getMaxCurrent();
|
||||
}
|
||||
sappend('c',SET_F("PPL"),(sumMa>0 && abs(sumMa - strip.ablMilliampsMax)>2)); // approxiamte detection if per-output limiter is enabled
|
||||
sappend('v',SET_F("MA"),strip.ablMilliampsMax);
|
||||
/*
|
||||
sappend('v',SET_F("LA"),strip.milliampsPerLed);
|
||||
if (strip.currentMilliamps)
|
||||
{
|
||||
@ -415,7 +423,7 @@ void getSettingsJS(byte subPage, char* dest)
|
||||
oappendi(strip.currentMilliamps);
|
||||
oappend(SET_F("mA\";"));
|
||||
}
|
||||
|
||||
*/
|
||||
oappend(SET_F("resetCOM("));
|
||||
oappend(itoa(WLED_MAX_COLOR_ORDER_MAPPINGS,nS,10));
|
||||
oappend(SET_F(");"));
|
||||
|
Loading…
Reference in New Issue
Block a user