Merge pull request #2184 from Aircoookie/fx-mods
FX optimisations and segment names
This commit is contained in:
commit
b8e8028eb9
261
wled00/FX.cpp
261
wled00/FX.cpp
@ -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.
|
||||
* Inspired by the Adafruit examples.
|
||||
*/
|
||||
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.
|
||||
*/
|
||||
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.
|
||||
*/
|
||||
uint16_t WS2812FX::running(uint32_t color1, uint32_t color2) {
|
||||
uint8_t pxw = 1 + (SEGMENT.intensity >> 5);
|
||||
uint32_t cycleTime = 35 + (255 - SEGMENT.speed);
|
||||
uint16_t WS2812FX::running(uint32_t color1, uint32_t color2, bool theatre) {
|
||||
uint8_t width = (theatre ? 3 : 1) + (SEGMENT.intensity >> 4); // window
|
||||
uint32_t cycleTime = 50 + (255 - SEGMENT.speed);
|
||||
uint32_t it = now / cycleTime;
|
||||
if (SEGMENT.speed == 0) it = 0;
|
||||
|
||||
bool usePalette = color1 == SEGCOLOR(0);
|
||||
|
||||
for(uint16_t i = 0; i < SEGLEN; i++) {
|
||||
if((i + SEGENV.aux0) % (pxw*2) < pxw) {
|
||||
if (color1 == SEGCOLOR(0))
|
||||
{
|
||||
setPixelColor(SEGLEN -i -1, color_from_palette(SEGLEN -i -1, true, PALETTE_SOLID_WRAP, 0));
|
||||
} else
|
||||
{
|
||||
setPixelColor(SEGLEN -i -1, color1);
|
||||
}
|
||||
uint32_t col = color2;
|
||||
if (usePalette) color1 = color_from_palette(i, true, PALETTE_SOLID_WRAP, 0);
|
||||
if (theatre) {
|
||||
if ((i % width) == SEGENV.aux0) col = color1;
|
||||
} 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 )
|
||||
{
|
||||
SEGENV.aux0 = (SEGENV.aux0 +1) % (pxw*2);
|
||||
SEGENV.aux0 = (SEGENV.aux0 +1) % (theatre ? width : (width<<1));
|
||||
SEGENV.step = it;
|
||||
}
|
||||
return FRAMETIME;
|
||||
@ -1247,44 +1216,19 @@ uint16_t WS2812FX::mode_loading(void) {
|
||||
|
||||
|
||||
//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 idexR = (counter * SEGLEN) >> 16;
|
||||
if (idexR >= SEGLEN) idexR = 0;
|
||||
|
||||
uint16_t topindex = SEGLEN >> 1;
|
||||
uint16_t idexB = (idexR > topindex) ? idexR - topindex : idexR + topindex;
|
||||
if (SEGENV.call == 0) SEGENV.aux0 = idexR;
|
||||
if (idexB >= SEGLEN) idexB = 0; //otherwise overflow on odd number of LEDs
|
||||
|
||||
if (all) { //different algo, ensuring immediate fill
|
||||
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;
|
||||
}
|
||||
}
|
||||
uint16_t delay = 1 + (FRAMETIME<<3) / SEGLEN; // longer segments should change faster
|
||||
uint32_t it = now / map(SEGMENT.speed, 0, 255, delay<<4, delay);
|
||||
uint16_t offset = it % SEGLEN;
|
||||
|
||||
if (!width) width = 1;
|
||||
for (uint16_t i = 0; i < width; i++) {
|
||||
uint16_t indexR = (offset + i) % SEGLEN;
|
||||
uint16_t indexB = (offset + i + (SEGLEN>>1)) % SEGLEN;
|
||||
setPixelColor(indexR, color1);
|
||||
setPixelColor(indexB, color2);
|
||||
}
|
||||
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
|
||||
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()
|
||||
{
|
||||
fill(SEGCOLOR(1));
|
||||
|
||||
return police_base(RED, BLUE, false);
|
||||
return police_base(RED, BLUE, ((SEGLEN*(SEGMENT.intensity+1))>>9)); // max width is half the strip
|
||||
}
|
||||
|
||||
|
||||
//Police All with custom colors
|
||||
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));
|
||||
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
|
||||
*/
|
||||
uint16_t WS2812FX::tricolor_chase(uint32_t color1, uint32_t color2) {
|
||||
uint32_t cycleTime = 50 + (255 - SEGMENT.speed)*2;
|
||||
uint32_t it = now / cycleTime;
|
||||
uint8_t width = (1 + SEGMENT.intensity/32) * 3; //value of 1-8 for each colour
|
||||
uint8_t index = it % width;
|
||||
uint32_t cycleTime = 50 + ((255 - SEGMENT.speed)<<1);
|
||||
uint32_t it = now / cycleTime; // iterator
|
||||
uint8_t width = (1 + (SEGMENT.intensity>>4)); // value of 1-16 for each colour
|
||||
uint8_t index = it % (width*3);
|
||||
|
||||
for(uint16_t i = 0; i < SEGLEN; i++, index++) {
|
||||
if(index > width-1) index = 0;
|
||||
for (uint16_t i = 0; i < SEGLEN; i++, index++) {
|
||||
if (index > (width*3)-1) index = 0;
|
||||
|
||||
uint32_t color = color1;
|
||||
if(index > width*2/3-1) color = color_from_palette(i, true, PALETTE_SOLID_WRAP, 1);
|
||||
else if(index > width/3-1) color = color2;
|
||||
if (index > (width<<1)-1) color = color_from_palette(i, true, PALETTE_SOLID_WRAP, 1);
|
||||
else if (index > width-1) color = color2;
|
||||
|
||||
setPixelColor(SEGLEN - i -1, color);
|
||||
}
|
||||
|
||||
return FRAMETIME;
|
||||
}
|
||||
|
||||
@ -1548,7 +1491,7 @@ uint16_t WS2812FX::mode_random_chase(void)
|
||||
return FRAMETIME;
|
||||
}
|
||||
|
||||
|
||||
//7 bytes
|
||||
typedef struct Oscillator {
|
||||
int16_t pos;
|
||||
int8_t size;
|
||||
@ -1780,7 +1723,7 @@ uint16_t WS2812FX::mode_fire_2012()
|
||||
// Step 1. Cool down every cell a little
|
||||
for (uint16_t i = 0; i < SEGLEN; i++) {
|
||||
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
|
||||
@ -1994,7 +1937,7 @@ uint16_t WS2812FX::mode_colortwinkle()
|
||||
|
||||
if (fadeUp) {
|
||||
CRGB incrementalColor = fastled_col;
|
||||
incrementalColor.nscale8_video( fadeUpAmount);
|
||||
incrementalColor.nscale8_video(fadeUpAmount);
|
||||
fastled_col += incrementalColor;
|
||||
|
||||
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);
|
||||
|
||||
if (col_to_crgb(getPixelColor(i)) == prev) //fix "stuck" pixels
|
||||
{
|
||||
if (col_to_crgb(getPixelColor(i)) == prev) { //fix "stuck" pixels
|
||||
fastled_col += fastled_col;
|
||||
setPixelColor(i, fastled_col.red, fastled_col.green, fastled_col.blue);
|
||||
}
|
||||
} else {
|
||||
fastled_col.nscale8( 255 - fadeDownAmount);
|
||||
fastled_col.nscale8(255 - fadeDownAmount);
|
||||
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) {
|
||||
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);
|
||||
if(getPixelColor(i) == 0) {
|
||||
if (getPixelColor(i) == 0) {
|
||||
fastled_col = ColorFromPalette(currentPalette, random8(), 64, NOBLEND);
|
||||
uint16_t index = i >> 3;
|
||||
uint8_t bitNum = i & 0x07;
|
||||
@ -2170,10 +2110,14 @@ typedef struct Ripple {
|
||||
uint16_t pos;
|
||||
} ripple;
|
||||
|
||||
#ifdef ESP8266
|
||||
#define MAX_RIPPLES 56
|
||||
#else
|
||||
#define MAX_RIPPLES 100
|
||||
#endif
|
||||
uint16_t WS2812FX::ripple_base(bool rainbow)
|
||||
{
|
||||
uint16_t maxRipples = 1 + (SEGLEN >> 2);
|
||||
if (maxRipples > 100) maxRipples = 100;
|
||||
uint16_t maxRipples = min(1 + (SEGLEN >> 2), MAX_RIPPLES); // 56 max for 18 segment ESP8266
|
||||
uint16_t dataSize = sizeof(ripple) * maxRipples;
|
||||
|
||||
if (!SEGENV.allocateData(dataSize)) return mode_static(); //allocation failed
|
||||
@ -2241,6 +2185,7 @@ uint16_t WS2812FX::ripple_base(bool rainbow)
|
||||
}
|
||||
return FRAMETIME;
|
||||
}
|
||||
#undef MAX_RIPPLES
|
||||
|
||||
uint16_t WS2812FX::mode_ripple(void) {
|
||||
return ripple_base(false);
|
||||
@ -2541,7 +2486,6 @@ uint16_t WS2812FX::mode_spots_fade()
|
||||
|
||||
|
||||
//each needs 12 bytes
|
||||
//Spark type is used for popcorn and 1D fireworks
|
||||
typedef struct Ball {
|
||||
unsigned long lastBounceTime;
|
||||
float impactVelocity;
|
||||
@ -2651,7 +2595,7 @@ uint16_t WS2812FX::mode_sinelon_dual(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
|
||||
typedef struct Spark {
|
||||
float pos;
|
||||
@ -2685,7 +2629,7 @@ typedef struct Spark {
|
||||
*/
|
||||
uint16_t WS2812FX::mode_popcorn(void) {
|
||||
//allocate segment data
|
||||
uint16_t maxNumPopcorn = 24;
|
||||
uint16_t maxNumPopcorn = 22; // max 22 on 18 segment ESP8266
|
||||
uint16_t dataSize = sizeof(spark) * maxNumPopcorn;
|
||||
if (!SEGENV.allocateData(dataSize)) return mode_static(); //allocation failed
|
||||
|
||||
@ -2744,7 +2688,7 @@ uint16_t WS2812FX::candle(bool multi)
|
||||
if (multi)
|
||||
{
|
||||
//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
|
||||
}
|
||||
|
||||
@ -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/
|
||||
/ Speed sets frequency of new starbursts, intensity is the intensity of the burst
|
||||
*/
|
||||
#define STARBURST_MAX_FRAG 12
|
||||
|
||||
//each needs 64 byte
|
||||
#ifdef ESP8266
|
||||
#define STARBURST_MAX_FRAG 4
|
||||
#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 {
|
||||
CRGB color;
|
||||
uint32_t birth =0;
|
||||
@ -2845,7 +2794,7 @@ typedef struct particle {
|
||||
|
||||
uint16_t WS2812FX::mode_starburst(void) {
|
||||
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;
|
||||
|
||||
if (!SEGENV.allocateData(dataSize)) return mode_static(); //allocation failed
|
||||
@ -2946,18 +2895,22 @@ uint16_t WS2812FX::mode_starburst(void) {
|
||||
}
|
||||
return FRAMETIME;
|
||||
}
|
||||
|
||||
#undef STARBURST_MAX_FRAG
|
||||
#undef STARBURST_MAX_STARS
|
||||
|
||||
/*
|
||||
* Exploding fireworks effect
|
||||
* 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)
|
||||
{
|
||||
//allocate segment data
|
||||
uint16_t numSparks = 2 + (SEGLEN >> 1);
|
||||
if (numSparks > 80) numSparks = 80;
|
||||
uint16_t numSparks = min(2 + (SEGLEN >> 1), MAX_SPARKS); // max 58 for 32 segment ESP32, 20 for 18 segment ESP8266
|
||||
uint16_t dataSize = sizeof(spark) * numSparks;
|
||||
if (!SEGENV.allocateData(dataSize)) return mode_static(); //allocation failed
|
||||
|
||||
@ -3052,7 +3005,7 @@ uint16_t WS2812FX::mode_exploding_fireworks(void)
|
||||
SEGENV.aux0--;
|
||||
if (SEGENV.aux0 < 4) {
|
||||
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;
|
||||
}
|
||||
#undef MAX_SPARKS
|
||||
|
||||
|
||||
/*
|
||||
@ -3069,7 +3023,7 @@ uint16_t WS2812FX::mode_exploding_fireworks(void)
|
||||
uint16_t WS2812FX::mode_drip(void)
|
||||
{
|
||||
//allocate segment data
|
||||
uint16_t numDrops = 4;
|
||||
uint8_t numDrops = 4;
|
||||
uint16_t dataSize = sizeof(spark) * numDrops;
|
||||
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);
|
||||
|
||||
numDrops = 1 + (SEGMENT.intensity >> 6);
|
||||
numDrops = 1 + (SEGMENT.intensity >> 6); // 255>>6 = 3
|
||||
|
||||
float gravity = -0.0005 - (SEGMENT.speed/50000.0);
|
||||
gravity *= SEGLEN;
|
||||
@ -3107,13 +3061,13 @@ uint16_t WS2812FX::mode_drip(void)
|
||||
if (drops[j].pos > 0) { // fall until end of segment
|
||||
drops[j].pos += drops[j].vel;
|
||||
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
|
||||
uint16_t pos = constrain(uint16_t(drops[j].pos) +i, 0, SEGLEN-1); //this is BAD, returns a pos >= SEGLEN occasionally
|
||||
setPixelColor(pos,color_blend(BLACK,SEGCOLOR(0),drops[j].col/i)); //spread pixel with fade while falling
|
||||
}
|
||||
|
||||
|
||||
if (drops[j].colIndex > 2) { // during bounce, some water is on the floor
|
||||
setPixelColor(0,color_blend(SEGCOLOR(0),BLACK,drops[j].col));
|
||||
}
|
||||
@ -3142,6 +3096,7 @@ uint16_t WS2812FX::mode_drip(void)
|
||||
* Tetris or Stacking (falling bricks) Effect
|
||||
* by Blaz Kristan (https://github.com/blazoncek, https://blaz.at/home)
|
||||
*/
|
||||
//12 bytes
|
||||
typedef struct Tetris {
|
||||
float pos;
|
||||
float speed;
|
||||
@ -3163,8 +3118,8 @@ uint16_t WS2812FX::mode_tetrix(void) {
|
||||
}
|
||||
|
||||
if (SEGENV.step == 0) { //init
|
||||
drop->speed = 0.0238 * (SEGMENT.speed ? (SEGMENT.speed>>3)+1 : random8(6,40)); // set speed
|
||||
drop->pos = SEGLEN-1; // start at end of segment
|
||||
drop->speed = 0.0238 * (SEGMENT.speed ? (SEGMENT.speed>>2)+1 : random8(6,64)); // set speed
|
||||
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
|
||||
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
|
||||
@ -3196,13 +3151,17 @@ uint16_t WS2812FX::mode_tetrix(void) {
|
||||
/ adapted from https://github.com/atuline/FastLED-Demos/blob/master/plasma/plasma.ino
|
||||
*/
|
||||
uint16_t WS2812FX::mode_plasma(void) {
|
||||
uint8_t thisPhase = beatsin8(6,-64,64); // Setting phase change for a couple of waves.
|
||||
uint8_t thatPhase = beatsin8(7,-64,64);
|
||||
// initialize phases on start
|
||||
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:
|
||||
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.
|
||||
+ 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 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.
|
||||
uint8_t thisBright = qsub8(colorIndex, beatsin8(7,0, (128 - (SEGMENT.intensity>>1))));
|
||||
CRGB color = ColorFromPalette(currentPalette, colorIndex, thisBright, LINEARBLEND);
|
||||
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
|
||||
//#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
|
||||
|
||||
CRGBPalette16* palettes = reinterpret_cast<CRGBPalette16*>(SEGENV.data);
|
||||
@ -3644,7 +3603,7 @@ uint16_t WS2812FX::mode_chunchun(void)
|
||||
return FRAMETIME;
|
||||
}
|
||||
|
||||
|
||||
//13 bytes
|
||||
typedef struct Spotlight {
|
||||
float speed;
|
||||
uint8_t colorIdx;
|
||||
@ -3661,6 +3620,11 @@ typedef struct Spotlight {
|
||||
#define SPOT_TYPE_3X_DOT 4
|
||||
#define SPOT_TYPE_4X_DOT 5
|
||||
#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.
|
||||
@ -3671,7 +3635,7 @@ typedef struct Spotlight {
|
||||
*/
|
||||
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;
|
||||
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
|
||||
*/
|
||||
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
|
||||
uint32_t* pixels = reinterpret_cast<uint32_t*>(SEGENV.data);
|
||||
uint8_t blendSpeed = map(SEGMENT.intensity, 0, UINT8_MAX, 10, 128);
|
||||
@ -3825,6 +3789,11 @@ uint16_t WS2812FX::mode_blends(void) {
|
||||
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 {
|
||||
uint32_t totalTime = 0;
|
||||
uint32_t fadeTime = 0;
|
||||
@ -3845,11 +3814,6 @@ typedef struct TvSim {
|
||||
uint16_t pb = 0;
|
||||
} 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 nr, ng, nb, r, g, b, i, hue;
|
||||
uint8_t sat, bri, j;
|
||||
@ -3957,10 +3921,15 @@ uint16_t WS2812FX::mode_tv_simulator(void) {
|
||||
*/
|
||||
|
||||
//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_WIDTH_FACTOR 6 //Higher number, smaller waves
|
||||
|
||||
//24 bytes
|
||||
class AuroraWave {
|
||||
private:
|
||||
uint16_t ttl;
|
||||
@ -4055,10 +4024,10 @@ uint16_t WS2812FX::mode_aurora(void) {
|
||||
|
||||
if(SEGENV.aux0 != SEGMENT.intensity || SEGENV.call == 0) {
|
||||
//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;
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
|
71
wled00/FX.h
71
wled00/FX.h
@ -24,8 +24,6 @@
|
||||
Modified for WLED
|
||||
*/
|
||||
|
||||
#include "wled.h"
|
||||
|
||||
#ifndef 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
|
||||
insufficient memory, decreasing MAX_NUM_SEGMENTS may help */
|
||||
#ifdef ESP8266
|
||||
#define MAX_NUM_SEGMENTS 12
|
||||
#define MAX_NUM_SEGMENTS 16
|
||||
/* How many color transitions can run at once */
|
||||
#define MAX_NUM_TRANSITIONS 8
|
||||
/* How much data bytes all segments combined may allocate */
|
||||
#define MAX_SEGMENT_DATA 2048
|
||||
#define MAX_SEGMENT_DATA 4096
|
||||
#else
|
||||
#ifndef MAX_NUM_SEGMENTS
|
||||
#define MAX_NUM_SEGMENTS 16
|
||||
#endif
|
||||
#define MAX_NUM_TRANSITIONS 16
|
||||
#define MAX_SEGMENT_DATA 8192
|
||||
#ifndef MAX_NUM_SEGMENTS
|
||||
#define MAX_NUM_SEGMENTS 32
|
||||
#endif
|
||||
#define MAX_NUM_TRANSITIONS 24
|
||||
#define MAX_SEGMENT_DATA 20480
|
||||
#endif
|
||||
|
||||
#define LED_SKIP_AMOUNT 1
|
||||
@ -245,7 +243,7 @@ class WS2812FX {
|
||||
|
||||
// segment parameters
|
||||
public:
|
||||
typedef struct Segment { // 25 (28 in memory?) bytes
|
||||
typedef struct Segment { // 29 (32 in memory?) bytes
|
||||
uint16_t start;
|
||||
uint16_t stop; //segment invalid if stop == 0
|
||||
uint16_t offset;
|
||||
@ -257,6 +255,7 @@ class WS2812FX {
|
||||
uint8_t grouping, spacing;
|
||||
uint8_t opacity;
|
||||
uint32_t colors[NUM_COLORS];
|
||||
char *name;
|
||||
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 (c == colors[slot]) return false;
|
||||
@ -296,19 +295,19 @@ class WS2812FX {
|
||||
{
|
||||
return ((options >> n) & 0x01);
|
||||
}
|
||||
bool isSelected()
|
||||
inline bool isSelected()
|
||||
{
|
||||
return getOption(0);
|
||||
}
|
||||
bool isActive()
|
||||
inline bool isActive()
|
||||
{
|
||||
return stop > start;
|
||||
}
|
||||
uint16_t length()
|
||||
inline uint16_t length()
|
||||
{
|
||||
return stop - start;
|
||||
}
|
||||
uint16_t groupLength()
|
||||
inline uint16_t groupLength()
|
||||
{
|
||||
return grouping + spacing;
|
||||
}
|
||||
@ -345,17 +344,23 @@ class WS2812FX {
|
||||
|
||||
// segment runtime parameters
|
||||
typedef struct Segment_runtime { // 28 bytes
|
||||
unsigned long next_time;
|
||||
uint32_t step;
|
||||
uint32_t call;
|
||||
uint16_t aux0;
|
||||
uint16_t aux1;
|
||||
unsigned long next_time; // millis() of next update
|
||||
uint32_t step; // custom "step" var
|
||||
uint32_t call; // call counter
|
||||
uint16_t aux0; // custom var
|
||||
uint16_t aux1; // custom var
|
||||
byte* data = nullptr;
|
||||
bool allocateData(uint16_t len){
|
||||
if (data && _dataLen == len) return true; //already allocated
|
||||
deallocateData();
|
||||
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
|
||||
WS2812FX::instance->_usedSegmentData += len;
|
||||
_dataLen = len;
|
||||
@ -363,7 +368,7 @@ class WS2812FX {
|
||||
return true;
|
||||
}
|
||||
void deallocateData(){
|
||||
delete[] data;
|
||||
free(data);
|
||||
data = nullptr;
|
||||
WS2812FX::instance->_usedSegmentData -= _dataLen;
|
||||
_dataLen = 0;
|
||||
@ -389,7 +394,7 @@ class WS2812FX {
|
||||
* the internal segment state should be reset.
|
||||
* Call resetIfRequired before calling the next effect function.
|
||||
*/
|
||||
void reset() { _requiresReset = true; }
|
||||
inline void reset() { _requiresReset = true; }
|
||||
private:
|
||||
uint16_t _dataLen = 0;
|
||||
bool _requiresReset = false;
|
||||
@ -622,11 +627,12 @@ class WS2812FX {
|
||||
trigger(void),
|
||||
setSegment(uint8_t n, uint16_t start, uint16_t stop, uint8_t grouping = 0, uint8_t spacing = 0),
|
||||
resetSegments(),
|
||||
populateDefaultSegments(),
|
||||
setPixelColor(uint16_t n, uint32_t c),
|
||||
setPixelColor(uint16_t n, uint8_t r, uint8_t g, uint8_t b, uint8_t w = 0),
|
||||
show(void),
|
||||
setColorOrder(uint8_t co),
|
||||
setPixelSegment(uint8_t n);
|
||||
setPixelSegment(uint8_t n),
|
||||
deserializeMap(uint8_t n=0);
|
||||
|
||||
bool
|
||||
isRgbw = false,
|
||||
@ -644,8 +650,6 @@ class WS2812FX {
|
||||
paletteFade = 0,
|
||||
paletteBlend = 0,
|
||||
milliampsPerLed = 55,
|
||||
// getStripType(uint8_t strip=0),
|
||||
// setStripType(uint8_t type, uint8_t strip=0),
|
||||
getBrightness(void),
|
||||
getMode(void),
|
||||
getSpeed(void),
|
||||
@ -654,24 +658,17 @@ class WS2812FX {
|
||||
getMaxSegments(void),
|
||||
//getFirstSelectedSegment(void),
|
||||
getMainSegmentId(void),
|
||||
getColorOrder(void),
|
||||
gamma8(uint8_t),
|
||||
gamma8_cal(uint8_t, float),
|
||||
sin_gap(uint16_t),
|
||||
get_random_wheel_index(uint8_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);
|
||||
|
||||
uint16_t
|
||||
ablMilliampsMax,
|
||||
currentMilliamps,
|
||||
// setStripLen(uint8_t strip, uint16_t len),
|
||||
// getStripLen(uint8_t strip=0),
|
||||
triwave16(uint16_t),
|
||||
getFps();
|
||||
|
||||
@ -849,7 +846,6 @@ class WS2812FX {
|
||||
color_wipe(bool, bool),
|
||||
dynamic(bool),
|
||||
scan(bool),
|
||||
theater_chase(uint32_t, uint32_t, bool),
|
||||
running_base(bool,bool),
|
||||
larson_scanner(bool),
|
||||
sinelon_base(bool,bool),
|
||||
@ -857,8 +853,8 @@ class WS2812FX {
|
||||
chase(uint32_t, uint32_t, uint32_t, bool),
|
||||
gradient_base(bool),
|
||||
ripple_base(bool),
|
||||
police_base(uint32_t, uint32_t, bool),
|
||||
running(uint32_t, uint32_t),
|
||||
police_base(uint32_t, uint32_t, uint16_t),
|
||||
running(uint32_t, uint32_t, bool theatre=false),
|
||||
tricolor_chase(uint32_t, uint32_t),
|
||||
twinklefox_base(bool),
|
||||
spots_base(uint16_t),
|
||||
@ -869,8 +865,7 @@ class WS2812FX {
|
||||
|
||||
void
|
||||
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),
|
||||
deserializeMap(void);
|
||||
startTransition(uint8_t oldBri, uint32_t oldCol, uint16_t dur, uint8_t segn, uint8_t slot);
|
||||
|
||||
uint16_t* customMappingTable = nullptr;
|
||||
uint16_t customMappingSize = 0;
|
||||
|
@ -23,7 +23,7 @@
|
||||
|
||||
Modified heavily for WLED
|
||||
*/
|
||||
|
||||
#include "wled.h"
|
||||
#include "FX.h"
|
||||
#include "palettes.h"
|
||||
|
||||
@ -40,7 +40,7 @@
|
||||
another example. Switches direction every 5 LEDs.
|
||||
{"map":[
|
||||
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
|
||||
@ -218,14 +218,13 @@ uint16_t WS2812FX::realPixelIndex(uint16_t i) {
|
||||
int16_t realIndex = iGroup;
|
||||
if (IS_REVERSE) {
|
||||
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 {
|
||||
realIndex = SEGMENT.length() - iGroup - 1;
|
||||
realIndex = (SEGMENT.length() - 1) - iGroup;
|
||||
}
|
||||
}
|
||||
|
||||
realIndex += SEGMENT.start;
|
||||
|
||||
return realIndex;
|
||||
}
|
||||
|
||||
@ -246,7 +245,6 @@ void WS2812FX::setPixelColor(uint16_t i, byte r, byte g, byte b, byte w)
|
||||
}
|
||||
|
||||
if (SEGLEN) {//from segment
|
||||
|
||||
//color_blend(getpixel, col, _bri_t); (pseudocode for future blending of segments)
|
||||
if (_bri_t < 255) {
|
||||
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));
|
||||
|
||||
bool reversed = IS_REVERSE;
|
||||
/* Set all the pixels in the group */
|
||||
uint16_t realIndex = realPixelIndex(i);
|
||||
uint16_t len = SEGMENT.length();
|
||||
|
||||
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 (IS_MIRROR) { //set the corresponding mirrored pixel
|
||||
uint16_t indexMir = SEGMENT.stop - indexSet + SEGMENT.start - 1;
|
||||
@ -273,8 +271,8 @@ void WS2812FX::setPixelColor(uint16_t i, byte r, byte g, byte b, byte w)
|
||||
busses.setPixelColor(indexMir, col);
|
||||
}
|
||||
/* offset/phase */
|
||||
indexSet += SEGMENT.offset;
|
||||
if (indexSet >= SEGMENT.stop) indexSet -= len;
|
||||
indexSet += SEGMENT.offset;
|
||||
if (indexSet >= SEGMENT.stop) indexSet -= len;
|
||||
|
||||
if (indexSet < customMappingSize) indexSet = customMappingTable[indexSet];
|
||||
busses.setPixelColor(indexSet, col);
|
||||
@ -282,7 +280,6 @@ void WS2812FX::setPixelColor(uint16_t i, byte r, byte g, byte b, byte w)
|
||||
}
|
||||
} else { //live data, etc.
|
||||
if (i < customMappingSize) i = customMappingTable[i];
|
||||
|
||||
uint32_t col = ((w << 24) | (r << 16) | (g << 8) | (b));
|
||||
busses.setPixelColor(i, col);
|
||||
}
|
||||
@ -563,6 +560,7 @@ uint32_t WS2812FX::getPixelColor(uint16_t i)
|
||||
}
|
||||
|
||||
if (i < customMappingSize) i = customMappingTable[i];
|
||||
if (i >= _length) return 0;
|
||||
|
||||
return busses.getPixelColor(i);
|
||||
}
|
||||
@ -584,15 +582,6 @@ uint32_t WS2812FX::getLastShow(void) {
|
||||
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) {
|
||||
if (n >= MAX_NUM_SEGMENTS) return;
|
||||
Segment& seg = _segments[n];
|
||||
@ -603,7 +592,11 @@ void WS2812FX::setSegment(uint8_t n, uint16_t i1, uint16_t i2, uint8_t grouping,
|
||||
if (seg.stop) setRange(seg.start, seg.stop -1, 0); //turn old segment range off
|
||||
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
|
||||
{
|
||||
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() {
|
||||
for (uint8_t i = 0; i < MAX_NUM_SEGMENTS; i++) if (_segments[i].name) delete _segments[i].name;
|
||||
mainSegment = 0;
|
||||
memset(_segments, 0, sizeof(_segments));
|
||||
//memset(_segment_runtimes, 0, sizeof(_segment_runtimes));
|
||||
@ -656,6 +650,25 @@ void WS2812FX::resetSegments() {
|
||||
_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)
|
||||
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
|
||||
void WS2812FX::deserializeMap(void) {
|
||||
if (!WLED_FS.exists("/ledmap.json")) return;
|
||||
void WS2812FX::deserializeMap(uint8_t n) {
|
||||
char fileName[32];
|
||||
strcpy_P(fileName, PSTR("/ledmap"));
|
||||
if (n) sprintf(fileName +7, "%d", n);
|
||||
strcat(fileName, ".json");
|
||||
bool isFile = WLED_FS.exists(fileName);
|
||||
|
||||
if (!isFile) {
|
||||
// erase custom mapping if selecting nonexistent ledmap.json (n==0)
|
||||
if (!n && customMappingTable != nullptr) {
|
||||
customMappingSize = 0;
|
||||
delete[] customMappingTable;
|
||||
customMappingTable = nullptr;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
DynamicJsonDocument doc(JSON_BUFFER_SIZE); // full sized buffer for larger maps
|
||||
DEBUG_PRINT(F("Reading LED map from "));
|
||||
DEBUG_PRINTLN(fileName);
|
||||
|
||||
DEBUG_PRINTLN(F("Reading LED map from /ledmap.json..."));
|
||||
|
||||
if (!readObjectFromFile("/ledmap.json", nullptr, &doc)) return; //if file does not exist just exit
|
||||
if (!readObjectFromFile(fileName, nullptr, &doc)) return; //if file does not exist just exit
|
||||
|
||||
// erase old custom ledmap
|
||||
if (customMappingTable != nullptr) {
|
||||
customMappingSize = 0;
|
||||
delete[] customMappingTable;
|
||||
customMappingTable = nullptr;
|
||||
customMappingSize = 0;
|
||||
}
|
||||
|
||||
JsonArray map = doc[F("map")];
|
||||
|
@ -106,7 +106,7 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
|
||||
uint16_t length = elm[F("len")] | 1;
|
||||
uint8_t colorOrder = (int)elm[F("order")];
|
||||
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;
|
||||
bool reversed = elm["rev"];
|
||||
|
||||
@ -398,7 +398,7 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
|
||||
JsonObject dmx = doc["dmx"];
|
||||
CJSON(DMXChannels, dmx[F("chan")]);
|
||||
CJSON(DMXGap,dmx[F("gap")]);
|
||||
CJSON(DMXStart, dmx[F("start")]);
|
||||
CJSON(DMXStart, dmx["start"]);
|
||||
CJSON(DMXStartLED,dmx[F("start-led")]);
|
||||
|
||||
JsonArray dmx_fixmap = dmx[F("fixmap")];
|
||||
@ -516,7 +516,7 @@ void serializeConfig() {
|
||||
Bus *bus = busses.getBus(s);
|
||||
if (!bus || bus->getLength()==0) break;
|
||||
JsonObject ins = hw_led_ins.createNestedObject();
|
||||
ins[F("start")] = bus->getStart();
|
||||
ins["start"] = bus->getStart();
|
||||
ins[F("len")] = bus->getLength();
|
||||
JsonArray ins_pin = ins.createNestedArray("pin");
|
||||
uint8_t pins[5];
|
||||
@ -719,7 +719,7 @@ void serializeConfig() {
|
||||
JsonObject dmx = doc.createNestedObject("dmx");
|
||||
dmx[F("chan")] = DMXChannels;
|
||||
dmx[F("gap")] = DMXGap;
|
||||
dmx[F("start")] = DMXStart;
|
||||
dmx["start"] = DMXStart;
|
||||
dmx[F("start-led")] = DMXStartLED;
|
||||
|
||||
JsonArray dmx_fixmap = dmx.createNestedArray(F("fixmap"));
|
||||
|
File diff suppressed because one or more lines are too long
@ -578,11 +578,13 @@ function populateSegments(s)
|
||||
<input type="checkbox" id="seg${i}sel" onchange="selSeg(${i})" ${inst.sel ? "checked":""}>
|
||||
<span class="checkmark schk"></span>
|
||||
</label>
|
||||
<div class="segname" onclick="selSegEx(${i})">
|
||||
Segment ${i}
|
||||
<div class="segname">
|
||||
<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})"></i>
|
||||
</div>
|
||||
<i class="icons e-icon flr ${expanded[i] ? "exp":""}" id="sege${i}" onclick="expand(${i})"></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">
|
||||
<i class="icons e-icon pwr ${powered[i] ? "act":""}" id="seg${i}pwr" onclick="setSegPwr(${i})"></i>
|
||||
<div class="sliderwrap il sws">
|
||||
@ -649,7 +651,7 @@ function populateSegments(s)
|
||||
function populateEffects(effects)
|
||||
{
|
||||
var html = `<div class="searchbar"><input type="text" class="search" placeholder="Search" oninput="search(this)" />
|
||||
<i class="icons search-cancel-icon" onclick="cancelSearch(this)"></i></div>`;
|
||||
<i class="icons search-icon"></i><i class="icons search-cancel-icon" onclick="cancelSearch(this)"></i></div>`;
|
||||
|
||||
effects.shift(); //remove solid
|
||||
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)" />
|
||||
<i class="icons search-cancel-icon" onclick="cancelSearch(this)"></i></div>`;
|
||||
<i class="icons search-icon"></i><i class="icons search-cancel-icon" onclick="cancelSearch(this)"></i></div>`;
|
||||
for (let i = 0; i < palettes.length; i++) {
|
||||
html += generateListItemHtml(
|
||||
'palette',
|
||||
@ -1258,9 +1260,11 @@ function makeSeg() {
|
||||
var cn = `<div class="seg">
|
||||
<div class="segname newseg">
|
||||
New segment ${lowestUnused}
|
||||
<i class="icons edit-icon" style="display: inline;" onclick="tglSegn(${lowestUnused})"></i>
|
||||
</div>
|
||||
<br>
|
||||
<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">
|
||||
<tr>
|
||||
<td class="segtd">Start LED</td>
|
||||
@ -1405,7 +1409,7 @@ function makeP(i,pl) {
|
||||
|
||||
return `
|
||||
<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 ${pl&&i==0?"style='display:none'":""}>
|
||||
<label class="check revchkl">
|
||||
@ -1482,6 +1486,12 @@ function tglCs(i){
|
||||
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)
|
||||
{
|
||||
var obj = {"seg":[]};
|
||||
@ -1498,10 +1508,11 @@ function selSeg(s){
|
||||
}
|
||||
|
||||
function setSeg(s){
|
||||
var name = d.getElementById(`seg${s}t`).value;
|
||||
var start = parseInt(d.getElementById(`seg${s}s`).value);
|
||||
var stop = parseInt(d.getElementById(`seg${s}e`).value);
|
||||
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`))
|
||||
{
|
||||
var grp = parseInt(d.getElementById(`seg${s}grp`).value);
|
||||
@ -1955,7 +1966,10 @@ function expand(i,a)
|
||||
if (!a) expanded[i] = !expanded[i];
|
||||
d.getElementById('seg' +i).style.display = (expanded[i]) ? "block":"none";
|
||||
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;
|
||||
d.getElementById(`p${p}o`).style.background = (expanded[i] || p != currentPreset)?"var(--c-2)":"var(--c-6)";
|
||||
|
@ -208,7 +208,7 @@
|
||||
</head>
|
||||
<body onload="S()">
|
||||
<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>
|
||||
<button type="button" onclick="B()">Back</button><button type="button" onclick="Save()">Save</button><br>
|
||||
<span id="lssuc" style="color:green; display:none">✔ Local UI settings saved!</span>
|
||||
|
@ -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="⚠ 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="⚠ 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
|
||||
id="form_s" name="Sf" method="post"><div
|
||||
style="position:sticky;top:0;background-color:#222"><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><span
|
||||
id="lssuc" style="color:green;display:none">✔ Local UI settings saved!
|
||||
</span> <span id="lserr" style="color:red;display:none">
|
||||
style="position:sticky;top:0;background-color:#222;z-index:1"><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><span id="lssuc" style="color:green;display:none">
|
||||
✔ Local UI settings saved!</span> <span id="lserr"
|
||||
style="color:red;display:none">
|
||||
⚠ 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"
|
||||
maxlength="32"><br>Sync button toggles both send and receive: <input
|
||||
|
4357
wled00/html_ui.h
4357
wled00/html_ui.h
File diff suppressed because it is too large
Load Diff
@ -15,13 +15,38 @@ void deserializeSegment(JsonObject elem, byte it, byte presetId)
|
||||
//WS2812FX::Segment prev;
|
||||
//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;
|
||||
|
||||
if (stop < 0) {
|
||||
uint16_t len = elem[F("len")];
|
||||
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 spc = elem[F("spc")] | seg.spacing;
|
||||
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;
|
||||
if (segmentBounds) {
|
||||
root[F("start")] = seg.start;
|
||||
root["start"] = seg.start;
|
||||
root["stop"] = seg.stop;
|
||||
}
|
||||
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;
|
||||
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)
|
||||
|
||||
for (uint8_t i = 0; i < 3; i++)
|
||||
@ -655,37 +682,37 @@ void serializePalettes(JsonObject root, AsyncWebServerRequest* request)
|
||||
curPalette.add("r");
|
||||
break;
|
||||
case 2: //primary color only
|
||||
curPalette.add(F("c1"));
|
||||
curPalette.add("c1");
|
||||
break;
|
||||
case 3: //primary + secondary
|
||||
curPalette.add(F("c1"));
|
||||
curPalette.add(F("c1"));
|
||||
curPalette.add(F("c2"));
|
||||
curPalette.add(F("c2"));
|
||||
curPalette.add("c1");
|
||||
curPalette.add("c1");
|
||||
curPalette.add("c2");
|
||||
curPalette.add("c2");
|
||||
break;
|
||||
case 4: //primary + secondary + tertiary
|
||||
curPalette.add(F("c3"));
|
||||
curPalette.add(F("c2"));
|
||||
curPalette.add(F("c1"));
|
||||
curPalette.add("c3");
|
||||
curPalette.add("c2");
|
||||
curPalette.add("c1");
|
||||
break;
|
||||
case 5: {//primary + secondary (+tert if not off), more distinct
|
||||
|
||||
curPalette.add(F("c1"));
|
||||
curPalette.add(F("c1"));
|
||||
curPalette.add(F("c1"));
|
||||
curPalette.add(F("c1"));
|
||||
curPalette.add(F("c1"));
|
||||
curPalette.add(F("c2"));
|
||||
curPalette.add(F("c2"));
|
||||
curPalette.add(F("c2"));
|
||||
curPalette.add(F("c2"));
|
||||
curPalette.add(F("c2"));
|
||||
curPalette.add(F("c3"));
|
||||
curPalette.add(F("c3"));
|
||||
curPalette.add(F("c3"));
|
||||
curPalette.add(F("c3"));
|
||||
curPalette.add(F("c3"));
|
||||
curPalette.add(F("c1"));
|
||||
curPalette.add("c1");
|
||||
curPalette.add("c1");
|
||||
curPalette.add("c1");
|
||||
curPalette.add("c1");
|
||||
curPalette.add("c1");
|
||||
curPalette.add("c2");
|
||||
curPalette.add("c2");
|
||||
curPalette.add("c2");
|
||||
curPalette.add("c2");
|
||||
curPalette.add("c2");
|
||||
curPalette.add("c3");
|
||||
curPalette.add("c3");
|
||||
curPalette.add("c3");
|
||||
curPalette.add("c3");
|
||||
curPalette.add("c3");
|
||||
curPalette.add("c1");
|
||||
break;}
|
||||
case 6: //Party colors
|
||||
setPaletteColors(curPalette, PartyColors_p);
|
||||
|
Loading…
Reference in New Issue
Block a user