Address issues reported

This commit is contained in:
Blaž Kristan 2025-04-26 20:08:15 +02:00
parent 7f2b6a3f10
commit c934776f45
14 changed files with 86 additions and 67 deletions

View File

@ -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 {

View File

@ -86,7 +86,7 @@ void WS2812FX::setUpMatrix() {
JsonArray map = pDoc->as<JsonArray>();
gapSize = map.size();
if (!map.isNull() && gapSize >= matrixSize) { // not an empty map
gapTable = static_cast<int8_t*>(w_malloc(gapSize));
gapTable = static_cast<int8_t*>(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<h; i++) { // character height

View File

@ -1034,10 +1034,10 @@ void Segment::blur(uint8_t blur_amount, bool smear) const {
}
#endif
uint8_t keep = smear ? 255 : 255 - blur_amount;
uint8_t seep = blur_amount >> (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)/FPS_CALC_AVG)) >> 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) {

View File

@ -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

View File

@ -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

View File

@ -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<CRGBPalette16> 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

View File

@ -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);

View File

@ -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<char*>(w_malloc(total+1)); // allocate new buffer
p_free(payloadStr); // release buffer if it exists
payloadStr = static_cast<char*>(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);

View File

@ -1,5 +1,5 @@
#include "pin_manager.h"
#include "wled.h"
#include "pin_manager.h"
#ifdef ARDUINO_ARCH_ESP32
#ifdef bitRead

View File

@ -3,11 +3,6 @@
/*
* Registers pins so there is no attempt for two interfaces to use the same pin
*/
#include <Arduino.h>
#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)

View File

@ -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<char*>(w_malloc(33));
if (!quickLoad) quickLoad = static_cast<char*>(w_malloc(9));
if (!saveName) saveName = static_cast<char*>(p_malloc(33));
if (!quickLoad) quickLoad = static_cast<char*>(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 {

View File

@ -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) {

View File

@ -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

View File

@ -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 <cstddef>
#include <vector>
// Library inclusions.
#include <Arduino.h>
#ifdef ESP8266