Merge pull request #2 from Aircoookie/master

Update repository
This commit is contained in:
srg74 2020-01-06 21:01:36 -05:00 committed by GitHub
commit 19f6fd2295
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 491 additions and 234 deletions

View File

@ -236,22 +236,24 @@ uint16_t WS2812FX::mode_random_color(void) {
// * to new random colors. // * to new random colors.
*/ */
uint16_t WS2812FX::mode_dynamic(void) { uint16_t WS2812FX::mode_dynamic(void) {
if (!SEGENV.allocateData(SEGLEN)) return mode_static(); //allocation failed
if(SEGENV.call == 0) { if(SEGENV.call == 0) {
for(uint16_t i=SEGMENT.start; i < SEGMENT.stop; i++) _locked[i] = random8(); for (uint16_t i = 0; i < SEGLEN; i++) SEGENV.data[i] = random8();
} }
uint32_t cycleTime = 50 + (255 - SEGMENT.speed)*15; uint32_t cycleTime = 50 + (255 - SEGMENT.speed)*15;
uint32_t it = now / cycleTime; uint32_t it = now / cycleTime;
if (it != SEGENV.step && SEGMENT.speed != 0) //new color if (it != SEGENV.step && SEGMENT.speed != 0) //new color
{ {
for(uint16_t i=SEGMENT.start; i < SEGMENT.stop; i++) { for (uint16_t i = 0; i < SEGLEN; i++) {
if (random8() <= SEGMENT.intensity) _locked[i] = random8(); if (random8() <= SEGMENT.intensity) SEGENV.data[i] = random8();
} }
SEGENV.step = it; SEGENV.step = it;
} }
for(uint16_t i=SEGMENT.start; i < SEGMENT.stop; i++) { for (uint16_t i = 0; i < SEGLEN; i++) {
setPixelColor(i, color_wheel(_locked[i])); setPixelColor(SEGMENT.start + i, color_wheel(SEGENV.data[i]));
} }
return FRAMETIME; return FRAMETIME;
} }
@ -1467,17 +1469,24 @@ typedef struct Oscillator {
*/ */
uint16_t WS2812FX::mode_oscillate(void) uint16_t WS2812FX::mode_oscillate(void)
{ {
static oscillator oscillators[NUM_COLORS] = { uint8_t numOscillators = 3;
{SEGLEN/4, SEGLEN/8, 1, 1}, uint16_t dataSize = sizeof(oscillator) * numOscillators;
{SEGLEN/4*3, SEGLEN/8, 1, 2},
{SEGLEN/4*2, SEGLEN/8, -1, 1} if (!SEGENV.allocateData(dataSize)) return mode_static(); //allocation failed
}; Oscillator* oscillators = reinterpret_cast<Oscillator*>(SEGENV.data);
if (SEGENV.call == 0)
{
oscillators[0] = {SEGLEN/4, SEGLEN/8, 1, 1};
oscillators[1] = {SEGLEN/4*3, SEGLEN/8, 1, 2};
oscillators[2] = {SEGLEN/4*2, SEGLEN/8, -1, 1};
}
uint32_t cycleTime = 20 + (2 * (uint32_t)(255 - SEGMENT.speed)); uint32_t cycleTime = 20 + (2 * (uint32_t)(255 - SEGMENT.speed));
uint32_t it = now / cycleTime; uint32_t it = now / cycleTime;
for(int8_t i=0; i < sizeof(oscillators)/sizeof(oscillators[0]); i++) { for(uint8_t i=0; i < numOscillators; i++) {
// if the counter has increased, move the oscillator by the random step // if the counter has increased, move the oscillator by the random step
if (it != SEGENV.step) oscillators[i].pos += oscillators[i].dir * oscillators[i].speed; if (it != SEGENV.step) oscillators[i].pos += oscillators[i].dir * oscillators[i].speed;
oscillators[i].size = SEGLEN/(3+SEGMENT.intensity/8); oscillators[i].size = SEGLEN/(3+SEGMENT.intensity/8);
@ -1494,9 +1503,9 @@ uint16_t WS2812FX::mode_oscillate(void)
} }
} }
for(int16_t i=0; i < SEGLEN; i++) { for(uint16_t i=0; i < SEGLEN; i++) {
uint32_t color = BLACK; uint32_t color = BLACK;
for(int8_t j=0; j < sizeof(oscillators)/sizeof(oscillators[0]); j++) { for(uint8_t j=0; j < numOscillators; j++) {
if(i >= oscillators[j].pos - oscillators[j].size && i <= oscillators[j].pos + oscillators[j].size) { if(i >= oscillators[j].pos - oscillators[j].size && i <= oscillators[j].pos + oscillators[j].size) {
color = (color == BLACK) ? SEGMENT.colors[j] : color_blend(color, SEGMENT.colors[j], 128); color = (color == BLACK) ? SEGMENT.colors[j] : color_blend(color, SEGMENT.colors[j], 128);
} }
@ -1666,30 +1675,34 @@ uint16_t WS2812FX::mode_fire_2012()
{ {
uint32_t it = now >> 5; //div 32 uint32_t it = now >> 5; //div 32
if (!SEGENV.allocateData(SEGLEN)) return mode_static(); //allocation failed
byte* heat = SEGENV.data;
if (it != SEGENV.step) if (it != SEGENV.step)
{ {
// Step 1. Cool down every cell a little // Step 1. Cool down every cell a little
for( int i = SEGMENT.start; i < SEGMENT.stop; i++) { for (uint16_t i = 0; i < SEGLEN; i++) {
_locked[i] = qsub8(_locked[i], random8(0, (((20 + SEGMENT.speed /3) * 10) / SEGLEN) + 2)); SEGENV.data[i] = qsub8(heat[i], random8(0, (((20 + SEGMENT.speed /3) * 10) / SEGLEN) + 2));
} }
// Step 2. Heat from each cell drifts 'up' and diffuses a little // Step 2. Heat from each cell drifts 'up' and diffuses a little
for( int k= SEGMENT.stop -1; k >= SEGMENT.start + 2; k--) { for (uint16_t k= SEGLEN -1; k > 1; k--) {
_locked[k] = (_locked[k - 1] + _locked[k - 2] + _locked[k - 2] ) / 3; heat[k] = (heat[k - 1] + heat[k - 2] + heat[k - 2] ) / 3;
} }
// Step 3. Randomly ignite new 'sparks' of heat near the bottom // Step 3. Randomly ignite new 'sparks' of heat near the bottom
if( random8() <= SEGMENT.intensity ) { if (random8() <= SEGMENT.intensity) {
int y = SEGMENT.start + random8(7); uint8_t y = random8(7);
if (y < SEGMENT.stop) _locked[y] = qadd8(_locked[y], random8(160,255) ); if (y < SEGLEN) heat[y] = qadd8(heat[y], random8(160,255));
} }
SEGENV.step = it; SEGENV.step = it;
} }
// Step 4. Map from heat cells to LED colors // Step 4. Map from heat cells to LED colors
for( int j = SEGMENT.start; j < SEGMENT.stop; j++) { for (uint16_t j = 0; j < SEGLEN; j++) {
CRGB color = ColorFromPalette( currentPalette, min(_locked[j],240), 255, LINEARBLEND); CRGB color = ColorFromPalette(currentPalette, min(heat[j],240), 255, LINEARBLEND);
setPixelColor(j, color.red, color.green, color.blue); setPixelColor(SEGMENT.start + j, color.red, color.green, color.blue);
} }
return FRAMETIME; return FRAMETIME;
} }
@ -1870,18 +1883,25 @@ uint16_t WS2812FX::mode_noise16_4()
//based on https://gist.github.com/kriegsman/5408ecd397744ba0393e //based on https://gist.github.com/kriegsman/5408ecd397744ba0393e
uint16_t WS2812FX::mode_colortwinkle() uint16_t WS2812FX::mode_colortwinkle()
{ {
uint16_t dataSize = (SEGLEN+7) >> 3; //1 bit per LED
if (!SEGENV.allocateData(dataSize)) return mode_static(); //allocation failed
CRGB fastled_col, prev; CRGB fastled_col, prev;
fract8 fadeUpAmount = 8 + (SEGMENT.speed/4), fadeDownAmount = 5 + (SEGMENT.speed/7); fract8 fadeUpAmount = 8 + (SEGMENT.speed/4), fadeDownAmount = 5 + (SEGMENT.speed/7);
for( uint16_t i = SEGMENT.start; i < SEGMENT.stop; i++) { for (uint16_t i = SEGMENT.start; i < SEGMENT.stop; i++) {
fastled_col = col_to_crgb(getPixelColor(i)); fastled_col = col_to_crgb(getPixelColor(i));
prev = fastled_col; prev = fastled_col;
if(_locked[i]) { uint16_t index = (i - SEGMENT.start) >> 3;
uint8_t bitNum = (i - SEGMENT.start) & 0x07;
bool fadeUp = bitRead(SEGENV.data[index], bitNum);
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) {
_locked[i] = false; bitWrite(SEGENV.data[index], bitNum, false);
} }
setPixelColor(i, fastled_col.red, fastled_col.green, fastled_col.blue); setPixelColor(i, fastled_col.red, fastled_col.green, fastled_col.blue);
@ -1898,13 +1918,15 @@ uint16_t WS2812FX::mode_colortwinkle()
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 = SEGMENT.start + random16(SEGLEN); int i = SEGMENT.start + random16(SEGLEN);
if(getPixelColor(i) == 0) { if(getPixelColor(i) == 0) {
fastled_col = ColorFromPalette(currentPalette, random8(), 64, NOBLEND); fastled_col = ColorFromPalette(currentPalette, random8(), 64, NOBLEND);
_locked[i] = true; uint16_t index = (i - SEGMENT.start) >> 3;
uint8_t bitNum = (i - SEGMENT.start) & 0x07;
bitWrite(SEGENV.data[index], bitNum, true);
setPixelColor(i, fastled_col.red, fastled_col.green, fastled_col.blue); setPixelColor(i, fastled_col.red, fastled_col.green, fastled_col.blue);
break; //only spawn 1 new pixel per frame per 50 LEDs break; //only spawn 1 new pixel per frame per 50 LEDs
} }
@ -1938,29 +1960,33 @@ uint16_t WS2812FX::mode_lake() {
// send a meteor from begining to to the end of the strip with a trail that randomly decays. // send a meteor from begining to to the end of the strip with a trail that randomly decays.
// adapted from https://www.tweaking4all.com/hardware/arduino/adruino-led-strip-effects/#LEDStripEffectMeteorRain // adapted from https://www.tweaking4all.com/hardware/arduino/adruino-led-strip-effects/#LEDStripEffectMeteorRain
uint16_t WS2812FX::mode_meteor() { uint16_t WS2812FX::mode_meteor() {
if (!SEGENV.allocateData(SEGLEN)) return mode_static(); //allocation failed
byte* trail = SEGENV.data;
byte meteorSize= 1+ SEGLEN / 10; byte meteorSize= 1+ SEGLEN / 10;
uint16_t counter = now * ((SEGMENT.speed >> 2) +8); uint16_t counter = now * ((SEGMENT.speed >> 2) +8);
uint16_t in = counter * SEGLEN >> 16; uint16_t in = counter * SEGLEN >> 16;
// fade all leds to colors[1] in LEDs one step // fade all leds to colors[1] in LEDs one step
for (uint16_t i = SEGMENT.start; i < SEGMENT.stop; i++) { for (uint16_t i = 0; i < SEGLEN; i++) {
if (random8() <= 255 - SEGMENT.intensity) if (random8() <= 255 - SEGMENT.intensity)
{ {
byte meteorTrailDecay = 128 + random8(127); byte meteorTrailDecay = 128 + random8(127);
_locked[i] = scale8(_locked[i], meteorTrailDecay); trail[i] = scale8(trail[i], meteorTrailDecay);
setPixelColor(i, color_from_palette(_locked[i], false, true, 255)); setPixelColor(SEGMENT.start + i, color_from_palette(trail[i], false, true, 255));
} }
} }
// draw meteor // draw meteor
for(int j = 0; j < meteorSize; j++) { for(int j = 0; j < meteorSize; j++) {
uint16_t index = in + j; uint16_t index = in + j;
if(in + j >= SEGMENT.stop) { if(index >= SEGLEN) {
index = SEGMENT.start + (in + j - SEGMENT.stop); index = (in + j - SEGLEN);
} }
_locked[index] = 240; trail[index] = 240;
setPixelColor(index, color_from_palette(_locked[index], false, true, 255)); setPixelColor(SEGMENT.start + index, color_from_palette(trail[index], false, true, 255));
} }
return FRAMETIME; return FRAMETIME;
@ -1971,29 +1997,33 @@ uint16_t WS2812FX::mode_meteor() {
// send a meteor from begining to to the end of the strip with a trail that randomly decays. // send a meteor from begining to to the end of the strip with a trail that randomly decays.
// adapted from https://www.tweaking4all.com/hardware/arduino/adruino-led-strip-effects/#LEDStripEffectMeteorRain // adapted from https://www.tweaking4all.com/hardware/arduino/adruino-led-strip-effects/#LEDStripEffectMeteorRain
uint16_t WS2812FX::mode_meteor_smooth() { uint16_t WS2812FX::mode_meteor_smooth() {
if (!SEGENV.allocateData(SEGLEN)) return mode_static(); //allocation failed
byte* trail = SEGENV.data;
byte meteorSize= 1+ SEGLEN / 10; byte meteorSize= 1+ SEGLEN / 10;
uint16_t in = map((SEGENV.step >> 6 & 0xFF), 0, 255, SEGMENT.start, SEGMENT.stop -1); uint16_t in = map((SEGENV.step >> 6 & 0xFF), 0, 255, 0, SEGLEN -1);
// fade all leds to colors[1] in LEDs one step // fade all leds to colors[1] in LEDs one step
for (uint16_t i = SEGMENT.start; i < SEGMENT.stop; i++) { for (uint16_t i = 0; i < SEGLEN; i++) {
if (_locked[i] != 0 && random8() <= 255 - SEGMENT.intensity) if (trail[i] != 0 && random8() <= 255 - SEGMENT.intensity)
{ {
int change = 3 - random8(12); //change each time between -8 and +3 int change = 3 - random8(12); //change each time between -8 and +3
_locked[i] += change; trail[i] += change;
if (_locked[i] > 245) _locked[i] = 0; if (trail[i] > 245) trail[i] = 0;
if (_locked[i] > 240) _locked[i] = 240; if (trail[i] > 240) trail[i] = 240;
setPixelColor(i, color_from_palette(_locked[i], false, true, 255)); setPixelColor(SEGMENT.start + i, color_from_palette(trail[i], false, true, 255));
} }
} }
// draw meteor // draw meteor
for(int j = 0; j < meteorSize; j++) { for(int j = 0; j < meteorSize; j++) {
uint16_t index = in + j; uint16_t index = in + j;
if(in + j >= SEGMENT.stop) { if(in + j >= SEGLEN) {
index = SEGMENT.start + (in + j - SEGMENT.stop); index = (in + j - SEGLEN);
} }
setPixelColor(index, color_blend(getPixelColor(index), color_from_palette(240, false, true, 255), 48)); setPixelColor(SEGMENT.start + index, color_blend(getPixelColor(SEGMENT.start + index), color_from_palette(240, false, true, 255), 48));
_locked[index] = 240; trail[index] = 240;
} }
SEGENV.step += SEGMENT.speed +1; SEGENV.step += SEGMENT.speed +1;
@ -2035,23 +2065,35 @@ uint16_t WS2812FX::mode_railway()
//Water ripple //Water ripple
//propagation velocity from speed //propagation velocity from speed
//drop rate from intensity //drop rate from intensity
//4 bytes
typedef struct Ripple {
uint8_t state;
uint8_t color;
uint16_t pos;
} ripple;
uint16_t WS2812FX::mode_ripple() uint16_t WS2812FX::mode_ripple()
{ {
uint16_t maxripples = SEGLEN / 4; uint16_t maxRipples = 1 + (SEGLEN >> 2);
if (maxripples == 0) return mode_static(); if (maxRipples > 100) maxRipples = 100;
uint16_t dataSize = sizeof(ripple) * maxRipples;
if (!SEGENV.allocateData(dataSize)) return mode_static(); //allocation failed
Ripple* ripples = reinterpret_cast<Ripple*>(SEGENV.data);
fill(SEGCOLOR(1)); fill(SEGCOLOR(1));
//draw wave //draw wave
for (uint16_t rippleI = 0; rippleI < maxripples; rippleI++) for (uint16_t i = 0; i < maxRipples; i++)
{ {
uint16_t storeI = SEGMENT.start + 4*rippleI; uint16_t ripplestate = ripples[i].state;
uint16_t ripplestate = _locked[storeI];
if (ripplestate) if (ripplestate)
{ {
uint8_t rippledecay = (SEGMENT.speed >> 4) +1; //faster decay if faster propagation uint8_t rippledecay = (SEGMENT.speed >> 4) +1; //faster decay if faster propagation
uint16_t rippleorigin = (_locked[storeI+1] << 8) + _locked[storeI+2]; uint16_t rippleorigin = ripples[i].pos;
uint32_t col = color_from_palette(_locked[storeI+3], false, false, 255); uint32_t col = color_from_palette(ripples[i].color, false, false, 255);
uint16_t propagation = ((ripplestate/rippledecay -1) * SEGMENT.speed); uint16_t propagation = ((ripplestate/rippledecay -1) * SEGMENT.speed);
int16_t propI = propagation >> 8; int16_t propI = propagation >> 8;
uint8_t propF = propagation & 0xFF; uint8_t propF = propagation & 0xFF;
@ -2061,7 +2103,7 @@ uint16_t WS2812FX::mode_ripple()
for (int16_t v = left; v < left +4; v++) for (int16_t v = left; v < left +4; v++)
{ {
uint8_t mag = scale8(cubicwave8((propF>>2)+(v-left)*64), amp); uint8_t mag = scale8(cubicwave8((propF>>2)+(v-left)*64), amp);
if (v >= SEGMENT.start) if (v < SEGMENT.stop && v >= SEGMENT.start)
{ {
setPixelColor(v, color_blend(getPixelColor(v), col, mag)); setPixelColor(v, color_blend(getPixelColor(v), col, mag));
} }
@ -2072,16 +2114,14 @@ uint16_t WS2812FX::mode_ripple()
} }
} }
ripplestate += rippledecay; ripplestate += rippledecay;
_locked[storeI] = (ripplestate > 254) ? 0 : ripplestate; ripples[i].state = (ripplestate > 254) ? 0 : ripplestate;
} else //randomly create new wave } else //randomly create new wave
{ {
if (random16(IBN + 10000) <= SEGMENT.intensity) if (random16(IBN + 10000) <= SEGMENT.intensity)
{ {
_locked[storeI] = 1; ripples[i].state = 1;
uint16_t origin = SEGMENT.start + random16(SEGLEN); ripples[i].pos = SEGMENT.start + random16(SEGLEN);
_locked[storeI+1] = origin >> 8; ripples[i].color = random8(); //color
_locked[storeI+2] = origin & 0xFF;
_locked[storeI+3] = random8(); //color
} }
} }
} }
@ -2376,6 +2416,110 @@ 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;
float height;
} ball;
/*
* Bouncing Balls Effect
*/
uint16_t WS2812FX::mode_bouncing_balls(void) {
//allocate segment data
uint16_t maxNumBalls = 16;
uint16_t dataSize = sizeof(ball) * maxNumBalls;
if (!SEGENV.allocateData(dataSize)) return mode_static(); //allocation failed
Ball* balls = reinterpret_cast<Ball*>(SEGENV.data);
// number of balls based on intensity setting to max of 7 (cycles colors)
// non-chosen color is a random color
uint8_t numBalls = int(((SEGMENT.intensity * (maxNumBalls - 0.8f)) / 255) + 1);
float gravity = -9.81; // standard value of gravity
float impactVelocityStart = sqrt( -2 * gravity);
unsigned long time = millis();
if (SEGENV.call == 0) {
for (uint8_t i = 0; i < maxNumBalls; i++) balls[i].lastBounceTime = time;
}
bool hasCol2 = SEGCOLOR(2);
fill(hasCol2 ? BLACK : SEGCOLOR(1));
for (uint8_t i = 0; i < numBalls; i++) {
float timeSinceLastBounce = (time - balls[i].lastBounceTime)/((255-SEGMENT.speed)*8/256 +1);
balls[i].height = 0.5 * gravity * pow(timeSinceLastBounce/1000 , 2.0) + balls[i].impactVelocity * timeSinceLastBounce/1000;
if (balls[i].height < 0) { //start bounce
balls[i].height = 0;
//damping for better effect using multiple balls
float dampening = 0.90 - float(i)/pow(numBalls,2);
balls[i].impactVelocity = dampening * balls[i].impactVelocity;
balls[i].lastBounceTime = time;
if (balls[i].impactVelocity < 0.015) {
balls[i].impactVelocity = impactVelocityStart;
}
}
uint32_t color = SEGCOLOR(0);
if (SEGMENT.palette) {
color = color_wheel(i*(256/max(numBalls, 8)));
} else if (hasCol2) {
color = SEGCOLOR(i % NUM_COLORS);
}
uint16_t pos = round(balls[i].height * (SEGLEN - 1));
setPixelColor(SEGMENT.start + pos, color);
}
return FRAMETIME;
}
/*
* Sinelon stolen from FASTLED examples
*/
uint16_t WS2812FX::sinelon_base(bool dual, bool rainbow=false) {
fade_out(SEGMENT.intensity);
int pos = beatsin16(SEGMENT.speed/10,0,SEGLEN-1);
uint32_t color1 = color_from_palette(pos, true, false, 0);
if (rainbow) {
color1 = color_wheel((pos & 0x07) * 32);
}
setPixelColor(SEGMENT.start + pos, color1);
if (dual) {
uint32_t color2 = SEGCOLOR(2);
if (!color2) color2 = color_from_palette(pos, true, false, 0);
if (rainbow) color2 = color1; //rainbow
setPixelColor(SEGMENT.start + SEGLEN-1-pos, color2);
}
return FRAMETIME;
}
uint16_t WS2812FX::mode_sinelon(void) {
return sinelon_base(false);
}
uint16_t WS2812FX::mode_sinelon_dual(void) {
return sinelon_base(true);
}
uint16_t WS2812FX::mode_sinelon_rainbow(void) {
return sinelon_base(true, true);
}
//Rainbow with glitter, inspired by https://gist.github.com/kriegsman/062e10f7f07ba8518af6 //Rainbow with glitter, inspired by https://gist.github.com/kriegsman/062e10f7f07ba8518af6
uint16_t WS2812FX::mode_glitter() uint16_t WS2812FX::mode_glitter()
{ {
@ -2390,6 +2534,72 @@ uint16_t WS2812FX::mode_glitter()
} }
//each needs 12 bytes
//Spark type is used for popcorn and 1D fireworks
typedef struct Spark {
float pos;
float vel;
uint16_t col;
uint8_t colIndex;
} spark;
/*
* POPCORN
* modified from https://github.com/kitesurfer1404/WS2812FX/blob/master/src/custom/Popcorn.h
*/
uint16_t WS2812FX::mode_popcorn(void) {
//allocate segment data
uint16_t maxNumPopcorn = 24;
uint16_t dataSize = sizeof(spark) * maxNumPopcorn;
if (!SEGENV.allocateData(dataSize)) return mode_static(); //allocation failed
Spark* popcorn = reinterpret_cast<Spark*>(SEGENV.data);
float gravity = -0.0001 - (SEGMENT.speed/200000.0); // m/s/s
gravity *= SEGLEN;
bool hasCol2 = SEGCOLOR(2);
fill(hasCol2 ? BLACK : SEGCOLOR(1));
uint8_t numPopcorn = SEGMENT.intensity*maxNumPopcorn/255;
if (numPopcorn == 0) numPopcorn = 1;
for(uint8_t i = 0; i < numPopcorn; i++) {
bool isActive = popcorn[i].pos >= 0.0f;
if(isActive) { // if kernel is active, update its position
popcorn[i].pos += popcorn[i].vel;
popcorn[i].vel += gravity;
uint32_t col = color_wheel(popcorn[i].colIndex);
if (!SEGMENT.palette && popcorn[i].colIndex < NUM_COLORS) col = SEGCOLOR(popcorn[i].colIndex);
uint16_t ledIndex = SEGMENT.start + popcorn[i].pos;
if(ledIndex >= SEGMENT.start && ledIndex < SEGMENT.stop) setPixelColor(ledIndex, col);
} else { // if kernel is inactive, randomly pop it
if(random8() < 2) { // POP!!!
popcorn[i].pos = 0.01f;
uint16_t peakHeight = 128 + random8(128); //0-255
peakHeight = (peakHeight * (SEGLEN -1)) >> 8;
popcorn[i].vel = sqrt(-2.0 * gravity * peakHeight);
if (SEGMENT.palette)
{
popcorn[i].colIndex = random8();
} else {
byte col = random8(0, NUM_COLORS);
if (!hasCol2 || !SEGCOLOR(col)) col = 0;
popcorn[i].colIndex = col;
}
}
}
}
return FRAMETIME;
}
//values close to 100 produce 5Hz flicker, which looks very candle-y //values close to 100 produce 5Hz flicker, which looks very candle-y
//Inspired by https://github.com/avanhanegem/ArduinoCandleEffectNeoPixel //Inspired by https://github.com/avanhanegem/ArduinoCandleEffectNeoPixel
//and https://cpldcpu.wordpress.com/2016/01/05/reverse-engineering-a-real-candle/ //and https://cpldcpu.wordpress.com/2016/01/05/reverse-engineering-a-real-candle/
@ -2446,6 +2656,7 @@ uint16_t WS2812FX::mode_candle()
*/ */
#define STARBURST_MAX_FRAG 12 #define STARBURST_MAX_FRAG 12
//each needs 64 byte
typedef struct particle { typedef struct particle {
CRGB color; CRGB color;
uint32_t birth =0; uint32_t birth =0;
@ -2456,10 +2667,16 @@ typedef struct particle {
} star; } star;
uint16_t WS2812FX::mode_starburst(void) { uint16_t WS2812FX::mode_starburst(void) {
uint8_t numStars = 1 + (SEGLEN >> 3);
if (numStars > 15) numStars = 15;
uint16_t dataSize = sizeof(star) * numStars;
if (!SEGENV.allocateData(dataSize)) return mode_static(); //allocation failed
uint32_t it = millis(); uint32_t it = millis();
const uint8_t numStars = 15; star* stars = reinterpret_cast<star*>(SEGENV.data);
static star stars[numStars];
float maxSpeed = 375.0f; // Max velocity float maxSpeed = 375.0f; // Max velocity
float particleIgnition = 250.0f; // How long to "flash" float particleIgnition = 250.0f; // How long to "flash"
float particleFadeTime = 1500.0f; // Fade out time float particleFadeTime = 1500.0f; // Fade out time
@ -2552,3 +2769,117 @@ uint16_t WS2812FX::mode_starburst(void) {
} }
return FRAMETIME; return FRAMETIME;
} }
/*
* Exploding fireworks effect
* adapted from: http://www.anirama.com/1000leds/1d-fireworks/
*/
uint16_t WS2812FX::mode_exploding_fireworks(void)
{
//allocate segment data
uint16_t numSparks = 2 + (SEGLEN >> 1);
if (numSparks > 80) numSparks = 80;
uint16_t dataSize = sizeof(spark) * numSparks;
if (!SEGENV.allocateData(dataSize)) return mode_static(); //allocation failed
fill(BLACK);
bool actuallyReverse = SEGMENT.getOption(1);
//have fireworks start in either direction based on intensity
SEGMENT.setOption(1, SEGENV.step);
Spark* sparks = reinterpret_cast<Spark*>(SEGENV.data);
Spark* flare = sparks; //first spark is flare data
float gravity = -0.0004 - (SEGMENT.speed/800000.0); // m/s/s
gravity *= SEGLEN;
if (SEGENV.aux0 < 2) { //FLARE
if (SEGENV.aux0 == 0) { //init flare
flare->pos = 0;
uint16_t peakHeight = 75 + random8(180); //0-255
peakHeight = (peakHeight * (SEGLEN -1)) >> 8;
flare->vel = sqrt(-2.0 * gravity * peakHeight);
flare->col = 255; //brightness
SEGENV.aux0 = 1;
}
// launch
if (flare->vel > 12 * gravity) {
// flare
setPixelColor(SEGMENT.start + int(flare->pos),flare->col,flare->col,flare->col);
flare->pos += flare->vel;
flare->pos = constrain(flare->pos, 0, SEGLEN-1);
flare->vel += gravity;
flare->col -= 2;
} else {
SEGENV.aux0 = 2; // ready to explode
}
} else if (SEGENV.aux0 < 4) {
/*
* Explode!
*
* Explosion happens where the flare ended.
* Size is proportional to the height.
*/
int nSparks = flare->pos;
nSparks = constrain(nSparks, 0, numSparks);
static float dying_gravity;
// initialize sparks
if (SEGENV.aux0 == 2) {
for (int i = 1; i < nSparks; i++) {
sparks[i].pos = flare->pos;
sparks[i].vel = (float(random16(0, 20000)) / 10000.0) - 0.9; // from -0.9 to 1.1
sparks[i].col = 345;//abs(sparks[i].vel * 750.0); // set colors before scaling velocity to keep them bright
//sparks[i].col = constrain(sparks[i].col, 0, 345);
sparks[i].colIndex = random8();
sparks[i].vel *= flare->pos/SEGLEN; // proportional to height
sparks[i].vel *= -gravity *50;
}
//sparks[1].col = 345; // this will be our known spark
dying_gravity = gravity/2;
SEGENV.aux0 = 3;
}
if (sparks[1].col > 4) {//&& sparks[1].pos > 0) { // as long as our known spark is lit, work with all the sparks
for (int i = 1; i < nSparks; i++) {
sparks[i].pos += sparks[i].vel;
sparks[i].vel += dying_gravity;
if (sparks[i].col > 3) sparks[i].col -= 4;
if (sparks[i].pos > 0 && sparks[i].pos < SEGLEN) {
uint16_t prog = sparks[i].col;
uint32_t spColor = (SEGMENT.palette) ? color_wheel(sparks[i].colIndex) : SEGCOLOR(0);
CRGB c = CRGB::Black; //HeatColor(sparks[i].col);
if (prog > 300) { //fade from white to spark color
c = col_to_crgb(color_blend(spColor, WHITE, (prog - 300)*5));
} else if (prog > 45) { //fade from spark color to black
c = col_to_crgb(color_blend(BLACK, spColor, prog - 45));
uint8_t cooling = (300 - prog) >> 5;
c.g = qsub8(c.g, cooling);
c.b = qsub8(c.b, cooling * 2);
}
setPixelColor(SEGMENT.start + int(sparks[i].pos), c.red, c.green, c.blue);
}
}
dying_gravity *= .99; // as sparks burn out they fall slower
} else {
SEGENV.aux0 = 6 + random8(10); //wait for this many frames
}
} else {
SEGENV.aux0--;
if (SEGENV.aux0 < 4) {
SEGENV.aux0 = 0; //back to flare
SEGENV.step = (SEGMENT.intensity > random8()); //decide firing side
}
}
SEGMENT.setOption(1, actuallyReverse);
return FRAMETIME;
}

View File

@ -91,7 +91,7 @@
#define IS_REVERSE ((SEGMENT.options & REVERSE ) == REVERSE ) #define IS_REVERSE ((SEGMENT.options & REVERSE ) == REVERSE )
#define IS_SELECTED ((SEGMENT.options & SELECTED) == SELECTED ) #define IS_SELECTED ((SEGMENT.options & SELECTED) == SELECTED )
#define MODE_COUNT 90 #define MODE_COUNT 96
#define FX_MODE_STATIC 0 #define FX_MODE_STATIC 0
#define FX_MODE_BLINK 1 #define FX_MODE_BLINK 1
@ -183,10 +183,19 @@
#define FX_MODE_GLITTER 87 #define FX_MODE_GLITTER 87
#define FX_MODE_CANDLE 88 #define FX_MODE_CANDLE 88
#define FX_MODE_STARBURST 89 #define FX_MODE_STARBURST 89
#define FX_MODE_EXPLODING_FIREWORKS 90
#define FX_MODE_BOUNCINGBALLS 91
#define FX_MODE_SINELON 92
#define FX_MODE_SINELON_DUAL 93
#define FX_MODE_SINELON_RAINBOW 94
#define FX_MODE_POPCORN 95
class WS2812FX { class WS2812FX {
typedef uint16_t (WS2812FX::*mode_ptr)(void); typedef uint16_t (WS2812FX::*mode_ptr)(void);
// pre show callback
typedef void (*show_callback) (void);
// segment parameters // segment parameters
public: public:
@ -248,9 +257,7 @@ class WS2812FX {
return true; return true;
} }
void deallocateData(){ void deallocateData(){
if (data) { delete[] data;
delete[] data;
}
data = nullptr; data = nullptr;
WS2812FX::_usedSegmentData -= _dataLen; WS2812FX::_usedSegmentData -= _dataLen;
_dataLen = 0; _dataLen = 0;
@ -352,6 +359,12 @@ class WS2812FX {
_mode[FX_MODE_GLITTER] = &WS2812FX::mode_glitter; _mode[FX_MODE_GLITTER] = &WS2812FX::mode_glitter;
_mode[FX_MODE_CANDLE] = &WS2812FX::mode_candle; _mode[FX_MODE_CANDLE] = &WS2812FX::mode_candle;
_mode[FX_MODE_STARBURST] = &WS2812FX::mode_starburst; _mode[FX_MODE_STARBURST] = &WS2812FX::mode_starburst;
_mode[FX_MODE_EXPLODING_FIREWORKS] = &WS2812FX::mode_exploding_fireworks;
_mode[FX_MODE_BOUNCINGBALLS] = &WS2812FX::mode_bouncing_balls;
_mode[FX_MODE_SINELON] = &WS2812FX::mode_sinelon;
_mode[FX_MODE_SINELON_DUAL] = &WS2812FX::mode_sinelon_dual;
_mode[FX_MODE_SINELON_RAINBOW] = &WS2812FX::mode_sinelon_rainbow;
_mode[FX_MODE_POPCORN] = &WS2812FX::mode_popcorn;
_brightness = DEFAULT_BRIGHTNESS; _brightness = DEFAULT_BRIGHTNESS;
currentPalette = CRGBPalette16(CRGB::Black); currentPalette = CRGBPalette16(CRGB::Black);
@ -359,8 +372,6 @@ class WS2812FX {
ablMilliampsMax = 850; ablMilliampsMax = 850;
currentMilliamps = 0; currentMilliamps = 0;
timebase = 0; timebase = 0;
_locked = nullptr;
_modeUsesLock = false;
bus = new NeoPixelWrapper(); bus = new NeoPixelWrapper();
resetSegments(); resetSegments();
} }
@ -377,13 +388,8 @@ class WS2812FX {
driverModeCronixie(bool b), driverModeCronixie(bool b),
setCronixieDigits(byte* d), setCronixieDigits(byte* d),
setCronixieBacklight(bool b), setCronixieBacklight(bool b),
setIndividual(uint16_t i, uint32_t col),
setRange(uint16_t i, uint16_t i2, uint32_t col), setRange(uint16_t i, uint16_t i2, uint32_t col),
lock(uint16_t i), setShowCallback(show_callback cb),
lockRange(uint16_t i, uint16_t i2),
unlock(uint16_t i),
unlockRange(uint16_t i, uint16_t i2),
unlockAll(void),
setTransitionMode(bool t), setTransitionMode(bool t),
trigger(void), trigger(void),
setSegment(uint8_t n, uint16_t start, uint16_t stop), setSegment(uint8_t n, uint16_t start, uint16_t stop),
@ -535,7 +541,13 @@ class WS2812FX {
mode_spots_fade(void), mode_spots_fade(void),
mode_glitter(void), mode_glitter(void),
mode_candle(void), mode_candle(void),
mode_starburst(void); mode_starburst(void),
mode_exploding_fireworks(void),
mode_bouncing_balls(void),
mode_sinelon(void),
mode_sinelon_dual(void),
mode_sinelon_rainbow(void),
mode_popcorn(void);
private: private:
@ -554,21 +566,20 @@ class WS2812FX {
void handle_palette(void); void handle_palette(void);
void fill(uint32_t); void fill(uint32_t);
bool modeUsesLock(uint8_t);
bool bool
_modeUsesLock,
_rgbwMode, _rgbwMode,
_cronixieMode, _cronixieMode,
_cronixieBacklightEnabled, _cronixieBacklightEnabled,
_skipFirstMode, _skipFirstMode,
_triggered; _triggered;
byte* _locked;
byte _cronixieDigits[6]; byte _cronixieDigits[6];
mode_ptr _mode[MODE_COUNT]; // SRAM footprint: 4 bytes per element mode_ptr _mode[MODE_COUNT]; // SRAM footprint: 4 bytes per element
show_callback _callback = nullptr;
// mode helper functions // mode helper functions
uint16_t uint16_t
blink(uint32_t, uint32_t, bool strobe, bool), blink(uint32_t, uint32_t, bool strobe, bool),
@ -577,6 +588,7 @@ class WS2812FX {
theater_chase(uint32_t, uint32_t, bool), theater_chase(uint32_t, uint32_t, bool),
running_base(bool), running_base(bool),
larson_scanner(bool), larson_scanner(bool),
sinelon_base(bool,bool),
dissolve(uint32_t), dissolve(uint32_t),
chase(uint32_t, uint32_t, uint32_t, bool), chase(uint32_t, uint32_t, uint32_t, bool),
gradient_base(bool), gradient_base(bool),
@ -612,7 +624,8 @@ const char JSON_mode_names[] PROGMEM = R"=====([
"Two Dots","Two Areas","Circus","Halloween","Tri Chase","Tri Wipe","Tri Fade","Lightning","ICU","Multi Comet", "Two Dots","Two Areas","Circus","Halloween","Tri Chase","Tri Wipe","Tri Fade","Lightning","ICU","Multi Comet",
"Scanner Dual","Stream 2","Oscillate","Pride 2015","Juggle","Palette","Fire 2012","Colorwaves","Bpm","Fill Noise", "Scanner Dual","Stream 2","Oscillate","Pride 2015","Juggle","Palette","Fire 2012","Colorwaves","Bpm","Fill Noise",
"Noise 1","Noise 2","Noise 3","Noise 4","Colortwinkles","Lake","Meteor","Meteor Smooth","Railway","Ripple", "Noise 1","Noise 2","Noise 3","Noise 4","Colortwinkles","Lake","Meteor","Meteor Smooth","Railway","Ripple",
"Twinklefox","Twinklecat","Halloween Eyes","Solid Pattern","Solid Pattern Tri","Spots","Spots Fade","Glitter","Candle","Fireworks Starburst" "Twinklefox","Twinklecat","Halloween Eyes","Solid Pattern","Solid Pattern Tri","Spots","Spots Fade","Glitter","Candle","Fireworks Starburst",
"Fireworks 1D","Bouncing Balls","Sinelon","Sinelon Dual","Sinelon Rainbow","Popcorn"
])====="; ])=====";

View File

@ -32,7 +32,7 @@
void WS2812FX::init(bool supportWhite, uint16_t countPixels, bool skipFirst, uint8_t disableNLeds) void WS2812FX::init(bool supportWhite, uint16_t countPixels, bool skipFirst, uint8_t disableNLeds)
{ {
if (supportWhite == _rgbwMode && countPixels == _length && _locked != NULL && disableNLeds == _disableNLeds) return; if (supportWhite == _rgbwMode && countPixels == _length && disableNLeds == _disableNLeds) return;
RESET_RUNTIME; RESET_RUNTIME;
_rgbwMode = supportWhite; _rgbwMode = supportWhite;
_skipFirstMode = skipFirst; _skipFirstMode = skipFirst;
@ -59,13 +59,9 @@ void WS2812FX::init(bool supportWhite, uint16_t countPixels, bool skipFirst, uin
bus->Begin((NeoPixelType)ty, _lengthRaw); bus->Begin((NeoPixelType)ty, _lengthRaw);
delete[] _locked;
_locked = new byte[_length];
_segments[0].start = 0; _segments[0].start = 0;
_segments[0].stop = _usableCount; _segments[0].stop = _usableCount;
unlockAll();
setBrightness(_brightness); setBrightness(_brightness);
} }
@ -96,14 +92,6 @@ void WS2812FX::service() {
_triggered = false; _triggered = false;
} }
bool WS2812FX::modeUsesLock(uint8_t m)
{
if (m == FX_MODE_FIRE_2012 || m == FX_MODE_COLORTWINKLE ||
m == FX_MODE_METEOR || m == FX_MODE_METEOR_SMOOTH ||
m == FX_MODE_RIPPLE || m == FX_MODE_DYNAMIC ) return true;
return false;
}
void WS2812FX::setPixelColor(uint16_t n, uint32_t c) { void WS2812FX::setPixelColor(uint16_t n, uint32_t c) {
uint8_t w = (c >> 24); uint8_t w = (c >> 24);
uint8_t r = (c >> 16); uint8_t r = (c >> 16);
@ -115,7 +103,6 @@ void WS2812FX::setPixelColor(uint16_t n, uint32_t c) {
void WS2812FX::setPixelColor(uint16_t i, byte r, byte g, byte b, byte w) void WS2812FX::setPixelColor(uint16_t i, byte r, byte g, byte b, byte w)
{ {
i = i * (_disableNLeds+1); i = i * (_disableNLeds+1);
if (_locked[i] && !_modeUsesLock) return;
if (IS_REVERSE) i = SEGMENT.stop -1 -i + SEGMENT.start; //reverse just individual segment if (IS_REVERSE) i = SEGMENT.stop -1 -i + SEGMENT.start; //reverse just individual segment
byte tmpg = g; byte tmpg = g;
switch (colorOrder) //0 = Grb, default switch (colorOrder) //0 = Grb, default
@ -208,6 +195,8 @@ void WS2812FX::setCronixieDigits(byte d[])
//you can set it to 0 if the ESP is powered by USB and the LEDs by external //you can set it to 0 if the ESP is powered by USB and the LEDs by external
void WS2812FX::show(void) { void WS2812FX::show(void) {
if (_callback) _callback();
//power limit calculation //power limit calculation
//each LED can draw up 195075 "power units" (approx. 53mA) //each LED can draw up 195075 "power units" (approx. 53mA)
//one PU is the power it takes to have 1 channel 1 step brighter per brightness step //one PU is the power it takes to have 1 channel 1 step brighter per brightness step
@ -273,7 +262,6 @@ void WS2812FX::trigger() {
void WS2812FX::setMode(uint8_t segid, uint8_t m) { void WS2812FX::setMode(uint8_t segid, uint8_t m) {
if (segid >= MAX_NUM_SEGMENTS) return; if (segid >= MAX_NUM_SEGMENTS) return;
bool anyUsedLock = _modeUsesLock, anyUseLock = false;
if (m >= MODE_COUNT) m = MODE_COUNT - 1; if (m >= MODE_COUNT) m = MODE_COUNT - 1;
if (_segments[segid].mode != m) if (_segments[segid].mode != m)
@ -281,13 +269,6 @@ void WS2812FX::setMode(uint8_t segid, uint8_t m) {
_segment_runtimes[segid].reset(); _segment_runtimes[segid].reset();
_segments[segid].mode = m; _segments[segid].mode = m;
} }
for (uint8_t i = 0; i < MAX_NUM_SEGMENTS; i++)
{
if (modeUsesLock(_segments[i].mode)) anyUseLock = true;
}
if (anyUsedLock && !anyUseLock) unlockAll();
_modeUsesLock = anyUseLock;
} }
uint8_t WS2812FX::getModeCount() uint8_t WS2812FX::getModeCount()
@ -454,12 +435,7 @@ void WS2812FX::setSegment(uint8_t n, uint16_t i1, uint16_t i2) {
if (n >= MAX_NUM_SEGMENTS) return; if (n >= MAX_NUM_SEGMENTS) return;
Segment& seg = _segments[n]; Segment& seg = _segments[n];
if (seg.start == i1 && seg.stop == i2) return; if (seg.start == i1 && seg.stop == i2) return;
if (seg.isActive() && modeUsesLock(seg.mode))
{
_modeUsesLock = false;
unlockRange(seg.start, seg.stop);
_modeUsesLock = true;
}
_segment_index = n; fill(0); //turn old segment range off _segment_index = n; fill(0); //turn old segment range off
if (i2 <= i1) //disable segment if (i2 <= i1) //disable segment
{ {
@ -489,61 +465,20 @@ void WS2812FX::resetSegments() {
_segment_runtimes[0].reset(); _segment_runtimes[0].reset();
} }
void WS2812FX::setIndividual(uint16_t i, uint32_t col)
{
if (modeUsesLock(SEGMENT.mode)) return;
if (i >= 0 && i < _length)
{
_locked[i] = false;
setPixelColor(i, col);
_locked[i] = true;
}
}
void WS2812FX::setRange(uint16_t i, uint16_t i2, uint32_t col) void WS2812FX::setRange(uint16_t i, uint16_t i2, uint32_t col)
{ {
if (i2 >= i) if (i2 >= i)
{ {
for (uint16_t x = i; x <= i2; x++) setIndividual(x,col); for (uint16_t x = i; x <= i2; x++) setPixelColor(x, col);
} else } else
{ {
for (uint16_t x = i2; x <= i; x++) setIndividual(x,col); for (uint16_t x = i2; x <= i; x++) setPixelColor(x, col);
} }
} }
void WS2812FX::lock(uint16_t i) void WS2812FX::setShowCallback(show_callback cb)
{ {
if (_modeUsesLock) return; _callback = cb;
if (i < _length) _locked[i] = true;
}
void WS2812FX::lockRange(uint16_t i, uint16_t i2)
{
if (_modeUsesLock) return;
for (uint16_t x = i; x < i2; x++)
{
if (x < _length) _locked[i] = true;
}
}
void WS2812FX::unlock(uint16_t i)
{
if (_modeUsesLock) return;
if (i < _length) _locked[i] = false;
}
void WS2812FX::unlockRange(uint16_t i, uint16_t i2)
{
if (_modeUsesLock) return;
for (uint16_t x = i; x < i2; x++)
{
if (x < _length) _locked[x] = false;
}
}
void WS2812FX::unlockAll()
{
for (int i=0; i < _length; i++) _locked[i] = false;
} }
void WS2812FX::setTransitionMode(bool t) void WS2812FX::setTransitionMode(bool t)

View File

@ -368,10 +368,10 @@ HTTP traffic is unencrypted. An attacker in the same network can intercept form
<button type="button" onclick="U()">Manual OTA Update</button><br> <button type="button" onclick="U()">Manual OTA Update</button><br>
Enable ArduinoOTA: <input type="checkbox" name="AO"><br> Enable ArduinoOTA: <input type="checkbox" name="AO"><br>
<h3>About</h3> <h3>About</h3>
<a href="https://github.com/Aircoookie/WLED" target="_blank">WLED</a> version 0.9.0-b1<br><br> <a href="https://github.com/Aircoookie/WLED" target="_blank">WLED</a> version 0.9.0-b2<br><br>
<a href="https://github.com/Aircoookie/WLED/wiki/Contributors-&-About" target="_blank">Contributors, dependencies and special thanks</a><br> <a href="https://github.com/Aircoookie/WLED/wiki/Contributors-&-About" target="_blank">Contributors, dependencies and special thanks</a><br>
A huge thank you to everyone who helped me create WLED!<br><br> A huge thank you to everyone who helped me create WLED!<br><br>
(c) 2016-2019 Christian Schwinne <br> (c) 2016-2020 Christian Schwinne <br>
<i>Licensed under the MIT license</i><br><br> <i>Licensed under the MIT license</i><br><br>
Server message: <span class="msg"> Response error! </span><hr> Server message: <span class="msg"> Response error! </span><hr>
<button type="button" onclick="B()">Back</button><button type="submit">Save & Reboot</button> <button type="button" onclick="B()">Back</button><button type="submit">Save & Reboot</button>

View File

@ -10,7 +10,7 @@
*/ */
/* /*
* @title Espalexa library * @title Espalexa library
* @version 2.4.3 * @version 2.4.4
* @author Christian Schwinne * @author Christian Schwinne
* @license MIT * @license MIT
* @contributors d-999 * @contributors d-999
@ -49,7 +49,7 @@
#include <WiFiUdp.h> #include <WiFiUdp.h>
#ifdef ESPALEXA_DEBUG #ifdef ESPALEXA_DEBUG
#pragma message "Espalexa 2.4.3 debug mode" #pragma message "Espalexa 2.4.4 debug mode"
#define EA_DEBUG(x) Serial.print (x) #define EA_DEBUG(x) Serial.print (x)
#define EA_DEBUGLN(x) Serial.println (x) #define EA_DEBUGLN(x) Serial.println (x)
#else #else
@ -164,7 +164,7 @@ private:
json += "\",\"modelid\":\"" + modelidString(dev->getType()); json += "\",\"modelid\":\"" + modelidString(dev->getType());
json += "\",\"manufacturername\":\"Philips\",\"productname\":\"E" + String(static_cast<uint8_t>(dev->getType())); json += "\",\"manufacturername\":\"Philips\",\"productname\":\"E" + String(static_cast<uint8_t>(dev->getType()));
json += "\",\"uniqueid\":\"" + String(encodeLightId(deviceId+1)); json += "\",\"uniqueid\":\"" + String(encodeLightId(deviceId+1));
json += "\",\"swversion\":\"espalexa-2.4.3\"}"; json += "\",\"swversion\":\"espalexa-2.4.4\"}";
return json; return json;
} }
@ -188,7 +188,7 @@ private:
} }
res += "\r\nFree Heap: " + (String)ESP.getFreeHeap(); res += "\r\nFree Heap: " + (String)ESP.getFreeHeap();
res += "\r\nUptime: " + (String)millis(); res += "\r\nUptime: " + (String)millis();
res += "\r\n\r\nEspalexa library v2.4.3 by Christian Schwinne 2019"; res += "\r\n\r\nEspalexa library v2.4.4 by Christian Schwinne 2020";
server->send(200, "text/plain", res); server->send(200, "text/plain", res);
} }
#endif #endif
@ -298,7 +298,7 @@ private:
"SERVER: FreeRTOS/6.0.5, UPnP/1.0, IpBridge/1.17.0\r\n" // _modelName, _modelNumber "SERVER: FreeRTOS/6.0.5, UPnP/1.0, IpBridge/1.17.0\r\n" // _modelName, _modelNumber
"hue-bridgeid: "+ escapedMac +"\r\n" "hue-bridgeid: "+ escapedMac +"\r\n"
"ST: urn:schemas-upnp-org:device:basic:1\r\n" // _deviceType "ST: urn:schemas-upnp-org:device:basic:1\r\n" // _deviceType
"USN: uuid:2f402f80-da50-11e1-9b23-"+ escapedMac +"::upnp:rootdevice\r\n" // _uuid::_deviceType "USN: uuid:2f402f80-da50-11e1-9b23-"+ escapedMac +"::ssdp:all\r\n" // _uuid::_deviceType
"\r\n"; "\r\n";
espalexaUdp.beginPacket(espalexaUdp.remoteIP(), espalexaUdp.remotePort()); espalexaUdp.beginPacket(espalexaUdp.remoteIP(), espalexaUdp.remotePort());
@ -372,7 +372,7 @@ public:
String request = packetBuffer; String request = packetBuffer;
if(request.indexOf("M-SEARCH") >= 0) { if(request.indexOf("M-SEARCH") >= 0) {
EA_DEBUGLN(request); EA_DEBUGLN(request);
if(request.indexOf("upnp:rootdevice") > 0 || request.indexOf("asic:1") > 0) { if(request.indexOf("upnp:rootdevice") > 0 || request.indexOf("asic:1") > 0 || request.indexOf("ssdp:all") > 0) {
EA_DEBUGLN("Responding search req..."); EA_DEBUGLN("Responding search req...");
respondToSearch(); respondToSearch();
} }
@ -584,4 +584,4 @@ public:
~Espalexa(){delete devices;} //note: Espalexa is NOT meant to be destructed ~Espalexa(){delete devices;} //note: Espalexa is NOT meant to be destructed
}; };
#endif #endif

View File

@ -130,30 +130,28 @@ uint32_t EspalexaDevice::getRGB()
case 350: rgb[0]=130,rgb[1]=90,rgb[2]=0;rgb[3]=255;break; case 350: rgb[0]=130,rgb[1]=90,rgb[2]=0;rgb[3]=255;break;
case 383: rgb[0]=255,rgb[1]=153,rgb[2]=0;rgb[3]=255;break; case 383: rgb[0]=255,rgb[1]=153,rgb[2]=0;rgb[3]=255;break;
default: { default: {
if( temp <= 66 ){
if( temp <= 66 ){ r = 255;
r = 255; g = temp;
g = temp; g = 99.470802 * log(g) - 161.119568;
g = 99.470802 * log(g) - 161.119568; if( temp <= 19){
if( temp <= 19){ b = 0;
b = 0; } else {
} else { b = temp-10;
b = temp-10; b = 138.517731 * log(b) - 305.044793;
b = 138.517731 * log(b) - 305.044793; }
} } else {
} else { r = temp - 60;
r = temp - 60; r = 329.698727 * pow(r, -0.13320476);
r = 329.698727 * pow(r, -0.13320476); g = temp - 60;
g = temp - 60; g = 288.12217 * pow(g, -0.07551485 );
g = 288.12217 * pow(g, -0.07551485 ); b = 255;
b = 255; }
}
rgb[0] = (byte)constrain(r,0.1,255.1); rgb[0] = (byte)constrain(r,0.1,255.1);
rgb[1] = (byte)constrain(g,0.1,255.1); rgb[1] = (byte)constrain(g,0.1,255.1);
rgb[2] = (byte)constrain(b,0.1,255.1); rgb[2] = (byte)constrain(b,0.1,255.1);
}
}
} }
} else if (_mode == EspalexaColorMode::hs) } else if (_mode == EspalexaColorMode::hs)
{ {
@ -232,6 +230,12 @@ uint32_t EspalexaDevice::getRGB()
return _rgb; return _rgb;
} }
//white channel for RGBW lights. Always 0 unless colormode is ct
uint8_t EspalexaDevice::getW()
{
return (getRGB() >> 24) & 0xFF;
}
uint8_t EspalexaDevice::getR() uint8_t EspalexaDevice::getR()
{ {
return (getRGB() >> 16) & 0xFF; return (getRGB() >> 16) & 0xFF;

View File

@ -51,6 +51,7 @@ public:
uint8_t getR(); uint8_t getR();
uint8_t getG(); uint8_t getG();
uint8_t getB(); uint8_t getB();
uint8_t getW();
EspalexaColorMode getColorMode(); EspalexaColorMode getColorMode();
EspalexaDeviceType getType(); EspalexaDeviceType getType();

View File

@ -98,7 +98,7 @@
//version code in format yymmddb (b = daily build) //version code in format yymmddb (b = daily build)
#define VERSION 1912312 #define VERSION 2001071
char versionString[] = "0.9.0-b2"; char versionString[] = "0.9.0-b2";
@ -424,6 +424,7 @@ AsyncMqttClient* mqtt = NULL;
void colorFromUint32(uint32_t,bool=false); void colorFromUint32(uint32_t,bool=false);
void serveMessage(AsyncWebServerRequest*,uint16_t,String,String,byte); void serveMessage(AsyncWebServerRequest*,uint16_t,String,String,byte);
void handleE131Packet(e131_packet_t*, IPAddress); void handleE131Packet(e131_packet_t*, IPAddress);
void handleOverlayDraw();
#define E131_MAX_UNIVERSE_COUNT 9 #define E131_MAX_UNIVERSE_COUNT 9

View File

@ -200,7 +200,6 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
if (request->hasArg("OL")){ if (request->hasArg("OL")){
overlayDefault = request->arg("OL").toInt(); overlayDefault = request->arg("OL").toInt();
if (overlayCurrent != overlayDefault) strip.unlockAll();
overlayCurrent = overlayDefault; overlayCurrent = overlayDefault;
} }
@ -459,29 +458,6 @@ bool handleSet(AsyncWebServerRequest *request, const String& req)
pos = req.indexOf("OL="); pos = req.indexOf("OL=");
if (pos > 0) { if (pos > 0) {
overlayCurrent = getNumVal(&req, pos); overlayCurrent = getNumVal(&req, pos);
strip.unlockAll();
}
//(un)lock pixel (ranges)
pos = req.indexOf("&L=");
if (pos > 0) {
uint16_t index = getNumVal(&req, pos);
pos = req.indexOf("L2=");
bool unlock = req.indexOf("UL") > 0;
if (pos > 0) {
uint16_t index2 = getNumVal(&req, pos);
if (unlock) {
strip.unlockRange(index, index2);
} else {
strip.lockRange(index, index2);
}
} else {
if (unlock) {
strip.unlock(index);
} else {
strip.lock(index);
}
}
} }
//apply macro //apply macro

View File

@ -96,6 +96,7 @@ void wledInit()
void beginStrip() void beginStrip()
{ {
// Initialize NeoPixel Strip and button // Initialize NeoPixel Strip and button
strip.setShowCallback(handleOverlayDraw);
#ifdef BTNPIN #ifdef BTNPIN
pinMode(BTNPIN, INPUT_PULLUP); pinMode(BTNPIN, INPUT_PULLUP);

View File

@ -79,7 +79,6 @@ void arlsLock(uint32_t timeoutMs)
{ {
strip.setPixelColor(i,0,0,0,0); strip.setPixelColor(i,0,0,0,0);
} }
strip.unlockAll();
realtimeActive = true; realtimeActive = true;
} }
realtimeTimeout = millis() + timeoutMs; realtimeTimeout = millis() + timeoutMs;
@ -127,7 +126,6 @@ void handleNotifications()
//unlock strip when realtime UDP times out //unlock strip when realtime UDP times out
if (realtimeActive && millis() > realtimeTimeout) if (realtimeActive && millis() > realtimeTimeout)
{ {
//strip.unlockAll();
strip.setBrightness(bri); strip.setBrightness(bri);
realtimeActive = false; realtimeActive = false;
//strip.setMode(effectCurrent); //strip.setMode(effectCurrent);

View File

@ -1,6 +1,7 @@
/* /*
* Used to draw clock overlays over the strip * Used to draw clock overlays over the strip
*/ */
void initCronixie() void initCronixie()
{ {
if (overlayCurrent == 3 && !cronixieInit) if (overlayCurrent == 3 && !cronixieInit)
@ -24,14 +25,8 @@ void handleOverlays()
initCronixie(); initCronixie();
updateLocalTime(); updateLocalTime();
checkTimers(); checkTimers();
switch (overlayCurrent) checkCountdown();
{ if (overlayCurrent == 3) _overlayCronixie();//Diamex cronixie clock kit
case 0: break;//no overlay
case 1: _overlayAnalogClock(); break;//2 analog clock
case 2: break;//nixie 1-digit, removed
case 3: _overlayCronixie();//Diamex cronixie clock kit
}
if (!countdownMode || overlayCurrent < 3) checkCountdown(); //countdown macro activation must work
overlayRefreshedTime = millis(); overlayRefreshedTime = millis();
} }
} }
@ -40,7 +35,6 @@ void handleOverlays()
void _overlayAnalogClock() void _overlayAnalogClock()
{ {
int overlaySize = overlayMax - overlayMin +1; int overlaySize = overlayMax - overlayMin +1;
strip.unlockAll();
if (countdownMode) if (countdownMode)
{ {
_overlayAnalogCountdown(); return; _overlayAnalogCountdown(); return;
@ -73,23 +67,19 @@ void _overlayAnalogClock()
{ {
pix = analogClock12pixel + round((overlaySize / 12.0) *i); pix = analogClock12pixel + round((overlaySize / 12.0) *i);
if (pix > overlayMax) pix -= overlaySize; if (pix > overlayMax) pix -= overlaySize;
strip.setIndividual(pix, 0x00FFAA); strip.setPixelColor(pix, 0x00FFAA);
} }
} }
if (!analogClockSecondsTrail) strip.setIndividual(secondPixel, 0xFF0000); if (!analogClockSecondsTrail) strip.setPixelColor(secondPixel, 0xFF0000);
strip.setIndividual(minutePixel, 0x00FF00); strip.setPixelColor(minutePixel, 0x00FF00);
strip.setIndividual(hourPixel, 0x0000FF); strip.setPixelColor(hourPixel, 0x0000FF);
overlayRefreshMs = 998; overlayRefreshMs = 998;
} }
void _overlayAnalogCountdown() void _overlayAnalogCountdown()
{ {
strip.unlockAll(); if (now() < countdownTime)
if (now() >= countdownTime)
{
checkCountdown();
} else
{ {
long diff = countdownTime - now(); long diff = countdownTime - now();
double pval = 60; double pval = 60;
@ -127,3 +117,9 @@ void _overlayAnalogCountdown()
} }
overlayRefreshMs = 998; overlayRefreshMs = 998;
} }
void handleOverlayDraw() {
if (overlayCurrent != 1) return; //only analog clock
_overlayAnalogClock();
}

View File

@ -145,7 +145,6 @@ void setCronixie()
void _overlayCronixie() void _overlayCronixie()
{ {
if (countdownMode) checkCountdown();
#ifndef WLED_DISABLE_CRONIXIE #ifndef WLED_DISABLE_CRONIXIE
byte h = hour(local); byte h = hour(local);
byte h0 = h; byte h0 = h;

View File

@ -257,7 +257,9 @@ void serializeInfo(JsonObject root)
JsonObject wifi_info = root.createNestedObject("wifi"); JsonObject wifi_info = root.createNestedObject("wifi");
wifi_info["bssid"] = WiFi.BSSIDstr(); wifi_info["bssid"] = WiFi.BSSIDstr();
wifi_info["signal"] = getSignalQuality(WiFi.RSSI()); int qrssi = WiFi.RSSI();
wifi_info["rssi"] = qrssi;
wifi_info["signal"] = getSignalQuality(qrssi);
wifi_info["channel"] = WiFi.channel(); wifi_info["channel"] = WiFi.channel();
#ifdef ARDUINO_ARCH_ESP32 #ifdef ARDUINO_ARCH_ESP32