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",
"version": "0.14.0",
"version": "0.14.1-a1",
"lockfileVersion": 1,
"requires": true,
"dependencies": {

View File

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

View File

@ -217,7 +217,7 @@ build_flags =
; restrict to minimal mime-types
-DMIMETYPE_MINIMAL
; 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
lib_deps =

View File

@ -52,9 +52,15 @@ class PWMFanUsermod : public Usermod {
uint8_t tachoUpdateSec = 30;
float targetTemperature = 35.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 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)
static const char _name[];
static const char _enabled[];
@ -63,6 +69,7 @@ class PWMFanUsermod : public Usermod {
static const char _temperature[];
static const char _tachoUpdateSec[];
static const char _minPWMValuePct[];
static const char _maxPWMValuePct[];
static const char _IRQperRotation[];
static const char _speed[];
static const char _lock[];
@ -156,31 +163,25 @@ class PWMFanUsermod : public Usermod {
void setFanPWMbasedOnTemperature(void) {
float temp = getActualTemperature();
float difftemp = temp - targetTemperature;
// Default to run fan at full speed.
int newPWMvalue = 255;
int pwmStep = ((100 - minPWMValuePct) * newPWMvalue) / (7*100);
int pwmMinimumValue = (minPWMValuePct * newPWMvalue) / 100;
// dividing minPercent and maxPercent into equal pwmvalue sizes
int pwmStepSize = ((maxPWMValuePct - minPWMValuePct) * _pwmMaxValue) / (_pwmMaxStepCount*100);
int pwmStep = calculatePwmStep(temp - targetTemperature);
// minimum based on full speed - not entered MaxPercent
int pwmMinimumValue = (minPWMValuePct * _pwmMaxValue) / 100;
updateFanSpeed(pwmMinimumValue + pwmStep*pwmStepSize);
}
if ((temp == NAN) || (temp <= -100.0)) {
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."));
} 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;
return _pwmMaxStepCount;
}
updateFanSpeed(newPWMvalue);
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:
@ -312,6 +313,7 @@ class PWMFanUsermod : public Usermod {
top[FPSTR(_tachoUpdateSec)] = tachoUpdateSec;
top[FPSTR(_temperature)] = targetTemperature;
top[FPSTR(_minPWMValuePct)] = minPWMValuePct;
top[FPSTR(_maxPWMValuePct)] = maxPWMValuePct;
top[FPSTR(_IRQperRotation)] = numberOfInterrupsInOneSingleRotation;
DEBUG_PRINTLN(F("Autosave config saved."));
}
@ -345,6 +347,8 @@ class PWMFanUsermod : public Usermod {
targetTemperature = top[FPSTR(_temperature)] | targetTemperature;
minPWMValuePct = top[FPSTR(_minPWMValuePct)] | minPWMValuePct;
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 = (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::_tachoUpdateSec[] PROGMEM = "tacho-update-s";
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::_speed[] PROGMEM = "speed";
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
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
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 umSSDRInverted = false;
bool umSSDRColonblink = true;
bool umSSDRLeadingZero = false;
bool umSSDREnableLDR = false;
String umSSDRHours = "";
String umSSDRMinutes = "";
@ -79,6 +80,7 @@ private:
static const char _str_timeEnabled[];
static const char _str_inverted[];
static const char _str_colonblink[];
static const char _str_leadingZero[];
static const char _str_displayMask[];
static const char _str_hours[];
static const char _str_minutes[];
@ -105,15 +107,15 @@ private:
switch (umSSDRDisplayMask[index]) {
case 'h':
timeVar = hourFormat12(localTime);
_showElements(&umSSDRHours, timeVar, 0, 1);
_showElements(&umSSDRHours, timeVar, 0, !umSSDRLeadingZero);
break;
case 'H':
timeVar = hour(localTime);
_showElements(&umSSDRHours, timeVar, 0, 1);
_showElements(&umSSDRHours, timeVar, 0, !umSSDRLeadingZero);
break;
case 'k':
timeVar = hour(localTime) + 1;
_showElements(&umSSDRHours, timeVar, 0, 0);
_showElements(&umSSDRHours, timeVar, 0, !umSSDRLeadingZero);
break;
case 'm':
timeVar = minute(localTime);
@ -309,6 +311,9 @@ private:
if (_cmpIntSetting_P(topic, payload, _str_colonblink, &umSSDRColonblink)) {
return true;
}
if (_cmpIntSetting_P(topic, payload, _str_leadingZero, &umSSDRLeadingZero)) {
return true;
}
if (strcmp_P(topic, _str_displayMask) == 0) {
umSSDRDisplayMask = String(payload);
_publishMQTTstr_P(_str_displayMask, umSSDRDisplayMask);
@ -323,6 +328,7 @@ private:
_publishMQTTint_P(_str_ldrEnabled, umSSDREnableLDR);
_publishMQTTint_P(_str_inverted, umSSDRInverted);
_publishMQTTint_P(_str_colonblink, umSSDRColonblink);
_publishMQTTint_P(_str_leadingZero, umSSDRLeadingZero);
_publishMQTTstr_P(_str_hours, umSSDRHours);
_publishMQTTstr_P(_str_minutes, umSSDRMinutes);
@ -347,6 +353,7 @@ private:
ssdrObj[FPSTR(_str_ldrEnabled)] = umSSDREnableLDR;
ssdrObj[FPSTR(_str_inverted)] = umSSDRInverted;
ssdrObj[FPSTR(_str_colonblink)] = umSSDRColonblink;
ssdrObj[FPSTR(_str_leadingZero)] = umSSDRLeadingZero;
ssdrObj[FPSTR(_str_displayMask)] = umSSDRDisplayMask;
ssdrObj[FPSTR(_str_hours)] = umSSDRHours;
ssdrObj[FPSTR(_str_minutes)] = umSSDRMinutes;
@ -425,6 +432,8 @@ public:
invert.add(umSSDRInverted);
JsonArray blink = user.createNestedArray("Blinking colon");
blink.add(umSSDRColonblink);
JsonArray zero = user.createNestedArray("Show the hour leading zero");
zero.add(umSSDRLeadingZero);
JsonArray ldrEnable = user.createNestedArray("Auto Brightness enabled");
ldrEnable.add(umSSDREnableLDR);
@ -454,6 +463,7 @@ public:
umSSDREnableLDR = ssdrObj[FPSTR(_str_ldrEnabled)] | umSSDREnableLDR;
umSSDRInverted = ssdrObj[FPSTR(_str_inverted)] | umSSDRInverted;
umSSDRColonblink = ssdrObj[FPSTR(_str_colonblink)] | umSSDRColonblink;
umSSDRLeadingZero = ssdrObj[FPSTR(_str_leadingZero)] | umSSDRLeadingZero;
umSSDRDisplayMask = ssdrObj[FPSTR(_str_displayMask)] | umSSDRDisplayMask;
}
}
@ -516,6 +526,7 @@ public:
umSSDREnableLDR = (top[FPSTR(_str_ldrEnabled)] | umSSDREnableLDR);
umSSDRInverted = (top[FPSTR(_str_inverted)] | umSSDRInverted);
umSSDRColonblink = (top[FPSTR(_str_colonblink)] | umSSDRColonblink);
umSSDRLeadingZero = (top[FPSTR(_str_leadingZero)] | umSSDRLeadingZero);
umSSDRDisplayMask = top[FPSTR(_str_displayMask)] | umSSDRDisplayMask;
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_inverted[] PROGMEM = "inverted";
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_hours[] PROGMEM = "LED-Numbers-Hours";
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;
}
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;
}
if (SEGENV.step == 3) {
SEGENV.aux0 = SEGMENT.get_random_wheel_index(SEGENV.aux1);
SEGENV.aux0 = get_random_wheel_index(SEGENV.aux1);
SEGENV.step = 0;
}
}
@ -271,7 +271,7 @@ uint16_t mode_random_color(void) {
if (it != SEGENV.step) //new color
{
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;
}
@ -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
{
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);
}
@ -1056,7 +1056,7 @@ uint16_t mode_chase_flash_random(void) {
SEGENV.aux1 = (SEGENV.aux1 + 1) % SEGLEN;
if (SEGENV.aux1 == 0) {
SEGENV.aux0 = SEGMENT.get_random_wheel_index(SEGENV.aux0);
SEGENV.aux0 = get_random_wheel_index(SEGENV.aux0);
}
}
return delay;
@ -2590,14 +2590,14 @@ uint16_t mode_twinklefox()
{
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()
{
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
@ -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("#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("#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);

View File

@ -109,20 +109,15 @@
#define PINK (uint32_t)0xFF1493
#define ULTRAWHITE (uint32_t)0xFFFFFFFF
#define DARKSLATEGRAY (uint32_t)0x2F4F4F
#define DARKSLATEGREY (uint32_t)0x2F4F4F
#define DARKSLATEGREY DARKSLATEGRAY
// 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
// segment options
#define NO_OPTIONS (uint16_t)0x0000
#define TRANSPOSED (uint16_t)0x0400 // rotated 90deg & reversed
#define REVERSE_Y_2D (uint16_t)0x0200
#define MIRROR_Y_2D (uint16_t)0x0100
#define TRANSITIONAL (uint16_t)0x0080
#define TRANSPOSED (uint16_t)0x0100 // rotated 90deg & reversed
#define MIRROR_Y_2D (uint16_t)0x0080
#define REVERSE_Y_2D (uint16_t)0x0040
#define RESET_REQ (uint16_t)0x0020
#define FROZEN (uint16_t)0x0010
#define MIRROR (uint16_t)0x0008
#define SEGMENT_ON (uint16_t)0x0004
#define REVERSE (uint16_t)0x0002
@ -348,12 +343,11 @@ typedef struct Segment {
bool mirror : 1; // 3 : mirrored
bool freeze : 1; // 4 : paused/frozen
bool reset : 1; // 5 : indicates that Segment runtime requires reset
bool transitional: 1; // 6 : transitional (there is transition occuring)
bool reverse_y : 1; // 7 : reversed Y (2D)
bool mirror_y : 1; // 8 : mirrored Y (2D)
bool transpose : 1; // 9 : transposed (2D, swapped X & Y)
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 : 1; // 13 : 0-1 sound simulation types ("soft" & "hard" or "on"/"off")
bool reverse_y : 1; // 6 : reversed Y (2D)
bool mirror_y : 1; // 7 : mirrored Y (2D)
bool transpose : 1; // 8 : 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 soundSim : 2; // 12-13 : 0-3 sound simulation types ("soft" & "hard" or "on"/"off")
uint8_t set : 2; // 14-15 : 0-3 UI segment sets/groups
};
};
@ -484,7 +478,6 @@ typedef struct Segment {
_dataLen(0),
_t(nullptr)
{
//refreshLightCapabilities();
#ifdef WLED_DEBUG
//Serial.printf("-- Creating segment: %p\n", this);
#endif
@ -519,6 +512,7 @@ typedef struct Segment {
inline bool getOption(uint8_t n) const { return ((options >> n) & 0x01); }
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 is2D(void) const { return (width()>1 && height()>1); }
inline bool hasRGB(void) const { return _isRGB; }
@ -569,15 +563,16 @@ typedef struct Segment {
void restoreSegenv(tmpsegd_t &tmpSegD);
#endif
uint16_t progress(void); //transition progression between 0-65535
uint8_t currentBri(uint8_t briNew, bool useCct = false);
uint8_t currentMode(uint8_t modeNew);
uint32_t currentColor(uint8_t slot, uint32_t colorNew);
uint8_t currentBri(bool useCct = false);
uint8_t currentMode(void);
uint32_t currentColor(uint8_t slot);
CRGBPalette16 &loadPalette(CRGBPalette16 &tgt, uint8_t pal);
CRGBPalette16 &currentPalette(CRGBPalette16 &tgt, uint8_t paletteID);
// 1D strip
uint16_t virtualLength(void) const;
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, CRGB c) { setPixelColor(n, RGBW32(c.r,c.g,c.b,0)); } // automatically inline
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, 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);
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_wheel(uint8_t pos);
@ -606,6 +600,7 @@ typedef struct Segment {
#ifndef WLED_DISABLE_2D
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(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, 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);
@ -881,16 +876,14 @@ class WS2812FX { // 96 bytes
std::vector<Panel> panel;
#endif
void
setUpMatrix(),
setPixelColorXY(int x, int y, uint32_t c);
void setUpMatrix();
// 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, CRGB c) { setPixelColorXY(x, y, RGBW32(c.r,c.g,c.b,0)); }
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)); }
uint32_t
getPixelColorXY(uint16_t, uint16_t);
inline uint32_t getPixelColorXY(uint16_t x, uint16_t y) { return getPixelColor(isMatrix ? y * Segment::maxWidth + x : x);}
// end 2D support

View File

@ -134,7 +134,7 @@ void WS2812FX::setUpMatrix() {
#ifdef WLED_DEBUG
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();
DEBUG_PRINTF("%4d,", customMappingTable[i]);
}
@ -155,31 +155,6 @@ void WS2812FX::setUpMatrix() {
#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
@ -188,18 +163,19 @@ uint32_t WS2812FX::getPixelColorXY(uint16_t x, uint16_t y) {
#ifndef WLED_DISABLE_2D
// 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 height = virtualHeight(); // segment height in logical pixels (is always >= 1)
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 (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) {
byte r = scale8(R(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) {
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
uint32_t col = getPixelColorXY(x,y);
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);
setPixelColorXY(x, y, color_add(getPixelColorXY(x,y), color, fast));
}
void Segment::fadePixelColorXY(uint16_t x, uint16_t y, uint8_t fade) {
if (!isActive()) return; // not active
CRGB pix = CRGB(getPixelColorXY(x,y)).nscale8_video(fade);
setPixelColorXY(x, y, pix);
setPixelColorXY(x, y, color_fade(getPixelColorXY(x,y), fade, true));
}
// blurRow: perform a blur on a row of a rectangular matrix
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 rows = virtualHeight();
@ -344,7 +305,7 @@ void Segment::blurRow(uint16_t row, fract8 blur_amount) {
uint8_t keep = 255 - blur_amount;
uint8_t seep = blur_amount >> 1;
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 before = cur; // remember color before blur
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
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 rows = virtualHeight();
@ -372,7 +333,7 @@ void Segment::blurCol(uint16_t col, fract8 blur_amount) {
uint8_t keep = 255 - blur_amount;
uint8_t seep = blur_amount >> 1;
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 part = cur;
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])
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 rows = virtualHeight();
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;
// 1D box blur
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 y = vertical ? j : i;
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;
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 y = vertical ? j : i;
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) {
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) {
@ -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) {
if (!isActive()) return; // not active
if (!isActive() || radius == 0) return; // not active
// Bresenhams Algorithm
int d = 3 - (2*radius);
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
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 rows = virtualHeight();
for (int16_t y = -radius; y <= radius; y++) {
@ -540,7 +501,7 @@ void Segment::nscale8(uint8_t scale) {
if (!isActive()) return; // not active
const uint16_t cols = virtualWidth();
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));
}
}

View File

@ -89,25 +89,19 @@ bool Segment::_modeBlend = false;
Segment::Segment(const Segment &orig) {
//DEBUG_PRINTF("-- Copy segment constructor: %p -> %p\n", &orig, this);
memcpy((void*)this, (void*)&orig, sizeof(Segment));
transitional = false; // copied segment cannot be in transition
name = nullptr;
data = nullptr;
_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); }
_t = nullptr; // copied segment cannot be in transition
if (orig.name) { name = new char[strlen(orig.name)+1]; if (name) strcpy(name, orig.name); } else { name = nullptr; }
if (orig.data) { if (allocateData(orig._dataLen)) memcpy(data, orig.data, orig._dataLen); } else { data = nullptr; _dataLen = 0; }
}
// move constructor
Segment::Segment(Segment &&orig) noexcept {
//DEBUG_PRINTF("-- Move segment constructor: %p -> %p\n", &orig, this);
memcpy((void*)this, (void*)&orig, sizeof(Segment));
orig.transitional = false; // old segment cannot be in transition any more
orig.name = nullptr;
orig.data = nullptr;
orig._dataLen = 0;
orig._t = nullptr;
orig._t = nullptr; // old segment cannot be in transition any more
}
// copy assignment
@ -115,27 +109,23 @@ Segment& Segment::operator= (const Segment &orig) {
//DEBUG_PRINTF("-- Copying segment: %p -> %p\n", &orig, this);
if (this != &orig) {
// clean destination
transitional = false; // copied segment cannot be in transition
if (name) delete[] name;
if (name) { delete[] name; name = nullptr; }
if (orig.name) { name = new char[strlen(orig.name)+1]; if (name) strcpy(name, orig.name); }
if (_t) {
#ifndef WLED_DISABLE_MODE_BLEND
if (_t->_segT._dataT) free(_t->_segT._dataT);
#endif
delete _t;
_t = nullptr; // copied segment cannot be in transition
}
deallocateData();
// copy source
memcpy((void*)this, (void*)&orig, sizeof(Segment));
transitional = false;
// erase pointers to allocated data
name = nullptr;
data = nullptr;
_dataLen = 0;
_t = nullptr;
// 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._t) { _t = new Transition(orig._t->_dur, orig._t->_briT, orig._t->_cctT, orig._t->_colorT); }
}
return *this;
}
@ -144,9 +134,7 @@ Segment& Segment::operator= (const Segment &orig) {
Segment& Segment::operator= (Segment &&orig) noexcept {
//DEBUG_PRINTF("-- Moving segment: %p -> %p\n", &orig, this);
if (this != &orig) {
transitional = false; // just temporary
if (name) { delete[] name; name = nullptr; } // free old name
deallocateData(); // free old runtime data
if (_t) {
#ifndef WLED_DISABLE_MODE_BLEND
if (_t->_segT._dataT) free(_t->_segT._dataT);
@ -154,12 +142,12 @@ Segment& Segment::operator= (Segment &&orig) noexcept {
delete _t;
_t = nullptr;
}
deallocateData(); // free old runtime data
memcpy((void*)this, (void*)&orig, sizeof(Segment));
orig.transitional = false; // old segment cannot be in transition
orig.name = nullptr;
orig.data = nullptr;
orig._dataLen = 0;
orig._t = nullptr;
orig._t = nullptr; // old segment cannot be in transition
}
return *this;
}
@ -237,7 +225,7 @@ CRGBPalette16 &Segment::loadPalette(CRGBPalette16 &targetPalette, uint8_t pal) {
switch (pal) {
case 0: //default palette. Exceptions for specific effects above
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;
if (timeSinceLastChange > randomPaletteChangeTime * 1000U) {
_randomPalette = _newRandomPalette;
@ -301,47 +289,45 @@ CRGBPalette16 &Segment::loadPalette(CRGBPalette16 &targetPalette, uint8_t pal) {
}
void Segment::startTransition(uint16_t dur) {
if (!dur) {
if (_t) _t->_dur = dur; // this will stop transition in next handleTransisiton()
else transitional = false;
if (dur == 0) {
if (isInTransition()) _t->_dur = dur; // this will stop transition in next handleTransisiton()
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
_t = new Transition(dur); // no previous transition running
if (!_t) return; // failed to allocate data
//DEBUG_PRINTF("-- Started transition: %p\n", this);
CRGBPalette16 _palT = CRGBPalette16(DEFAULT_COLOR); loadPalette(_palT, palette);
_t->_palT = _palT;
loadPalette(_t->_palT, palette);
_t->_briT = on ? opacity : 0;
_t->_cctT = cct;
#ifndef WLED_DISABLE_MODE_BLEND
swapSegenv(_t->_segT);
_t->_modeT = mode;
_t->_segT._optionsT |= 0b0000000001000000; // mark old segment transitional
_t->_segT._dataLenT = 0;
_t->_segT._dataT = nullptr;
if (_dataLen > 0 && data) {
_t->_segT._dataT = (byte *)malloc(_dataLen);
if (_t->_segT._dataT) {
//DEBUG_PRINTF("-- Allocated duplicate data (%d): %p\n", _dataLen, _t->_segT._dataT);
memcpy(_t->_segT._dataT, data, _dataLen);
_t->_segT._dataLenT = _dataLen;
if (modeBlending) {
swapSegenv(_t->_segT);
_t->_modeT = mode;
_t->_segT._dataLenT = 0;
_t->_segT._dataT = nullptr;
if (_dataLen > 0 && data) {
_t->_segT._dataT = (byte *)malloc(_dataLen);
if (_t->_segT._dataT) {
//DEBUG_PRINTF("-- Allocated duplicate data (%d): %p\n", _dataLen, _t->_segT._dataT);
memcpy(_t->_segT._dataT, data, _dataLen);
_t->_segT._dataLenT = _dataLen;
}
}
} else {
for (size_t i=0; i<NUM_COLORS; i++) _t->_segT._colorT[i] = colors[i];
}
#else
for (size_t i=0; i<NUM_COLORS; i++) _t->_colorT[i] = colors[i];
#endif
transitional = true; // setOption(SEG_OPTION_TRANSITIONAL, true);
}
void Segment::stopTransition() {
if (!transitional) return;
transitional = false; // finish transitioning segment
//DEBUG_PRINTF("-- Stopping transition: %p\n", this);
if (_t) {
if (isInTransition()) {
#ifndef WLED_DISABLE_MODE_BLEND
if (_t->_segT._dataT && _t->_segT._dataLenT > 0) {
//DEBUG_PRINTF("-- Released duplicate data (%d): %p\n", _t->_segT._dataLenT, _t->_segT._dataT);
@ -356,14 +342,13 @@ void Segment::stopTransition() {
}
void Segment::handleTransition() {
if (!transitional) return;
uint16_t _progress = progress();
if (_progress == 0xFFFFU) stopTransition();
}
// transition progression between 0-65535
uint16_t Segment::progress() {
if (transitional && _t) {
if (isInTransition()) {
unsigned long timeNow = millis();
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._callT = call;
//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._dataLenT = _dataLen; // sometimes memory gets re-allocated (!! INVESTIGATE WHY !!)
_t->_segT._dataT = data;
_t->_segT._dataLenT = _dataLen;
}
options = tmpSeg._optionsT;
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
uint8_t Segment::currentBri(uint8_t briNew, bool useCct) {
uint8_t Segment::currentBri(bool useCct) {
uint32_t prog = progress();
if (prog < 0xFFFFU) {
if (useCct) return ((briNew * prog) + _t->_cctT * (0xFFFFU - prog)) >> 16;
else return ((briNew * prog) + _t->_briT * (0xFFFFU - prog)) >> 16;
uint32_t curBri = (useCct ? cct : (on ? opacity : 0)) * prog;
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
uint16_t prog = progress(); // implicit check for transitional & _t in progress()
if (prog < 0xFFFFU) return _t->_modeT;
uint16_t prog = progress();
if (modeBlending && prog < 0xFFFFU) return _t->_modeT;
#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
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
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
}
CRGBPalette16 &Segment::currentPalette(CRGBPalette16 &targetPalette, uint8_t pal) {
loadPalette(targetPalette, pal);
if (progress() < 0xFFFFU) {
uint16_t prog = progress();
if (strip.paletteFade && prog < 0xFFFFU) {
// blend palettes
// 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
unsigned long timeMS = millis() - _t->_start;
uint16_t noOfBlends = (255U * timeMS / _t->_dur) - _t->_prevPaletteBlends;
uint16_t noOfBlends = ((255U * prog) / 0xFFFFU) - _t->_prevPaletteBlends;
for (int i=0; i<noOfBlends; i++, _t->_prevPaletteBlends++) nblendPaletteTowardPalette(_t->_palT, targetPalette, 48);
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))
&& (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 (grp) { // prevent assignment of 0
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;
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();
if (boundsUnchanged) return;
@ -564,7 +556,6 @@ void Segment::setCCT(uint16_t k) {
void Segment::setOpacity(uint8_t o) {
if (opacity == o) return;
if (fadeTransition) startTransition(strip.getTransition()); // start transition prior to change
DEBUG_PRINT(F("-- Setting opacity: ")); DEBUG_PRINTLN(o);
opacity = o;
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 (val) 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) {
// if we have a valid mode & is not reserved
if (fx < strip.getModeCount() && strncmp_P("RSVD", strip.getModeData(fx), 4)) {
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;
// load default values from effect string
if (loadDefaults) {
@ -743,7 +736,7 @@ void IRAM_ATTR Segment::setPixelColor(int i, uint32_t col)
#endif
uint16_t len = length();
uint8_t _bri_t = currentBri(on ? opacity : 0);
uint8_t _bri_t = currentBri();
if (_bri_t < 255) {
byte r = scale8(R(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 (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]
if ((options & 0b1111111110011110U) != (b.options & 0b1111111110011110U)) d |= SEG_DIFFERS_OPT;
//bit pattern: (msb first)
// 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;
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;
}
@ -912,7 +906,7 @@ void Segment::refreshLightCapabilities() {
segStopIdx = stop;
}
for (uint8_t b = 0; b < busses.getNumBusses(); b++) {
for (unsigned b = 0; b < busses.getNumBusses(); b++) {
Bus *bus = busses.getBus(b);
if (bus == nullptr || bus->getLength()==0) break;
if (!bus->isOk()) continue;
@ -942,7 +936,7 @@ void Segment::fill(uint32_t c) {
if (!isActive()) return; // not active
const uint16_t cols = is2D() ? virtualWidth() : virtualLength();
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);
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.
void Segment::addPixelColor(int n, uint32_t color, bool fast) {
if (!isActive()) return; // not active
uint32_t col = getPixelColor(n);
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);
setPixelColor(n, color_add(getPixelColor(n), color, fast));
}
void Segment::fadePixelColor(uint16_t n, uint8_t fade) {
if (!isActive()) return; // not active
CRGB pix = CRGB(getPixelColor(n)).nscale8_video(fade);
setPixelColor(n, pix);
setPixelColor(n, color_fade(getPixelColor(n), fade, true));
}
/*
@ -996,7 +975,7 @@ void Segment::fade_out(uint8_t rate) {
int g2 = G(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);
int w1 = W(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 rows = virtualHeight(); // will be 1 for 1D
for (uint16_t y = 0; y < rows; y++) for (uint16_t x = 0; x < cols; x++) {
if (is2D()) setPixelColorXY(x, y, CRGB(getPixelColorXY(x,y)).nscale8(255-fadeBy));
else setPixelColor(x, CRGB(getPixelColor(x)).nscale8(255-fadeBy));
for (int y = 0; y < rows; y++) for (int x = 0; x < cols; x++) {
if (is2D()) setPixelColorXY(x, y, color_fade(getPixelColorXY(x,y), 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
if (is2D()) {
// compatibility with 2D
const uint_fast16_t cols = virtualWidth();
const uint_fast16_t rows = virtualHeight();
for (uint_fast16_t 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
const unsigned cols = virtualWidth();
const unsigned rows = virtualHeight();
for (unsigned i = 0; i < rows; i++) blurRow(i, blur_amount); // blur all rows
for (unsigned k = 0; k < cols; k++) blurCol(k, blur_amount); // blur all columns
return;
}
#endif
uint8_t keep = 255 - blur_amount;
uint8_t seep = blur_amount >> 1;
CRGB carryover = CRGB::Black;
uint_fast16_t vlength = virtualLength();
for(uint_fast16_t i = 0; i < vlength; i++)
{
CRGB cur = CRGB(getPixelColor(i));
CRGB part = cur;
CRGB before = cur; // remember color before blur
part.nscale8(seep);
cur.nscale8(keep);
cur += carryover;
if(i > 0) {
uint32_t carryover = BLACK;
unsigned vlength = virtualLength();
for (unsigned i = 0; i < vlength; i++) {
uint32_t cur = getPixelColor(i);
uint32_t part = color_fade(cur, seep);
cur = color_add(color_fade(cur, keep), carryover, true);
if (i > 0) {
uint32_t c = getPixelColor(i-1);
uint8_t r = R(c);
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));
setPixelColor(i-1, color_add(c, part, true));
}
if (before != cur) // optimization: only set pixel if color has changed
setPixelColor((uint16_t)i,cur.red, cur.green, cur.blue);
setPixelColor(i, cur);
carryover = part;
}
}
@ -1080,7 +1051,7 @@ void Segment::blur(uint8_t blur_amount)
uint32_t Segment::color_wheel(uint8_t pos) {
if (palette) return color_from_palette(pos, false, true, 0);
pos = 255 - pos;
if(pos < 85) {
if (pos < 85) {
return ((uint32_t)(255 - pos * 3) << 16) | ((uint32_t)(0) << 8) | (pos * 3);
} else if(pos < 170) {
pos -= 85;
@ -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.
* @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
if ((palette == 0 && mcol < NUM_COLORS) || !_isRGB) {
uint32_t color = currentColor(mcol, colors[mcol]);
uint32_t color = currentColor(mcol);
color = gamma32(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;
if (mapping && virtualLength() > 1) paletteIndex = (i*255)/(virtualLength() -1);
if (!wrap) paletteIndex = scale8(paletteIndex, 240); //cut off blend at palette "end"
CRGB fastled_col;
if (!wrap && strip.paletteBlend != 3) paletteIndex = scale8(paletteIndex, 240); //cut off blend at palette "end"
CRGBPalette16 curPal;
if (transitional && _t) curPal = _t->_palT;
else loadPalette(curPal, palette);
fastled_col = ColorFromPalette(curPal, paletteIndex, pbri, (strip.paletteBlend == 3)? NOBLEND:LINEARBLEND); // NOTE: paletteBlend should be global
curPal = currentPalette(curPal, palette);
//if (isInTransition()) curPal = _t->_palT;
//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);
}
@ -1166,7 +1122,7 @@ void WS2812FX::finalizeInit(void)
const uint8_t defNumBusses = ((sizeof defDataPins) / (sizeof defDataPins[0]));
const uint8_t defNumCounts = ((sizeof defCounts) / (sizeof defCounts[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]};
uint16_t start = prevLen;
uint16_t count = defCounts[(i < defNumCounts) ? i : defNumCounts -1];
@ -1177,7 +1133,7 @@ void WS2812FX::finalizeInit(void)
}
_length = 0;
for (uint8_t i=0; i<busses.getNumBusses(); i++) {
for (int i=0; i<busses.getNumBusses(); i++) {
Bus *bus = busses.getBus(i);
if (bus == nullptr) continue;
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
_virtualSegmentLength = seg.virtualLength();
_colors_t[0] = seg.currentColor(0, seg.colors[0]);
_colors_t[1] = seg.currentColor(1, seg.colors[1]);
_colors_t[2] = seg.currentColor(2, seg.colors[2]);
seg.currentPalette(_currentPalette, seg.palette);
_colors_t[0] = seg.currentColor(0);
_colors_t[1] = seg.currentColor(1);
_colors_t[2] = seg.currentColor(2);
seg.currentPalette(_currentPalette, seg.palette); // we need to pass reference
if (!cctFromRgb || correctWB) busses.setSegmentCCT(seg.currentBri(seg.cct, true), correctWB);
for (uint8_t c = 0; c < NUM_COLORS; c++) _colors_t[c] = gamma32(_colors_t[c]);
if (!cctFromRgb || correctWB) busses.setSegmentCCT(seg.currentBri(true), correctWB);
for (int c = 0; c < NUM_COLORS; c++) _colors_t[c] = gamma32(_colors_t[c]);
// Effect blending
// 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
// 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.
[[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
#ifndef WLED_DISABLE_MODE_BLEND
if (seg.mode != tmpMode) {
if (modeBlending && seg.mode != tmpMode) {
Segment::tmpsegd_t _tmpSegData;
Segment::modeBlend(true); // set semaphore
seg.swapSegenv(_tmpSegData); // temporarily store new mode state (and swap it with transitional state)
@ -1262,7 +1218,7 @@ void WS2812FX::service() {
}
#endif
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;
@ -1399,12 +1355,12 @@ void WS2812FX::show(void) {
// or async show has a separate buffer (ESP32 RMT and I2S are ok)
if (newBri < _brightness) busses.setBrightness(_brightness);
unsigned long now = millis();
size_t diff = now - _lastShow;
unsigned long showNow = millis();
size_t diff = showNow - _lastShow;
size_t fpsCurr = 200;
if (diff > 0) fpsCurr = 1000 / diff;
_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;
_qGrouping = grouping; _qSpacing = spacing; _qOffset = offset;
_queuedChangesSegId = segId;
DEBUG_PRINT(F("Segment queued: ")); DEBUG_PRINTLN(segId);
return; // queued changes are applied immediately after effect function returns
}
_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() {
@ -1729,7 +1687,7 @@ void WS2812FX::fixInvalidSegments() {
bool WS2812FX::checkSegmentAlignment() {
bool aligned = false;
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);
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) {
if (i2 >= i)
{
for (uint16_t x = i; x <= i2; x++) setPixelColor(x, col);
} else
{
for (uint16_t x = i2; x <= i; x++) setPixelColor(x, col);
}
if (i2 < i) std::swap(i,i2);
for (unsigned x = i; x <= i2; x++) setPixelColor(x, col);
}
void WS2812FX::setTransitionMode(bool t) {
@ -1793,7 +1746,7 @@ void WS2812FX::loadCustomPalettes() {
if (readObjectFromFile(fileName, nullptr, &pDoc)) {
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 *>()) {
// we have an array of index & hex strings
size_t palSize = MIN(pal.size(), 36);
@ -1802,7 +1755,7 @@ void WS2812FX::loadCustomPalettes() {
uint8_t rgbw[] = {0,0,0,0};
tcp[ j ] = (uint8_t) pal[ i ].as<int>(); // index
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]));
}
} else {
@ -1810,13 +1763,15 @@ void WS2812FX::loadCustomPalettes() {
palSize -= palSize % 4; // make sure size is multiple of 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+1] = (uint8_t) pal[i+1].as<int>(); // R
tcp[i+2] = (uint8_t) pal[i+2].as<int>(); // G
tcp[i+3] = (uint8_t) pal[i+3].as<int>(); // B
tcp[i+1] = gamma8((uint8_t) pal[i+1].as<int>()); // R
tcp[i+2] = gamma8((uint8_t) pal[i+2].as<int>()); // G
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]));
}
}
customPalettes.push_back(targetPalette.loadDynamicGradientPalette(tcp));
} else {
DEBUG_PRINTLN(F("Wrong palette format."));
}
}
} else {
@ -1832,7 +1787,7 @@ bool WS2812FX::deserializeMap(uint8_t n) {
char fileName[32];
strcpy_P(fileName, PSTR("/ledmap"));
if (n) sprintf(fileName +7, "%d", n);
strcat(fileName, ".json");
strcat_P(fileName, PSTR(".json"));
bool isFile = WLED_FS.exists(fileName);
if (!isFile) {
@ -1866,7 +1821,7 @@ bool WS2812FX::deserializeMap(uint8_t n) {
if (!map.isNull() && map.size()) { // not an empty map
customMappingSize = map.size();
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]);
}
}

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))
{
// 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;
pinManager.deallocatePin(pin,PinOwner::Button);
}
@ -357,6 +359,7 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
JsonObject light_tr = light["tr"];
CJSON(fadeTransition, light_tr["mode"]);
CJSON(modeBlending, light_tr["fx"]);
int tdd = light_tr["dur"] | -1;
if (tdd >= 0) transitionDelay = transitionDelayDefault = tdd * 100;
CJSON(strip.paletteFade, light_tr["pal"]);
@ -827,6 +830,7 @@ void serializeConfig() {
JsonObject light_tr = light.createNestedObject("tr");
light_tr["mode"] = fadeTransition;
light_tr["fx"] = modeBlending;
light_tr["dur"] = transitionDelayDefault / 100;
light_tr["pal"] = strip.paletteFade;
light_tr[F("rpc")] = randomPaletteChangeTime;
@ -888,6 +892,7 @@ void serializeConfig() {
if_live[F("no-gc")] = arlsDisableGammaCorrection;
if_live[F("offset")] = arlsOffset;
#ifndef WLED_DISABLE_ALEXA
JsonObject if_va = interfaces.createNestedObject("va");
if_va[F("alexa")] = alexaEnabled;
@ -896,6 +901,7 @@ void serializeConfig() {
if_va_macros.add(macroAlexaOff);
if_va["p"] = alexaNumPresets;
#endif
#ifdef WLED_ENABLE_MQTT
JsonObject if_mqtt = interfaces.createNestedObject("mqtt");
@ -1033,7 +1039,7 @@ bool deserializeConfigSec() {
JsonObject ap = doc["ap"];
getStringFromJson(apPass, ap["psk"] , 65);
JsonObject interfaces = doc["if"];
[[maybe_unused]] JsonObject interfaces = doc["if"];
#ifdef WLED_ENABLE_MQTT
JsonObject if_mqtt = interfaces["mqtt"];
@ -1072,7 +1078,7 @@ void serializeConfigSec() {
JsonObject ap = doc.createNestedObject("ap");
ap["psk"] = apPass;
JsonObject interfaces = doc.createNestedObject("if");
[[maybe_unused]] JsonObject interfaces = doc.createNestedObject("if");
#ifdef WLED_ENABLE_MQTT
JsonObject if_mqtt = interfaces.createNestedObject("mqtt");
if_mqtt["psk"] = mqttPass;

View File

@ -35,23 +35,59 @@ uint32_t color_blend(uint32_t color1, uint32_t color2, uint16_t blend, bool b16)
* color add function that preserves ratio
* 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)
{
uint32_t r = R(c1) + R(c2);
uint32_t g = G(c1) + G(c2);
uint32_t b = B(c1) + B(c2);
uint32_t w = W(c1) + W(c2);
uint16_t max = r;
if (g > max) max = g;
if (b > max) max = b;
if (w > max) max = 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);
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 g = G(c1) + G(c2);
uint32_t b = B(c1) + B(c2);
uint32_t w = W(c1) + W(c2);
uint16_t max = r;
if (g > max) max = g;
if (b > max) max = b;
if (w > max) max = 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);
}
}
/*
* 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)
{
lastRandomIndex = strip.getMainSegment().get_random_wheel_index(lastRandomIndex);
lastRandomIndex = get_random_wheel_index(lastRandomIndex);
colorHStoRGB(lastRandomIndex*256,255,rgb);
}

View File

@ -166,7 +166,7 @@
#define CALL_MODE_NO_NOTIFY 5
#define CALL_MODE_FX_CHANGED 6 //no longer used
#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_ALEXA 10
#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_FREEZE 4 //Segment contents will not be refreshed
#define SEG_OPTION_RESET 5 //Segment runtime requires reset
#define SEG_OPTION_TRANSITIONAL 6
#define SEG_OPTION_REVERSED_Y 7
#define SEG_OPTION_MIRROR_Y 8
#define SEG_OPTION_TRANSPOSED 9
#define SEG_OPTION_REVERSED_Y 6
#define SEG_OPTION_MIRROR_Y 7
#define SEG_OPTION_TRANSPOSED 8
//Segment differs return byte
#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_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_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_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)

View File

@ -198,10 +198,11 @@
</label>
</div>
</div>
<div style="padding-bottom: 10px;">
<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>
</div>
</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="palettesData=null;localStorage.removeItem('wledPalx');requestJson({rmcpal:true});setTimeout(loadPalettes,250,loadPalettesData);"><i class="icons btn-icon">&#xe037;</i></button>
</div>
</div>
@ -392,6 +393,7 @@
<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>
</div>
<i id="roverstar" class="icons huge" onclick="setLor(0)">&#xe410;</i><br>
<script src="index.js"></script>
</body>

View File

@ -33,10 +33,9 @@ var hol = [
[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
[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"],
[0,6,4,1,"https://initiate.alphacoders.com/download/wallpaper/516792/images/jpg/510921363292536"], // 4th of July
[0,0,1,1,"https://initiate.alphacoders.com/download/wallpaper/1198800/images/jpg/2522807481585600"] // new year
[0,6,4,1,"https://images.alphacoders.com/516/516792.jpg"], // 4th of July
[0,0,1,1,"https://images.alphacoders.com/119/1198800.jpg"] // new year
];
function handleVisibilityChange() {if (!d.hidden && new Date () - lastUpdate > 3000) requestJson();}
@ -1209,7 +1208,7 @@ function updateUI()
if (hasRGB) {
updateTrail(gId('sliderR'));
updateTrail(gId('sliderG'));
updateTrail(gId('sliderB'));
updateTrail(gId('sliderB'));
}
if (hasWhite) updateTrail(gId('sliderW'));
@ -1514,12 +1513,15 @@ function setEffectParameters(idx)
}
// set the bottom position of selected effect (sticky) as the top of sliders div
setInterval(()=>{
function setSelectedEffectPosition() {
let top = parseInt(getComputedStyle(gId("sliders")).height);
top += 5;
let sel = d.querySelector('#fxlist .selected');
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
var cslLabel = '';
var sep = '';
@ -1700,7 +1702,7 @@ function toggleLiveview()
let wsOn = ws && ws.readyState === WebSocket.OPEN;
var lvID = "liveview";
if (isM && wsOn) {
if (isM && wsOn) {
lvID += "2D";
if (isLv) gId('klv2D').innerHTML = `<iframe id="${lvID}" src="about:blank"></iframe>`;
gId('mlv2D').style.transform = (isLv) ? "translateY(0px)":"translateY(100%)";
@ -1887,7 +1889,7 @@ function makeP(i,pl)
end: 0
};
var rep = plJson[i].repeat ? plJson[i].repeat : 0;
content =
content =
`<div id="ple${i}" style="margin-top:10px;"></div><label class="check revchkl">Shuffle
<input type="checkbox" id="pl${i}rtgl" onchange="plR(${i})" ${plJson[i].r||rep<0?"checked":""}>
<span class="checkmark"></span>

View File

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

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> %
<h3>Transitions</h3>
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>
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>

View File

@ -63,7 +63,8 @@ class NeoGammaWLEDMethod {
#define gamma32(c) NeoGammaWLEDMethod::Correct32(c)
#define gamma8(c) NeoGammaWLEDMethod::rawGamma8(c)
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]))); }
void colorHStoRGB(uint16_t hue, byte sat, byte* rgb); //hue, sat to 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);
um_data_t* simulateSound(uint8_t simulationId);
void enumerateLedmaps();
uint8_t get_random_wheel_index(uint8_t pos);
#ifdef WLED_ADD_EEPROM_SUPPORT
//wled_eeprom.cpp

View File

@ -9,7 +9,7 @@
// Autogenerated from wled00/data/cpal/cpal.htm, do not edit!!
const uint16_t PAGE_cpal_L = 4721;
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,
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,

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
bool useMdnsName = (strcmp(serverDescription, "WLED") == 0 && strlen(cmDNS) > 0);
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};
sendImprovRPCResult(ImprovRPCType::Request_Info, 4, str);

View File

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

View File

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

View File

@ -382,8 +382,9 @@ void handleNotifications()
}
if (version > 11) {
// 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
selseg.options = (selseg.options & 0x0071U) | (udpIn[28+ofs]<<8) | (udpIn[9 +ofs] & 0x8E); // ignore selected, freeze, reset & transitional
// freeze, reset should never be synced
// 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) {
selseg.custom1 = udpIn[29+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)
typedef enum UM_SoundSimulations {
UMS_BeatSin = 0,
UMS_WeWillRockYou
//UMS_10_13,
//UMS_14_3
UMS_WeWillRockYou,
UMS_10_13,
UMS_14_3
} um_soundSimulations_t;
um_data_t* simulateSound(uint8_t simulationId)
@ -503,7 +503,7 @@ um_data_t* simulateSound(uint8_t simulationId)
fftResult[i] = 0;
}
break;
/*case UMS_10_3:
case UMS_10_13:
for (int i = 0; i<16; i++)
fftResult[i] = inoise8(beatsin8(90 / (i+1), 0, 200)*15 + (ms>>10), ms>>3);
volumeSmth = fftResult[8];
@ -512,7 +512,7 @@ um_data_t* simulateSound(uint8_t simulationId)
for (int i = 0; i<16; i++)
fftResult[i] = inoise8(beatsin8(120 / (i+1), 10, 30)*10 + (ms>>14), ms>>3);
volumeSmth = fftResult[8];
break;*/
break;
}
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 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 modeBlending _INIT(true); // enable effect blending
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)

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) {
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;
}
if (!index) {
@ -86,7 +86,7 @@ void createEditHandler(bool enable) {
#endif
} else {
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);
} else {
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();
return;
}
@ -211,6 +211,8 @@ void initServer()
if (verboseResponse) {
if (!isConfig) {
lastInterfaceUpdate = millis(); // prevent WS update until cooldown
interfaceUpdateCallMode = CALL_MODE_WS_SEND; // schedule WS update
serveJson(request); return; //if JSON contains "v"
} else {
doSerializeConfig = true; //serializeConfig(); //Save new settings to FS
@ -282,7 +284,7 @@ void initServer()
//init ota page
server.on("/update", HTTP_GET, [](AsyncWebServerRequest *request){
if (otaLock) {
serveMessage(request, 500, "Access Denied", FPSTR(s_unlock_ota), 254);
serveMessage(request, 401, "Access Denied", FPSTR(s_unlock_ota), 254);
} else
serveSettings(request); // checks for "upd" in URL and handles PIN
});
@ -292,7 +294,11 @@ void initServer()
serveSettings(request, true); // handle PIN page POST request
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);
} else {
serveMessage(request, 200, F("Update successful!"), F("Rebooting..."), 131);
@ -533,7 +539,7 @@ void serveSettingsJS(AsyncWebServerRequest* request)
}
if (subPage > 0 && !correctPIN && strlen(settingsPIN)>0) {
strcpy_P(buf, PSTR("alert('PIN incorrect.');"));
request->send(403, "application/javascript", buf);
request->send(401, "application/javascript", buf);
return;
}
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 ((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
@ -605,7 +611,7 @@ void serveSettings(AsyncWebServerRequest* request, bool post)
if (!s2[0]) strcpy_P(s2, s_redirecting);
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;
}
}
@ -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);
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_JS : serveSettingsJS(request); return;
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)
{
#ifndef WLED_DISABLE_2D // include only if 2D is compiled in
oappend(PSTR("gId('2dbtn').style.display='';"));
#ifdef WLED_DISABLE_2D // include only if 2D is not compiled in
oappend(PSTR("gId('2dbtn').style.display='none';"));
#endif
#ifdef WLED_ENABLE_DMX // include only if DMX is enabled
oappend(PSTR("gId('dmxbtn').style.display='';"));
@ -441,6 +441,7 @@ void getSettingsJS(byte subPage, char* dest)
sappend('c',SET_F("GC"),gammaCorrectCol);
dtostrf(gammaCorrectVal,3,1,nS); sappends('s',SET_F("GV"),nS);
sappend('c',SET_F("TF"),fadeTransition);
sappend('c',SET_F("EB"),modeBlending);
sappend('v',SET_F("TD"),transitionDelayDefault);
sappend('c',SET_F("PF"),strip.paletteFade);
sappend('v',SET_F("TP"),randomPaletteChangeTime);
@ -478,7 +479,7 @@ void getSettingsJS(byte subPage, char* dest)
if (subPage == SUBPAGE_SYNC)
{
char nS[32];
[[maybe_unused]] char nS[32];
sappend('v',SET_F("UP"),udpPort);
sappend('v',SET_F("U2"),udpPort2);
sappend('v',SET_F("GS"),syncGroups);