From ff46e6ea86fea147d1b39aa4d8439244f2754dfd Mon Sep 17 00:00:00 2001 From: cschwinne Date: Tue, 4 Dec 2018 00:58:06 +0100 Subject: [PATCH] Added Auto Brightness Limiter and power calculation --- wled00/WS2812FX.cpp | 107 +++++++++++++++++++++++----------- wled00/WS2812FX.h | 11 ++-- wled00/data/settings_leds.htm | Bin 10124 -> 12626 bytes wled00/html_settings.h | 50 ++++++++++------ wled00/wled00.ino | 7 ++- wled00/wled01_eeprom.ino | 49 ++++++++++------ wled00/wled02_xml.ino | 8 +++ wled00/wled03_set.ino | 1 + wled00/wled05_init.ino | 3 +- wled00/wled18_server.ino | 15 +++-- 10 files changed, 164 insertions(+), 87 deletions(-) diff --git a/wled00/WS2812FX.cpp b/wled00/WS2812FX.cpp index c2500cf06..7a2314fd9 100644 --- a/wled00/WS2812FX.cpp +++ b/wled00/WS2812FX.cpp @@ -59,7 +59,6 @@ void WS2812FX::init(bool supportWhite, uint16_t countPixels, bool skipFirst) _segments[0].stop = _length -1; unlockAll(); setBrightness(_brightness); - show(); _running = true; } @@ -127,13 +126,13 @@ void WS2812FX::setPixelColor(uint16_t i, byte r, byte g, byte b, byte w) byte o = 10*i; if (_cronixieBacklightEnabled && _cronixieDigits[i] <11) { - byte rCorr = (int)(((double)((_segments[0].colors[1]>>16) & 0xFF))*_cronixieSecMultiplier); - byte gCorr = (int)(((double)((_segments[0].colors[1]>>8) & 0xFF))*_cronixieSecMultiplier); - byte bCorr = (int)(((double)((_segments[0].colors[1]) & 0xFF))*_cronixieSecMultiplier); - byte wCorr = (int)(((double)((_segments[0].colors[1]>>24) & 0xFF))*_cronixieSecMultiplier); + byte r2 = (_segments[0].colors[1] >>16) & 0xFF; + byte g2 = (_segments[0].colors[1] >> 8) & 0xFF; + byte b2 = (_segments[0].colors[1] ) & 0xFF; + byte w2 = (_segments[0].colors[1] >>24) & 0xFF; for (int j=o; j< o+19; j++) { - bus->SetPixelColor((_skipFirstMode)?j+1:j,RgbwColor(rCorr,gCorr,bCorr,wCorr)); + bus->SetPixelColor((_skipFirstMode)?j+1:j,RgbwColor(r2,g2,b2,w2)); } } else { @@ -182,7 +181,75 @@ void WS2812FX::setCronixieDigits(byte d[]) } } + +//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 PU_PER_MA 3600 //power units per milliamperere for accurate power estimation + //formula: 195075 divided by mA per fully lit LED, here ~54mA) + //lowering the value increases the estimated usage and therefore makes the ABL more aggressive + +#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 + void WS2812FX::show(void) { + //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) + + if (ablMilliampsMax > 149 && ablMilliampsMax < 65000) //lower numbers and 65000 turn off calculation + { + uint32_t powerBudget = (ablMilliampsMax - MA_FOR_ESP) * PU_PER_MA; //100mA for ESP power + if (powerBudget > PU_PER_MA * _length) //each LED uses about 1mA in standby, exclude that from power budget + { + powerBudget -= PU_PER_MA * _length; + } else + { + powerBudget = 0; + } + + uint32_t powerSum = 0; + + for (uint16_t i = 0; i < _length; i++) //sum up the usage of each LED + { + RgbwColor c = bus->GetPixelColorRgbw(i); + powerSum += (c.R + c.G + c.B + c.W); + } + + if (_rgbwMode) //RGBW led total output with white LEDs enabled is still 50mA, so each channel uses less + { + powerSum *= 3; + powerSum >> 2; //same as /= 4 + } + + uint32_t powerSum0 = powerSum; + powerSum *= _brightness; + + if (powerSum > powerBudget) //scale brightness down to stay in current limit + { + float scale = (float)powerBudget / (float)powerSum; + uint16_t scaleI = scale * 255; + uint8_t scaleB = (scaleI > 255) ? 255 : scaleI; + uint8_t newBri = scale8(_brightness, scaleB); + bus->SetBrightness(newBri); + currentMilliamps = (powerSum0 * newBri) / PU_PER_MA; + } else + { + currentMilliamps = powerSum / PU_PER_MA; + bus->SetBrightness(_brightness); + } + currentMilliamps += MA_FOR_ESP; //add power of ESP back to estimate + currentMilliamps += _length; //add standby power back to estimate + } else { + currentMilliamps = 0; + } + bus->Show(); } @@ -237,7 +304,6 @@ void WS2812FX::setColor(uint32_t c) { void WS2812FX::setSecondaryColor(uint32_t c) { _segments[0].colors[1] = c; - if (_cronixieMode) _cronixieSecMultiplier = getSafePowerMultiplier(900, 100, c, _brightness); } void WS2812FX::setBrightness(uint8_t b) { @@ -442,33 +508,6 @@ uint32_t WS2812FX::color_blend(uint32_t color1, uint32_t color2, uint8_t blend) } -double WS2812FX::getPowerEstimate(uint16_t leds, uint32_t c, byte b) -{ - double _mARequired = 100; //ESP power - double _mul = (double)b/255; - double _sum = ((c & 0xFF000000) >> 24) + ((c & 0x00FF0000) >> 16) + ((c & 0x0000FF00) >> 8) + ((c & 0x000000FF) >> 0); - _sum /= (_rgbwMode)?1024:768; - double _mAPerLed = 50*(_mul*_sum); - _mARequired += leds*_mAPerLed; - return _mARequired; -} - -//DISCLAIMER -//This is just a helper function for huge amounts of LEDs. -//It is NOT guaranteed to stay within the safeAmps margin. -//Stay safe with high amperage and have a reasonable safety margin! -//I am NOT to be held liable for burned down garages! -double WS2812FX::getSafePowerMultiplier(double safeMilliAmps, uint16_t leds, uint32_t c, byte b) -{ - double _mARequired = getPowerEstimate(leds,c,b); - if (_mARequired > safeMilliAmps) - { - return safeMilliAmps/_mARequired; - } - return 1.0; -} - - /* ##################################################### # # Color and Blinken Functions diff --git a/wled00/WS2812FX.h b/wled00/WS2812FX.h index 8c5df9c2f..679ee7312 100644 --- a/wled00/WS2812FX.h +++ b/wled00/WS2812FX.h @@ -289,6 +289,8 @@ class WS2812FX { colorOrder = 0; paletteFade = 0; paletteBlend = 0; + ablMilliampsMax = 750; + currentMilliamps = 0; _locked = NULL; _cronixieDigits = new byte[6]; bus = new NeoPixelWrapper(); @@ -352,10 +354,6 @@ class WS2812FX { getPixelColor(uint16_t), getColor(void); - double - getPowerEstimate(uint16_t leds, uint32_t c, byte b), - getSafePowerMultiplier(double safeMilliAmps, uint16_t leds, uint32_t c, byte b); - WS2812FX::Segment getSegment(void); @@ -367,6 +365,8 @@ class WS2812FX { // mode helper functions uint16_t + ablMilliampsMax, + currentMilliamps, blink(uint32_t, uint32_t, bool strobe, bool), color_wipe(uint32_t, uint32_t, bool , bool), scan(bool), @@ -473,9 +473,6 @@ class WS2812FX { void handle_palette(void); bool modeUsesLock(uint8_t); - double - _cronixieSecMultiplier; - boolean _running, _rgbwMode, diff --git a/wled00/data/settings_leds.htm b/wled00/data/settings_leds.htm index a91ea5a9789b741970571eed0448b6763037a5a5..248448f274d93848265431b71c5defdc5c4d0bc7 100644 GIT binary patch delta 2422 zcma)8+inwA6y1|hRW6q#o&*hY%8(i`s~wwzzq7X zl9E|@B43LqTXIXK3vyGI!>66+&nNpKn)l8Te_IOD5LYVFlFPVX#9Eh{tjR;{c~XUo zj68tkL9Zow+{;*N*sozH2dNClGthd3-5G>FAqV9PN$9T}`n3KtYFm{}SQq7j9Q50Y z8|u!5t5Rr^g@UWn0`BEdN=q6cT;vxANrdo!hV~r*X~KO=LuHUm3344$0ed#VR69jK z&T$i2ZOK(Ni*_%;WEslLuL0Dn$dMUlkZDn?a39xo1+)a`GYD3N2VOl$^H^0RW+mj? zPIt#X`&y)UJ8B2-QR%MZndM?o53X76l$@3meN2XuY~LD|1%N6mPs1n->R6V6%l za{rG`K5!1)>l-I#n%9xb7L3AE$P`At@)q!BQEq5PHGr@%*2CQD$nB8^wVjG95uFsP z093+I7PTIndG@-f-S$Ai1#rA%oPpuo0tR;xGS7hm(C}1Q2bv^gRym2cOR67fQ2SJ9 zg*fywMan;hWd#-$__&OF13x_^&Fisa{nsUD{uxY9&FO3x3fDp*_6G=-^gKk7R3wI|vdG;7F%^KBWy(}GfL zn?XLT*J7K_*P!7ce^+C=Ku|?Z*yLzV>@>!GU>`Sqyp9ORgQuhgASEr_4aiJ{P%MaE zLpi+%&a#ev3$_mx63a?ZO+?`Lf-u=Q-$M_24O{25oPeVJblH5gdf01duGZn+igfe{ z{jw8RL4`OXjHn6vnsWiTR`mrBpD5F~R5WG_`4#1dhfU=u>H)K!2DS+SGm}*iSqDb|WdL*lR +body{font-family:var(--cFn),sans-serif;text-align:center;background:var(--cCol);color:var(--tCol);line-height:200%;margin:0;background-attachment:fixed}hr{border-color:var(--dCol);filter:drop-shadow(-5px -5px 5px var(--sCol))}button{background:var(--bCol);color:var(--tCol);font-family:var(--cFn),sans-serif;border:.3ch solid var(--bCol);display:inline-block;filter:drop-shadow(-5px -5px 5px var(--sCol));font-size:20px;margin:8px;margin-top:12px}.helpB{text-align:left;position:absolute;width:60px}input{background:var(--bCol);color:var(--tCol);font-family:var(--cFn),sans-serif;border:.5ch solid var(--bCol);filter:drop-shadow(-5px -5px 5px var(--sCol))}input[type=number]{width:4em}select{background:var(--bCol);color:var(--tCol);font-family:var(--cFn),sans-serif;border:0.5ch solid var(--bCol);filter:drop-shadow( -5px -5px 5px var(--sCol) );} )====="; @@ -84,50 +84,62 @@ AP IP: Not active
const char PAGE_settings_leds0[] PROGMEM = R"=====( -LED Settings