diff --git a/usermods/audioreactive/audio_reactive.h b/usermods/audioreactive/audio_reactive.h
index ad449fc83..9c463e0a1 100644
--- a/usermods/audioreactive/audio_reactive.h
+++ b/usermods/audioreactive/audio_reactive.h
@@ -75,7 +75,7 @@ static uint8_t soundAgc = 0; // Automagic gain control: 0 - n
//static float volumeSmth = 0.0f; // either sampleAvg or sampleAgc depending on soundAgc; smoothed sample
static float FFT_MajorPeak = 1.0f; // FFT: strongest (peak) frequency
static float FFT_Magnitude = 0.0f; // FFT: volume (magnitude) of peak frequency
-static bool samplePeak = false; // Boolean flag for peak - used in effects. Responding routine may reset this flag. Auto-reset after strip.getMinShowDelay()
+static bool samplePeak = false; // Boolean flag for peak - used in effects. Responding routine may reset this flag. Auto-reset after strip.getFrameTime()
static bool udpSamplePeak = false; // Boolean flag for peak. Set at the same time as samplePeak, but reset by transmitAudioData
static unsigned long timeOfPeak = 0; // time of last sample peak detection.
static uint8_t fftResult[NUM_GEQ_CHANNELS]= {0};// Our calculated freq. channel result table to be used by effects
@@ -536,8 +536,8 @@ static void detectSamplePeak(void) {
#endif
static void autoResetPeak(void) {
- uint16_t MinShowDelay = MAX(50, strip.getMinShowDelay()); // Fixes private class variable compiler error. Unsure if this is the correct way of fixing the root problem. -THATDONFC
- if (millis() - timeOfPeak > MinShowDelay) { // Auto-reset of samplePeak after a complete frame has passed.
+ uint16_t peakDelay = max(uint16_t(50), strip.getFrameTime());
+ if (millis() - timeOfPeak > peakDelay) { // Auto-reset of samplePeak after at least one complete frame has passed.
samplePeak = false;
if (audioSyncEnabled == 0) udpSamplePeak = false; // this is normally reset by transmitAudioData
}
diff --git a/wled00/FX.h b/wled00/FX.h
index 545161546..39373c079 100644
--- a/wled00/FX.h
+++ b/wled00/FX.h
@@ -46,6 +46,14 @@
#define WLED_FPS 42
#define FRAMETIME_FIXED (1000/WLED_FPS)
#define FRAMETIME strip.getFrameTime()
+#if defined(ARDUINO_ARCH_ESP32) && !defined(CONFIG_IDF_TARGET_ESP32C3) && !defined(CONFIG_IDF_TARGET_ESP32S2)
+ #define MIN_FRAME_DELAY 2 // minimum wait between repaints, to keep other functions like WiFi alive
+#elif defined(CONFIG_IDF_TARGET_ESP32S2) || defined(CONFIG_IDF_TARGET_ESP32C3)
+ #define MIN_FRAME_DELAY 3 // S2/C3 are slower than normal esp32, and only have one core
+#else
+ #define MIN_FRAME_DELAY 8 // 8266 legacy MIN_SHOW_DELAY
+#endif
+#define FPS_UNLIMITED 0
// FPS calculation (can be defined as compile flag for debugging)
#ifndef FPS_CALC_AVG
@@ -77,8 +85,6 @@
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 ? 8 : 15)
-
#define NUM_COLORS 3 /* number of colors per segment */
#define SEGMENT strip._segments[strip.getCurrSegmentId()]
#define SEGENV strip._segments[strip.getCurrSegmentId()]
@@ -748,6 +754,7 @@ class WS2812FX { // 96 bytes
customMappingTable(nullptr),
customMappingSize(0),
_lastShow(0),
+ _lastServiceShow(0),
_segment_index(0),
_mainSegment(0)
{
@@ -846,7 +853,7 @@ class WS2812FX { // 96 bytes
getMappedPixelIndex(uint16_t index) const;
inline uint16_t getFrameTime() const { return _frametime; } // returns amount of time a frame should take (in ms)
- inline uint16_t getMinShowDelay() const { return MIN_SHOW_DELAY; } // returns minimum amount of time strip.service() can be delayed (constant)
+ inline uint16_t getMinShowDelay() const { return MIN_FRAME_DELAY; } // returns minimum amount of time strip.service() can be delayed (constant)
inline uint16_t getLength() const { return _length; } // returns actual amount of LEDs on a strip (2D matrix may have less LEDs than W*H)
inline uint16_t getTransition() const { return _transitionDur; } // returns currently set transition time (in ms)
@@ -958,6 +965,7 @@ class WS2812FX { // 96 bytes
uint16_t customMappingSize;
unsigned long _lastShow;
+ unsigned long _lastServiceShow;
uint8_t _segment_index;
uint8_t _mainSegment;
diff --git a/wled00/FX_fcn.cpp b/wled00/FX_fcn.cpp
index 7177ca89e..220cc4481 100644
--- a/wled00/FX_fcn.cpp
+++ b/wled00/FX_fcn.cpp
@@ -1304,7 +1304,14 @@ void WS2812FX::finalizeInit() {
void WS2812FX::service() {
unsigned long nowUp = millis(); // Be aware, millis() rolls over every 49 days
now = nowUp + timebase;
- if (nowUp - _lastShow < MIN_SHOW_DELAY || _suspend) return;
+ if (_suspend) return;
+ unsigned long elapsed = nowUp - _lastServiceShow;
+
+ if (elapsed <= MIN_FRAME_DELAY) return; // keep wifi alive - no matter if triggered or unlimited
+ if ( !_triggered && (_targetFps != FPS_UNLIMITED)) { // unlimited mode = no frametime
+ if (elapsed < _frametime) return; // too early for service
+ }
+
bool doShow = false;
_isServicing = true;
@@ -1321,7 +1328,7 @@ void WS2812FX::service() {
if (!seg.isActive()) continue;
// last condition ensures all solid segments are updated at the same time
- if (nowUp > seg.next_time || _triggered || (doShow && seg.mode == FX_MODE_STATIC))
+ if (nowUp >= seg.next_time || _triggered || (doShow && seg.mode == FX_MODE_STATIC))
{
doShow = true;
unsigned frameDelay = FRAMETIME;
@@ -1371,15 +1378,16 @@ void WS2812FX::service() {
_triggered = false;
#ifdef WLED_DEBUG
- if (millis() - nowUp > _frametime) DEBUG_PRINTF_P(PSTR("Slow effects %u/%d.\n"), (unsigned)(millis()-nowUp), (int)_frametime);
+ if ((_targetFps != FPS_UNLIMITED) && (millis() - nowUp > _frametime)) DEBUG_PRINTF_P(PSTR("Slow effects %u/%d.\n"), (unsigned)(millis()-nowUp), (int)_frametime);
#endif
if (doShow) {
yield();
Segment::handleRandomPalette(); // slowly transition random palette; move it into for loop when each segment has individual random palette
show();
+ _lastServiceShow = nowUp; // update timestamp, for precise FPS control
}
#ifdef WLED_DEBUG
- if (millis() - nowUp > _frametime) DEBUG_PRINTF_P(PSTR("Slow strip %u/%d.\n"), (unsigned)(millis()-nowUp), (int)_frametime);
+ if ((_targetFps != FPS_UNLIMITED) && (millis() - nowUp > _frametime)) DEBUG_PRINTF_P(PSTR("Slow strip %u/%d.\n"), (unsigned)(millis()-nowUp), (int)_frametime);
#endif
}
@@ -1399,13 +1407,13 @@ void WS2812FX::show() {
// avoid race condition, capture _callback value
show_callback callback = _callback;
if (callback) callback();
+ unsigned long showNow = millis();
// 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
BusManager::show();
- unsigned long showNow = millis();
size_t diff = showNow - _lastShow;
if (diff > 0) { // skip calculation if no time has passed
@@ -1433,8 +1441,9 @@ uint16_t WS2812FX::getFps() const {
}
void WS2812FX::setTargetFps(uint8_t fps) {
- if (fps > 0 && fps <= 120) _targetFps = fps;
- _frametime = 1000 / _targetFps;
+ if (fps <= 250) _targetFps = fps;
+ if (_targetFps > 0) _frametime = 1000 / _targetFps;
+ else _frametime = MIN_FRAME_DELAY; // unlimited mode
}
void WS2812FX::setMode(uint8_t segid, uint8_t m) {
@@ -1482,7 +1491,7 @@ void WS2812FX::setBrightness(uint8_t b, bool direct) {
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
+ if (_segments[0].next_time > t + 22 && t - _lastShow > MIN_FRAME_DELAY) trigger(); //apply brightness change immediately if no refresh soon
}
}
diff --git a/wled00/data/settings_leds.htm b/wled00/data/settings_leds.htm
index baf80a5d7..62dfe99b6 100644
--- a/wled00/data/settings_leds.htm
+++ b/wled00/data/settings_leds.htm
@@ -379,6 +379,11 @@
gId('psu').innerHTML = s;
gId('psu2').innerHTML = s2;
gId("json").style.display = d.Sf.IT.value==8 ? "" : "none";
+
+ // show/hide FPS warning messages
+ gId('fpsNone').style.display = (d.Sf.FR.value == 0) ? 'block':'none';
+ gId('fpsWarn').style.display = (d.Sf.FR.value == 0) || (d.Sf.FR.value >= 80) ? 'block':'none';
+ gId('fpsHigh').style.display = (d.Sf.FR.value >= 80) ? 'block':'none';
}
function lastEnd(i) {
if (i-- < 1) return 0;
@@ -869,7 +874,10 @@ Swap:
- Target refresh rate: FPS
+ Target refresh rate: FPS
+