Merge pull request #2184 from Aircoookie/fx-mods

FX optimisations and segment names
This commit is contained in:
Christian Schwinne 2021-09-19 15:16:23 +02:00 committed by GitHub
commit b8e8028eb9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 2474 additions and 2462 deletions

View File

@ -388,41 +388,12 @@ uint16_t WS2812FX::mode_rainbow_cycle(void) {
} }
/*
* theater chase function
*/
uint16_t WS2812FX::theater_chase(uint32_t color1, uint32_t color2, bool do_palette) {
byte gap = 2 + ((255 - SEGMENT.intensity) >> 5);
uint32_t cycleTime = 50 + (255 - SEGMENT.speed)*2;
uint32_t it = now / cycleTime;
if (it != SEGENV.step) //new color
{
SEGENV.aux0 = (SEGENV.aux0 +1) % gap;
SEGENV.step = it;
}
for(uint16_t i = 0; i < SEGLEN; i++) {
if((i % gap) == SEGENV.aux0) {
if (do_palette)
{
setPixelColor(i, color_from_palette(i, true, PALETTE_SOLID_WRAP, 0));
} else {
setPixelColor(i, color1);
}
} else {
setPixelColor(i, color2);
}
}
return FRAMETIME;
}
/* /*
* Theatre-style crawling lights. * Theatre-style crawling lights.
* Inspired by the Adafruit examples. * Inspired by the Adafruit examples.
*/ */
uint16_t WS2812FX::mode_theater_chase(void) { uint16_t WS2812FX::mode_theater_chase(void) {
return theater_chase(SEGCOLOR(0), SEGCOLOR(1), true); return running(SEGCOLOR(0), SEGCOLOR(1), true);
} }
@ -431,7 +402,7 @@ uint16_t WS2812FX::mode_theater_chase(void) {
* Inspired by the Adafruit examples. * Inspired by the Adafruit examples.
*/ */
uint16_t WS2812FX::mode_theater_chase_rainbow(void) { uint16_t WS2812FX::mode_theater_chase_rainbow(void) {
return theater_chase(color_wheel(SEGENV.step), SEGCOLOR(1), false); return running(color_wheel(SEGENV.step), SEGCOLOR(1), true);
} }
@ -976,29 +947,27 @@ uint16_t WS2812FX::mode_chase_flash_random(void) {
/* /*
* Alternating pixels running function. * Alternating pixels running function.
*/ */
uint16_t WS2812FX::running(uint32_t color1, uint32_t color2) { uint16_t WS2812FX::running(uint32_t color1, uint32_t color2, bool theatre) {
uint8_t pxw = 1 + (SEGMENT.intensity >> 5); uint8_t width = (theatre ? 3 : 1) + (SEGMENT.intensity >> 4); // window
uint32_t cycleTime = 35 + (255 - SEGMENT.speed); uint32_t cycleTime = 50 + (255 - SEGMENT.speed);
uint32_t it = now / cycleTime; uint32_t it = now / cycleTime;
if (SEGMENT.speed == 0) it = 0; bool usePalette = color1 == SEGCOLOR(0);
for(uint16_t i = 0; i < SEGLEN; i++) { for(uint16_t i = 0; i < SEGLEN; i++) {
if((i + SEGENV.aux0) % (pxw*2) < pxw) { uint32_t col = color2;
if (color1 == SEGCOLOR(0)) if (usePalette) color1 = color_from_palette(i, true, PALETTE_SOLID_WRAP, 0);
{ if (theatre) {
setPixelColor(SEGLEN -i -1, color_from_palette(SEGLEN -i -1, true, PALETTE_SOLID_WRAP, 0)); if ((i % width) == SEGENV.aux0) col = color1;
} else
{
setPixelColor(SEGLEN -i -1, color1);
}
} else { } else {
setPixelColor(SEGLEN -i -1, color2); int8_t pos = (i % (width<<1));
if ((pos < SEGENV.aux0-width) || ((pos >= SEGENV.aux0) && (pos < SEGENV.aux0+width))) col = color1;
} }
setPixelColor(i,col);
} }
if (it != SEGENV.step ) if (it != SEGENV.step )
{ {
SEGENV.aux0 = (SEGENV.aux0 +1) % (pxw*2); SEGENV.aux0 = (SEGENV.aux0 +1) % (theatre ? width : (width<<1));
SEGENV.step = it; SEGENV.step = it;
} }
return FRAMETIME; return FRAMETIME;
@ -1247,44 +1216,19 @@ uint16_t WS2812FX::mode_loading(void) {
//American Police Light with all LEDs Red and Blue //American Police Light with all LEDs Red and Blue
uint16_t WS2812FX::police_base(uint32_t color1, uint32_t color2, bool all) uint16_t WS2812FX::police_base(uint32_t color1, uint32_t color2, uint16_t width)
{ {
uint16_t counter = now * ((SEGMENT.speed >> 2) +1); uint16_t delay = 1 + (FRAMETIME<<3) / SEGLEN; // longer segments should change faster
uint16_t idexR = (counter * SEGLEN) >> 16; uint32_t it = now / map(SEGMENT.speed, 0, 255, delay<<4, delay);
if (idexR >= SEGLEN) idexR = 0; uint16_t offset = it % SEGLEN;
uint16_t topindex = SEGLEN >> 1; if (!width) width = 1;
uint16_t idexB = (idexR > topindex) ? idexR - topindex : idexR + topindex; for (uint16_t i = 0; i < width; i++) {
if (SEGENV.call == 0) SEGENV.aux0 = idexR; uint16_t indexR = (offset + i) % SEGLEN;
if (idexB >= SEGLEN) idexB = 0; //otherwise overflow on odd number of LEDs uint16_t indexB = (offset + i + (SEGLEN>>1)) % SEGLEN;
setPixelColor(indexR, color1);
if (all) { //different algo, ensuring immediate fill setPixelColor(indexB, color2);
if (idexB > idexR) {
fill(color2);
for (uint16_t i = idexR; i < idexB; i++) setPixelColor(i, color1);
} else {
fill(color1);
for (uint16_t i = idexB; i < idexR; i++) setPixelColor(i, color2);
} }
} else { //regular dot-only mode
uint8_t size = 1 + (SEGMENT.intensity >> 3);
if (size > SEGLEN/2) size = 1+ SEGLEN/2;
for (uint8_t i=0; i <= size; i++) {
setPixelColor(idexR+i, color1);
setPixelColor(idexB+i, color2);
}
if (SEGENV.aux0 != idexR) {
uint8_t gap = (SEGENV.aux0 < idexR)? idexR - SEGENV.aux0:SEGLEN - SEGENV.aux0 + idexR;
for (uint8_t i = 0; i <= gap ; i++) {
if ((idexR - i) < 0) idexR = SEGLEN-1 + i;
if ((idexB - i) < 0) idexB = SEGLEN-1 + i;
setPixelColor(idexR-i, color1);
setPixelColor(idexB-i, color2);
}
SEGENV.aux0 = idexR;
}
}
return FRAMETIME; return FRAMETIME;
} }
@ -1292,7 +1236,7 @@ uint16_t WS2812FX::police_base(uint32_t color1, uint32_t color2, bool all)
//American Police Light with all LEDs Red and Blue //American Police Light with all LEDs Red and Blue
uint16_t WS2812FX::mode_police_all() uint16_t WS2812FX::mode_police_all()
{ {
return police_base(RED, BLUE, true); return police_base(RED, BLUE, (SEGLEN>>1));
} }
@ -1300,15 +1244,15 @@ uint16_t WS2812FX::mode_police_all()
uint16_t WS2812FX::mode_police() uint16_t WS2812FX::mode_police()
{ {
fill(SEGCOLOR(1)); fill(SEGCOLOR(1));
return police_base(RED, BLUE, ((SEGLEN*(SEGMENT.intensity+1))>>9)); // max width is half the strip
return police_base(RED, BLUE, false);
} }
//Police All with custom colors //Police All with custom colors
uint16_t WS2812FX::mode_two_areas() uint16_t WS2812FX::mode_two_areas()
{ {
return police_base(SEGCOLOR(0), SEGCOLOR(1), true); fill(SEGCOLOR(2));
return police_base(SEGCOLOR(0), SEGCOLOR(1), ((SEGLEN*(SEGMENT.intensity+1))>>9)); // max width is half the strip
} }
@ -1318,7 +1262,7 @@ uint16_t WS2812FX::mode_two_dots()
fill(SEGCOLOR(2)); fill(SEGCOLOR(2));
uint32_t color2 = (SEGCOLOR(1) == SEGCOLOR(2)) ? SEGCOLOR(0) : SEGCOLOR(1); uint32_t color2 = (SEGCOLOR(1) == SEGCOLOR(2)) ? SEGCOLOR(0) : SEGCOLOR(1);
return police_base(SEGCOLOR(0), color2, false); return police_base(SEGCOLOR(0), color2, ((SEGLEN*(SEGMENT.intensity+1))>>9)); // max width is half the strip
} }
@ -1326,21 +1270,20 @@ uint16_t WS2812FX::mode_two_dots()
* Tricolor chase function * Tricolor chase function
*/ */
uint16_t WS2812FX::tricolor_chase(uint32_t color1, uint32_t color2) { uint16_t WS2812FX::tricolor_chase(uint32_t color1, uint32_t color2) {
uint32_t cycleTime = 50 + (255 - SEGMENT.speed)*2; uint32_t cycleTime = 50 + ((255 - SEGMENT.speed)<<1);
uint32_t it = now / cycleTime; uint32_t it = now / cycleTime; // iterator
uint8_t width = (1 + SEGMENT.intensity/32) * 3; //value of 1-8 for each colour uint8_t width = (1 + (SEGMENT.intensity>>4)); // value of 1-16 for each colour
uint8_t index = it % width; uint8_t index = it % (width*3);
for(uint16_t i = 0; i < SEGLEN; i++, index++) { for (uint16_t i = 0; i < SEGLEN; i++, index++) {
if(index > width-1) index = 0; if (index > (width*3)-1) index = 0;
uint32_t color = color1; uint32_t color = color1;
if(index > width*2/3-1) color = color_from_palette(i, true, PALETTE_SOLID_WRAP, 1); if (index > (width<<1)-1) color = color_from_palette(i, true, PALETTE_SOLID_WRAP, 1);
else if(index > width/3-1) color = color2; else if (index > width-1) color = color2;
setPixelColor(SEGLEN - i -1, color); setPixelColor(SEGLEN - i -1, color);
} }
return FRAMETIME; return FRAMETIME;
} }
@ -1548,7 +1491,7 @@ uint16_t WS2812FX::mode_random_chase(void)
return FRAMETIME; return FRAMETIME;
} }
//7 bytes
typedef struct Oscillator { typedef struct Oscillator {
int16_t pos; int16_t pos;
int8_t size; int8_t size;
@ -1780,7 +1723,7 @@ uint16_t WS2812FX::mode_fire_2012()
// Step 1. Cool down every cell a little // Step 1. Cool down every cell a little
for (uint16_t i = 0; i < SEGLEN; i++) { for (uint16_t i = 0; i < SEGLEN; i++) {
uint8_t temp = qsub8(heat[i], random8(0, (((20 + SEGMENT.speed /3) * 10) / SEGLEN) + 2)); uint8_t temp = qsub8(heat[i], random8(0, (((20 + SEGMENT.speed /3) * 10) / SEGLEN) + 2));
heat[i] = (temp==0 && i<ignition) ? 2 : temp; // prevent ignition area from becoming black heat[i] = (temp==0 && i<ignition) ? 16 : temp; // prevent ignition area from becoming black
} }
// Step 2. Heat from each cell drifts 'up' and diffuses a little // Step 2. Heat from each cell drifts 'up' and diffuses a little
@ -1994,7 +1937,7 @@ uint16_t WS2812FX::mode_colortwinkle()
if (fadeUp) { if (fadeUp) {
CRGB incrementalColor = fastled_col; CRGB incrementalColor = fastled_col;
incrementalColor.nscale8_video( fadeUpAmount); incrementalColor.nscale8_video(fadeUpAmount);
fastled_col += incrementalColor; fastled_col += incrementalColor;
if (fastled_col.red == 255 || fastled_col.green == 255 || fastled_col.blue == 255) { if (fastled_col.red == 255 || fastled_col.green == 255 || fastled_col.blue == 255) {
@ -2002,24 +1945,21 @@ uint16_t WS2812FX::mode_colortwinkle()
} }
setPixelColor(i, fastled_col.red, fastled_col.green, fastled_col.blue); setPixelColor(i, fastled_col.red, fastled_col.green, fastled_col.blue);
if (col_to_crgb(getPixelColor(i)) == prev) //fix "stuck" pixels if (col_to_crgb(getPixelColor(i)) == prev) { //fix "stuck" pixels
{
fastled_col += fastled_col; fastled_col += fastled_col;
setPixelColor(i, fastled_col.red, fastled_col.green, fastled_col.blue); setPixelColor(i, fastled_col.red, fastled_col.green, fastled_col.blue);
} }
} else { } else {
fastled_col.nscale8( 255 - fadeDownAmount); fastled_col.nscale8(255 - fadeDownAmount);
setPixelColor(i, fastled_col.red, fastled_col.green, fastled_col.blue); setPixelColor(i, fastled_col.red, fastled_col.green, fastled_col.blue);
} }
} }
for (uint16_t j = 0; j <= SEGLEN / 50; j++) for (uint16_t j = 0; j <= SEGLEN / 50; j++) {
{
if (random8() <= SEGMENT.intensity) { if (random8() <= SEGMENT.intensity) {
for (uint8_t times = 0; times < 5; times++) //attempt to spawn a new pixel 5 times for (uint8_t times = 0; times < 5; times++) { //attempt to spawn a new pixel 5 times
{
int i = random16(SEGLEN); int i = random16(SEGLEN);
if(getPixelColor(i) == 0) { if (getPixelColor(i) == 0) {
fastled_col = ColorFromPalette(currentPalette, random8(), 64, NOBLEND); fastled_col = ColorFromPalette(currentPalette, random8(), 64, NOBLEND);
uint16_t index = i >> 3; uint16_t index = i >> 3;
uint8_t bitNum = i & 0x07; uint8_t bitNum = i & 0x07;
@ -2170,10 +2110,14 @@ typedef struct Ripple {
uint16_t pos; uint16_t pos;
} ripple; } ripple;
#ifdef ESP8266
#define MAX_RIPPLES 56
#else
#define MAX_RIPPLES 100
#endif
uint16_t WS2812FX::ripple_base(bool rainbow) uint16_t WS2812FX::ripple_base(bool rainbow)
{ {
uint16_t maxRipples = 1 + (SEGLEN >> 2); uint16_t maxRipples = min(1 + (SEGLEN >> 2), MAX_RIPPLES); // 56 max for 18 segment ESP8266
if (maxRipples > 100) maxRipples = 100;
uint16_t dataSize = sizeof(ripple) * maxRipples; uint16_t dataSize = sizeof(ripple) * maxRipples;
if (!SEGENV.allocateData(dataSize)) return mode_static(); //allocation failed if (!SEGENV.allocateData(dataSize)) return mode_static(); //allocation failed
@ -2241,6 +2185,7 @@ uint16_t WS2812FX::ripple_base(bool rainbow)
} }
return FRAMETIME; return FRAMETIME;
} }
#undef MAX_RIPPLES
uint16_t WS2812FX::mode_ripple(void) { uint16_t WS2812FX::mode_ripple(void) {
return ripple_base(false); return ripple_base(false);
@ -2541,7 +2486,6 @@ uint16_t WS2812FX::mode_spots_fade()
//each needs 12 bytes //each needs 12 bytes
//Spark type is used for popcorn and 1D fireworks
typedef struct Ball { typedef struct Ball {
unsigned long lastBounceTime; unsigned long lastBounceTime;
float impactVelocity; float impactVelocity;
@ -2651,7 +2595,7 @@ uint16_t WS2812FX::mode_sinelon_dual(void) {
} }
uint16_t WS2812FX::mode_sinelon_rainbow(void) { uint16_t WS2812FX::mode_sinelon_rainbow(void) {
return sinelon_base(true, true); return sinelon_base(false, true);
} }
@ -2670,7 +2614,7 @@ uint16_t WS2812FX::mode_glitter()
//each needs 12 bytes //each needs 11 bytes
//Spark type is used for popcorn, 1D fireworks, and drip //Spark type is used for popcorn, 1D fireworks, and drip
typedef struct Spark { typedef struct Spark {
float pos; float pos;
@ -2685,7 +2629,7 @@ typedef struct Spark {
*/ */
uint16_t WS2812FX::mode_popcorn(void) { uint16_t WS2812FX::mode_popcorn(void) {
//allocate segment data //allocate segment data
uint16_t maxNumPopcorn = 24; uint16_t maxNumPopcorn = 22; // max 22 on 18 segment ESP8266
uint16_t dataSize = sizeof(spark) * maxNumPopcorn; uint16_t dataSize = sizeof(spark) * maxNumPopcorn;
if (!SEGENV.allocateData(dataSize)) return mode_static(); //allocation failed if (!SEGENV.allocateData(dataSize)) return mode_static(); //allocation failed
@ -2744,7 +2688,7 @@ uint16_t WS2812FX::candle(bool multi)
if (multi) if (multi)
{ {
//allocate segment data //allocate segment data
uint16_t dataSize = (SEGLEN -1) *3; uint16_t dataSize = (SEGLEN -1) *3; // max length of segment on 18 segment ESP8266 is 75 pixels
if (!SEGENV.allocateData(dataSize)) return candle(false); //allocation failed if (!SEGENV.allocateData(dataSize)) return candle(false); //allocation failed
} }
@ -2831,9 +2775,14 @@ uint16_t WS2812FX::mode_candle_multi()
/ based on the video: https://www.reddit.com/r/arduino/comments/c3sd46/i_made_this_fireworks_effect_for_my_led_strips/ / based on the video: https://www.reddit.com/r/arduino/comments/c3sd46/i_made_this_fireworks_effect_for_my_led_strips/
/ Speed sets frequency of new starbursts, intensity is the intensity of the burst / Speed sets frequency of new starbursts, intensity is the intensity of the burst
*/ */
#define STARBURST_MAX_FRAG 12 #ifdef ESP8266
#define STARBURST_MAX_FRAG 4
//each needs 64 byte #define STARBURST_MAX_STARS 6
#else
#define STARBURST_MAX_FRAG 10
#define STARBURST_MAX_STARS 11
#endif
//each needs 18+STARBURST_MAX_FRAG*4 bytes
typedef struct particle { typedef struct particle {
CRGB color; CRGB color;
uint32_t birth =0; uint32_t birth =0;
@ -2845,7 +2794,7 @@ typedef struct particle {
uint16_t WS2812FX::mode_starburst(void) { uint16_t WS2812FX::mode_starburst(void) {
uint8_t numStars = 1 + (SEGLEN >> 3); uint8_t numStars = 1 + (SEGLEN >> 3);
if (numStars > 15) numStars = 15; if (numStars > STARBURST_MAX_STARS) numStars = STARBURST_MAX_STARS; // 11 * 58 * 32 = 19k (ESP32), 6 * 34 * 18 = 4k (ESP8266)
uint16_t dataSize = sizeof(star) * numStars; uint16_t dataSize = sizeof(star) * numStars;
if (!SEGENV.allocateData(dataSize)) return mode_static(); //allocation failed if (!SEGENV.allocateData(dataSize)) return mode_static(); //allocation failed
@ -2946,18 +2895,22 @@ uint16_t WS2812FX::mode_starburst(void) {
} }
return FRAMETIME; return FRAMETIME;
} }
#undef STARBURST_MAX_FRAG
#undef STARBURST_MAX_STARS
/* /*
* Exploding fireworks effect * Exploding fireworks effect
* adapted from: http://www.anirama.com/1000leds/1d-fireworks/ * adapted from: http://www.anirama.com/1000leds/1d-fireworks/
*/ */
#ifdef ESP8266
#define MAX_SPARKS 20 // number of fragments
#else
#define MAX_SPARKS 58 // number of fragments
#endif
uint16_t WS2812FX::mode_exploding_fireworks(void) uint16_t WS2812FX::mode_exploding_fireworks(void)
{ {
//allocate segment data //allocate segment data
uint16_t numSparks = 2 + (SEGLEN >> 1); uint16_t numSparks = min(2 + (SEGLEN >> 1), MAX_SPARKS); // max 58 for 32 segment ESP32, 20 for 18 segment ESP8266
if (numSparks > 80) numSparks = 80;
uint16_t dataSize = sizeof(spark) * numSparks; uint16_t dataSize = sizeof(spark) * numSparks;
if (!SEGENV.allocateData(dataSize)) return mode_static(); //allocation failed if (!SEGENV.allocateData(dataSize)) return mode_static(); //allocation failed
@ -3052,7 +3005,7 @@ uint16_t WS2812FX::mode_exploding_fireworks(void)
SEGENV.aux0--; SEGENV.aux0--;
if (SEGENV.aux0 < 4) { if (SEGENV.aux0 < 4) {
SEGENV.aux0 = 0; //back to flare SEGENV.aux0 = 0; //back to flare
SEGENV.step = (SEGMENT.intensity > random8()); //decide firing side SEGENV.step = actuallyReverse ^ (SEGMENT.intensity > random8()); //decide firing side
} }
} }
@ -3060,6 +3013,7 @@ uint16_t WS2812FX::mode_exploding_fireworks(void)
return FRAMETIME; return FRAMETIME;
} }
#undef MAX_SPARKS
/* /*
@ -3069,7 +3023,7 @@ uint16_t WS2812FX::mode_exploding_fireworks(void)
uint16_t WS2812FX::mode_drip(void) uint16_t WS2812FX::mode_drip(void)
{ {
//allocate segment data //allocate segment data
uint16_t numDrops = 4; uint8_t numDrops = 4;
uint16_t dataSize = sizeof(spark) * numDrops; uint16_t dataSize = sizeof(spark) * numDrops;
if (!SEGENV.allocateData(dataSize)) return mode_static(); //allocation failed if (!SEGENV.allocateData(dataSize)) return mode_static(); //allocation failed
@ -3077,7 +3031,7 @@ uint16_t WS2812FX::mode_drip(void)
Spark* drops = reinterpret_cast<Spark*>(SEGENV.data); Spark* drops = reinterpret_cast<Spark*>(SEGENV.data);
numDrops = 1 + (SEGMENT.intensity >> 6); numDrops = 1 + (SEGMENT.intensity >> 6); // 255>>6 = 3
float gravity = -0.0005 - (SEGMENT.speed/50000.0); float gravity = -0.0005 - (SEGMENT.speed/50000.0);
gravity *= SEGLEN; gravity *= SEGLEN;
@ -3107,7 +3061,7 @@ uint16_t WS2812FX::mode_drip(void)
if (drops[j].pos > 0) { // fall until end of segment if (drops[j].pos > 0) { // fall until end of segment
drops[j].pos += drops[j].vel; drops[j].pos += drops[j].vel;
if (drops[j].pos < 0) drops[j].pos = 0; if (drops[j].pos < 0) drops[j].pos = 0;
drops[j].vel += gravity; drops[j].vel += gravity; // gravity is negative
for (uint16_t i=1;i<7-drops[j].colIndex;i++) { // some minor math so we don't expand bouncing droplets for (uint16_t i=1;i<7-drops[j].colIndex;i++) { // some minor math so we don't expand bouncing droplets
uint16_t pos = constrain(uint16_t(drops[j].pos) +i, 0, SEGLEN-1); //this is BAD, returns a pos >= SEGLEN occasionally uint16_t pos = constrain(uint16_t(drops[j].pos) +i, 0, SEGLEN-1); //this is BAD, returns a pos >= SEGLEN occasionally
@ -3142,6 +3096,7 @@ uint16_t WS2812FX::mode_drip(void)
* Tetris or Stacking (falling bricks) Effect * Tetris or Stacking (falling bricks) Effect
* by Blaz Kristan (https://github.com/blazoncek, https://blaz.at/home) * by Blaz Kristan (https://github.com/blazoncek, https://blaz.at/home)
*/ */
//12 bytes
typedef struct Tetris { typedef struct Tetris {
float pos; float pos;
float speed; float speed;
@ -3163,8 +3118,8 @@ uint16_t WS2812FX::mode_tetrix(void) {
} }
if (SEGENV.step == 0) { //init if (SEGENV.step == 0) { //init
drop->speed = 0.0238 * (SEGMENT.speed ? (SEGMENT.speed>>3)+1 : random8(6,40)); // set speed drop->speed = 0.0238 * (SEGMENT.speed ? (SEGMENT.speed>>2)+1 : random8(6,64)); // set speed
drop->pos = SEGLEN-1; // start at end of segment drop->pos = SEGLEN; // start at end of segment (no need to subtract 1)
drop->col = color_from_palette(random8(0,15)<<4,false,false,0); // limit color choices so there is enough HUE gap drop->col = color_from_palette(random8(0,15)<<4,false,false,0); // limit color choices so there is enough HUE gap
SEGENV.step = 1; // drop state (0 init, 1 forming, 2 falling) SEGENV.step = 1; // drop state (0 init, 1 forming, 2 falling)
SEGENV.aux0 = (SEGMENT.intensity ? (SEGMENT.intensity>>5)+1 : random8(1,5)) * (1+(SEGLEN>>6)); // size of brick SEGENV.aux0 = (SEGMENT.intensity ? (SEGMENT.intensity>>5)+1 : random8(1,5)) * (1+(SEGLEN>>6)); // size of brick
@ -3196,13 +3151,17 @@ uint16_t WS2812FX::mode_tetrix(void) {
/ adapted from https://github.com/atuline/FastLED-Demos/blob/master/plasma/plasma.ino / adapted from https://github.com/atuline/FastLED-Demos/blob/master/plasma/plasma.ino
*/ */
uint16_t WS2812FX::mode_plasma(void) { uint16_t WS2812FX::mode_plasma(void) {
uint8_t thisPhase = beatsin8(6,-64,64); // Setting phase change for a couple of waves. // initialize phases on start
uint8_t thatPhase = beatsin8(7,-64,64); if (SEGENV.call == 0) {
SEGENV.aux0 = random8(0,2); // add a bit of randomness
}
uint8_t thisPhase = beatsin8(6+SEGENV.aux0,-64,64);
uint8_t thatPhase = beatsin8(7+SEGENV.aux0,-64,64);
for (int i = 0; i < SEGLEN; i++) { // For each of the LED's in the strand, set color & brightness based on a wave as follows: for (int i = 0; i < SEGLEN; i++) { // For each of the LED's in the strand, set color & brightness based on a wave as follows:
uint8_t colorIndex = cubicwave8(((i*(1+ 3*(SEGMENT.speed >> 5)))+(thisPhase)) & 0xFF)/2 // factor=23 // Create a wave and add a phase change and add another wave with its own phase change. uint8_t colorIndex = cubicwave8((i*(2+ 3*(SEGMENT.speed >> 5))+thisPhase) & 0xFF)/2 // factor=23 // Create a wave and add a phase change and add another wave with its own phase change.
+ cos8(((i*(1+ 2*(SEGMENT.speed >> 5)))+(thatPhase)) & 0xFF)/2; // factor=15 // Hey, you can even change the frequencies if you wish. + cos8((i*(1+ 2*(SEGMENT.speed >> 5))+thatPhase) & 0xFF)/2; // factor=15 // Hey, you can even change the frequencies if you wish.
uint8_t thisBright = qsub8(colorIndex, beatsin8(6,0, (255 - SEGMENT.intensity)|0x01 )); uint8_t thisBright = qsub8(colorIndex, beatsin8(7,0, (128 - (SEGMENT.intensity>>1))));
CRGB color = ColorFromPalette(currentPalette, colorIndex, thisBright, LINEARBLEND); CRGB color = ColorFromPalette(currentPalette, colorIndex, thisBright, LINEARBLEND);
setPixelColor(i, color.red, color.green, color.blue); setPixelColor(i, color.red, color.green, color.blue);
} }
@ -3531,7 +3490,7 @@ uint16_t WS2812FX::mode_noisepal(void) { // S
uint16_t scale = 15 + (SEGMENT.intensity >> 2); //default was 30 uint16_t scale = 15 + (SEGMENT.intensity >> 2); //default was 30
//#define scale 30 //#define scale 30
uint16_t dataSize = sizeof(CRGBPalette16) * 2; //allocate space for 2 Palettes uint16_t dataSize = sizeof(CRGBPalette16) * 2; //allocate space for 2 Palettes (2 * 16 * 3 = 96 bytes)
if (!SEGENV.allocateData(dataSize)) return mode_static(); //allocation failed if (!SEGENV.allocateData(dataSize)) return mode_static(); //allocation failed
CRGBPalette16* palettes = reinterpret_cast<CRGBPalette16*>(SEGENV.data); CRGBPalette16* palettes = reinterpret_cast<CRGBPalette16*>(SEGENV.data);
@ -3644,7 +3603,7 @@ uint16_t WS2812FX::mode_chunchun(void)
return FRAMETIME; return FRAMETIME;
} }
//13 bytes
typedef struct Spotlight { typedef struct Spotlight {
float speed; float speed;
uint8_t colorIdx; uint8_t colorIdx;
@ -3661,6 +3620,11 @@ typedef struct Spotlight {
#define SPOT_TYPE_3X_DOT 4 #define SPOT_TYPE_3X_DOT 4
#define SPOT_TYPE_4X_DOT 5 #define SPOT_TYPE_4X_DOT 5
#define SPOT_TYPES_COUNT 6 #define SPOT_TYPES_COUNT 6
#ifdef ESP8266
#define SPOT_MAX_COUNT 17 //Number of simultaneous waves
#else
#define SPOT_MAX_COUNT 49 //Number of simultaneous waves
#endif
/* /*
* Spotlights moving back and forth that cast dancing shadows. * Spotlights moving back and forth that cast dancing shadows.
@ -3671,7 +3635,7 @@ typedef struct Spotlight {
*/ */
uint16_t WS2812FX::mode_dancing_shadows(void) uint16_t WS2812FX::mode_dancing_shadows(void)
{ {
uint8_t numSpotlights = map(SEGMENT.intensity, 0, 255, 2, 50); uint8_t numSpotlights = map(SEGMENT.intensity, 0, 255, 2, SPOT_MAX_COUNT); // 49 on 32 segment ESP32, 17 on 18 segment ESP8266
bool initialize = SEGENV.aux0 != numSpotlights; bool initialize = SEGENV.aux0 != numSpotlights;
SEGENV.aux0 = numSpotlights; SEGENV.aux0 = numSpotlights;
@ -3810,7 +3774,7 @@ uint16_t WS2812FX::mode_washing_machine(void) {
Modified, originally by Mark Kriegsman https://gist.github.com/kriegsman/1f7ccbbfa492a73c015e Modified, originally by Mark Kriegsman https://gist.github.com/kriegsman/1f7ccbbfa492a73c015e
*/ */
uint16_t WS2812FX::mode_blends(void) { uint16_t WS2812FX::mode_blends(void) {
uint16_t dataSize = sizeof(uint32_t) * SEGLEN; uint16_t dataSize = sizeof(uint32_t) * SEGLEN; // max segment length of 56 pixels on 18 segment ESP8266
if (!SEGENV.allocateData(dataSize)) return mode_static(); //allocation failed if (!SEGENV.allocateData(dataSize)) return mode_static(); //allocation failed
uint32_t* pixels = reinterpret_cast<uint32_t*>(SEGENV.data); uint32_t* pixels = reinterpret_cast<uint32_t*>(SEGENV.data);
uint8_t blendSpeed = map(SEGMENT.intensity, 0, UINT8_MAX, 10, 128); uint8_t blendSpeed = map(SEGMENT.intensity, 0, UINT8_MAX, 10, 128);
@ -3825,6 +3789,11 @@ uint16_t WS2812FX::mode_blends(void) {
return FRAMETIME; return FRAMETIME;
} }
/*
TV Simulator
Modified and adapted to WLED by Def3nder, based on "Fake TV Light for Engineers" by Phillip Burgess https://learn.adafruit.com/fake-tv-light-for-engineers/arduino-sketch
*/
//43 bytes
typedef struct TvSim { typedef struct TvSim {
uint32_t totalTime = 0; uint32_t totalTime = 0;
uint32_t fadeTime = 0; uint32_t fadeTime = 0;
@ -3845,11 +3814,6 @@ typedef struct TvSim {
uint16_t pb = 0; uint16_t pb = 0;
} tvSim; } tvSim;
/*
TV Simulator
Modified and adapted to WLED by Def3nder, based on "Fake TV Light for Engineers" by Phillip Burgess https://learn.adafruit.com/fake-tv-light-for-engineers/arduino-sketch
*/
uint16_t WS2812FX::mode_tv_simulator(void) { uint16_t WS2812FX::mode_tv_simulator(void) {
uint16_t nr, ng, nb, r, g, b, i, hue; uint16_t nr, ng, nb, r, g, b, i, hue;
uint8_t sat, bri, j; uint8_t sat, bri, j;
@ -3957,10 +3921,15 @@ uint16_t WS2812FX::mode_tv_simulator(void) {
*/ */
//CONFIG //CONFIG
#define W_MAX_COUNT 20 //Number of simultaneous waves #ifdef ESP8266
#define W_MAX_COUNT 9 //Number of simultaneous waves
#else
#define W_MAX_COUNT 20 //Number of simultaneous waves
#endif
#define W_MAX_SPEED 6 //Higher number, higher speed #define W_MAX_SPEED 6 //Higher number, higher speed
#define W_WIDTH_FACTOR 6 //Higher number, smaller waves #define W_WIDTH_FACTOR 6 //Higher number, smaller waves
//24 bytes
class AuroraWave { class AuroraWave {
private: private:
uint16_t ttl; uint16_t ttl;
@ -4055,10 +4024,10 @@ uint16_t WS2812FX::mode_aurora(void) {
if(SEGENV.aux0 != SEGMENT.intensity || SEGENV.call == 0) { if(SEGENV.aux0 != SEGMENT.intensity || SEGENV.call == 0) {
//Intensity slider changed or first call //Intensity slider changed or first call
SEGENV.aux1 = ((float)SEGMENT.intensity / 255) * W_MAX_COUNT; SEGENV.aux1 = map(SEGMENT.intensity, 0, 255, 2, W_MAX_COUNT);
SEGENV.aux0 = SEGMENT.intensity; SEGENV.aux0 = SEGMENT.intensity;
if(!SEGENV.allocateData(sizeof(AuroraWave) * SEGENV.aux1)) { if(!SEGENV.allocateData(sizeof(AuroraWave) * SEGENV.aux1)) { // 26 on 32 segment ESP32, 9 on 18 segment ESP8266
return mode_static(); //allocation failed return mode_static(); //allocation failed
} }

View File

@ -24,8 +24,6 @@
Modified for WLED Modified for WLED
*/ */
#include "wled.h"
#ifndef WS2812FX_h #ifndef WS2812FX_h
#define WS2812FX_h #define WS2812FX_h
@ -55,17 +53,17 @@
/* each segment uses 52 bytes of SRAM memory, so if you're application fails because of /* each segment uses 52 bytes of SRAM memory, so if you're application fails because of
insufficient memory, decreasing MAX_NUM_SEGMENTS may help */ insufficient memory, decreasing MAX_NUM_SEGMENTS may help */
#ifdef ESP8266 #ifdef ESP8266
#define MAX_NUM_SEGMENTS 12 #define MAX_NUM_SEGMENTS 16
/* How many color transitions can run at once */ /* How many color transitions can run at once */
#define MAX_NUM_TRANSITIONS 8 #define MAX_NUM_TRANSITIONS 8
/* How much data bytes all segments combined may allocate */ /* How much data bytes all segments combined may allocate */
#define MAX_SEGMENT_DATA 2048 #define MAX_SEGMENT_DATA 4096
#else #else
#ifndef MAX_NUM_SEGMENTS #ifndef MAX_NUM_SEGMENTS
#define MAX_NUM_SEGMENTS 16 #define MAX_NUM_SEGMENTS 32
#endif #endif
#define MAX_NUM_TRANSITIONS 16 #define MAX_NUM_TRANSITIONS 24
#define MAX_SEGMENT_DATA 8192 #define MAX_SEGMENT_DATA 20480
#endif #endif
#define LED_SKIP_AMOUNT 1 #define LED_SKIP_AMOUNT 1
@ -245,7 +243,7 @@ class WS2812FX {
// segment parameters // segment parameters
public: public:
typedef struct Segment { // 25 (28 in memory?) bytes typedef struct Segment { // 29 (32 in memory?) bytes
uint16_t start; uint16_t start;
uint16_t stop; //segment invalid if stop == 0 uint16_t stop; //segment invalid if stop == 0
uint16_t offset; uint16_t offset;
@ -257,6 +255,7 @@ class WS2812FX {
uint8_t grouping, spacing; uint8_t grouping, spacing;
uint8_t opacity; uint8_t opacity;
uint32_t colors[NUM_COLORS]; uint32_t colors[NUM_COLORS];
char *name;
bool setColor(uint8_t slot, uint32_t c, uint8_t segn) { //returns true if changed bool setColor(uint8_t slot, uint32_t c, uint8_t segn) { //returns true if changed
if (slot >= NUM_COLORS || segn >= MAX_NUM_SEGMENTS) return false; if (slot >= NUM_COLORS || segn >= MAX_NUM_SEGMENTS) return false;
if (c == colors[slot]) return false; if (c == colors[slot]) return false;
@ -296,19 +295,19 @@ class WS2812FX {
{ {
return ((options >> n) & 0x01); return ((options >> n) & 0x01);
} }
bool isSelected() inline bool isSelected()
{ {
return getOption(0); return getOption(0);
} }
bool isActive() inline bool isActive()
{ {
return stop > start; return stop > start;
} }
uint16_t length() inline uint16_t length()
{ {
return stop - start; return stop - start;
} }
uint16_t groupLength() inline uint16_t groupLength()
{ {
return grouping + spacing; return grouping + spacing;
} }
@ -345,17 +344,23 @@ class WS2812FX {
// segment runtime parameters // segment runtime parameters
typedef struct Segment_runtime { // 28 bytes typedef struct Segment_runtime { // 28 bytes
unsigned long next_time; unsigned long next_time; // millis() of next update
uint32_t step; uint32_t step; // custom "step" var
uint32_t call; uint32_t call; // call counter
uint16_t aux0; uint16_t aux0; // custom var
uint16_t aux1; uint16_t aux1; // custom var
byte* data = nullptr; byte* data = nullptr;
bool allocateData(uint16_t len){ bool allocateData(uint16_t len){
if (data && _dataLen == len) return true; //already allocated if (data && _dataLen == len) return true; //already allocated
deallocateData(); deallocateData();
if (WS2812FX::instance->_usedSegmentData + len > MAX_SEGMENT_DATA) return false; //not enough memory if (WS2812FX::instance->_usedSegmentData + len > MAX_SEGMENT_DATA) return false; //not enough memory
data = new (std::nothrow) byte[len]; // if possible use SPI RAM on ESP32
#if defined(ARDUINO_ARCH_ESP32) && defined(WLED_USE_PSRAM)
if (psramFound())
data = (byte*) ps_malloc(len);
else
#endif
data = (byte*) malloc(len);
if (!data) return false; //allocation failed if (!data) return false; //allocation failed
WS2812FX::instance->_usedSegmentData += len; WS2812FX::instance->_usedSegmentData += len;
_dataLen = len; _dataLen = len;
@ -363,7 +368,7 @@ class WS2812FX {
return true; return true;
} }
void deallocateData(){ void deallocateData(){
delete[] data; free(data);
data = nullptr; data = nullptr;
WS2812FX::instance->_usedSegmentData -= _dataLen; WS2812FX::instance->_usedSegmentData -= _dataLen;
_dataLen = 0; _dataLen = 0;
@ -389,7 +394,7 @@ class WS2812FX {
* the internal segment state should be reset. * the internal segment state should be reset.
* Call resetIfRequired before calling the next effect function. * Call resetIfRequired before calling the next effect function.
*/ */
void reset() { _requiresReset = true; } inline void reset() { _requiresReset = true; }
private: private:
uint16_t _dataLen = 0; uint16_t _dataLen = 0;
bool _requiresReset = false; bool _requiresReset = false;
@ -622,11 +627,12 @@ class WS2812FX {
trigger(void), trigger(void),
setSegment(uint8_t n, uint16_t start, uint16_t stop, uint8_t grouping = 0, uint8_t spacing = 0), setSegment(uint8_t n, uint16_t start, uint16_t stop, uint8_t grouping = 0, uint8_t spacing = 0),
resetSegments(), resetSegments(),
populateDefaultSegments(),
setPixelColor(uint16_t n, uint32_t c), setPixelColor(uint16_t n, uint32_t c),
setPixelColor(uint16_t n, uint8_t r, uint8_t g, uint8_t b, uint8_t w = 0), setPixelColor(uint16_t n, uint8_t r, uint8_t g, uint8_t b, uint8_t w = 0),
show(void), show(void),
setColorOrder(uint8_t co), setPixelSegment(uint8_t n),
setPixelSegment(uint8_t n); deserializeMap(uint8_t n=0);
bool bool
isRgbw = false, isRgbw = false,
@ -644,8 +650,6 @@ class WS2812FX {
paletteFade = 0, paletteFade = 0,
paletteBlend = 0, paletteBlend = 0,
milliampsPerLed = 55, milliampsPerLed = 55,
// getStripType(uint8_t strip=0),
// setStripType(uint8_t type, uint8_t strip=0),
getBrightness(void), getBrightness(void),
getMode(void), getMode(void),
getSpeed(void), getSpeed(void),
@ -654,24 +658,17 @@ class WS2812FX {
getMaxSegments(void), getMaxSegments(void),
//getFirstSelectedSegment(void), //getFirstSelectedSegment(void),
getMainSegmentId(void), getMainSegmentId(void),
getColorOrder(void),
gamma8(uint8_t), gamma8(uint8_t),
gamma8_cal(uint8_t, float), gamma8_cal(uint8_t, float),
sin_gap(uint16_t), sin_gap(uint16_t),
get_random_wheel_index(uint8_t); get_random_wheel_index(uint8_t);
int8_t int8_t
// setStripPin(uint8_t strip, int8_t pin),
// getStripPin(uint8_t strip=0),
// setStripPinClk(uint8_t strip, int8_t pin),
// getStripPinClk(uint8_t strip=0),
tristate_square8(uint8_t x, uint8_t pulsewidth, uint8_t attdec); tristate_square8(uint8_t x, uint8_t pulsewidth, uint8_t attdec);
uint16_t uint16_t
ablMilliampsMax, ablMilliampsMax,
currentMilliamps, currentMilliamps,
// setStripLen(uint8_t strip, uint16_t len),
// getStripLen(uint8_t strip=0),
triwave16(uint16_t), triwave16(uint16_t),
getFps(); getFps();
@ -849,7 +846,6 @@ class WS2812FX {
color_wipe(bool, bool), color_wipe(bool, bool),
dynamic(bool), dynamic(bool),
scan(bool), scan(bool),
theater_chase(uint32_t, uint32_t, bool),
running_base(bool,bool), running_base(bool,bool),
larson_scanner(bool), larson_scanner(bool),
sinelon_base(bool,bool), sinelon_base(bool,bool),
@ -857,8 +853,8 @@ class WS2812FX {
chase(uint32_t, uint32_t, uint32_t, bool), chase(uint32_t, uint32_t, uint32_t, bool),
gradient_base(bool), gradient_base(bool),
ripple_base(bool), ripple_base(bool),
police_base(uint32_t, uint32_t, bool), police_base(uint32_t, uint32_t, uint16_t),
running(uint32_t, uint32_t), running(uint32_t, uint32_t, bool theatre=false),
tricolor_chase(uint32_t, uint32_t), tricolor_chase(uint32_t, uint32_t),
twinklefox_base(bool), twinklefox_base(bool),
spots_base(uint16_t), spots_base(uint16_t),
@ -869,8 +865,7 @@ class WS2812FX {
void void
blendPixelColor(uint16_t n, uint32_t color, uint8_t blend), blendPixelColor(uint16_t n, uint32_t color, uint8_t blend),
startTransition(uint8_t oldBri, uint32_t oldCol, uint16_t dur, uint8_t segn, uint8_t slot), startTransition(uint8_t oldBri, uint32_t oldCol, uint16_t dur, uint8_t segn, uint8_t slot);
deserializeMap(void);
uint16_t* customMappingTable = nullptr; uint16_t* customMappingTable = nullptr;
uint16_t customMappingSize = 0; uint16_t customMappingSize = 0;

View File

@ -23,7 +23,7 @@
Modified heavily for WLED Modified heavily for WLED
*/ */
#include "wled.h"
#include "FX.h" #include "FX.h"
#include "palettes.h" #include "palettes.h"
@ -40,7 +40,7 @@
another example. Switches direction every 5 LEDs. another example. Switches direction every 5 LEDs.
{"map":[ {"map":[
0, 1, 2, 3, 4, 9, 8, 7, 6, 5, 10, 11, 12, 13, 14, 0, 1, 2, 3, 4, 9, 8, 7, 6, 5, 10, 11, 12, 13, 14,
19, 18, 17, 16, 15, 20, 21, 22, 23, 24, 29, 28, 27, 26, 25] 19, 18, 17, 16, 15, 20, 21, 22, 23, 24, 29, 28, 27, 26, 25]}
*/ */
//factory defaults LED setup //factory defaults LED setup
@ -218,14 +218,13 @@ uint16_t WS2812FX::realPixelIndex(uint16_t i) {
int16_t realIndex = iGroup; int16_t realIndex = iGroup;
if (IS_REVERSE) { if (IS_REVERSE) {
if (IS_MIRROR) { if (IS_MIRROR) {
realIndex = (SEGMENT.length() -1) / 2 - iGroup; //only need to index half the pixels realIndex = (SEGMENT.length() - 1) / 2 - iGroup; //only need to index half the pixels
} else { } else {
realIndex = SEGMENT.length() - iGroup - 1; realIndex = (SEGMENT.length() - 1) - iGroup;
} }
} }
realIndex += SEGMENT.start; realIndex += SEGMENT.start;
return realIndex; return realIndex;
} }
@ -246,7 +245,6 @@ void WS2812FX::setPixelColor(uint16_t i, byte r, byte g, byte b, byte w)
} }
if (SEGLEN) {//from segment if (SEGLEN) {//from segment
//color_blend(getpixel, col, _bri_t); (pseudocode for future blending of segments) //color_blend(getpixel, col, _bri_t); (pseudocode for future blending of segments)
if (_bri_t < 255) { if (_bri_t < 255) {
r = scale8(r, _bri_t); r = scale8(r, _bri_t);
@ -256,12 +254,12 @@ void WS2812FX::setPixelColor(uint16_t i, byte r, byte g, byte b, byte w)
} }
uint32_t col = ((w << 24) | (r << 16) | (g << 8) | (b)); uint32_t col = ((w << 24) | (r << 16) | (g << 8) | (b));
bool reversed = IS_REVERSE; /* Set all the pixels in the group */
uint16_t realIndex = realPixelIndex(i); uint16_t realIndex = realPixelIndex(i);
uint16_t len = SEGMENT.length(); uint16_t len = SEGMENT.length();
for (uint16_t j = 0; j < SEGMENT.grouping; j++) { for (uint16_t j = 0; j < SEGMENT.grouping; j++) {
int indexSet = realIndex + (reversed ? -j : j); uint16_t indexSet = realIndex + (IS_REVERSE ? -j : j);
if (indexSet >= SEGMENT.start && indexSet < SEGMENT.stop) { if (indexSet >= SEGMENT.start && indexSet < SEGMENT.stop) {
if (IS_MIRROR) { //set the corresponding mirrored pixel if (IS_MIRROR) { //set the corresponding mirrored pixel
uint16_t indexMir = SEGMENT.stop - indexSet + SEGMENT.start - 1; uint16_t indexMir = SEGMENT.stop - indexSet + SEGMENT.start - 1;
@ -282,7 +280,6 @@ void WS2812FX::setPixelColor(uint16_t i, byte r, byte g, byte b, byte w)
} }
} else { //live data, etc. } else { //live data, etc.
if (i < customMappingSize) i = customMappingTable[i]; if (i < customMappingSize) i = customMappingTable[i];
uint32_t col = ((w << 24) | (r << 16) | (g << 8) | (b)); uint32_t col = ((w << 24) | (r << 16) | (g << 8) | (b));
busses.setPixelColor(i, col); busses.setPixelColor(i, col);
} }
@ -563,6 +560,7 @@ uint32_t WS2812FX::getPixelColor(uint16_t i)
} }
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);
} }
@ -584,15 +582,6 @@ uint32_t WS2812FX::getLastShow(void) {
return _lastShow; return _lastShow;
} }
//TODO these need to be on a per-strip basis
uint8_t WS2812FX::getColorOrder(void) {
return COL_ORDER_GRB;
}
void WS2812FX::setColorOrder(uint8_t co) {
//bus->SetColorOrder(co);
}
void WS2812FX::setSegment(uint8_t n, uint16_t i1, uint16_t i2, uint8_t grouping, uint8_t spacing) { void WS2812FX::setSegment(uint8_t n, uint16_t i1, uint16_t i2, uint8_t grouping, uint8_t spacing) {
if (n >= MAX_NUM_SEGMENTS) return; if (n >= MAX_NUM_SEGMENTS) return;
Segment& seg = _segments[n]; Segment& seg = _segments[n];
@ -604,6 +593,10 @@ void WS2812FX::setSegment(uint8_t n, uint16_t i1, uint16_t i2, uint8_t grouping,
if (i2 <= i1) //disable segment if (i2 <= i1) //disable segment
{ {
seg.stop = 0; seg.stop = 0;
if (seg.name) {
delete[] seg.name;
seg.name = nullptr;
}
if (n == mainSegment) //if main segment is deleted, set first active as main segment if (n == mainSegment) //if main segment is deleted, set first active as main segment
{ {
for (uint8_t i = 0; i < MAX_NUM_SEGMENTS; i++) for (uint8_t i = 0; i < MAX_NUM_SEGMENTS; i++)
@ -628,6 +621,7 @@ void WS2812FX::setSegment(uint8_t n, uint16_t i1, uint16_t i2, uint8_t grouping,
} }
void WS2812FX::resetSegments() { void WS2812FX::resetSegments() {
for (uint8_t i = 0; i < MAX_NUM_SEGMENTS; i++) if (_segments[i].name) delete _segments[i].name;
mainSegment = 0; mainSegment = 0;
memset(_segments, 0, sizeof(_segments)); memset(_segments, 0, sizeof(_segments));
//memset(_segment_runtimes, 0, sizeof(_segment_runtimes)); //memset(_segment_runtimes, 0, sizeof(_segment_runtimes));
@ -656,6 +650,25 @@ void WS2812FX::resetSegments() {
_segment_runtimes[0].reset(); _segment_runtimes[0].reset();
} }
void WS2812FX::populateDefaultSegments() {
uint16_t length = 0;
for (uint8_t i=0; i<busses.getNumBusses(); i++) {
Bus *bus = busses.getBus(i);
if (bus == nullptr) continue;
_segments[i].start = bus->getStart();
length += bus->getLength();
_segments[i].stop = _segments[i].start + bus->getLength();
_segments[i].mode = DEFAULT_MODE;
_segments[i].colors[0] = DEFAULT_COLOR;
_segments[i].speed = DEFAULT_SPEED;
_segments[i].intensity = DEFAULT_INTENSITY;
_segments[i].grouping = 1;
_segments[i].setOption(SEG_OPTION_SELECTED, 1);
_segments[i].setOption(SEG_OPTION_ON, 1);
_segments[i].opacity = 255;
}
}
//After this function is called, setPixelColor() will use that segment (offsets, grouping, ... will apply) //After this function is called, setPixelColor() will use that segment (offsets, grouping, ... will apply)
void WS2812FX::setPixelSegment(uint8_t n) void WS2812FX::setPixelSegment(uint8_t n)
{ {
@ -1033,18 +1046,34 @@ uint32_t WS2812FX::color_from_palette(uint16_t i, bool mapping, bool wrap, uint8
//load custom mapping table from JSON file //load custom mapping table from JSON file
void WS2812FX::deserializeMap(void) { void WS2812FX::deserializeMap(uint8_t n) {
if (!WLED_FS.exists("/ledmap.json")) return; char fileName[32];
DynamicJsonDocument doc(JSON_BUFFER_SIZE); // full sized buffer for larger maps strcpy_P(fileName, PSTR("/ledmap"));
if (n) sprintf(fileName +7, "%d", n);
strcat(fileName, ".json");
bool isFile = WLED_FS.exists(fileName);
DEBUG_PRINTLN(F("Reading LED map from /ledmap.json...")); if (!isFile) {
// erase custom mapping if selecting nonexistent ledmap.json (n==0)
if (!readObjectFromFile("/ledmap.json", nullptr, &doc)) return; //if file does not exist just exit if (!n && customMappingTable != nullptr) {
customMappingSize = 0;
if (customMappingTable != nullptr) {
delete[] customMappingTable; delete[] customMappingTable;
customMappingTable = nullptr; customMappingTable = nullptr;
}
return;
}
DynamicJsonDocument doc(JSON_BUFFER_SIZE); // full sized buffer for larger maps
DEBUG_PRINT(F("Reading LED map from "));
DEBUG_PRINTLN(fileName);
if (!readObjectFromFile(fileName, nullptr, &doc)) return; //if file does not exist just exit
// erase old custom ledmap
if (customMappingTable != nullptr) {
customMappingSize = 0; customMappingSize = 0;
delete[] customMappingTable;
customMappingTable = nullptr;
} }
JsonArray map = doc[F("map")]; JsonArray map = doc[F("map")];

View File

@ -106,7 +106,7 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
uint16_t length = elm[F("len")] | 1; uint16_t length = elm[F("len")] | 1;
uint8_t colorOrder = (int)elm[F("order")]; uint8_t colorOrder = (int)elm[F("order")];
uint8_t skipFirst = elm[F("skip")]; uint8_t skipFirst = elm[F("skip")];
uint16_t start = elm[F("start")] | 0; uint16_t start = elm["start"] | 0;
uint8_t ledType = elm["type"] | TYPE_WS2812_RGB; uint8_t ledType = elm["type"] | TYPE_WS2812_RGB;
bool reversed = elm["rev"]; bool reversed = elm["rev"];
@ -398,7 +398,7 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
JsonObject dmx = doc["dmx"]; JsonObject dmx = doc["dmx"];
CJSON(DMXChannels, dmx[F("chan")]); CJSON(DMXChannels, dmx[F("chan")]);
CJSON(DMXGap,dmx[F("gap")]); CJSON(DMXGap,dmx[F("gap")]);
CJSON(DMXStart, dmx[F("start")]); CJSON(DMXStart, dmx["start"]);
CJSON(DMXStartLED,dmx[F("start-led")]); CJSON(DMXStartLED,dmx[F("start-led")]);
JsonArray dmx_fixmap = dmx[F("fixmap")]; JsonArray dmx_fixmap = dmx[F("fixmap")];
@ -516,7 +516,7 @@ void serializeConfig() {
Bus *bus = busses.getBus(s); Bus *bus = busses.getBus(s);
if (!bus || bus->getLength()==0) break; if (!bus || bus->getLength()==0) break;
JsonObject ins = hw_led_ins.createNestedObject(); JsonObject ins = hw_led_ins.createNestedObject();
ins[F("start")] = bus->getStart(); ins["start"] = bus->getStart();
ins[F("len")] = bus->getLength(); ins[F("len")] = bus->getLength();
JsonArray ins_pin = ins.createNestedArray("pin"); JsonArray ins_pin = ins.createNestedArray("pin");
uint8_t pins[5]; uint8_t pins[5];
@ -719,7 +719,7 @@ void serializeConfig() {
JsonObject dmx = doc.createNestedObject("dmx"); JsonObject dmx = doc.createNestedObject("dmx");
dmx[F("chan")] = DMXChannels; dmx[F("chan")] = DMXChannels;
dmx[F("gap")] = DMXGap; dmx[F("gap")] = DMXGap;
dmx[F("start")] = DMXStart; dmx["start"] = DMXStart;
dmx[F("start-led")] = DMXStartLED; dmx[F("start-led")] = DMXStartLED;
JsonArray dmx_fixmap = dmx.createNestedArray(F("fixmap")); JsonArray dmx_fixmap = dmx.createNestedArray(F("fixmap"));

File diff suppressed because one or more lines are too long

View File

@ -578,11 +578,13 @@ function populateSegments(s)
<input type="checkbox" id="seg${i}sel" onchange="selSeg(${i})" ${inst.sel ? "checked":""}> <input type="checkbox" id="seg${i}sel" onchange="selSeg(${i})" ${inst.sel ? "checked":""}>
<span class="checkmark schk"></span> <span class="checkmark schk"></span>
</label> </label>
<div class="segname" onclick="selSegEx(${i})"> <div class="segname">
Segment ${i} <div class="segntxt" onclick="selSegEx(${i})">${inst.n ? inst.n : "Segment "+i}</div>
<i class="icons edit-icon" id="seg${i}nedit" onclick="tglSegn(${i})">&#xe2c6;</i>
</div> </div>
<i class="icons e-icon flr ${expanded[i] ? "exp":""}" id="sege${i}" onclick="expand(${i})">&#xe395;</i> <i class="icons e-icon flr ${expanded[i] ? "exp":""}" id="sege${i}" onclick="expand(${i})">&#xe395;</i>
<div class="segin ${expanded[i] ? "expanded":""}" id="seg${i}"> <div class="segin ${expanded[i] ? "expanded":""}" id="seg${i}">
<input type="text" class="ptxt stxt noslide" id="seg${i}t" autocomplete="off" maxlength=32 value="${inst.n?inst.n:""}" placeholder="Enter name..."/>
<div class="sbs"> <div class="sbs">
<i class="icons e-icon pwr ${powered[i] ? "act":""}" id="seg${i}pwr" onclick="setSegPwr(${i})">&#xe08f;</i> <i class="icons e-icon pwr ${powered[i] ? "act":""}" id="seg${i}pwr" onclick="setSegPwr(${i})">&#xe08f;</i>
<div class="sliderwrap il sws"> <div class="sliderwrap il sws">
@ -649,7 +651,7 @@ function populateSegments(s)
function populateEffects(effects) function populateEffects(effects)
{ {
var html = `<div class="searchbar"><input type="text" class="search" placeholder="Search" oninput="search(this)" /> var html = `<div class="searchbar"><input type="text" class="search" placeholder="Search" oninput="search(this)" />
<i class="icons search-cancel-icon" onclick="cancelSearch(this)">&#xe38f;</i></div>`; <i class="icons search-icon">&#xe0a1;</i><i class="icons search-cancel-icon" onclick="cancelSearch(this)">&#xe38f;</i></div>`;
effects.shift(); //remove solid effects.shift(); //remove solid
for (let i = 0; i < effects.length; i++) { for (let i = 0; i < effects.length; i++) {
@ -695,7 +697,7 @@ function populatePalettes(palettes)
}); });
var html = `<div class="searchbar"><input type="text" class="search" placeholder="Search" oninput="search(this)" /> var html = `<div class="searchbar"><input type="text" class="search" placeholder="Search" oninput="search(this)" />
<i class="icons search-cancel-icon" onclick="cancelSearch(this)">&#xe38f;</i></div>`; <i class="icons search-icon">&#xe0a1;</i><i class="icons search-cancel-icon" onclick="cancelSearch(this)">&#xe38f;</i></div>`;
for (let i = 0; i < palettes.length; i++) { for (let i = 0; i < palettes.length; i++) {
html += generateListItemHtml( html += generateListItemHtml(
'palette', 'palette',
@ -1258,9 +1260,11 @@ function makeSeg() {
var cn = `<div class="seg"> var cn = `<div class="seg">
<div class="segname newseg"> <div class="segname newseg">
New segment ${lowestUnused} New segment ${lowestUnused}
<i class="icons edit-icon" style="display: inline;" onclick="tglSegn(${lowestUnused})">&#xe2c6;</i>
</div> </div>
<br> <br>
<div class="segin expanded"> <div class="segin expanded">
<input type="text" class="ptxt stxt noslide" id="seg${lowestUnused}t" autocomplete="off" maxlength=32 value="" placeholder="Enter name..."/>
<table class="segt"> <table class="segt">
<tr> <tr>
<td class="segtd">Start LED</td> <td class="segtd">Start LED</td>
@ -1405,7 +1409,7 @@ function makeP(i,pl) {
return ` return `
<input type="text" class="ptxt noslide" id="p${i}txt" autocomplete="off" maxlength=32 value="${(i>0)?pName(i):""}" placeholder="Enter name..."/><br> <input type="text" class="ptxt noslide" id="p${i}txt" autocomplete="off" maxlength=32 value="${(i>0)?pName(i):""}" placeholder="Enter name..."/><br>
<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="qltxt noslide" 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">
@ -1482,6 +1486,12 @@ function tglCs(i){
d.getElementById(`p${i}o2`).style.display = !pss? "block" : "none"; d.getElementById(`p${i}o2`).style.display = !pss? "block" : "none";
} }
function tglSegn(s)
{
d.getElementById(`seg${s}t`).style.display =
(window.getComputedStyle(d.getElementById(`seg${s}t`)).display === "none") ? "inline":"none";
}
function selSegEx(s) function selSegEx(s)
{ {
var obj = {"seg":[]}; var obj = {"seg":[]};
@ -1498,10 +1508,11 @@ function selSeg(s){
} }
function setSeg(s){ function setSeg(s){
var name = d.getElementById(`seg${s}t`).value;
var start = parseInt(d.getElementById(`seg${s}s`).value); var start = parseInt(d.getElementById(`seg${s}s`).value);
var stop = parseInt(d.getElementById(`seg${s}e`).value); var stop = parseInt(d.getElementById(`seg${s}e`).value);
if (stop <= start) {delSeg(s); return;} if (stop <= start) {delSeg(s); return;}
var obj = {"seg": {"id": s, "start": start, "stop": (cfg.comp.seglen?start:0)+stop}}; var obj = {"seg": {"id": s, "n": name, "start": start, "stop": (cfg.comp.seglen?start:0)+stop}};
if (d.getElementById(`seg${s}grp`)) if (d.getElementById(`seg${s}grp`))
{ {
var grp = parseInt(d.getElementById(`seg${s}grp`).value); var grp = parseInt(d.getElementById(`seg${s}grp`).value);
@ -1955,7 +1966,10 @@ function expand(i,a)
if (!a) expanded[i] = !expanded[i]; if (!a) expanded[i] = !expanded[i];
d.getElementById('seg' +i).style.display = (expanded[i]) ? "block":"none"; d.getElementById('seg' +i).style.display = (expanded[i]) ? "block":"none";
d.getElementById('sege' +i).style.transform = (expanded[i]) ? "rotate(180deg)":"rotate(0deg)"; d.getElementById('sege' +i).style.transform = (expanded[i]) ? "rotate(180deg)":"rotate(0deg)";
if (i < 100) return; //no preset, we are done if (i < 100) {
d.getElementById(`seg${i}nedit`).style.display = (expanded[i]) ? "inline":"none";
return; //no preset, we are done
}
var p = i-100; var p = i-100;
d.getElementById(`p${p}o`).style.background = (expanded[i] || p != currentPreset)?"var(--c-2)":"var(--c-6)"; d.getElementById(`p${p}o`).style.background = (expanded[i] || p != currentPreset)?"var(--c-2)":"var(--c-6)";

View File

@ -208,7 +208,7 @@
</head> </head>
<body onload="S()"> <body onload="S()">
<form id="form_s" name="Sf" method="post"> <form id="form_s" name="Sf" method="post">
<div style="position:sticky;top:0;background-color:#222;"> <div style="position:sticky;top:0;background-color:#222;z-index:1;">
<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="button" onclick="Save()">Save</button><br> <button type="button" onclick="B()">Back</button><button type="button" onclick="Save()">Save</button><br>
<span id="lssuc" style="color:green; display:none">&#10004; Local UI settings saved!</span> <span id="lssuc" style="color:green; display:none">&#10004; Local UI settings saved!</span>

View File

@ -196,11 +196,12 @@ name="viewport" content="width=500"><title>UI Settings</title><script>
var initial_ds,initial_st,timeout,d=document,sett=null,l={comp:{labels:"Show button labels",colors:{LABEL:"Color selection methods",picker:"Color Wheel",rgb:"RGB sliders",quick:"Quick color selectors",hex:"HEX color input"},pcmbot:"Show bottom tab bar in PC mode",pid:"Show preset IDs",seglen:"Set segment length instead of stop LED",css:"Enable custom CSS",hdays:"Enable custom Holidays list"},theme:{alpha:{bg:"Background opacity",tab:"Button opacity"},bg:{url:"BG image URL",random:"Random BG image"},color:{bg:"BG HEX color"}}};function gId(e){return d.getElementById(e)}function isObject(e){return e&&"object"==typeof e&&!Array.isArray(e)}function set(e,t,i){for(var n=t,s=e.split("_"),o=s.length,a=0;a<o-1;a++){var l=s[a];n[l]||(n[l]={}),n=n[l]}n[s[o-1]]=i}function showToast(e,t=!1){var i=gId("toast");i.innerHTML=e,i.className=t?"error":"show",clearTimeout(timeout),i.style.animation="none",timeout=setTimeout((function(){i.className=i.className.replace("show","")}),2900)}function addRec(e,t="",n=null){var s="";for(i in e){var o=t+(t?"_":"")+i;if(isObject(e[i]))n&&n[i]&&n[i].LABEL&&(s+=`<h3>${n[i].LABEL}</h3>`),s+=addRec(e[i],o,n?n[i]:null);else{var a=o;if(n&&n[i]?a=n[i]:e[i+"LABEL"]&&(a=e[i+"LABEL"]),i.indexOf("LABEL")>0)continue;var l=typeof e[i];gId(o)?("boolean"===l?gId(o).checked=e[i]:gId(o).value=e[i],gId(o).previousElementSibling.matches(".l")&&(gId(o).previousElementSibling.innerHTML=a)):"boolean"===l?s+=`${a}: <input class="agi cb" type="checkbox" id=${o} ${e[i]?"checked":""}><br>`:"number"===l?s+=`${a}: <input class="agi" type="number" id=${o} value=${e[i]}><br>`:"string"===l&&(s+=`${a}:<br><input class="agi" id=${o} value=${e[i]}><br>`)}}return s}function genForm(e){var t;t=addRec(e,"",l),gId("gen").innerHTML=t}function GetLS(){(sett=localStorage.getItem("wledUiCfg"))||(gId("lserr").style.display="inline");try{sett=JSON.parse(sett)}catch(e){sett={},gId("lserr").style.display="inline",gId("lserr").innerHTML="&#9888; Settings JSON parsing failed. ("+e+")"}genForm(sett),gId("dm").checked="light"===gId("theme_base").value}function SetLS(){for(var e=d.querySelectorAll(".agi"),t=0;t<e.length;t++){var i=e[t],n=i.classList.contains("cb")?i.checked:i.value;set(i.id,sett,n),console.log(`${i.id} set to ${n}`)}try{localStorage.setItem("wledUiCfg",JSON.stringify(sett)),gId("lssuc").style.display="inline"}catch(i){gId("lssuc").style.display="none",gId("lserr").style.display="inline",gId("lserr").innerHTML="&#9888; Settings JSON saving failed. ("+i+")"}}function Save(){SetLS(),d.Sf.DS.value==initial_ds&&d.Sf.ST.checked==initial_st||d.Sf.submit()}function S(){GetV(),initial_ds=d.Sf.DS.value,initial_st=d.Sf.ST.checked,GetLS()}function H(){window.open("https://github.com/Aircoookie/WLED/wiki/Settings#user-interface-settings")}function B(){window.open("/settings","_self")}function UI(){gId("idonthateyou").style.display=gId("dm").checked?"inline":"none";var e=gId("theme_base");e&&(e.value=gId("dm").checked?"light":"dark")}function setRandomBg(){gId("theme_bg_random").checked?gId("theme_bg_url").value="https://picsum.photos/1920/1080":gId("theme_bg_url").value=""}function checkRandomBg(){"https://picsum.photos/1920/1080"===gId("theme_bg_url").value?gId("theme_bg_random").checked=!0:gId("theme_bg_random").checked=!1}function uploadFile(e,t){var i=new XMLHttpRequest;i.addEventListener("load",(function(){showToast(this.responseText)})),i.addEventListener("error",(function(e){showToast(e.stack,!0)})),i.open("POST","/upload");var n=new FormData;return n.append("data",e.files[0],t),i.send(n),e.value="",!1}function GetV() {var d=document; var initial_ds,initial_st,timeout,d=document,sett=null,l={comp:{labels:"Show button labels",colors:{LABEL:"Color selection methods",picker:"Color Wheel",rgb:"RGB sliders",quick:"Quick color selectors",hex:"HEX color input"},pcmbot:"Show bottom tab bar in PC mode",pid:"Show preset IDs",seglen:"Set segment length instead of stop LED",css:"Enable custom CSS",hdays:"Enable custom Holidays list"},theme:{alpha:{bg:"Background opacity",tab:"Button opacity"},bg:{url:"BG image URL",random:"Random BG image"},color:{bg:"BG HEX color"}}};function gId(e){return d.getElementById(e)}function isObject(e){return e&&"object"==typeof e&&!Array.isArray(e)}function set(e,t,i){for(var n=t,s=e.split("_"),o=s.length,a=0;a<o-1;a++){var l=s[a];n[l]||(n[l]={}),n=n[l]}n[s[o-1]]=i}function showToast(e,t=!1){var i=gId("toast");i.innerHTML=e,i.className=t?"error":"show",clearTimeout(timeout),i.style.animation="none",timeout=setTimeout((function(){i.className=i.className.replace("show","")}),2900)}function addRec(e,t="",n=null){var s="";for(i in e){var o=t+(t?"_":"")+i;if(isObject(e[i]))n&&n[i]&&n[i].LABEL&&(s+=`<h3>${n[i].LABEL}</h3>`),s+=addRec(e[i],o,n?n[i]:null);else{var a=o;if(n&&n[i]?a=n[i]:e[i+"LABEL"]&&(a=e[i+"LABEL"]),i.indexOf("LABEL")>0)continue;var l=typeof e[i];gId(o)?("boolean"===l?gId(o).checked=e[i]:gId(o).value=e[i],gId(o).previousElementSibling.matches(".l")&&(gId(o).previousElementSibling.innerHTML=a)):"boolean"===l?s+=`${a}: <input class="agi cb" type="checkbox" id=${o} ${e[i]?"checked":""}><br>`:"number"===l?s+=`${a}: <input class="agi" type="number" id=${o} value=${e[i]}><br>`:"string"===l&&(s+=`${a}:<br><input class="agi" id=${o} value=${e[i]}><br>`)}}return s}function genForm(e){var t;t=addRec(e,"",l),gId("gen").innerHTML=t}function GetLS(){(sett=localStorage.getItem("wledUiCfg"))||(gId("lserr").style.display="inline");try{sett=JSON.parse(sett)}catch(e){sett={},gId("lserr").style.display="inline",gId("lserr").innerHTML="&#9888; Settings JSON parsing failed. ("+e+")"}genForm(sett),gId("dm").checked="light"===gId("theme_base").value}function SetLS(){for(var e=d.querySelectorAll(".agi"),t=0;t<e.length;t++){var i=e[t],n=i.classList.contains("cb")?i.checked:i.value;set(i.id,sett,n),console.log(`${i.id} set to ${n}`)}try{localStorage.setItem("wledUiCfg",JSON.stringify(sett)),gId("lssuc").style.display="inline"}catch(i){gId("lssuc").style.display="none",gId("lserr").style.display="inline",gId("lserr").innerHTML="&#9888; Settings JSON saving failed. ("+i+")"}}function Save(){SetLS(),d.Sf.DS.value==initial_ds&&d.Sf.ST.checked==initial_st||d.Sf.submit()}function S(){GetV(),initial_ds=d.Sf.DS.value,initial_st=d.Sf.ST.checked,GetLS()}function H(){window.open("https://github.com/Aircoookie/WLED/wiki/Settings#user-interface-settings")}function B(){window.open("/settings","_self")}function UI(){gId("idonthateyou").style.display=gId("dm").checked?"inline":"none";var e=gId("theme_base");e&&(e.value=gId("dm").checked?"light":"dark")}function setRandomBg(){gId("theme_bg_random").checked?gId("theme_bg_url").value="https://picsum.photos/1920/1080":gId("theme_bg_url").value=""}function checkRandomBg(){"https://picsum.photos/1920/1080"===gId("theme_bg_url").value?gId("theme_bg_random").checked=!0:gId("theme_bg_random").checked=!1}function uploadFile(e,t){var i=new XMLHttpRequest;i.addEventListener("load",(function(){showToast(this.responseText)})),i.addEventListener("error",(function(e){showToast(e.stack,!0)})),i.open("POST","/upload");var n=new FormData;return n.append("data",e.files[0],t),i.send(n),e.value="",!1}function GetV() {var d=document;
%CSS%%SCSS%</head><body onload="S()"><form %CSS%%SCSS%</head><body onload="S()"><form
id="form_s" name="Sf" method="post"><div id="form_s" name="Sf" method="post"><div
style="position:sticky;top:0;background-color:#222"><div class="helpB"><button style="position:sticky;top:0;background-color:#222;z-index:1"><div
type="button" onclick="H()">?</button></div><button type="button" onclick="B()"> class="helpB"><button type="button" onclick="H()">?</button></div><button
Back</button><button type="button" onclick="Save()">Save</button><br><span type="button" onclick="B()">Back</button><button type="button" onclick="Save()">
id="lssuc" style="color:green;display:none">&#10004; Local UI settings saved! Save</button><br><span id="lssuc" style="color:green;display:none">
</span> <span id="lserr" style="color:red;display:none"> &#10004; Local UI settings saved!</span> <span id="lserr"
style="color:red;display:none">
&#9888; Could not access local storage. Make sure it is enabled in your browser. &#9888; Could not access local storage. Make sure it is enabled in your browser.
</span><hr></div><h2>Web Setup</h2>Server description: <input name="DS" </span><hr></div><h2>Web Setup</h2>Server description: <input name="DS"
maxlength="32"><br>Sync button toggles both send and receive: <input maxlength="32"><br>Sync button toggles both send and receive: <input

File diff suppressed because it is too large Load Diff

View File

@ -15,13 +15,38 @@ void deserializeSegment(JsonObject elem, byte it, byte presetId)
//WS2812FX::Segment prev; //WS2812FX::Segment prev;
//prev = seg; //make a backup so we can tell if something changed //prev = seg; //make a backup so we can tell if something changed
uint16_t start = elem[F("start")] | seg.start; uint16_t start = elem["start"] | seg.start;
int stop = elem["stop"] | -1; int stop = elem["stop"] | -1;
if (stop < 0) { if (stop < 0) {
uint16_t len = elem[F("len")]; uint16_t len = elem[F("len")];
stop = (len > 0) ? start + len : seg.stop; stop = (len > 0) ? start + len : seg.stop;
} }
if (elem["n"]) {
// name field exists
if (seg.name) { //clear old name
delete[] seg.name;
seg.name = nullptr;
}
const char * name = elem["n"].as<const char*>();
size_t len = 0;
if (name != nullptr) len = strlen(name);
if (len > 0 && len < 33) {
seg.name = new char[len+1];
if (seg.name) strlcpy(seg.name, name, 33);
} else {
// but is empty (already deleted above)
elem.remove("n");
}
} else if (start != seg.start || stop != seg.stop) {
// clearing or setting segment without name field
if (seg.name) {
delete[] seg.name;
seg.name = nullptr;
}
}
uint16_t grp = elem["grp"] | seg.grouping; uint16_t grp = elem["grp"] | seg.grouping;
uint16_t spc = elem[F("spc")] | seg.spacing; uint16_t spc = elem[F("spc")] | seg.spacing;
strip.setSegment(id, start, stop, grp, spc); strip.setSegment(id, start, stop, grp, spc);
@ -328,7 +353,7 @@ void serializeSegment(JsonObject& root, WS2812FX::Segment& seg, byte id, bool fo
{ {
root["id"] = id; root["id"] = id;
if (segmentBounds) { if (segmentBounds) {
root[F("start")] = seg.start; root["start"] = seg.start;
root["stop"] = seg.stop; root["stop"] = seg.stop;
} }
if (!forPreset) root[F("len")] = seg.stop - seg.start; if (!forPreset) root[F("len")] = seg.stop - seg.start;
@ -339,6 +364,8 @@ void serializeSegment(JsonObject& root, WS2812FX::Segment& seg, byte id, bool fo
byte segbri = seg.opacity; byte segbri = seg.opacity;
root["bri"] = (segbri) ? segbri : 255; root["bri"] = (segbri) ? segbri : 255;
if (segmentBounds && seg.name != nullptr) root["n"] = reinterpret_cast<const char *>(seg.name); //not good practice, but decreases required JSON buffer
char colstr[70]; colstr[0] = '['; colstr[1] = '\0'; //max len 68 (5 chan, all 255) char colstr[70]; colstr[0] = '['; colstr[1] = '\0'; //max len 68 (5 chan, all 255)
for (uint8_t i = 0; i < 3; i++) for (uint8_t i = 0; i < 3; i++)
@ -655,37 +682,37 @@ void serializePalettes(JsonObject root, AsyncWebServerRequest* request)
curPalette.add("r"); curPalette.add("r");
break; break;
case 2: //primary color only case 2: //primary color only
curPalette.add(F("c1")); curPalette.add("c1");
break; break;
case 3: //primary + secondary case 3: //primary + secondary
curPalette.add(F("c1")); curPalette.add("c1");
curPalette.add(F("c1")); curPalette.add("c1");
curPalette.add(F("c2")); curPalette.add("c2");
curPalette.add(F("c2")); curPalette.add("c2");
break; break;
case 4: //primary + secondary + tertiary case 4: //primary + secondary + tertiary
curPalette.add(F("c3")); curPalette.add("c3");
curPalette.add(F("c2")); curPalette.add("c2");
curPalette.add(F("c1")); curPalette.add("c1");
break; break;
case 5: {//primary + secondary (+tert if not off), more distinct case 5: {//primary + secondary (+tert if not off), more distinct
curPalette.add(F("c1")); curPalette.add("c1");
curPalette.add(F("c1")); curPalette.add("c1");
curPalette.add(F("c1")); curPalette.add("c1");
curPalette.add(F("c1")); curPalette.add("c1");
curPalette.add(F("c1")); curPalette.add("c1");
curPalette.add(F("c2")); curPalette.add("c2");
curPalette.add(F("c2")); curPalette.add("c2");
curPalette.add(F("c2")); curPalette.add("c2");
curPalette.add(F("c2")); curPalette.add("c2");
curPalette.add(F("c2")); curPalette.add("c2");
curPalette.add(F("c3")); curPalette.add("c3");
curPalette.add(F("c3")); curPalette.add("c3");
curPalette.add(F("c3")); curPalette.add("c3");
curPalette.add(F("c3")); curPalette.add("c3");
curPalette.add(F("c3")); curPalette.add("c3");
curPalette.add(F("c1")); curPalette.add("c1");
break;} break;}
case 6: //Party colors case 6: //Party colors
setPaletteColors(curPalette, PartyColors_p); setPaletteColors(curPalette, PartyColors_p);