Merge branch '0_14_1' into power-supply

This commit is contained in:
Blaz Kristan 2023-11-01 10:26:42 +01:00
commit 4f5816f42e
34 changed files with 6241 additions and 6225 deletions

2
package-lock.json generated
View File

@ -1,6 +1,6 @@
{ {
"name": "wled", "name": "wled",
"version": "0.14.0", "version": "0.14.1-a1",
"lockfileVersion": 1, "lockfileVersion": 1,
"requires": true, "requires": true,
"dependencies": { "dependencies": {

View File

@ -1,6 +1,6 @@
{ {
"name": "wled", "name": "wled",
"version": "0.14.0", "version": "0.14.1-a1",
"description": "Tools for WLED project", "description": "Tools for WLED project",
"main": "tools/cdata.js", "main": "tools/cdata.js",
"directories": { "directories": {

View File

@ -217,7 +217,7 @@ build_flags =
; restrict to minimal mime-types ; restrict to minimal mime-types
-DMIMETYPE_MINIMAL -DMIMETYPE_MINIMAL
; other special-purpose framework flags (see https://docs.platformio.org/en/latest/platforms/espressif8266.html) ; other special-purpose framework flags (see https://docs.platformio.org/en/latest/platforms/espressif8266.html)
; -D PIO_FRAMEWORK_ARDUINO_MMU_CACHE16_IRAM48 ;; in case of linker errors like "section `.text1' will not fit in region `iram1_0_seg'" -D PIO_FRAMEWORK_ARDUINO_MMU_CACHE16_IRAM48 ;; in case of linker errors like "section `.text1' will not fit in region `iram1_0_seg'"
; -D PIO_FRAMEWORK_ARDUINO_MMU_CACHE16_IRAM48_SECHEAP_SHARED ;; (experimental) adds some extra heap, but may cause slowdown ; -D PIO_FRAMEWORK_ARDUINO_MMU_CACHE16_IRAM48_SECHEAP_SHARED ;; (experimental) adds some extra heap, but may cause slowdown
lib_deps = lib_deps =

View File

@ -52,9 +52,15 @@ class PWMFanUsermod : public Usermod {
uint8_t tachoUpdateSec = 30; uint8_t tachoUpdateSec = 30;
float targetTemperature = 35.0; float targetTemperature = 35.0;
uint8_t minPWMValuePct = 0; uint8_t minPWMValuePct = 0;
uint8_t maxPWMValuePct = 100;
uint8_t numberOfInterrupsInOneSingleRotation = 2; // Number of interrupts ESP32 sees on tacho signal on a single fan rotation. All the fans I've seen trigger two interrups. uint8_t numberOfInterrupsInOneSingleRotation = 2; // Number of interrupts ESP32 sees on tacho signal on a single fan rotation. All the fans I've seen trigger two interrups.
uint8_t pwmValuePct = 0; uint8_t pwmValuePct = 0;
// constant values
static const uint8_t _pwmMaxValue = 255;
static const uint8_t _pwmMaxStepCount = 7;
float _pwmTempStepSize = 0.5f;
// strings to reduce flash memory usage (used more than twice) // strings to reduce flash memory usage (used more than twice)
static const char _name[]; static const char _name[];
static const char _enabled[]; static const char _enabled[];
@ -63,6 +69,7 @@ class PWMFanUsermod : public Usermod {
static const char _temperature[]; static const char _temperature[];
static const char _tachoUpdateSec[]; static const char _tachoUpdateSec[];
static const char _minPWMValuePct[]; static const char _minPWMValuePct[];
static const char _maxPWMValuePct[];
static const char _IRQperRotation[]; static const char _IRQperRotation[];
static const char _speed[]; static const char _speed[];
static const char _lock[]; static const char _lock[];
@ -156,31 +163,25 @@ class PWMFanUsermod : public Usermod {
void setFanPWMbasedOnTemperature(void) { void setFanPWMbasedOnTemperature(void) {
float temp = getActualTemperature(); float temp = getActualTemperature();
float difftemp = temp - targetTemperature; // dividing minPercent and maxPercent into equal pwmvalue sizes
// Default to run fan at full speed. int pwmStepSize = ((maxPWMValuePct - minPWMValuePct) * _pwmMaxValue) / (_pwmMaxStepCount*100);
int newPWMvalue = 255; int pwmStep = calculatePwmStep(temp - targetTemperature);
int pwmStep = ((100 - minPWMValuePct) * newPWMvalue) / (7*100); // minimum based on full speed - not entered MaxPercent
int pwmMinimumValue = (minPWMValuePct * newPWMvalue) / 100; int pwmMinimumValue = (minPWMValuePct * _pwmMaxValue) / 100;
updateFanSpeed(pwmMinimumValue + pwmStep*pwmStepSize);
if ((temp == NAN) || (temp <= -100.0)) {
DEBUG_PRINTLN(F("WARNING: no temperature value available. Cannot do temperature control. Will set PWM fan to 255."));
} else if (difftemp <= 0.0) {
// Temperature is below target temperature. Run fan at minimum speed.
newPWMvalue = pwmMinimumValue;
} else if (difftemp <= 0.5) {
newPWMvalue = pwmMinimumValue + pwmStep;
} else if (difftemp <= 1.0) {
newPWMvalue = pwmMinimumValue + 2*pwmStep;
} else if (difftemp <= 1.5) {
newPWMvalue = pwmMinimumValue + 3*pwmStep;
} else if (difftemp <= 2.0) {
newPWMvalue = pwmMinimumValue + 4*pwmStep;
} else if (difftemp <= 2.5) {
newPWMvalue = pwmMinimumValue + 5*pwmStep;
} else if (difftemp <= 3.0) {
newPWMvalue = pwmMinimumValue + 6*pwmStep;
} }
updateFanSpeed(newPWMvalue);
uint8_t calculatePwmStep(float diffTemp){
if ((diffTemp == NAN) || (diffTemp <= -100.0)) {
DEBUG_PRINTLN(F("WARNING: no temperature value available. Cannot do temperature control. Will set PWM fan to 255."));
return _pwmMaxStepCount;
}
if(diffTemp <=0){
return 0;
}
int calculatedStep = (diffTemp / _pwmTempStepSize)+1;
// anything greater than max stepcount gets max
return (uint8_t)min((int)_pwmMaxStepCount,calculatedStep);
} }
public: public:
@ -312,6 +313,7 @@ class PWMFanUsermod : public Usermod {
top[FPSTR(_tachoUpdateSec)] = tachoUpdateSec; top[FPSTR(_tachoUpdateSec)] = tachoUpdateSec;
top[FPSTR(_temperature)] = targetTemperature; top[FPSTR(_temperature)] = targetTemperature;
top[FPSTR(_minPWMValuePct)] = minPWMValuePct; top[FPSTR(_minPWMValuePct)] = minPWMValuePct;
top[FPSTR(_maxPWMValuePct)] = maxPWMValuePct;
top[FPSTR(_IRQperRotation)] = numberOfInterrupsInOneSingleRotation; top[FPSTR(_IRQperRotation)] = numberOfInterrupsInOneSingleRotation;
DEBUG_PRINTLN(F("Autosave config saved.")); DEBUG_PRINTLN(F("Autosave config saved."));
} }
@ -345,6 +347,8 @@ class PWMFanUsermod : public Usermod {
targetTemperature = top[FPSTR(_temperature)] | targetTemperature; targetTemperature = top[FPSTR(_temperature)] | targetTemperature;
minPWMValuePct = top[FPSTR(_minPWMValuePct)] | minPWMValuePct; minPWMValuePct = top[FPSTR(_minPWMValuePct)] | minPWMValuePct;
minPWMValuePct = (uint8_t) min(100,max(0,(int)minPWMValuePct)); // bounds checking minPWMValuePct = (uint8_t) min(100,max(0,(int)minPWMValuePct)); // bounds checking
maxPWMValuePct = top[FPSTR(_maxPWMValuePct)] | maxPWMValuePct;
maxPWMValuePct = (uint8_t) min(100,max((int)minPWMValuePct,(int)maxPWMValuePct)); // bounds checking
numberOfInterrupsInOneSingleRotation = top[FPSTR(_IRQperRotation)] | numberOfInterrupsInOneSingleRotation; numberOfInterrupsInOneSingleRotation = top[FPSTR(_IRQperRotation)] | numberOfInterrupsInOneSingleRotation;
numberOfInterrupsInOneSingleRotation = (uint8_t) max(1,(int)numberOfInterrupsInOneSingleRotation); // bounds checking numberOfInterrupsInOneSingleRotation = (uint8_t) max(1,(int)numberOfInterrupsInOneSingleRotation); // bounds checking
@ -389,6 +393,7 @@ const char PWMFanUsermod::_pwmPin[] PROGMEM = "PWM-pin";
const char PWMFanUsermod::_temperature[] PROGMEM = "target-temp-C"; const char PWMFanUsermod::_temperature[] PROGMEM = "target-temp-C";
const char PWMFanUsermod::_tachoUpdateSec[] PROGMEM = "tacho-update-s"; const char PWMFanUsermod::_tachoUpdateSec[] PROGMEM = "tacho-update-s";
const char PWMFanUsermod::_minPWMValuePct[] PROGMEM = "min-PWM-percent"; const char PWMFanUsermod::_minPWMValuePct[] PROGMEM = "min-PWM-percent";
const char PWMFanUsermod::_maxPWMValuePct[] PROGMEM = "max-PWM-percent";
const char PWMFanUsermod::_IRQperRotation[] PROGMEM = "IRQs-per-rotation"; const char PWMFanUsermod::_IRQperRotation[] PROGMEM = "IRQs-per-rotation";
const char PWMFanUsermod::_speed[] PROGMEM = "speed"; const char PWMFanUsermod::_speed[] PROGMEM = "speed";
const char PWMFanUsermod::_lock[] PROGMEM = "lock"; const char PWMFanUsermod::_lock[] PROGMEM = "lock";

View File

@ -24,6 +24,9 @@ Enables the inverted mode in which the background should be enabled and the digi
### Colon-blinking ### Colon-blinking
Enables the blinking colon(s) if they are defined Enables the blinking colon(s) if they are defined
### Leading-Zero
Shows the leading zero of the hour if it exists (i.e. shows `07` instead of `7`)
### enable-auto-brightness ### enable-auto-brightness
Enables the auto brightness feature. Can be used only when the usermod SN_Photoresistor is installed. Enables the auto brightness feature. Can be used only when the usermod SN_Photoresistor is installed.

View File

@ -17,6 +17,7 @@ private:
bool umSSDRDisplayTime = false; bool umSSDRDisplayTime = false;
bool umSSDRInverted = false; bool umSSDRInverted = false;
bool umSSDRColonblink = true; bool umSSDRColonblink = true;
bool umSSDRLeadingZero = false;
bool umSSDREnableLDR = false; bool umSSDREnableLDR = false;
String umSSDRHours = ""; String umSSDRHours = "";
String umSSDRMinutes = ""; String umSSDRMinutes = "";
@ -79,6 +80,7 @@ private:
static const char _str_timeEnabled[]; static const char _str_timeEnabled[];
static const char _str_inverted[]; static const char _str_inverted[];
static const char _str_colonblink[]; static const char _str_colonblink[];
static const char _str_leadingZero[];
static const char _str_displayMask[]; static const char _str_displayMask[];
static const char _str_hours[]; static const char _str_hours[];
static const char _str_minutes[]; static const char _str_minutes[];
@ -105,15 +107,15 @@ private:
switch (umSSDRDisplayMask[index]) { switch (umSSDRDisplayMask[index]) {
case 'h': case 'h':
timeVar = hourFormat12(localTime); timeVar = hourFormat12(localTime);
_showElements(&umSSDRHours, timeVar, 0, 1); _showElements(&umSSDRHours, timeVar, 0, !umSSDRLeadingZero);
break; break;
case 'H': case 'H':
timeVar = hour(localTime); timeVar = hour(localTime);
_showElements(&umSSDRHours, timeVar, 0, 1); _showElements(&umSSDRHours, timeVar, 0, !umSSDRLeadingZero);
break; break;
case 'k': case 'k':
timeVar = hour(localTime) + 1; timeVar = hour(localTime) + 1;
_showElements(&umSSDRHours, timeVar, 0, 0); _showElements(&umSSDRHours, timeVar, 0, !umSSDRLeadingZero);
break; break;
case 'm': case 'm':
timeVar = minute(localTime); timeVar = minute(localTime);
@ -309,6 +311,9 @@ private:
if (_cmpIntSetting_P(topic, payload, _str_colonblink, &umSSDRColonblink)) { if (_cmpIntSetting_P(topic, payload, _str_colonblink, &umSSDRColonblink)) {
return true; return true;
} }
if (_cmpIntSetting_P(topic, payload, _str_leadingZero, &umSSDRLeadingZero)) {
return true;
}
if (strcmp_P(topic, _str_displayMask) == 0) { if (strcmp_P(topic, _str_displayMask) == 0) {
umSSDRDisplayMask = String(payload); umSSDRDisplayMask = String(payload);
_publishMQTTstr_P(_str_displayMask, umSSDRDisplayMask); _publishMQTTstr_P(_str_displayMask, umSSDRDisplayMask);
@ -323,6 +328,7 @@ private:
_publishMQTTint_P(_str_ldrEnabled, umSSDREnableLDR); _publishMQTTint_P(_str_ldrEnabled, umSSDREnableLDR);
_publishMQTTint_P(_str_inverted, umSSDRInverted); _publishMQTTint_P(_str_inverted, umSSDRInverted);
_publishMQTTint_P(_str_colonblink, umSSDRColonblink); _publishMQTTint_P(_str_colonblink, umSSDRColonblink);
_publishMQTTint_P(_str_leadingZero, umSSDRLeadingZero);
_publishMQTTstr_P(_str_hours, umSSDRHours); _publishMQTTstr_P(_str_hours, umSSDRHours);
_publishMQTTstr_P(_str_minutes, umSSDRMinutes); _publishMQTTstr_P(_str_minutes, umSSDRMinutes);
@ -347,6 +353,7 @@ private:
ssdrObj[FPSTR(_str_ldrEnabled)] = umSSDREnableLDR; ssdrObj[FPSTR(_str_ldrEnabled)] = umSSDREnableLDR;
ssdrObj[FPSTR(_str_inverted)] = umSSDRInverted; ssdrObj[FPSTR(_str_inverted)] = umSSDRInverted;
ssdrObj[FPSTR(_str_colonblink)] = umSSDRColonblink; ssdrObj[FPSTR(_str_colonblink)] = umSSDRColonblink;
ssdrObj[FPSTR(_str_leadingZero)] = umSSDRLeadingZero;
ssdrObj[FPSTR(_str_displayMask)] = umSSDRDisplayMask; ssdrObj[FPSTR(_str_displayMask)] = umSSDRDisplayMask;
ssdrObj[FPSTR(_str_hours)] = umSSDRHours; ssdrObj[FPSTR(_str_hours)] = umSSDRHours;
ssdrObj[FPSTR(_str_minutes)] = umSSDRMinutes; ssdrObj[FPSTR(_str_minutes)] = umSSDRMinutes;
@ -425,6 +432,8 @@ public:
invert.add(umSSDRInverted); invert.add(umSSDRInverted);
JsonArray blink = user.createNestedArray("Blinking colon"); JsonArray blink = user.createNestedArray("Blinking colon");
blink.add(umSSDRColonblink); blink.add(umSSDRColonblink);
JsonArray zero = user.createNestedArray("Show the hour leading zero");
zero.add(umSSDRLeadingZero);
JsonArray ldrEnable = user.createNestedArray("Auto Brightness enabled"); JsonArray ldrEnable = user.createNestedArray("Auto Brightness enabled");
ldrEnable.add(umSSDREnableLDR); ldrEnable.add(umSSDREnableLDR);
@ -454,6 +463,7 @@ public:
umSSDREnableLDR = ssdrObj[FPSTR(_str_ldrEnabled)] | umSSDREnableLDR; umSSDREnableLDR = ssdrObj[FPSTR(_str_ldrEnabled)] | umSSDREnableLDR;
umSSDRInverted = ssdrObj[FPSTR(_str_inverted)] | umSSDRInverted; umSSDRInverted = ssdrObj[FPSTR(_str_inverted)] | umSSDRInverted;
umSSDRColonblink = ssdrObj[FPSTR(_str_colonblink)] | umSSDRColonblink; umSSDRColonblink = ssdrObj[FPSTR(_str_colonblink)] | umSSDRColonblink;
umSSDRLeadingZero = ssdrObj[FPSTR(_str_leadingZero)] | umSSDRLeadingZero;
umSSDRDisplayMask = ssdrObj[FPSTR(_str_displayMask)] | umSSDRDisplayMask; umSSDRDisplayMask = ssdrObj[FPSTR(_str_displayMask)] | umSSDRDisplayMask;
} }
} }
@ -516,6 +526,7 @@ public:
umSSDREnableLDR = (top[FPSTR(_str_ldrEnabled)] | umSSDREnableLDR); umSSDREnableLDR = (top[FPSTR(_str_ldrEnabled)] | umSSDREnableLDR);
umSSDRInverted = (top[FPSTR(_str_inverted)] | umSSDRInverted); umSSDRInverted = (top[FPSTR(_str_inverted)] | umSSDRInverted);
umSSDRColonblink = (top[FPSTR(_str_colonblink)] | umSSDRColonblink); umSSDRColonblink = (top[FPSTR(_str_colonblink)] | umSSDRColonblink);
umSSDRLeadingZero = (top[FPSTR(_str_leadingZero)] | umSSDRLeadingZero);
umSSDRDisplayMask = top[FPSTR(_str_displayMask)] | umSSDRDisplayMask; umSSDRDisplayMask = top[FPSTR(_str_displayMask)] | umSSDRDisplayMask;
umSSDRHours = top[FPSTR(_str_hours)] | umSSDRHours; umSSDRHours = top[FPSTR(_str_hours)] | umSSDRHours;
@ -546,6 +557,7 @@ const char UsermodSSDR::_str_name[] PROGMEM = "UsermodSSDR";
const char UsermodSSDR::_str_timeEnabled[] PROGMEM = "enabled"; const char UsermodSSDR::_str_timeEnabled[] PROGMEM = "enabled";
const char UsermodSSDR::_str_inverted[] PROGMEM = "inverted"; const char UsermodSSDR::_str_inverted[] PROGMEM = "inverted";
const char UsermodSSDR::_str_colonblink[] PROGMEM = "Colon-blinking"; const char UsermodSSDR::_str_colonblink[] PROGMEM = "Colon-blinking";
const char UsermodSSDR::_str_leadingZero[] PROGMEM = "Leading-Zero";
const char UsermodSSDR::_str_displayMask[] PROGMEM = "Display-Mask"; const char UsermodSSDR::_str_displayMask[] PROGMEM = "Display-Mask";
const char UsermodSSDR::_str_hours[] PROGMEM = "LED-Numbers-Hours"; const char UsermodSSDR::_str_hours[] PROGMEM = "LED-Numbers-Hours";
const char UsermodSSDR::_str_minutes[] PROGMEM = "LED-Numbers-Minutes"; const char UsermodSSDR::_str_minutes[] PROGMEM = "LED-Numbers-Minutes";

View File

@ -177,11 +177,11 @@ uint16_t color_wipe(bool rev, bool useRandomColors) {
SEGENV.step = 3; SEGENV.step = 3;
} }
if (SEGENV.step == 1) { //if flag set, change to new random color if (SEGENV.step == 1) { //if flag set, change to new random color
SEGENV.aux1 = SEGMENT.get_random_wheel_index(SEGENV.aux0); SEGENV.aux1 = get_random_wheel_index(SEGENV.aux0);
SEGENV.step = 2; SEGENV.step = 2;
} }
if (SEGENV.step == 3) { if (SEGENV.step == 3) {
SEGENV.aux0 = SEGMENT.get_random_wheel_index(SEGENV.aux1); SEGENV.aux0 = get_random_wheel_index(SEGENV.aux1);
SEGENV.step = 0; SEGENV.step = 0;
} }
} }
@ -271,7 +271,7 @@ uint16_t mode_random_color(void) {
if (it != SEGENV.step) //new color if (it != SEGENV.step) //new color
{ {
SEGENV.aux1 = SEGENV.aux0; SEGENV.aux1 = SEGENV.aux0;
SEGENV.aux0 = SEGMENT.get_random_wheel_index(SEGENV.aux0); //aux0 will store our random color wheel index SEGENV.aux0 = get_random_wheel_index(SEGENV.aux0); //aux0 will store our random color wheel index
SEGENV.step = it; SEGENV.step = it;
} }
@ -816,7 +816,7 @@ uint16_t chase(uint32_t color1, uint32_t color2, uint32_t color3, bool do_palett
if (a < SEGENV.step) //we hit the start again, choose new color for Chase random if (a < SEGENV.step) //we hit the start again, choose new color for Chase random
{ {
SEGENV.aux1 = SEGENV.aux0; //store previous random color SEGENV.aux1 = SEGENV.aux0; //store previous random color
SEGENV.aux0 = SEGMENT.get_random_wheel_index(SEGENV.aux0); SEGENV.aux0 = get_random_wheel_index(SEGENV.aux0);
} }
color1 = SEGMENT.color_wheel(SEGENV.aux0); color1 = SEGMENT.color_wheel(SEGENV.aux0);
} }
@ -1056,7 +1056,7 @@ uint16_t mode_chase_flash_random(void) {
SEGENV.aux1 = (SEGENV.aux1 + 1) % SEGLEN; SEGENV.aux1 = (SEGENV.aux1 + 1) % SEGLEN;
if (SEGENV.aux1 == 0) { if (SEGENV.aux1 == 0) {
SEGENV.aux0 = SEGMENT.get_random_wheel_index(SEGENV.aux0); SEGENV.aux0 = get_random_wheel_index(SEGENV.aux0);
} }
} }
return delay; return delay;
@ -2590,14 +2590,14 @@ uint16_t mode_twinklefox()
{ {
return twinklefox_base(false); return twinklefox_base(false);
} }
static const char _data_FX_MODE_TWINKLEFOX[] PROGMEM = "Twinklefox@!,Twinkle rate,,,,Cool;;!"; static const char _data_FX_MODE_TWINKLEFOX[] PROGMEM = "Twinklefox@!,Twinkle rate,,,,Cool;!,!;!";
uint16_t mode_twinklecat() uint16_t mode_twinklecat()
{ {
return twinklefox_base(true); return twinklefox_base(true);
} }
static const char _data_FX_MODE_TWINKLECAT[] PROGMEM = "Twinklecat@!,Twinkle rate,,,,Cool;;!"; static const char _data_FX_MODE_TWINKLECAT[] PROGMEM = "Twinklecat@!,Twinkle rate,,,,Cool;!,!;!";
//inspired by https://www.tweaking4all.com/hardware/arduino/adruino-led-strip-effects/#LEDStripEffectBlinkingHalloweenEyes //inspired by https://www.tweaking4all.com/hardware/arduino/adruino-led-strip-effects/#LEDStripEffectBlinkingHalloweenEyes
@ -5946,6 +5946,8 @@ uint16_t mode_2Dscrollingtext(void) {
else if (!strncmp_P(text,PSTR("#MMDD"),5)) sprintf_P(text, zero?PSTR("%02d/%02d") :PSTR("%d/%d"), month(localTime), day(localTime)); else if (!strncmp_P(text,PSTR("#MMDD"),5)) sprintf_P(text, zero?PSTR("%02d/%02d") :PSTR("%d/%d"), month(localTime), day(localTime));
else if (!strncmp_P(text,PSTR("#TIME"),5)) sprintf_P(text, zero?PSTR("%02d:%02d%s") :PSTR("%2d:%02d%s"), AmPmHour, minute(localTime), sec); else if (!strncmp_P(text,PSTR("#TIME"),5)) sprintf_P(text, zero?PSTR("%02d:%02d%s") :PSTR("%2d:%02d%s"), AmPmHour, minute(localTime), sec);
else if (!strncmp_P(text,PSTR("#HHMM"),5)) sprintf_P(text, zero?PSTR("%02d:%02d") :PSTR("%d:%02d"), AmPmHour, minute(localTime)); else if (!strncmp_P(text,PSTR("#HHMM"),5)) sprintf_P(text, zero?PSTR("%02d:%02d") :PSTR("%d:%02d"), AmPmHour, minute(localTime));
else if (!strncmp_P(text,PSTR("#HH"),3)) sprintf_P(text, zero?PSTR("%02d") :PSTR("%d"), AmPmHour);
else if (!strncmp_P(text,PSTR("#MM"),3)) sprintf_P(text, zero?PSTR("%02d") :PSTR("%d"), minute(localTime));
} }
const int numberOfLetters = strlen(text); const int numberOfLetters = strlen(text);

View File

@ -109,20 +109,15 @@
#define PINK (uint32_t)0xFF1493 #define PINK (uint32_t)0xFF1493
#define ULTRAWHITE (uint32_t)0xFFFFFFFF #define ULTRAWHITE (uint32_t)0xFFFFFFFF
#define DARKSLATEGRAY (uint32_t)0x2F4F4F #define DARKSLATEGRAY (uint32_t)0x2F4F4F
#define DARKSLATEGREY (uint32_t)0x2F4F4F #define DARKSLATEGREY DARKSLATEGRAY
// options // segment options
// bit 7: segment is in transition mode
// bits 4-6: TBD
// bit 3: mirror effect within segment
// bit 2: segment is on
// bit 1: reverse segment
// bit 0: segment is selected
#define NO_OPTIONS (uint16_t)0x0000 #define NO_OPTIONS (uint16_t)0x0000
#define TRANSPOSED (uint16_t)0x0400 // rotated 90deg & reversed #define TRANSPOSED (uint16_t)0x0100 // rotated 90deg & reversed
#define REVERSE_Y_2D (uint16_t)0x0200 #define MIRROR_Y_2D (uint16_t)0x0080
#define MIRROR_Y_2D (uint16_t)0x0100 #define REVERSE_Y_2D (uint16_t)0x0040
#define TRANSITIONAL (uint16_t)0x0080 #define RESET_REQ (uint16_t)0x0020
#define FROZEN (uint16_t)0x0010
#define MIRROR (uint16_t)0x0008 #define MIRROR (uint16_t)0x0008
#define SEGMENT_ON (uint16_t)0x0004 #define SEGMENT_ON (uint16_t)0x0004
#define REVERSE (uint16_t)0x0002 #define REVERSE (uint16_t)0x0002
@ -348,12 +343,11 @@ typedef struct Segment {
bool mirror : 1; // 3 : mirrored bool mirror : 1; // 3 : mirrored
bool freeze : 1; // 4 : paused/frozen bool freeze : 1; // 4 : paused/frozen
bool reset : 1; // 5 : indicates that Segment runtime requires reset bool reset : 1; // 5 : indicates that Segment runtime requires reset
bool transitional: 1; // 6 : transitional (there is transition occuring) bool reverse_y : 1; // 6 : reversed Y (2D)
bool reverse_y : 1; // 7 : reversed Y (2D) bool mirror_y : 1; // 7 : mirrored Y (2D)
bool mirror_y : 1; // 8 : mirrored Y (2D) bool transpose : 1; // 8 : transposed (2D, swapped X & Y)
bool transpose : 1; // 9 : transposed (2D, swapped X & Y) uint8_t map1D2D : 3; // 9-11 : mapping for 1D effect on 2D (0-use as strip, 1-expand vertically, 2-circular/arc, 3-rectangular/corner, ...)
uint8_t map1D2D : 3; // 10-12 : mapping for 1D effect on 2D (0-use as strip, 1-expand vertically, 2-circular/arc, 3-rectangular/corner, ...) uint8_t soundSim : 2; // 12-13 : 0-3 sound simulation types ("soft" & "hard" or "on"/"off")
uint8_t soundSim : 1; // 13 : 0-1 sound simulation types ("soft" & "hard" or "on"/"off")
uint8_t set : 2; // 14-15 : 0-3 UI segment sets/groups uint8_t set : 2; // 14-15 : 0-3 UI segment sets/groups
}; };
}; };
@ -484,7 +478,6 @@ typedef struct Segment {
_dataLen(0), _dataLen(0),
_t(nullptr) _t(nullptr)
{ {
//refreshLightCapabilities();
#ifdef WLED_DEBUG #ifdef WLED_DEBUG
//Serial.printf("-- Creating segment: %p\n", this); //Serial.printf("-- Creating segment: %p\n", this);
#endif #endif
@ -519,6 +512,7 @@ typedef struct Segment {
inline bool getOption(uint8_t n) const { return ((options >> n) & 0x01); } inline bool getOption(uint8_t n) const { return ((options >> n) & 0x01); }
inline bool isSelected(void) const { return selected; } inline bool isSelected(void) const { return selected; }
inline bool isInTransition(void) const { return _t != nullptr; }
inline bool isActive(void) const { return stop > start; } inline bool isActive(void) const { return stop > start; }
inline bool is2D(void) const { return (width()>1 && height()>1); } inline bool is2D(void) const { return (width()>1 && height()>1); }
inline bool hasRGB(void) const { return _isRGB; } inline bool hasRGB(void) const { return _isRGB; }
@ -569,15 +563,16 @@ typedef struct Segment {
void restoreSegenv(tmpsegd_t &tmpSegD); void restoreSegenv(tmpsegd_t &tmpSegD);
#endif #endif
uint16_t progress(void); //transition progression between 0-65535 uint16_t progress(void); //transition progression between 0-65535
uint8_t currentBri(uint8_t briNew, bool useCct = false); uint8_t currentBri(bool useCct = false);
uint8_t currentMode(uint8_t modeNew); uint8_t currentMode(void);
uint32_t currentColor(uint8_t slot, uint32_t colorNew); uint32_t currentColor(uint8_t slot);
CRGBPalette16 &loadPalette(CRGBPalette16 &tgt, uint8_t pal); CRGBPalette16 &loadPalette(CRGBPalette16 &tgt, uint8_t pal);
CRGBPalette16 &currentPalette(CRGBPalette16 &tgt, uint8_t paletteID); CRGBPalette16 &currentPalette(CRGBPalette16 &tgt, uint8_t paletteID);
// 1D strip // 1D strip
uint16_t virtualLength(void) const; uint16_t virtualLength(void) const;
void setPixelColor(int n, uint32_t c); // set relative pixel within segment with color void setPixelColor(int n, uint32_t c); // set relative pixel within segment with color
void setPixelColor(unsigned n, uint32_t c) { setPixelColor(int(n), c); }
void setPixelColor(int n, byte r, byte g, byte b, byte w = 0) { setPixelColor(n, RGBW32(r,g,b,w)); } // automatically inline void setPixelColor(int n, byte r, byte g, byte b, byte w = 0) { setPixelColor(n, RGBW32(r,g,b,w)); } // automatically inline
void setPixelColor(int n, CRGB c) { setPixelColor(n, RGBW32(c.r,c.g,c.b,0)); } // automatically inline void setPixelColor(int n, CRGB c) { setPixelColor(n, RGBW32(c.r,c.g,c.b,0)); } // automatically inline
void setPixelColor(float i, uint32_t c, bool aa = true); void setPixelColor(float i, uint32_t c, bool aa = true);
@ -595,7 +590,6 @@ typedef struct Segment {
void addPixelColor(int n, byte r, byte g, byte b, byte w = 0, bool fast = false) { addPixelColor(n, RGBW32(r,g,b,w), fast); } // automatically inline void addPixelColor(int n, byte r, byte g, byte b, byte w = 0, bool fast = false) { addPixelColor(n, RGBW32(r,g,b,w), fast); } // automatically inline
void addPixelColor(int n, CRGB c, bool fast = false) { addPixelColor(n, RGBW32(c.r,c.g,c.b,0), fast); } // automatically inline void addPixelColor(int n, CRGB c, bool fast = false) { addPixelColor(n, RGBW32(c.r,c.g,c.b,0), fast); } // automatically inline
void fadePixelColor(uint16_t n, uint8_t fade); void fadePixelColor(uint16_t n, uint8_t fade);
uint8_t get_random_wheel_index(uint8_t pos);
uint32_t color_from_palette(uint16_t, bool mapping, bool wrap, uint8_t mcol, uint8_t pbri = 255); uint32_t color_from_palette(uint16_t, bool mapping, bool wrap, uint8_t mcol, uint8_t pbri = 255);
uint32_t color_wheel(uint8_t pos); uint32_t color_wheel(uint8_t pos);
@ -606,6 +600,7 @@ typedef struct Segment {
#ifndef WLED_DISABLE_2D #ifndef WLED_DISABLE_2D
uint16_t XY(uint16_t x, uint16_t y); // support function to get relative index within segment uint16_t XY(uint16_t x, uint16_t y); // support function to get relative index within segment
void setPixelColorXY(int x, int y, uint32_t c); // set relative pixel within segment with color void setPixelColorXY(int x, int y, uint32_t c); // set relative pixel within segment with color
void setPixelColorXY(unsigned x, unsigned y, uint32_t c) { setPixelColorXY(int(x), int(y), c); }
void setPixelColorXY(int x, int y, byte r, byte g, byte b, byte w = 0) { setPixelColorXY(x, y, RGBW32(r,g,b,w)); } // automatically inline void setPixelColorXY(int x, int y, byte r, byte g, byte b, byte w = 0) { setPixelColorXY(x, y, RGBW32(r,g,b,w)); } // automatically inline
void setPixelColorXY(int x, int y, CRGB c) { setPixelColorXY(x, y, RGBW32(c.r,c.g,c.b,0)); } // automatically inline void setPixelColorXY(int x, int y, CRGB c) { setPixelColorXY(x, y, RGBW32(c.r,c.g,c.b,0)); } // automatically inline
void setPixelColorXY(float x, float y, uint32_t c, bool aa = true); void setPixelColorXY(float x, float y, uint32_t c, bool aa = true);
@ -881,16 +876,14 @@ class WS2812FX { // 96 bytes
std::vector<Panel> panel; std::vector<Panel> panel;
#endif #endif
void void setUpMatrix();
setUpMatrix(),
setPixelColorXY(int x, int y, uint32_t c);
// outsmart the compiler :) by correctly overloading // outsmart the compiler :) by correctly overloading
inline void setPixelColorXY(int x, int y, byte r, byte g, byte b, byte w = 0) { setPixelColorXY(x, y, RGBW32(r,g,b,w)); } // automatically inline inline void setPixelColorXY(int x, int y, uint32_t c) { setPixelColor(y * Segment::maxWidth + x, c); }
inline void setPixelColorXY(int x, int y, byte r, byte g, byte b, byte w = 0) { setPixelColorXY(x, y, RGBW32(r,g,b,w)); }
inline void setPixelColorXY(int x, int y, CRGB c) { setPixelColorXY(x, y, RGBW32(c.r,c.g,c.b,0)); } inline void setPixelColorXY(int x, int y, CRGB c) { setPixelColorXY(x, y, RGBW32(c.r,c.g,c.b,0)); }
uint32_t inline uint32_t getPixelColorXY(uint16_t x, uint16_t y) { return getPixelColor(isMatrix ? y * Segment::maxWidth + x : x);}
getPixelColorXY(uint16_t, uint16_t);
// end 2D support // end 2D support

View File

@ -134,7 +134,7 @@ void WS2812FX::setUpMatrix() {
#ifdef WLED_DEBUG #ifdef WLED_DEBUG
DEBUG_PRINT(F("Matrix ledmap:")); DEBUG_PRINT(F("Matrix ledmap:"));
for (uint16_t i=0; i<customMappingSize; i++) { for (unsigned i=0; i<customMappingSize; i++) {
if (!(i%Segment::maxWidth)) DEBUG_PRINTLN(); if (!(i%Segment::maxWidth)) DEBUG_PRINTLN();
DEBUG_PRINTF("%4d,", customMappingTable[i]); DEBUG_PRINTF("%4d,", customMappingTable[i]);
} }
@ -155,31 +155,6 @@ void WS2812FX::setUpMatrix() {
#endif #endif
} }
// absolute matrix version of setPixelColor()
void /*IRAM_ATTR*/ WS2812FX::setPixelColorXY(int x, int y, uint32_t col)
{
#ifndef WLED_DISABLE_2D
if (!isMatrix) return; // not a matrix set-up
uint16_t index = y * Segment::maxWidth + x;
#else
uint16_t index = x;
#endif
if (index < customMappingSize) index = customMappingTable[index];
if (index >= _length) return;
busses.setPixelColor(index, col);
}
// returns RGBW values of pixel
uint32_t WS2812FX::getPixelColorXY(uint16_t x, uint16_t y) {
#ifndef WLED_DISABLE_2D
uint16_t index = (y * Segment::maxWidth + x);
#else
uint16_t index = x;
#endif
if (index < customMappingSize) index = customMappingTable[index];
if (index >= _length) return 0;
return busses.getPixelColor(index);
}
/////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////
// Segment:: routines // Segment:: routines
@ -188,18 +163,19 @@ uint32_t WS2812FX::getPixelColorXY(uint16_t x, uint16_t y) {
#ifndef WLED_DISABLE_2D #ifndef WLED_DISABLE_2D
// XY(x,y) - gets pixel index within current segment (often used to reference leds[] array element) // XY(x,y) - gets pixel index within current segment (often used to reference leds[] array element)
uint16_t /*IRAM_ATTR*/ Segment::XY(uint16_t x, uint16_t y) { uint16_t IRAM_ATTR Segment::XY(uint16_t x, uint16_t y)
{
uint16_t width = virtualWidth(); // segment width in logical pixels (can be 0 if segment is inactive) uint16_t width = virtualWidth(); // segment width in logical pixels (can be 0 if segment is inactive)
uint16_t height = virtualHeight(); // segment height in logical pixels (is always >= 1) uint16_t height = virtualHeight(); // segment height in logical pixels (is always >= 1)
return isActive() ? (x%width) + (y%height) * width : 0; return isActive() ? (x%width) + (y%height) * width : 0;
} }
void /*IRAM_ATTR*/ Segment::setPixelColorXY(int x, int y, uint32_t col) void IRAM_ATTR Segment::setPixelColorXY(int x, int y, uint32_t col)
{ {
if (!isActive()) return; // not active if (!isActive()) return; // not active
if (x >= virtualWidth() || y >= virtualHeight() || x<0 || y<0) return; // if pixel would fall out of virtual segment just exit if (x >= virtualWidth() || y >= virtualHeight() || x<0 || y<0) return; // if pixel would fall out of virtual segment just exit
uint8_t _bri_t = currentBri(on ? opacity : 0); uint8_t _bri_t = currentBri();
if (_bri_t < 255) { if (_bri_t < 255) {
byte r = scale8(R(col), _bri_t); byte r = scale8(R(col), _bri_t);
byte g = scale8(G(col), _bri_t); byte g = scale8(G(col), _bri_t);
@ -310,32 +286,17 @@ void Segment::blendPixelColorXY(uint16_t x, uint16_t y, uint32_t color, uint8_t
void Segment::addPixelColorXY(int x, int y, uint32_t color, bool fast) { void Segment::addPixelColorXY(int x, int y, uint32_t color, bool fast) {
if (!isActive()) return; // not active if (!isActive()) return; // not active
if (x >= virtualWidth() || y >= virtualHeight() || x<0 || y<0) return; // if pixel would fall out of virtual segment just exit if (x >= virtualWidth() || y >= virtualHeight() || x<0 || y<0) return; // if pixel would fall out of virtual segment just exit
uint32_t col = getPixelColorXY(x,y); setPixelColorXY(x, y, color_add(getPixelColorXY(x,y), color, fast));
uint8_t r = R(col);
uint8_t g = G(col);
uint8_t b = B(col);
uint8_t w = W(col);
if (fast) {
r = qadd8(r, R(color));
g = qadd8(g, G(color));
b = qadd8(b, B(color));
w = qadd8(w, W(color));
col = RGBW32(r,g,b,w);
} else {
col = color_add(col, color);
}
setPixelColorXY(x, y, col);
} }
void Segment::fadePixelColorXY(uint16_t x, uint16_t y, uint8_t fade) { void Segment::fadePixelColorXY(uint16_t x, uint16_t y, uint8_t fade) {
if (!isActive()) return; // not active if (!isActive()) return; // not active
CRGB pix = CRGB(getPixelColorXY(x,y)).nscale8_video(fade); setPixelColorXY(x, y, color_fade(getPixelColorXY(x,y), fade, true));
setPixelColorXY(x, y, pix);
} }
// blurRow: perform a blur on a row of a rectangular matrix // blurRow: perform a blur on a row of a rectangular matrix
void Segment::blurRow(uint16_t row, fract8 blur_amount) { void Segment::blurRow(uint16_t row, fract8 blur_amount) {
if (!isActive()) return; // not active if (!isActive() || blur_amount == 0) return; // not active
const uint_fast16_t cols = virtualWidth(); const uint_fast16_t cols = virtualWidth();
const uint_fast16_t rows = virtualHeight(); const uint_fast16_t rows = virtualHeight();
@ -344,7 +305,7 @@ void Segment::blurRow(uint16_t row, fract8 blur_amount) {
uint8_t keep = 255 - blur_amount; uint8_t keep = 255 - blur_amount;
uint8_t seep = blur_amount >> 1; uint8_t seep = blur_amount >> 1;
CRGB carryover = CRGB::Black; CRGB carryover = CRGB::Black;
for (uint_fast16_t x = 0; x < cols; x++) { for (unsigned x = 0; x < cols; x++) {
CRGB cur = getPixelColorXY(x, row); CRGB cur = getPixelColorXY(x, row);
CRGB before = cur; // remember color before blur CRGB before = cur; // remember color before blur
CRGB part = cur; CRGB part = cur;
@ -363,7 +324,7 @@ void Segment::blurRow(uint16_t row, fract8 blur_amount) {
// blurCol: perform a blur on a column of a rectangular matrix // blurCol: perform a blur on a column of a rectangular matrix
void Segment::blurCol(uint16_t col, fract8 blur_amount) { void Segment::blurCol(uint16_t col, fract8 blur_amount) {
if (!isActive()) return; // not active if (!isActive() || blur_amount == 0) return; // not active
const uint_fast16_t cols = virtualWidth(); const uint_fast16_t cols = virtualWidth();
const uint_fast16_t rows = virtualHeight(); const uint_fast16_t rows = virtualHeight();
@ -372,7 +333,7 @@ void Segment::blurCol(uint16_t col, fract8 blur_amount) {
uint8_t keep = 255 - blur_amount; uint8_t keep = 255 - blur_amount;
uint8_t seep = blur_amount >> 1; uint8_t seep = blur_amount >> 1;
CRGB carryover = CRGB::Black; CRGB carryover = CRGB::Black;
for (uint_fast16_t y = 0; y < rows; y++) { for (unsigned y = 0; y < rows; y++) {
CRGB cur = getPixelColorXY(col, y); CRGB cur = getPixelColorXY(col, y);
CRGB part = cur; CRGB part = cur;
CRGB before = cur; // remember color before blur CRGB before = cur; // remember color before blur
@ -391,7 +352,7 @@ void Segment::blurCol(uint16_t col, fract8 blur_amount) {
// 1D Box blur (with added weight - blur_amount: [0=no blur, 255=max blur]) // 1D Box blur (with added weight - blur_amount: [0=no blur, 255=max blur])
void Segment::box_blur(uint16_t i, bool vertical, fract8 blur_amount) { void Segment::box_blur(uint16_t i, bool vertical, fract8 blur_amount) {
if (!isActive()) return; // not active if (!isActive() || blur_amount == 0) return; // not active
const uint16_t cols = virtualWidth(); const uint16_t cols = virtualWidth();
const uint16_t rows = virtualHeight(); const uint16_t rows = virtualHeight();
const uint16_t dim1 = vertical ? rows : cols; const uint16_t dim1 = vertical ? rows : cols;
@ -401,7 +362,7 @@ void Segment::box_blur(uint16_t i, bool vertical, fract8 blur_amount) {
const float keep = 3.f - 2.f*seep; const float keep = 3.f - 2.f*seep;
// 1D box blur // 1D box blur
CRGB tmp[dim1]; CRGB tmp[dim1];
for (uint16_t j = 0; j < dim1; j++) { for (int j = 0; j < dim1; j++) {
uint16_t x = vertical ? i : j; uint16_t x = vertical ? i : j;
uint16_t y = vertical ? j : i; uint16_t y = vertical ? j : i;
int16_t xp = vertical ? x : x-1; // "signed" to prevent underflow int16_t xp = vertical ? x : x-1; // "signed" to prevent underflow
@ -417,7 +378,7 @@ void Segment::box_blur(uint16_t i, bool vertical, fract8 blur_amount) {
b = (curr.b*keep + (prev.b + next.b)*seep) / 3; b = (curr.b*keep + (prev.b + next.b)*seep) / 3;
tmp[j] = CRGB(r,g,b); tmp[j] = CRGB(r,g,b);
} }
for (uint16_t j = 0; j < dim1; j++) { for (int j = 0; j < dim1; j++) {
uint16_t x = vertical ? i : j; uint16_t x = vertical ? i : j;
uint16_t y = vertical ? j : i; uint16_t y = vertical ? j : i;
setPixelColorXY(x, y, tmp[j]); setPixelColorXY(x, y, tmp[j]);
@ -440,7 +401,7 @@ void Segment::box_blur(uint16_t i, bool vertical, fract8 blur_amount) {
void Segment::blur1d(fract8 blur_amount) { void Segment::blur1d(fract8 blur_amount) {
const uint16_t rows = virtualHeight(); const uint16_t rows = virtualHeight();
for (uint16_t y = 0; y < rows; y++) blurRow(y, blur_amount); for (unsigned y = 0; y < rows; y++) blurRow(y, blur_amount);
} }
void Segment::moveX(int8_t delta, bool wrap) { void Segment::moveX(int8_t delta, bool wrap) {
@ -498,7 +459,7 @@ void Segment::move(uint8_t dir, uint8_t delta, bool wrap) {
} }
void Segment::draw_circle(uint16_t cx, uint16_t cy, uint8_t radius, CRGB col) { void Segment::draw_circle(uint16_t cx, uint16_t cy, uint8_t radius, CRGB col) {
if (!isActive()) return; // not active if (!isActive() || radius == 0) return; // not active
// Bresenhams Algorithm // Bresenhams Algorithm
int d = 3 - (2*radius); int d = 3 - (2*radius);
int y = radius, x = 0; int y = radius, x = 0;
@ -523,7 +484,7 @@ void Segment::draw_circle(uint16_t cx, uint16_t cy, uint8_t radius, CRGB col) {
// by stepko, taken from https://editor.soulmatelights.com/gallery/573-blobs // by stepko, taken from https://editor.soulmatelights.com/gallery/573-blobs
void Segment::fill_circle(uint16_t cx, uint16_t cy, uint8_t radius, CRGB col) { void Segment::fill_circle(uint16_t cx, uint16_t cy, uint8_t radius, CRGB col) {
if (!isActive()) return; // not active if (!isActive() || radius == 0) return; // not active
const uint16_t cols = virtualWidth(); const uint16_t cols = virtualWidth();
const uint16_t rows = virtualHeight(); const uint16_t rows = virtualHeight();
for (int16_t y = -radius; y <= radius; y++) { for (int16_t y = -radius; y <= radius; y++) {
@ -540,7 +501,7 @@ void Segment::nscale8(uint8_t scale) {
if (!isActive()) return; // not active if (!isActive()) return; // not active
const uint16_t cols = virtualWidth(); const uint16_t cols = virtualWidth();
const uint16_t rows = virtualHeight(); const uint16_t rows = virtualHeight();
for(uint16_t y = 0; y < rows; y++) for (uint16_t x = 0; x < cols; x++) { for (int y = 0; y < rows; y++) for (int x = 0; x < cols; x++) {
setPixelColorXY(x, y, CRGB(getPixelColorXY(x, y)).nscale8(scale)); setPixelColorXY(x, y, CRGB(getPixelColorXY(x, y)).nscale8(scale));
} }
} }

View File

@ -89,25 +89,19 @@ bool Segment::_modeBlend = false;
Segment::Segment(const Segment &orig) { Segment::Segment(const Segment &orig) {
//DEBUG_PRINTF("-- Copy segment constructor: %p -> %p\n", &orig, this); //DEBUG_PRINTF("-- Copy segment constructor: %p -> %p\n", &orig, this);
memcpy((void*)this, (void*)&orig, sizeof(Segment)); memcpy((void*)this, (void*)&orig, sizeof(Segment));
transitional = false; // copied segment cannot be in transition _t = nullptr; // copied segment cannot be in transition
name = nullptr; if (orig.name) { name = new char[strlen(orig.name)+1]; if (name) strcpy(name, orig.name); } else { name = nullptr; }
data = nullptr; if (orig.data) { if (allocateData(orig._dataLen)) memcpy(data, orig.data, orig._dataLen); } else { data = nullptr; _dataLen = 0; }
_dataLen = 0;
_t = nullptr;
if (orig.name) { name = new char[strlen(orig.name)+1]; if (name) strcpy(name, orig.name); }
if (orig.data) { if (allocateData(orig._dataLen)) memcpy(data, orig.data, orig._dataLen); }
//if (orig._t) { _t = new Transition(orig._t->_dur); }
} }
// move constructor // move constructor
Segment::Segment(Segment &&orig) noexcept { Segment::Segment(Segment &&orig) noexcept {
//DEBUG_PRINTF("-- Move segment constructor: %p -> %p\n", &orig, this); //DEBUG_PRINTF("-- Move segment constructor: %p -> %p\n", &orig, this);
memcpy((void*)this, (void*)&orig, sizeof(Segment)); memcpy((void*)this, (void*)&orig, sizeof(Segment));
orig.transitional = false; // old segment cannot be in transition any more
orig.name = nullptr; orig.name = nullptr;
orig.data = nullptr; orig.data = nullptr;
orig._dataLen = 0; orig._dataLen = 0;
orig._t = nullptr; orig._t = nullptr; // old segment cannot be in transition any more
} }
// copy assignment // copy assignment
@ -115,27 +109,23 @@ Segment& Segment::operator= (const Segment &orig) {
//DEBUG_PRINTF("-- Copying segment: %p -> %p\n", &orig, this); //DEBUG_PRINTF("-- Copying segment: %p -> %p\n", &orig, this);
if (this != &orig) { if (this != &orig) {
// clean destination // clean destination
transitional = false; // copied segment cannot be in transition if (name) { delete[] name; name = nullptr; }
if (name) delete[] name; if (orig.name) { name = new char[strlen(orig.name)+1]; if (name) strcpy(name, orig.name); }
if (_t) { if (_t) {
#ifndef WLED_DISABLE_MODE_BLEND #ifndef WLED_DISABLE_MODE_BLEND
if (_t->_segT._dataT) free(_t->_segT._dataT); if (_t->_segT._dataT) free(_t->_segT._dataT);
#endif #endif
delete _t; delete _t;
_t = nullptr; // copied segment cannot be in transition
} }
deallocateData(); deallocateData();
// copy source // copy source
memcpy((void*)this, (void*)&orig, sizeof(Segment)); memcpy((void*)this, (void*)&orig, sizeof(Segment));
transitional = false;
// erase pointers to allocated data // erase pointers to allocated data
name = nullptr;
data = nullptr; data = nullptr;
_dataLen = 0; _dataLen = 0;
_t = nullptr;
// copy source data // copy source data
if (orig.name) { name = new char[strlen(orig.name)+1]; if (name) strcpy(name, orig.name); }
if (orig.data) { if (allocateData(orig._dataLen)) memcpy(data, orig.data, orig._dataLen); } if (orig.data) { if (allocateData(orig._dataLen)) memcpy(data, orig.data, orig._dataLen); }
//if (orig._t) { _t = new Transition(orig._t->_dur, orig._t->_briT, orig._t->_cctT, orig._t->_colorT); }
} }
return *this; return *this;
} }
@ -144,9 +134,7 @@ Segment& Segment::operator= (const Segment &orig) {
Segment& Segment::operator= (Segment &&orig) noexcept { Segment& Segment::operator= (Segment &&orig) noexcept {
//DEBUG_PRINTF("-- Moving segment: %p -> %p\n", &orig, this); //DEBUG_PRINTF("-- Moving segment: %p -> %p\n", &orig, this);
if (this != &orig) { if (this != &orig) {
transitional = false; // just temporary
if (name) { delete[] name; name = nullptr; } // free old name if (name) { delete[] name; name = nullptr; } // free old name
deallocateData(); // free old runtime data
if (_t) { if (_t) {
#ifndef WLED_DISABLE_MODE_BLEND #ifndef WLED_DISABLE_MODE_BLEND
if (_t->_segT._dataT) free(_t->_segT._dataT); if (_t->_segT._dataT) free(_t->_segT._dataT);
@ -154,12 +142,12 @@ Segment& Segment::operator= (Segment &&orig) noexcept {
delete _t; delete _t;
_t = nullptr; _t = nullptr;
} }
deallocateData(); // free old runtime data
memcpy((void*)this, (void*)&orig, sizeof(Segment)); memcpy((void*)this, (void*)&orig, sizeof(Segment));
orig.transitional = false; // old segment cannot be in transition
orig.name = nullptr; orig.name = nullptr;
orig.data = nullptr; orig.data = nullptr;
orig._dataLen = 0; orig._dataLen = 0;
orig._t = nullptr; orig._t = nullptr; // old segment cannot be in transition
} }
return *this; return *this;
} }
@ -237,7 +225,7 @@ CRGBPalette16 &Segment::loadPalette(CRGBPalette16 &targetPalette, uint8_t pal) {
switch (pal) { switch (pal) {
case 0: //default palette. Exceptions for specific effects above case 0: //default palette. Exceptions for specific effects above
targetPalette = PartyColors_p; break; targetPalette = PartyColors_p; break;
case 1: {//periodically replace palette with a random one. Transition palette change in 500ms case 1: {//periodically replace palette with a random one
unsigned long timeSinceLastChange = millis() - _lastPaletteChange; unsigned long timeSinceLastChange = millis() - _lastPaletteChange;
if (timeSinceLastChange > randomPaletteChangeTime * 1000U) { if (timeSinceLastChange > randomPaletteChangeTime * 1000U) {
_randomPalette = _newRandomPalette; _randomPalette = _newRandomPalette;
@ -301,26 +289,24 @@ CRGBPalette16 &Segment::loadPalette(CRGBPalette16 &targetPalette, uint8_t pal) {
} }
void Segment::startTransition(uint16_t dur) { void Segment::startTransition(uint16_t dur) {
if (!dur) { if (dur == 0) {
if (_t) _t->_dur = dur; // this will stop transition in next handleTransisiton() if (isInTransition()) _t->_dur = dur; // this will stop transition in next handleTransisiton()
else transitional = false;
return; return;
} }
if (transitional && _t) return; // already in transition no need to store anything if (isInTransition()) return; // already in transition no need to store anything
// starting a transition has to occur before change so we get current values 1st // starting a transition has to occur before change so we get current values 1st
_t = new Transition(dur); // no previous transition running _t = new Transition(dur); // no previous transition running
if (!_t) return; // failed to allocate data if (!_t) return; // failed to allocate data
//DEBUG_PRINTF("-- Started transition: %p\n", this); //DEBUG_PRINTF("-- Started transition: %p\n", this);
CRGBPalette16 _palT = CRGBPalette16(DEFAULT_COLOR); loadPalette(_palT, palette); loadPalette(_t->_palT, palette);
_t->_palT = _palT;
_t->_briT = on ? opacity : 0; _t->_briT = on ? opacity : 0;
_t->_cctT = cct; _t->_cctT = cct;
#ifndef WLED_DISABLE_MODE_BLEND #ifndef WLED_DISABLE_MODE_BLEND
if (modeBlending) {
swapSegenv(_t->_segT); swapSegenv(_t->_segT);
_t->_modeT = mode; _t->_modeT = mode;
_t->_segT._optionsT |= 0b0000000001000000; // mark old segment transitional
_t->_segT._dataLenT = 0; _t->_segT._dataLenT = 0;
_t->_segT._dataT = nullptr; _t->_segT._dataT = nullptr;
if (_dataLen > 0 && data) { if (_dataLen > 0 && data) {
@ -331,17 +317,17 @@ void Segment::startTransition(uint16_t dur) {
_t->_segT._dataLenT = _dataLen; _t->_segT._dataLenT = _dataLen;
} }
} }
} else {
for (size_t i=0; i<NUM_COLORS; i++) _t->_segT._colorT[i] = colors[i];
}
#else #else
for (size_t i=0; i<NUM_COLORS; i++) _t->_colorT[i] = colors[i]; for (size_t i=0; i<NUM_COLORS; i++) _t->_colorT[i] = colors[i];
#endif #endif
transitional = true; // setOption(SEG_OPTION_TRANSITIONAL, true);
} }
void Segment::stopTransition() { void Segment::stopTransition() {
if (!transitional) return;
transitional = false; // finish transitioning segment
//DEBUG_PRINTF("-- Stopping transition: %p\n", this); //DEBUG_PRINTF("-- Stopping transition: %p\n", this);
if (_t) { if (isInTransition()) {
#ifndef WLED_DISABLE_MODE_BLEND #ifndef WLED_DISABLE_MODE_BLEND
if (_t->_segT._dataT && _t->_segT._dataLenT > 0) { if (_t->_segT._dataT && _t->_segT._dataLenT > 0) {
//DEBUG_PRINTF("-- Released duplicate data (%d): %p\n", _t->_segT._dataLenT, _t->_segT._dataT); //DEBUG_PRINTF("-- Released duplicate data (%d): %p\n", _t->_segT._dataLenT, _t->_segT._dataT);
@ -356,14 +342,13 @@ void Segment::stopTransition() {
} }
void Segment::handleTransition() { void Segment::handleTransition() {
if (!transitional) return;
uint16_t _progress = progress(); uint16_t _progress = progress();
if (_progress == 0xFFFFU) stopTransition(); if (_progress == 0xFFFFU) stopTransition();
} }
// transition progression between 0-65535 // transition progression between 0-65535
uint16_t Segment::progress() { uint16_t Segment::progress() {
if (transitional && _t) { if (isInTransition()) {
unsigned long timeNow = millis(); unsigned long timeNow = millis();
if (_t->_dur > 0 && timeNow - _t->_start < _t->_dur) return (timeNow - _t->_start) * 0xFFFFU / _t->_dur; if (_t->_dur > 0 && timeNow - _t->_start < _t->_dur) return (timeNow - _t->_start) * 0xFFFFU / _t->_dur;
} }
@ -420,8 +405,8 @@ void Segment::restoreSegenv(tmpsegd_t &tmpSeg) {
_t->_segT._stepT = step; _t->_segT._stepT = step;
_t->_segT._callT = call; _t->_segT._callT = call;
//if (_t->_segT._dataT != data) DEBUG_PRINTF("--- data re-allocated: (%p) %p -> %p\n", this, _t->_segT._dataT, data); //if (_t->_segT._dataT != data) DEBUG_PRINTF("--- data re-allocated: (%p) %p -> %p\n", this, _t->_segT._dataT, data);
_t->_segT._dataT = data; // sometimes memory gets re-allocated (!! INVESTIGATE WHY !!) _t->_segT._dataT = data;
_t->_segT._dataLenT = _dataLen; // sometimes memory gets re-allocated (!! INVESTIGATE WHY !!) _t->_segT._dataLenT = _dataLen;
} }
options = tmpSeg._optionsT; options = tmpSeg._optionsT;
for (size_t i=0; i<NUM_COLORS; i++) colors[i] = tmpSeg._colorT[i]; for (size_t i=0; i<NUM_COLORS; i++) colors[i] = tmpSeg._colorT[i];
@ -443,39 +428,40 @@ void Segment::restoreSegenv(tmpsegd_t &tmpSeg) {
} }
#endif #endif
uint8_t Segment::currentBri(uint8_t briNew, bool useCct) { uint8_t Segment::currentBri(bool useCct) {
uint32_t prog = progress(); uint32_t prog = progress();
if (prog < 0xFFFFU) { if (prog < 0xFFFFU) {
if (useCct) return ((briNew * prog) + _t->_cctT * (0xFFFFU - prog)) >> 16; uint32_t curBri = (useCct ? cct : (on ? opacity : 0)) * prog;
else return ((briNew * prog) + _t->_briT * (0xFFFFU - prog)) >> 16; curBri += (useCct ? _t->_cctT : (on ? _t->_briT : 0)) * (0xFFFFU - prog);
return curBri / 0xFFFFU;
} }
return briNew; return (useCct ? cct : (on ? opacity : 0));
} }
uint8_t Segment::currentMode(uint8_t newMode) { uint8_t Segment::currentMode() {
#ifndef WLED_DISABLE_MODE_BLEND #ifndef WLED_DISABLE_MODE_BLEND
uint16_t prog = progress(); // implicit check for transitional & _t in progress() uint16_t prog = progress();
if (prog < 0xFFFFU) return _t->_modeT; if (modeBlending && prog < 0xFFFFU) return _t->_modeT;
#endif #endif
return newMode; return mode;
} }
uint32_t Segment::currentColor(uint8_t slot, uint32_t colorNew) { uint32_t Segment::currentColor(uint8_t slot) {
#ifndef WLED_DISABLE_MODE_BLEND #ifndef WLED_DISABLE_MODE_BLEND
return transitional && _t ? color_blend(_t->_segT._colorT[slot], colorNew, progress(), true) : colorNew; return isInTransition() ? color_blend(_t->_segT._colorT[slot], colors[slot], progress(), true) : colors[slot];
#else #else
return transitional && _t ? color_blend(_t->_colorT[slot], colorNew, progress(), true) : colorNew; return isInTransition() ? color_blend(_t->_colorT[slot], colors[slot], progress(), true) : colors[slot];
#endif #endif
} }
CRGBPalette16 &Segment::currentPalette(CRGBPalette16 &targetPalette, uint8_t pal) { CRGBPalette16 &Segment::currentPalette(CRGBPalette16 &targetPalette, uint8_t pal) {
loadPalette(targetPalette, pal); loadPalette(targetPalette, pal);
if (progress() < 0xFFFFU) { uint16_t prog = progress();
if (strip.paletteFade && prog < 0xFFFFU) {
// blend palettes // blend palettes
// there are about 255 blend passes of 48 "blends" to completely blend two palettes (in _dur time) // there are about 255 blend passes of 48 "blends" to completely blend two palettes (in _dur time)
// minimum blend time is 100ms maximum is 65535ms // minimum blend time is 100ms maximum is 65535ms
unsigned long timeMS = millis() - _t->_start; uint16_t noOfBlends = ((255U * prog) / 0xFFFFU) - _t->_prevPaletteBlends;
uint16_t noOfBlends = (255U * timeMS / _t->_dur) - _t->_prevPaletteBlends;
for (int i=0; i<noOfBlends; i++, _t->_prevPaletteBlends++) nblendPaletteTowardPalette(_t->_palT, targetPalette, 48); for (int i=0; i<noOfBlends; i++, _t->_prevPaletteBlends++) nblendPaletteTowardPalette(_t->_palT, targetPalette, 48);
targetPalette = _t->_palT; // copy transitioning/temporary palette targetPalette = _t->_palT; // copy transitioning/temporary palette
} }
@ -500,6 +486,8 @@ void Segment::setUp(uint16_t i1, uint16_t i2, uint8_t grp, uint8_t spc, uint16_t
&& (!grp || (grouping == grp && spacing == spc)) && (!grp || (grouping == grp && spacing == spc))
&& (ofs == UINT16_MAX || ofs == offset)) return; && (ofs == UINT16_MAX || ofs == offset)) return;
stateChanged = true; // send UDP/WS broadcast
if (stop) fill(BLACK); // turn old segment range off (clears pixels if changing spacing) if (stop) fill(BLACK); // turn old segment range off (clears pixels if changing spacing)
if (grp) { // prevent assignment of 0 if (grp) { // prevent assignment of 0
grouping = grp; grouping = grp;
@ -510,6 +498,10 @@ void Segment::setUp(uint16_t i1, uint16_t i2, uint8_t grp, uint8_t spc, uint16_t
} }
if (ofs < UINT16_MAX) offset = ofs; if (ofs < UINT16_MAX) offset = ofs;
DEBUG_PRINT(F("setUp segment: ")); DEBUG_PRINT(i1);
DEBUG_PRINT(','); DEBUG_PRINT(i2);
DEBUG_PRINT(F(" -> ")); DEBUG_PRINT(i1Y);
DEBUG_PRINT(','); DEBUG_PRINTLN(i2Y);
markForReset(); markForReset();
if (boundsUnchanged) return; if (boundsUnchanged) return;
@ -564,7 +556,6 @@ void Segment::setCCT(uint16_t k) {
void Segment::setOpacity(uint8_t o) { void Segment::setOpacity(uint8_t o) {
if (opacity == o) return; if (opacity == o) return;
if (fadeTransition) startTransition(strip.getTransition()); // start transition prior to change if (fadeTransition) startTransition(strip.getTransition()); // start transition prior to change
DEBUG_PRINT(F("-- Setting opacity: ")); DEBUG_PRINTLN(o);
opacity = o; opacity = o;
stateChanged = true; // send UDP/WS broadcast stateChanged = true; // send UDP/WS broadcast
} }
@ -574,14 +565,16 @@ void Segment::setOption(uint8_t n, bool val) {
if (fadeTransition && n == SEG_OPTION_ON && val != prevOn) startTransition(strip.getTransition()); // start transition prior to change if (fadeTransition && n == SEG_OPTION_ON && val != prevOn) startTransition(strip.getTransition()); // start transition prior to change
if (val) options |= 0x01 << n; if (val) options |= 0x01 << n;
else options &= ~(0x01 << n); else options &= ~(0x01 << n);
if (!(n == SEG_OPTION_SELECTED || n == SEG_OPTION_RESET || n == SEG_OPTION_TRANSITIONAL)) stateChanged = true; // send UDP/WS broadcast if (!(n == SEG_OPTION_SELECTED || n == SEG_OPTION_RESET)) stateChanged = true; // send UDP/WS broadcast
} }
void Segment::setMode(uint8_t fx, bool loadDefaults) { void Segment::setMode(uint8_t fx, bool loadDefaults) {
// if we have a valid mode & is not reserved // if we have a valid mode & is not reserved
if (fx < strip.getModeCount() && strncmp_P("RSVD", strip.getModeData(fx), 4)) { if (fx < strip.getModeCount() && strncmp_P("RSVD", strip.getModeData(fx), 4)) {
if (fx != mode) { if (fx != mode) {
if (fadeTransition) startTransition(strip.getTransition()); // set effect transitions #ifndef WLED_DISABLE_MODE_BLEND
if (modeBlending) startTransition(strip.getTransition()); // set effect transitions
#endif
mode = fx; mode = fx;
// load default values from effect string // load default values from effect string
if (loadDefaults) { if (loadDefaults) {
@ -743,7 +736,7 @@ void IRAM_ATTR Segment::setPixelColor(int i, uint32_t col)
#endif #endif
uint16_t len = length(); uint16_t len = length();
uint8_t _bri_t = currentBri(on ? opacity : 0); uint8_t _bri_t = currentBri();
if (_bri_t < 255) { if (_bri_t < 255) {
byte r = scale8(R(col), _bri_t); byte r = scale8(R(col), _bri_t);
byte g = scale8(G(col), _bri_t); byte g = scale8(G(col), _bri_t);
@ -877,10 +870,11 @@ uint8_t Segment::differs(Segment& b) const {
if (startY != b.startY) d |= SEG_DIFFERS_BOUNDS; if (startY != b.startY) d |= SEG_DIFFERS_BOUNDS;
if (stopY != b.stopY) d |= SEG_DIFFERS_BOUNDS; if (stopY != b.stopY) d |= SEG_DIFFERS_BOUNDS;
//bit pattern: (msb first) set:2, sound:1, mapping:3, transposed, mirrorY, reverseY, [transitional, reset,] paused, mirrored, on, reverse, [selected] //bit pattern: (msb first)
if ((options & 0b1111111110011110U) != (b.options & 0b1111111110011110U)) d |= SEG_DIFFERS_OPT; // set:2, sound:2, mapping:3, transposed, mirrorY, reverseY, [reset,] paused, mirrored, on, reverse, [selected]
if ((options & 0b1111111111011110U) != (b.options & 0b1111111111011110U)) d |= SEG_DIFFERS_OPT;
if ((options & 0x0001U) != (b.options & 0x0001U)) d |= SEG_DIFFERS_SEL; if ((options & 0x0001U) != (b.options & 0x0001U)) d |= SEG_DIFFERS_SEL;
for (uint8_t i = 0; i < NUM_COLORS; i++) if (colors[i] != b.colors[i]) d |= SEG_DIFFERS_COL; for (unsigned i = 0; i < NUM_COLORS; i++) if (colors[i] != b.colors[i]) d |= SEG_DIFFERS_COL;
return d; return d;
} }
@ -912,7 +906,7 @@ void Segment::refreshLightCapabilities() {
segStopIdx = stop; segStopIdx = stop;
} }
for (uint8_t b = 0; b < busses.getNumBusses(); b++) { for (unsigned b = 0; b < busses.getNumBusses(); b++) {
Bus *bus = busses.getBus(b); Bus *bus = busses.getBus(b);
if (bus == nullptr || bus->getLength()==0) break; if (bus == nullptr || bus->getLength()==0) break;
if (!bus->isOk()) continue; if (!bus->isOk()) continue;
@ -942,7 +936,7 @@ void Segment::fill(uint32_t c) {
if (!isActive()) return; // not active if (!isActive()) return; // not active
const uint16_t cols = is2D() ? virtualWidth() : virtualLength(); const uint16_t cols = is2D() ? virtualWidth() : virtualLength();
const uint16_t rows = virtualHeight(); // will be 1 for 1D const uint16_t rows = virtualHeight(); // will be 1 for 1D
for(uint16_t y = 0; y < rows; y++) for (uint16_t x = 0; x < cols; x++) { for (int y = 0; y < rows; y++) for (int x = 0; x < cols; x++) {
if (is2D()) setPixelColorXY(x, y, c); if (is2D()) setPixelColorXY(x, y, c);
else setPixelColor(x, c); else setPixelColor(x, c);
} }
@ -956,27 +950,12 @@ void Segment::blendPixelColor(int n, uint32_t color, uint8_t blend) {
// Adds the specified color with the existing pixel color perserving color balance. // Adds the specified color with the existing pixel color perserving color balance.
void Segment::addPixelColor(int n, uint32_t color, bool fast) { void Segment::addPixelColor(int n, uint32_t color, bool fast) {
if (!isActive()) return; // not active if (!isActive()) return; // not active
uint32_t col = getPixelColor(n); setPixelColor(n, color_add(getPixelColor(n), color, fast));
uint8_t r = R(col);
uint8_t g = G(col);
uint8_t b = B(col);
uint8_t w = W(col);
if (fast) {
r = qadd8(r, R(color));
g = qadd8(g, G(color));
b = qadd8(b, B(color));
w = qadd8(w, W(color));
col = RGBW32(r,g,b,w);
} else {
col = color_add(col, color);
}
setPixelColor(n, col);
} }
void Segment::fadePixelColor(uint16_t n, uint8_t fade) { void Segment::fadePixelColor(uint16_t n, uint8_t fade) {
if (!isActive()) return; // not active if (!isActive()) return; // not active
CRGB pix = CRGB(getPixelColor(n)).nscale8_video(fade); setPixelColor(n, color_fade(getPixelColor(n), fade, true));
setPixelColor(n, pix);
} }
/* /*
@ -996,7 +975,7 @@ void Segment::fade_out(uint8_t rate) {
int g2 = G(color); int g2 = G(color);
int b2 = B(color); int b2 = B(color);
for (uint16_t y = 0; y < rows; y++) for (uint16_t x = 0; x < cols; x++) { for (int y = 0; y < rows; y++) for (int x = 0; x < cols; x++) {
color = is2D() ? getPixelColorXY(x, y) : getPixelColor(x); color = is2D() ? getPixelColorXY(x, y) : getPixelColor(x);
int w1 = W(color); int w1 = W(color);
int r1 = R(color); int r1 = R(color);
@ -1025,9 +1004,9 @@ void Segment::fadeToBlackBy(uint8_t fadeBy) {
const uint16_t cols = is2D() ? virtualWidth() : virtualLength(); const uint16_t cols = is2D() ? virtualWidth() : virtualLength();
const uint16_t rows = virtualHeight(); // will be 1 for 1D const uint16_t rows = virtualHeight(); // will be 1 for 1D
for (uint16_t y = 0; y < rows; y++) for (uint16_t x = 0; x < cols; x++) { for (int y = 0; y < rows; y++) for (int x = 0; x < cols; x++) {
if (is2D()) setPixelColorXY(x, y, CRGB(getPixelColorXY(x,y)).nscale8(255-fadeBy)); if (is2D()) setPixelColorXY(x, y, color_fade(getPixelColorXY(x,y), 255-fadeBy));
else setPixelColor(x, CRGB(getPixelColor(x)).nscale8(255-fadeBy)); else setPixelColor(x, color_fade(getPixelColor(x), 255-fadeBy));
} }
} }
@ -1040,34 +1019,26 @@ void Segment::blur(uint8_t blur_amount)
#ifndef WLED_DISABLE_2D #ifndef WLED_DISABLE_2D
if (is2D()) { if (is2D()) {
// compatibility with 2D // compatibility with 2D
const uint_fast16_t cols = virtualWidth(); const unsigned cols = virtualWidth();
const uint_fast16_t rows = virtualHeight(); const unsigned rows = virtualHeight();
for (uint_fast16_t i = 0; i < rows; i++) blurRow(i, blur_amount); // blur all rows for (unsigned i = 0; i < rows; i++) blurRow(i, blur_amount); // blur all rows
for (uint_fast16_t k = 0; k < cols; k++) blurCol(k, blur_amount); // blur all columns for (unsigned k = 0; k < cols; k++) blurCol(k, blur_amount); // blur all columns
return; return;
} }
#endif #endif
uint8_t keep = 255 - blur_amount; uint8_t keep = 255 - blur_amount;
uint8_t seep = blur_amount >> 1; uint8_t seep = blur_amount >> 1;
CRGB carryover = CRGB::Black; uint32_t carryover = BLACK;
uint_fast16_t vlength = virtualLength(); unsigned vlength = virtualLength();
for(uint_fast16_t i = 0; i < vlength; i++) for (unsigned i = 0; i < vlength; i++) {
{ uint32_t cur = getPixelColor(i);
CRGB cur = CRGB(getPixelColor(i)); uint32_t part = color_fade(cur, seep);
CRGB part = cur; cur = color_add(color_fade(cur, keep), carryover, true);
CRGB before = cur; // remember color before blur
part.nscale8(seep);
cur.nscale8(keep);
cur += carryover;
if (i > 0) { if (i > 0) {
uint32_t c = getPixelColor(i-1); uint32_t c = getPixelColor(i-1);
uint8_t r = R(c); setPixelColor(i-1, color_add(c, part, true));
uint8_t g = G(c);
uint8_t b = B(c);
setPixelColor((uint16_t)(i-1), qadd8(r, part.red), qadd8(g, part.green), qadd8(b, part.blue));
} }
if (before != cur) // optimization: only set pixel if color has changed setPixelColor(i, cur);
setPixelColor((uint16_t)i,cur.red, cur.green, cur.blue);
carryover = part; carryover = part;
} }
} }
@ -1091,21 +1062,6 @@ uint32_t Segment::color_wheel(uint8_t pos) {
} }
} }
/*
* Returns a new, random wheel index with a minimum distance of 42 from pos.
*/
uint8_t Segment::get_random_wheel_index(uint8_t pos) {
uint8_t r = 0, x = 0, y = 0, d = 0;
while(d < 42) {
r = random8();
x = abs(pos - r);
y = 255 - x;
d = MIN(x, y);
}
return r;
}
/* /*
* Gets a single color from the currently selected palette. * Gets a single color from the currently selected palette.
* @param i Palette Index (if mapping is true, the full palette will be _virtualSegmentLength long, if false, 255). Will wrap around automatically. * @param i Palette Index (if mapping is true, the full palette will be _virtualSegmentLength long, if false, 255). Will wrap around automatically.
@ -1119,20 +1075,20 @@ uint32_t Segment::color_from_palette(uint16_t i, bool mapping, bool wrap, uint8_
{ {
// default palette or no RGB support on segment // default palette or no RGB support on segment
if ((palette == 0 && mcol < NUM_COLORS) || !_isRGB) { if ((palette == 0 && mcol < NUM_COLORS) || !_isRGB) {
uint32_t color = currentColor(mcol, colors[mcol]); uint32_t color = currentColor(mcol);
color = gamma32(color); color = gamma32(color);
if (pbri == 255) return color; if (pbri == 255) return color;
return RGBW32(scale8_video(R(color),pbri), scale8_video(G(color),pbri), scale8_video(B(color),pbri), scale8_video(W(color),pbri)); return color_fade(color, pbri, true);
} }
uint8_t paletteIndex = i; uint8_t paletteIndex = i;
if (mapping && virtualLength() > 1) paletteIndex = (i*255)/(virtualLength() -1); if (mapping && virtualLength() > 1) paletteIndex = (i*255)/(virtualLength() -1);
if (!wrap) paletteIndex = scale8(paletteIndex, 240); //cut off blend at palette "end" if (!wrap && strip.paletteBlend != 3) paletteIndex = scale8(paletteIndex, 240); //cut off blend at palette "end"
CRGB fastled_col;
CRGBPalette16 curPal; CRGBPalette16 curPal;
if (transitional && _t) curPal = _t->_palT; curPal = currentPalette(curPal, palette);
else loadPalette(curPal, palette); //if (isInTransition()) curPal = _t->_palT;
fastled_col = ColorFromPalette(curPal, paletteIndex, pbri, (strip.paletteBlend == 3)? NOBLEND:LINEARBLEND); // NOTE: paletteBlend should be global //else loadPalette(curPal, palette);
CRGB fastled_col = ColorFromPalette(curPal, paletteIndex, pbri, (strip.paletteBlend == 3)? NOBLEND:LINEARBLEND); // NOTE: paletteBlend should be global
return RGBW32(fastled_col.r, fastled_col.g, fastled_col.b, 0); return RGBW32(fastled_col.r, fastled_col.g, fastled_col.b, 0);
} }
@ -1166,7 +1122,7 @@ void WS2812FX::finalizeInit(void)
const uint8_t defNumBusses = ((sizeof defDataPins) / (sizeof defDataPins[0])); const uint8_t defNumBusses = ((sizeof defDataPins) / (sizeof defDataPins[0]));
const uint8_t defNumCounts = ((sizeof defCounts) / (sizeof defCounts[0])); const uint8_t defNumCounts = ((sizeof defCounts) / (sizeof defCounts[0]));
uint16_t prevLen = 0; uint16_t prevLen = 0;
for (uint8_t i = 0; i < defNumBusses && i < WLED_MAX_BUSSES+WLED_MIN_VIRTUAL_BUSSES; i++) { for (int i = 0; i < defNumBusses && i < WLED_MAX_BUSSES+WLED_MIN_VIRTUAL_BUSSES; i++) {
uint8_t defPin[] = {defDataPins[i]}; uint8_t defPin[] = {defDataPins[i]};
uint16_t start = prevLen; uint16_t start = prevLen;
uint16_t count = defCounts[(i < defNumCounts) ? i : defNumCounts -1]; uint16_t count = defCounts[(i < defNumCounts) ? i : defNumCounts -1];
@ -1177,7 +1133,7 @@ void WS2812FX::finalizeInit(void)
} }
_length = 0; _length = 0;
for (uint8_t i=0; i<busses.getNumBusses(); i++) { for (int i=0; i<busses.getNumBusses(); i++) {
Bus *bus = busses.getBus(i); Bus *bus = busses.getBus(i);
if (bus == nullptr) continue; if (bus == nullptr) continue;
if (bus->getStart() + bus->getLength() > MAX_LEDS) break; if (bus->getStart() + bus->getLength() > MAX_LEDS) break;
@ -1234,24 +1190,24 @@ void WS2812FX::service() {
if (!seg.freeze) { //only run effect function if not frozen if (!seg.freeze) { //only run effect function if not frozen
_virtualSegmentLength = seg.virtualLength(); _virtualSegmentLength = seg.virtualLength();
_colors_t[0] = seg.currentColor(0, seg.colors[0]); _colors_t[0] = seg.currentColor(0);
_colors_t[1] = seg.currentColor(1, seg.colors[1]); _colors_t[1] = seg.currentColor(1);
_colors_t[2] = seg.currentColor(2, seg.colors[2]); _colors_t[2] = seg.currentColor(2);
seg.currentPalette(_currentPalette, seg.palette); seg.currentPalette(_currentPalette, seg.palette); // we need to pass reference
if (!cctFromRgb || correctWB) busses.setSegmentCCT(seg.currentBri(seg.cct, true), correctWB); if (!cctFromRgb || correctWB) busses.setSegmentCCT(seg.currentBri(true), correctWB);
for (uint8_t c = 0; c < NUM_COLORS; c++) _colors_t[c] = gamma32(_colors_t[c]); for (int c = 0; c < NUM_COLORS; c++) _colors_t[c] = gamma32(_colors_t[c]);
// Effect blending // Effect blending
// When two effects are being blended, each may have different segment data, this // When two effects are being blended, each may have different segment data, this
// data needs to be saved first and then restored before running previous/transitional mode. // data needs to be saved first and then restored before running previous mode.
// The blending will largely depend on the effect behaviour since actual output (LEDs) may be // The blending will largely depend on the effect behaviour since actual output (LEDs) may be
// overwritten by later effect. To enable seamless blending for every effect, additional LED buffer // overwritten by later effect. To enable seamless blending for every effect, additional LED buffer
// would need to be allocated for each effect and then blended together for each pixel. // would need to be allocated for each effect and then blended together for each pixel.
[[maybe_unused]] uint8_t tmpMode = seg.currentMode(seg.mode); // this will return old mode while in transition [[maybe_unused]] uint8_t tmpMode = seg.currentMode(); // this will return old mode while in transition
delay = (*_mode[seg.mode])(); // run new/current mode delay = (*_mode[seg.mode])(); // run new/current mode
#ifndef WLED_DISABLE_MODE_BLEND #ifndef WLED_DISABLE_MODE_BLEND
if (seg.mode != tmpMode) { if (modeBlending && seg.mode != tmpMode) {
Segment::tmpsegd_t _tmpSegData; Segment::tmpsegd_t _tmpSegData;
Segment::modeBlend(true); // set semaphore Segment::modeBlend(true); // set semaphore
seg.swapSegenv(_tmpSegData); // temporarily store new mode state (and swap it with transitional state) seg.swapSegenv(_tmpSegData); // temporarily store new mode state (and swap it with transitional state)
@ -1262,7 +1218,7 @@ void WS2812FX::service() {
} }
#endif #endif
if (seg.mode != FX_MODE_HALLOWEEN_EYES) seg.call++; if (seg.mode != FX_MODE_HALLOWEEN_EYES) seg.call++;
if (seg.transitional && delay > FRAMETIME) delay = FRAMETIME; // force faster updates during transition if (seg.isInTransition() && delay > FRAMETIME) delay = FRAMETIME; // force faster updates during transition
} }
seg.next_time = nowUp + delay; seg.next_time = nowUp + delay;
@ -1399,12 +1355,12 @@ void WS2812FX::show(void) {
// or async show has a separate buffer (ESP32 RMT and I2S are ok) // or async show has a separate buffer (ESP32 RMT and I2S are ok)
if (newBri < _brightness) busses.setBrightness(_brightness); if (newBri < _brightness) busses.setBrightness(_brightness);
unsigned long now = millis(); unsigned long showNow = millis();
size_t diff = now - _lastShow; size_t diff = showNow - _lastShow;
size_t fpsCurr = 200; size_t fpsCurr = 200;
if (diff > 0) fpsCurr = 1000 / diff; if (diff > 0) fpsCurr = 1000 / diff;
_cumulativeFps = (3 * _cumulativeFps + fpsCurr +2) >> 2; // "+2" for proper rounding (2/4 = 0.5) _cumulativeFps = (3 * _cumulativeFps + fpsCurr +2) >> 2; // "+2" for proper rounding (2/4 = 0.5)
_lastShow = now; _lastShow = showNow;
} }
/** /**
@ -1597,10 +1553,12 @@ void WS2812FX::setSegment(uint8_t segId, uint16_t i1, uint16_t i2, uint8_t group
_qStart = i1; _qStop = i2; _qStartY = startY; _qStopY = stopY; _qStart = i1; _qStop = i2; _qStartY = startY; _qStopY = stopY;
_qGrouping = grouping; _qSpacing = spacing; _qOffset = offset; _qGrouping = grouping; _qSpacing = spacing; _qOffset = offset;
_queuedChangesSegId = segId; _queuedChangesSegId = segId;
DEBUG_PRINT(F("Segment queued: ")); DEBUG_PRINTLN(segId);
return; // queued changes are applied immediately after effect function returns return; // queued changes are applied immediately after effect function returns
} }
_segments[segId].setUp(i1, i2, grouping, spacing, offset, startY, stopY); _segments[segId].setUp(i1, i2, grouping, spacing, offset, startY, stopY);
if (segId > 0 && segId == getSegmentsNum()-1 && i2 <= i1) _segments.pop_back(); // if last segment was deleted remove it from vector
} }
void WS2812FX::setUpSegmentFromQueuedChanges() { void WS2812FX::setUpSegmentFromQueuedChanges() {
@ -1729,7 +1687,7 @@ void WS2812FX::fixInvalidSegments() {
bool WS2812FX::checkSegmentAlignment() { bool WS2812FX::checkSegmentAlignment() {
bool aligned = false; bool aligned = false;
for (segment &seg : _segments) { for (segment &seg : _segments) {
for (uint8_t b = 0; b<busses.getNumBusses(); b++) { for (unsigned b = 0; b<busses.getNumBusses(); b++) {
Bus *bus = busses.getBus(b); Bus *bus = busses.getBus(b);
if (seg.start == bus->getStart() && seg.stop == bus->getStart() + bus->getLength()) aligned = true; if (seg.start == bus->getStart() && seg.stop == bus->getStart() + bus->getLength()) aligned = true;
} }
@ -1752,13 +1710,8 @@ uint8_t WS2812FX::setPixelSegment(uint8_t n) {
} }
void WS2812FX::setRange(uint16_t i, uint16_t i2, uint32_t col) { void WS2812FX::setRange(uint16_t i, uint16_t i2, uint32_t col) {
if (i2 >= i) if (i2 < i) std::swap(i,i2);
{ for (unsigned x = i; x <= i2; x++) setPixelColor(x, col);
for (uint16_t x = i; x <= i2; x++) setPixelColor(x, col);
} else
{
for (uint16_t x = i2; x <= i; x++) setPixelColor(x, col);
}
} }
void WS2812FX::setTransitionMode(bool t) { void WS2812FX::setTransitionMode(bool t) {
@ -1793,7 +1746,7 @@ void WS2812FX::loadCustomPalettes() {
if (readObjectFromFile(fileName, nullptr, &pDoc)) { if (readObjectFromFile(fileName, nullptr, &pDoc)) {
JsonArray pal = pDoc[F("palette")]; JsonArray pal = pDoc[F("palette")];
if (!pal.isNull() && pal.size()>4) { // not an empty palette (at least 2 entries) if (!pal.isNull() && pal.size()>3) { // not an empty palette (at least 2 entries)
if (pal[0].is<int>() && pal[1].is<const char *>()) { if (pal[0].is<int>() && pal[1].is<const char *>()) {
// we have an array of index & hex strings // we have an array of index & hex strings
size_t palSize = MIN(pal.size(), 36); size_t palSize = MIN(pal.size(), 36);
@ -1802,7 +1755,7 @@ void WS2812FX::loadCustomPalettes() {
uint8_t rgbw[] = {0,0,0,0}; uint8_t rgbw[] = {0,0,0,0};
tcp[ j ] = (uint8_t) pal[ i ].as<int>(); // index tcp[ j ] = (uint8_t) pal[ i ].as<int>(); // index
colorFromHexString(rgbw, pal[i+1].as<const char *>()); // will catch non-string entires colorFromHexString(rgbw, pal[i+1].as<const char *>()); // will catch non-string entires
for (size_t c=0; c<3; c++) tcp[j+1+c] = rgbw[c]; // only use RGB component for (size_t c=0; c<3; c++) tcp[j+1+c] = gamma8(rgbw[c]); // only use RGB component
DEBUG_PRINTF("%d(%d) : %d %d %d\n", i, int(tcp[j]), int(tcp[j+1]), int(tcp[j+2]), int(tcp[j+3])); DEBUG_PRINTF("%d(%d) : %d %d %d\n", i, int(tcp[j]), int(tcp[j+1]), int(tcp[j+2]), int(tcp[j+3]));
} }
} else { } else {
@ -1810,13 +1763,15 @@ void WS2812FX::loadCustomPalettes() {
palSize -= palSize % 4; // make sure size is multiple of 4 palSize -= palSize % 4; // make sure size is multiple of 4
for (size_t i=0; i<palSize && pal[i].as<int>()<256; i+=4) { for (size_t i=0; i<palSize && pal[i].as<int>()<256; i+=4) {
tcp[ i ] = (uint8_t) pal[ i ].as<int>(); // index tcp[ i ] = (uint8_t) pal[ i ].as<int>(); // index
tcp[i+1] = (uint8_t) pal[i+1].as<int>(); // R tcp[i+1] = gamma8((uint8_t) pal[i+1].as<int>()); // R
tcp[i+2] = (uint8_t) pal[i+2].as<int>(); // G tcp[i+2] = gamma8((uint8_t) pal[i+2].as<int>()); // G
tcp[i+3] = (uint8_t) pal[i+3].as<int>(); // B tcp[i+3] = gamma8((uint8_t) pal[i+3].as<int>()); // B
DEBUG_PRINTF("%d(%d) : %d %d %d\n", i, int(tcp[i]), int(tcp[i+1]), int(tcp[i+2]), int(tcp[i+3])); DEBUG_PRINTF("%d(%d) : %d %d %d\n", i, int(tcp[i]), int(tcp[i+1]), int(tcp[i+2]), int(tcp[i+3]));
} }
} }
customPalettes.push_back(targetPalette.loadDynamicGradientPalette(tcp)); customPalettes.push_back(targetPalette.loadDynamicGradientPalette(tcp));
} else {
DEBUG_PRINTLN(F("Wrong palette format."));
} }
} }
} else { } else {
@ -1832,7 +1787,7 @@ bool WS2812FX::deserializeMap(uint8_t n) {
char fileName[32]; char fileName[32];
strcpy_P(fileName, PSTR("/ledmap")); strcpy_P(fileName, PSTR("/ledmap"));
if (n) sprintf(fileName +7, "%d", n); if (n) sprintf(fileName +7, "%d", n);
strcat(fileName, ".json"); strcat_P(fileName, PSTR(".json"));
bool isFile = WLED_FS.exists(fileName); bool isFile = WLED_FS.exists(fileName);
if (!isFile) { if (!isFile) {
@ -1866,7 +1821,7 @@ bool WS2812FX::deserializeMap(uint8_t n) {
if (!map.isNull() && map.size()) { // not an empty map if (!map.isNull() && map.size()) { // not an empty map
customMappingSize = map.size(); customMappingSize = map.size();
customMappingTable = new uint16_t[customMappingSize]; customMappingTable = new uint16_t[customMappingSize];
for (uint16_t i=0; i<customMappingSize; i++) { for (unsigned i=0; i<customMappingSize; i++) {
customMappingTable[i] = (uint16_t) (map[i]<0 ? 0xFFFFU : map[i]); customMappingTable[i] = (uint16_t) (map[i]<0 ? 0xFFFFU : map[i]);
} }
} }

View File

@ -216,7 +216,9 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
if (((buttonType[s] == BTN_TYPE_ANALOG) || (buttonType[s] == BTN_TYPE_ANALOG_INVERTED)) && (digitalPinToAnalogChannel(btnPin[s]) < 0)) if (((buttonType[s] == BTN_TYPE_ANALOG) || (buttonType[s] == BTN_TYPE_ANALOG_INVERTED)) && (digitalPinToAnalogChannel(btnPin[s]) < 0))
{ {
// not an ADC analog pin // not an ADC analog pin
DEBUG_PRINTF("PIN ALLOC error: GPIO%d for analog button #%d is not an analog pin!\n", btnPin[s], s); DEBUG_PRINT(F("PIN ALLOC error: GPIO")); DEBUG_PRINT(btnPin[s]);
DEBUG_PRINT(F("for analog button #")); DEBUG_PRINT(s);
DEBUG_PRINTLN(F(" is not an analog pin!"));
btnPin[s] = -1; btnPin[s] = -1;
pinManager.deallocatePin(pin,PinOwner::Button); pinManager.deallocatePin(pin,PinOwner::Button);
} }
@ -357,6 +359,7 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
JsonObject light_tr = light["tr"]; JsonObject light_tr = light["tr"];
CJSON(fadeTransition, light_tr["mode"]); CJSON(fadeTransition, light_tr["mode"]);
CJSON(modeBlending, light_tr["fx"]);
int tdd = light_tr["dur"] | -1; int tdd = light_tr["dur"] | -1;
if (tdd >= 0) transitionDelay = transitionDelayDefault = tdd * 100; if (tdd >= 0) transitionDelay = transitionDelayDefault = tdd * 100;
CJSON(strip.paletteFade, light_tr["pal"]); CJSON(strip.paletteFade, light_tr["pal"]);
@ -827,6 +830,7 @@ void serializeConfig() {
JsonObject light_tr = light.createNestedObject("tr"); JsonObject light_tr = light.createNestedObject("tr");
light_tr["mode"] = fadeTransition; light_tr["mode"] = fadeTransition;
light_tr["fx"] = modeBlending;
light_tr["dur"] = transitionDelayDefault / 100; light_tr["dur"] = transitionDelayDefault / 100;
light_tr["pal"] = strip.paletteFade; light_tr["pal"] = strip.paletteFade;
light_tr[F("rpc")] = randomPaletteChangeTime; light_tr[F("rpc")] = randomPaletteChangeTime;
@ -888,6 +892,7 @@ void serializeConfig() {
if_live[F("no-gc")] = arlsDisableGammaCorrection; if_live[F("no-gc")] = arlsDisableGammaCorrection;
if_live[F("offset")] = arlsOffset; if_live[F("offset")] = arlsOffset;
#ifndef WLED_DISABLE_ALEXA
JsonObject if_va = interfaces.createNestedObject("va"); JsonObject if_va = interfaces.createNestedObject("va");
if_va[F("alexa")] = alexaEnabled; if_va[F("alexa")] = alexaEnabled;
@ -896,6 +901,7 @@ void serializeConfig() {
if_va_macros.add(macroAlexaOff); if_va_macros.add(macroAlexaOff);
if_va["p"] = alexaNumPresets; if_va["p"] = alexaNumPresets;
#endif
#ifdef WLED_ENABLE_MQTT #ifdef WLED_ENABLE_MQTT
JsonObject if_mqtt = interfaces.createNestedObject("mqtt"); JsonObject if_mqtt = interfaces.createNestedObject("mqtt");
@ -1033,7 +1039,7 @@ bool deserializeConfigSec() {
JsonObject ap = doc["ap"]; JsonObject ap = doc["ap"];
getStringFromJson(apPass, ap["psk"] , 65); getStringFromJson(apPass, ap["psk"] , 65);
JsonObject interfaces = doc["if"]; [[maybe_unused]] JsonObject interfaces = doc["if"];
#ifdef WLED_ENABLE_MQTT #ifdef WLED_ENABLE_MQTT
JsonObject if_mqtt = interfaces["mqtt"]; JsonObject if_mqtt = interfaces["mqtt"];
@ -1072,7 +1078,7 @@ void serializeConfigSec() {
JsonObject ap = doc.createNestedObject("ap"); JsonObject ap = doc.createNestedObject("ap");
ap["psk"] = apPass; ap["psk"] = apPass;
JsonObject interfaces = doc.createNestedObject("if"); [[maybe_unused]] JsonObject interfaces = doc.createNestedObject("if");
#ifdef WLED_ENABLE_MQTT #ifdef WLED_ENABLE_MQTT
JsonObject if_mqtt = interfaces.createNestedObject("mqtt"); JsonObject if_mqtt = interfaces.createNestedObject("mqtt");
if_mqtt["psk"] = mqttPass; if_mqtt["psk"] = mqttPass;

View File

@ -35,8 +35,19 @@ uint32_t color_blend(uint32_t color1, uint32_t color2, uint16_t blend, bool b16)
* color add function that preserves ratio * color add function that preserves ratio
* idea: https://github.com/Aircoookie/WLED/pull/2465 by https://github.com/Proto-molecule * idea: https://github.com/Aircoookie/WLED/pull/2465 by https://github.com/Proto-molecule
*/ */
uint32_t color_add(uint32_t c1, uint32_t c2) uint32_t color_add(uint32_t c1, uint32_t c2, bool fast)
{ {
if (fast) {
uint8_t r = R(c1);
uint8_t g = G(c1);
uint8_t b = B(c1);
uint8_t w = W(c1);
r = qadd8(r, R(c2));
g = qadd8(g, G(c2));
b = qadd8(b, B(c2));
w = qadd8(w, W(c2));
return RGBW32(r,g,b,w);
} else {
uint32_t r = R(c1) + R(c2); uint32_t r = R(c1) + R(c2);
uint32_t g = G(c1) + G(c2); uint32_t g = G(c1) + G(c2);
uint32_t b = B(c1) + B(c2); uint32_t b = B(c1) + B(c2);
@ -48,10 +59,35 @@ uint32_t color_add(uint32_t c1, uint32_t c2)
if (max < 256) return RGBW32(r, g, b, w); if (max < 256) return RGBW32(r, g, b, w);
else return RGBW32(r * 255 / max, g * 255 / max, b * 255 / max, w * 255 / max); else return RGBW32(r * 255 / max, g * 255 / max, b * 255 / max, w * 255 / max);
} }
}
/*
* fades color toward black
* if using "video" method the resulting color will never become black unless it is already black
*/
uint32_t color_fade(uint32_t c1, uint8_t amount, bool video)
{
uint8_t r = R(c1);
uint8_t g = G(c1);
uint8_t b = B(c1);
uint8_t w = W(c1);
if (video) {
r = scale8_video(r, amount);
g = scale8_video(g, amount);
b = scale8_video(b, amount);
w = scale8_video(w, amount);
} else {
r = scale8(r, amount);
g = scale8(g, amount);
b = scale8(b, amount);
w = scale8(w, amount);
}
return RGBW32(r, g, b, w);
}
void setRandomColor(byte* rgb) void setRandomColor(byte* rgb)
{ {
lastRandomIndex = strip.getMainSegment().get_random_wheel_index(lastRandomIndex); lastRandomIndex = get_random_wheel_index(lastRandomIndex);
colorHStoRGB(lastRandomIndex*256,255,rgb); colorHStoRGB(lastRandomIndex*256,255,rgb);
} }

View File

@ -166,7 +166,7 @@
#define CALL_MODE_NO_NOTIFY 5 #define CALL_MODE_NO_NOTIFY 5
#define CALL_MODE_FX_CHANGED 6 //no longer used #define CALL_MODE_FX_CHANGED 6 //no longer used
#define CALL_MODE_HUE 7 #define CALL_MODE_HUE 7
#define CALL_MODE_PRESET_CYCLE 8 #define CALL_MODE_PRESET_CYCLE 8 //no longer used
#define CALL_MODE_BLYNK 9 //no longer used #define CALL_MODE_BLYNK 9 //no longer used
#define CALL_MODE_ALEXA 10 #define CALL_MODE_ALEXA 10
#define CALL_MODE_WS_SEND 11 //special call mode, not for notifier, updates websocket only #define CALL_MODE_WS_SEND 11 //special call mode, not for notifier, updates websocket only
@ -313,10 +313,9 @@
#define SEG_OPTION_MIRROR 3 //Indicates that the effect will be mirrored within the segment #define SEG_OPTION_MIRROR 3 //Indicates that the effect will be mirrored within the segment
#define SEG_OPTION_FREEZE 4 //Segment contents will not be refreshed #define SEG_OPTION_FREEZE 4 //Segment contents will not be refreshed
#define SEG_OPTION_RESET 5 //Segment runtime requires reset #define SEG_OPTION_RESET 5 //Segment runtime requires reset
#define SEG_OPTION_TRANSITIONAL 6 #define SEG_OPTION_REVERSED_Y 6
#define SEG_OPTION_REVERSED_Y 7 #define SEG_OPTION_MIRROR_Y 7
#define SEG_OPTION_MIRROR_Y 8 #define SEG_OPTION_TRANSPOSED 8
#define SEG_OPTION_TRANSPOSED 9
//Segment differs return byte //Segment differs return byte
#define SEG_DIFFERS_BRI 0x01 // opacity #define SEG_DIFFERS_BRI 0x01 // opacity
@ -345,6 +344,7 @@
#define ERR_FS_QUOTA 11 // The FS is full or the maximum file size is reached #define ERR_FS_QUOTA 11 // The FS is full or the maximum file size is reached
#define ERR_FS_PLOAD 12 // It was attempted to load a preset that does not exist #define ERR_FS_PLOAD 12 // It was attempted to load a preset that does not exist
#define ERR_FS_IRLOAD 13 // It was attempted to load an IR JSON cmd, but the "ir.json" file does not exist #define ERR_FS_IRLOAD 13 // It was attempted to load an IR JSON cmd, but the "ir.json" file does not exist
#define ERR_FS_RMLOAD 14 // It was attempted to load an remote JSON cmd, but the "remote.json" file does not exist
#define ERR_FS_GENERAL 19 // A general unspecified filesystem error occured #define ERR_FS_GENERAL 19 // A general unspecified filesystem error occured
#define ERR_OVERTEMP 30 // An attached temperature sensor has measured above threshold temperature (not implemented) #define ERR_OVERTEMP 30 // An attached temperature sensor has measured above threshold temperature (not implemented)
#define ERR_OVERCURRENT 31 // An attached current sensor has measured a current above the threshold (not implemented) #define ERR_OVERCURRENT 31 // An attached current sensor has measured a current above the threshold (not implemented)

View File

@ -198,12 +198,13 @@
</label> </label>
</div> </div>
</div> </div>
<div style="padding-bottom: 10px;"> </div>
<div style="padding-block: 10px;">
<button class="btn btn-xs" type="button" onclick="togglePixelMagicTool()"><i class="icons btn-icon">&#xe410;</i></button>
<button class="btn btn-xs" type="button" onclick="window.location.href=getURL('/cpal.htm')"><i class="icons btn-icon">&#xe18a;</i></button> <button class="btn btn-xs" type="button" onclick="window.location.href=getURL('/cpal.htm')"><i class="icons btn-icon">&#xe18a;</i></button>
<button class="btn btn-xs" type="button" onclick="palettesData=null;localStorage.removeItem('wledPalx');requestJson({rmcpal:true});setTimeout(loadPalettes,250,loadPalettesData);"><i class="icons btn-icon">&#xe037;</i></button> <button class="btn btn-xs" type="button" onclick="palettesData=null;localStorage.removeItem('wledPalx');requestJson({rmcpal:true});setTimeout(loadPalettes,250,loadPalettesData);"><i class="icons btn-icon">&#xe037;</i></button>
</div> </div>
</div> </div>
</div>
<div id="Effects" class="tabcontent"> <div id="Effects" class="tabcontent">
<div id="fx"> <div id="fx">
@ -392,6 +393,7 @@
<button class="btn" onclick="setLor(2)">Override until reboot</button><br> <button class="btn" onclick="setLor(2)">Override until reboot</button><br>
<span class="h">For best performance, it is recommended to turn off the streaming source when not in use.</span> <span class="h">For best performance, it is recommended to turn off the streaming source when not in use.</span>
</div> </div>
<i id="roverstar" class="icons huge" onclick="setLor(0)">&#xe410;</i><br> <i id="roverstar" class="icons huge" onclick="setLor(0)">&#xe410;</i><br>
<script src="index.js"></script> <script src="index.js"></script>
</body> </body>

View File

@ -33,10 +33,9 @@ var hol = [
[0,11,24,4,"https://aircoookie.github.io/xmas.png"], // christmas [0,11,24,4,"https://aircoookie.github.io/xmas.png"], // christmas
[0,2,17,1,"https://images.alphacoders.com/491/491123.jpg"], // st. Patrick's day [0,2,17,1,"https://images.alphacoders.com/491/491123.jpg"], // st. Patrick's day
[2025,3,20,2,"https://aircoookie.github.io/easter.png"], [2025,3,20,2,"https://aircoookie.github.io/easter.png"],
[2023,3,9,2,"https://aircoookie.github.io/easter.png"],
[2024,2,31,2,"https://aircoookie.github.io/easter.png"], [2024,2,31,2,"https://aircoookie.github.io/easter.png"],
[0,6,4,1,"https://initiate.alphacoders.com/download/wallpaper/516792/images/jpg/510921363292536"], // 4th of July [0,6,4,1,"https://images.alphacoders.com/516/516792.jpg"], // 4th of July
[0,0,1,1,"https://initiate.alphacoders.com/download/wallpaper/1198800/images/jpg/2522807481585600"] // new year [0,0,1,1,"https://images.alphacoders.com/119/1198800.jpg"] // new year
]; ];
function handleVisibilityChange() {if (!d.hidden && new Date () - lastUpdate > 3000) requestJson();} function handleVisibilityChange() {if (!d.hidden && new Date () - lastUpdate > 3000) requestJson();}
@ -1514,12 +1513,15 @@ function setEffectParameters(idx)
} }
// set the bottom position of selected effect (sticky) as the top of sliders div // set the bottom position of selected effect (sticky) as the top of sliders div
setInterval(()=>{ function setSelectedEffectPosition() {
let top = parseInt(getComputedStyle(gId("sliders")).height); let top = parseInt(getComputedStyle(gId("sliders")).height);
top += 5; top += 5;
let sel = d.querySelector('#fxlist .selected'); let sel = d.querySelector('#fxlist .selected');
if (sel) sel.style.bottom = top + "px"; // we will need to remove this when unselected (in setFX()) if (sel) sel.style.bottom = top + "px"; // we will need to remove this when unselected (in setFX())
},750); }
setSelectedEffectPosition();
setInterval(setSelectedEffectPosition,750);
// set html color items on/off // set html color items on/off
var cslLabel = ''; var cslLabel = '';
var sep = ''; var sep = '';

View File

@ -7,13 +7,10 @@
<title>Pixel Magic Tool</title> <title>Pixel Magic Tool</title>
<!-- <link
rel="shortcut icon"
href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAK9JREFUeNqUUssNwyAMJZWVUw4dhRHakZA6RqWMFEbwKDnk1FNBekEWxOBYQggL/D68yXXq+M7PtHkcefn89vrOw/UrP96w/NUFGiDLRz71GyY0QJa1Yn+nFa0ShqUNYCAF0QvoceOB4naEZif6UTNRapYaTyauRi4DEspr4Hbs5YKsbmtMyeJ0LxeESV4gB+hlSy4oO2txWysyus0a0+lO6vBjxcTMlG4mt2H6F2AAhU5NWu4dorQAAAAASUVORK5CYII=
" /> -->
<style> <style>
:root { :root {
--s-thumb: #0006;
--s-background: #0003;
--overlay: rgba(0, 0, 0, 0.5); --overlay: rgba(0, 0, 0, 0.5);
--background: #111; --background: #111;
--text: #bbb; --text: #bbb;
@ -34,6 +31,24 @@
--warning-light: #f48c06; --warning-light: #f48c06;
} }
::-webkit-scrollbar {
width: 6px;
}
::-webkit-scrollbar-track {
background: transparent;
}
::-webkit-scrollbar-thumb {
background: var(--s-thumb);
opacity: 0.2;
border-radius: 5px;
}
::-webkit-scrollbar-thumb:hover {
background: var(--s-background);
}
::selection { ::selection {
background: var(--blue-light); background: var(--blue-light);
} }
@ -65,7 +80,7 @@
display: block; display: block;
font-weight: 400; font-weight: 400;
margin: 2px 0 5px; margin: 2px 0 5px;
color: var(--text); color: var(--gray-light);
font-size: 12px; font-size: 12px;
} }
@ -83,10 +98,19 @@
font-weight: 600; font-weight: 600;
} }
:is(a:hover, a:focus, a:active) { a:is(:hover, :focus, :active) {
color: var(--blue-medium); color: var(--blue-medium);
} }
#wledEdit {
padding: 4px 8px;
background: var(--blue-light);
margin-left: 6px;
display: inline-block;
border-radius: 4px;
color: var(--gray-light);
}
.m-zero { .m-zero {
margin: 0 !important; margin: 0 !important;
} }
@ -108,8 +132,7 @@
} }
.content { .content {
width: calc(100% - 40px); width: min(768px, calc(100% - 40px));
max-width: 768px;
margin: 20px; margin: 20px;
} }
@ -117,26 +140,43 @@
display: flex; display: flex;
flex-wrap: nowrap; flex-wrap: nowrap;
justify-content: space-between; justify-content: space-between;
margin: 20px 0 0; margin-top: 20px;
} }
.column { .column {
flex-basis: calc(50% - 10px); flex-basis: calc(50% - 10px);
position: relative; position: relative;
padding: 0 5px; padding-inline: 5px;
} }
.column-full { .column-full {
flex-basis: 100%; flex-basis: 100%;
position: relative; position: relative;
padding: 0 5px; padding-inline: 5px;
}
.header {
display: flex;
flex-direction: column;
align-items: center;
padding-bottom: 20px;
}
.header .brand {
width: 100%;
max-width: 200px;
height: 100%;
display: block;
outline: none;
border: 0;
} }
label { label {
display: block; display: flex;
margin-bottom: 5px; margin-bottom: 5px;
font-weight: bold; font-weight: bold;
color: var(--text); color: var(--text);
align-items: center;
} }
input[type="text"], input[type="text"],
@ -157,7 +197,7 @@
width: 32px; width: 32px;
height: 32px; height: 32px;
cursor: pointer; cursor: pointer;
padding: 0px 1px; padding-inline: 1px;
outline: none; outline: none;
} }
@ -172,18 +212,19 @@
} }
.input-group .input-description { .input-group .input-description {
width: 38px; width: 100%;
max-width: 38px;
height: 38px; height: 38px;
padding: 10px 0; display: flex;
justify-content: center;
align-items: center;
color: var(--gray-dark); color: var(--gray-dark);
background: var(--gray-light); background: var(--gray-light);
border-radius: 0px 5px 5px 0; border-radius: 0px 8px 8px 0;
border: 1px solid var(--gray-light); border: 1px solid var(--gray-light);
border-left: 0; border-left: 0;
text-align: center;
font-size: 14px; font-size: 14px;
line-height: 16px; line-height: 16px;
font-weight: 600;
} }
.input-group .square { .input-group .square {
@ -191,10 +232,19 @@
margin-left: 10px; margin-left: 10px;
} }
.input-group .square input {
text-align: center;
background: none;
padding: 0;
border: 0;
color: var(--gray-dark);
}
textarea { textarea {
resize: vertical; resize: none;
min-height: 200px; min-height: 200px;
border-radius: 8px; border-radius: 8px;
overflow-x: hidden;
} }
.custom-select { .custom-select {
@ -231,7 +281,7 @@
text-align: center; text-align: center;
padding: 40px 10px; padding: 40px 10px;
border-radius: 8px; border-radius: 8px;
margin: 20px 0 0; margin-top: 20px;
transition: all 0.5s ease-in-out; transition: all 0.5s ease-in-out;
} }
@ -253,14 +303,15 @@
width: 100%; width: 100%;
border-radius: 10px; border-radius: 10px;
outline: none; outline: none;
margin: 16px 0; margin-block: 15px;
} }
.range-slider::-webkit-slider-thumb { .range-slider::-webkit-slider-thumb,
.range-slider::-moz-range-thumb {
appearance: none; appearance: none;
height: 16px; height: 16px;
width: 16px; width: 16px;
background-color: var(--gray-dark); background-color: var(--blue-light);
border-radius: 50%; border-radius: 50%;
cursor: pointer; cursor: pointer;
border: 0; border: 0;
@ -326,7 +377,7 @@
align-items: center; align-items: center;
width: auto; width: auto;
padding: 6px 12px; padding: 6px 12px;
margin: 10px 0 0; margin-top: 10px;
border-radius: 8px; border-radius: 8px;
transform: translateY(30px); transform: translateY(30px);
opacity: 0; opacity: 0;
@ -334,7 +385,7 @@
} }
.toast .toast-body { .toast .toast-body {
padding: 8px 0; padding-block: 8px;
font-weight: 600; font-weight: 600;
color: var(--text); color: var(--text);
letter-spacing: 0.5px; letter-spacing: 0.5px;
@ -360,7 +411,7 @@
height: 3px; height: 3px;
transform: scaleX(0); transform: scaleX(0);
transform-origin: left; transform-origin: left;
border-radius: inherit; border-radius: 8px;
} }
.toast.success .toast-progress { .toast.success .toast-progress {
@ -387,22 +438,6 @@
); );
} }
.header {
display: flex;
flex-direction: column;
align-items: center;
padding: 0 0 20px;
}
.header .brand {
width: 100%;
max-width: 200px;
height: 100%;
display: block;
outline: none;
border: 0;
}
.carousel { .carousel {
display: flex; display: flex;
height: 100%; height: 100%;
@ -410,18 +445,6 @@
cursor: pointer; cursor: pointer;
} }
.carousel img {
display: block;
width: 100%;
height: 100%;
margin-right: 20px;
border: 0;
}
.carousel img:last-child {
margin-right: 0;
}
.button { .button {
width: 100%; width: 100%;
border: 0; border: 0;
@ -429,7 +452,7 @@
border-radius: 50px; border-radius: 50px;
color: var(--text); color: var(--text);
cursor: pointer; cursor: pointer;
margin: 0 0 10px; margin-bottom: 10px;
background: var(--gray-medium); background: var(--gray-medium);
border: 1px solid var(--gray-dark); border: 1px solid var(--gray-dark);
transition: all 0.5s ease-in-out; transition: all 0.5s ease-in-out;
@ -477,7 +500,7 @@
} }
#recreatedImage { #recreatedImage {
margin: 20px 0; margin-block: 20px;
} }
.invalid { .invalid {
@ -487,16 +510,12 @@
.error-message { .error-message {
display: block; display: block;
color: var(--error-dark); color: var(--error-dark);
padding: 4px 0; padding-block: 4px;
font-weight: 600; font-weight: 600;
font-size: 12px; font-size: 12px;
} }
@media (max-width: 767px) { @media (max-width: 767px) {
.header {
padding-bottom: 0;
}
.row { .row {
flex-wrap: wrap; flex-wrap: wrap;
flex-direction: column; flex-direction: column;
@ -506,7 +525,7 @@
.column, .column,
.column-full { .column-full {
flex-basis: 100%; flex-basis: 100%;
margin: 20px 0 0; margin-top: 20px;
padding: 0; padding: 0;
} }
} }
@ -542,68 +561,7 @@
<div class="content"> <div class="content">
<form id="formGenerate" novalidate> <form id="formGenerate" novalidate>
<div class="header"> <div class="header">
<svg <img src=" data:image/png;base64,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" alt="Pixel Magic Tool" class="brand">
class="brand"
version="1.1"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
x="0px"
y="0px"
width="200px"
height="130px"
viewBox="0 0 200 130"
enable-background="new 0 0 200 130"
xml:space="preserve">
<image
width="200"
height="130"
x="0"
y="0"
href="data:image/png;base64,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
ZVJlYWR5ccllPAAAAABJRU5ErkJggg==" />
</svg>
</div> </div>
<div class="row"> <div class="row">
<div class="column" validate> <div class="column" validate>
@ -626,11 +584,11 @@
<label for="pattern">Pattern</label> <label for="pattern">Pattern</label>
<select name="pattern" id="pattern" required> <select name="pattern" id="pattern" required>
<option value="">Select a choice</option> <option value="">Select a choice</option>
<option value="1" title="['ffffff']" selected> <option value="1" title="['ffffff']">Individual</option>
Individual
</option>
<option value="2" title="[0, 'ffffff']">Index</option> <option value="2" title="[0, 'ffffff']">Index</option>
<option value="3" title="[0, 5, 'ffffff']">Range</option> <option value="3" title="[0, 5, 'ffffff']" selected>
Range
</option>
</select> </select>
</div> </div>
</div> </div>
@ -686,19 +644,25 @@
max="255" max="255"
value="128" value="128"
class="range-slider" /> class="range-slider" />
<div class="input-description square">
<input <input
type="text" type="text"
name="brightnessValue"
id="brightnessValue" id="brightnessValue"
class="input-description square"
value="128" /> value="128" />
</div> </div>
</div> </div>
</div> </div>
</div>
<div class="row"> <div class="row">
<div class="column" validate> <div class="column" validate>
<label for="animation">Animation</label> <label for="animation">Animation</label>
<label class="switch"> <label class="switch">
<input type="checkbox" name="animation" id="animation" /> <input
type="checkbox"
name="animation"
id="animation"
data-parent="animation" />
<span class="switch-slider"></span> <span class="switch-slider"></span>
</label> </label>
</div> </div>
@ -708,19 +672,25 @@
<input <input
type="checkbox" type="checkbox"
name="transparentImage" name="transparentImage"
id="transparentImage" /> id="transparentImage"
data-parent="transparentImage" />
<span class="switch-slider"></span> <span class="switch-slider"></span>
</label> </label>
</div> </div>
<div class="column" validate> <div class="column" validate>
<label for="resizeImage">Resize Image</label> <label for="resizeImage">Resize Image</label>
<label class="switch"> <label class="switch">
<input type="checkbox" name="resizeImage" id="resizeImage" /> <input
type="checkbox"
name="resizeImage"
id="resizeImage"
data-parent="resizeImage"
checked />
<span class="switch-slider"></span> <span class="switch-slider"></span>
</label> </label>
</div> </div>
</div> </div>
<div class="row resizeImage" style="display: none"> <div class="row resizeImage">
<div class="column" validate> <div class="column" validate>
<label for="width">Width</label> <label for="width">Width</label>
<input type="number" name="width" id="width" value="16" /> <input type="number" name="width" id="width" value="16" />
@ -747,7 +717,9 @@
min="0" min="0"
step="0.1" step="0.1"
inputmode="numeric" /> inputmode="numeric" />
<div class="input-description">sec</div> <div class="input-description">
<span>sec</span>
</div>
</div> </div>
</div> </div>
<div class="column" validate> <div class="column" validate>
@ -762,7 +734,9 @@
min="0" min="0"
step="0.1" step="0.1"
inputmode="numeric" /> inputmode="numeric" />
<div class="input-description">sec</div> <div class="input-description">
<span>sec</span>
</div>
</div> </div>
</div> </div>
</div> </div>
@ -773,30 +747,27 @@
Color that will replace the Color that will replace the
<strong>transparent pixels</strong> in the image <strong>transparent pixels</strong> in the image
</small> </small>
<input type="color" name="color" id="color" value="#eeeeee" /> <input type="color" name="color" id="color" value="#00BFFF" />
</div> </div>
</div> </div>
<div class="row"> <div class="row">
<div class="column-full" validate> <div class="column-full" validate>
<div class="custom-select"> <div class="custom-select">
<label for="images">Images</label> <label for="images">
<select name="images" id="images" required> <span>Images upload to WLED</span>
<option value="">Select a choice</option> <a id="wledEdit" href="http://[wled-ip]/edit" target="_blank">
<option value="upload">Upload</option> upload
</a>
</label>
<select name="images" id="images">
<option value="">Select image</option>
</select> </select>
</div> </div>
<small>
Images uploaded to
<a id="wledEdit" href="http://[wled-ip]/edit" target="_blank">
<strong>WLED</strong>
</a>
or upload image
</small>
</div> </div>
</div> </div>
<div id="dropzone" class="dropzone" validate style="display: none"> <div id="dropzone" class="dropzone" validate>
<p id="dropzoneLabel"> <p id="dropzoneLabel">
Drag and drop a file here or click to select a file Drag and drop a file here or click to select a local file
</p> </p>
<input <input
type="file" type="file"
@ -868,7 +839,7 @@
const hostname = element("hostname"); const hostname = element("hostname");
hostname.value = host; hostname.value = host;
hostname.addEventListener("change", async () => { hostname.addEventListener("blur", async () => {
WLED_URL = `${protocol}//${hostname.value}`; WLED_URL = `${protocol}//${hostname.value}`;
await segments(); await segments();
@ -893,7 +864,7 @@
function hostnameLabel() { function hostnameLabel() {
const link = element("wledEdit"); const link = element("wledEdit");
link.href = link.href.replace("[wled-ip]", hostname.value); link.href = WLED_URL + "/edit";
} }
async function playlist() { async function playlist() {
@ -999,6 +970,7 @@
if (success) { if (success) {
toast(`Preset "${item.n}" save successfully`); toast(`Preset "${item.n}" save successfully`);
window.parent.postMessage("loadPresets", WLED_URL);
} }
} catch (error) { } catch (error) {
toast(`Error saving preset: ${error}`, "error"); toast(`Error saving preset: ${error}`, "error");
@ -1047,12 +1019,9 @@
return mimeTypes.includes(mimetype); return mimeTypes.includes(mimetype);
}); });
const options = [ const options = [{ text: "Select image", value: "" }];
{ text: "Select a choice", value: "" },
{ text: "Upload", value: "upload" },
];
if (images) { if (images.length > 0) {
options.push( options.push(
...images.map(({ name }) => ({ ...images.map(({ name }) => ({
text: name, text: name,
@ -1064,6 +1033,11 @@
options.forEach(({ text, value }) => { options.forEach(({ text, value }) => {
const option = new Option(text, value); const option = new Option(text, value);
if (index === 0) {
option.selected = true;
}
select.appendChild(option); select.appendChild(option);
}); });
} }
@ -1076,7 +1050,6 @@
async function segments() { async function segments() {
const select = element("segments"); const select = element("segments");
const pattern = element("pattern");
const width = element("width"); const width = element("width");
const height = element("height"); const height = element("height");
@ -1114,7 +1087,6 @@
option.selected = true; option.selected = true;
width.value = w; width.value = w;
height.value = h; height.value = h;
pattern.value = w * h > 512 ? 3 : 1;
} }
select.add(option); select.add(option);
@ -1278,12 +1250,12 @@
const dropzone = element("dropzone"); const dropzone = element("dropzone");
const { value } = e.target.selectedOptions[0]; const { value } = e.target.selectedOptions[0];
if (value === "upload") { if (!value) {
const dropzoneLabel = element("dropzoneLabel"); const dropzoneLabel = element("dropzoneLabel");
const source = element("source"); const source = element("source");
dropzoneLabel.textContent = dropzoneLabel.textContent =
"Drag and drop a file here or click to select a file"; "Drag and drop a file here or click to select a local file";
source.value = ""; source.value = "";
dropzone.style.display = "block"; dropzone.style.display = "block";
@ -1293,43 +1265,35 @@
}); });
element("transparentImage").addEventListener("change", (e) => { element("transparentImage").addEventListener("change", (e) => {
const transparentImage = d.getElementsByClassName("transparentImage"); const transparentImage = d.getElementsByClassName("transparentImage")[0];
const { checked } = e.target; const { checked } = e.target;
Array.from(transparentImage).forEach(function (element) {
if (checked) { if (checked) {
element.style.display = "flex"; transparentImage.style.display = "flex";
} else { } else {
element.style.display = "none"; transparentImage.style.display = "none";
} }
}); });
});
element("resizeImage").addEventListener("change", (e) => { element("resizeImage").addEventListener("change", (e) => {
const resizeImage = d.getElementsByClassName("resizeImage"); const resizeImage = d.getElementsByClassName("resizeImage")[0];
const pattern = element("pattern"); const pattern = element("pattern");
const { checked } = e.target; const { checked } = e.target;
Array.from(resizeImage).forEach(function (element) {
if (checked) { if (checked) {
pattern.value = 3; resizeImage.style.display = "flex";
element.style.display = "flex";
} else { } else {
pattern.value = 1; resizeImage.style.display = "none";
element.style.display = "none";
} }
}); });
});
element("animation").addEventListener("change", (e) => { element("animation").addEventListener("change", (e) => {
const animation = d.getElementsByClassName("animation"); const animation = d.getElementsByClassName("animation")[0];
const pattern = element("pattern"); const pattern = element("pattern");
const source = element("source"); const source = element("source");
const { checked } = e.target; const { checked } = e.target;
Array.from(animation).forEach(function (element) {
if (checked) { if (checked) {
toast( toast(
'If you want all frames in the image, set it to "0"', 'If you want all frames in the image, set it to "0"',
@ -1338,18 +1302,15 @@
); );
source.setAttribute("accept", "image/gif"); source.setAttribute("accept", "image/gif");
element.style.display = "flex"; animation.style.display = "flex";
pattern.value = 3;
} else { } else {
source.setAttribute( source.setAttribute(
"accept", "accept",
"image/jpg,image/jpeg,image/png,image/gif" "image/jpg,image/jpeg,image/png,image/gif"
); );
element.style.display = "none"; animation.style.display = "none";
pattern.value = 1;
} }
}); });
});
element("btnGenerate").addEventListener("click", async (event) => { element("btnGenerate").addEventListener("click", async (event) => {
const { checked } = element("animation"); const { checked } = element("animation");
@ -1402,8 +1363,7 @@
const response = element("response"); const response = element("response");
const recreatedImage = element("recreatedImage"); const recreatedImage = element("recreatedImage");
const urlImage = const urlImage = !images ? URL.createObjectURL(file) : images;
images === "upload" ? URL.createObjectURL(file) : images;
const image = await loadImage(urlImage); const image = await loadImage(urlImage);
const { canvas, bri, id, i } = recreate(image); const { canvas, bri, id, i } = recreate(image);

View File

@ -80,7 +80,7 @@
<button type=submit id="b" onclick="window.location=getURL('/')">Back</button> <button type=submit id="b" onclick="window.location=getURL('/')">Back</button>
<button type="submit" onclick="window.location=getURL('/settings/wifi')">WiFi Setup</button> <button type="submit" onclick="window.location=getURL('/settings/wifi')">WiFi Setup</button>
<button type="submit" onclick="window.location=getURL('/settings/leds')">LED Preferences</button> <button type="submit" onclick="window.location=getURL('/settings/leds')">LED Preferences</button>
<button id="2dbtn" style="display:none;" type="submit" onclick="window.location=getURL('/settings/2D')">2D Configuration</button> <button id="2dbtn" type="submit" onclick="window.location=getURL('/settings/2D')">2D Configuration</button>
<button type="submit" onclick="window.location=getURL('/settings/ui')">User Interface</button> <button type="submit" onclick="window.location=getURL('/settings/ui')">User Interface</button>
<button id="dmxbtn" style="display:none;" type="submit" onclick="window.location=getURL('/settings/dmx')">DMX Output</button> <button id="dmxbtn" style="display:none;" type="submit" onclick="window.location=getURL('/settings/dmx')">DMX Output</button>
<button type="submit" onclick="window.location=getURL('/settings/sync')">Sync Interfaces</button> <button type="submit" onclick="window.location=getURL('/settings/sync')">Sync Interfaces</button>

View File

@ -773,6 +773,7 @@ Length: <input type="number" name="XC${i}" id="xc${i}" class="l" min="1" max="65
Brightness factor: <input name="BF" type="number" class="m" min="1" max="255" required> % Brightness factor: <input name="BF" type="number" class="m" min="1" max="255" required> %
<h3>Transitions</h3> <h3>Transitions</h3>
Crossfade: <input type="checkbox" name="TF"><br> Crossfade: <input type="checkbox" name="TF"><br>
Effect blending: <input type="checkbox" name="EB"><br>
Transition Time: <input name="TD" type="number" class="xl" min="0" max="65500"> ms<br> Transition Time: <input name="TD" type="number" class="xl" min="0" max="65500"> ms<br>
Enable Palette transitions: <input type="checkbox" name="PF"><br> Enable Palette transitions: <input type="checkbox" name="PF"><br>
<i>Random Cycle</i> Palette Time: <input name="TP" type="number" class="m" min="1" max="255"> s<br> <i>Random Cycle</i> Palette Time: <input name="TP" type="number" class="m" min="1" max="255"> s<br>

View File

@ -63,7 +63,8 @@ class NeoGammaWLEDMethod {
#define gamma32(c) NeoGammaWLEDMethod::Correct32(c) #define gamma32(c) NeoGammaWLEDMethod::Correct32(c)
#define gamma8(c) NeoGammaWLEDMethod::rawGamma8(c) #define gamma8(c) NeoGammaWLEDMethod::rawGamma8(c)
uint32_t color_blend(uint32_t,uint32_t,uint16_t,bool b16=false); uint32_t color_blend(uint32_t,uint32_t,uint16_t,bool b16=false);
uint32_t color_add(uint32_t,uint32_t); uint32_t color_add(uint32_t,uint32_t, bool fast=false);
uint32_t color_fade(uint32_t c1, uint8_t amount, bool video=false);
inline uint32_t colorFromRgbw(byte* rgbw) { return uint32_t((byte(rgbw[3]) << 24) | (byte(rgbw[0]) << 16) | (byte(rgbw[1]) << 8) | (byte(rgbw[2]))); } 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 colorHStoRGB(uint16_t hue, byte sat, byte* rgb); //hue, sat to rgb void colorHStoRGB(uint16_t hue, byte sat, byte* rgb); //hue, sat to rgb
void colorKtoRGB(uint16_t kelvin, byte* rgb); void colorKtoRGB(uint16_t kelvin, byte* rgb);
@ -353,6 +354,7 @@ void checkSettingsPIN(const char *pin);
uint16_t crc16(const unsigned char* data_p, size_t length); uint16_t crc16(const unsigned char* data_p, size_t length);
um_data_t* simulateSound(uint8_t simulationId); um_data_t* simulateSound(uint8_t simulationId);
void enumerateLedmaps(); void enumerateLedmaps();
uint8_t get_random_wheel_index(uint8_t pos);
#ifdef WLED_ADD_EEPROM_SUPPORT #ifdef WLED_ADD_EEPROM_SUPPORT
//wled_eeprom.cpp //wled_eeprom.cpp

View File

@ -9,7 +9,7 @@
// Autogenerated from wled00/data/cpal/cpal.htm, do not edit!! // Autogenerated from wled00/data/cpal/cpal.htm, do not edit!!
const uint16_t PAGE_cpal_L = 4721; const uint16_t PAGE_cpal_L = 4721;
const uint8_t PAGE_cpal[] PROGMEM = { const uint8_t PAGE_cpal[] PROGMEM = {
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x0a, 0xbd, 0x3b, 0x7f, 0x73, 0xdb, 0xb6, 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x13, 0xbd, 0x3b, 0x7f, 0x73, 0xdb, 0xb6,
0x92, 0xff, 0xe7, 0x53, 0x20, 0x4c, 0x5f, 0x42, 0xd6, 0x14, 0x45, 0xd2, 0xb6, 0x64, 0x4b, 0xa2, 0x92, 0xff, 0xe7, 0x53, 0x20, 0x4c, 0x5f, 0x42, 0xd6, 0x14, 0x45, 0xd2, 0xb6, 0x64, 0x4b, 0xa2,
0x3b, 0xa9, 0x93, 0x77, 0xce, 0x8d, 0xdd, 0x64, 0x5e, 0x7c, 0x6e, 0x7b, 0x3e, 0xbf, 0x31, 0x4d, 0x3b, 0xa9, 0x93, 0x77, 0xce, 0x8d, 0xdd, 0x64, 0x5e, 0x7c, 0x6e, 0x7b, 0x3e, 0xbf, 0x31, 0x4d,
0x42, 0x12, 0x1b, 0x8a, 0xe0, 0x03, 0x21, 0xd9, 0xae, 0xac, 0xef, 0x7e, 0xbb, 0x00, 0x48, 0x91, 0x42, 0x12, 0x1b, 0x8a, 0xe0, 0x03, 0x21, 0xd9, 0xae, 0xac, 0xef, 0x7e, 0xbb, 0x00, 0x48, 0x91,

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -210,7 +210,7 @@ void sendImprovInfoResponse() {
//Use serverDescription if it has been changed from the default "WLED", else mDNS name //Use serverDescription if it has been changed from the default "WLED", else mDNS name
bool useMdnsName = (strcmp(serverDescription, "WLED") == 0 && strlen(cmDNS) > 0); bool useMdnsName = (strcmp(serverDescription, "WLED") == 0 && strlen(cmDNS) > 0);
char vString[20]; char vString[20];
sprintf_P(vString, PSTR("0.14.0/%i"), VERSION); sprintf_P(vString, PSTR("0.14.1-a1/%i"), VERSION);
const char *str[4] = {"WLED", vString, bString, useMdnsName ? cmDNS : serverDescription}; const char *str[4] = {"WLED", vString, bString, useMdnsName ? cmDNS : serverDescription};
sendImprovRPCResult(ImprovRPCType::Request_Info, 4, str); sendImprovRPCResult(ImprovRPCType::Request_Info, 4, str);

View File

@ -165,6 +165,8 @@ void updateInterfaces(uint8_t callMode)
sendDataWs(); sendDataWs();
lastInterfaceUpdate = millis(); lastInterfaceUpdate = millis();
interfaceUpdateCallMode = 0; //disable
if (callMode == CALL_MODE_WS_SEND) return; if (callMode == CALL_MODE_WS_SEND) return;
#ifndef WLED_DISABLE_ALEXA #ifndef WLED_DISABLE_ALEXA
@ -174,7 +176,6 @@ void updateInterfaces(uint8_t callMode)
} }
#endif #endif
doPublishMqtt = true; doPublishMqtt = true;
interfaceUpdateCallMode = 0; //disable
} }

View File

@ -247,6 +247,7 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
} }
fadeTransition = request->hasArg(F("TF")); fadeTransition = request->hasArg(F("TF"));
modeBlending = request->hasArg(F("EB"));
t = request->arg(F("TD")).toInt(); t = request->arg(F("TD")).toInt();
if (t >= 0) transitionDelayDefault = t; if (t >= 0) transitionDelayDefault = t;
strip.paletteFade = request->hasArg(F("PF")); strip.paletteFade = request->hasArg(F("PF"));

View File

@ -382,8 +382,9 @@ void handleNotifications()
} }
if (version > 11) { if (version > 11) {
// when applying synced options ignore selected as it may be used as indicator of which segments to sync // when applying synced options ignore selected as it may be used as indicator of which segments to sync
// freeze, reset & transitional should never be synced // freeze, reset should never be synced
selseg.options = (selseg.options & 0x0071U) | (udpIn[28+ofs]<<8) | (udpIn[9 +ofs] & 0x8E); // ignore selected, freeze, reset & transitional // LSB to MSB: select, reverse, on, mirror, freeze, reset, reverse_y, mirror_y, transpose, map1d2d (3), ssim (2), set (2)
selseg.options = (selseg.options & 0b0000000000110001U) | (udpIn[28+ofs]<<8) | (udpIn[9 +ofs] & 0b11001110U); // ignore selected, freeze, reset
if (applyEffects) { if (applyEffects) {
selseg.custom1 = udpIn[29+ofs]; selseg.custom1 = udpIn[29+ofs];
selseg.custom2 = udpIn[30+ofs]; selseg.custom2 = udpIn[30+ofs];

View File

@ -416,9 +416,9 @@ uint16_t crc16(const unsigned char* data_p, size_t length) {
// (only 2 used as stored in 1 bit in segment options, consider switching to a single global simulation type) // (only 2 used as stored in 1 bit in segment options, consider switching to a single global simulation type)
typedef enum UM_SoundSimulations { typedef enum UM_SoundSimulations {
UMS_BeatSin = 0, UMS_BeatSin = 0,
UMS_WeWillRockYou UMS_WeWillRockYou,
//UMS_10_13, UMS_10_13,
//UMS_14_3 UMS_14_3
} um_soundSimulations_t; } um_soundSimulations_t;
um_data_t* simulateSound(uint8_t simulationId) um_data_t* simulateSound(uint8_t simulationId)
@ -503,7 +503,7 @@ um_data_t* simulateSound(uint8_t simulationId)
fftResult[i] = 0; fftResult[i] = 0;
} }
break; break;
/*case UMS_10_3: case UMS_10_13:
for (int i = 0; i<16; i++) for (int i = 0; i<16; i++)
fftResult[i] = inoise8(beatsin8(90 / (i+1), 0, 200)*15 + (ms>>10), ms>>3); fftResult[i] = inoise8(beatsin8(90 / (i+1), 0, 200)*15 + (ms>>10), ms>>3);
volumeSmth = fftResult[8]; volumeSmth = fftResult[8];
@ -512,7 +512,7 @@ um_data_t* simulateSound(uint8_t simulationId)
for (int i = 0; i<16; i++) for (int i = 0; i<16; i++)
fftResult[i] = inoise8(beatsin8(120 / (i+1), 10, 30)*10 + (ms>>14), ms>>3); fftResult[i] = inoise8(beatsin8(120 / (i+1), 10, 30)*10 + (ms>>14), ms>>3);
volumeSmth = fftResult[8]; volumeSmth = fftResult[8];
break;*/ break;
} }
samplePeak = random8() > 250; samplePeak = random8() > 250;
@ -573,3 +573,17 @@ void enumerateLedmaps() {
} }
} }
/*
* Returns a new, random color wheel index with a minimum distance of 42 from pos.
*/
uint8_t get_random_wheel_index(uint8_t pos) {
uint8_t r = 0, x = 0, y = 0, d = 0;
while (d < 42) {
r = random8();
x = abs(pos - r);
y = 255 - x;
d = MIN(x, y);
}
return r;
}

View File

@ -351,6 +351,7 @@ WLED_GLOBAL byte nightlightTargetBri _INIT(0); // brightness after nightlig
WLED_GLOBAL byte nightlightDelayMins _INIT(60); WLED_GLOBAL byte nightlightDelayMins _INIT(60);
WLED_GLOBAL byte nightlightMode _INIT(NL_MODE_FADE); // See const.h for available modes. Was nightlightFade WLED_GLOBAL byte nightlightMode _INIT(NL_MODE_FADE); // See const.h for available modes. Was nightlightFade
WLED_GLOBAL bool fadeTransition _INIT(true); // enable crossfading color transition WLED_GLOBAL bool fadeTransition _INIT(true); // enable crossfading color transition
WLED_GLOBAL bool modeBlending _INIT(true); // enable effect blending
WLED_GLOBAL uint16_t transitionDelay _INIT(750); // default crossfade duration in ms WLED_GLOBAL uint16_t transitionDelay _INIT(750); // default crossfade duration in ms
WLED_GLOBAL byte briMultiplier _INIT(100); // % of brightness to set (to limit power, if you set it to 50 and set bri to 255, actual brightness will be 127) WLED_GLOBAL byte briMultiplier _INIT(100); // % of brightness to set (to limit power, if you set it to 50 and set bri to 255, actual brightness will be 127)

View File

@ -40,7 +40,7 @@ bool isIp(String str) {
void handleUpload(AsyncWebServerRequest *request, const String& filename, size_t index, uint8_t *data, size_t len, bool final) { void handleUpload(AsyncWebServerRequest *request, const String& filename, size_t index, uint8_t *data, size_t len, bool final) {
if (!correctPIN) { if (!correctPIN) {
if (final) request->send(500, "text/plain", FPSTR(s_unlock_cfg)); if (final) request->send(401, "text/plain", FPSTR(s_unlock_cfg));
return; return;
} }
if (!index) { if (!index) {
@ -86,7 +86,7 @@ void createEditHandler(bool enable) {
#endif #endif
} else { } else {
editHandler = &server.on("/edit", HTTP_ANY, [](AsyncWebServerRequest *request){ editHandler = &server.on("/edit", HTTP_ANY, [](AsyncWebServerRequest *request){
serveMessage(request, 500, "Access Denied", FPSTR(s_unlock_cfg), 254); serveMessage(request, 401, "Access Denied", FPSTR(s_unlock_cfg), 254);
}); });
} }
} }
@ -201,7 +201,7 @@ void initServer()
verboseResponse = deserializeState(root); verboseResponse = deserializeState(root);
} else { } else {
if (!correctPIN && strlen(settingsPIN)>0) { if (!correctPIN && strlen(settingsPIN)>0) {
request->send(403, "application/json", F("{\"error\":1}")); // ERR_DENIED request->send(401, "application/json", F("{\"error\":1}")); // ERR_DENIED
releaseJSONBufferLock(); releaseJSONBufferLock();
return; return;
} }
@ -211,6 +211,8 @@ void initServer()
if (verboseResponse) { if (verboseResponse) {
if (!isConfig) { if (!isConfig) {
lastInterfaceUpdate = millis(); // prevent WS update until cooldown
interfaceUpdateCallMode = CALL_MODE_WS_SEND; // schedule WS update
serveJson(request); return; //if JSON contains "v" serveJson(request); return; //if JSON contains "v"
} else { } else {
doSerializeConfig = true; //serializeConfig(); //Save new settings to FS doSerializeConfig = true; //serializeConfig(); //Save new settings to FS
@ -282,7 +284,7 @@ void initServer()
//init ota page //init ota page
server.on("/update", HTTP_GET, [](AsyncWebServerRequest *request){ server.on("/update", HTTP_GET, [](AsyncWebServerRequest *request){
if (otaLock) { if (otaLock) {
serveMessage(request, 500, "Access Denied", FPSTR(s_unlock_ota), 254); serveMessage(request, 401, "Access Denied", FPSTR(s_unlock_ota), 254);
} else } else
serveSettings(request); // checks for "upd" in URL and handles PIN serveSettings(request); // checks for "upd" in URL and handles PIN
}); });
@ -292,7 +294,11 @@ void initServer()
serveSettings(request, true); // handle PIN page POST request serveSettings(request, true); // handle PIN page POST request
return; return;
} }
if (Update.hasError() || otaLock) { if (otaLock) {
serveMessage(request, 401, "Access Denied", FPSTR(s_unlock_ota), 254);
return;
}
if (Update.hasError()) {
serveMessage(request, 500, F("Update failed!"), F("Please check your file and retry!"), 254); serveMessage(request, 500, F("Update failed!"), F("Please check your file and retry!"), 254);
} else { } else {
serveMessage(request, 200, F("Update successful!"), F("Rebooting..."), 131); serveMessage(request, 200, F("Update successful!"), F("Rebooting..."), 131);
@ -533,7 +539,7 @@ void serveSettingsJS(AsyncWebServerRequest* request)
} }
if (subPage > 0 && !correctPIN && strlen(settingsPIN)>0) { if (subPage > 0 && !correctPIN && strlen(settingsPIN)>0) {
strcpy_P(buf, PSTR("alert('PIN incorrect.');")); strcpy_P(buf, PSTR("alert('PIN incorrect.');"));
request->send(403, "application/javascript", buf); request->send(401, "application/javascript", buf);
return; return;
} }
strcat_P(buf,PSTR("function GetV(){var d=document;")); strcat_P(buf,PSTR("function GetV(){var d=document;"));
@ -575,7 +581,7 @@ void serveSettings(AsyncWebServerRequest* request, bool post)
// if OTA locked or too frequent PIN entry requests fail hard // if OTA locked or too frequent PIN entry requests fail hard
if ((subPage == SUBPAGE_WIFI && wifiLock && otaLock) || (post && !correctPIN && millis()-lastEditTime < PIN_RETRY_COOLDOWN)) if ((subPage == SUBPAGE_WIFI && wifiLock && otaLock) || (post && !correctPIN && millis()-lastEditTime < PIN_RETRY_COOLDOWN))
{ {
serveMessage(request, 500, "Access Denied", FPSTR(s_unlock_ota), 254); return; serveMessage(request, 401, "Access Denied", FPSTR(s_unlock_ota), 254); return;
} }
if (post) { //settings/set POST request, saving if (post) { //settings/set POST request, saving
@ -605,7 +611,7 @@ void serveSettings(AsyncWebServerRequest* request, bool post)
if (!s2[0]) strcpy_P(s2, s_redirecting); if (!s2[0]) strcpy_P(s2, s_redirecting);
bool redirectAfter9s = (subPage == SUBPAGE_WIFI || ((subPage == SUBPAGE_SEC || subPage == SUBPAGE_UM) && doReboot)); bool redirectAfter9s = (subPage == SUBPAGE_WIFI || ((subPage == SUBPAGE_SEC || subPage == SUBPAGE_UM) && doReboot));
serveMessage(request, 200, s, s2, redirectAfter9s ? 129 : (correctPIN ? 1 : 3)); serveMessage(request, (correctPIN ? 200 : 401), s, s2, redirectAfter9s ? 129 : (correctPIN ? 1 : 3));
return; return;
} }
} }
@ -633,7 +639,7 @@ void serveSettings(AsyncWebServerRequest* request, bool post)
serveMessage(request, 200, strlen(settingsPIN) > 0 ? PSTR("Settings locked") : PSTR("No PIN set"), FPSTR(s_redirecting), 1); serveMessage(request, 200, strlen(settingsPIN) > 0 ? PSTR("Settings locked") : PSTR("No PIN set"), FPSTR(s_redirecting), 1);
return; return;
} }
case SUBPAGE_PINREQ : response = request->beginResponse_P(200, "text/html", PAGE_settings_pin, PAGE_settings_pin_length); break; case SUBPAGE_PINREQ : response = request->beginResponse_P(401, "text/html", PAGE_settings_pin, PAGE_settings_pin_length); break;
case SUBPAGE_CSS : response = request->beginResponse_P(200, "text/css", PAGE_settingsCss, PAGE_settingsCss_length); break; case SUBPAGE_CSS : response = request->beginResponse_P(200, "text/css", PAGE_settingsCss, PAGE_settingsCss_length); break;
case SUBPAGE_JS : serveSettingsJS(request); return; case SUBPAGE_JS : serveSettingsJS(request); return;
case SUBPAGE_WELCOME : response = request->beginResponse_P(200, "text/html", PAGE_welcome, PAGE_welcome_length); break; case SUBPAGE_WELCOME : response = request->beginResponse_P(200, "text/html", PAGE_welcome, PAGE_welcome_length); break;

View File

@ -238,8 +238,8 @@ void getSettingsJS(byte subPage, char* dest)
if (subPage == SUBPAGE_MENU) if (subPage == SUBPAGE_MENU)
{ {
#ifndef WLED_DISABLE_2D // include only if 2D is compiled in #ifdef WLED_DISABLE_2D // include only if 2D is not compiled in
oappend(PSTR("gId('2dbtn').style.display='';")); oappend(PSTR("gId('2dbtn').style.display='none';"));
#endif #endif
#ifdef WLED_ENABLE_DMX // include only if DMX is enabled #ifdef WLED_ENABLE_DMX // include only if DMX is enabled
oappend(PSTR("gId('dmxbtn').style.display='';")); oappend(PSTR("gId('dmxbtn').style.display='';"));
@ -441,6 +441,7 @@ void getSettingsJS(byte subPage, char* dest)
sappend('c',SET_F("GC"),gammaCorrectCol); sappend('c',SET_F("GC"),gammaCorrectCol);
dtostrf(gammaCorrectVal,3,1,nS); sappends('s',SET_F("GV"),nS); dtostrf(gammaCorrectVal,3,1,nS); sappends('s',SET_F("GV"),nS);
sappend('c',SET_F("TF"),fadeTransition); sappend('c',SET_F("TF"),fadeTransition);
sappend('c',SET_F("EB"),modeBlending);
sappend('v',SET_F("TD"),transitionDelayDefault); sappend('v',SET_F("TD"),transitionDelayDefault);
sappend('c',SET_F("PF"),strip.paletteFade); sappend('c',SET_F("PF"),strip.paletteFade);
sappend('v',SET_F("TP"),randomPaletteChangeTime); sappend('v',SET_F("TP"),randomPaletteChangeTime);
@ -478,7 +479,7 @@ void getSettingsJS(byte subPage, char* dest)
if (subPage == SUBPAGE_SYNC) if (subPage == SUBPAGE_SYNC)
{ {
char nS[32]; [[maybe_unused]] char nS[32];
sappend('v',SET_F("UP"),udpPort); sappend('v',SET_F("UP"),udpPort);
sappend('v',SET_F("U2"),udpPort2); sappend('v',SET_F("U2"),udpPort2);
sappend('v',SET_F("GS"),syncGroups); sappend('v',SET_F("GS"),syncGroups);