From cf3f6ede7249e3bfe0e01066544e3202499cb417 Mon Sep 17 00:00:00 2001 From: Blaz Kristan Date: Fri, 22 Dec 2023 15:39:07 +0100 Subject: [PATCH 1/4] Suspend strip during operations --- wled00/FX.h | 13 ++++++++++--- wled00/FX_fcn.cpp | 26 +++++++++++++++----------- wled00/json.cpp | 7 ++++--- wled00/presets.cpp | 6 +++++- wled00/udp.cpp | 16 +++++++++------- wled00/wled.cpp | 2 +- wled00/wled_server.cpp | 4 +++- 7 files changed, 47 insertions(+), 27 deletions(-) diff --git a/wled00/FX.h b/wled00/FX.h index 1b5b00dae..1df13292c 100644 --- a/wled00/FX.h +++ b/wled00/FX.h @@ -531,7 +531,7 @@ typedef struct Segment { #endif static void handleRandomPalette(); - void setUp(uint16_t i1, uint16_t i2, uint8_t grp=1, uint8_t spc=0, uint16_t ofs=UINT16_MAX, uint16_t i1Y=0, uint16_t i2Y=1, uint8_t segId = 255); + void setUp(uint16_t i1, uint16_t i2, uint8_t grp=1, uint8_t spc=0, uint16_t ofs=UINT16_MAX, uint16_t i1Y=0, uint16_t i2Y=1); bool setColor(uint8_t slot, uint32_t c); //returns true if changed void setCCT(uint16_t k); void setOpacity(uint8_t o); @@ -697,6 +697,7 @@ class WS2812FX { // 96 bytes _colors_t{0,0,0}, _virtualSegmentLength(0), // true private variables + _suspend(false), _length(DEFAULT_LED_COUNT), _brightness(DEFAULT_BRIGHTNESS), _transitionDur(750), @@ -754,7 +755,7 @@ class WS2812FX { // 96 bytes setCCT(uint16_t k), setBrightness(uint8_t b, bool direct = false), setRange(uint16_t i, uint16_t i2, uint32_t col), - purgeSegments(bool force = false), + purgeSegments(void), setSegment(uint8_t n, uint16_t start, uint16_t stop, uint8_t grouping = 1, uint8_t spacing = 0, uint16_t offset = UINT16_MAX, uint16_t startY=0, uint16_t stopY=1), setMainSegmentId(uint8_t n), resetSegments(), @@ -777,6 +778,8 @@ class WS2812FX { // 96 bytes inline void setShowCallback(show_callback cb) { _callback = cb; } inline void setTransition(uint16_t t) { _transitionDur = t; } inline void appendSegment(const Segment &seg = Segment()) { if (_segments.size() < getMaxSegments()) _segments.push_back(seg); } + inline void suspend(void) { _suspend = true; } + inline void resume(void) { _suspend = false; } bool paletteFade, @@ -790,6 +793,7 @@ class WS2812FX { // 96 bytes inline bool isServicing(void) { return _isServicing; } inline bool hasWhiteChannel(void) {return _hasWhiteChannel;} inline bool isOffRefreshRequired(void) {return _isOffRefreshRequired;} + inline bool isSuspended(void) { return _suspend; } uint8_t paletteBlend, @@ -899,6 +903,8 @@ class WS2812FX { // 96 bytes friend class Segment; private: + volatile bool _suspend; + uint16_t _length; uint8_t _brightness; uint16_t _transitionDur; @@ -932,9 +938,10 @@ class WS2812FX { // 96 bytes uint16_t _qStart, _qStop, _qStartY, _qStopY; uint8_t _qGrouping, _qSpacing; uint16_t _qOffset; - +/* void setUpSegmentFromQueuedChanges(void); +*/ }; extern const char JSON_mode_names[]; diff --git a/wled00/FX_fcn.cpp b/wled00/FX_fcn.cpp index 42984a051..961f1ba73 100644 --- a/wled00/FX_fcn.cpp +++ b/wled00/FX_fcn.cpp @@ -468,7 +468,7 @@ void Segment::handleRandomPalette() { } // segId is given when called from network callback, changes are queued if that segment is currently in its effect function -void Segment::setUp(uint16_t i1, uint16_t i2, uint8_t grp, uint8_t spc, uint16_t ofs, uint16_t i1Y, uint16_t i2Y, uint8_t segId) { +void Segment::setUp(uint16_t i1, uint16_t i2, uint8_t grp, uint8_t spc, uint16_t ofs, uint16_t i1Y, uint16_t i2Y) { // return if neither bounds nor grouping have changed bool boundsUnchanged = (start == i1 && stop == i2); #ifndef WLED_DISABLE_2D @@ -1137,13 +1137,15 @@ void WS2812FX::finalizeInit(void) { void WS2812FX::service() { unsigned long nowUp = millis(); // Be aware, millis() rolls over every 49 days now = nowUp + timebase; - if (nowUp - _lastShow < MIN_SHOW_DELAY) return; + if (nowUp - _lastShow < MIN_SHOW_DELAY || _suspend) return; bool doShow = false; _isServicing = true; _segment_index = 0; Segment::handleRandomPalette(); // move it into for loop when each segment has individual random palette for (segment &seg : _segments) { + if (_suspend) break; // immediately stop processing segments if suspend requested during service() + // process transition (mode changes in the middle of transition) seg.handleTransition(); // reset the segment runtime data if needed @@ -1190,7 +1192,7 @@ void WS2812FX::service() { seg.next_time = nowUp + delay; } - if (_segment_index == _queuedChangesSegId) setUpSegmentFromQueuedChanges(); +// if (_segment_index == _queuedChangesSegId) setUpSegmentFromQueuedChanges(); _segment_index++; } _virtualSegmentLength = 0; @@ -1394,18 +1396,18 @@ bool WS2812FX::hasCCTBus(void) { return false; } -void WS2812FX::purgeSegments(bool force) { +void WS2812FX::purgeSegments() { // remove all inactive segments (from the back) int deleted = 0; if (_segments.size() <= 1) return; for (size_t i = _segments.size()-1; i > 0; i--) - if (_segments[i].stop == 0 || force) { + if (_segments[i].stop == 0) { deleted++; _segments.erase(_segments.begin() + i); } if (deleted) { _segments.shrink_to_fit(); - /*if (_mainSegment >= _segments.size())*/ setMainSegmentId(0); + setMainSegmentId(0); } } @@ -1420,7 +1422,7 @@ void WS2812FX::setSegment(uint8_t segId, uint16_t i1, uint16_t i2, uint8_t group appendSegment(Segment(0, strip.getLengthTotal())); segId = getSegmentsNum()-1; // segments are added at the end of list } - +/* if (_queuedChangesSegId == segId) _queuedChangesSegId = 255; // cancel queued change if already queued for this segment if (segId < getMaxSegments() && segId == getCurrSegmentId() && isServicing()) { // queue change to prevent concurrent access @@ -1432,17 +1434,19 @@ void WS2812FX::setSegment(uint8_t segId, uint16_t i1, uint16_t i2, uint8_t group DEBUG_PRINT(F("Segment queued: ")); DEBUG_PRINTLN(segId); return; // queued changes are applied immediately after effect function returns } - +*/ + suspend(); _segments[segId].setUp(i1, i2, grouping, spacing, offset, startY, stopY); + resume(); if (segId > 0 && segId == getSegmentsNum()-1 && i2 <= i1) _segments.pop_back(); // if last segment was deleted remove it from vector } - +/* void WS2812FX::setUpSegmentFromQueuedChanges() { if (_queuedChangesSegId >= getSegmentsNum()) return; - getSegment(_queuedChangesSegId).setUp(_qStart, _qStop, _qGrouping, _qSpacing, _qOffset, _qStartY, _qStopY); + _segments[_queuedChangesSegId].setUp(_qStart, _qStop, _qGrouping, _qSpacing, _qOffset, _qStartY, _qStopY); _queuedChangesSegId = 255; } - +*/ void WS2812FX::resetSegments() { _segments.clear(); // destructs all Segment as part of clearing #ifndef WLED_DISABLE_2D diff --git a/wled00/json.cpp b/wled00/json.cpp index 5b20baac1..6bbc5c04b 100644 --- a/wled00/json.cpp +++ b/wled00/json.cpp @@ -117,9 +117,8 @@ bool deserializeSegment(JsonObject elem, byte it, byte presetId) if (stop > start && of > len -1) of = len -1; // update segment (delete if necessary) - // do not call seg.setUp() here, as it may cause a crash due to concurrent access if the segment is currently drawing effects - // WS2812FX handles queueing of the change - strip.setSegment(id, start, stop, grp, spc, of, startY, stopY); + seg.setUp(start, stop, grp, spc, of, startY, stopY); // strip needs to be suspended for this to work without issues + if (newSeg) seg.refreshLightCapabilities(); // fix for #3403 if (seg.reset && seg.stop == 0) { @@ -381,6 +380,7 @@ bool deserializeState(JsonObject root, byte callMode, byte presetId) int it = 0; JsonVariant segVar = root["seg"]; + if (!segVar.isNull()) strip.suspend(); if (segVar.is()) { int id = segVar["id"] | -1; @@ -408,6 +408,7 @@ bool deserializeState(JsonObject root, byte callMode, byte presetId) } if (strip.getSegmentsNum() > 3 && deleted >= strip.getSegmentsNum()/2U) strip.purgeSegments(); // batch deleting more than half segments } + strip.resume(); usermods.readFromJsonState(root); diff --git a/wled00/presets.cpp b/wled00/presets.cpp index 975b68974..60b3cf692 100644 --- a/wled00/presets.cpp +++ b/wled00/presets.cpp @@ -24,7 +24,9 @@ static void doSaveState() { bool persist = (presetToSave < 251); const char *filename = getFileName(persist); - if (!requestJSONBufferLock(10)) return; // will set fileDoc + unsigned long start = millis(); + while (strip.isUpdating() && millis()-start > (2*FRAMETIME_FIXED)+1) yield(); // wait 2 frames + if (strip.isUpdating() || !requestJSONBufferLock(10)) return; // will set fileDoc initPresetsFile(); // just in case if someone deleted presets.json using /edit JsonObject sObj = doc.to(); @@ -132,7 +134,9 @@ void applyPresetWithFallback(uint8_t index, uint8_t callMode, uint8_t effectID, void handlePresets() { if (presetToSave) { + strip.suspend(); doSaveState(); + strip.resume(); return; } diff --git a/wled00/udp.cpp b/wled00/udp.cpp index e00fe0766..b7a98c768 100644 --- a/wled00/udp.cpp +++ b/wled00/udp.cpp @@ -286,10 +286,10 @@ void parseNotifyPacket(uint8_t *udpIn) { uint16_t stopY = version > 11 ? (udpIn[34+ofs] << 8 | udpIn[35+ofs]) : 1; uint16_t offset = (udpIn[7+ofs] << 8 | udpIn[8+ofs]); if (!receiveSegmentOptions) { - //selseg.setUp(start, stop, selseg.grouping, selseg.spacing, offset, startY, stopY); - // we have to use strip.setSegment() instead of selseg.setUp() to prevent crash if segment would change during drawing DEBUG_PRINTF("Set segment w/o options: %d [%d,%d;%d,%d]\n", id, (int)start, (int)stop, (int)startY, (int)stopY); - strip.setSegment(id, start, stop, selseg.grouping, selseg.spacing, offset, startY, stopY); + strip.suspend(); //should not be needed as UDP handling is not done in ISR callbacks but still added "just in case" + selseg.setUp(start, stop, selseg.grouping, selseg.spacing, offset, startY, stopY); + strip.resume(); continue; // we do receive bounds, but not options } selseg.options = (selseg.options & 0x0071U) | (udpIn[9 +ofs] & 0x0E); // ignore selected, freeze, reset & transitional @@ -326,12 +326,14 @@ void parseNotifyPacket(uint8_t *udpIn) { } if (receiveSegmentBounds) { DEBUG_PRINTF("Set segment w/ options: %d [%d,%d;%d,%d]\n", id, (int)start, (int)stop, (int)startY, (int)stopY); - // we have to use strip.setSegment() instead of selseg.setUp() to prevent crash if segment would change during drawing - strip.setSegment(id, start, stop, udpIn[5+ofs], udpIn[6+ofs], offset, startY, stopY); + strip.suspend(); //should not be needed as UDP handling is not done in ISR callbacks but still added "just in case" + selseg.setUp(start, stop, udpIn[5+ofs], udpIn[6+ofs], offset, startY, stopY); + strip.resume(); } else { - // we have to use strip.setSegment() instead of selseg.setUp() to prevent crash if segment would change during drawing DEBUG_PRINTF("Set segment grouping: %d [%d,%d]\n", id, (int)udpIn[5+ofs], (int)udpIn[6+ofs]); - strip.setSegment(id, selseg.start, selseg.stop, udpIn[5+ofs], udpIn[6+ofs], selseg.offset, selseg.startY, selseg.stopY); + strip.suspend(); //should not be needed as UDP handling is not done in ISR callbacks but still added "just in case" + selseg.setUp(selseg.start, selseg.stop, udpIn[5+ofs], udpIn[6+ofs], selseg.offset, selseg.startY, selseg.stopY); + strip.resume(); } } stateChanged = true; diff --git a/wled00/wled.cpp b/wled00/wled.cpp index 57a79d4d4..e50bc8310 100644 --- a/wled00/wled.cpp +++ b/wled00/wled.cpp @@ -846,7 +846,7 @@ void WLED::handleConnection() DEBUG_PRINT(F("Heap too low! ")); DEBUG_PRINTLN(heap); forceReconnect = true; - strip.purgeSegments(true); // remove all but one segments from memory + strip.resetSegments(); } else if (heap < MIN_HEAP_SIZE) { strip.purgeSegments(); } diff --git a/wled00/wled_server.cpp b/wled00/wled_server.cpp index 7d9d1a1ec..11020abff 100644 --- a/wled00/wled_server.cpp +++ b/wled00/wled_server.cpp @@ -289,8 +289,9 @@ void initServer() #endif usermods.onUpdateBegin(true); // notify usermods that update is about to begin (some may require task de-init) lastEditTime = millis(); // make sure PIN does not lock during update + strip.suspend(); #ifdef ESP8266 - strip.purgeSegments(true); // free as much memory as you can + strip.resetSegments(); // free as much memory as you can Update.runAsync(true); #endif Update.begin((ESP.getFreeSketchSpace() - 0x1000) & 0xFFFFF000); @@ -301,6 +302,7 @@ void initServer() DEBUG_PRINTLN(F("Update Success")); } else { DEBUG_PRINTLN(F("Update Failed")); + strip.resume(); usermods.onUpdateBegin(false); // notify usermods that update has failed (some may require task init) #if WLED_WATCHDOG_TIMEOUT > 0 WLED::instance().enableWatchdog(); From f414af4d82f0bf01ddb8003796c65d4f092264df Mon Sep 17 00:00:00 2001 From: Blaz Kristan Date: Mon, 25 Dec 2023 12:27:27 +0100 Subject: [PATCH 2/4] Fix incorrect compare --- wled00/presets.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wled00/presets.cpp b/wled00/presets.cpp index 60b3cf692..276b1fd43 100644 --- a/wled00/presets.cpp +++ b/wled00/presets.cpp @@ -25,7 +25,7 @@ static void doSaveState() { const char *filename = getFileName(persist); unsigned long start = millis(); - while (strip.isUpdating() && millis()-start > (2*FRAMETIME_FIXED)+1) yield(); // wait 2 frames + while (strip.isUpdating() && millis()-start < (2*FRAMETIME_FIXED)+1) yield(); // wait 2 frames if (strip.isUpdating() || !requestJSONBufferLock(10)) return; // will set fileDoc initPresetsFile(); // just in case if someone deleted presets.json using /edit From b3a768a04bf771b23cc3eab3b424931914602eac Mon Sep 17 00:00:00 2001 From: Blaz Kristan Date: Mon, 25 Dec 2023 12:29:12 +0100 Subject: [PATCH 3/4] Remove check for strip --- wled00/presets.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wled00/presets.cpp b/wled00/presets.cpp index 276b1fd43..f19ee3992 100644 --- a/wled00/presets.cpp +++ b/wled00/presets.cpp @@ -26,7 +26,7 @@ static void doSaveState() { unsigned long start = millis(); while (strip.isUpdating() && millis()-start < (2*FRAMETIME_FIXED)+1) yield(); // wait 2 frames - if (strip.isUpdating() || !requestJSONBufferLock(10)) return; // will set fileDoc + if (!requestJSONBufferLock(10)) return; // will set fileDoc initPresetsFile(); // just in case if someone deleted presets.json using /edit JsonObject sObj = doc.to(); From f070dc5527fbc80f80a7deef2cd8968d043526cb Mon Sep 17 00:00:00 2001 From: Blaz Kristan Date: Thu, 4 Jan 2024 17:40:23 +0100 Subject: [PATCH 4/4] Cosmetic & needsUpdate() --- wled00/FX.h | 112 +++++++++++++++++++++++----------------------- wled00/FX_fcn.cpp | 2 +- wled00/wled.cpp | 2 +- wled00/wled.h | 2 +- 4 files changed, 59 insertions(+), 59 deletions(-) diff --git a/wled00/FX.h b/wled00/FX.h index 85854897c..4a642d85d 100644 --- a/wled00/FX.h +++ b/wled00/FX.h @@ -559,17 +559,17 @@ typedef struct Segment { inline void markForReset(void) { reset = true; } // setOption(SEG_OPTION_RESET, true) // transition functions - void startTransition(uint16_t dur); // transition has to start before actual segment values change - void stopTransition(void); + void startTransition(uint16_t dur); // transition has to start before actual segment values change + void stopTransition(void); // ends transition mode by destroying transition structure void handleTransition(void); #ifndef WLED_DISABLE_MODE_BLEND void swapSegenv(tmpsegd_t &tmpSegD); // copies segment data into specifed buffer, if buffer is not a transition buffer, segment data is overwritten from transition buffer void restoreSegenv(tmpsegd_t &tmpSegD); // restores segment data from buffer, if buffer is not transition buffer, changed values are copied to transition buffer #endif - uint16_t progress(void); // transition progression between 0-65535 - uint8_t currentBri(bool useCct = false); - uint8_t currentMode(void); - uint32_t currentColor(uint8_t slot); + uint16_t progress(void); // transition progression between 0-65535 + uint8_t currentBri(bool useCct = false); // current segment brightness/CCT (blended while in transition) + uint8_t currentMode(void); // currently active effect/mode (while in transition) + uint32_t currentColor(uint8_t slot); // currently active segment color (blended while in transition) CRGBPalette16 &loadPalette(CRGBPalette16 &tgt, uint8_t pal); CRGBPalette16 ¤tPalette(CRGBPalette16 &tgt, uint8_t paletteID); @@ -598,9 +598,9 @@ typedef struct Segment { uint32_t color_wheel(uint8_t pos); // 2D matrix - uint16_t virtualWidth(void) const; - uint16_t virtualHeight(void) const; - uint16_t nrOfVStrips(void) const; + uint16_t virtualWidth(void) const; // segment width in virtual pixels (accounts for groupping and spacing) + uint16_t virtualHeight(void) const; // segment height in virtual pixels (accounts for groupping and spacing) + uint16_t nrOfVStrips(void) const; // returns number of virtual vertical strips in 2D matrix (used to expand 1D effects into 2D) #ifndef WLED_DISABLE_2D uint16_t XY(uint16_t x, uint16_t y); // support function to get relative index within segment void setPixelColorXY(int x, int y, uint32_t c); // set relative pixel within segment with color @@ -747,40 +747,39 @@ class WS2812FX { // 96 bytes void #ifdef WLED_DEBUG - printSize(), + printSize(), // prints memory usage for strip components #endif - finalizeInit(), - service(void), - setMode(uint8_t segid, uint8_t m), - setColor(uint8_t slot, uint32_t c), - setCCT(uint16_t k), - setBrightness(uint8_t b, bool direct = false), - setRange(uint16_t i, uint16_t i2, uint32_t col), - purgeSegments(void), + finalizeInit(), // initialises strip components + service(void), // executes effect functions when due and calls strip.show() + setMode(uint8_t segid, uint8_t m), // sets effect/mode for given segment (high level API) + setColor(uint8_t slot, uint32_t c), // sets color (in slot) for given segment (high level API) + setCCT(uint16_t k), // sets global CCT (either in relative 0-255 value or in K) + setBrightness(uint8_t b, bool direct = false), // sets strip brightness + setRange(uint16_t i, uint16_t i2, uint32_t col), // used for clock overlay + purgeSegments(void), // removes inactive segments from RAM (may incure penalty and memory fragmentation but reduces vector footprint) setSegment(uint8_t n, uint16_t start, uint16_t stop, uint8_t grouping = 1, uint8_t spacing = 0, uint16_t offset = UINT16_MAX, uint16_t startY=0, uint16_t stopY=1), setMainSegmentId(uint8_t n), - resetSegments(), - makeAutoSegments(bool forceReset = false), - fixInvalidSegments(), - setPixelColor(unsigned n, uint32_t c), - show(void), + resetSegments(), // marks all segments for reset + makeAutoSegments(bool forceReset = false), // will create segments based on configured outputs + fixInvalidSegments(), // fixes incorrect segment configuration + setPixelColor(unsigned n, uint32_t c), // paints absolute strip pixel with index n and color c + show(void), // initiates LED output setTargetFps(uint8_t fps), addEffect(uint8_t id, mode_ptr mode_fn, const char *mode_name), // add effect to the list; defined in FX.cpp - setupEffectData(void); // add default effects to the list; defined in FX.cpp + setupEffectData(void); // add default effects to the list; defined in FX.cpp - inline void restartRuntime() { for (Segment &seg : _segments) seg.markForReset(); } + inline void restartRuntime() { for (Segment &seg : _segments) seg.markForReset(); } inline void setTransitionMode(bool t) { for (Segment &seg : _segments) seg.startTransition(t ? _transitionDur : 0); } - inline void setColor(uint8_t slot, uint8_t r, uint8_t g, uint8_t b, uint8_t w = 0) { setColor(slot, RGBW32(r,g,b,w)); } - inline void fill(uint32_t c) { for (unsigned i = 0; i < getLengthTotal(); i++) setPixelColor(i, c); } // fill whole strip with color (inline) - // outsmart the compiler :) by correctly overloading + inline void setColor(uint8_t slot, uint8_t r, uint8_t g, uint8_t b, uint8_t w = 0) { setColor(slot, RGBW32(r,g,b,w)); } inline void setPixelColor(unsigned n, uint8_t r, uint8_t g, uint8_t b, uint8_t w = 0) { setPixelColor(n, RGBW32(r,g,b,w)); } - inline void setPixelColor(unsigned n, CRGB c) { setPixelColor(n, c.red, c.green, c.blue); } - inline void trigger(void) { _triggered = true; } // Forces the next frame to be computed on all active segments. - inline void setShowCallback(show_callback cb) { _callback = cb; } - inline void setTransition(uint16_t t) { _transitionDur = t; } + inline void setPixelColor(unsigned n, CRGB c) { setPixelColor(n, c.red, c.green, c.blue); } + inline void fill(uint32_t c) { for (unsigned i = 0; i < getLengthTotal(); i++) setPixelColor(i, c); } // fill whole strip with color (inline) + inline void trigger(void) { _triggered = true; } // Forces the next frame to be computed on all active segments. + inline void setShowCallback(show_callback cb) { _callback = cb; } + inline void setTransition(uint16_t t) { _transitionDur = t; } // sets transition time (in ms) inline void appendSegment(const Segment &seg = Segment()) { if (_segments.size() < getMaxSegments()) _segments.push_back(seg); } - inline void suspend(void) { _suspend = true; } - inline void resume(void) { _suspend = false; } + inline void suspend(void) { _suspend = true; } // will suspend (and canacel) strip.service() execution + inline void resume(void) { _suspend = false; } // will resume strip.service() execution bool paletteFade, @@ -791,10 +790,11 @@ class WS2812FX { // 96 bytes isUpdating(void), deserializeMap(uint8_t n=0); - inline bool isServicing(void) { return _isServicing; } - inline bool hasWhiteChannel(void) {return _hasWhiteChannel;} - inline bool isOffRefreshRequired(void) {return _isOffRefreshRequired;} - inline bool isSuspended(void) { return _suspend; } + inline bool isServicing(void) { return _isServicing; } // returns true if strip.service() is executing + inline bool hasWhiteChannel(void) { return _hasWhiteChannel; } // returns true if strip contains separate white chanel + inline bool isOffRefreshRequired(void) { return _isOffRefreshRequired; } // returns true if strip requires regular updates (i.e. TM1814 chipset) + inline bool isSuspended(void) { return _suspend; } // returns true if strip.service() execution is suspended + inline bool needsUpdate(void) { return _triggered; } // returns true if strip received a trigger() request uint8_t paletteBlend, @@ -805,32 +805,32 @@ class WS2812FX { // 96 bytes getActiveSegsLightCapabilities(bool selectedOnly = false), setPixelSegment(uint8_t n); - inline uint8_t getBrightness(void) { return _brightness; } - inline uint8_t getMaxSegments(void) { return MAX_NUM_SEGMENTS; } // returns maximum number of supported segments (fixed value) - inline uint8_t getSegmentsNum(void) { return _segments.size(); } // returns currently present segments - inline uint8_t getCurrSegmentId(void) { return _segment_index; } - inline uint8_t getMainSegmentId(void) { return _mainSegment; } - inline uint8_t getPaletteCount() { return 13 + GRADIENT_PALETTE_COUNT; } // will only return built-in palette count - inline uint8_t getTargetFps() { return _targetFps; } - inline uint8_t getModeCount() { return _modeCount; } + inline uint8_t getBrightness(void) { return _brightness; } // returns current strip brightness + inline uint8_t getMaxSegments(void) { return MAX_NUM_SEGMENTS; } // returns maximum number of supported segments (fixed value) + inline uint8_t getSegmentsNum(void) { return _segments.size(); } // returns currently present segments + inline uint8_t getCurrSegmentId(void) { return _segment_index; } // returns current segment index (only valid while strip.isServicing()) + inline uint8_t getMainSegmentId(void) { return _mainSegment; } // returns main segment index + inline uint8_t getPaletteCount() { return 13 + GRADIENT_PALETTE_COUNT; } // will only return built-in palette count + inline uint8_t getTargetFps() { return _targetFps; } // returns rough FPS value for las 2s interval + inline uint8_t getModeCount() { return _modeCount; } // returns number of registered modes/effects uint16_t getLengthPhysical(void), getLengthTotal(void), // will include virtual/nonexistent pixels in matrix getFps(); - inline uint16_t getFrameTime(void) { return _frametime; } - inline uint16_t getMinShowDelay(void) { return MIN_SHOW_DELAY; } - inline uint16_t getLength(void) { return _length; } // 2D matrix may have less pixels than W*H - inline uint16_t getTransition(void) { return _transitionDur; } + inline uint16_t getFrameTime(void) { return _frametime; } // returns amount of time a frame should take (in ms) + inline uint16_t getMinShowDelay(void) { return MIN_SHOW_DELAY; } // returns minimum amount of time strip.service() can be delayed (constant) + inline uint16_t getLength(void) { return _length; } // returns actual amount of LEDs on a strip (2D matrix may have less LEDs than W*H) + inline uint16_t getTransition(void) { return _transitionDur; } // returns currently set transition time (in ms) uint32_t now, timebase, getPixelColor(uint16_t); - inline uint32_t getLastShow(void) { return _lastShow; } - inline uint32_t segColor(uint8_t i) { return _colors_t[i]; } + inline uint32_t getLastShow(void) { return _lastShow; } // returns millis() timestamp of last strip.show() call + inline uint32_t segColor(uint8_t i) { return _colors_t[i]; } // returns currently valid color (for slot i) AKA SEGCOLOR(); may be blended between two colors while in transition const char * getModeData(uint8_t id = 0) { return (id && id<_modeCount) ? _modeData[id] : PSTR("Solid"); } @@ -839,9 +839,9 @@ class WS2812FX { // 96 bytes getModeDataSrc(void) { return &(_modeData[0]); } // vectors use arrays for underlying data Segment& getSegment(uint8_t id); - inline Segment& getFirstSelectedSeg(void) { return _segments[getFirstSelectedSegId()]; } - inline Segment& getMainSegment(void) { return _segments[getMainSegmentId()]; } - inline Segment* getSegments(void) { return &(_segments[0]); } + inline Segment& getFirstSelectedSeg(void) { return _segments[getFirstSelectedSegId()]; } // returns reference to first segment that is "selected" + inline Segment& getMainSegment(void) { return _segments[getMainSegmentId()]; } // returns reference to main segment + inline Segment* getSegments(void) { return &(_segments[0]); } // returns pointer to segment vector structure (warning: use carefully) // 2D support (panels) bool @@ -877,7 +877,7 @@ class WS2812FX { // 96 bytes std::vector panel; #endif - void setUpMatrix(); + void setUpMatrix(); // sets up automatic matrix ledmap from panel configuration // outsmart the compiler :) by correctly overloading inline void setPixelColorXY(int x, int y, uint32_t c) { setPixelColor((unsigned)(y * Segment::maxWidth + x), c); } diff --git a/wled00/FX_fcn.cpp b/wled00/FX_fcn.cpp index bf3b1c124..3a762afb4 100644 --- a/wled00/FX_fcn.cpp +++ b/wled00/FX_fcn.cpp @@ -1143,7 +1143,7 @@ void WS2812FX::service() { _segment_index = 0; Segment::handleRandomPalette(); // move it into for loop when each segment has individual random palette for (segment &seg : _segments) { - if (_suspend) break; // immediately stop processing segments if suspend requested during service() + if (_suspend) return; // immediately stop processing segments if suspend requested during service() // process transition (mode changes in the middle of transition) seg.handleTransition(); diff --git a/wled00/wled.cpp b/wled00/wled.cpp index e68251ff9..d21e75a4c 100644 --- a/wled00/wled.cpp +++ b/wled00/wled.cpp @@ -108,7 +108,7 @@ void WLED::loop() handlePresets(); yield(); - if (!offMode || strip.isOffRefreshRequired()) + if (!offMode || strip.isOffRefreshRequired() || strip.needsUpdate()) strip.service(); #ifdef ESP8266 else if (!noWifiSleep) diff --git a/wled00/wled.h b/wled00/wled.h index 76f492079..8b89285c5 100644 --- a/wled00/wled.h +++ b/wled00/wled.h @@ -8,7 +8,7 @@ */ // version code in format yymmddb (b = daily build) -#define VERSION 2401010 +#define VERSION 2401040 //uncomment this if you have a "my_config.h" file you'd like to use //#define WLED_USE_MY_CONFIG