Skip to content

Commit f593d40

Browse files
committed
Merge pull request #4244 from MoonModules/framerate_ac015
Improved framerate control code - strip.show(), strip.service()
1 parent b75a2de commit f593d40

File tree

5 files changed

+41
-16
lines changed

5 files changed

+41
-16
lines changed

usermods/audioreactive/audio_reactive.h

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ static uint8_t soundAgc = 0; // Automagic gain control: 0 - n
7575
//static float volumeSmth = 0.0f; // either sampleAvg or sampleAgc depending on soundAgc; smoothed sample
7676
static float FFT_MajorPeak = 1.0f; // FFT: strongest (peak) frequency
7777
static float FFT_Magnitude = 0.0f; // FFT: volume (magnitude) of peak frequency
78-
static bool samplePeak = false; // Boolean flag for peak - used in effects. Responding routine may reset this flag. Auto-reset after strip.getMinShowDelay()
78+
static bool samplePeak = false; // Boolean flag for peak - used in effects. Responding routine may reset this flag. Auto-reset after strip.getFrameTime()
7979
static bool udpSamplePeak = false; // Boolean flag for peak. Set at the same time as samplePeak, but reset by transmitAudioData
8080
static unsigned long timeOfPeak = 0; // time of last sample peak detection.
8181
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) {
536536
#endif
537537

538538
static void autoResetPeak(void) {
539-
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
540-
if (millis() - timeOfPeak > MinShowDelay) { // Auto-reset of samplePeak after a complete frame has passed.
539+
uint16_t peakDelay = max(uint16_t(50), strip.getFrameTime());
540+
if (millis() - timeOfPeak > peakDelay) { // Auto-reset of samplePeak after at least one complete frame has passed.
541541
samplePeak = false;
542542
if (audioSyncEnabled == 0) udpSamplePeak = false; // this is normally reset by transmitAudioData
543543
}

wled00/FX.h

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,14 @@
4646
#define WLED_FPS 42
4747
#define FRAMETIME_FIXED (1000/WLED_FPS)
4848
#define FRAMETIME strip.getFrameTime()
49+
#if defined(ARDUINO_ARCH_ESP32) && !defined(CONFIG_IDF_TARGET_ESP32C3) && !defined(CONFIG_IDF_TARGET_ESP32S2)
50+
#define MIN_FRAME_DELAY 2 // minimum wait between repaints, to keep other functions like WiFi alive
51+
#elif defined(CONFIG_IDF_TARGET_ESP32S2) || defined(CONFIG_IDF_TARGET_ESP32C3)
52+
#define MIN_FRAME_DELAY 3 // S2/C3 are slower than normal esp32, and only have one core
53+
#else
54+
#define MIN_FRAME_DELAY 8 // 8266 legacy MIN_SHOW_DELAY
55+
#endif
56+
#define FPS_UNLIMITED 0
4957

5058
// FPS calculation (can be defined as compile flag for debugging)
5159
#ifndef FPS_CALC_AVG
@@ -77,8 +85,6 @@
7785
assuming each segment uses the same amount of data. 256 for ESP8266, 640 for ESP32. */
7886
#define FAIR_DATA_PER_SEG (MAX_SEGMENT_DATA / strip.getMaxSegments())
7987

80-
#define MIN_SHOW_DELAY (_frametime < 16 ? 8 : 15)
81-
8288
#define NUM_COLORS 3 /* number of colors per segment */
8389
#define SEGMENT strip._segments[strip.getCurrSegmentId()]
8490
#define SEGENV strip._segments[strip.getCurrSegmentId()]
@@ -748,6 +754,7 @@ class WS2812FX { // 96 bytes
748754
customMappingTable(nullptr),
749755
customMappingSize(0),
750756
_lastShow(0),
757+
_lastServiceShow(0),
751758
_segment_index(0),
752759
_mainSegment(0)
753760
{
@@ -846,7 +853,7 @@ class WS2812FX { // 96 bytes
846853
getMappedPixelIndex(uint16_t index) const;
847854

848855
inline uint16_t getFrameTime() const { return _frametime; } // returns amount of time a frame should take (in ms)
849-
inline uint16_t getMinShowDelay() const { return MIN_SHOW_DELAY; } // returns minimum amount of time strip.service() can be delayed (constant)
856+
inline uint16_t getMinShowDelay() const { return MIN_FRAME_DELAY; } // returns minimum amount of time strip.service() can be delayed (constant)
850857
inline uint16_t getLength() const { return _length; } // returns actual amount of LEDs on a strip (2D matrix may have less LEDs than W*H)
851858
inline uint16_t getTransition() const { return _transitionDur; } // returns currently set transition time (in ms)
852859

@@ -958,6 +965,7 @@ class WS2812FX { // 96 bytes
958965
uint16_t customMappingSize;
959966

960967
unsigned long _lastShow;
968+
unsigned long _lastServiceShow;
961969

962970
uint8_t _segment_index;
963971
uint8_t _mainSegment;

wled00/FX_fcn.cpp

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1304,7 +1304,14 @@ void WS2812FX::finalizeInit() {
13041304
void WS2812FX::service() {
13051305
unsigned long nowUp = millis(); // Be aware, millis() rolls over every 49 days
13061306
now = nowUp + timebase;
1307-
if (nowUp - _lastShow < MIN_SHOW_DELAY || _suspend) return;
1307+
if (_suspend) return;
1308+
unsigned long elapsed = nowUp - _lastServiceShow;
1309+
1310+
if (elapsed <= MIN_FRAME_DELAY) return; // keep wifi alive - no matter if triggered or unlimited
1311+
if ( !_triggered && (_targetFps != FPS_UNLIMITED)) { // unlimited mode = no frametime
1312+
if (elapsed < _frametime) return; // too early for service
1313+
}
1314+
13081315
bool doShow = false;
13091316

13101317
_isServicing = true;
@@ -1321,7 +1328,7 @@ void WS2812FX::service() {
13211328
if (!seg.isActive()) continue;
13221329

13231330
// last condition ensures all solid segments are updated at the same time
1324-
if (nowUp > seg.next_time || _triggered || (doShow && seg.mode == FX_MODE_STATIC))
1331+
if (nowUp >= seg.next_time || _triggered || (doShow && seg.mode == FX_MODE_STATIC))
13251332
{
13261333
doShow = true;
13271334
unsigned frameDelay = FRAMETIME;
@@ -1371,15 +1378,16 @@ void WS2812FX::service() {
13711378
_triggered = false;
13721379

13731380
#ifdef WLED_DEBUG
1374-
if (millis() - nowUp > _frametime) DEBUG_PRINTF_P(PSTR("Slow effects %u/%d.\n"), (unsigned)(millis()-nowUp), (int)_frametime);
1381+
if ((_targetFps != FPS_UNLIMITED) && (millis() - nowUp > _frametime)) DEBUG_PRINTF_P(PSTR("Slow effects %u/%d.\n"), (unsigned)(millis()-nowUp), (int)_frametime);
13751382
#endif
13761383
if (doShow) {
13771384
yield();
13781385
Segment::handleRandomPalette(); // slowly transition random palette; move it into for loop when each segment has individual random palette
13791386
show();
1387+
_lastServiceShow = nowUp; // update timestamp, for precise FPS control
13801388
}
13811389
#ifdef WLED_DEBUG
1382-
if (millis() - nowUp > _frametime) DEBUG_PRINTF_P(PSTR("Slow strip %u/%d.\n"), (unsigned)(millis()-nowUp), (int)_frametime);
1390+
if ((_targetFps != FPS_UNLIMITED) && (millis() - nowUp > _frametime)) DEBUG_PRINTF_P(PSTR("Slow strip %u/%d.\n"), (unsigned)(millis()-nowUp), (int)_frametime);
13831391
#endif
13841392
}
13851393

@@ -1399,13 +1407,13 @@ void WS2812FX::show() {
13991407
// avoid race condition, capture _callback value
14001408
show_callback callback = _callback;
14011409
if (callback) callback();
1410+
unsigned long showNow = millis();
14021411

14031412
// some buses send asynchronously and this method will return before
14041413
// all of the data has been sent.
14051414
// See https://github.com/Makuna/NeoPixelBus/wiki/ESP32-NeoMethods#neoesp32rmt-methods
14061415
BusManager::show();
14071416

1408-
unsigned long showNow = millis();
14091417
size_t diff = showNow - _lastShow;
14101418

14111419
if (diff > 0) { // skip calculation if no time has passed
@@ -1433,8 +1441,9 @@ uint16_t WS2812FX::getFps() const {
14331441
}
14341442

14351443
void WS2812FX::setTargetFps(uint8_t fps) {
1436-
if (fps > 0 && fps <= 120) _targetFps = fps;
1437-
_frametime = 1000 / _targetFps;
1444+
if (fps <= 250) _targetFps = fps;
1445+
if (_targetFps > 0) _frametime = 1000 / _targetFps;
1446+
else _frametime = MIN_FRAME_DELAY; // unlimited mode
14381447
}
14391448

14401449
void WS2812FX::setMode(uint8_t segid, uint8_t m) {
@@ -1482,7 +1491,7 @@ void WS2812FX::setBrightness(uint8_t b, bool direct) {
14821491
BusManager::setBrightness(b);
14831492
if (!direct) {
14841493
unsigned long t = millis();
1485-
if (_segments[0].next_time > t + 22 && t - _lastShow > MIN_SHOW_DELAY) trigger(); //apply brightness change immediately if no refresh soon
1494+
if (_segments[0].next_time > t + 22 && t - _lastShow > MIN_FRAME_DELAY) trigger(); //apply brightness change immediately if no refresh soon
14861495
}
14871496
}
14881497

wled00/data/settings_leds.htm

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -379,6 +379,11 @@
379379
gId('psu').innerHTML = s;
380380
gId('psu2').innerHTML = s2;
381381
gId("json").style.display = d.Sf.IT.value==8 ? "" : "none";
382+
383+
// show/hide FPS warning messages
384+
gId('fpsNone').style.display = (d.Sf.FR.value == 0) ? 'block':'none';
385+
gId('fpsWarn').style.display = (d.Sf.FR.value == 0) || (d.Sf.FR.value >= 80) ? 'block':'none';
386+
gId('fpsHigh').style.display = (d.Sf.FR.value >= 80) ? 'block':'none';
382387
}
383388
function lastEnd(i) {
384389
if (i-- < 1) return 0;
@@ -869,7 +874,10 @@ <h3>Advanced</h3>
869874
<option value="2">Linear (never wrap)</option>
870875
<option value="3">None (not recommended)</option>
871876
</select><br>
872-
Target refresh rate: <input type="number" class="s" min="1" max="120" name="FR" required> FPS
877+
Target refresh rate: <input type="number" class="s" min="0" max="250" name="FR" oninput="UI()" required> FPS
878+
<div id="fpsNone" class="warn" style="display: none;">&#9888; Unlimited FPS Mode is experimental &#9888;<br></div>
879+
<div id="fpsHigh" class="warn" style="display: none;">&#9888; High FPS Mode is experimental.<br></div>
880+
<div id="fpsWarn" class="warn" style="display: none;">Please <a class="lnk" href="sec#backup">backup</a> WLED configuration and presets first!<br></div>
873881
<hr class="sml">
874882
<div id="cfg">Config template: <input type="file" name="data2" accept=".json"><button type="button" class="sml" onclick="loadCfg(d.Sf.data2)">Apply</button><br></div>
875883
<hr>

wled00/data/settings_sec.htm

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ <h2>Security & Update setup</h2>
5757
<h3>Software Update</h3>
5858
<button type="button" onclick="U()">Manual OTA Update</button><br>
5959
Enable ArduinoOTA: <input type="checkbox" name="AO">
60-
<hr>
60+
<hr id="backup">
6161
<h3>Backup & Restore</h3>
6262
<div class="warn">&#9888; Restoring presets/configuration will OVERWRITE your current presets/configuration.<br>
6363
Incorrect upload or configuration may require a factory reset or re-flashing of your ESP.<br>

0 commit comments

Comments
 (0)