Compare commits

..

3 Commits

Author SHA1 Message Date
Frank d643cef23a Merge branch 'main' into spots_improvement 2026-04-08 21:39:18 +02:00
Frank e2bee4fdb2 fixpoint scaling bugfix
* use named constants for fixed point scaling
* remove buggy "SEGLEN / (zones >>3)" shortcut
* remove an unnecessary casts to uint32_t
2026-02-02 23:56:30 +01:00
Frank 8a03b274ec spots effect improvements
* avoid gaps on left/light side of strip, by using higher accuracy zoneLength
* prevent overflows
* ensure that zones calculation are performed in full 32bit
* fix wrong slider name in "spots fade"
2026-02-02 23:30:41 +01:00
3 changed files with 22 additions and 24 deletions
+13 -8
View File
@@ -2914,20 +2914,25 @@ static void spots_base(uint16_t threshold)
if (SEGLEN <= 1) FX_FALLBACK_STATIC;
if (!SEGMENT.check2) SEGMENT.fill(SEGCOLOR(1));
unsigned maxZones = SEGLEN >> 2;
unsigned zones = 1 + ((SEGMENT.intensity * maxZones) >> 8);
unsigned zoneLen = SEGLEN / zones;
unsigned offset = (SEGLEN - zones * zoneLen) >> 1;
// constants for fixed point scaling
constexpr uint8_t ZONELEN_FP_SHIFT = 3;
constexpr uint32_t ZONELEN_FP_SCALE = 1U << ZONELEN_FP_SHIFT;
unsigned maxZones = max(1U, SEGLEN >> 2); // prevents "0 zones"
unsigned zones = 1U + ((uint32_t(SEGMENT.intensity) * maxZones) >> 8);
unsigned zoneLen = SEGLEN / zones;
unsigned zoneLen8 = (SEGLEN * ZONELEN_FP_SCALE) / zones; // zoneLength * 8 (fixedpoint) -> avoids gaps at right/left sides
unsigned offset = (SEGLEN - ((zones * zoneLen8) >> ZONELEN_FP_SHIFT)) >> 1;
for (unsigned z = 0; z < zones; z++)
{
unsigned pos = offset + z * zoneLen;
unsigned pos = offset + ((z * zoneLen8) >> ZONELEN_FP_SHIFT);
for (unsigned i = 0; i < zoneLen; i++)
{
unsigned wave = triwave16((i * 0xFFFF) / zoneLen);
if (wave > threshold) {
unsigned index = 0 + pos + i;
unsigned s = (wave - threshold)*255 / (0xFFFF - threshold);
unsigned index = pos + i;
unsigned s = ((wave - threshold)*255 / (0xFFFF - threshold)) & 0xFF; // & 0xFF prevents overflow in next line
SEGMENT.setPixelColor(index, color_blend(SEGMENT.color_from_palette(index, true, PALETTE_SOLID_WRAP, 0), SEGCOLOR(1), uint8_t(255-s)));
}
}
@@ -2951,7 +2956,7 @@ void mode_spots_fade()
unsigned tr = (t >> 1) + (t >> 2);
spots_base(tr);
}
static const char _data_FX_MODE_SPOTS_FADE[] PROGMEM = "Spots Fade@Spread,Width,,,,,Overlay;!,!;!";
static const char _data_FX_MODE_SPOTS_FADE[] PROGMEM = "Spots Fade@Speed,Width,,,,,Overlay;!,!;!";
//each needs 12 bytes
typedef struct Ball {
+6 -8
View File
@@ -84,8 +84,7 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
JsonArray nw_ins = nw["ins"];
if (!nw_ins.isNull()) {
// as password are stored separately in wsec.json when reading configuration vector resize happens there, but for dynamic config we need to resize if necessary
size_t newSize = min((size_t)WLED_MAX_WIFI_COUNT, nw_ins.size()); // cap at WLED_MAX_WIFI_COUNT (prevent oversizing when too many Wi-Fi entries)
if (nw_ins.size() > 1 && newSize > multiWiFi.size()) multiWiFi.resize(newSize); // resize constructs objects while resizing
if (nw_ins.size() > 1 && nw_ins.size() > multiWiFi.size()) multiWiFi.resize(nw_ins.size()); // resize constructs objects while resizing
for (JsonObject wifi : nw_ins) {
JsonArray ip = wifi["ip"];
JsonArray gw = wifi["gw"];
@@ -791,14 +790,14 @@ void resetConfig() {
}
bool deserializeConfigFromFS() {
(void) deserializeConfigSec(); // success/failure is ignored intentionally. We need to read cfg.json even when wsec.json has errors.
[[maybe_unused]] bool success = deserializeConfigSec();
if (!requestJSONBufferLock(JSON_LOCK_CFG_DES)) return false;
DEBUG_PRINTLN(F("Reading settings from /cfg.json..."));
bool success = readObjectFromFile(s_cfg_json, nullptr, pDoc);
if (!success || (pDoc->overflowed())) pDoc->clear(); // corrupted/too-large → same as missing: seed defaults
success = readObjectFromFile(s_cfg_json, nullptr, pDoc);
// NOTE: This routine deserializes *and* applies the configuration
// Therefore, must also initialize ethernet from this function
JsonObject root = pDoc->as<JsonObject>();
@@ -1264,7 +1263,7 @@ bool deserializeConfigSec() {
if (!requestJSONBufferLock(JSON_LOCK_CFG_SEC_DES)) return false;
bool success = readObjectFromFile(s_wsec_json, nullptr, pDoc);
if (!success || pDoc->overflowed() || pDoc->size() < 1) { // reject if overflowed (noMemory) or empty
if (!success) {
releaseJSONBufferLock();
return false;
}
@@ -1274,8 +1273,7 @@ bool deserializeConfigSec() {
size_t n = 0;
JsonArray nw_ins = root["nw"]["ins"];
if (!nw_ins.isNull()) {
size_t newSize = min((size_t)WLED_MAX_WIFI_COUNT, nw_ins.size()); // cap at WLED_MAX_WIFI_COUNT (prevent oversizing when too many Wi-Fi entries)
if (nw_ins.size() > 1 && newSize > multiWiFi.size()) multiWiFi.resize(newSize); // resize constructs objects while resizing
if (nw_ins.size() > 1 && nw_ins.size() > multiWiFi.size()) multiWiFi.resize(nw_ins.size()); // resize constructs objects while resizing
for (JsonObject wifi : nw_ins) {
char pw[65] = "";
getStringFromJson(pw, wifi["psk"], 65);
+3 -8
View File
@@ -355,17 +355,12 @@ bool readObjectFromFile(const char* file, const char* key, JsonDocument* dest, c
return false;
}
DeserializationError jsonErr = DeserializationError::Ok;
if (filter) jsonErr = deserializeJson(*dest, f, DeserializationOption::Filter(*filter));
else jsonErr = deserializeJson(*dest, f);
if (filter) deserializeJson(*dest, f, DeserializationOption::Filter(*filter));
else deserializeJson(*dest, f);
f.close();
DEBUGFS_PRINTF("Read, took %lu ms\n", millis() - s);
if (jsonErr) { // EmptyInput, IncompleteInput, InvalidInput, NoMemory, TooDeep
DEBUG_PRINTF_P(PSTR("readObjectFromFile(%s): JSON %s !\n"), fileName, jsonErr.c_str());
return jsonErr == DeserializationError::NoMemory || jsonErr == DeserializationError::EmptyInput; // NoMemory => data is partial but usable; empty => handled by caller
} else return true;
return true;
}
void updateFSInfo() {