Merge pull request #3107 from Aircoookie/onepx-segment

Tweaks & bugfixes.
This commit is contained in:
Christian Schwinne 2023-03-20 23:42:30 +01:00 committed by GitHub
commit fb1999c474
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
23 changed files with 3670 additions and 3519 deletions

View File

@ -106,6 +106,7 @@ class FourLineDisplayUsermod : public Usermod {
static FourLineDisplayUsermod *instance; static FourLineDisplayUsermod *instance;
bool initDone = false; bool initDone = false;
volatile bool drawing = false; volatile bool drawing = false;
volatile bool lockRedraw = false;
// HW interface & configuration // HW interface & configuration
U8X8 *u8x8 = nullptr; // pointer to U8X8 display object U8X8 *u8x8 = nullptr; // pointer to U8X8 display object
@ -198,25 +199,33 @@ class FourLineDisplayUsermod : public Usermod {
} }
void drawString(uint8_t col, uint8_t row, const char *string, bool ignoreLH=false) { void drawString(uint8_t col, uint8_t row, const char *string, bool ignoreLH=false) {
if (type == NONE || !enabled) return; if (type == NONE || !enabled) return;
drawing = true;
u8x8->setFont(u8x8_font_chroma48medium8_r); u8x8->setFont(u8x8_font_chroma48medium8_r);
if (!ignoreLH && lineHeight==2) u8x8->draw1x2String(col, row, string); if (!ignoreLH && lineHeight==2) u8x8->draw1x2String(col, row, string);
else u8x8->drawString(col, row, string); else u8x8->drawString(col, row, string);
drawing = false;
} }
void draw2x2String(uint8_t col, uint8_t row, const char *string) { void draw2x2String(uint8_t col, uint8_t row, const char *string) {
if (type == NONE || !enabled) return; if (type == NONE || !enabled) return;
drawing = true;
u8x8->setFont(u8x8_font_chroma48medium8_r); u8x8->setFont(u8x8_font_chroma48medium8_r);
u8x8->draw2x2String(col, row, string); u8x8->draw2x2String(col, row, string);
drawing = false;
} }
void drawGlyph(uint8_t col, uint8_t row, char glyph, const uint8_t *font, bool ignoreLH=false) { void drawGlyph(uint8_t col, uint8_t row, char glyph, const uint8_t *font, bool ignoreLH=false) {
if (type == NONE || !enabled) return; if (type == NONE || !enabled) return;
drawing = true;
u8x8->setFont(font); u8x8->setFont(font);
if (!ignoreLH && lineHeight==2) u8x8->draw1x2Glyph(col, row, glyph); if (!ignoreLH && lineHeight==2) u8x8->draw1x2Glyph(col, row, glyph);
else u8x8->drawGlyph(col, row, glyph); else u8x8->drawGlyph(col, row, glyph);
drawing = false;
} }
void draw2x2Glyph(uint8_t col, uint8_t row, char glyph, const uint8_t *font) { void draw2x2Glyph(uint8_t col, uint8_t row, char glyph, const uint8_t *font) {
if (type == NONE || !enabled) return; if (type == NONE || !enabled) return;
drawing = true;
u8x8->setFont(font); u8x8->setFont(font);
u8x8->draw2x2Glyph(col, row, glyph); u8x8->draw2x2Glyph(col, row, glyph);
drawing = false;
} }
uint8_t getCols() { uint8_t getCols() {
if (type==NONE || !enabled) return 0; if (type==NONE || !enabled) return 0;
@ -224,7 +233,9 @@ class FourLineDisplayUsermod : public Usermod {
} }
void clear() { void clear() {
if (type == NONE || !enabled) return; if (type == NONE || !enabled) return;
drawing = true;
u8x8->clear(); u8x8->clear();
drawing = false;
} }
void setPowerSave(uint8_t save) { void setPowerSave(uint8_t save) {
if (type == NONE || !enabled) return; if (type == NONE || !enabled) return;
@ -238,6 +249,7 @@ class FourLineDisplayUsermod : public Usermod {
} }
void draw2x2GlyphIcons() { void draw2x2GlyphIcons() {
drawing = true;
if (lineHeight == 2) { if (lineHeight == 2) {
drawGlyph( 1, 0, 1, u8x8_4LineDisplay_WLED_icons_2x2, true); //brightness icon drawGlyph( 1, 0, 1, u8x8_4LineDisplay_WLED_icons_2x2, true); //brightness icon
drawGlyph( 5, 0, 2, u8x8_4LineDisplay_WLED_icons_2x2, true); //speed icon drawGlyph( 5, 0, 2, u8x8_4LineDisplay_WLED_icons_2x2, true); //speed icon
@ -251,6 +263,7 @@ class FourLineDisplayUsermod : public Usermod {
drawGlyph(15, 2, 4, u8x8_4LineDisplay_WLED_icons_1x1); //palette icon drawGlyph(15, 2, 4, u8x8_4LineDisplay_WLED_icons_1x1); //palette icon
drawGlyph(15, 3, 5, u8x8_4LineDisplay_WLED_icons_1x1); //effect icon drawGlyph(15, 3, 5, u8x8_4LineDisplay_WLED_icons_1x1); //effect icon
} }
drawing = false;
} }
/** /**
@ -262,8 +275,8 @@ class FourLineDisplayUsermod : public Usermod {
if (type == NONE || !enabled || !displayTurnedOff) return; if (type == NONE || !enabled || !displayTurnedOff) return;
unsigned long now = millis(); unsigned long now = millis();
while (drawing && millis()-now < 250) delay(1); // wait if someone else is drawing while (drawing && millis()-now < 125) delay(1); // wait if someone else is drawing
drawing = true; if (drawing) return;
char lineBuffer[LINE_BUFFER_SIZE]; char lineBuffer[LINE_BUFFER_SIZE];
static byte lastSecond; static byte lastSecond;
@ -299,7 +312,23 @@ class FourLineDisplayUsermod : public Usermod {
sprintf_P(lineBuffer, PSTR("%02d"), secondCurrent); sprintf_P(lineBuffer, PSTR("%02d"), secondCurrent);
drawString(12, lineHeight*2+1, lineBuffer, true); // even with double sized rows print seconds in 1 line drawString(12, lineHeight*2+1, lineBuffer, true); // even with double sized rows print seconds in 1 line
} }
drawing = false; }
/**
* Enable sleep (turn the display off) or clock mode.
*/
void sleepOrClock(bool enabled) {
if (enabled) {
displayTurnedOff = true;
if (clockMode && ntpEnabled) {
knownMinute = knownHour = 99;
showTime();
} else
setPowerSave(1);
} else {
displayTurnedOff = false;
setPowerSave(0);
}
} }
public: public:
@ -483,7 +512,8 @@ class FourLineDisplayUsermod : public Usermod {
} }
} }
while (drawing && millis()-now < 250) delay(1); // wait if someone else is drawing while (drawing && millis()-now < 25) delay(1); // wait if someone else is drawing
if (drawing || lockRedraw) return;
if (apActive && WLED_WIFI_CONFIGURED && now<15000) { if (apActive && WLED_WIFI_CONFIGURED && now<15000) {
knownSsid = apSSID; knownSsid = apSSID;
@ -546,7 +576,7 @@ class FourLineDisplayUsermod : public Usermod {
} }
lastRedraw = now; lastRedraw = now;
// Turn the display back on // Turn the display back on
wakeDisplay(); wakeDisplay();
@ -580,44 +610,49 @@ class FourLineDisplayUsermod : public Usermod {
void updateBrightness() { void updateBrightness() {
knownBrightness = bri; knownBrightness = bri;
if (overlayUntil == 0) { if (overlayUntil == 0) {
lockRedraw = true;
brightness100 = ((uint16_t)bri*100)/255; brightness100 = ((uint16_t)bri*100)/255;
char lineBuffer[4]; char lineBuffer[4];
sprintf_P(lineBuffer, PSTR("%-3d"), brightness100); sprintf_P(lineBuffer, PSTR("%-3d"), brightness100);
drawString(1, lineHeight, lineBuffer); drawString(1, lineHeight, lineBuffer);
//lastRedraw = millis(); lockRedraw = false;
} }
} }
void updateSpeed() { void updateSpeed() {
knownEffectSpeed = effectSpeed; knownEffectSpeed = effectSpeed;
if (overlayUntil == 0) { if (overlayUntil == 0) {
lockRedraw = true;
fxspeed100 = ((uint16_t)effectSpeed*100)/255; fxspeed100 = ((uint16_t)effectSpeed*100)/255;
char lineBuffer[4]; char lineBuffer[4];
sprintf_P(lineBuffer, PSTR("%-3d"), fxspeed100); sprintf_P(lineBuffer, PSTR("%-3d"), fxspeed100);
drawString(5, lineHeight, lineBuffer); drawString(5, lineHeight, lineBuffer);
//lastRedraw = millis(); lockRedraw = false;
} }
} }
void updateIntensity() { void updateIntensity() {
knownEffectIntensity = effectIntensity; knownEffectIntensity = effectIntensity;
if (overlayUntil == 0) { if (overlayUntil == 0) {
lockRedraw = true;
fxintensity100 = ((uint16_t)effectIntensity*100)/255; fxintensity100 = ((uint16_t)effectIntensity*100)/255;
char lineBuffer[4]; char lineBuffer[4];
sprintf_P(lineBuffer, PSTR("%-3d"), fxintensity100); sprintf_P(lineBuffer, PSTR("%-3d"), fxintensity100);
drawString(9, lineHeight, lineBuffer); drawString(9, lineHeight, lineBuffer);
//lastRedraw = millis(); lockRedraw = false;
} }
} }
void drawStatusIcons() { void drawStatusIcons() {
uint8_t col = 15; uint8_t col = 15;
uint8_t row = 0; uint8_t row = 0;
lockRedraw = true;
drawGlyph(col, row, (wificonnected ? 20 : 0), u8x8_4LineDisplay_WLED_icons_1x1, true); // wifi icon drawGlyph(col, row, (wificonnected ? 20 : 0), u8x8_4LineDisplay_WLED_icons_1x1, true); // wifi icon
if (lineHeight==2) { col--; } else { row++; } if (lineHeight==2) { col--; } else { row++; }
drawGlyph(col, row, (bri > 0 ? 9 : 0), u8x8_4LineDisplay_WLED_icons_1x1, true); // power icon drawGlyph(col, row, (bri > 0 ? 9 : 0), u8x8_4LineDisplay_WLED_icons_1x1, true); // power icon
if (lineHeight==2) { col--; } else { col = row = 0; } if (lineHeight==2) { col--; } else { col = row = 0; }
drawGlyph(col, row, (nightlightActive ? 6 : 0), u8x8_4LineDisplay_WLED_icons_1x1, true); // moon icon for nighlight mode drawGlyph(col, row, (nightlightActive ? 6 : 0), u8x8_4LineDisplay_WLED_icons_1x1, true); // moon icon for nighlight mode
lockRedraw = false;
} }
/** /**
@ -632,7 +667,9 @@ class FourLineDisplayUsermod : public Usermod {
//Draw the arrow for the current setting beiong changed //Draw the arrow for the current setting beiong changed
void drawArrow() { void drawArrow() {
lockRedraw = true;
if (markColNum != 255 && markLineNum !=255) drawGlyph(markColNum, markLineNum*lineHeight, 21, u8x8_4LineDisplay_WLED_icons_1x1); if (markColNum != 255 && markLineNum !=255) drawGlyph(markColNum, markLineNum*lineHeight, 21, u8x8_4LineDisplay_WLED_icons_1x1);
lockRedraw = false;
} }
//Display the current effect or palette (desiredEntry) //Display the current effect or palette (desiredEntry)
@ -640,6 +677,7 @@ class FourLineDisplayUsermod : public Usermod {
void showCurrentEffectOrPalette(int inputEffPal, const char *qstring, uint8_t row) { void showCurrentEffectOrPalette(int inputEffPal, const char *qstring, uint8_t row) {
char lineBuffer[MAX_JSON_CHARS]; char lineBuffer[MAX_JSON_CHARS];
if (overlayUntil == 0) { if (overlayUntil == 0) {
lockRedraw = true;
// Find the mode name in JSON // Find the mode name in JSON
uint8_t printedChars = extractModeName(inputEffPal, qstring, lineBuffer, MAX_JSON_CHARS-1); uint8_t printedChars = extractModeName(inputEffPal, qstring, lineBuffer, MAX_JSON_CHARS-1);
if (lineBuffer[0]=='*' && lineBuffer[1]==' ') { if (lineBuffer[0]=='*' && lineBuffer[1]==' ') {
@ -692,6 +730,7 @@ class FourLineDisplayUsermod : public Usermod {
smallBuffer3[smallChars3] = 0; smallBuffer3[smallChars3] = 0;
drawString(1, row*lineHeight, smallBuffer3, true); drawString(1, row*lineHeight, smallBuffer3, true);
} }
lockRedraw = false;
} }
} }
@ -706,12 +745,12 @@ class FourLineDisplayUsermod : public Usermod {
if (displayTurnedOff) { if (displayTurnedOff) {
unsigned long now = millis(); unsigned long now = millis();
while (drawing && millis()-now < 250) delay(1); // wait if someone else is drawing while (drawing && millis()-now < 250) delay(1); // wait if someone else is drawing
drawing = true; if (drawing) return false;
lockRedraw = true;
clear(); clear();
// Turn the display back on // Turn the display back on
sleepOrClock(false); sleepOrClock(false);
//lastRedraw = millis(); lockRedraw = false;
drawing = false;
return true; return true;
} }
return false; return false;
@ -724,8 +763,9 @@ class FourLineDisplayUsermod : public Usermod {
*/ */
void overlay(const char* line1, long showHowLong, byte glyphType) { void overlay(const char* line1, long showHowLong, byte glyphType) {
unsigned long now = millis(); unsigned long now = millis();
while (drawing && millis()-now < 250) delay(1); // wait if someone else is drawing while (drawing && millis()-now < 125) delay(1); // wait if someone else is drawing
drawing = true; if (drawing) return;
lockRedraw = true;
// Turn the display back on // Turn the display back on
if (!wakeDisplay()) clear(); if (!wakeDisplay()) clear();
// Print the overlay // Print the overlay
@ -739,7 +779,7 @@ class FourLineDisplayUsermod : public Usermod {
drawString(0, (glyphType<255?3:0)*lineHeight, buf.c_str()); drawString(0, (glyphType<255?3:0)*lineHeight, buf.c_str());
} }
overlayUntil = millis() + showHowLong; overlayUntil = millis() + showHowLong;
drawing = false; lockRedraw = false;
} }
/** /**
@ -748,8 +788,9 @@ class FourLineDisplayUsermod : public Usermod {
*/ */
void overlayLogo(long showHowLong) { void overlayLogo(long showHowLong) {
unsigned long now = millis(); unsigned long now = millis();
while (drawing && millis()-now < 250) delay(1); // wait if someone else is drawing while (drawing && millis()-now < 125) delay(1); // wait if someone else is drawing
drawing = true; if (drawing) return;
lockRedraw = true;
// Turn the display back on // Turn the display back on
if (!wakeDisplay()) clear(); if (!wakeDisplay()) clear();
// Print the overlay // Print the overlay
@ -799,7 +840,7 @@ class FourLineDisplayUsermod : public Usermod {
} }
} }
overlayUntil = millis() + showHowLong; overlayUntil = millis() + showHowLong;
drawing = false; lockRedraw = false;
} }
/** /**
@ -809,8 +850,9 @@ class FourLineDisplayUsermod : public Usermod {
*/ */
void overlay(const char* line1, const char* line2, long showHowLong) { void overlay(const char* line1, const char* line2, long showHowLong) {
unsigned long now = millis(); unsigned long now = millis();
while (drawing && millis()-now < 250) delay(1); // wait if someone else is drawing while (drawing && millis()-now < 125) delay(1); // wait if someone else is drawing
drawing = true; if (drawing) return;
lockRedraw = true;
// Turn the display back on // Turn the display back on
if (!wakeDisplay()) clear(); if (!wakeDisplay()) clear();
// Print the overlay // Print the overlay
@ -825,13 +867,14 @@ class FourLineDisplayUsermod : public Usermod {
drawString(0, 2*lineHeight, buf.c_str()); drawString(0, 2*lineHeight, buf.c_str());
} }
overlayUntil = millis() + showHowLong; overlayUntil = millis() + showHowLong;
drawing = false; lockRedraw = false;
} }
void networkOverlay(const char* line1, long showHowLong) { void networkOverlay(const char* line1, long showHowLong) {
unsigned long now = millis(); unsigned long now = millis();
while (drawing && millis()-now < 250) delay(1); // wait if someone else is drawing while (drawing && millis()-now < 125) delay(1); // wait if someone else is drawing
drawing = true; if (drawing) return;
lockRedraw = true;
String line; String line;
// Turn the display back on // Turn the display back on
@ -863,27 +906,10 @@ class FourLineDisplayUsermod : public Usermod {
center(line, getCols()); center(line, getCols());
drawString(0, lineHeight*3, line.c_str()); drawString(0, lineHeight*3, line.c_str());
overlayUntil = millis() + showHowLong; overlayUntil = millis() + showHowLong;
drawing = false; lockRedraw = false;
} }
/**
* Enable sleep (turn the display off) or clock mode.
*/
void sleepOrClock(bool enabled) {
if (enabled) {
displayTurnedOff = true;
if (clockMode && ntpEnabled) {
knownMinute = knownHour = 99;
showTime();
} else
setPowerSave(1);
} else {
displayTurnedOff = false;
setPowerSave(0);
}
}
/** /**
* handleButton() can be used to override default button behaviour. Returning true * handleButton() can be used to override default button behaviour. Returning true
* will prevent button working in a default way. * will prevent button working in a default way.

View File

@ -124,7 +124,7 @@ uint16_t blink(uint32_t color1, uint32_t color2, bool strobe, bool do_palette) {
uint16_t mode_blink(void) { uint16_t mode_blink(void) {
return blink(SEGCOLOR(0), SEGCOLOR(1), false, true); return blink(SEGCOLOR(0), SEGCOLOR(1), false, true);
} }
static const char _data_FX_MODE_BLINK[] PROGMEM = "Blink@!,Duty cycle;!,!;!"; static const char _data_FX_MODE_BLINK[] PROGMEM = "Blink@!,Duty cycle;!,!;!;01";
/* /*
@ -133,7 +133,7 @@ static const char _data_FX_MODE_BLINK[] PROGMEM = "Blink@!,Duty cycle;!,!;!";
uint16_t mode_blink_rainbow(void) { uint16_t mode_blink_rainbow(void) {
return blink(SEGMENT.color_wheel(SEGENV.call & 0xFF), SEGCOLOR(1), false, false); return blink(SEGMENT.color_wheel(SEGENV.call & 0xFF), SEGCOLOR(1), false, false);
} }
static const char _data_FX_MODE_BLINK_RAINBOW[] PROGMEM = "Blink Rainbow@Frequency,Blink duration;!,!;!"; static const char _data_FX_MODE_BLINK_RAINBOW[] PROGMEM = "Blink Rainbow@Frequency,Blink duration;!,!;!;01";
/* /*
@ -142,7 +142,7 @@ static const char _data_FX_MODE_BLINK_RAINBOW[] PROGMEM = "Blink Rainbow@Frequen
uint16_t mode_strobe(void) { uint16_t mode_strobe(void) {
return blink(SEGCOLOR(0), SEGCOLOR(1), true, true); return blink(SEGCOLOR(0), SEGCOLOR(1), true, true);
} }
static const char _data_FX_MODE_STROBE[] PROGMEM = "Strobe@!;!,!;!"; static const char _data_FX_MODE_STROBE[] PROGMEM = "Strobe@!;!,!;!;01";
/* /*
@ -151,7 +151,7 @@ static const char _data_FX_MODE_STROBE[] PROGMEM = "Strobe@!;!,!;!";
uint16_t mode_strobe_rainbow(void) { uint16_t mode_strobe_rainbow(void) {
return blink(SEGMENT.color_wheel(SEGENV.call & 0xFF), SEGCOLOR(1), true, false); return blink(SEGMENT.color_wheel(SEGENV.call & 0xFF), SEGCOLOR(1), true, false);
} }
static const char _data_FX_MODE_STROBE_RAINBOW[] PROGMEM = "Strobe Rainbow@!;,!;!"; static const char _data_FX_MODE_STROBE_RAINBOW[] PROGMEM = "Strobe Rainbow@!;,!;!;01";
/* /*
@ -350,7 +350,7 @@ uint16_t mode_breath(void) {
return FRAMETIME; return FRAMETIME;
} }
static const char _data_FX_MODE_BREATH[] PROGMEM = "Breathe@!;!,!;!"; static const char _data_FX_MODE_BREATH[] PROGMEM = "Breathe@!;!,!;!;01";
/* /*
@ -366,7 +366,7 @@ uint16_t mode_fade(void) {
return FRAMETIME; return FRAMETIME;
} }
static const char _data_FX_MODE_FADE[] PROGMEM = "Fade@!;!,!;!"; static const char _data_FX_MODE_FADE[] PROGMEM = "Fade@!;!,!;!;01";
/* /*
@ -754,7 +754,7 @@ uint16_t mode_multi_strobe(void) {
return FRAMETIME; return FRAMETIME;
} }
static const char _data_FX_MODE_MULTI_STROBE[] PROGMEM = "Strobe Mega@!,!;!,!;!"; static const char _data_FX_MODE_MULTI_STROBE[] PROGMEM = "Strobe Mega@!,!;!,!;!;01";
/* /*
@ -976,6 +976,7 @@ static const char _data_FX_MODE_COLORFUL[] PROGMEM = "Colorful@!,Saturation;1,2,
* Emulates a traffic light. * Emulates a traffic light.
*/ */
uint16_t mode_traffic_light(void) { uint16_t mode_traffic_light(void) {
if (SEGLEN == 1) return mode_static();
for (int i=0; i < SEGLEN; i++) for (int i=0; i < SEGLEN; i++)
SEGMENT.setPixelColor(i, SEGMENT.color_from_palette(i, true, PALETTE_SOLID_WRAP, 1)); SEGMENT.setPixelColor(i, SEGMENT.color_from_palette(i, true, PALETTE_SOLID_WRAP, 1));
uint32_t mdelay = 500; uint32_t mdelay = 500;
@ -1008,6 +1009,7 @@ static const char _data_FX_MODE_TRAFFIC_LIGHT[] PROGMEM = "Traffic Light@!,US st
*/ */
#define FLASH_COUNT 4 #define FLASH_COUNT 4
uint16_t mode_chase_flash(void) { uint16_t mode_chase_flash(void) {
if (SEGLEN == 1) return mode_static();
uint8_t flash_step = SEGENV.call % ((FLASH_COUNT * 2) + 1); uint8_t flash_step = SEGENV.call % ((FLASH_COUNT * 2) + 1);
for (int i = 0; i < SEGLEN; i++) { for (int i = 0; i < SEGLEN; i++) {
@ -1037,6 +1039,7 @@ static const char _data_FX_MODE_CHASE_FLASH[] PROGMEM = "Chase Flash@!;Bg,Fx;!";
* Prim flashes running, followed by random color. * Prim flashes running, followed by random color.
*/ */
uint16_t mode_chase_flash_random(void) { uint16_t mode_chase_flash_random(void) {
if (SEGLEN == 1) return mode_static();
uint8_t flash_step = SEGENV.call % ((FLASH_COUNT * 2) + 1); uint8_t flash_step = SEGENV.call % ((FLASH_COUNT * 2) + 1);
for (int i = 0; i < SEGENV.aux1; i++) { for (int i = 0; i < SEGENV.aux1; i++) {
@ -1170,6 +1173,7 @@ static const char _data_FX_MODE_DUAL_LARSON_SCANNER[] PROGMEM = "Scanner Dual@!,
* Firing comets from one end. "Lighthouse" * Firing comets from one end. "Lighthouse"
*/ */
uint16_t mode_comet(void) { uint16_t mode_comet(void) {
if (SEGLEN == 1) return mode_static();
uint16_t counter = strip.now * ((SEGMENT.speed >>2) +1); uint16_t counter = strip.now * ((SEGMENT.speed >>2) +1);
uint16_t index = (counter * SEGLEN) >> 16; uint16_t index = (counter * SEGLEN) >> 16;
if (SEGENV.call == 0) SEGENV.aux0 = index; if (SEGENV.call == 0) SEGENV.aux0 = index;
@ -1197,7 +1201,8 @@ static const char _data_FX_MODE_COMET[] PROGMEM = "Lighthouse@!,Fade rate;!,!;!"
* Fireworks function. * Fireworks function.
*/ */
uint16_t mode_fireworks() { uint16_t mode_fireworks() {
const uint16_t width = strip.isMatrix ? SEGMENT.virtualWidth() : SEGMENT.virtualLength(); if (SEGLEN == 1) return mode_static();
const uint16_t width = SEGMENT.is2D() ? SEGMENT.virtualWidth() : SEGMENT.virtualLength();
const uint16_t height = SEGMENT.virtualHeight(); const uint16_t height = SEGMENT.virtualHeight();
if (SEGENV.call == 0) { if (SEGENV.call == 0) {
@ -1211,18 +1216,18 @@ uint16_t mode_fireworks() {
bool valid1 = (SEGENV.aux0 < width*height); bool valid1 = (SEGENV.aux0 < width*height);
bool valid2 = (SEGENV.aux1 < width*height); bool valid2 = (SEGENV.aux1 < width*height);
uint32_t sv1 = 0, sv2 = 0; uint32_t sv1 = 0, sv2 = 0;
if (valid1) sv1 = strip.isMatrix ? SEGMENT.getPixelColorXY(SEGENV.aux0%width, SEGENV.aux0/width) : SEGMENT.getPixelColor(SEGENV.aux0); // get spark color if (valid1) sv1 = SEGMENT.is2D() ? SEGMENT.getPixelColorXY(SEGENV.aux0%width, SEGENV.aux0/width) : SEGMENT.getPixelColor(SEGENV.aux0); // get spark color
if (valid2) sv2 = strip.isMatrix ? SEGMENT.getPixelColorXY(SEGENV.aux1%width, SEGENV.aux1/width) : SEGMENT.getPixelColor(SEGENV.aux1); if (valid2) sv2 = SEGMENT.is2D() ? SEGMENT.getPixelColorXY(SEGENV.aux1%width, SEGENV.aux1/width) : SEGMENT.getPixelColor(SEGENV.aux1);
if (!SEGENV.step) SEGMENT.blur(16); if (!SEGENV.step) SEGMENT.blur(16);
if (valid1) { if (strip.isMatrix) SEGMENT.setPixelColorXY(SEGENV.aux0%width, SEGENV.aux0/width, sv1); else SEGMENT.setPixelColor(SEGENV.aux0, sv1); } // restore spark color after blur if (valid1) { if (SEGMENT.is2D()) SEGMENT.setPixelColorXY(SEGENV.aux0%width, SEGENV.aux0/width, sv1); else SEGMENT.setPixelColor(SEGENV.aux0, sv1); } // restore spark color after blur
if (valid2) { if (strip.isMatrix) SEGMENT.setPixelColorXY(SEGENV.aux1%width, SEGENV.aux1/width, sv2); else SEGMENT.setPixelColor(SEGENV.aux1, sv2); } // restore old spark color after blur if (valid2) { if (SEGMENT.is2D()) SEGMENT.setPixelColorXY(SEGENV.aux1%width, SEGENV.aux1/width, sv2); else SEGMENT.setPixelColor(SEGENV.aux1, sv2); } // restore old spark color after blur
for (int i=0; i<MAX(1, width/20); i++) { for (int i=0; i<MAX(1, width/20); i++) {
if (random8(129 - (SEGMENT.intensity >> 1)) == 0) { if (random8(129 - (SEGMENT.intensity >> 1)) == 0) {
uint16_t index = random16(width*height); uint16_t index = random16(width*height);
uint16_t j = index % width, k = index / width; uint16_t j = index % width, k = index / width;
uint32_t col = SEGMENT.color_from_palette(random8(), false, false, 0); uint32_t col = SEGMENT.color_from_palette(random8(), false, false, 0);
if (strip.isMatrix) SEGMENT.setPixelColorXY(j, k, col); if (SEGMENT.is2D()) SEGMENT.setPixelColorXY(j, k, col);
else SEGMENT.setPixelColor(index, col); else SEGMENT.setPixelColor(index, col);
SEGENV.aux1 = SEGENV.aux0; // old spark SEGENV.aux1 = SEGENV.aux0; // old spark
SEGENV.aux0 = index; // remember where spark occured SEGENV.aux0 = index; // remember where spark occured
@ -1234,8 +1239,8 @@ static const char _data_FX_MODE_FIREWORKS[] PROGMEM = "Fireworks@,Frequency;!,!;
//Twinkling LEDs running. Inspired by https://github.com/kitesurfer1404/WS2812FX/blob/master/src/custom/Rain.h //Twinkling LEDs running. Inspired by https://github.com/kitesurfer1404/WS2812FX/blob/master/src/custom/Rain.h
uint16_t mode_rain() uint16_t mode_rain() {
{ if (SEGLEN == 1) return mode_static();
const uint16_t width = SEGMENT.virtualWidth(); const uint16_t width = SEGMENT.virtualWidth();
const uint16_t height = SEGMENT.virtualHeight(); const uint16_t height = SEGMENT.virtualHeight();
SEGENV.step += FRAMETIME; SEGENV.step += FRAMETIME;
@ -1294,7 +1299,7 @@ uint16_t mode_fire_flicker(void) {
SEGENV.step = it; SEGENV.step = it;
return FRAMETIME; return FRAMETIME;
} }
static const char _data_FX_MODE_FIRE_FLICKER[] PROGMEM = "Fire Flicker@!,!;!;!"; static const char _data_FX_MODE_FIRE_FLICKER[] PROGMEM = "Fire Flicker@!,!;!;!;01";
/* /*
@ -1345,8 +1350,8 @@ static const char _data_FX_MODE_LOADING[] PROGMEM = "Loading@!,Fade;!,!;!;;ix=16
//American Police Light with all LEDs Red and Blue //American Police Light with all LEDs Red and Blue
uint16_t police_base(uint32_t color1, uint32_t color2) uint16_t police_base(uint32_t color1, uint32_t color2) {
{ if (SEGLEN == 1) return mode_static();
uint16_t delay = 1 + (FRAMETIME<<3) / SEGLEN; // longer segments should change faster uint16_t delay = 1 + (FRAMETIME<<3) / SEGLEN; // longer segments should change faster
uint32_t it = strip.now / map(SEGMENT.speed, 0, 255, delay<<4, delay); uint32_t it = strip.now / map(SEGMENT.speed, 0, 255, delay<<4, delay);
uint16_t offset = it % SEGLEN; uint16_t offset = it % SEGLEN;
@ -1794,9 +1799,9 @@ uint16_t mode_oscillate(void) {
} }
} }
for (int i=0; i < SEGLEN; i++) { for (int i = 0; i < SEGLEN; i++) {
uint32_t color = BLACK; uint32_t color = BLACK;
for (int j=0; j < numOscillators; j++) { for (int j = 0; j < numOscillators; j++) {
if(i >= oscillators[j].pos - oscillators[j].size && i <= oscillators[j].pos + oscillators[j].size) { if(i >= oscillators[j].pos - oscillators[j].size && i <= oscillators[j].pos + oscillators[j].size) {
color = (color == BLACK) ? SEGCOLOR(j) : color_blend(color, SEGCOLOR(j), 128); color = (color == BLACK) ? SEGCOLOR(j) : color_blend(color, SEGCOLOR(j), 128);
} }
@ -1812,6 +1817,7 @@ static const char _data_FX_MODE_OSCILLATE[] PROGMEM = "Oscillate";
//TODO //TODO
uint16_t mode_lightning(void) { uint16_t mode_lightning(void) {
if (SEGLEN == 1) return mode_static();
uint16_t ledstart = random16(SEGLEN); // Determine starting location of flash uint16_t ledstart = random16(SEGLEN); // Determine starting location of flash
uint16_t ledlen = 1 + random16(SEGLEN -ledstart); // Determine length of flash (not to go beyond NUM_LEDS-1) uint16_t ledlen = 1 + random16(SEGLEN -ledstart); // Determine length of flash (not to go beyond NUM_LEDS-1)
uint8_t bri = 255/random8(1, 3); uint8_t bri = 255/random8(1, 3);
@ -1872,7 +1878,6 @@ uint16_t mode_pride_2015(void) {
sPseudotime += duration * msmultiplier; sPseudotime += duration * msmultiplier;
sHue16 += duration * beatsin88( 400, 5,9); sHue16 += duration * beatsin88( 400, 5,9);
uint16_t brightnesstheta16 = sPseudotime; uint16_t brightnesstheta16 = sPseudotime;
CRGB fastled_col;
for (int i = 0 ; i < SEGLEN; i++) { for (int i = 0 ; i < SEGLEN; i++) {
hue16 += hueinc16; hue16 += hueinc16;
@ -1898,6 +1903,7 @@ static const char _data_FX_MODE_PRIDE_2015[] PROGMEM = "Pride 2015@!;;";
//eight colored dots, weaving in and out of sync with each other //eight colored dots, weaving in and out of sync with each other
uint16_t mode_juggle(void) { uint16_t mode_juggle(void) {
if (SEGLEN == 1) return mode_static();
if (SEGENV.call == 0) { if (SEGENV.call == 0) {
SEGMENT.setUpLeds(); //lossless getPixelColor() SEGMENT.setUpLeds(); //lossless getPixelColor()
SEGMENT.fill(BLACK); SEGMENT.fill(BLACK);
@ -1967,6 +1973,7 @@ static const char _data_FX_MODE_PALETTE[] PROGMEM = "Palette@Cycle speed;;!;;c3=
// feel of your fire: COOLING (used in step 1 above) (Speed = COOLING), and SPARKING (used // feel of your fire: COOLING (used in step 1 above) (Speed = COOLING), and SPARKING (used
// in step 3 above) (Effect Intensity = Sparking). // in step 3 above) (Effect Intensity = Sparking).
uint16_t mode_fire_2012() { uint16_t mode_fire_2012() {
if (SEGLEN == 1) return mode_static();
const uint16_t strips = SEGMENT.nrOfVStrips(); const uint16_t strips = SEGMENT.nrOfVStrips();
if (!SEGENV.allocateData(strips * SEGLEN)) return mode_static(); //allocation failed if (!SEGENV.allocateData(strips * SEGLEN)) return mode_static(); //allocation failed
byte* heat = SEGENV.data; byte* heat = SEGENV.data;
@ -2267,6 +2274,7 @@ static const char _data_FX_MODE_LAKE[] PROGMEM = "Lake@!;Fx;!";
// send a meteor from begining to to the end of the strip with a trail that randomly decays. // send a meteor from begining to to the end of the strip with a trail that randomly decays.
// adapted from https://www.tweaking4all.com/hardware/arduino/adruino-led-strip-effects/#LEDStripEffectMeteorRain // adapted from https://www.tweaking4all.com/hardware/arduino/adruino-led-strip-effects/#LEDStripEffectMeteorRain
uint16_t mode_meteor() { uint16_t mode_meteor() {
if (SEGLEN == 1) return mode_static();
if (!SEGENV.allocateData(SEGLEN)) return mode_static(); //allocation failed if (!SEGENV.allocateData(SEGLEN)) return mode_static(); //allocation failed
byte* trail = SEGENV.data; byte* trail = SEGENV.data;
@ -2304,6 +2312,7 @@ static const char _data_FX_MODE_METEOR[] PROGMEM = "Meteor@!,Trail length;!;!";
// send a meteor from begining to to the end of the strip with a trail that randomly decays. // send a meteor from begining to to the end of the strip with a trail that randomly decays.
// adapted from https://www.tweaking4all.com/hardware/arduino/adruino-led-strip-effects/#LEDStripEffectMeteorRain // adapted from https://www.tweaking4all.com/hardware/arduino/adruino-led-strip-effects/#LEDStripEffectMeteorRain
uint16_t mode_meteor_smooth() { uint16_t mode_meteor_smooth() {
if (SEGLEN == 1) return mode_static();
if (!SEGENV.allocateData(SEGLEN)) return mode_static(); //allocation failed if (!SEGENV.allocateData(SEGLEN)) return mode_static(); //allocation failed
byte* trail = SEGENV.data; byte* trail = SEGENV.data;
@ -2341,6 +2350,7 @@ static const char _data_FX_MODE_METEOR_SMOOTH[] PROGMEM = "Meteor Smooth@!,Trail
//Railway Crossing / Christmas Fairy lights //Railway Crossing / Christmas Fairy lights
uint16_t mode_railway() { uint16_t mode_railway() {
if (SEGLEN == 1) return mode_static();
uint16_t dur = (256 - SEGMENT.speed) * 40; uint16_t dur = (256 - SEGMENT.speed) * 40;
uint16_t rampdur = (dur * SEGMENT.intensity) >> 8; uint16_t rampdur = (dur * SEGMENT.intensity) >> 8;
if (SEGENV.step > dur) if (SEGENV.step > dur)
@ -2441,6 +2451,7 @@ uint16_t ripple_base()
uint16_t mode_ripple(void) { uint16_t mode_ripple(void) {
if (SEGLEN == 1) return mode_static();
if (!SEGMENT.check2) SEGMENT.fill(SEGCOLOR(1)); if (!SEGMENT.check2) SEGMENT.fill(SEGCOLOR(1));
return ripple_base(); return ripple_base();
} }
@ -2448,6 +2459,7 @@ static const char _data_FX_MODE_RIPPLE[] PROGMEM = "Ripple@!,Wave #,,,,,Overlay;
uint16_t mode_ripple_rainbow(void) { uint16_t mode_ripple_rainbow(void) {
if (SEGLEN == 1) return mode_static();
if (SEGENV.call ==0) { if (SEGENV.call ==0) {
SEGENV.aux0 = random8(); SEGENV.aux0 = random8();
SEGENV.aux1 = random8(); SEGENV.aux1 = random8();
@ -2612,6 +2624,7 @@ static const char _data_FX_MODE_TWINKLECAT[] PROGMEM = "Twinklecat@!,Twinkle rat
//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
uint16_t mode_halloween_eyes() uint16_t mode_halloween_eyes()
{ {
if (SEGLEN == 1) return mode_static();
const uint16_t maxWidth = strip.isMatrix ? SEGMENT.virtualWidth() : SEGLEN; const uint16_t maxWidth = strip.isMatrix ? SEGMENT.virtualWidth() : SEGLEN;
const uint16_t HALLOWEEN_EYE_SPACE = MAX(2, strip.isMatrix ? SEGMENT.virtualWidth()>>4: SEGLEN>>5); const uint16_t HALLOWEEN_EYE_SPACE = MAX(2, strip.isMatrix ? SEGMENT.virtualWidth()>>4: SEGLEN>>5);
const uint16_t HALLOWEEN_EYE_WIDTH = HALLOWEEN_EYE_SPACE/2; const uint16_t HALLOWEEN_EYE_WIDTH = HALLOWEEN_EYE_SPACE/2;
@ -2721,6 +2734,7 @@ static const char _data_FX_MODE_TRI_STATIC_PATTERN[] PROGMEM = "Solid Pattern Tr
uint16_t spots_base(uint16_t threshold) uint16_t spots_base(uint16_t threshold)
{ {
if (SEGLEN == 1) return mode_static();
if (!SEGMENT.check2) SEGMENT.fill(SEGCOLOR(1)); if (!SEGMENT.check2) SEGMENT.fill(SEGCOLOR(1));
uint16_t maxZones = SEGLEN >> 2; uint16_t maxZones = SEGLEN >> 2;
@ -2776,6 +2790,7 @@ typedef struct Ball {
* Bouncing Balls Effect * Bouncing Balls Effect
*/ */
uint16_t mode_bouncing_balls(void) { uint16_t mode_bouncing_balls(void) {
if (SEGLEN == 1) return mode_static();
//allocate segment data //allocate segment data
const uint16_t strips = SEGMENT.nrOfVStrips(); // adapt for 2D const uint16_t strips = SEGMENT.nrOfVStrips(); // adapt for 2D
const size_t maxNumBalls = 16; const size_t maxNumBalls = 16;
@ -2848,6 +2863,7 @@ static const char _data_FX_MODE_BOUNCINGBALLS[] PROGMEM = "Bouncing Balls@Gravit
* Sinelon stolen from FASTLED examples * Sinelon stolen from FASTLED examples
*/ */
uint16_t sinelon_base(bool dual, bool rainbow=false) { uint16_t sinelon_base(bool dual, bool rainbow=false) {
if (SEGLEN == 1) return mode_static();
SEGMENT.fade_out(SEGMENT.intensity); SEGMENT.fade_out(SEGMENT.intensity);
uint16_t pos = beatsin16(SEGMENT.speed/10,0,SEGLEN-1); uint16_t pos = beatsin16(SEGMENT.speed/10,0,SEGLEN-1);
if (SEGENV.call == 0) SEGENV.aux0 = pos; if (SEGENV.call == 0) SEGENV.aux0 = pos;
@ -2945,6 +2961,7 @@ typedef struct Spark {
* modified from https://github.com/kitesurfer1404/WS2812FX/blob/master/src/custom/Popcorn.h * modified from https://github.com/kitesurfer1404/WS2812FX/blob/master/src/custom/Popcorn.h
*/ */
uint16_t mode_popcorn(void) { uint16_t mode_popcorn(void) {
if (SEGLEN == 1) return mode_static();
//allocate segment data //allocate segment data
uint16_t strips = SEGMENT.nrOfVStrips(); uint16_t strips = SEGMENT.nrOfVStrips();
uint16_t dataSize = sizeof(spark) * maxNumPopcorn; uint16_t dataSize = sizeof(spark) * maxNumPopcorn;
@ -3087,7 +3104,7 @@ uint16_t mode_candle()
{ {
return candle(false); return candle(false);
} }
static const char _data_FX_MODE_CANDLE[] PROGMEM = "Candle@!,!;!,!;!;1;sx=96,ix=224,pal=0"; static const char _data_FX_MODE_CANDLE[] PROGMEM = "Candle@!,!;!,!;!;01;sx=96,ix=224,pal=0";
uint16_t mode_candle_multi() uint16_t mode_candle_multi()
@ -3118,6 +3135,7 @@ typedef struct particle {
} star; } star;
uint16_t mode_starburst(void) { uint16_t mode_starburst(void) {
if (SEGLEN == 1) return mode_static();
uint16_t maxData = FAIR_DATA_PER_SEG; //ESP8266: 256 ESP32: 640 uint16_t maxData = FAIR_DATA_PER_SEG; //ESP8266: 256 ESP32: 640
uint8_t segs = strip.getActiveSegmentsNum(); uint8_t segs = strip.getActiveSegmentsNum();
if (segs <= (strip.getMaxSegments() /2)) maxData *= 2; //ESP8266: 512 if <= 8 segs ESP32: 1280 if <= 16 segs if (segs <= (strip.getMaxSegments() /2)) maxData *= 2; //ESP8266: 512 if <= 8 segs ESP32: 1280 if <= 16 segs
@ -3237,6 +3255,7 @@ static const char _data_FX_MODE_STARBURST[] PROGMEM = "Fireworks Starburst@Chanc
*/ */
uint16_t mode_exploding_fireworks(void) uint16_t mode_exploding_fireworks(void)
{ {
if (SEGLEN == 1) return mode_static();
const uint16_t cols = strip.isMatrix ? SEGMENT.virtualWidth() : 1; const uint16_t cols = strip.isMatrix ? SEGMENT.virtualWidth() : 1;
const uint16_t rows = strip.isMatrix ? SEGMENT.virtualHeight() : SEGMENT.virtualLength(); const uint16_t rows = strip.isMatrix ? SEGMENT.virtualHeight() : SEGMENT.virtualLength();
@ -3371,6 +3390,7 @@ static const char _data_FX_MODE_EXPLODING_FIREWORKS[] PROGMEM = "Fireworks 1D@Gr
*/ */
uint16_t mode_drip(void) uint16_t mode_drip(void)
{ {
if (SEGLEN == 1) return mode_static();
//allocate segment data //allocate segment data
uint16_t strips = SEGMENT.nrOfVStrips(); uint16_t strips = SEGMENT.nrOfVStrips();
const int maxNumDrops = 4; const int maxNumDrops = 4;
@ -3466,6 +3486,7 @@ typedef struct Tetris {
} tetris; } tetris;
uint16_t mode_tetrix(void) { uint16_t mode_tetrix(void) {
if (SEGLEN == 1) return mode_static();
uint16_t strips = SEGMENT.nrOfVStrips(); // allow running on virtual strips (columns in 2D segment) uint16_t strips = SEGMENT.nrOfVStrips(); // allow running on virtual strips (columns in 2D segment)
uint16_t dataSize = sizeof(tetris); uint16_t dataSize = sizeof(tetris);
if (!SEGENV.allocateData(dataSize * strips)) return mode_static(); //allocation failed if (!SEGENV.allocateData(dataSize * strips)) return mode_static(); //allocation failed
@ -3508,7 +3529,7 @@ uint16_t mode_tetrix(void) {
if (drop->pos > drop->stack) { // fall until top of stack if (drop->pos > drop->stack) { // fall until top of stack
drop->pos -= drop->speed; // may add gravity as: speed += gravity drop->pos -= drop->speed; // may add gravity as: speed += gravity
if (int(drop->pos) < int(drop->stack)) drop->pos = drop->stack; if (int(drop->pos) < int(drop->stack)) drop->pos = drop->stack;
for (int i=int(drop->pos); i<SEGLEN; i++) { for (int i = int(drop->pos); i < SEGLEN; i++) {
uint32_t col = i<int(drop->pos)+drop->brick ? SEGMENT.color_from_palette(drop->col, false, false, 0) : SEGCOLOR(1); uint32_t col = i<int(drop->pos)+drop->brick ? SEGMENT.color_from_palette(drop->col, false, false, 0) : SEGCOLOR(1);
SEGMENT.setPixelColor(indexToVStrip(i, stripNr), col); SEGMENT.setPixelColor(indexToVStrip(i, stripNr), col);
} }
@ -3523,7 +3544,7 @@ uint16_t mode_tetrix(void) {
drop->brick = 0; // reset brick size (no more growing) drop->brick = 0; // reset brick size (no more growing)
if (drop->step > millis()) { if (drop->step > millis()) {
// allow fading of virtual strip // allow fading of virtual strip
for (int i=0; i<SEGLEN; i++) SEGMENT.blendPixelColor(indexToVStrip(i, stripNr), SEGCOLOR(1), 25); // 10% blend for (int i = 0; i < SEGLEN; i++) SEGMENT.blendPixelColor(indexToVStrip(i, stripNr), SEGCOLOR(1), 25); // 10% blend
} else { } else {
drop->stack = 0; // reset brick stack size drop->stack = 0; // reset brick stack size
drop->step = 0; // proceed with next brick drop->step = 0; // proceed with next brick
@ -3650,7 +3671,7 @@ uint16_t mode_heartbeat(void) {
return FRAMETIME; return FRAMETIME;
} }
static const char _data_FX_MODE_HEARTBEAT[] PROGMEM = "Heartbeat@!,!;!,!;!;;m12=1"; static const char _data_FX_MODE_HEARTBEAT[] PROGMEM = "Heartbeat@!,!;!,!;!;01;m12=1";
// "Pacifica" // "Pacifica"
@ -3774,6 +3795,7 @@ static const char _data_FX_MODE_PACIFICA[] PROGMEM = "Pacifica@!,Angle;;!;;pal=5
* Mode simulates a gradual sunrise * Mode simulates a gradual sunrise
*/ */
uint16_t mode_sunrise() { uint16_t mode_sunrise() {
if (SEGLEN == 1) return mode_static();
//speed 0 - static sun //speed 0 - static sun
//speed 1 - 60: sunrise time in minutes //speed 1 - 60: sunrise time in minutes
//speed 60 - 120 : sunset time in minutes - 60; //speed 60 - 120 : sunset time in minutes - 60;
@ -3867,7 +3889,7 @@ static const char _data_FX_MODE_PHASEDNOISE[] PROGMEM = "Phased Noise@!,!;!,!;!"
uint16_t mode_twinkleup(void) { // A very short twinkle routine with fade-in and dual controls. By Andrew Tuline. uint16_t mode_twinkleup(void) { // A very short twinkle routine with fade-in and dual controls. By Andrew Tuline.
random16_set_seed(535); // The randomizer needs to be re-set each time through the loop in order for the same 'random' numbers to be the same each time through. random16_set_seed(535); // The randomizer needs to be re-set each time through the loop in order for the same 'random' numbers to be the same each time through.
for (int i = 0; i<SEGLEN; i++) { for (int i = 0; i < SEGLEN; i++) {
uint8_t ranstart = random8(); // The starting value (aka brightness) for each pixel. Must be consistent each time through the loop for this to work. uint8_t ranstart = random8(); // The starting value (aka brightness) for each pixel. Must be consistent each time through the loop for this to work.
uint8_t pixBri = sin8(ranstart + 16 * strip.now/(256-SEGMENT.speed)); uint8_t pixBri = sin8(ranstart + 16 * strip.now/(256-SEGMENT.speed));
if (random8() > SEGMENT.intensity) pixBri = 0; if (random8() > SEGMENT.intensity) pixBri = 0;
@ -3929,7 +3951,7 @@ uint16_t mode_sinewave(void) { // Adjustable sinewave. By Andrew Tul
SEGENV.step += SEGMENT.speed/16; // Speed of animation. SEGENV.step += SEGMENT.speed/16; // Speed of animation.
uint16_t freq = SEGMENT.intensity/4;//SEGMENT.fft2/8; // Frequency of the signal. uint16_t freq = SEGMENT.intensity/4;//SEGMENT.fft2/8; // Frequency of the signal.
for (int i=0; i<SEGLEN; i++) { // For each of the LED's in the strand, set a brightness based on a wave as follows: for (int i = 0; i < SEGLEN; i++) { // For each of the LED's in the strand, set a brightness based on a wave as follows:
int pixBri = cubicwave8((i*freq)+SEGENV.step);//qsuba(cubicwave8((i*freq)+SEGENV.step), (255-SEGMENT.intensity)); // qsub sets a minimum value called thiscutoff. If < thiscutoff, then bright = 0. Otherwise, bright = 128 (as defined in qsub).. int pixBri = cubicwave8((i*freq)+SEGENV.step);//qsuba(cubicwave8((i*freq)+SEGENV.step), (255-SEGMENT.intensity)); // qsub sets a minimum value called thiscutoff. If < thiscutoff, then bright = 0. Otherwise, bright = 128 (as defined in qsub)..
//setPixCol(i, i*colorIndex/255, pixBri); //setPixCol(i, i*colorIndex/255, pixBri);
SEGMENT.setPixelColor(i, color_blend(SEGCOLOR(1), SEGMENT.color_from_palette(i*colorIndex/255, false, PALETTE_SOLID_WRAP, 0), pixBri)); SEGMENT.setPixelColor(i, color_blend(SEGCOLOR(1), SEGMENT.color_from_palette(i*colorIndex/255, false, PALETTE_SOLID_WRAP, 0), pixBri));
@ -3984,6 +4006,7 @@ static const char _data_FX_MODE_FLOW[] PROGMEM = "Flow@!,Zones;;!;;m12=1"; //ver
*/ */
uint16_t mode_chunchun(void) uint16_t mode_chunchun(void)
{ {
if (SEGLEN == 1) return mode_static();
SEGMENT.fade_out(254); // add a bit of trail SEGMENT.fade_out(254); // add a bit of trail
uint16_t counter = strip.now * (6 + (SEGMENT.speed >> 4)); uint16_t counter = strip.now * (6 + (SEGMENT.speed >> 4));
uint16_t numBirds = 2 + (SEGLEN >> 3); // 2 + 1/8 of a segment uint16_t numBirds = 2 + (SEGLEN >> 3); // 2 + 1/8 of a segment
@ -4035,6 +4058,7 @@ typedef struct Spotlight {
*/ */
uint16_t mode_dancing_shadows(void) uint16_t mode_dancing_shadows(void)
{ {
if (SEGLEN == 1) return mode_static();
uint8_t numSpotlights = map(SEGMENT.intensity, 0, 255, 2, SPOT_MAX_COUNT); // 49 on 32 segment ESP32, 17 on 16 segment ESP8266 uint8_t numSpotlights = map(SEGMENT.intensity, 0, 255, 2, SPOT_MAX_COUNT); // 49 on 32 segment ESP32, 17 on 16 segment ESP8266
bool initialize = SEGENV.aux0 != numSpotlights; bool initialize = SEGENV.aux0 != numSpotlights;
SEGENV.aux0 = numSpotlights; SEGENV.aux0 = numSpotlights;
@ -4161,7 +4185,7 @@ uint16_t mode_washing_machine(void) {
SEGENV.step += (speed * 128.0f); SEGENV.step += (speed * 128.0f);
for (int i=0; i<SEGLEN; i++) { for (int i = 0; i < SEGLEN; i++) {
uint8_t col = sin8(((SEGMENT.intensity / 25 + 1) * 255 * i / SEGLEN) + (SEGENV.step >> 7)); uint8_t col = sin8(((SEGMENT.intensity / 25 + 1) * 255 * i / SEGLEN) + (SEGENV.step >> 7));
SEGMENT.setPixelColor(i, SEGMENT.color_from_palette(col, false, PALETTE_SOLID_WRAP, 3)); SEGMENT.setPixelColor(i, SEGMENT.color_from_palette(col, false, PALETTE_SOLID_WRAP, 3));
} }
@ -4498,7 +4522,7 @@ static const char _data_FX_MODE_AURORA[] PROGMEM = "Aurora@!,!;1,2,3;!;;sx=24,pa
// 16 bit perlinmove. Use Perlin Noise instead of sinewaves for movement. By Andrew Tuline. // 16 bit perlinmove. Use Perlin Noise instead of sinewaves for movement. By Andrew Tuline.
// Controls are speed, # of pixels, faderate. // Controls are speed, # of pixels, faderate.
uint16_t mode_perlinmove(void) { uint16_t mode_perlinmove(void) {
if (SEGLEN == 1) return mode_static();
SEGMENT.fade_out(255-SEGMENT.custom1); SEGMENT.fade_out(255-SEGMENT.custom1);
for (int i = 0; i < SEGMENT.intensity/16 + 1; i++) { for (int i = 0; i < SEGMENT.intensity/16 + 1; i++) {
uint16_t locn = inoise16(millis()*128/(260-SEGMENT.speed)+i*15000, millis()*128/(260-SEGMENT.speed)); // Get a new pixel location from moving noise. uint16_t locn = inoise16(millis()*128/(260-SEGMENT.speed)+i*15000, millis()*128/(260-SEGMENT.speed)); // Get a new pixel location from moving noise.
@ -6354,7 +6378,7 @@ uint16_t mode_matripix(void) { // Matripix. By Andrew Tuline.
SEGENV.aux0 = secondHand; SEGENV.aux0 = secondHand;
int pixBri = volumeRaw * SEGMENT.intensity / 64; int pixBri = volumeRaw * SEGMENT.intensity / 64;
for (int i=0; i<SEGLEN-1; i++) SEGMENT.setPixelColor(i, SEGMENT.getPixelColor(i+1)); // shift left for (int i = 0; i < SEGLEN-1; i++) SEGMENT.setPixelColor(i, SEGMENT.getPixelColor(i+1)); // shift left
SEGMENT.setPixelColor(SEGLEN-1, color_blend(SEGCOLOR(1), SEGMENT.color_from_palette(millis(), false, PALETTE_SOLID_WRAP, 0), pixBri)); SEGMENT.setPixelColor(SEGLEN-1, color_blend(SEGCOLOR(1), SEGMENT.color_from_palette(millis(), false, PALETTE_SOLID_WRAP, 0), pixBri));
} }
@ -6525,7 +6549,7 @@ uint16_t mode_plasmoid(void) { // Plasmoid. By Andrew Tuline.
plasmoip->thisphase += beatsin8(6,-4,4); // You can change direction and speed individually. plasmoip->thisphase += beatsin8(6,-4,4); // You can change direction and speed individually.
plasmoip->thatphase += beatsin8(7,-4,4); // Two phase values to make a complex pattern. By Andrew Tuline. plasmoip->thatphase += beatsin8(7,-4,4); // Two phase values to make a complex pattern. By Andrew Tuline.
for (int i=0; i<SEGLEN; i++) { // For each of the LED's in the strand, set a brightness based on a wave as follows. for (int i = 0; i < SEGLEN; i++) { // For each of the LED's in the strand, set a brightness based on a wave as follows.
// updated, similar to "plasma" effect - softhack007 // updated, similar to "plasma" effect - softhack007
uint8_t thisbright = cubicwave8(((i*(1 + (3*SEGMENT.speed/32)))+plasmoip->thisphase) & 0xFF)/2; uint8_t thisbright = cubicwave8(((i*(1 + (3*SEGMENT.speed/32)))+plasmoip->thisphase) & 0xFF)/2;
thisbright += cos8(((i*(97 +(5*SEGMENT.speed/32)))+plasmoip->thatphase) & 0xFF)/2; // Let's munge the brightness a bit and animate it all with the phases. thisbright += cos8(((i*(97 +(5*SEGMENT.speed/32)))+plasmoip->thatphase) & 0xFF)/2; // Let's munge the brightness a bit and animate it all with the phases.
@ -7078,7 +7102,7 @@ uint16_t mode_waterfall(void) { // Waterfall. By: Andrew Tulin
} else { } else {
SEGMENT.setPixelColor(SEGLEN-1, color_blend(SEGCOLOR(1), SEGMENT.color_from_palette(pixCol+SEGMENT.intensity, false, PALETTE_SOLID_WRAP, 0), (int)my_magnitude)); SEGMENT.setPixelColor(SEGLEN-1, color_blend(SEGCOLOR(1), SEGMENT.color_from_palette(pixCol+SEGMENT.intensity, false, PALETTE_SOLID_WRAP, 0), (int)my_magnitude));
} }
for (int i=0; i<SEGLEN-1; i++) SEGMENT.setPixelColor(i, SEGMENT.getPixelColor(i+1)); // shift left for (int i = 0; i < SEGLEN-1; i++) SEGMENT.setPixelColor(i, SEGMENT.getPixelColor(i+1)); // shift left
} }
return FRAMETIME; return FRAMETIME;

View File

@ -72,7 +72,11 @@
#ifndef MAX_NUM_SEGMENTS #ifndef MAX_NUM_SEGMENTS
#define MAX_NUM_SEGMENTS 32 #define MAX_NUM_SEGMENTS 32
#endif #endif
#define MAX_SEGMENT_DATA 32767 #if defined(ARDUINO_ARCH_ESP32S2)
#define MAX_SEGMENT_DATA 24576
#else
#define MAX_SEGMENT_DATA 32767
#endif
#endif #endif
/* How much data bytes each segment should max allocate to leave enough space for other segments, /* How much data bytes each segment should max allocate to leave enough space for other segments,
@ -496,6 +500,9 @@ typedef struct Segment {
inline bool isSelected(void) const { return selected; } inline bool isSelected(void) const { return selected; }
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 hasWhite(void) const { return _hasW; }
inline bool isCCT(void) const { return _isCCT; }
inline uint16_t width(void) const { return stop - start; } // segment width in physical pixels (length if 1D) inline uint16_t width(void) const { return stop - start; } // segment width in physical pixels (length if 1D)
inline uint16_t height(void) const { return stopY - startY; } // segment height (if 2D) in physical pixels inline uint16_t height(void) const { return stopY - startY; } // segment height (if 2D) in physical pixels
inline uint16_t length(void) const { return width() * height(); } // segment length (count) in physical pixels inline uint16_t length(void) const { return width() * height(); } // segment length (count) in physical pixels
@ -711,7 +718,6 @@ class WS2812FX { // 96 bytes
finalizeInit(), finalizeInit(),
service(void), service(void),
setMode(uint8_t segid, uint8_t m), setMode(uint8_t segid, uint8_t m),
setColor(uint8_t slot, uint8_t r, uint8_t g, uint8_t b, uint8_t w = 0),
setColor(uint8_t slot, uint32_t c), setColor(uint8_t slot, uint32_t c),
setCCT(uint16_t k), setCCT(uint16_t k),
setBrightness(uint8_t b, bool direct = false), setBrightness(uint8_t b, bool direct = false),
@ -728,7 +734,8 @@ class WS2812FX { // 96 bytes
show(void), show(void),
setTargetFps(uint8_t fps); setTargetFps(uint8_t fps);
void fill(uint32_t c) { for (int i = 0; i < _length; i++) setPixelColor(i, c); } // fill whole strip with color (inline) void setColor(uint8_t slot, uint8_t r, uint8_t g, uint8_t b, uint8_t w = 0) { setColor(slot, RGBW32(r,g,b,w)); }
void fill(uint32_t c) { for (int i = 0; i < getLengthTotal(); i++) setPixelColor(i, c); } // fill whole strip with color (inline)
void addEffect(uint8_t id, mode_ptr mode_fn, const char *mode_name); // add effect to the list; defined in FX.cpp void addEffect(uint8_t id, mode_ptr mode_fn, const char *mode_name); // add effect to the list; defined in FX.cpp
void setupEffectData(void); // add default effects to the list; defined in FX.cpp void setupEffectData(void); // add default effects to the list; defined in FX.cpp
@ -776,17 +783,17 @@ class WS2812FX { // 96 bytes
ablMilliampsMax, ablMilliampsMax,
currentMilliamps, currentMilliamps,
getLengthPhysical(void), getLengthPhysical(void),
getLengthTotal(void), // will include virtual/nonexistent pixels in matrix
getFps(); getFps();
inline uint16_t getFrameTime(void) { return _frametime; } inline uint16_t getFrameTime(void) { return _frametime; }
inline uint16_t getMinShowDelay(void) { return MIN_SHOW_DELAY; } inline uint16_t getMinShowDelay(void) { return MIN_SHOW_DELAY; }
inline uint16_t getLengthTotal(void) { return _length; } inline uint16_t getLength(void) { return _length; } // 2D matrix may have less pixels than W*H
inline uint16_t getTransition(void) { return _transitionDur; } inline uint16_t getTransition(void) { return _transitionDur; }
uint32_t uint32_t
now, now,
timebase, timebase,
currentColor(uint32_t colorNew, uint8_t tNr),
getPixelColor(uint16_t); getPixelColor(uint16_t);
inline uint32_t getLastShow(void) { return _lastShow; } inline uint32_t getLastShow(void) { return _lastShow; }
@ -826,6 +833,13 @@ class WS2812FX { // 96 bytes
bool serpentine : 1; // is serpentine? bool serpentine : 1; // is serpentine?
}; };
}; };
panel_t()
: xOffset(0)
, yOffset(0)
, width(8)
, height(8)
, options(0)
{}
} Panel; } Panel;
std::vector<Panel> panel; std::vector<Panel> panel;
#endif #endif

View File

@ -156,17 +156,16 @@ void WS2812FX::setUpMatrix() {
} }
// absolute matrix version of setPixelColor() // absolute matrix version of setPixelColor()
void IRAM_ATTR WS2812FX::setPixelColorXY(int x, int y, uint32_t col) void /*IRAM_ATTR*/ WS2812FX::setPixelColorXY(int x, int y, uint32_t col)
{ {
#ifndef WLED_DISABLE_2D #ifndef WLED_DISABLE_2D
if (!isMatrix) return; // not a matrix set-up if (!isMatrix) return; // not a matrix set-up
uint16_t index = y * Segment::maxWidth + x; uint16_t index = y * Segment::maxWidth + x;
if (index >= customMappingSize) return;
#else #else
uint16_t index = x; uint16_t index = x;
if (index >= _length) return;
#endif #endif
if (index < customMappingSize) index = customMappingTable[index]; if (index < customMappingSize) index = customMappingTable[index];
if (index >= _length) return;
busses.setPixelColor(index, col); busses.setPixelColor(index, col);
} }
@ -174,12 +173,11 @@ void IRAM_ATTR WS2812FX::setPixelColorXY(int x, int y, uint32_t col)
uint32_t WS2812FX::getPixelColorXY(uint16_t x, uint16_t y) { uint32_t WS2812FX::getPixelColorXY(uint16_t x, uint16_t y) {
#ifndef WLED_DISABLE_2D #ifndef WLED_DISABLE_2D
uint16_t index = (y * Segment::maxWidth + x); uint16_t index = (y * Segment::maxWidth + x);
if (index >= customMappingSize) return 0; // customMappingSize is always W * H of matrix in 2D setup
#else #else
uint16_t index = x; uint16_t index = x;
if (index >= _length) return 0;
#endif #endif
if (index < customMappingSize) index = customMappingTable[index]; if (index < customMappingSize) index = customMappingTable[index];
if (index >= _length) return 0;
return busses.getPixelColor(index); return busses.getPixelColor(index);
} }
@ -190,13 +188,13 @@ 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 uint16_t width = virtualWidth(); // segment width in logical pixels
uint16_t height = virtualHeight(); // segment height in logical pixels uint16_t height = virtualHeight(); // segment height in logical pixels
return (x%width) + (y%height) * width; return (x%width) + (y%height) * width;
} }
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 (Segment::maxHeight==1) return; // not a matrix set-up if (Segment::maxHeight==1) return; // not a matrix set-up
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

View File

@ -81,7 +81,7 @@ uint16_t Segment::maxHeight = 1;
// copy constructor // copy constructor
Segment::Segment(const Segment &orig) { Segment::Segment(const Segment &orig) {
//DEBUG_PRINTLN(F("-- Copy segment constructor --")); //DEBUG_PRINTLN(F("-- Copy segment constructor --"));
memcpy(this, &orig, sizeof(Segment)); memcpy((void*)this, (void*)&orig, sizeof(Segment));
name = nullptr; name = nullptr;
data = nullptr; data = nullptr;
_dataLen = 0; _dataLen = 0;
@ -96,7 +96,7 @@ Segment::Segment(const Segment &orig) {
// move constructor // move constructor
Segment::Segment(Segment &&orig) noexcept { Segment::Segment(Segment &&orig) noexcept {
//DEBUG_PRINTLN(F("-- Move segment constructor --")); //DEBUG_PRINTLN(F("-- Move segment constructor --"));
memcpy(this, &orig, sizeof(Segment)); memcpy((void*)this, (void*)&orig, sizeof(Segment));
orig.name = nullptr; orig.name = nullptr;
orig.data = nullptr; orig.data = nullptr;
orig._dataLen = 0; orig._dataLen = 0;
@ -114,7 +114,7 @@ Segment& Segment::operator= (const Segment &orig) {
if (leds && !Segment::_globalLeds) free(leds); if (leds && !Segment::_globalLeds) free(leds);
deallocateData(); deallocateData();
// copy source // copy source
memcpy(this, &orig, sizeof(Segment)); memcpy((void*)this, (void*)&orig, sizeof(Segment));
// erase pointers to allocated data // erase pointers to allocated data
name = nullptr; name = nullptr;
data = nullptr; data = nullptr;
@ -138,7 +138,7 @@ Segment& Segment::operator= (Segment &&orig) noexcept {
deallocateData(); // free old runtime data deallocateData(); // free old runtime data
if (_t) delete _t; if (_t) delete _t;
if (leds && !Segment::_globalLeds) free(leds); if (leds && !Segment::_globalLeds) free(leds);
memcpy(this, &orig, sizeof(Segment)); memcpy((void*)this, (void*)&orig, sizeof(Segment));
orig.name = nullptr; orig.name = nullptr;
orig.data = nullptr; orig.data = nullptr;
orig._dataLen = 0; orig._dataLen = 0;
@ -184,7 +184,8 @@ void Segment::deallocateData() {
void Segment::resetIfRequired() { void Segment::resetIfRequired() {
if (reset) { if (reset) {
if (leds && !Segment::_globalLeds) { free(leds); leds = nullptr; } if (leds && !Segment::_globalLeds) { free(leds); leds = nullptr; }
//if (_t) { delete _t; _t = nullptr; transitional = false; } if (transitional && _t) { transitional = false; delete _t; _t = nullptr; }
deallocateData();
next_time = 0; step = 0; call = 0; aux0 = 0; aux1 = 0; next_time = 0; step = 0; call = 0; aux0 = 0; aux1 = 0;
reset = false; // setOption(SEG_OPTION_RESET, false); reset = false; // setOption(SEG_OPTION_RESET, false);
} }
@ -415,6 +416,10 @@ void Segment::set(uint16_t i1, uint16_t i2, uint8_t grp, uint8_t spc, uint16_t o
bool Segment::setColor(uint8_t slot, uint32_t c) { //returns true if changed bool Segment::setColor(uint8_t slot, uint32_t c) { //returns true if changed
if (slot >= NUM_COLORS || c == colors[slot]) return false; if (slot >= NUM_COLORS || c == colors[slot]) return false;
if (!_isRGB && !_hasW) {
if (slot == 0 && c == BLACK) return false; // on/off segment cannot have primary color black
if (slot == 1 && c != BLACK) return false; // on/off segment cannot have secondary color non black
}
if (fadeTransition) startTransition(strip.getTransition()); // start transition prior to change if (fadeTransition) startTransition(strip.getTransition()); // start transition prior to change
colors[slot] = c; colors[slot] = c;
stateChanged = true; // send UDP/WS broadcast stateChanged = true; // send UDP/WS broadcast
@ -546,7 +551,9 @@ uint16_t Segment::virtualLength() const {
void IRAM_ATTR Segment::setPixelColor(int i, uint32_t col) void IRAM_ATTR Segment::setPixelColor(int i, uint32_t col)
{ {
#ifndef WLED_DISABLE_2D
int vStrip = i>>16; // hack to allow running on virtual strips (2D segment columns/rows) int vStrip = i>>16; // hack to allow running on virtual strips (2D segment columns/rows)
#endif
i &= 0xFFFF; i &= 0xFFFF;
if (i >= virtualLength() || i<0) return; // if pixel would fall out of segment just exit if (i >= virtualLength() || i<0) return; // if pixel would fall out of segment just exit
@ -686,7 +693,9 @@ void Segment::setPixelColor(float i, uint32_t col, bool aa)
uint32_t Segment::getPixelColor(int i) uint32_t Segment::getPixelColor(int i)
{ {
#ifndef WLED_DISABLE_2D
int vStrip = i>>16; int vStrip = i>>16;
#endif
i &= 0xFFFF; i &= 0xFFFF;
#ifndef WLED_DISABLE_2D #ifndef WLED_DISABLE_2D
@ -762,6 +771,7 @@ void Segment::refreshLightCapabilities() {
if (segStartIdx > index) segStartIdx = index; if (segStartIdx > index) segStartIdx = index;
if (segStopIdx < index) segStopIdx = index; if (segStopIdx < index) segStopIdx = index;
} }
if (segStartIdx == segStopIdx) segStopIdx++; // we only have 1 pixel segment
} }
} else { } else {
// we are on the strip located after the matrix // we are on the strip located after the matrix
@ -1043,7 +1053,7 @@ void WS2812FX::finalizeInit(void)
Segment::_globalLeds = nullptr; Segment::_globalLeds = nullptr;
} }
if (useLedsArray) { if (useLedsArray) {
size_t arrSize = sizeof(CRGB) * MAX(_length, Segment::maxWidth*Segment::maxHeight); size_t arrSize = sizeof(CRGB) * getLengthTotal();
#if defined(ARDUINO_ARCH_ESP32) && defined(WLED_USE_PSRAM) #if defined(ARDUINO_ARCH_ESP32) && defined(WLED_USE_PSRAM)
if (psramFound()) if (psramFound())
Segment::_globalLeds = (CRGB*) ps_malloc(arrSize); Segment::_globalLeds = (CRGB*) ps_malloc(arrSize);
@ -1117,15 +1127,15 @@ void WS2812FX::service() {
void IRAM_ATTR WS2812FX::setPixelColor(int i, uint32_t col) void IRAM_ATTR WS2812FX::setPixelColor(int i, uint32_t col)
{ {
if (i >= _length) return;
if (i < customMappingSize) i = customMappingTable[i]; if (i < customMappingSize) i = customMappingTable[i];
if (i >= _length) return;
busses.setPixelColor(i, col); busses.setPixelColor(i, col);
} }
uint32_t WS2812FX::getPixelColor(uint16_t i) uint32_t WS2812FX::getPixelColor(uint16_t i)
{ {
if (i >= _length) return 0;
if (i < customMappingSize) i = customMappingTable[i]; if (i < customMappingSize) i = customMappingTable[i];
if (i >= _length) return 0;
return busses.getPixelColor(i); return busses.getPixelColor(i);
} }
@ -1338,6 +1348,12 @@ uint8_t WS2812FX::getActiveSegmentsNum(void) {
return c; return c;
} }
uint16_t WS2812FX::getLengthTotal(void) {
uint16_t len = Segment::maxWidth * Segment::maxHeight; // will be _length for 1D (see finalizeInit()) but should cover whole matrix for 2D
if (isMatrix && _length > len) len = _length; // for 2D with trailing strip
return len;
}
uint16_t WS2812FX::getLengthPhysical(void) { uint16_t WS2812FX::getLengthPhysical(void) {
uint16_t len = 0; uint16_t len = 0;
for (size_t b = 0; b < busses.getNumBusses(); b++) { for (size_t b = 0; b < busses.getNumBusses(); b++) {
@ -1380,7 +1396,6 @@ void WS2812FX::purgeSegments(bool force) {
if (_segments.size() <= 1) return; if (_segments.size() <= 1) return;
for (size_t i = _segments.size()-1; i > 0; i--) for (size_t i = _segments.size()-1; i > 0; i--)
if (_segments[i].stop == 0 || force) { if (_segments[i].stop == 0 || force) {
DEBUG_PRINT(F("Purging segment segment: ")); DEBUG_PRINTLN(i);
deleted++; deleted++;
_segments.erase(_segments.begin() + i); _segments.erase(_segments.begin() + i);
} }
@ -1415,45 +1430,31 @@ void WS2812FX::resetSegments() {
} }
void WS2812FX::makeAutoSegments(bool forceReset) { void WS2812FX::makeAutoSegments(bool forceReset) {
if (isMatrix) { if (autoSegments) { //make one segment per bus
#ifndef WLED_DISABLE_2D
// only create 1 2D segment
if (forceReset || getSegmentsNum() == 0) resetSegments(); // initialises 1 segment
else if (getActiveSegmentsNum() == 1) {
size_t i = getLastActiveSegmentId();
_segments[i].start = 0;
_segments[i].stop = Segment::maxWidth;
_segments[i].startY = 0;
_segments[i].stopY = Segment::maxHeight;
_segments[i].grouping = 1;
_segments[i].spacing = 0;
_mainSegment = i;
}
// do we have LEDs after the matrix? (ignore buses)
if (autoSegments && _length > Segment::maxWidth*Segment::maxHeight /*&& getActiveSegmentsNum() == 2*/) {
if (_segments.size() == getLastActiveSegmentId()+1U) {
_segments.push_back(Segment(Segment::maxWidth*Segment::maxHeight, _length));
} else {
size_t i = getLastActiveSegmentId() + 1;
_segments[i].start = Segment::maxWidth*Segment::maxHeight;
_segments[i].stop = _length;
_segments[i].startY = 0;
_segments[i].stopY = 1;
_segments[i].grouping = 1;
_segments[i].spacing = 0;
}
}
#endif
} else if (autoSegments) { //make one segment per bus
uint16_t segStarts[MAX_NUM_SEGMENTS] = {0}; uint16_t segStarts[MAX_NUM_SEGMENTS] = {0};
uint16_t segStops [MAX_NUM_SEGMENTS] = {0}; uint16_t segStops [MAX_NUM_SEGMENTS] = {0};
uint8_t s = 0; size_t s = 0;
for (uint8_t i = 0; i < busses.getNumBusses(); i++) {
#ifndef WLED_DISABLE_2D
// 2D segment is the 1st one using entire matrix
if (isMatrix) {
segStarts[0] = 0;
segStops[0] = Segment::maxWidth*Segment::maxHeight;
s++;
}
#endif
for (size_t i = s; i < busses.getNumBusses(); i++) {
Bus* b = busses.getBus(i); Bus* b = busses.getBus(i);
segStarts[s] = b->getStart(); segStarts[s] = b->getStart();
segStops[s] = segStarts[s] + b->getLength(); segStops[s] = segStarts[s] + b->getLength();
#ifndef WLED_DISABLE_2D
if (isMatrix && segStops[s] < Segment::maxWidth*Segment::maxHeight) continue; // ignore buses comprising matrix
if (isMatrix && segStarts[s] < Segment::maxWidth*Segment::maxHeight) segStarts[s] = Segment::maxWidth*Segment::maxHeight;
#endif
//check for overlap with previous segments //check for overlap with previous segments
for (size_t j = 0; j < s; j++) { for (size_t j = 0; j < s; j++) {
if (segStops[j] > segStarts[s] && segStarts[j] < segStops[s]) { if (segStops[j] > segStarts[s] && segStarts[j] < segStops[s]) {
@ -1465,24 +1466,40 @@ void WS2812FX::makeAutoSegments(bool forceReset) {
} }
s++; s++;
} }
_segments.clear(); _segments.clear();
_segments.reserve(s); // prevent reallocations _segments.reserve(s); // prevent reallocations
for (size_t i = 0; i < s; i++) { // there is always at least one segment (but we need to differentiate between 1D and 2D)
Segment seg = Segment(segStarts[i], segStops[i]); #ifndef WLED_DISABLE_2D
seg.selected = true; if (isMatrix)
_segments.push_back(seg); _segments.push_back(Segment(0, Segment::maxWidth, 0, Segment::maxHeight));
else
#endif
_segments.push_back(Segment(segStarts[0], segStops[0]));
for (size_t i = 1; i < s; i++) {
_segments.push_back(Segment(segStarts[i], segStops[i]));
} }
_mainSegment = 0;
} else { } else {
if (forceReset || getSegmentsNum() == 0) resetSegments(); if (forceReset || getSegmentsNum() == 0) resetSegments();
//expand the main seg to the entire length, but only if there are no other segments, or reset is forced //expand the main seg to the entire length, but only if there are no other segments, or reset is forced
else if (getActiveSegmentsNum() == 1) { else if (getActiveSegmentsNum() == 1) {
size_t i = getLastActiveSegmentId(); size_t i = getLastActiveSegmentId();
#ifndef WLED_DISABLE_2D
_segments[i].start = 0;
_segments[i].stop = Segment::maxWidth;
_segments[i].startY = 0;
_segments[i].stopY = Segment::maxHeight;
_segments[i].grouping = 1;
_segments[i].spacing = 0;
#else
_segments[i].start = 0; _segments[i].start = 0;
_segments[i].stop = _length; _segments[i].stop = _length;
_mainSegment = 0; #endif
} }
} }
_mainSegment = 0;
fixInvalidSegments(); fixInvalidSegments();
} }
@ -1561,7 +1578,8 @@ void WS2812FX::printSize() {
DEBUG_PRINTF("Modes: %d*%d=%uB\n", sizeof(mode_ptr), _mode.size(), (_mode.capacity()*sizeof(mode_ptr))); DEBUG_PRINTF("Modes: %d*%d=%uB\n", sizeof(mode_ptr), _mode.size(), (_mode.capacity()*sizeof(mode_ptr)));
DEBUG_PRINTF("Data: %d*%d=%uB\n", sizeof(const char *), _modeData.size(), (_modeData.capacity()*sizeof(const char *))); DEBUG_PRINTF("Data: %d*%d=%uB\n", sizeof(const char *), _modeData.size(), (_modeData.capacity()*sizeof(const char *)));
DEBUG_PRINTF("Map: %d*%d=%uB\n", sizeof(uint16_t), (int)customMappingSize, customMappingSize*sizeof(uint16_t)); DEBUG_PRINTF("Map: %d*%d=%uB\n", sizeof(uint16_t), (int)customMappingSize, customMappingSize*sizeof(uint16_t));
if (useLedsArray) DEBUG_PRINTF("Buffer: %d*%d=%uB\n", sizeof(CRGB), (int)_length, _length*sizeof(CRGB)); size = getLengthTotal();
if (useLedsArray) DEBUG_PRINTF("Buffer: %d*%u=%uB\n", sizeof(CRGB), size, size*sizeof(CRGB));
} }
#endif #endif

View File

@ -361,7 +361,7 @@ void BusOnOff::setPixelColor(uint16_t pix, uint32_t c) {
uint8_t b = B(c); uint8_t b = B(c);
uint8_t w = W(c); uint8_t w = W(c);
_data = bool((r+g+b+w) && _bri) ? 0xFF : 0; _data = bool(r|g|b|w) && bool(_bri) ? 0xFF : 0;
} }
uint32_t BusOnOff::getPixelColor(uint16_t pix) { uint32_t BusOnOff::getPixelColor(uint16_t pix) {

View File

@ -57,39 +57,42 @@ void setRandomColor(byte* rgb)
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
{ {
float h = ((float)hue)/65535.0; float h = ((float)hue)/65535.0f;
float s = ((float)sat)/255.0; float s = ((float)sat)/255.0f;
byte i = floor(h*6); int i = floorf(h*6);
float f = h * 6-i; float f = h * 6.0f - i;
float p = 255 * (1-s); int p = int(255.0f * (1.0f-s));
float q = 255 * (1-f*s); int q = int(255.0f * (1.0f-f*s));
float t = 255 * (1-(1-f)*s); int t = int(255.0f * (1.0f-(1.0f-f)*s));
p = constrain(p, 0, 255);
q = constrain(q, 0, 255);
t = constrain(t, 0, 255);
switch (i%6) { switch (i%6) {
case 0: rgb[0]=255,rgb[1]=t,rgb[2]=p;break; case 0: rgb[0]=255,rgb[1]=t, rgb[2]=p; break;
case 1: rgb[0]=q,rgb[1]=255,rgb[2]=p;break; case 1: rgb[0]=q, rgb[1]=255,rgb[2]=p; break;
case 2: rgb[0]=p,rgb[1]=255,rgb[2]=t;break; case 2: rgb[0]=p, rgb[1]=255,rgb[2]=t; break;
case 3: rgb[0]=p,rgb[1]=q,rgb[2]=255;break; case 3: rgb[0]=p, rgb[1]=q, rgb[2]=255;break;
case 4: rgb[0]=t,rgb[1]=p,rgb[2]=255;break; case 4: rgb[0]=t, rgb[1]=p, rgb[2]=255;break;
case 5: rgb[0]=255,rgb[1]=p,rgb[2]=q; case 5: rgb[0]=255,rgb[1]=p, rgb[2]=q; break;
} }
} }
//get RGB values from color temperature in K (https://tannerhelland.com/2012/09/18/convert-temperature-rgb-algorithm-code.html) //get RGB values from color temperature in K (https://tannerhelland.com/2012/09/18/convert-temperature-rgb-algorithm-code.html)
void colorKtoRGB(uint16_t kelvin, byte* rgb) //white spectrum to rgb, calc void colorKtoRGB(uint16_t kelvin, byte* rgb) //white spectrum to rgb, calc
{ {
float r = 0, g = 0, b = 0; int r = 0, g = 0, b = 0;
float temp = kelvin / 100; float temp = kelvin / 100.0f;
if (temp <= 66) { if (temp <= 66.0f) {
r = 255; r = 255;
g = round(99.4708025861 * log(temp) - 161.1195681661); g = roundf(99.4708025861f * logf(temp) - 161.1195681661f);
if (temp <= 19) { if (temp <= 19.0f) {
b = 0; b = 0;
} else { } else {
b = round(138.5177312231 * log((temp - 10)) - 305.0447927307); b = roundf(138.5177312231f * logf((temp - 10.0f)) - 305.0447927307f);
} }
} else { } else {
r = round(329.698727446 * pow((temp - 60), -0.1332047592)); r = roundf(329.698727446f * powf((temp - 60.0f), -0.1332047592f));
g = round(288.1221695283 * pow((temp - 60), -0.0755148492)); g = roundf(288.1221695283f * powf((temp - 60.0f), -0.0755148492f));
b = 255; b = 255;
} }
//g += 12; //mod by Aircoookie, a bit less accurate but visibly less pinkish //g += 12; //mod by Aircoookie, a bit less accurate but visibly less pinkish
@ -147,9 +150,9 @@ void colorXYtoRGB(float x, float y, byte* rgb) //coordinates to rgb (https://www
b = 1.0f; b = 1.0f;
} }
// Apply gamma correction // Apply gamma correction
r = r <= 0.0031308f ? 12.92f * r : (1.0f + 0.055f) * pow(r, (1.0f / 2.4f)) - 0.055f; r = r <= 0.0031308f ? 12.92f * r : (1.0f + 0.055f) * powf(r, (1.0f / 2.4f)) - 0.055f;
g = g <= 0.0031308f ? 12.92f * g : (1.0f + 0.055f) * pow(g, (1.0f / 2.4f)) - 0.055f; g = g <= 0.0031308f ? 12.92f * g : (1.0f + 0.055f) * powf(g, (1.0f / 2.4f)) - 0.055f;
b = b <= 0.0031308f ? 12.92f * b : (1.0f + 0.055f) * pow(b, (1.0f / 2.4f)) - 0.055f; b = b <= 0.0031308f ? 12.92f * b : (1.0f + 0.055f) * powf(b, (1.0f / 2.4f)) - 0.055f;
if (r > b && r > g) { if (r > b && r > g) {
// red is biggest // red is biggest
@ -173,9 +176,9 @@ void colorXYtoRGB(float x, float y, byte* rgb) //coordinates to rgb (https://www
b = 1.0f; b = 1.0f;
} }
} }
rgb[0] = 255.0*r; rgb[0] = byte(255.0f*r);
rgb[1] = 255.0*g; rgb[1] = byte(255.0f*g);
rgb[2] = 255.0*b; rgb[2] = byte(255.0f*b);
} }
void colorRGBtoXY(byte* rgb, float* xy) //rgb to coordinates (https://www.developers.meethue.com/documentation/color-conversions-rgb-xy) void colorRGBtoXY(byte* rgb, float* xy) //rgb to coordinates (https://www.developers.meethue.com/documentation/color-conversions-rgb-xy)
@ -242,35 +245,13 @@ float maxf (float v, float w)
return v; return v;
} }
/*
uint32_t colorRGBtoRGBW(uint32_t c)
{
byte rgb[4];
rgb[0] = R(c);
rgb[1] = G(c);
rgb[2] = B(c);
rgb[3] = W(c);
colorRGBtoRGBW(rgb);
return RGBW32(rgb[0], rgb[1], rgb[2], rgb[3]);
}
void colorRGBtoRGBW(byte* rgb) //rgb to rgbw (http://codewelt.com/rgbw). (RGBW_MODE_LEGACY)
{
float low = minf(rgb[0],minf(rgb[1],rgb[2]));
float high = maxf(rgb[0],maxf(rgb[1],rgb[2]));
if (high < 0.1f) return;
float sat = 100.0f * ((high - low) / high); // maximum saturation is 100 (corrected from 255)
rgb[3] = (byte)((255.0f - sat) / 255.0f * (rgb[0] + rgb[1] + rgb[2]) / 3);
}
*/
byte correctionRGB[4] = {0,0,0,0};
uint16_t lastKelvin = 0;
// adjust RGB values based on color temperature in K (range [2800-10200]) (https://en.wikipedia.org/wiki/Color_balance) // adjust RGB values based on color temperature in K (range [2800-10200]) (https://en.wikipedia.org/wiki/Color_balance)
// called from bus manager when color correction is enabled!
uint32_t colorBalanceFromKelvin(uint16_t kelvin, uint32_t rgb) uint32_t colorBalanceFromKelvin(uint16_t kelvin, uint32_t rgb)
{ {
//remember so that slow colorKtoRGB() doesn't have to run for every setPixelColor() //remember so that slow colorKtoRGB() doesn't have to run for every setPixelColor()
static byte correctionRGB[4] = {0,0,0,0};
static uint16_t lastKelvin = 0;
if (lastKelvin != kelvin) colorKtoRGB(kelvin, correctionRGB); // convert Kelvin to RGB if (lastKelvin != kelvin) colorKtoRGB(kelvin, correctionRGB); // convert Kelvin to RGB
lastKelvin = kelvin; lastKelvin = kelvin;
byte rgbw[4]; byte rgbw[4];

View File

@ -404,8 +404,8 @@
#define JSON_BUFFER_SIZE 24576 #define JSON_BUFFER_SIZE 24576
#endif #endif
//#define MIN_HEAP_SIZE (MAX_LED_MEMORY+2048) //#define MIN_HEAP_SIZE (8k for AsyncWebServer)
#define MIN_HEAP_SIZE (8192) #define MIN_HEAP_SIZE 8192
// Maximum size of node map (list of other WLED instances) // Maximum size of node map (list of other WLED instances)
#ifdef ESP8266 #ifdef ESP8266

View File

@ -392,12 +392,16 @@ button {
} }
.slider { .slider {
background-color: var(--c-2);
max-width: 300px; max-width: 300px;
min-width: 280px; min-width: 260px;
margin: 0 auto; /* add 5px; if you want some vertical space but looks ugly */ margin: 0 auto; /* add 5px; if you want some vertical space but looks ugly */
border-radius: 24px; border-radius: 24px;
position: relative; position: relative;
padding-bottom: 2px;
}
#sliders .slider, #info .slider {
background-color: var(--c-2);
} }
.filter, .option { .filter, .option {
@ -425,14 +429,14 @@ button {
box-shadow: 4px 4px 10px 4px var(--c-1); box-shadow: 4px 4px 10px 4px var(--c-1);
color: var(--c-f); color: var(--c-f);
text-align: center; text-align: center;
padding: 5px 10px; padding: 4px 8px;
border-radius: 6px; border-radius: 6px;
/* Position the tooltip text */ /* Position the tooltip text */
width: 160px; width: 160px;
position: absolute; position: absolute;
z-index: 1; z-index: 1;
bottom: 100%; bottom: 80%;
left: 50%; left: 50%;
margin-left: -92px; margin-left: -92px;
@ -647,7 +651,7 @@ img {
#wbal .sliderdisplay { background: linear-gradient(90deg, #ff8f1f 0%, #fff 50%, #cbdbff); } #wbal .sliderdisplay { background: linear-gradient(90deg, #ff8f1f 0%, #fff 50%, #cbdbff); }
/* wrapper divs hidden by default */ /* wrapper divs hidden by default */
#rgbwrap, #kwrap, #wwrap, #wbal, #qcs-w, #hexw { #rgbwrap, #swrap, #hwrap, #kwrap, #wwrap, #wbal, #qcs-w, #hexw {
display: none; display: none;
} }
@ -731,7 +735,11 @@ input[type=range]::-moz-range-thumb {
#Colors .sliderwrap { #Colors .sliderwrap {
width: 260px; width: 260px;
margin: 10px 0 0; margin: 4px 0 0;
}
#Colors {
padding-top: 18px;
} }
/* Dynamically hide brightness slider label */ /* Dynamically hide brightness slider label */
@ -744,13 +752,14 @@ input[type=range]::-moz-range-thumb {
margin-top: var(--bmt); margin-top: var(--bmt);
} }
#picker, #rgbwrap, #kwrap, #wwrap, #wbal, #vwrap, #qcs-w, #hexw, #pall, #ledmap { #picker, #qcs-w, #hexw, #pall, #ledmap {
margin: 0 auto; margin: 0 auto;
width: 260px; width: 260px;
/*background-color: unset;*/
} }
#picker { #picker {
margin-top: 10px; margin-top: -10px !important;
} }
/* buttons */ /* buttons */
@ -1409,6 +1418,9 @@ TD .checkmark, TD .radiomark {
.expanded { .expanded {
display: inline-block !important; display: inline-block !important;
} }
.expanded .segin.hide, .expanded .presin.hide, .expanded .sbs.hide {
display: none !important;
}
.m6 { .m6 {
margin: 6px 0; margin: 6px 0;

View File

@ -88,68 +88,73 @@
<div class ="container"> <div class ="container">
<div id="Colors" class="tabcontent"> <div id="Colors" class="tabcontent">
<div id="picker" class="noslide"></div> <div id="picker" class="noslide"></div>
<div id="hwrap"> <div id="hwrap" class="slider">
<!--p class="labels hd">Hue</p-->
<div class="sliderwrap il"> <div class="sliderwrap il">
<input id="sliderH" class="noslide" oninput="fromH()" onchange="setColor(0)" max="359" min="0" type="range" value="0" step="any"> <input id="sliderH" class="noslide" oninput="fromH()" onchange="setColor(0)" max="359" min="0" type="range" value="0" step="any">
<div class="sliderdisplay" style="background: linear-gradient(90deg, #f00 2%, #ff0 19%, #0f0 35%, #0ff 52%, #00f 68%, #f0f 85%, #f00)"></div> <div class="sliderdisplay" style="background: linear-gradient(90deg, #f00 2%, #ff0 19%, #0f0 35%, #0ff 52%, #00f 68%, #f0f 85%, #f00)"></div>
</div><br> </div>
<span class="tooltiptext">Hue</span>
</div> </div>
<div id="swrap"> <div id="swrap" class="slider">
<!--p class="labels hd">Saturation</p-->
<div class="sliderwrap il"> <div class="sliderwrap il">
<input id="sliderS" class="noslide" oninput="fromS()" onchange="setColor(0)" max="100" min="0" type="range" value="100" step="any"> <input id="sliderS" class="noslide" oninput="fromS()" onchange="setColor(0)" max="100" min="0" type="range" value="100" step="any">
<div class="sliderdisplay" style="background: linear-gradient(90deg, #aaa 0%, #f00)"></div> <div class="sliderdisplay" style="background: linear-gradient(90deg, #aaa 0%, #f00)"></div>
</div><br> </div>
<span class="tooltiptext">Saturation</span>
</div> </div>
<div id="vwrap"> <div id="vwrap" class="slider">
<!--p class="labels hd">Value</p-->
<div class="sliderwrap il"> <div class="sliderwrap il">
<input id="sliderV" class="noslide" oninput="fromV()" onchange="setColor(0)" max="100" min="0" type="range" value="100" step="any" /> <input id="sliderV" class="noslide" oninput="fromV()" onchange="setColor(0)" max="100" min="0" type="range" value="100" step="any" />
<div class="sliderdisplay"></div> <div class="sliderdisplay"></div>
</div><br> </div>
<span class="tooltiptext">Value/Brightness</span>
</div> </div>
<div id="kwrap"> <div id="kwrap" class="slider">
<!--p class="labels hd">Temperature</p-->
<div class="sliderwrap il"> <div class="sliderwrap il">
<input id="sliderK" class="noslide" oninput="fromK()" onchange="setColor(0)" max="10091" min="1900" type="range" value="6550" /> <input id="sliderK" class="noslide" oninput="fromK()" onchange="setColor(0)" max="10091" min="1900" type="range" value="6550" />
<div class="sliderdisplay"></div> <div class="sliderdisplay"></div>
</div> </div>
<span class="tooltiptext">Kelvin/Temperature</span>
</div> </div>
<div id="rgbwrap"> <div id="rgbwrap">
<p class="labels hd">RGB color</p> <!--p class="labels hd">RGB color</p-->
<div id="rwrap" class="il"> <div id="rwrap" class="slider">
<div class="sliderwrap il"> <div class="sliderwrap il">
<input id="sliderR" class="noslide" oninput="fromRgb()" onchange="setColor(0)" max="255" min="0" type="range" value="128" /> <input id="sliderR" class="noslide" oninput="fromRgb()" onchange="setColor(0)" max="255" min="0" type="range" value="128" />
<div class="sliderdisplay"></div> <div class="sliderdisplay"></div>
</div> </div>
<span class="tooltiptext">Red channel</span>
</div> </div>
<div id="gwrap" class="il"> <div id="gwrap" class="slider">
<div class="sliderwrap il"> <div class="sliderwrap il">
<input id="sliderG" class="noslide" oninput="fromRgb()" onchange="setColor(0)" max="255" min="0" type="range" value="128" /> <input id="sliderG" class="noslide" oninput="fromRgb()" onchange="setColor(0)" max="255" min="0" type="range" value="128" />
<div class="sliderdisplay"></div> <div class="sliderdisplay"></div>
</div> </div>
<span class="tooltiptext">Green channel</span>
</div> </div>
<div id="bwrap" class="il"> <div id="bwrap" class="slider">
<div class="sliderwrap il"> <div class="sliderwrap il">
<input id="sliderB" class="noslide" oninput="fromRgb()" onchange="setColor(0)" max="255" min="0" type="range" value="128" /> <input id="sliderB" class="noslide" oninput="fromRgb()" onchange="setColor(0)" max="255" min="0" type="range" value="128" />
<div class="sliderdisplay"></div> <div class="sliderdisplay"></div>
</div> </div>
<span class="tooltiptext">Blue channel</span>
</div> </div>
</div> </div>
<div id="wwrap"> <div id="wwrap" class="slider">
<p class="labels hd">White channel</p> <!--p class="labels hd">White channel</p-->
<div id="whibri" class="sliderwrap il"> <div id="whibri" class="sliderwrap il">
<input id="sliderW" class="noslide" oninput="fromW()" onchange="setColor(0)" max="255" min="0" type="range" value="128" /> <input id="sliderW" class="noslide" oninput="fromW()" onchange="setColor(0)" max="255" min="0" type="range" value="128" />
<div class="sliderdisplay"></div> <div class="sliderdisplay"></div>
</div> </div>
<span class="tooltiptext">White channel</span>
</div> </div>
<div id="wbal"> <div id="wbal" class="slider">
<p class="labels hd">White balance</p> <!--p class="labels hd">White balance</p-->
<div class="sliderwrap il"> <div class="sliderwrap il">
<input id="sliderA" class="noslide" onchange="setBalance(this.value)" max="255" min="0" type="range" value="128" /> <input id="sliderA" class="noslide" onchange="setBalance(this.value)" max="255" min="0" type="range" value="128" />
<div class="sliderdisplay"></div> <div class="sliderdisplay"></div>
</div> </div>
<span class="tooltiptext">White balance</span>
</div> </div>
<div id="qcs-w"> <div id="qcs-w">
<div class="qcs" onclick="pC('#ff0000');" title="Red" style="background-color:#ff0000;"></div> <div class="qcs" onclick="pC('#ff0000');" title="Red" style="background-color:#ff0000;"></div>
@ -310,7 +315,7 @@
<div id="segutil2"> <div id="segutil2">
<button class="btn btn-s" id="rsbtn" onclick="rSegs()">Reset segments</button> <button class="btn btn-s" id="rsbtn" onclick="rSegs()">Reset segments</button>
</div> </div>
<p>Transition: <input id="tt" class="noslide" type="number" min="0" max="65.5" step="0.1" value="0.7">&nbsp;s</p> <p>Transition: <input id="tt" type="number" min="0" max="65.5" step="0.1" value="0.7">&nbsp;s</p>
<p id="ledmap" class="hide"></p> <p id="ledmap" class="hide"></p>
</div> </div>

View File

@ -4,6 +4,7 @@ var isOn = false, nlA = false, isLv = false, isInfo = false, isNodes = false, sy
var hasWhite = false, hasRGB = false, hasCCT = false; var hasWhite = false, hasRGB = false, hasCCT = false;
var nlDur = 60, nlTar = 0; var nlDur = 60, nlTar = 0;
var nlMode = false; var nlMode = false;
var segLmax = 0; // size (in pixels) of largest selected segment
var selectedFx = 0; var selectedFx = 0;
var selectedPal = 0; var selectedPal = 0;
var csel = 0; // selected color slot (0-2) var csel = 0; // selected color slot (0-2)
@ -60,13 +61,10 @@ function setCSL(cs)
let w = cs.dataset.w ? parseInt(cs.dataset.w) : 0; let w = cs.dataset.w ? parseInt(cs.dataset.w) : 0;
let hasShadow = getComputedStyle(cs).textShadow !== "none"; let hasShadow = getComputedStyle(cs).textShadow !== "none";
if (hasRGB && !isRgbBlack(cs.dataset)) { if (hasRGB && !isRgbBlack(cs.dataset)) {
cs.style.backgroundColor = rgbStr(cs.dataset);
if (!hasShadow) cs.style.color = rgbBri(cs.dataset) > 127 ? "#000":"#fff"; // if text has no CSS "shadow" if (!hasShadow) cs.style.color = rgbBri(cs.dataset) > 127 ? "#000":"#fff"; // if text has no CSS "shadow"
if (hasWhite && w > 0) { cs.style.background = (hasWhite && w > 0) ? `linear-gradient(180deg, ${rgbStr(cs.dataset)} 30%, rgb(${w},${w},${w}))` : rgbStr(cs.dataset);
cs.style.background = `linear-gradient(180deg, ${rgbStr(cs.dataset)} 30%, rgb(${w},${w},${w}))`;
}
} else { } else {
if (!hasWhite) w = 0; if (hasRGB && !hasWhite) w = 0;
cs.style.background = `rgb(${w},${w},${w})`; cs.style.background = `rgb(${w},${w},${w})`;
if (!hasShadow) cs.style.color = w > 127 ? "#000":"#fff"; if (!hasShadow) cs.style.color = w > 127 ? "#000":"#fff";
} }
@ -746,7 +744,7 @@ function populateSegments(s)
<i class="icons e-icon flr" id="sege${i}" onclick="expand(${i})">&#xe395;</i> <i class="icons e-icon flr" id="sege${i}" onclick="expand(${i})">&#xe395;</i>
${cfg.comp.segpwr?segp:''} ${cfg.comp.segpwr?segp:''}
<div class="segin" id="seg${i}in"> <div class="segin" id="seg${i}in">
<input type="text" class="ptxt noslide" id="seg${i}t" autocomplete="off" maxlength=32 value="${inst.n?inst.n:""}" placeholder="Enter name..."/> <input type="text" class="ptxt" id="seg${i}t" autocomplete="off" maxlength=32 value="${inst.n?inst.n:""}" placeholder="Enter name..."/>
<table class="infot segt"> <table class="infot segt">
<tr> <tr>
<td>${isM&&staX<mw*mh?'Start X':'Start LED'}</td> <td>${isM&&staX<mw*mh?'Start X':'Start LED'}</td>
@ -754,24 +752,24 @@ function populateSegments(s)
<td>${isM&&staX<mw*mh?'':'Offset'}</td> <td>${isM&&staX<mw*mh?'':'Offset'}</td>
</tr> </tr>
<tr> <tr>
<td><input class="noslide segn" id="seg${i}s" type="number" min="0" max="${(isM&&staX<mw*mh?mw:ledCount)-1}" value="${staX}" oninput="updateLen(${i})" onkeydown="segEnter(${i})"></td> <td><input class="segn" id="seg${i}s" type="number" min="0" max="${(isM&&staX<mw*mh?mw:ledCount)-1}" value="${staX}" oninput="updateLen(${i})" onkeydown="segEnter(${i})"></td>
<td><input class="noslide segn" id="seg${i}e" type="number" min="0" max="${(isM&&staX<mw*mh?mw:ledCount)}" value="${stoX-(cfg.comp.seglen?staX:0)}" oninput="updateLen(${i})" onkeydown="segEnter(${i})"></td> <td><input class="segn" id="seg${i}e" type="number" min="0" max="${(isM&&staX<mw*mh?mw:ledCount)}" value="${stoX-(cfg.comp.seglen?staX:0)}" oninput="updateLen(${i})" onkeydown="segEnter(${i})"></td>
<td style="text-align:revert;">${isM&&staX<mw*mh?miXck+'<br>'+rvXck:''}<input class="noslide segn ${isM&&staX<mw*mh?'hide':''}" id="seg${i}of" type="number" value="${inst.of}" oninput="updateLen(${i})"></td> <td style="text-align:revert;">${isM&&staX<mw*mh?miXck+'<br>'+rvXck:''}<input class="segn ${isM&&staX<mw*mh?'hide':''}" id="seg${i}of" type="number" value="${inst.of}" oninput="updateLen(${i})"></td>
</tr> </tr>
${isM&&staX<mw*mh ? '<tr><td>Start Y</td><td>'+(cfg.comp.seglen?'Height':'Stop Y')+'</td><td></td></tr>'+ ${isM&&staX<mw*mh ? '<tr><td>Start Y</td><td>'+(cfg.comp.seglen?'Height':'Stop Y')+'</td><td></td></tr>'+
'<tr>'+ '<tr>'+
'<td><input class="noslide segn" id="seg'+i+'sY" type="number" min="0" max="'+(mh-1)+'" value="'+staY+'" oninput="updateLen('+i+')" onkeydown="segEnter('+i+')"></td>'+ '<td><input class="segn" id="seg'+i+'sY" type="number" min="0" max="'+(mh-1)+'" value="'+staY+'" oninput="updateLen('+i+')" onkeydown="segEnter('+i+')"></td>'+
'<td><input class="noslide segn" id="seg'+i+'eY" type="number" min="0" max="'+mh+'" value="'+(stoY-(cfg.comp.seglen?staY:0))+'" oninput="updateLen('+i+')" onkeydown="segEnter('+i+')"></td>'+ '<td><input class="segn" id="seg'+i+'eY" type="number" min="0" max="'+mh+'" value="'+(stoY-(cfg.comp.seglen?staY:0))+'" oninput="updateLen('+i+')" onkeydown="segEnter('+i+')"></td>'+
'<td style="text-align:revert;">'+miYck+'<br>'+rvYck+'</td>'+ '<td style="text-align:revert;">'+miYck+'<br>'+rvYck+'</td>'+
'</tr>':''} '</tr>':''}
<tr> <tr>
<td>Grouping</td> <td>Grouping</td>
<td>Spacing</td> <td>Spacing</td>
<td><!--Apply--></td> <td></td>
</tr> </tr>
<tr> <tr>
<td><input class="noslide segn" id="seg${i}grp" type="number" min="1" max="255" value="${inst.grp}" oninput="updateLen(${i})" onkeydown="segEnter(${i})"></td> <td><input class="segn" id="seg${i}grp" type="number" min="1" max="255" value="${inst.grp}" oninput="updateLen(${i})" onkeydown="segEnter(${i})"></td>
<td><input class="noslide segn" id="seg${i}spc" type="number" min="0" max="255" value="${inst.spc}" oninput="updateLen(${i})" onkeydown="segEnter(${i})"></td> <td><input class="segn" id="seg${i}spc" type="number" min="0" max="255" value="${inst.spc}" oninput="updateLen(${i})" onkeydown="segEnter(${i})"></td>
<td style="text-align:revert;"><button class="btn btn-xs" onclick="setSeg(${i})"><i class="icons btn-icon" id="segc${i}">&#xe390;</i></button></td> <td style="text-align:revert;"><button class="btn btn-xs" onclick="setSeg(${i})"><i class="icons btn-icon" id="segc${i}">&#xe390;</i></button></td>
</tr> </tr>
</table> </table>
@ -801,10 +799,13 @@ function populateSegments(s)
for (var i = 0; i <= lSeg; i++) { for (var i = 0; i <= lSeg; i++) {
updateLen(i); updateLen(i);
updateTrail(gId(`seg${i}bri`)); updateTrail(gId(`seg${i}bri`));
gId(`segr${i}`).style.display = "none"; gId(`segr${i}`).classList.add("hide");
if (!gId(`seg${i}sel`).checked && gId('selall')) gId('selall').checked = false; // uncheck if at least one is unselected. if (!gId(`seg${i}sel`).checked && gId('selall')) gId('selall').checked = false; // uncheck if at least one is unselected.
} }
if (segCount < 2) gId(`segd${lSeg}`).style.display = "none"; if (segCount < 2) {
gId(`segd${lSeg}`).classList.add("hide");
gId(`segp0`).classList.add("hide");
}
if (!isM && !noNewSegs && (cfg.comp.seglen?parseInt(gId(`seg${lSeg}s`).value):0)+parseInt(gId(`seg${lSeg}e`).value)<ledCount) gId(`segr${lSeg}`).style.display = "inline"; if (!isM && !noNewSegs && (cfg.comp.seglen?parseInt(gId(`seg${lSeg}s`).value):0)+parseInt(gId(`seg${lSeg}e`).value)<ledCount) gId(`segr${lSeg}`).style.display = "inline";
gId('segutil2').style.display = (segCount > 1) ? "block":"none"; // rsbtn parent gId('segutil2').style.display = (segCount > 1) ? "block":"none"; // rsbtn parent
@ -852,6 +853,7 @@ function populateEffects()
let m = (eP.length<4 || eP[3]==='')?'1':eP[3]; // flags let m = (eP.length<4 || eP[3]==='')?'1':eP[3]; // flags
if (id == 0) m = ''; // solid has no flags if (id == 0) m = ''; // solid has no flags
if (m.length>0) { if (m.length>0) {
if (m.includes('0')) nm += "&#8226;"; // 0D effects (PWM & On/Off)
if (m.includes('1')) nm += "&#8942;"; // 1D effects if (m.includes('1')) nm += "&#8942;"; // 1D effects
if (m.includes('2')) nm += "&#9638;"; // 2D effects if (m.includes('2')) nm += "&#9638;"; // 2D effects
if (m.includes('v')) nm += "&#9834;"; // volume effects if (m.includes('v')) nm += "&#9834;"; // volume effects
@ -1165,18 +1167,19 @@ function updateUI()
} }
if (hasWhite) updateTrail(gId('sliderW')); if (hasWhite) updateTrail(gId('sliderW'));
gId('wwrap').style.display = (hasWhite) ? "block":"none"; // white channel
gId('wbal').style.display = (hasCCT) ? "block":"none"; // white balance
var ccfg = cfg.comp.colors; var ccfg = cfg.comp.colors;
gId('hexw').style.display = ccfg.hex ? "block":"none"; // HEX input gId('wwrap').style.display = (hasWhite) ? "block":"none"; // white channel
gId('picker').style.display = (hasRGB && ccfg.picker) ? "block":"none"; // color picker wheel gId('wbal').style.display = (hasCCT) ? "block":"none"; // white balance
gId('hwrap').style.display = (hasRGB && !ccfg.picker) ? "block":"none"; // color picker wheel gId('hexw').style.display = (ccfg.hex) ? "block":"none"; // HEX input
gId('swrap').style.display = (hasRGB && !ccfg.picker) ? "block":"none"; // color picker wheel gId('picker').style.display = (hasRGB && ccfg.picker) ? "block":"none"; // color picker wheel
gId('vwrap').style.display = (hasRGB /*&& ccfg.picker*/) ? "block":"none"; // brightness (value) slider gId('hwrap').style.display = (hasRGB && !ccfg.picker) ? "block":"none"; // hue slider
gId('kwrap').style.display = (hasRGB && !hasCCT /*&& ccfg.picker*/) ? "block":"none"; // Kelvin slider gId('swrap').style.display = (hasRGB && !ccfg.picker) ? "block":"none"; // saturation slider
gId('rgbwrap').style.display = (hasRGB && ccfg.rgb) ? "block":"none"; // RGB sliders gId('vwrap').style.display = (hasRGB) ? "block":"none"; // brightness (value) slider
gId('qcs-w').style.display = (hasRGB && ccfg.quick) ? "block":"none"; // quick selection gId('kwrap').style.display = (hasRGB && !hasCCT) ? "block":"none"; // Kelvin slider
//gId('palw').style.display = hasRGB ? "block":"none"; // palettes gId('rgbwrap').style.display = (hasRGB && ccfg.rgb) ? "block":"none"; // RGB sliders
gId('qcs-w').style.display = (hasRGB && ccfg.quick) ? "block":"none"; // quick selection
//gId('csl').style.display = (hasRGB || hasWhite) ? "block":"none"; // color selectors (hide for On/Off bus)
//gId('palw').style.display = (hasRGB) ? "inline-block":"none"; // palettes are shown/hidden in setEffectParameters()
updatePA(); updatePA();
updatePSliders(); updatePSliders();
@ -1221,7 +1224,19 @@ function updateSelectedFx()
if (selectedEffect) { if (selectedEffect) {
selectedEffect.classList.add('selected'); selectedEffect.classList.add('selected');
setEffectParameters(selectedFx); setEffectParameters(selectedFx);
// hide non-0D effects if segment only has 1 pixel (0D)
var fxs = parent.querySelectorAll('.lstI');
for (const fx of fxs) {
let opts = fx.dataset.opt.split(";");
if (fx.dataset.id>0) {
if (segLmax==0) fx.classList.add('hide'); // none of the segments selected (hide all effects)
else {
if (segLmax==1 && (!opts[3] || opts[3].indexOf("0")<0)) fx.classList.add('hide');
else fx.classList.remove('hide');
}
}
}
// hide 2D mapping and/or sound simulation options
var selectedName = selectedEffect.querySelector(".lstIname").innerText; var selectedName = selectedEffect.querySelector(".lstIname").innerText;
var segs = gId("segcont").querySelectorAll(`div[data-map="map2D"]`); var segs = gId("segcont").querySelectorAll(`div[data-map="map2D"]`);
for (const seg of segs) if (selectedName.indexOf("\u25A6")<0) seg.classList.remove('hide'); else seg.classList.add('hide'); for (const seg of segs) if (selectedName.indexOf("\u25A6")<0) seg.classList.remove('hide'); else seg.classList.add('hide');
@ -1308,6 +1323,7 @@ function readState(s,command=false)
var selc=0; var selc=0;
var sellvl=0; // 0: selc is invalid, 1: selc is mainseg, 2: selc is first selected var sellvl=0; // 0: selc is invalid, 1: selc is mainseg, 2: selc is first selected
hasRGB = hasWhite = hasCCT = false; hasRGB = hasWhite = hasCCT = false;
segLmax = 0;
for (let i = 0; i < (s.seg||[]).length; i++) for (let i = 0; i < (s.seg||[]).length; i++)
{ {
if (sellvl == 0 && s.seg[i].id == s.mainseg) { if (sellvl == 0 && s.seg[i].id == s.mainseg) {
@ -1321,6 +1337,8 @@ function readState(s,command=false)
hasRGB |= !!(lc & 0x01); hasRGB |= !!(lc & 0x01);
hasWhite |= !!(lc & 0x02); hasWhite |= !!(lc & 0x02);
hasCCT |= !!(lc & 0x04); hasCCT |= !!(lc & 0x04);
let sLen = (s.seg[i].stop - s.seg[i].start)*(s.seg[i].stopY?(s.seg[i].stopY - s.seg[i].startY):1);
segLmax = segLmax < sLen ? sLen : segLmax;
} }
} }
var i=s.seg[selc]; var i=s.seg[selc];
@ -1341,7 +1359,7 @@ function readState(s,command=false)
cd[e].dataset.r = i.col[e][0]; cd[e].dataset.r = i.col[e][0];
cd[e].dataset.g = i.col[e][1]; cd[e].dataset.g = i.col[e][1];
cd[e].dataset.b = i.col[e][2]; cd[e].dataset.b = i.col[e][2];
if (hasWhite) { cd[e].dataset.w = i.col[e][3]; } if (hasWhite || (!hasRGB && !hasWhite)) { cd[e].dataset.w = i.col[e][3]; }
setCSL(cd[e]); setCSL(cd[e]);
} }
selectSlot(csel); selectSlot(csel);
@ -1493,8 +1511,8 @@ function setEffectParameters(idx)
var palw = gId("palw"); // wrapper var palw = gId("palw"); // wrapper
var pall = gId("pall"); // label var pall = gId("pall"); // label
// if not controlDefined or palette has a value // if not controlDefined or palette has a value
if ((!controlDefined) || (paOnOff.length>0 && paOnOff[0]!="" && isNaN(paOnOff[0]))) { if (hasRGB && ((!controlDefined) || (paOnOff.length>0 && paOnOff[0]!="" && isNaN(paOnOff[0])))) {
palw.style.display = hasRGB ? "inline-block" : "none"; palw.style.display = "inline-block";
if (paOnOff.length>0 && paOnOff[0].indexOf("=")>0) { if (paOnOff.length>0 && paOnOff[0].indexOf("=")>0) {
// embeded default values // embeded default values
var dPos = paOnOff[0].indexOf("="); var dPos = paOnOff[0].indexOf("=");
@ -1704,21 +1722,21 @@ function makeSeg()
}); });
var cn = `<div class="seg lstI expanded"> var cn = `<div class="seg lstI expanded">
<div class="segin"> <div class="segin">
<input type="text" class="noslide" id="seg${lu}t" autocomplete="off" maxlength=32 value="" placeholder="New segment ${lu}"/> <input type="text" id="seg${lu}t" autocomplete="off" maxlength=32 value="" placeholder="New segment ${lu}"/>
<table class="segt"> <table class="segt">
<tr> <tr>
<td width="38%">${isM?'Start X':'Start LED'}</td> <td width="38%">${isM?'Start X':'Start LED'}</td>
<td width="38%">${isM?(cfg.comp.seglen?"Width":"Stop X"):(cfg.comp.seglen?"LED count":"Stop LED")}</td> <td width="38%">${isM?(cfg.comp.seglen?"Width":"Stop X"):(cfg.comp.seglen?"LED count":"Stop LED")}</td>
</tr> </tr>
<tr> <tr>
<td><input class="noslide segn" id="seg${lu}s" type="number" min="0" max="${isM?mw-1:ledCount-1}" value="${ns}" oninput="updateLen(${lu})" onkeydown="segEnter(${lu})"></td> <td><input class="segn" id="seg${lu}s" type="number" min="0" max="${isM?mw-1:ledCount-1}" value="${ns}" oninput="updateLen(${lu})" onkeydown="segEnter(${lu})"></td>
<td><input class="noslide segn" id="seg${lu}e" type="number" min="0" max="${ct}" value="${ct}" oninput="updateLen(${lu})" onkeydown="segEnter(${lu})"></td> <td><input class="segn" id="seg${lu}e" type="number" min="0" max="${ct}" value="${ct}" oninput="updateLen(${lu})" onkeydown="segEnter(${lu})"></td>
<td><button class="btn btn-xs" onclick="setSeg(${lu});"><i class="icons bth-icon" id="segc${lu}">&#xe390;</i></button></td> <td><button class="btn btn-xs" onclick="setSeg(${lu});"><i class="icons bth-icon" id="segc${lu}">&#xe390;</i></button></td>
</tr> </tr>
<tr id="mkSYH" class="${isM?"":"hide"}"><td>Start Y</td><td>${cfg.comp.seglen?'Height':'Stop Y'}</td></tr> <tr id="mkSYH" class="${isM?"":"hide"}"><td>Start Y</td><td>${cfg.comp.seglen?'Height':'Stop Y'}</td></tr>
<tr id="mkSYD" class="${isM?"":"hide"}"> <tr id="mkSYD" class="${isM?"":"hide"}">
<td><input class="noslide segn" id="seg${lu}sY" type="number" min="0" max="${mh-1}" value="0" oninput="updateLen(${lu})" onkeydown="segEnter(${lu})"></td> <td><input class="segn" id="seg${lu}sY" type="number" min="0" max="${mh-1}" value="0" oninput="updateLen(${lu})" onkeydown="segEnter(${lu})"></td>
<td><input class="noslide segn" id="seg${lu}eY" type="number" min="0" max="${mh}" value="${isM?mh:1}" oninput="updateLen(${lu})" onkeydown="segEnter(${lu})"></td> <td><input class="segn" id="seg${lu}eY" type="number" min="0" max="${mh}" value="${isM?mh:1}" oninput="updateLen(${lu})" onkeydown="segEnter(${lu})"></td>
</tr> </tr>
</table> </table>
<div class="h" id="seg${lu}len">${ledCount - ns} LEDs</div> <div class="h" id="seg${lu}len">${ledCount - ns} LEDs</div>
@ -1843,7 +1861,7 @@ function makeP(i,pl)
<span class="checkmark"></span> <span class="checkmark"></span>
</label> </label>
<div id="pl${i}o1" style="display:${rep>0?"block":"none"}"> <div id="pl${i}o1" style="display:${rep>0?"block":"none"}">
<div class="c">Repeat <input class="noslide" type="number" id="pl${i}rp" oninput="plR(${i})" max=127 min=0 value=${rep>0?rep:1}> times</div> <div class="c">Repeat <input type="number" id="pl${i}rp" oninput="plR(${i})" max=127 min=0 value=${rep>0?rep:1}> times</div>
<div class="sel">End preset:<br> <div class="sel">End preset:<br>
<div class="sel-p"><select class="sel-ple" id="pl${i}selEnd" onchange="plR(${i})" data-val=${plJson[i].end?plJson[i].end:0}> <div class="sel-p"><select class="sel-ple" id="pl${i}selEnd" onchange="plR(${i})" data-val=${plJson[i].end?plJson[i].end:0}>
<option value="0">None</option> <option value="0">None</option>
@ -1882,8 +1900,8 @@ ${makePlSel(plJson[i].end?plJson[i].end:0, true)}
} }
} }
return `<input type="text" class="ptxt noslide ${i==0?'show':''}" id="p${i}txt" autocomplete="off" maxlength=32 value="${(i>0)?pName(i):""}" placeholder="Enter name..."/> return `<input type="text" class="ptxt ${i==0?'show':''}" id="p${i}txt" autocomplete="off" maxlength=32 value="${(i>0)?pName(i):""}" placeholder="Enter name..."/>
<div class="c">Quick load label: <input type="text" class="stxt noslide" maxlength=2 value="${qlName(i)}" id="p${i}ql" autocomplete="off"/></div> <div class="c">Quick load label: <input type="text" class="stxt" maxlength=2 value="${qlName(i)}" id="p${i}ql" autocomplete="off"/></div>
<div class="h">(leave empty for no Quick load button)</div> <div class="h">(leave empty for no Quick load button)</div>
<div ${pl&&i==0?"style='display:none'":""}> <div ${pl&&i==0?"style='display:none'":""}>
<label class="check revchkl"> <label class="check revchkl">
@ -1894,9 +1912,9 @@ ${makePlSel(plJson[i].end?plJson[i].end:0, true)}
<span class="checkmark"></span> <span class="checkmark"></span>
</label> </label>
</div> </div>
<div class="po2" id="p${i}o2">API command<br><textarea class="apitxt noslide" id="p${i}api"></textarea></div> <div class="po2" id="p${i}o2">API command<br><textarea class="apitxt" id="p${i}api"></textarea></div>
<div class="po1" id="p${i}o1">${content}</div> <div class="po1" id="p${i}o1">${content}</div>
<div class="c m6">Save to ID <input class="noslide" id="p${i}id" type="number" oninput="checkUsed(${i})" max=250 min=1 value=${(i>0)?i:getLowestUnusedP()}></div> <div class="c m6">Save to ID <input id="p${i}id" type="number" oninput="checkUsed(${i})" max=250 min=1 value=${(i>0)?i:getLowestUnusedP()}></div>
<div class="c"> <div class="c">
<button class="btn btn-p" onclick="saveP(${i},${pl})"><i class="icons btn-icon">&#xe390;</i>Save</button> <button class="btn btn-p" onclick="saveP(${i},${pl})"><i class="icons btn-icon">&#xe390;</i>Save</button>
${(i>0)?'<button class="btn btn-p" id="p'+i+'del" onclick="delP('+i+')"><i class="icons btn-icon">&#xe037;</i>Delete':'<button class="btn btn-p" onclick="resetPUtil()">Cancel'}</button> ${(i>0)?'<button class="btn btn-p" id="p'+i+'del" onclick="delP('+i+')"><i class="icons btn-icon">&#xe037;</i>Delete':'<button class="btn btn-p" onclick="resetPUtil()">Cancel'}</button>
@ -1941,8 +1959,8 @@ function makePlEntry(p,i)
<td class="c">#${i+1}</td> <td class="c">#${i+1}</td>
</tr> </tr>
<tr> <tr>
<td class="c" width="40%"><input class="noslide segn" type="number" placeholder="Duration" max=6553.0 min=0.2 step=0.1 oninput="pleDur(${p},${i},this)" value="${plJson[p].dur[i]/10.0}">s</td> <td class="c" width="40%"><input class="segn" type="number" placeholder="Duration" max=6553.0 min=0.2 step=0.1 oninput="pleDur(${p},${i},this)" value="${plJson[p].dur[i]/10.0}">s</td>
<td class="c" width="40%"><input class="noslide segn" type="number" placeholder="Transition" max=65.0 min=0.0 step=0.1 oninput="pleTr(${p},${i},this)" value="${plJson[p].transition[i]/10.0}">s</td> <td class="c" width="40%"><input class="segn" type="number" placeholder="Transition" max=65.0 min=0.0 step=0.1 oninput="pleTr(${p},${i},this)" value="${plJson[p].transition[i]/10.0}">s</td>
<td class="c"><button class="btn btn-pl-del" onclick="delPl(${p},${i})"><i class="icons btn-icon">&#xe037;</i></button></div></td> <td class="c"><button class="btn btn-pl-del" onclick="delPl(${p},${i})"><i class="icons btn-icon">&#xe037;</i></button></div></td>
</tr> </tr>
</table> </table>
@ -2234,7 +2252,7 @@ function setLor(i)
function setPreset(i) function setPreset(i)
{ {
var obj = {"ps":i}; var obj = {"ps":i};
if (pJson && pJson[i] && (!pJson[i].win || pJson[i].win.indexOf("Please") <= 0)) { if (!isPlaylist(i) && pJson && pJson[i] && (!pJson[i].win || pJson[i].win.indexOf("Please") <= 0)) {
// we will send the complete preset content as to avoid delay introduced by // we will send the complete preset content as to avoid delay introduced by
// async nature of applyPreset() and having to read the preset from file system. // async nature of applyPreset() and having to read the preset from file system.
obj = {"pd":i}; // use "pd" instead of "ps" to indicate that we are sending the preset content directly obj = {"pd":i}; // use "pd" instead of "ps" to indicate that we are sending the preset content directly
@ -2740,7 +2758,7 @@ function hasIroClass(classList)
} }
return false; return false;
} }
//required by rangetouch.js
function lock(e) function lock(e)
{ {
if (pcMode) return; if (pcMode) return;
@ -2754,7 +2772,7 @@ function lock(e)
_C.classList.toggle('smooth', !(locked = true)); _C.classList.toggle('smooth', !(locked = true));
} }
//required by rangetouch.js
function move(e) function move(e)
{ {
if(!locked || pcMode) return; if(!locked || pcMode) return;

View File

@ -13,6 +13,7 @@
function H(){window.open("https://kno.wled.ge/features/2D");} function H(){window.open("https://kno.wled.ge/features/2D");}
function B(){window.open("/settings","_self");} function B(){window.open("/settings","_self");}
function gId(n){return d.getElementById(n);} function gId(n){return d.getElementById(n);}
function fS(){d.Sf.submit();} // <button type=submit> sometimes didn't work
// https://www.educative.io/edpresso/how-to-dynamically-load-a-js-file-in-javascript // https://www.educative.io/edpresso/how-to-dynamically-load-a-js-file-in-javascript
function loadJS(FILE_URL, async = true) { function loadJS(FILE_URL, async = true) {
let scE = d.createElement("script"); let scE = d.createElement("script");
@ -294,7 +295,7 @@ Y:<input name="P${i}Y" type="number" min="0" max="255" value="0" oninput="UI()">
<form id="form_s" name="Sf" method="post"> <form id="form_s" name="Sf" method="post">
<div class="toprow"> <div class="toprow">
<div class="helpB"><button type="button" onclick="H()">?</button></div> <div class="helpB"><button type="button" onclick="H()">?</button></div>
<button type="button" onclick="B()">Back</button><button type="submit">Save</button><hr> <button type="button" onclick="B()">Back</button><button type="button" onclick="fS()">Save</button><hr>
</div> </div>
<h2>2D setup</h2> <h2>2D setup</h2>
Strip or panel: Strip or panel:
@ -341,7 +342,7 @@ Y:<input name="P${i}Y" type="number" min="0" max="255" value="0" oninput="UI()">
A value of -1 means that pixel at that position is missing, a value of 0 means never paint that pixel, and 1 means regular pixel.</i> A value of -1 means that pixel at that position is missing, a value of 0 means never paint that pixel, and 1 means regular pixel.</i>
</div> </div>
<hr> <hr>
<button type="button" onclick="B()">Back</button><button type="submit">Save</button> <button type="button" onclick="B()">Back</button><button type="button" onclick="fS()">Save</button>
</form> </form>
<div id="toast"></div> <div id="toast"></div>
</body> </body>

View File

@ -143,7 +143,7 @@
function UI(change=false) function UI(change=false)
{ {
var isRGBW = false, memu = 0; let isRGBW = false, gRGBW = false, memu = 0;
gId('ampwarning').style.display = (d.Sf.MA.value > 7200) ? 'inline':'none'; gId('ampwarning').style.display = (d.Sf.MA.value > 7200) ? 'inline':'none';
@ -184,7 +184,7 @@
if (t > 31 && t < 48) d.getElementsByName("LC"+n)[0].value = 1; // for sanity change analog count just to 1 LED if (t > 31 && t < 48) d.getElementsByName("LC"+n)[0].value = 1; // for sanity change analog count just to 1 LED
} }
gId("rf"+n).onclick = (t == 31) ? (()=>{return false}) : (()=>{}); // prevent change for TM1814 gId("rf"+n).onclick = (t == 31) ? (()=>{return false}) : (()=>{}); // prevent change for TM1814
isRGBW = ((t > 17 && t < 22) || t == 30 || t == 31 || (t > 40 && t < 46 && t != 43) || t == 88); // RGBW checkbox, TYPE_xxxx values from const.h gRGBW |= isRGBW = ((t > 17 && t < 22) || t == 30 || t == 31 || (t > 40 && t < 46 && t != 43) || t == 88); // RGBW checkbox, TYPE_xxxx values from const.h
gId("co"+n).style.display = ((t >= 80 && t < 96) || (t >= 40 && t < 48)) ? "none":"inline"; // hide color order for PWM gId("co"+n).style.display = ((t >= 80 && t < 96) || (t >= 40 && t < 48)) ? "none":"inline"; // hide color order for PWM
gId("dig"+n+"w").style.display = (t == 30 || t == 31) ? "inline":"none"; // show swap channels dropdown gId("dig"+n+"w").style.display = (t == 30 || t == 31) ? "inline":"none"; // show swap channels dropdown
if (!(t == 30 || t == 31)) d.getElementsByName("WO"+n)[0].value = 0; // reset swapping if (!(t == 30 || t == 31)) d.getElementsByName("WO"+n)[0].value = 0; // reset swapping
@ -197,11 +197,11 @@
gId("psd"+n).innerHTML = (t >= 40 && t < 48) ? "Index:":"Start:"; // change analog start description gId("psd"+n).innerHTML = (t >= 40 && t < 48) ? "Index:":"Start:"; // change analog start description
} }
} }
// display white channel calculation method // display global white channel overrides
var myC = d.querySelectorAll('.wc'), gId("wc").style.display = (gRGBW) ? 'inline':'none';
l = myC.length; if (!gRGBW) {
for (i = 0; i < l; i++) { d.Sf.AW.selectedIndex = 0;
myC[i].style.display = (isRGBW) ? 'inline':'none'; d.Sf.CR.checked = false;
} }
// check for pin conflicts // check for pin conflicts
var LCs = d.getElementsByTagName("input"); var LCs = d.getElementsByTagName("input");
@ -660,8 +660,8 @@ Length: <input type="number" name="XC${i}" id="xc${i}" class="l" min="1" max="65
<option value="3">Sunrise</option> <option value="3">Sunrise</option>
</select> </select>
<h3>White management</h3> <h3>White management</h3>
White Balance correction: <input type="checkbox" name="CCT"> <br> White Balance correction: <input type="checkbox" name="CCT"><br>
<span class="wc"> <div id="wc">
Global override for Auto-calculate white:<br> Global override for Auto-calculate white:<br>
<select name="AW"> <select name="AW">
<option value=255>Disabled</option> <option value=255>Disabled</option>
@ -671,9 +671,10 @@ Length: <input type="number" name="XC${i}" id="xc${i}" class="l" min="1" max="65
<option value=3>Dual</option> <option value=3>Dual</option>
<option value=4>Max</option> <option value=4>Max</option>
</select> </select>
<br> <br>
Calculate CCT from RGB: <input type="checkbox" name="CR"> <br> Calculate CCT from RGB: <input type="checkbox" name="CR"><br>
CCT additive blending: <input type="number" class="s" min="0" max="100" name="CB" required> %</span> CCT additive blending: <input type="number" class="s" min="0" max="100" name="CB" required> %
</div>
<h3>Advanced</h3> <h3>Advanced</h3>
Palette blending: Palette blending:
<select name="PB"> <select name="PB">

View File

@ -127,19 +127,19 @@
<p class="label h">RGB channels</p> <p class="label h">RGB channels</p>
<div id="rwrap" class="il"> <div id="rwrap" class="il">
<div class="sliderwrap il"> <div class="sliderwrap il">
<input id="sliderR" onchange="fromRgb()" max="255" min="0" type="range" value="128" /> <input id="sliderR" class="noslide" onchange="fromRgb()" max="255" min="0" type="range" value="128" />
<div class="sliderdisplay"></div> <div class="sliderdisplay"></div>
</div> </div>
</div><br> </div><br>
<div id="gwrap" class="il"> <div id="gwrap" class="il">
<div class="sliderwrap il"> <div class="sliderwrap il">
<input id="sliderG" onchange="fromRgb()" max="255" min="0" type="range" value="128" /> <input id="sliderG" class="noslide" onchange="fromRgb()" max="255" min="0" type="range" value="128" />
<div class="sliderdisplay"></div> <div class="sliderdisplay"></div>
</div> </div>
</div><br> </div><br>
<div id="bwrap" class="il"> <div id="bwrap" class="il">
<div class="sliderwrap il"> <div class="sliderwrap il">
<input id="sliderB" onchange="fromRgb()" max="255" min="0" type="range" value="128" /> <input id="sliderB" class="noslide" onchange="fromRgb()" max="255" min="0" type="range" value="128" />
<div class="sliderdisplay"></div> <div class="sliderdisplay"></div>
</div> </div>
</div><br> </div><br>
@ -147,14 +147,14 @@
<div id="wwrap" class="center"> <div id="wwrap" class="center">
<p class="label h">White channel</p> <p class="label h">White channel</p>
<div class="sliderwrap il"> <div class="sliderwrap il">
<input id="sliderW" onchange="setColor(0)" max="255" min="0" type="range" value="128" /> <input id="sliderW" class="noslide" onchange="setColor(0)" max="255" min="0" type="range" value="128" />
<div class="sliderdisplay"></div> <div class="sliderdisplay"></div>
</div> </div>
</div> </div>
<div id="wbal"> <div id="wbal">
<p class="label h">White balance</p> <p class="label h">White balance</p>
<div class="sliderwrap il"> <div class="sliderwrap il">
<input id="sliderA" onchange="setBalance(this.value)" max="255" min="0" type="range" value="128" /> <input id="sliderA" class="noslide" onchange="setBalance(this.value)" max="255" min="0" type="range" value="128" />
<div class="sliderdisplay"></div> <div class="sliderdisplay"></div>
</div> </div>
</div> </div>

View File

@ -551,7 +551,7 @@ function populateSegments(s)
<div class="il"> <div class="il">
<i class="icons slider-icon pwr ${powered[i] ? "act":""}" id="seg${i}pwr" onclick="setSegPwr(${i})" title="${inst.n}">&#xe08f;</i> <i class="icons slider-icon pwr ${powered[i] ? "act":""}" id="seg${i}pwr" onclick="setSegPwr(${i})" title="${inst.n}">&#xe08f;</i>
<div id="sliderSeg${i}Bri" class="sliderwrap il"> <div id="sliderSeg${i}Bri" class="sliderwrap il">
<input id="seg${i}bri" class="noslide" onchange="setSegBri(${i})" oninput="updateTrail(this)" max="255" min="1" type="range" value="${inst.bri}" /> <input id="seg${i}bri" onchange="setSegBri(${i})" oninput="updateTrail(this)" max="255" min="1" type="range" value="${inst.bri}" />
<div class="sliderdisplay"></div> <div class="sliderdisplay"></div>
</div> </div>
<output class="sliderbubble"></output> <output class="sliderbubble"></output>
@ -1379,7 +1379,7 @@ function hasIroClass(classList)
} }
return false; return false;
} }
//required by rangetouch.js
function lock(e) function lock(e)
{ {
var l = e.target.classList; var l = e.target.classList;
@ -1392,7 +1392,7 @@ function lock(e)
_C.classList.toggle('smooth', !(locked = true)); _C.classList.toggle('smooth', !(locked = true));
} }
//required by rangetouch.js
function move(e) function move(e)
{ {
if(!locked) return; if(!locked) return;

View File

@ -124,7 +124,7 @@ void handleIR();
#include "src/dependencies/json/AsyncJson-v6.h" #include "src/dependencies/json/AsyncJson-v6.h"
#include "FX.h" #include "FX.h"
void deserializeSegment(JsonObject elem, byte it, byte presetId = 0); bool deserializeSegment(JsonObject elem, byte it, byte presetId = 0);
bool deserializeState(JsonObject root, byte callMode = CALL_MODE_DIRECT_CHANGE, byte presetId = 0); bool deserializeState(JsonObject root, byte callMode = CALL_MODE_DIRECT_CHANGE, byte presetId = 0);
void serializeSegment(JsonObject& root, Segment& seg, byte id, bool forPreset = false, bool segmentBounds = true); void serializeSegment(JsonObject& root, Segment& seg, byte id, bool forPreset = false, bool segmentBounds = true);
void serializeState(JsonObject root, bool forPreset = false, bool includeBri = true, bool segmentBounds = true, bool selectedSegmentsOnly = false); void serializeState(JsonObject root, bool forPreset = false, bool includeBri = true, bool segmentBounds = true, bool selectedSegmentsOnly = false);

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

@ -14,16 +14,16 @@
* JSON API (De)serialization * JSON API (De)serialization
*/ */
void deserializeSegment(JsonObject elem, byte it, byte presetId) bool deserializeSegment(JsonObject elem, byte it, byte presetId)
{ {
byte id = elem["id"] | it; byte id = elem["id"] | it;
if (id >= strip.getMaxSegments()) return; if (id >= strip.getMaxSegments()) return false;
int stop = elem["stop"] | -1; int stop = elem["stop"] | -1;
// if using vectors use this code to append segment // if using vectors use this code to append segment
if (id >= strip.getSegmentsNum()) { if (id >= strip.getSegmentsNum()) {
if (stop <= 0) return; // ignore empty/inactive segments if (stop <= 0) return false; // ignore empty/inactive segments
strip.appendSegment(Segment(0, strip.getLengthTotal())); strip.appendSegment(Segment(0, strip.getLengthTotal()));
id = strip.getSegmentsNum()-1; // segments are added at the end of list id = strip.getSegmentsNum()-1; // segments are added at the end of list
} }
@ -56,7 +56,7 @@ void deserializeSegment(JsonObject elem, byte it, byte presetId)
elem["rev"] = !elem["rev"]; // alternate reverse on even/odd segments elem["rev"] = !elem["rev"]; // alternate reverse on even/odd segments
deserializeSegment(elem, i, presetId); // recursive call with new id deserializeSegment(elem, i, presetId); // recursive call with new id
} }
return; return true;
} }
if (elem["n"]) { if (elem["n"]) {
@ -107,6 +107,8 @@ void deserializeSegment(JsonObject elem, byte it, byte presetId)
if (stop > start && of > len -1) of = len -1; if (stop > start && of > len -1) of = len -1;
seg.set(start, stop, grp, spc, of, startY, stopY); seg.set(start, stop, grp, spc, of, startY, stopY);
if (seg.reset && seg.stop == 0) return true; // segment was deleted & is marked for reset, no need to change anything else
byte segbri = seg.opacity; byte segbri = seg.opacity;
if (getVal(elem["bri"], &segbri)) { if (getVal(elem["bri"], &segbri)) {
if (segbri > 0) seg.setOpacity(segbri); if (segbri > 0) seg.setOpacity(segbri);
@ -262,6 +264,8 @@ void deserializeSegment(JsonObject elem, byte it, byte presetId)
} }
// send UDP/WS if segment options changed (except selection; will also deselect current preset) // send UDP/WS if segment options changed (except selection; will also deselect current preset)
if (seg.differs(prev) & 0x7F) stateChanged = true; if (seg.differs(prev) & 0x7F) stateChanged = true;
return true;
} }
// deserializes WLED state (fileDoc points to doc object if called from web server) // deserializes WLED state (fileDoc points to doc object if called from web server)
@ -377,11 +381,12 @@ bool deserializeState(JsonObject root, byte callMode, byte presetId)
deserializeSegment(segVar, id, presetId); //apply only the segment with the specified ID deserializeSegment(segVar, id, presetId); //apply only the segment with the specified ID
} }
} else { } else {
size_t deleted = 0;
JsonArray segs = segVar.as<JsonArray>(); JsonArray segs = segVar.as<JsonArray>();
for (JsonObject elem : segs) { for (JsonObject elem : segs) {
deserializeSegment(elem, it, presetId); if (deserializeSegment(elem, it++, presetId) && !elem["stop"].isNull() && elem["stop"]==0) deleted++;
it++;
} }
if (strip.getSegmentsNum() > 3 && deleted >= strip.getSegmentsNum()/2U) strip.purgeSegments(); // batch deleting more than half segments
} }
usermods.readFromJsonState(root); usermods.readFromJsonState(root);

View File

@ -11,15 +11,15 @@ void _overlayAnalogClock()
{ {
_overlayAnalogCountdown(); return; _overlayAnalogCountdown(); return;
} }
double hourP = ((double)(hour(localTime)%12))/12; float hourP = ((float)(hour(localTime)%12))/12.0f;
double minuteP = ((double)minute(localTime))/60; float minuteP = ((float)minute(localTime))/60.0f;
hourP = hourP + minuteP/12; hourP = hourP + minuteP/12.0f;
double secondP = ((double)second(localTime))/60; float secondP = ((float)second(localTime))/60.0f;
int hourPixel = floor(analogClock12pixel + overlaySize*hourP); int hourPixel = floorf(analogClock12pixel + overlaySize*hourP);
if (hourPixel > overlayMax) hourPixel = overlayMin -1 + hourPixel - overlayMax; if (hourPixel > overlayMax) hourPixel = overlayMin -1 + hourPixel - overlayMax;
int minutePixel = floor(analogClock12pixel + overlaySize*minuteP); int minutePixel = floorf(analogClock12pixel + overlaySize*minuteP);
if (minutePixel > overlayMax) minutePixel = overlayMin -1 + minutePixel - overlayMax; if (minutePixel > overlayMax) minutePixel = overlayMin -1 + minutePixel - overlayMax;
int secondPixel = floor(analogClock12pixel + overlaySize*secondP); int secondPixel = floorf(analogClock12pixel + overlaySize*secondP);
if (secondPixel > overlayMax) secondPixel = overlayMin -1 + secondPixel - overlayMax; if (secondPixel > overlayMax) secondPixel = overlayMin -1 + secondPixel - overlayMax;
if (analogClockSecondsTrail) if (analogClockSecondsTrail)
{ {
@ -36,7 +36,7 @@ void _overlayAnalogClock()
{ {
for (byte i = 0; i <= 12; i++) for (byte i = 0; i <= 12; i++)
{ {
int pix = analogClock12pixel + round((overlaySize / 12.0) *i); int pix = analogClock12pixel + roundf((overlaySize / 12.0f) *i);
if (pix > overlayMax) pix -= overlaySize; if (pix > overlayMax) pix -= overlaySize;
strip.setPixelColor(pix, 0x00FFAA); strip.setPixelColor(pix, 0x00FFAA);
} }
@ -52,29 +52,29 @@ void _overlayAnalogCountdown()
if ((unsigned long)toki.second() < countdownTime) if ((unsigned long)toki.second() < countdownTime)
{ {
long diff = countdownTime - toki.second(); long diff = countdownTime - toki.second();
double pval = 60; float pval = 60.0f;
if (diff > 31557600L) //display in years if more than 365 days if (diff > 31557600L) //display in years if more than 365 days
{ {
pval = 315576000L; //10 years pval = 315576000.0f; //10 years
} else if (diff > 2592000L) //display in months if more than a month } else if (diff > 2592000L) //display in months if more than a month
{ {
pval = 31557600L; //1 year pval = 31557600.0f; //1 year
} else if (diff > 604800) //display in weeks if more than a week } else if (diff > 604800) //display in weeks if more than a week
{ {
pval = 2592000L; //1 month pval = 2592000.0f; //1 month
} else if (diff > 86400) //display in days if more than 24 hours } else if (diff > 86400) //display in days if more than 24 hours
{ {
pval = 604800; //1 week pval = 604800.0f; //1 week
} else if (diff > 3600) //display in hours if more than 60 minutes } else if (diff > 3600) //display in hours if more than 60 minutes
{ {
pval = 86400; //1 day pval = 86400.0f; //1 day
} else if (diff > 60) //display in minutes if more than 60 seconds } else if (diff > 60) //display in minutes if more than 60 seconds
{ {
pval = 3600; //1 hour pval = 3600.0f; //1 hour
} }
int overlaySize = overlayMax - overlayMin +1; int overlaySize = overlayMax - overlayMin +1;
double perc = (pval-(double)diff)/pval; float perc = (pval-(float)diff)/pval;
if (perc > 1.0) perc = 1.0; if (perc > 1.0f) perc = 1.0f;
byte pixelCnt = perc*overlaySize; byte pixelCnt = perc*overlaySize;
if (analogClock12pixel + pixelCnt > overlayMax) if (analogClock12pixel + pixelCnt > overlayMax)
{ {

View File

@ -319,8 +319,7 @@ void WLED::setup()
DEBUG_PRINT(F("esp8266 ")); DEBUG_PRINT(F("esp8266 "));
DEBUG_PRINTLN(ESP.getCoreVersion()); DEBUG_PRINTLN(ESP.getCoreVersion());
#endif #endif
DEBUG_PRINT(F("heap ")); DEBUG_PRINT(F("heap ")); DEBUG_PRINTLN(ESP.getFreeHeap());
DEBUG_PRINTLN(ESP.getFreeHeap());
#if defined(ARDUINO_ARCH_ESP32) && defined(WLED_USE_PSRAM) #if defined(ARDUINO_ARCH_ESP32) && defined(WLED_USE_PSRAM)
if (psramFound()) { if (psramFound()) {
@ -355,6 +354,8 @@ void WLED::setup()
DEBUG_PRINTLN(F("Registering usermods ...")); DEBUG_PRINTLN(F("Registering usermods ..."));
registerUsermods(); registerUsermods();
DEBUG_PRINT(F("heap ")); DEBUG_PRINTLN(ESP.getFreeHeap());
for (uint8_t i=1; i<WLED_MAX_BUTTONS; i++) btnPin[i] = -1; for (uint8_t i=1; i<WLED_MAX_BUTTONS; i++) btnPin[i] = -1;
bool fsinit = false; bool fsinit = false;
@ -395,10 +396,12 @@ void WLED::setup()
DEBUG_PRINTLN(F("Initializing strip")); DEBUG_PRINTLN(F("Initializing strip"));
beginStrip(); beginStrip();
DEBUG_PRINT(F("heap ")); DEBUG_PRINTLN(ESP.getFreeHeap());
DEBUG_PRINTLN(F("Usermods setup")); DEBUG_PRINTLN(F("Usermods setup"));
userSetup(); userSetup();
usermods.setup(); usermods.setup();
DEBUG_PRINT(F("heap ")); DEBUG_PRINTLN(ESP.getFreeHeap());
if (strcmp(clientSSID, DEFAULT_CLIENT_SSID) == 0) if (strcmp(clientSSID, DEFAULT_CLIENT_SSID) == 0)
showWelcomePage = true; showWelcomePage = true;
@ -454,7 +457,9 @@ void WLED::setup()
#endif #endif
// HTTP server page init // HTTP server page init
DEBUG_PRINTLN(F("initServer"));
initServer(); initServer();
DEBUG_PRINT(F("heap ")); DEBUG_PRINTLN(ESP.getFreeHeap());
enableWatchdog(); enableWatchdog();
@ -750,6 +755,8 @@ void WLED::handleConnection()
DEBUG_PRINTLN(heap); DEBUG_PRINTLN(heap);
forceReconnect = true; forceReconnect = true;
strip.purgeSegments(true); // remove all but one segments from memory strip.purgeSegments(true); // remove all but one segments from memory
} else if (heap < MIN_HEAP_SIZE) {
strip.purgeSegments();
} }
lastHeap = heap; lastHeap = heap;
heapTime = now; heapTime = now;

View File

@ -111,9 +111,17 @@ void sendDataWs(AsyncWebSocketClient * client)
DEBUG_PRINTF("JSON buffer size: %u for WS request (%u).\n", doc.memoryUsage(), len); DEBUG_PRINTF("JSON buffer size: %u for WS request (%u).\n", doc.memoryUsage(), len);
size_t heap1 = ESP.getFreeHeap(); size_t heap1 = ESP.getFreeHeap();
DEBUG_PRINT(F("heap ")); DEBUG_PRINTLN(ESP.getFreeHeap());
#ifdef ESP8266
if (len>heap1) {
DEBUG_PRINTLN(F("Out of memory (WS)!"));
return;
}
#endif
buffer = ws.makeBuffer(len); // will not allocate correct memory sometimes on ESP8266 buffer = ws.makeBuffer(len); // will not allocate correct memory sometimes on ESP8266
#ifdef ESP8266 #ifdef ESP8266
size_t heap2 = ESP.getFreeHeap(); size_t heap2 = ESP.getFreeHeap();
DEBUG_PRINT(F("heap ")); DEBUG_PRINTLN(ESP.getFreeHeap());
#else #else
size_t heap2 = 0; // ESP32 variants do not have the same issue and will work without checking heap allocation size_t heap2 = 0; // ESP32 variants do not have the same issue and will work without checking heap allocation
#endif #endif
@ -148,11 +156,17 @@ bool sendLiveLedsWs(uint32_t wsClient)
AsyncWebSocketClient * wsc = ws.client(wsClient); AsyncWebSocketClient * wsc = ws.client(wsClient);
if (!wsc || wsc->queueLength() > 0) return false; //only send if queue free if (!wsc || wsc->queueLength() > 0) return false; //only send if queue free
uint16_t used = strip.getLengthTotal(); size_t used = strip.getLengthTotal();
const uint16_t MAX_LIVE_LEDS_WS = strip.isMatrix ? 1024 : 256; #ifdef ESP8266
uint16_t n = ((used -1)/MAX_LIVE_LEDS_WS) +1; //only serve every n'th LED if count over MAX_LIVE_LEDS_WS const size_t MAX_LIVE_LEDS_WS = 256U;
uint16_t pos = (strip.isMatrix ? 4 : 2); #else
uint16_t bufSize = pos + (used/n)*3; const size_t MAX_LIVE_LEDS_WS = 1024U;
#endif
size_t n = ((used -1)/MAX_LIVE_LEDS_WS) +1; //only serve every n'th LED if count over MAX_LIVE_LEDS_WS
size_t pos = (strip.isMatrix ? 4 : 2); // start of data
size_t bufSize = pos + (used/n)*3;
size_t skipLines = 0;
AsyncWebSocketMessageBuffer * wsBuf = ws.makeBuffer(bufSize); AsyncWebSocketMessageBuffer * wsBuf = ws.makeBuffer(bufSize);
if (!wsBuf) return false; //out of memory if (!wsBuf) return false; //out of memory
uint8_t* buffer = wsBuf->get(); uint8_t* buffer = wsBuf->get();
@ -163,11 +177,25 @@ bool sendLiveLedsWs(uint32_t wsClient)
buffer[1] = 2; //version buffer[1] = 2; //version
buffer[2] = Segment::maxWidth; buffer[2] = Segment::maxWidth;
buffer[3] = Segment::maxHeight; buffer[3] = Segment::maxHeight;
if (Segment::maxWidth * Segment::maxHeight > MAX_LIVE_LEDS_WS*4) {
buffer[2] = Segment::maxWidth/4;
buffer[3] = Segment::maxHeight/4;
skipLines = 3;
} else if (Segment::maxWidth * Segment::maxHeight > MAX_LIVE_LEDS_WS) {
buffer[2] = Segment::maxWidth/2;
buffer[3] = Segment::maxHeight/2;
skipLines = 1;
}
} }
#endif #endif
for (uint16_t i = 0; pos < bufSize -2; i += n) for (size_t i = 0; pos < bufSize -2; i += n)
{ {
#ifndef WLED_DISABLE_2D
if (strip.isMatrix && skipLines) {
if ((i/Segment::maxWidth)%(skipLines+1)) i += Segment::maxWidth * skipLines;
}
#endif
uint32_t c = strip.getPixelColor(i); uint32_t c = strip.getPixelColor(i);
buffer[pos++] = qadd8(W(c), R(c)); //R, add white channel to RGB channels as a simple RGBW -> RGB map buffer[pos++] = qadd8(W(c), R(c)); //R, add white channel to RGB channels as a simple RGBW -> RGB map
buffer[pos++] = qadd8(W(c), G(c)); //G buffer[pos++] = qadd8(W(c), G(c)); //G