diff --git a/CHANGELOG.md b/CHANGELOG.md index a285f1fe0..ae4121484 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,19 +1,37 @@ ## WLED changelog -#### Build 2309120 till build 2311120 +#### Build 2309120 till build 2312290 - WLED version 0.15.0-a0 +- Per port Auto Brightness Limiter (ABL) +- Use PSRAM for JSON buffer (double size, larger ledmaps, up to 2k) +- Reduced heap fragmentation by allocating ledmap array only once and not deallocating effect buffer +- HTTP retries on failed UI load +- UI Search: scroll to top (#3587 by @WoodyLetsCode) +- Return to inline iro.js and rangetouch.js (#3597 by @WoodyLetsCode) +- Better caching (#3591 by @WoodyLetsCode) +- Do not send 404 for missing `skin.css` (#3590 by @WoodyLetsCode) +- Simplified UI rework (#3511 by @WoodyLetsCode) +- Domoticz device ID for PIR and Temperature usermods +- Bugfix for UCS8904 `hasWhite()` +- Better search in UI (#3540 by @WoodyLetsCode) +- Seeding FastLED PRNG (#3552 by @TripleWhy) +- WIZ Smart Button support (#3547 by @micw) +- New button type (button switch, fix for #3537) +- Pixel Magic Tool update (#3483 by @ajotanc) +- Effect: 2D Matrix fix for gaps +- Bugfix #3526, #3533, #3561 - Spookier Halloween Eyes (#3501) - Compile time options for Multi Relay usermod (#3498) -- Fix for Dissolve (#3502) +- Effect: Fix for Dissolve (#3502) - Better reverse proxy support (nested paths) - Implement global JSON API boolean toggle (i.e. instead of "var":true or "var":false -> "var":"t"). - Sort presets by ID -- Fix #3496 +- Fix for #3496, #2922, #3593, #3514, #3522, #3578 (partial), #3606 (@WoodyLetsCode) - Improved random bg image and added random bg image options (@WoodyLetsCode, #3481) - Audio palettes (Audioreactive usermod, credit @netmindz) - Better UI tooltips (@ajotnac, #3464) - Better effect filters (filter dropdown) -- Fix udp sync (fix for #3487) +- UDP sync fix (for #3487) - Power button override (solves #3431) - Additional HTTP request throttling (ESP8266) - Additional UI/UX improvements diff --git a/usermods/ST7789_display/ST7789_display.h b/usermods/ST7789_display/ST7789_display.h index 144cccbfa..59e9cd231 100644 --- a/usermods/ST7789_display/ST7789_display.h +++ b/usermods/ST7789_display/ST7789_display.h @@ -307,7 +307,7 @@ class St7789DisplayUsermod : public Usermod { // Print estimated milliamp usage (must specify the LED type in LED prefs for this to be a reasonable estimate). tft.print("Current: "); tft.setTextColor(TFT_ORANGE); - tft.print(strip.currentMilliamps); + tft.print(BusManager::currentMilliamps()); tft.print("mA"); } diff --git a/usermods/quinled-an-penta/quinled-an-penta.h b/usermods/quinled-an-penta/quinled-an-penta.h index 5153ee58a..10b784334 100644 --- a/usermods/quinled-an-penta/quinled-an-penta.h +++ b/usermods/quinled-an-penta/quinled-an-penta.h @@ -84,11 +84,11 @@ class QuinLEDAnPentaUsermod : public Usermod void getCurrentUsedLedPins() { for (int8_t lp = 0; lp <= 4; lp++) currentLedPins[lp] = 0; - byte numBusses = busses.getNumBusses(); + byte numBusses = BusManager::getNumBusses(); byte numUsedPins = 0; for (int8_t b = 0; b < numBusses; b++) { - Bus* curBus = busses.getBus(b); + Bus* curBus = BusManager::getBus(b); if (curBus != nullptr) { uint8_t pins[5] = {0, 0, 0, 0, 0}; currentBussesNumPins[b] = curBus->getPins(pins); @@ -104,11 +104,11 @@ class QuinLEDAnPentaUsermod : public Usermod void getCurrentLedcValues() { - byte numBusses = busses.getNumBusses(); + byte numBusses = BusManager::getNumBusses(); byte numLedc = 0; for (int8_t b = 0; b < numBusses; b++) { - Bus* curBus = busses.getBus(b); + Bus* curBus = BusManager::getBus(b); if (curBus != nullptr) { uint32_t curPixColor = curBus->getPixelColor(0); uint8_t _data[5] = {255, 255, 255, 255, 255}; diff --git a/wled00/FX.cpp b/wled00/FX.cpp index 068076bcd..a27fc4fbe 100644 --- a/wled00/FX.cpp +++ b/wled00/FX.cpp @@ -3444,8 +3444,8 @@ static const char _data_FX_MODE_STARBURST[] PROGMEM = "Fireworks Starburst@Chanc uint16_t mode_exploding_fireworks(void) { if (SEGLEN == 1) return mode_static(); - const uint16_t cols = strip.isMatrix ? SEGMENT.virtualWidth() : 1; - const uint16_t rows = strip.isMatrix ? SEGMENT.virtualHeight() : SEGMENT.virtualLength(); + const uint16_t cols = SEGMENT.is2D() ? SEGMENT.virtualWidth() : 1; + const uint16_t rows = SEGMENT.is2D() ? SEGMENT.virtualHeight() : SEGMENT.virtualLength(); //allocate segment data uint16_t maxData = FAIR_DATA_PER_SEG; //ESP8266: 256 ESP32: 640 @@ -3476,11 +3476,11 @@ uint16_t mode_exploding_fireworks(void) if (SEGENV.aux0 < 2) { //FLARE if (SEGENV.aux0 == 0) { //init flare flare->pos = 0; - flare->posX = strip.isMatrix ? random16(2,cols-3) : (SEGMENT.intensity > random8()); // will enable random firing side on 1D + flare->posX = SEGMENT.is2D() ? random16(2,cols-3) : (SEGMENT.intensity > random8()); // will enable random firing side on 1D uint16_t peakHeight = 75 + random8(180); //0-255 peakHeight = (peakHeight * (rows -1)) >> 8; flare->vel = sqrtf(-2.0f * gravity * peakHeight); - flare->velX = strip.isMatrix ? (random8(9)-4)/32.f : 0; // no X velocity on 1D + flare->velX = SEGMENT.is2D() ? (random8(9)-4)/64.0f : 0; // no X velocity on 1D flare->col = 255; //brightness SEGENV.aux0 = 1; } @@ -3488,12 +3488,14 @@ uint16_t mode_exploding_fireworks(void) // launch if (flare->vel > 12 * gravity) { // flare - if (strip.isMatrix) SEGMENT.setPixelColorXY(int(flare->posX), rows - uint16_t(flare->pos) - 1, flare->col, flare->col, flare->col); - else SEGMENT.setPixelColor(int(flare->posX) ? rows - int(flare->pos) - 1 : int(flare->pos), flare->col, flare->col, flare->col); + if (SEGMENT.is2D()) SEGMENT.setPixelColorXY(int(flare->posX), rows - uint16_t(flare->pos) - 1, flare->col, flare->col, flare->col); + else SEGMENT.setPixelColor((flare->posX > 0.0f) ? rows - int(flare->pos) - 1 : int(flare->pos), flare->col, flare->col, flare->col); flare->pos += flare->vel; - flare->posX += flare->velX; flare->pos = constrain(flare->pos, 0, rows-1); - flare->posX = constrain(flare->posX, 0, cols-strip.isMatrix); + if (SEGMENT.is2D()) { + flare->posX += flare->velX; + flare->posX = constrain(flare->posX, 0, cols-1); + } flare->vel += gravity; flare->col -= 2; } else { @@ -3516,12 +3518,12 @@ uint16_t mode_exploding_fireworks(void) sparks[i].posX = flare->posX; sparks[i].vel = (float(random16(20001)) / 10000.0f) - 0.9f; // from -0.9 to 1.1 sparks[i].vel *= rows<32 ? 0.5f : 1; // reduce velocity for smaller strips - sparks[i].velX = strip.isMatrix ? (float(random16(10001)) / 10000.0f) - 0.5f : 0; // from -0.5 to 0.5 + sparks[i].velX = SEGMENT.is2D() ? (float(random16(20001)) / 10000.0f) - 1.0f : 0; // from -1 to 1 sparks[i].col = 345;//abs(sparks[i].vel * 750.0); // set colors before scaling velocity to keep them bright //sparks[i].col = constrain(sparks[i].col, 0, 345); sparks[i].colIndex = random8(); sparks[i].vel *= flare->pos/rows; // proportional to height - sparks[i].velX *= strip.isMatrix ? flare->posX/cols : 0; // proportional to width + sparks[i].velX *= SEGMENT.is2D() ? flare->posX/cols : 0; // proportional to width sparks[i].vel *= -gravity *50; } //sparks[1].col = 345; // this will be our known spark @@ -3534,11 +3536,11 @@ uint16_t mode_exploding_fireworks(void) sparks[i].pos += sparks[i].vel; sparks[i].posX += sparks[i].velX; sparks[i].vel += *dying_gravity; - sparks[i].velX += strip.isMatrix ? *dying_gravity : 0; + sparks[i].velX += SEGMENT.is2D() ? *dying_gravity : 0; if (sparks[i].col > 3) sparks[i].col -= 4; if (sparks[i].pos > 0 && sparks[i].pos < rows) { - if (strip.isMatrix && !(sparks[i].posX >= 0 && sparks[i].posX < cols)) continue; + if (SEGMENT.is2D() && !(sparks[i].posX >= 0 && sparks[i].posX < cols)) continue; uint16_t prog = sparks[i].col; uint32_t spColor = (SEGMENT.palette) ? SEGMENT.color_wheel(sparks[i].colIndex) : SEGCOLOR(0); CRGB c = CRGB::Black; //HeatColor(sparks[i].col); @@ -3550,7 +3552,7 @@ uint16_t mode_exploding_fireworks(void) c.g = qsub8(c.g, cooling); c.b = qsub8(c.b, cooling * 2); } - if (strip.isMatrix) SEGMENT.setPixelColorXY(int(sparks[i].posX), rows - int(sparks[i].pos) - 1, c.red, c.green, c.blue); + if (SEGMENT.is2D()) SEGMENT.setPixelColorXY(int(sparks[i].posX), rows - int(sparks[i].pos) - 1, c.red, c.green, c.blue); else SEGMENT.setPixelColor(int(sparks[i].posX) ? rows - int(sparks[i].pos) - 1 : int(sparks[i].pos), c.red, c.green, c.blue); } } diff --git a/wled00/FX.h b/wled00/FX.h index 6d41b86e3..85854897c 100644 --- a/wled00/FX.h +++ b/wled00/FX.h @@ -62,10 +62,10 @@ //#define FRAMETIME _frametime #define FRAMETIME strip.getFrameTime() -/* each segment uses 52 bytes of SRAM memory, so if you're application fails because of +/* each segment uses 82 bytes of SRAM memory, so if you're application fails because of insufficient memory, decreasing MAX_NUM_SEGMENTS may help */ #ifdef ESP8266 - #define MAX_NUM_SEGMENTS 16 + #define MAX_NUM_SEGMENTS 12 /* How much data bytes all segments combined may allocate */ #define MAX_SEGMENT_DATA 5120 #else @@ -73,9 +73,13 @@ #define MAX_NUM_SEGMENTS 32 #endif #if defined(ARDUINO_ARCH_ESP32S2) - #define MAX_SEGMENT_DATA 24576 + #if defined(BOARD_HAS_PSRAM) && defined(WLED_USE_PSRAM) + #define MAX_SEGMENT_DATA MAX_NUM_SEGMENTS*1024 // 32k by default + #else + #define MAX_SEGMENT_DATA MAX_NUM_SEGMENTS*768 // 24k by default + #endif #else - #define MAX_SEGMENT_DATA 32767 + #define MAX_SEGMENT_DATA MAX_NUM_SEGMENTS*1280 // 40k by default #endif #endif @@ -682,10 +686,7 @@ class WS2812FX { // 96 bytes WS2812FX() : paletteFade(0), paletteBlend(0), - milliampsPerLed(55), cctBlending(0), - ablMilliampsMax(ABL_MILLIAMPS_DEFAULT), - currentMilliamps(0), now(millis()), timebase(0), isMatrix(false), @@ -797,7 +798,6 @@ class WS2812FX { // 96 bytes uint8_t paletteBlend, - milliampsPerLed, cctBlending, getActiveSegmentsNum(void), getFirstSelectedSegId(void), @@ -815,8 +815,6 @@ class WS2812FX { // 96 bytes inline uint8_t getModeCount() { return _modeCount; } uint16_t - ablMilliampsMax, - currentMilliamps, getLengthPhysical(void), getLengthTotal(void), // will include virtual/nonexistent pixels in matrix getFps(); diff --git a/wled00/FX_2Dfcn.cpp b/wled00/FX_2Dfcn.cpp index 7a1453925..6f5075d08 100644 --- a/wled00/FX_2Dfcn.cpp +++ b/wled00/FX_2Dfcn.cpp @@ -91,12 +91,12 @@ void WS2812FX::setUpMatrix() { DEBUG_PRINT(F("Reading LED gap from ")); DEBUG_PRINTLN(fileName); // read the array into global JSON buffer - if (readObjectFromFile(fileName, nullptr, &doc)) { + if (readObjectFromFile(fileName, nullptr, pDoc)) { // the array is similar to ledmap, except it has only 3 values: // -1 ... missing pixel (do not increase pixel count) // 0 ... inactive pixel (it does count, but should be mapped out (-1)) // 1 ... active pixel (it will count and will be mapped) - JsonArray map = doc.as(); + JsonArray map = pDoc->as(); gapSize = map.size(); if (!map.isNull() && gapSize >= matrixSize) { // not an empty map gapTable = new int8_t[gapSize]; diff --git a/wled00/FX_fcn.cpp b/wled00/FX_fcn.cpp index 8041cb4c3..bf3b1c124 100644 --- a/wled00/FX_fcn.cpp +++ b/wled00/FX_fcn.cpp @@ -900,8 +900,8 @@ void Segment::refreshLightCapabilities() { segStopIdx = stop; } - for (unsigned b = 0; b < busses.getNumBusses(); b++) { - Bus *bus = busses.getBus(b); + for (unsigned b = 0; b < BusManager::getNumBusses(); b++) { + Bus *bus = BusManager::getBus(b); if (bus == nullptr || bus->getLength()==0) break; if (!bus->isOk()) continue; if (bus->getStart() >= segStopIdx) continue; @@ -1086,7 +1086,7 @@ void WS2812FX::finalizeInit(void) { _hasWhiteChannel = _isOffRefreshRequired = false; //if busses failed to load, add default (fresh install, FS issue, ...) - if (busses.getNumBusses() == 0) { + if (BusManager::getNumBusses() == 0) { DEBUG_PRINTLN(F("No busses, init default")); const uint8_t defDataPins[] = {DATA_PINS}; const uint16_t defCounts[] = {PIXEL_COUNTS}; @@ -1099,13 +1099,13 @@ void WS2812FX::finalizeInit(void) { uint16_t count = defCounts[(i < defNumCounts) ? i : defNumCounts -1]; prevLen += count; BusConfig defCfg = BusConfig(DEFAULT_LED_TYPE, defPin, start, count, DEFAULT_LED_COLOR_ORDER, false, 0, RGBW_MODE_MANUAL_ONLY); - if (busses.add(defCfg) == -1) break; + if (BusManager::add(defCfg) == -1) break; } } _length = 0; - for (int i=0; igetStart() + bus->getLength() > MAX_LEDS) break; //RGBW mode is enabled if at least one of the strips is RGBW @@ -1164,7 +1164,7 @@ void WS2812FX::service() { _colors_t[1] = gamma32(seg.currentColor(1)); _colors_t[2] = gamma32(seg.currentColor(2)); seg.currentPalette(_currentPalette, seg.palette); // we need to pass reference - if (!cctFromRgb || correctWB) busses.setSegmentCCT(seg.currentBri(true), correctWB); + if (!cctFromRgb || correctWB) BusManager::setSegmentCCT(seg.currentBri(true), correctWB); // Effect blending // When two effects are being blended, each may have different segment data, this // data needs to be saved first and then restored before running previous mode. @@ -1195,7 +1195,7 @@ void WS2812FX::service() { _segment_index++; } _virtualSegmentLength = 0; - busses.setSegmentCCT(-1); + BusManager::setSegmentCCT(-1); _isServicing = false; _triggered = false; @@ -1214,13 +1214,13 @@ void WS2812FX::service() { void IRAM_ATTR WS2812FX::setPixelColor(unsigned i, uint32_t col) { if (i < customMappingSize) i = customMappingTable[i]; if (i >= _length) return; - busses.setPixelColor(i, col); + BusManager::setPixelColor(i, col); } uint32_t IRAM_ATTR WS2812FX::getPixelColor(uint16_t i) { if (i < customMappingSize) i = customMappingTable[i]; if (i >= _length) return 0; - return busses.getPixelColor(i); + return BusManager::getPixelColor(i); } void WS2812FX::show(void) { @@ -1231,7 +1231,7 @@ void WS2812FX::show(void) { // 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(); + BusManager::show(); unsigned long showNow = millis(); size_t diff = showNow - _lastShow; @@ -1246,7 +1246,7 @@ void WS2812FX::show(void) { * On some hardware (ESP32), strip updates are done asynchronously. */ bool WS2812FX::isUpdating() { - return !busses.canAllShow(); + return !BusManager::canAllShow(); } /** @@ -1305,7 +1305,7 @@ void WS2812FX::setBrightness(uint8_t b, bool direct) { } // setting brightness with NeoPixelBusLg has no effect on already painted pixels, // so we need to force an update to existing buffer - busses.setBrightness(b); + BusManager::setBrightness(b); if (!direct) { unsigned long t = millis(); if (_segments[0].next_time > t + 22 && t - _lastShow > MIN_SHOW_DELAY) trigger(); //apply brightness change immediately if no refresh soon @@ -1361,8 +1361,8 @@ uint16_t WS2812FX::getLengthTotal(void) { uint16_t WS2812FX::getLengthPhysical(void) { uint16_t len = 0; - for (size_t b = 0; b < busses.getNumBusses(); b++) { - Bus *bus = busses.getBus(b); + for (size_t b = 0; b < BusManager::getNumBusses(); b++) { + Bus *bus = BusManager::getBus(b); if (bus->getType() >= TYPE_NET_DDP_RGB) continue; //exclude non-physical network busses len += bus->getLength(); } @@ -1373,8 +1373,8 @@ uint16_t WS2812FX::getLengthPhysical(void) { //returns if there is an RGBW bus (supports RGB and White, not only white) //not influenced by auto-white mode, also true if white slider does not affect output white channel bool WS2812FX::hasRGBWBus(void) { - for (size_t b = 0; b < busses.getNumBusses(); b++) { - Bus *bus = busses.getBus(b); + for (size_t b = 0; b < BusManager::getNumBusses(); b++) { + Bus *bus = BusManager::getBus(b); if (bus == nullptr || bus->getLength()==0) break; if (bus->hasRGB() && bus->hasWhite()) return true; } @@ -1383,8 +1383,8 @@ bool WS2812FX::hasRGBWBus(void) { bool WS2812FX::hasCCTBus(void) { if (cctFromRgb && !correctWB) return false; - for (size_t b = 0; b < busses.getNumBusses(); b++) { - Bus *bus = busses.getBus(b); + for (size_t b = 0; b < BusManager::getNumBusses(); b++) { + Bus *bus = BusManager::getBus(b); if (bus == nullptr || bus->getLength()==0) break; switch (bus->getType()) { case TYPE_ANALOG_5CH: @@ -1472,8 +1472,8 @@ void WS2812FX::makeAutoSegments(bool forceReset) { } #endif - for (size_t i = s; i < busses.getNumBusses(); i++) { - Bus* b = busses.getBus(i); + for (size_t i = s; i < BusManager::getNumBusses(); i++) { + Bus* b = BusManager::getBus(i); segStarts[s] = b->getStart(); segStops[s] = segStarts[s] + b->getLength(); @@ -1562,8 +1562,8 @@ void WS2812FX::fixInvalidSegments() { bool WS2812FX::checkSegmentAlignment() { bool aligned = false; for (segment &seg : _segments) { - for (unsigned b = 0; bgetStart() && seg.stop == bus->getStart() + bus->getLength()) aligned = true; } if (seg.start == 0 && seg.stop == _length) aligned = true; @@ -1670,7 +1670,7 @@ bool WS2812FX::deserializeMap(uint8_t n) { if (!isFile || !requestJSONBufferLock(7)) return false; // this will trigger setUpMatrix() when called from wled.cpp - if (!readObjectFromFile(fileName, nullptr, &doc)) { + if (!readObjectFromFile(fileName, nullptr, pDoc)) { DEBUG_PRINT(F("ERROR Invalid ledmap in ")); DEBUG_PRINTLN(fileName); releaseJSONBufferLock(); return false; // if file does not load properly then exit @@ -1680,7 +1680,8 @@ bool WS2812FX::deserializeMap(uint8_t n) { if (customMappingTable == nullptr) customMappingTable = new uint16_t[getLengthTotal()]; - JsonArray map = doc[F("map")]; + JsonObject root = pDoc->as(); + JsonArray map = root[F("map")]; if (!map.isNull() && map.size()) { // not an empty map customMappingSize = min((unsigned)map.size(), (unsigned)getLengthTotal()); for (unsigned i=0; i getLength()) { //each LED uses about 1mA in standby, exclude that from power budget + powerBudget -= getLength(); + } else { + powerBudget = 0; + } uint32_t busPowerSum = 0; for (unsigned i = 0; i < getLength(); i++) { //sum up the usage of each LED @@ -178,29 +183,26 @@ uint8_t BusDigital::estimateCurrentAndLimitBri() { 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; + _milliAmpsTotal = busPowerSum * _bri / 255; 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; + if (scale >= 1.0f) return _bri; + _milliAmpsTotal = ceilf((float)_milliAmpsTotal * scale); + uint8_t scaleB = min((int)(scale * 255), 255); newBri = unsigned(_bri * scaleB) / 256 + 1; } return newBri; } void BusDigital::show() { + _milliAmpsTotal = 0; if (!_valid) return; - uint8_t newBri = estimateCurrentAndLimitBri(); + uint8_t newBri = estimateCurrentAndLimitBri(); // will fill _milliAmpsTotal if (newBri < _bri) PolyBus::setBrightness(_busPtr, _iType, newBri); // limit brightness to stay within current limits if (_data) { // use _buffering this causes ~20% FPS drop @@ -646,9 +648,12 @@ void BusManager::removeAll() { } void BusManager::show() { + _milliAmpsUsed = 0; for (unsigned i = 0; i < numBusses; i++) { busses[i]->show(); + _milliAmpsUsed += busses[i]->getUsedCurrent(); } + if (_milliAmpsUsed) _milliAmpsUsed += MA_FOR_ESP; } void BusManager::setStatusPixel(uint32_t c) { @@ -714,3 +719,11 @@ uint16_t BusManager::getTotalLength() { int16_t Bus::_cct = -1; uint8_t Bus::_cctBlend = 0; uint8_t Bus::_gAWM = 255; + +uint16_t BusDigital::_milliAmpsTotal = 0; + +uint8_t BusManager::numBusses = 0; +Bus* BusManager::busses[WLED_MAX_BUSSES+WLED_MIN_VIRTUAL_BUSSES]; +ColorOrderMap BusManager::colorOrderMap = {}; +uint16_t BusManager::_milliAmpsUsed = 0; +uint16_t BusManager::_milliAmpsMax = ABL_MILLIAMPS_DEFAULT; \ No newline at end of file diff --git a/wled00/bus_manager.h b/wled00/bus_manager.h index 9d322749c..12fb81b39 100644 --- a/wled00/bus_manager.h +++ b/wled00/bus_manager.h @@ -137,6 +137,7 @@ class Bus { virtual uint8_t skippedLeds() { return 0; } virtual uint16_t getFrequency() { return 0U; } virtual uint16_t getLEDCurrent() { return 0; } + virtual uint16_t getUsedCurrent() { return 0; } virtual uint16_t getMaxCurrent() { return 0; } inline void setReversed(bool reversed) { _reversed = reversed; } inline uint16_t getStart() { return _start; } @@ -219,6 +220,7 @@ class BusDigital : public Bus { uint16_t getFrequency() { return _frequencykHz; } uint8_t estimateCurrentAndLimitBri(); uint16_t getLEDCurrent() { return _milliAmpsPerLed; } + uint16_t getUsedCurrent() { return _milliAmpsTotal; } uint16_t getMaxCurrent() { return _milliAmpsMax; } void reinit(); void cleanup(); @@ -233,7 +235,8 @@ class BusDigital : public Bus { uint16_t _milliAmpsMax; void * _busPtr; const ColorOrderMap &_colorOrderMap; - //bool _buffering; // temporary until we figure out why comparison "_data" causes severe FPS drop + + static uint16_t _milliAmpsTotal; // is overwitten/recalculated on each show() inline uint32_t restoreColorLossy(uint32_t c, uint8_t restoreBri) { if (restoreBri < 255) { @@ -314,39 +317,44 @@ class BusNetwork : public Bus { class BusManager { public: - BusManager() : numBusses(0) {}; + BusManager() {}; //utility to get the approx. memory usage of a given BusConfig static uint32_t memUsage(BusConfig &bc); + static uint16_t currentMilliamps(void) { return _milliAmpsUsed; } + static uint16_t ablMilliampsMax(void) { return _milliAmpsMax; } - int add(BusConfig &bc); + static int add(BusConfig &bc); //do not call this method from system context (network callback) - void removeAll(); + static void removeAll(); - void show(); - bool canAllShow(); - void setStatusPixel(uint32_t c); - void setPixelColor(uint16_t pix, uint32_t c); - void setBrightness(uint8_t b); - void setSegmentCCT(int16_t cct, bool allowWBCorrection = false); - uint32_t getPixelColor(uint16_t pix); + static void show(); + static bool canAllShow(); + static void setStatusPixel(uint32_t c); + static void setPixelColor(uint16_t pix, uint32_t c); + static void setBrightness(uint8_t b); + static void setSegmentCCT(int16_t cct, bool allowWBCorrection = false); + static void setMilliampsMax(uint16_t max) { _milliAmpsMax = max;} + static uint32_t getPixelColor(uint16_t pix); - Bus* getBus(uint8_t busNr); + static Bus* getBus(uint8_t busNr); //semi-duplicate of strip.getLengthTotal() (though that just returns strip._length, calculated in finalizeInit()) - uint16_t getTotalLength(); - inline uint8_t getNumBusses() const { return numBusses; } + static uint16_t getTotalLength(); + static uint8_t getNumBusses() { return numBusses; } - inline void updateColorOrderMap(const ColorOrderMap &com) { memcpy(&colorOrderMap, &com, sizeof(ColorOrderMap)); } - inline const ColorOrderMap& getColorOrderMap() const { return colorOrderMap; } + static void updateColorOrderMap(const ColorOrderMap &com) { memcpy(&colorOrderMap, &com, sizeof(ColorOrderMap)); } + static const ColorOrderMap& getColorOrderMap() { return colorOrderMap; } private: - uint8_t numBusses; - Bus* busses[WLED_MAX_BUSSES+WLED_MIN_VIRTUAL_BUSSES]; - ColorOrderMap colorOrderMap; + static uint8_t numBusses; + static Bus* busses[WLED_MAX_BUSSES+WLED_MIN_VIRTUAL_BUSSES]; + static ColorOrderMap colorOrderMap; + static uint16_t _milliAmpsUsed; + static uint16_t _milliAmpsMax; - inline uint8_t getNumVirtualBusses() { + static uint8_t getNumVirtualBusses() { int j = 0; for (int i=0; igetType() >= TYPE_NET_DDP_RGB && busses[i]->getType() < 96) j++; return j; diff --git a/wled00/button.cpp b/wled00/button.cpp index f1487396a..b1e7f2a79 100644 --- a/wled00/button.cpp +++ b/wled00/button.cpp @@ -347,10 +347,10 @@ void handleButton() void esp32RMTInvertIdle() { bool idle_out; - for (uint8_t u = 0; u < busses.getNumBusses(); u++) + for (uint8_t u = 0; u < BusManager::getNumBusses(); u++) { if (u > 7) return; // only 8 RMT channels, TODO: ESP32 variants have less RMT channels - Bus *bus = busses.getBus(u); + Bus *bus = BusManager::getBus(u); if (!bus || bus->getLength()==0 || !IS_DIGITAL(bus->getType()) || IS_2PIN(bus->getType())) continue; //assumes that bus number to rmt channel mapping stays 1:1 rmt_channel_t ch = static_cast(u); diff --git a/wled00/cfg.cpp b/wled00/cfg.cpp index 489793b24..a4a1e3372 100644 --- a/wled00/cfg.cpp +++ b/wled00/cfg.cpp @@ -87,8 +87,8 @@ bool deserializeConfig(JsonObject doc, bool fromFS) { JsonObject hw_led = hw["led"]; uint16_t total = hw_led[F("total")] | strip.getLengthTotal(); - CJSON(strip.ablMilliampsMax, hw_led[F("maxpwr")]); - CJSON(strip.milliampsPerLed, hw_led[F("ledma")]); // no longer used + uint16_t ablMilliampsMax = hw_led[F("maxpwr")] | BusManager::ablMilliampsMax(); + BusManager::setMilliampsMax(ablMilliampsMax); Bus::setGlobalAWMode(hw_led[F("rgbwm")] | AW_GLOBAL_DISABLED); CJSON(correctWB, hw_led["cct"]); CJSON(cctFromRgb, hw_led[F("cr")]); @@ -138,7 +138,7 @@ bool deserializeConfig(JsonObject doc, bool fromFS) { if (fromFS || !ins.isNull()) { uint8_t s = 0; // bus iterator - if (fromFS) busses.removeAll(); // can't safely manipulate busses directly in network callback + if (fromFS) BusManager::removeAll(); // can't safely manipulate busses directly in network callback uint32_t mem = 0, globalBufMem = 0; uint16_t maxlen = 0; bool busesChanged = false; @@ -164,8 +164,8 @@ bool deserializeConfig(JsonObject doc, bool fromFS) { bool refresh = elm["ref"] | false; uint16_t freqkHz = elm[F("freq")] | 0; // will be in kHz for DotStar and Hz for PWM (not yet implemented fully) uint8_t AWmode = elm[F("rgbwm")] | RGBW_MODE_MANUAL_ONLY; - 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 + uint8_t maPerLed = elm[F("ledma")] | 55; + uint16_t maMax = elm[F("maxpwr")] | (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; @@ -179,7 +179,7 @@ bool deserializeConfig(JsonObject doc, bool fromFS) { maxlen = start + length; globalBufMem = maxlen * 4; } - if (mem + globalBufMem <= MAX_LED_MEMORY) if (busses.add(bc) == -1) break; // finalization will be done in WLED::beginStrip() + if (mem + globalBufMem <= MAX_LED_MEMORY) if (BusManager::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, maPerLed, maMax); @@ -190,7 +190,7 @@ bool deserializeConfig(JsonObject doc, bool fromFS) { doInitBusses = busesChanged; // finalization done in beginStrip() } - if (hw_led["rev"]) busses.getBus(0)->setReversed(true); //set 0.11 global reversed setting for first bus + if (hw_led["rev"]) BusManager::getBus(0)->setReversed(true); //set 0.11 global reversed setting for first bus // read color order map configuration JsonArray hw_com = hw[F("com")]; @@ -205,7 +205,7 @@ bool deserializeConfig(JsonObject doc, bool fromFS) { com.add(start, len, colorOrder); s++; } - busses.updateColorOrderMap(com); + BusManager::updateColorOrderMap(com); } // read multiple button configuration @@ -609,7 +609,7 @@ void deserializeConfigFromFS() { DEBUG_PRINTLN(F("Reading settings from /cfg.json...")); - success = readObjectFromFile("/cfg.json", nullptr, &doc); + success = readObjectFromFile("/cfg.json", nullptr, pDoc); if (!success) { // if file does not exist, optionally try reading from EEPROM and then save defaults to FS releaseJSONBufferLock(); #ifdef WLED_ADD_EEPROM_SUPPORT @@ -630,7 +630,8 @@ void deserializeConfigFromFS() { // NOTE: This routine deserializes *and* applies the configuration // Therefore, must also initialize ethernet from this function - bool needsSave = deserializeConfig(doc.as(), true); + JsonObject root = pDoc->as(); + bool needsSave = deserializeConfig(root, true); releaseJSONBufferLock(); if (needsSave) serializeConfig(); // usermods required new parameters @@ -643,19 +644,21 @@ void serializeConfig() { if (!requestJSONBufferLock(2)) return; - JsonArray rev = doc.createNestedArray("rev"); + JsonObject root = pDoc->to(); + + JsonArray rev = root.createNestedArray("rev"); rev.add(1); //major settings revision rev.add(0); //minor settings revision - doc[F("vid")] = VERSION; + root[F("vid")] = VERSION; - JsonObject id = doc.createNestedObject("id"); + JsonObject id = root.createNestedObject("id"); id[F("mdns")] = cmDNS; id[F("name")] = serverDescription; id[F("inv")] = alexaInvocationName; id[F("sui")] = simplifiedUI; - JsonObject nw = doc.createNestedObject("nw"); + JsonObject nw = root.createNestedObject("nw"); #ifndef WLED_DISABLE_ESPNOW nw[F("espnow")] = enableESPNow; nw[F("linked_remote")] = linked_remote; @@ -677,7 +680,7 @@ void serializeConfig() { nw_ins_0_sn.add(staticSubnet[i]); } - JsonObject ap = doc.createNestedObject("ap"); + JsonObject ap = root.createNestedObject("ap"); ap[F("ssid")] = apSSID; ap[F("pskl")] = strlen(apPass); ap[F("chan")] = apChannel; @@ -690,12 +693,12 @@ void serializeConfig() { ap_ip.add(2); ap_ip.add(1); - JsonObject wifi = doc.createNestedObject("wifi"); + JsonObject wifi = root.createNestedObject("wifi"); wifi[F("sleep")] = !noWifiSleep; wifi[F("phy")] = (int)force802_3g; #ifdef WLED_USE_ETHERNET - JsonObject ethernet = doc.createNestedObject("eth"); + JsonObject ethernet = root.createNestedObject("eth"); ethernet["type"] = ethernetType; if (ethernetType != WLED_ETH_NONE && ethernetType < WLED_NUM_ETH_TYPES) { JsonArray pins = ethernet.createNestedArray("pin"); @@ -718,12 +721,12 @@ void serializeConfig() { } #endif - JsonObject hw = doc.createNestedObject("hw"); + JsonObject hw = root.createNestedObject("hw"); 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")] = strip.ablMilliampsMax; - hw_led[F("ledma")] = strip.milliampsPerLed; // no longer used + hw_led[F("maxpwr")] = BusManager::ablMilliampsMax(); + hw_led[F("ledma")] = 0; // no longer used hw_led["cct"] = correctWB; hw_led[F("cr")] = cctFromRgb; hw_led[F("cb")] = strip.cctBlending; @@ -753,8 +756,8 @@ void serializeConfig() { JsonArray hw_led_ins = hw_led.createNestedArray("ins"); - for (uint8_t s = 0; s < busses.getNumBusses(); s++) { - Bus *bus = busses.getBus(s); + for (uint8_t s = 0; s < BusManager::getNumBusses(); s++) { + Bus *bus = BusManager::getBus(s); if (!bus || bus->getLength()==0) break; JsonObject ins = hw_led_ins.createNestedObject(); ins["start"] = bus->getStart(); @@ -775,7 +778,7 @@ void serializeConfig() { } JsonArray hw_com = hw.createNestedArray(F("com")); - const ColorOrderMap& com = busses.getColorOrderMap(); + const ColorOrderMap& com = BusManager::getColorOrderMap(); for (uint8_t s = 0; s < com.count(); s++) { const ColorOrderMapEntry *entry = com.get(s); if (!entry) break; @@ -830,7 +833,7 @@ void serializeConfig() { //JsonObject hw_status = hw.createNestedObject("status"); //hw_status["pin"] = -1; - JsonObject light = doc.createNestedObject(F("light")); + JsonObject light = root.createNestedObject(F("light")); light[F("scale-bri")] = briMultiplier; light[F("pal-mode")] = strip.paletteBlend; light[F("aseg")] = autoSegments; @@ -853,12 +856,12 @@ void serializeConfig() { light_nl[F("tbri")] = nightlightTargetBri; light_nl["macro"] = macroNl; - JsonObject def = doc.createNestedObject("def"); + JsonObject def = root.createNestedObject("def"); def["ps"] = bootPreset; def["on"] = turnOnAtBoot; def["bri"] = briS; - JsonObject interfaces = doc.createNestedObject("if"); + JsonObject interfaces = root.createNestedObject("if"); JsonObject if_sync = interfaces.createNestedObject("sync"); if_sync[F("port0")] = udpPort; @@ -961,7 +964,7 @@ void serializeConfig() { if_ntp[F("ln")] = longitude; if_ntp[F("lt")] = latitude; - JsonObject ol = doc.createNestedObject("ol"); + JsonObject ol = root.createNestedObject("ol"); ol[F("clock")] = overlayCurrent; ol[F("cntdwn")] = countdownMode; @@ -971,7 +974,7 @@ void serializeConfig() { ol[F("o5m")] = analogClock5MinuteMarks; ol[F("osec")] = analogClockSecondsTrail; - JsonObject timers = doc.createNestedObject(F("timers")); + JsonObject timers = root.createNestedObject(F("timers")); JsonObject cntdwn = timers.createNestedObject(F("cntdwn")); JsonArray goal = cntdwn.createNestedArray(F("goal")); @@ -999,14 +1002,14 @@ void serializeConfig() { } } - JsonObject ota = doc.createNestedObject("ota"); + JsonObject ota = root.createNestedObject("ota"); ota[F("lock")] = otaLock; ota[F("lock-wifi")] = wifiLock; ota[F("pskl")] = strlen(otaPass); ota[F("aota")] = aOtaEnabled; #ifdef WLED_ENABLE_DMX - JsonObject dmx = doc.createNestedObject("dmx"); + JsonObject dmx = root.createNestedObject("dmx"); dmx[F("chan")] = DMXChannels; dmx[F("gap")] = DMXGap; dmx["start"] = DMXStart; @@ -1020,11 +1023,11 @@ void serializeConfig() { dmx[F("e131proxy")] = e131ProxyUniverse; #endif - JsonObject usermods_settings = doc.createNestedObject("um"); + JsonObject usermods_settings = root.createNestedObject("um"); usermods.addToConfig(usermods_settings); File f = WLED_FS.open("/cfg.json", "w"); - if (f) serializeJson(doc, f); + if (f) serializeJson(root, f); f.close(); releaseJSONBufferLock(); @@ -1037,19 +1040,21 @@ bool deserializeConfigSec() { if (!requestJSONBufferLock(3)) return false; - bool success = readObjectFromFile("/wsec.json", nullptr, &doc); + bool success = readObjectFromFile("/wsec.json", nullptr, pDoc); if (!success) { releaseJSONBufferLock(); return false; } - JsonObject nw_ins_0 = doc["nw"]["ins"][0]; + JsonObject root = pDoc->as(); + + JsonObject nw_ins_0 = root["nw"]["ins"][0]; getStringFromJson(clientPass, nw_ins_0["psk"], 65); - JsonObject ap = doc["ap"]; + JsonObject ap = root["ap"]; getStringFromJson(apPass, ap["psk"] , 65); - [[maybe_unused]] JsonObject interfaces = doc["if"]; + [[maybe_unused]] JsonObject interfaces = root["if"]; #ifdef WLED_ENABLE_MQTT JsonObject if_mqtt = interfaces["mqtt"]; @@ -1060,10 +1065,10 @@ bool deserializeConfigSec() { getStringFromJson(hueApiKey, interfaces["hue"][F("key")], 47); #endif - getStringFromJson(settingsPIN, doc["pin"], 5); + getStringFromJson(settingsPIN, root["pin"], 5); correctPIN = !strlen(settingsPIN); - JsonObject ota = doc["ota"]; + JsonObject ota = root["ota"]; getStringFromJson(otaPass, ota[F("pwd")], 33); CJSON(otaLock, ota[F("lock")]); CJSON(wifiLock, ota[F("lock-wifi")]); @@ -1078,17 +1083,19 @@ void serializeConfigSec() { if (!requestJSONBufferLock(4)) return; - JsonObject nw = doc.createNestedObject("nw"); + JsonObject root = pDoc->to(); + + JsonObject nw = root.createNestedObject("nw"); JsonArray nw_ins = nw.createNestedArray("ins"); JsonObject nw_ins_0 = nw_ins.createNestedObject(); nw_ins_0["psk"] = clientPass; - JsonObject ap = doc.createNestedObject("ap"); + JsonObject ap = root.createNestedObject("ap"); ap["psk"] = apPass; - [[maybe_unused]] JsonObject interfaces = doc.createNestedObject("if"); + [[maybe_unused]] JsonObject interfaces = root.createNestedObject("if"); #ifdef WLED_ENABLE_MQTT JsonObject if_mqtt = interfaces.createNestedObject("mqtt"); if_mqtt["psk"] = mqttPass; @@ -1098,16 +1105,16 @@ void serializeConfigSec() { if_hue[F("key")] = hueApiKey; #endif - doc["pin"] = settingsPIN; + root["pin"] = settingsPIN; - JsonObject ota = doc.createNestedObject("ota"); + JsonObject ota = root.createNestedObject("ota"); ota[F("pwd")] = otaPass; ota[F("lock")] = otaLock; ota[F("lock-wifi")] = wifiLock; ota[F("aota")] = aOtaEnabled; File f = WLED_FS.open("/wsec.json", "w"); - if (f) serializeJson(doc, f); + if (f) serializeJson(root, f); f.close(); releaseJSONBufferLock(); } diff --git a/wled00/const.h b/wled00/const.h index 1bdd0ca39..8ccd20a4c 100644 --- a/wled00/const.h +++ b/wled00/const.h @@ -450,7 +450,11 @@ #ifdef ESP8266 #define JSON_BUFFER_SIZE 10240 #else - #define JSON_BUFFER_SIZE 24576 + #if defined(ARDUINO_ARCH_ESP32S2) + #define JSON_BUFFER_SIZE 24576 + #else + #define JSON_BUFFER_SIZE 32767 + #endif #endif //#define MIN_HEAP_SIZE (8k for AsyncWebServer) diff --git a/wled00/data/cpal/cpal.htm b/wled00/data/cpal/cpal.htm index 5a8c801e5..873252bad 100644 --- a/wled00/data/cpal/cpal.htm +++ b/wled00/data/cpal/cpal.htm @@ -1,6 +1,7 @@ + @@ -45,6 +46,7 @@ width: 7px; top: 50%; transform: translateY(-50%); + touch-action: none; } .color-picker-marker { height: 7px; @@ -94,9 +96,14 @@ line-height: 1; } .wrap { - width: 800px; + width: 100%; margin: 0 auto; } + @media (min-width: 800px) { + .wrap { + width: 800px; + } + } .palette { height: 20px; } @@ -136,6 +143,9 @@ .sendSpan, .editSpan{ cursor: pointer; } + h1 { + font-size: 1.6rem; + } @@ -349,24 +359,31 @@ var gradientLength = maxX - minX + 1; elmnt.onmousedown = dragMouseDown; + elmnt.ontouchstart = dragMouseDown; function dragMouseDown(e) { removeTrashcan(event) e = e || window.event; - e.preventDefault(); + var isTouch = e.type.startsWith('touch'); + if (!isTouch) e.preventDefault(); // get the mouse cursor position at startup: - mousePos = e.clientX; + mousePos = isTouch ? e.touches[0].clientX : e.clientX; d.onmouseup = closeDragElement; + d.ontouchcancel = closeDragElement; + d.ontouchend = closeDragElement; // call a function whenever the cursor moves: d.onmousemove = elementDrag; + d.ontouchmove = elementDrag; } function elementDrag(e) { e = e || window.event; - e.preventDefault(); + var isTouch = e.type.startsWith('touch'); + if (!isTouch) e.preventDefault(); // calculate the new cursor position: - posNew = mousePos - e.clientX; - mousePos = e.clientX; + var clientX = isTouch ? e.touches[0].clientX : e.clientX; + posNew = mousePos - clientX; + mousePos = clientX; mousePosInGradient = mousePos - (minX + 1) truePos = Math.round((mousePosInGradient/gradientLength)*256); @@ -393,7 +410,10 @@ function closeDragElement() { /* stop moving when mouse button is released:*/ d.onmouseup = null; + d.ontouchcancel = null; + d.ontouchend = null; d.onmousemove = null; + d.ontouchmove = null; } } diff --git a/wled00/data/settings_leds.htm b/wled00/data/settings_leds.htm index 835b396fa..0b99c63d1 100644 --- a/wled00/data/settings_leds.htm +++ b/wled00/data/settings_leds.htm @@ -26,8 +26,8 @@ // success event scE.addEventListener("load", () => { GetV(); - setABL(); checkSi(); + setABL(); d.Sf.addEventListener("submit", trySubmit); if (d.um_p[0]==-1) d.um_p.shift(); pinDropdowns(); @@ -107,30 +107,36 @@ e.preventDefault(); if (!pinsOK()) {e.stopPropagation();return false;} // Prevent form submission and contact with server if (bquot > 100) {var msg = "Too many LEDs for me to handle!"; if (maxM < 10000) msg += "\n\rConsider using an ESP32."; alert(msg);} + if (!d.Sf.ABL.checked || d.Sf.PPL.checked) d.Sf.MA.value = 0; // submit 0 as ABL (PPL will handle it) if (d.Sf.checkValidity()) d.Sf.submit(); //https://stackoverflow.com/q/37323914 } function enABL() { - var en = d.Sf["ABL"].checked; - d.Sf["MA"].min = en ? 250 : 0; + var en = d.Sf.ABL.checked; gId('abl').style.display = (en) ? 'inline':'none'; gId('psu2').style.display = (en) ? 'inline':'none'; - if (!en) d.Sf["PPL"].checked = false; + if (!en) d.Sf.PPL.checked = false; enPPL(); UI(); } function enPPL() { - const abl = d.Sf["ABL"].checked; - const en = d.Sf["PPL"].checked; - d.Sf["MA"].readonly = en; - gId("ppldis").style.display = en ? 'inline' : 'none'; + const abl = d.Sf.ABL.checked; + const ppl = d.Sf.PPL.checked; + let sumMA = 0; + d.Sf.MA.readonly = ppl; + d.Sf.MA.min = abl && !ppl ? 250 : 0; + gId("psuMA").style.display = ppl ? 'none' : 'inline'; + gId("ppldis").style.display = ppl ? 'inline' : 'none'; + // set PPL minimum value and clear actual PPL limit if ABL disabled d.Sf.querySelectorAll("#mLC input[name^=MA]").forEach((i,n)=>{ - gId("PSU"+n).style.display = en ? "inline" : "none"; + gId("PSU"+n).style.display = ppl ? "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; + i.min = ppl && !((t >= 80 && t < 96) || (t >= 40 && t < 48)) ? 250 : 0; if (!abl) i.value = 0; + else if (ppl) sumMA += parseInt(i.value,10); }); + if (ppl) d.Sf.MA.value = sumMA; // populate UI ABL value if PPL used } function enLA(s,n) { @@ -141,10 +147,11 @@ } function setABL() { - d.Sf["ABL"].checked = false; + console.log(d.Sf.MA.value); + d.Sf.ABL.checked = parseInt(d.Sf.MA.value) > 0; // 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; + if (parseInt(i.value) > 0) d.Sf.ABL.checked = true; }); // select appropriate LED current d.Sf.querySelectorAll("#mLC select[name^=LAsel]").forEach((sel,n)=>{ @@ -190,8 +197,8 @@ let isRGBW = false, gRGBW = false, memu = 0; 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; + 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)=>{ @@ -244,7 +251,7 @@ 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); + sumMA += parseInt(d.Sf["MA"+n].value); // summarize PPL ABL limit (fields) } }); // display global white channel overrides @@ -269,11 +276,12 @@ if (s+c > sLC) sLC = s+c; //update total count if (c > maxLC) maxLC = c; //max per output 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))) sDI += c; if (!((t >= 80 && t < 96) || (t >= 40 && t < 48))) { + sDI += c; // summarize digital LED count let maPL = parseInt(d.Sf["LA"+n].value); if (maPL == 255) maPL = 12; - busMA += maPL*c; + busMA += maPL*c; // summarize maximum bus current (calculated) } } // increase led count return; @@ -316,14 +324,18 @@ else LC.style.color = d.ro_gpio.some((e)=>e==parseInt(LC.value)) ? "orange" : "#fff"; } }); + // distribute ABL current if not using PPL, otherwise sumMA contains summarized ABL limit 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; + let t = parseInt(d.Sf["LT"+n].value); //get LED type + if (!ablEN || (t >= 80 && t < 96) || (t >= 40 && t < 48)) { + // virtual and analog LEDs have no limiter + d.Sf["MA"+n].value = 0; + return; + } + if (!pplEN) d.Sf["MA"+n].value = Math.round(parseInt(d.Sf.MA.value,10)*c/sDI); }); + if (pplEN) d.Sf.MA.value = sumMA; // update global ABL if using PPL // update total led count gId("lc").textContent = sLC; gId("pc").textContent = (sLC == sPC) ? "":"(" + sPC + " physical)"; @@ -336,7 +348,6 @@ 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 ? ` (ERROR: Using over ${maxM}B!)` : "") : "800 LEDs per output"; // calculate power - 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; @@ -409,7 +420,7 @@ mA/LED:
-
PSU: mA
+
PSU: mA
Color Order: gId("+").style.display = (i0) ? "inline":"none"; - if (!init) UI(); + if (!init) { + enPPL(); + UI(); + } } function addCOM(start=0,len=1,co=0) { @@ -750,7 +764,7 @@ Length: mA
+
Maximum PSU Current: mA
Use per-output limiter: