diff --git a/wled00/FX.h b/wled00/FX.h index c060c868d..9c895c0c0 100755 --- a/wled00/FX.h +++ b/wled00/FX.h @@ -833,8 +833,8 @@ class WS2812FX { _length(DEFAULT_LED_COUNT), _transitionDur(750), _frametime(FRAMETIME_FIXED), + _cumulativeFps(WLED_FPS << FPS_CALC_SHIFT), _targetFps(WLED_FPS), - _cumulativeFps(WLED_FPS), _isServicing(false), _isOffRefreshRequired(false), _hasWhiteChannel(false), @@ -845,7 +845,8 @@ class WS2812FX { _callback(nullptr), customMappingTable(nullptr), customMappingSize(0), - _lastShow(0) + _lastShow(0), + _lastServiceShow(0) { _mode.reserve(_modeCount); // allocate memory to prevent initial fragmentation (does not increase size()) _modeData.reserve(_modeCount); // allocate memory to prevent initial fragmentation (does not increase size()) @@ -1011,8 +1012,8 @@ class WS2812FX { uint16_t _transitionDur; uint16_t _frametime; + uint16_t _cumulativeFps; uint8_t _targetFps; - uint8_t _cumulativeFps; // will require only 1 byte struct { diff --git a/wled00/FX_2Dfcn.cpp b/wled00/FX_2Dfcn.cpp index 69c743183..9a3c6fbe8 100644 --- a/wled00/FX_2Dfcn.cpp +++ b/wled00/FX_2Dfcn.cpp @@ -86,7 +86,7 @@ void WS2812FX::setUpMatrix() { JsonArray map = pDoc->as(); gapSize = map.size(); if (!map.isNull() && gapSize >= matrixSize) { // not an empty map - gapTable = static_cast(w_malloc(gapSize)); + gapTable = static_cast(p_malloc(gapSize)); if (gapTable) for (size_t i = 0; i < gapSize; i++) { gapTable[i] = constrain(map[i], -1, 1); } @@ -113,7 +113,7 @@ void WS2812FX::setUpMatrix() { } // delete gap array as we no longer need it - w_free(gapTable); + p_free(gapTable); resume(); #ifdef WLED_DEBUG @@ -246,11 +246,11 @@ void Segment::blur2D(uint8_t blur_x, uint8_t blur_y, bool smear) const { const unsigned cols = vWidth(); const unsigned rows = vHeight(); const auto XY = [&](unsigned x, unsigned y){ return x + y*cols; }; - uint32_t lastnew; + uint32_t lastnew; // not necessary to initialize lastnew and last, as both will be initialized by the first loop iteration uint32_t last; if (blur_x) { const uint8_t keepx = smear ? 255 : 255 - blur_x; - const uint8_t seepx = blur_x >> (1 + smear); + const uint8_t seepx = blur_x >> 1; for (unsigned row = 0; row < rows; row++) { // blur rows (x direction) uint32_t carryover = BLACK; uint32_t curnew = BLACK; @@ -273,7 +273,7 @@ void Segment::blur2D(uint8_t blur_x, uint8_t blur_y, bool smear) const { } if (blur_y) { const uint8_t keepy = smear ? 255 : 255 - blur_y; - const uint8_t seepy = blur_y >> (1 + smear); + const uint8_t seepy = blur_y >> 1; for (unsigned col = 0; col < cols; col++) { uint32_t carryover = BLACK; uint32_t curnew = BLACK; @@ -584,6 +584,7 @@ void Segment::drawCharacter(unsigned char chr, int16_t x, int16_t y, uint8_t w, chr -= 32; // align with font table entries const int font = w*h; + // if col2 == BLACK then use currently selected palette for gradient otherwise create gradient from color and col2 CRGBPalette16 grad = col2 ? CRGBPalette16(CRGB(color), CRGB(col2)) : SEGPALETTE; // selected palette as gradient for (int i = 0; i> (1 + smear); + uint8_t seep = blur_amount >> 1; unsigned vlength = vLength(); uint32_t carryover = BLACK; - uint32_t lastnew; + uint32_t lastnew; // not necessary to initialize lastnew and last, as both will be initialized by the first loop iteration uint32_t last; uint32_t curnew = BLACK; for (unsigned i = 0; i < vlength; i++) { @@ -1198,7 +1198,12 @@ 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_FRAME_DELAY || _suspend) return; + unsigned long elapsed = nowUp - _lastServiceShow; + if (_suspend || 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; @@ -1255,15 +1260,16 @@ void WS2812FX::service() { } #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 && !_suspend) { yield(); Segment::handleRandomPalette(); // slowly transition random palette; move it into for loop when each segment has individual random palette + _lastServiceShow = nowUp; // update timestamp, for precise FPS control show(); } #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 _triggered = false; @@ -1612,8 +1618,8 @@ void WS2812FX::show() { if (newBri != _brightness) BusManager::setBrightness(_brightness); if (diff > 0) { // skip calculation if no time has passed - int fpsCurr = (1000 << FPS_CALC_SHIFT) / diff; // fixed point math (shift left for better precision) - _cumulativeFps += ((fpsCurr - (_cumulativeFps << FPS_CALC_SHIFT)) / FPS_CALC_AVG + ((1<> FPS_CALC_SHIFT; // simple PI controller over FPS_CALC_AVG frames + size_t fpsCurr = (1000 << FPS_CALC_SHIFT) / diff; // fixed point math + _cumulativeFps = (FPS_CALC_AVG * _cumulativeFps + fpsCurr + FPS_CALC_AVG / 2) / (FPS_CALC_AVG + 1); // "+FPS_CALC_AVG/2" for proper rounding _lastShow = showNow; } } @@ -1653,8 +1659,9 @@ void WS2812FX::waitForIt() { }; void WS2812FX::setTargetFps(unsigned 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::setCCT(uint16_t k) { diff --git a/wled00/bus_manager.cpp b/wled00/bus_manager.cpp index 59d6f5435..56e594795 100644 --- a/wled00/bus_manager.cpp +++ b/wled00/bus_manager.cpp @@ -37,19 +37,21 @@ uint8_t realtimeBroadcast(uint8_t type, IPAddress client, uint16_t length, const //util.cpp // PSRAM allocation wrappers #ifndef ESP8266 -void *w_malloc(size_t); // prefer PSRAM over DRAM -void *w_calloc(size_t, size_t); // prefer PSRAM over DRAM -void *w_realloc(void *, size_t); // prefer PSRAM over DRAM -inline void w_free(void *ptr) { heap_caps_free(ptr); } -void *d_malloc(size_t); // prefer DRAM over PSRAM -void *d_calloc(size_t, size_t); // prefer DRAM over PSRAM -void *d_realloc(void *, size_t); // prefer DRAM over PSRAM -inline void d_free(void *ptr) { heap_caps_free(ptr); } +extern "C" { + void *p_malloc(size_t); // prefer PSRAM over DRAM + void *p_calloc(size_t, size_t); // prefer PSRAM over DRAM + void *p_realloc(void *, size_t); // prefer PSRAM over DRAM + inline void p_free(void *ptr) { heap_caps_free(ptr); } + void *d_malloc(size_t); // prefer DRAM over PSRAM + void *d_calloc(size_t, size_t); // prefer DRAM over PSRAM + void *d_realloc(void *, size_t); // prefer DRAM over PSRAM + inline void d_free(void *ptr) { heap_caps_free(ptr); } +} #else -#define w_malloc malloc -#define w_calloc calloc -#define w_realloc realloc -#define w_free free +#define p_malloc malloc +#define p_calloc calloc +#define p_realloc realloc +#define p_free free #define d_malloc malloc #define d_calloc calloc #define d_realloc realloc diff --git a/wled00/const.h b/wled00/const.h index 877e02dc1..cfcd0a6b8 100644 --- a/wled00/const.h +++ b/wled00/const.h @@ -1,3 +1,4 @@ +#pragma once #ifndef WLED_CONST_H #define WLED_CONST_H @@ -49,6 +50,9 @@ #define WLED_MAX_ANALOG_CHANNELS 5 #define WLED_MIN_VIRTUAL_BUSSES 3 // no longer used for bus creation but used to distinguish S2/S3 in UI #else + #if !defined(LEDC_CHANNEL_MAX) || !defined(LEDC_SPEED_MODE_MAX) + #include "driver/ledc.h" // needed for analog/LEDC channel counts + #endif #define WLED_MAX_ANALOG_CHANNELS (LEDC_CHANNEL_MAX*LEDC_SPEED_MODE_MAX) #if defined(CONFIG_IDF_TARGET_ESP32C3) // 2 RMT, 6 LEDC, only has 1 I2S but NPB does not support it ATM #define WLED_MAX_DIGITAL_CHANNELS 2 @@ -76,6 +80,7 @@ #undef WLED_MAX_BUSSES #endif #define WLED_MAX_BUSSES (WLED_MAX_DIGITAL_CHANNELS+WLED_MAX_ANALOG_CHANNELS) +static_assert(WLED_MAX_BUSSES <= 32, "WLED_MAX_BUSSES exceeds hard limit"); // Maximum number of pins per output. 5 for RGBCCT analog LEDs. #define OUTPUT_MAX_PINS 5 diff --git a/wled00/fcn_declare.h b/wled00/fcn_declare.h index 62d05ecd5..486e5c562 100644 --- a/wled00/fcn_declare.h +++ b/wled00/fcn_declare.h @@ -173,7 +173,8 @@ inline uint32_t color_blend16(uint32_t c1, uint32_t c2, uint16_t b) { return col CRGBPalette16 generateHarmonicRandomPalette(const CRGBPalette16 &basepalette); CRGBPalette16 generateRandomPalette(); void loadCustomPalettes(); -#define getPaletteCount() (13 + GRADIENT_PALETTE_COUNT + customPalettes.size()) +extern std::vector customPalettes; +inline size_t getPaletteCount() { return 13 + GRADIENT_PALETTE_COUNT + customPalettes.size(); } inline uint32_t colorFromRgbw(byte* rgbw) { return uint32_t((byte(rgbw[3]) << 24) | (byte(rgbw[0]) << 16) | (byte(rgbw[1]) << 8) | (byte(rgbw[2]))); } void hsv2rgb(const CHSV32& hsv, uint32_t& rgb); void colorHStoRGB(uint16_t hue, byte sat, byte* rgb); @@ -545,19 +546,21 @@ inline uint8_t hw_random8(uint32_t lowerlimit, uint32_t upperlimit) { uint32_t r // PSRAM allocation wrappers #ifndef ESP8266 -void *w_malloc(size_t); // prefer PSRAM over DRAM -void *w_calloc(size_t, size_t); // prefer PSRAM over DRAM -void *w_realloc(void *, size_t); // prefer PSRAM over DRAM -inline void w_free(void *ptr) { heap_caps_free(ptr); } -void *d_malloc(size_t); // prefer DRAM over PSRAM -void *d_calloc(size_t, size_t); // prefer DRAM over PSRAM -void *d_realloc(void *, size_t); // prefer DRAM over PSRAM -inline void d_free(void *ptr) { heap_caps_free(ptr); } +extern "C" { + void *p_malloc(size_t); // prefer PSRAM over DRAM + void *p_calloc(size_t, size_t); // prefer PSRAM over DRAM + void *p_realloc(void *, size_t); // prefer PSRAM over DRAM + inline void p_free(void *ptr) { heap_caps_free(ptr); } + void *d_malloc(size_t); // prefer DRAM over PSRAM + void *d_calloc(size_t, size_t); // prefer DRAM over PSRAM + void *d_realloc(void *, size_t); // prefer DRAM over PSRAM + inline void d_free(void *ptr) { heap_caps_free(ptr); } +} #else -#define w_malloc malloc -#define w_calloc calloc -#define w_realloc realloc -#define w_free free +#define p_malloc malloc +#define p_calloc calloc +#define p_realloc realloc +#define p_free free #define d_malloc malloc #define d_calloc calloc #define d_realloc realloc diff --git a/wled00/file.cpp b/wled00/file.cpp index 4df331997..c1960e616 100644 --- a/wled00/file.cpp +++ b/wled00/file.cpp @@ -392,7 +392,7 @@ static const uint8_t *getPresetCache(size_t &size) { if ((presetsModifiedTime != presetsCachedTime) || (presetsCachedValidate != cacheInvalidate)) { if (presetsCached) { - w_free(presetsCached); + p_free(presetsCached); presetsCached = nullptr; } } @@ -403,7 +403,7 @@ static const uint8_t *getPresetCache(size_t &size) { presetsCachedTime = presetsModifiedTime; presetsCachedValidate = cacheInvalidate; presetsCachedSize = 0; - presetsCached = (uint8_t*)w_malloc(file.size() + 1); + presetsCached = (uint8_t*)p_malloc(file.size() + 1); if (presetsCached) { presetsCachedSize = file.size(); file.read(presetsCached, presetsCachedSize); diff --git a/wled00/mqtt.cpp b/wled00/mqtt.cpp index a1f659510..19d4e889c 100644 --- a/wled00/mqtt.cpp +++ b/wled00/mqtt.cpp @@ -68,8 +68,8 @@ static void onMqttMessage(char* topic, char* payload, AsyncMqttClientMessageProp } if (index == 0) { // start (1st partial packet or the only packet) - w_free(payloadStr); // release buffer if it exists - payloadStr = static_cast(w_malloc(total+1)); // allocate new buffer + p_free(payloadStr); // release buffer if it exists + payloadStr = static_cast(p_malloc(total+1)); // allocate new buffer } if (payloadStr == nullptr) return; // buffer not allocated @@ -94,7 +94,7 @@ static void onMqttMessage(char* topic, char* payload, AsyncMqttClientMessageProp } else { // Non-Wled Topic used here. Probably a usermod subscribed to this topic. UsermodManager::onMqttMessage(topic, payloadStr); - w_free(payloadStr); + p_free(payloadStr); payloadStr = nullptr; return; } @@ -124,7 +124,7 @@ static void onMqttMessage(char* topic, char* payload, AsyncMqttClientMessageProp // topmost topic (just wled/MAC) parseMQTTBriPayload(payloadStr); } - w_free(payloadStr); + p_free(payloadStr); payloadStr = nullptr; } @@ -196,7 +196,7 @@ bool initMqtt() if (!mqttEnabled || mqttServer[0] == 0 || !WLED_CONNECTED) return false; if (mqtt == nullptr) { - void *ptr = w_malloc(sizeof(AsyncMqttClient)); + void *ptr = p_malloc(sizeof(AsyncMqttClient)); mqtt = new (ptr) AsyncMqttClient(); // use placement new (into PSRAM), client will never be deleted if (!mqtt) return false; mqtt->onMessage(onMqttMessage); diff --git a/wled00/pin_manager.cpp b/wled00/pin_manager.cpp index 6f1652301..cdbb85267 100644 --- a/wled00/pin_manager.cpp +++ b/wled00/pin_manager.cpp @@ -1,5 +1,5 @@ -#include "pin_manager.h" #include "wled.h" +#include "pin_manager.h" #ifdef ARDUINO_ARCH_ESP32 #ifdef bitRead diff --git a/wled00/pin_manager.h b/wled00/pin_manager.h index b285b6ee5..662e499b2 100644 --- a/wled00/pin_manager.h +++ b/wled00/pin_manager.h @@ -3,11 +3,6 @@ /* * Registers pins so there is no attempt for two interfaces to use the same pin */ -#include -#ifdef ARDUINO_ARCH_ESP32 -#include "driver/ledc.h" // needed for analog/LEDC channel counts -#endif -#include "const.h" // for USERMOD_* values #ifdef ESP8266 #define WLED_NUM_PINS (GPIO_PIN_COUNT+1) // somehow they forgot GPIO 16 (0-16==17) diff --git a/wled00/presets.cpp b/wled00/presets.cpp index 0a4380f8c..fed2c1ed9 100644 --- a/wled00/presets.cpp +++ b/wled00/presets.cpp @@ -57,10 +57,10 @@ static void doSaveState() { */ #if defined(ARDUINO_ARCH_ESP32) if (!persist) { - w_free(tmpRAMbuffer); + p_free(tmpRAMbuffer); size_t len = measureJson(*pDoc) + 1; // if possible use SPI RAM on ESP32 - tmpRAMbuffer = (char*)w_malloc(len); + tmpRAMbuffer = (char*)p_malloc(len); if (tmpRAMbuffer!=nullptr) { serializeJson(*pDoc, tmpRAMbuffer, len); } else { @@ -77,8 +77,8 @@ static void doSaveState() { // clean up saveLedmap = -1; presetToSave = 0; - w_free(saveName); - w_free(quickLoad); + p_free(saveName); + p_free(quickLoad); saveName = nullptr; quickLoad = nullptr; playlistSave = false; @@ -203,7 +203,7 @@ void handlePresets() #if defined(ARDUINO_ARCH_ESP32) //Aircoookie recommended not to delete buffer if (tmpPreset==255 && tmpRAMbuffer!=nullptr) { - w_free(tmpRAMbuffer); + p_free(tmpRAMbuffer); tmpRAMbuffer = nullptr; } #endif @@ -217,8 +217,8 @@ void handlePresets() //called from handleSet(PS=) [network callback (sObj is empty), IR (irrational), deserializeState, UDP] and deserializeState() [network callback (filedoc!=nullptr)] void savePreset(byte index, const char* pname, JsonObject sObj) { - if (!saveName) saveName = static_cast(w_malloc(33)); - if (!quickLoad) quickLoad = static_cast(w_malloc(9)); + if (!saveName) saveName = static_cast(p_malloc(33)); + if (!quickLoad) quickLoad = static_cast(p_malloc(9)); if (!saveName || !quickLoad) return; if (index == 0 || (index > 250 && index < 255)) return; @@ -264,8 +264,8 @@ void savePreset(byte index, const char* pname, JsonObject sObj) presetsModifiedTime = toki.second(); //unix time updateFSInfo(); } - w_free(saveName); - w_free(quickLoad); + p_free(saveName); + p_free(quickLoad); saveName = nullptr; quickLoad = nullptr; } else { diff --git a/wled00/util.cpp b/wled00/util.cpp index 8276c9c87..97e1e3b03 100644 --- a/wled00/util.cpp +++ b/wled00/util.cpp @@ -620,7 +620,7 @@ int32_t hw_random(int32_t lowerlimit, int32_t upperlimit) { } #ifndef ESP8266 -void *w_malloc(size_t size) { +void *p_malloc(size_t size) { int caps1 = MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT; int caps2 = MALLOC_CAP_DEFAULT | MALLOC_CAP_8BIT; if (psramSafe) { @@ -630,7 +630,7 @@ void *w_malloc(size_t size) { return heap_caps_malloc(size, caps2); } -void *w_realloc(void *ptr, size_t size) { +void *p_realloc(void *ptr, size_t size) { int caps1 = MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT; int caps2 = MALLOC_CAP_DEFAULT | MALLOC_CAP_8BIT; if (psramSafe) { @@ -640,7 +640,7 @@ void *w_realloc(void *ptr, size_t size) { return heap_caps_realloc(ptr, size, caps2); } -void *w_calloc(size_t count, size_t size) { +void *p_calloc(size_t count, size_t size) { int caps1 = MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT; int caps2 = MALLOC_CAP_DEFAULT | MALLOC_CAP_8BIT; if (psramSafe) { diff --git a/wled00/wled.cpp b/wled00/wled.cpp index e22b94c77..111fc12e9 100644 --- a/wled00/wled.cpp +++ b/wled00/wled.cpp @@ -753,7 +753,9 @@ void WLED::handleConnection() static bool scanDone = true; static byte stacO = 0; const unsigned long now = millis(); + #ifdef WLED_DEBUG const unsigned long nowS = now/1000; + #endif const bool wifiConfigured = WLED_WIFI_CONFIGURED; // ignore connection handling if WiFi is configured and scan still running diff --git a/wled00/wled.h b/wled00/wled.h index 230b9cbcf..efcbacc11 100644 --- a/wled00/wled.h +++ b/wled00/wled.h @@ -64,6 +64,9 @@ //This is generally a terrible idea, but improves boot success on boards with a 3.3v regulator + cap setup that can't provide 400mA peaks //#define WLED_DISABLE_BROWNOUT_DET +#include +#include + // Library inclusions. #include #ifdef ESP8266