From 7c0ecdf7c44a115eeca7cd6624af7139368f0b06 Mon Sep 17 00:00:00 2001
From: Frank <91616163+softhack007@users.noreply.github.com>
Date: Fri, 1 Nov 2024 14:51:12 +0100
Subject: [PATCH 1/6] make "target FPS" work
---
wled00/FX.h | 8 +++++---
wled00/FX_fcn.cpp | 37 ++++++++++++++++++++++++++-----------
2 files changed, 31 insertions(+), 14 deletions(-)
diff --git a/wled00/FX.h b/wled00/FX.h
index cec8e5d2..c8a9d539 100644
--- a/wled00/FX.h
+++ b/wled00/FX.h
@@ -43,13 +43,14 @@ bool strip_uses_global_leds(void) __attribute__((pure)); // WLEDMM implemented
#endif
/* Not used in all effects yet */
+#define FPS_UNLIMITED 249
#if defined(ARDUINO_ARCH_ESP32) && defined(WLEDMM_FASTPATH) // WLEDMM go faster on ESP32
#define WLED_FPS 120
#define FRAMETIME_FIXED (strip.getFrameTime() < 10 ? 12 : 24)
#define WLED_FPS_SLOW 60
#define FRAMETIME_FIXED_SLOW (15) // = 66 FPS => 1000/66
-//#define FRAMETIME _frametime
#define FRAMETIME strip.getFrameTime()
+#define MIN_SHOW_DELAY (max(2, (_frametime*5)/8)) // WLEDMM support higher framerates (up to 250fps) -- 5/8 = 62%
#else
#define WLED_FPS 42
#define FRAMETIME_FIXED (1000/WLED_FPS)
@@ -57,6 +58,7 @@ bool strip_uses_global_leds(void) __attribute__((pure)); // WLEDMM implemented
#define FRAMETIME_FIXED_SLOW (1000/WLED_FPS_SLOW)
//#define FRAMETIME _frametime
#define FRAMETIME strip.getFrameTime()
+#define MIN_SHOW_DELAY (_frametime < 16 ? 8 : 15)
#endif
/* each segment uses 52 bytes of SRAM memory, so if you're application fails because of
@@ -82,8 +84,6 @@ bool strip_uses_global_leds(void) __attribute__((pure)); // WLEDMM implemented
assuming each segment uses the same amount of data. 256 for ESP8266, 640 for ESP32. */
#define FAIR_DATA_PER_SEG (MAX_SEGMENT_DATA / strip.getMaxSegments())
-#define MIN_SHOW_DELAY (_frametime < 16 ? (_frametime <8? (_frametime <7? (_frametime <6 ? 2 :3) :4) : 8) : 15) // WLEDMM support higher framerates (up to 250fps)
-
#define NUM_COLORS 3 /* number of colors per segment */
#define SEGMENT strip._segments[strip.getCurrSegmentId()]
#define SEGENV strip._segments[strip.getCurrSegmentId()]
@@ -852,6 +852,7 @@ class WS2812FX { // 96 bytes
customMappingTableSize(0), //WLEDMM
customMappingSize(0),
_lastShow(0),
+ _lastServiceShow(0),
_segment_index(0),
_mainSegment(0)
{
@@ -1097,6 +1098,7 @@ class WS2812FX { // 96 bytes
uint16_t customMappingSize;
/*uint32_t*/ unsigned long _lastShow; // WLEDMM avoid losing precision
+ unsigned long _lastServiceShow; // WLEDMM last call of strip.show (timestamp)
uint8_t _segment_index;
uint8_t _mainSegment;
diff --git a/wled00/FX_fcn.cpp b/wled00/FX_fcn.cpp
index 54c21b63..6760c2c2 100644
--- a/wled00/FX_fcn.cpp
+++ b/wled00/FX_fcn.cpp
@@ -1873,13 +1873,19 @@ void WS2812FX::service() {
if (OTAisRunning) return; // WLEDMM avoid flickering during OTA
now = nowUp + timebase;
- #if defined(ARDUINO_ARCH_ESP32) && defined(WLEDMM_FASTPATH)
- if ((_frametime > 2) && (_frametime < 32) && (nowUp - _lastShow) < (_frametime/2)) return; // WLEDMM experimental - stabilizes frametimes but increases CPU load
- else if (nowUp - _lastShow < MIN_SHOW_DELAY) return; // WLEDMM fallback
- #else
- if (nowUp - _lastShow < MIN_SHOW_DELAY) return;
+ unsigned long elapsed = nowUp - _lastServiceShow;
+ #if defined(ARDUINO_ARCH_ESP32) && defined(WLEDMM_FASTPATH) // WLEDMM go faster on ESP32
+ //if (_suspend) return;
+ if (elapsed < 2) return; // keep wifi alive
+ if ( !_triggered && (_targetFps < FPS_UNLIMITED) && (_targetFps > 0)) {
+ if (elapsed < MIN_SHOW_DELAY) return; // WLEDMM too early for service
+ }
+ #else // legacy
+ if (elapsed < MIN_SHOW_DELAY) return;
#endif
+
bool doShow = false;
+ unsigned speedLimit = (_targetFps < FPS_UNLIMITED) ? (0.85f * FRAMETIME) : 1; // WLEDMM lower limit for effect frametime
_isServicing = true;
_segment_index = 0;
@@ -1914,6 +1920,8 @@ void WS2812FX::service() {
// actual code may be a bit more involved as effects have runtime data including allocated memory
//if (seg.transitional && seg._modeP) (*_mode[seg._modeP])(progress());
frameDelay = (*_mode[seg.currentMode(seg.mode)])();
+
+ if (frameDelay < speedLimit) frameDelay = FRAMETIME; // WLEDMM limit effects that want to go faster than target FPS
if (seg.mode != FX_MODE_HALLOWEEN_EYES) seg.call++;
if (seg.transitional && frameDelay > FRAMETIME) frameDelay = FRAMETIME; // force faster updates during transition
@@ -1929,6 +1937,7 @@ void WS2812FX::service() {
if(doShow) {
yield();
show();
+ _lastServiceShow = nowUp; // WLEDMM use correct timestamp
}
_triggered = false;
_isServicing = false;
@@ -2053,25 +2062,30 @@ void WS2812FX::show(void) {
estimateCurrentAndLimitBri();
#if defined(ARDUINO_ARCH_ESP32) && defined(WLEDMM_FASTPATH)
- unsigned long b4show = millis(); // WLEDMM the time before calling "show"
+ unsigned long now = millis();
+ #ifdef ARDUINO_ARCH_ESP32 // WLEDMM more accurate FPS measurement for ESP32
+ uint64_t now500 = esp_timer_get_time() / 2; // native timer; micros /2 -> millis * 500
#endif
+ #endif
+
// 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();
unsigned long now = millis();
+ #ifdef ARDUINO_ARCH_ESP32 // WLEDMM more accurate FPS measurement for ESP32
+ uint64_t now500 = esp_timer_get_time() / 2; // native timer; micros /2 -> millis * 500
+ #endif
+ #endif
+
unsigned long diff = now - _lastShow;
uint16_t fpsCurr = 200;
if (diff > 0) fpsCurr = 1000 / diff;
_cumulativeFps = (3 * _cumulativeFps + fpsCurr +2) >> 2; // "+2" for proper rounding (2/4 = 0.5)
- #if defined(ARDUINO_ARCH_ESP32) && defined(WLEDMM_FASTPATH)
- _lastShow = b4show; // WLEDMM this is more accurate, however it also increases CPU load - strip.service will run more frequently
- #else
_lastShow = now;
- #endif
+ _lastServiceShow = now;
#ifdef ARDUINO_ARCH_ESP32 // WLEDMM more accurate FPS measurement for ESP32
- uint64_t now500 = esp_timer_get_time() / 2; // native timer; micros /2 -> millis * 500
int64_t diff500 = now500 - _lastShow500;
if ((diff500 > 300) && (diff500 < 800000)) { // exclude stupid values (timer rollover, major hickups)
float fpcCurr500 = 500000.0f / float(diff500);
@@ -2107,6 +2121,7 @@ void WS2812FX::setTargetFps(uint8_t fps) {
if (fps > 0 && fps <= 251) _targetFps = fps; // WLEDMM allow higher framerates
_frametime = 1000 / _targetFps;
if (_frametime < 1) _frametime = 1; // WLEDMM better safe than sorry
+ if (fps >= FPS_UNLIMITED) _frametime = 3; // WLEDMM unlimited mode
}
void WS2812FX::setMode(uint8_t segid, uint8_t m) {
From 1c782aa3690b61bca553e2cbf4969b6e8f37f7a7 Mon Sep 17 00:00:00 2001
From: Frank <91616163+softhack007@users.noreply.github.com>
Date: Fri, 1 Nov 2024 14:58:12 +0100
Subject: [PATCH 2/6] improvement for non-fastpath
---
wled00/FX.h | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/wled00/FX.h b/wled00/FX.h
index c8a9d539..af79c514 100644
--- a/wled00/FX.h
+++ b/wled00/FX.h
@@ -58,7 +58,8 @@ bool strip_uses_global_leds(void) __attribute__((pure)); // WLEDMM implemented
#define FRAMETIME_FIXED_SLOW (1000/WLED_FPS_SLOW)
//#define FRAMETIME _frametime
#define FRAMETIME strip.getFrameTime()
-#define MIN_SHOW_DELAY (_frametime < 16 ? 8 : 15)
+//#define MIN_SHOW_DELAY (_frametime < 16 ? 8 : 15) // Upstream legacy
+#define MIN_SHOW_DELAY (_frametime < 16 ? (_frametime <8? (_frametime <7? (_frametime <6 ? 2 :3) :4) : 8) : 15) // WLEDMM support higher framerates (up to 250fps)
#endif
/* each segment uses 52 bytes of SRAM memory, so if you're application fails because of
From bf5fb9ccaa735ca8bce2e1224974ae0b224cb38e Mon Sep 17 00:00:00 2001
From: Frank <91616163+softhack007@users.noreply.github.com>
Date: Fri, 1 Nov 2024 15:12:07 +0100
Subject: [PATCH 3/6] fix compile errors
---
wled00/FX_fcn.cpp | 12 +++++++-----
1 file changed, 7 insertions(+), 5 deletions(-)
diff --git a/wled00/FX_fcn.cpp b/wled00/FX_fcn.cpp
index 6760c2c2..c23bb183 100644
--- a/wled00/FX_fcn.cpp
+++ b/wled00/FX_fcn.cpp
@@ -2062,7 +2062,7 @@ void WS2812FX::show(void) {
estimateCurrentAndLimitBri();
#if defined(ARDUINO_ARCH_ESP32) && defined(WLEDMM_FASTPATH)
- unsigned long now = millis();
+ unsigned long nowUp = millis();
#ifdef ARDUINO_ARCH_ESP32 // WLEDMM more accurate FPS measurement for ESP32
uint64_t now500 = esp_timer_get_time() / 2; // native timer; micros /2 -> millis * 500
#endif
@@ -2072,18 +2072,20 @@ void WS2812FX::show(void) {
// all of the data has been sent.
// See https://github.com/Makuna/NeoPixelBus/wiki/ESP32-NeoMethods#neoesp32rmt-methods
busses.show();
- unsigned long now = millis();
+
+ #if !defined(ARDUINO_ARCH_ESP32) || !defined(WLEDMM_FASTPATH) // upstream legacy
+ unsigned long nowUp = millis();
#ifdef ARDUINO_ARCH_ESP32 // WLEDMM more accurate FPS measurement for ESP32
uint64_t now500 = esp_timer_get_time() / 2; // native timer; micros /2 -> millis * 500
#endif
#endif
- unsigned long diff = now - _lastShow;
+ unsigned long diff = nowUp - _lastShow;
uint16_t fpsCurr = 200;
if (diff > 0) fpsCurr = 1000 / diff;
_cumulativeFps = (3 * _cumulativeFps + fpsCurr +2) >> 2; // "+2" for proper rounding (2/4 = 0.5)
- _lastShow = now;
- _lastServiceShow = now;
+ _lastShow = nowUp;
+ _lastServiceShow = nowUp;
#ifdef ARDUINO_ARCH_ESP32 // WLEDMM more accurate FPS measurement for ESP32
int64_t diff500 = now500 - _lastShow500;
From 6bae3567890be082e692b51537a913bc03c8f72a Mon Sep 17 00:00:00 2001
From: Frank <91616163+softhack007@users.noreply.github.com>
Date: Fri, 1 Nov 2024 17:17:54 +0100
Subject: [PATCH 4/6] renaming
upstream has renamed "now" to "showNow" - so we follow
---
wled00/FX_fcn.cpp | 10 +++++-----
1 file changed, 5 insertions(+), 5 deletions(-)
diff --git a/wled00/FX_fcn.cpp b/wled00/FX_fcn.cpp
index c23bb183..44642689 100644
--- a/wled00/FX_fcn.cpp
+++ b/wled00/FX_fcn.cpp
@@ -2062,7 +2062,7 @@ void WS2812FX::show(void) {
estimateCurrentAndLimitBri();
#if defined(ARDUINO_ARCH_ESP32) && defined(WLEDMM_FASTPATH)
- unsigned long nowUp = millis();
+ unsigned long showNow = millis();
#ifdef ARDUINO_ARCH_ESP32 // WLEDMM more accurate FPS measurement for ESP32
uint64_t now500 = esp_timer_get_time() / 2; // native timer; micros /2 -> millis * 500
#endif
@@ -2074,18 +2074,18 @@ void WS2812FX::show(void) {
busses.show();
#if !defined(ARDUINO_ARCH_ESP32) || !defined(WLEDMM_FASTPATH) // upstream legacy
- unsigned long nowUp = millis();
+ unsigned long showNow = millis();
#ifdef ARDUINO_ARCH_ESP32 // WLEDMM more accurate FPS measurement for ESP32
uint64_t now500 = esp_timer_get_time() / 2; // native timer; micros /2 -> millis * 500
#endif
#endif
- unsigned long diff = nowUp - _lastShow;
+ unsigned long diff = showNow - _lastShow;
uint16_t fpsCurr = 200;
if (diff > 0) fpsCurr = 1000 / diff;
_cumulativeFps = (3 * _cumulativeFps + fpsCurr +2) >> 2; // "+2" for proper rounding (2/4 = 0.5)
- _lastShow = nowUp;
- _lastServiceShow = nowUp;
+ _lastShow = showNow;
+ _lastServiceShow = showNow;
#ifdef ARDUINO_ARCH_ESP32 // WLEDMM more accurate FPS measurement for ESP32
int64_t diff500 = now500 - _lastShow500;
From dc4964a491bbdec3985bbb044965d4b7985e77b9 Mon Sep 17 00:00:00 2001
From: Frank <91616163+softhack007@users.noreply.github.com>
Date: Mon, 4 Nov 2024 13:47:05 +0100
Subject: [PATCH 5/6] small update
based on feedback from the PR to upstream
* support "0 FPS" meaning "unlimited"
* keep FRAMETIME_FIXED fixed
---
wled00/FX.h | 15 ++++++++-------
wled00/FX_fcn.cpp | 14 +++++++-------
wled00/data/settings_leds.htm | 6 ++++--
3 files changed, 19 insertions(+), 16 deletions(-)
diff --git a/wled00/FX.h b/wled00/FX.h
index af79c514..f20b1caf 100644
--- a/wled00/FX.h
+++ b/wled00/FX.h
@@ -43,20 +43,21 @@ bool strip_uses_global_leds(void) __attribute__((pure)); // WLEDMM implemented
#endif
/* Not used in all effects yet */
-#define FPS_UNLIMITED 249
+#define FPS_UNLIMITED 250
+#define FPS_UNLIMITED_AC 0 // WLEDMM upstream uses "0 fps" for unlimited. We support both ways
#if defined(ARDUINO_ARCH_ESP32) && defined(WLEDMM_FASTPATH) // WLEDMM go faster on ESP32
-#define WLED_FPS 120
-#define FRAMETIME_FIXED (strip.getFrameTime() < 10 ? 12 : 24)
-#define WLED_FPS_SLOW 60
-#define FRAMETIME_FIXED_SLOW (15) // = 66 FPS => 1000/66
#define FRAMETIME strip.getFrameTime()
#define MIN_SHOW_DELAY (max(2, (_frametime*5)/8)) // WLEDMM support higher framerates (up to 250fps) -- 5/8 = 62%
+#define WLED_FPS 120
+#define WLED_FPS_SLOW 60
+#define FRAMETIME_FIXED 24 // used in Blurz, Freqmap, Scrolling text
+//#define FRAMETIME_FIXED (strip.getFrameTime() < 10 ? 12 : 24)
+#define FRAMETIME_FIXED_SLOW (15) // = 66 FPS => 1000/66 // used in Solid, Colortwinkles, Candle
#else
#define WLED_FPS 42
#define FRAMETIME_FIXED (1000/WLED_FPS)
-#define WLED_FPS_SLOW 42
+#define WLED_FPS_SLOW 42
#define FRAMETIME_FIXED_SLOW (1000/WLED_FPS_SLOW)
-//#define FRAMETIME _frametime
#define FRAMETIME strip.getFrameTime()
//#define MIN_SHOW_DELAY (_frametime < 16 ? 8 : 15) // Upstream legacy
#define MIN_SHOW_DELAY (_frametime < 16 ? (_frametime <8? (_frametime <7? (_frametime <6 ? 2 :3) :4) : 8) : 15) // WLEDMM support higher framerates (up to 250fps)
diff --git a/wled00/FX_fcn.cpp b/wled00/FX_fcn.cpp
index 44642689..5ad6583c 100644
--- a/wled00/FX_fcn.cpp
+++ b/wled00/FX_fcn.cpp
@@ -1877,15 +1877,15 @@ void WS2812FX::service() {
#if defined(ARDUINO_ARCH_ESP32) && defined(WLEDMM_FASTPATH) // WLEDMM go faster on ESP32
//if (_suspend) return;
if (elapsed < 2) return; // keep wifi alive
- if ( !_triggered && (_targetFps < FPS_UNLIMITED) && (_targetFps > 0)) {
+ if ( !_triggered && (_targetFps != FPS_UNLIMITED) && (_targetFps != FPS_UNLIMITED_AC)) {
if (elapsed < MIN_SHOW_DELAY) return; // WLEDMM too early for service
}
#else // legacy
- if (elapsed < MIN_SHOW_DELAY) return;
+ if (nowUp - _lastShow < MIN_SHOW_DELAY) return;
#endif
bool doShow = false;
- unsigned speedLimit = (_targetFps < FPS_UNLIMITED) ? (0.85f * FRAMETIME) : 1; // WLEDMM lower limit for effect frametime
+ unsigned speedLimit = (_targetFps != FPS_UNLIMITED) && (_targetFps != FPS_UNLIMITED_AC) ? (0.85f * FRAMETIME) : 1; // WLEDMM minimum for effect frametime
_isServicing = true;
_segment_index = 0;
@@ -2120,10 +2120,10 @@ uint16_t WS2812FX::getFps() const {
}
void WS2812FX::setTargetFps(uint8_t fps) {
- if (fps > 0 && fps <= 251) _targetFps = fps; // WLEDMM allow higher framerates
- _frametime = 1000 / _targetFps;
- if (_frametime < 1) _frametime = 1; // WLEDMM better safe than sorry
- if (fps >= FPS_UNLIMITED) _frametime = 3; // WLEDMM unlimited mode
+ if (fps <= 251) _targetFps = fps; // WLEDMM allow higher framerates
+ if (fps > 0) _frametime = 1000 / _targetFps;
+ else _frametime = 2; // AC WLED compatibility
+ if (fps >= FPS_UNLIMITED) _frametime = 2; // WLEDMM unlimited mode
}
void WS2812FX::setMode(uint8_t segid, uint8_t m) {
diff --git a/wled00/data/settings_leds.htm b/wled00/data/settings_leds.htm
index 35d32321..7deff3a4 100644
--- a/wled00/data/settings_leds.htm
+++ b/wled00/data/settings_leds.htm
@@ -166,7 +166,8 @@
gId('ampwarning').style.display = (d.Sf.MA.value > 7200) ? 'inline':'none';
- gId('fpswarning').style.display = (d.Sf.FR.value > 69) ? 'block':'none'; // WLEDMM
+ gId('fpsNone').style.display = ((d.Sf.FR.value == 0) || (d.Sf.FR.value > 249))? 'block':'none'; // WLEDMM
+ gId('fpswarning').style.display = (d.Sf.FR.value > 69) || (d.Sf.FR.value == 0) ? 'block':'none'; // WLEDMM
gId('fpshelp1').style.display = ((d.Sf.FR.value > 80) && (d.Sf.FR.value < 132)) ? 'block':'none'; // WLEDMM
gId('fpshelp2').style.display = ((d.Sf.FR.value > 132) && (d.Sf.FR.value < 196)) ? 'block':'none'; // WLEDMM
gId('fpshelp3').style.display = (d.Sf.FR.value > 196) ? 'block':'none'; // WLEDMM
@@ -732,7 +733,8 @@ Length: Linear (never wrap)
- Target refresh rate: FPS
+ Target refresh rate: FPS
+