commit
19f6fd2295
459
wled00/FX.cpp
459
wled00/FX.cpp
@ -236,22 +236,24 @@ uint16_t WS2812FX::mode_random_color(void) {
|
||||
// * to new random colors.
|
||||
*/
|
||||
uint16_t WS2812FX::mode_dynamic(void) {
|
||||
if (!SEGENV.allocateData(SEGLEN)) return mode_static(); //allocation failed
|
||||
|
||||
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 it = now / cycleTime;
|
||||
if (it != SEGENV.step && SEGMENT.speed != 0) //new color
|
||||
{
|
||||
for(uint16_t i=SEGMENT.start; i < SEGMENT.stop; i++) {
|
||||
if (random8() <= SEGMENT.intensity) _locked[i] = random8();
|
||||
for (uint16_t i = 0; i < SEGLEN; i++) {
|
||||
if (random8() <= SEGMENT.intensity) SEGENV.data[i] = random8();
|
||||
}
|
||||
SEGENV.step = it;
|
||||
}
|
||||
|
||||
for(uint16_t i=SEGMENT.start; i < SEGMENT.stop; i++) {
|
||||
setPixelColor(i, color_wheel(_locked[i]));
|
||||
for (uint16_t i = 0; i < SEGLEN; i++) {
|
||||
setPixelColor(SEGMENT.start + i, color_wheel(SEGENV.data[i]));
|
||||
}
|
||||
return FRAMETIME;
|
||||
}
|
||||
@ -1467,17 +1469,24 @@ typedef struct Oscillator {
|
||||
*/
|
||||
uint16_t WS2812FX::mode_oscillate(void)
|
||||
{
|
||||
static oscillator oscillators[NUM_COLORS] = {
|
||||
{SEGLEN/4, SEGLEN/8, 1, 1},
|
||||
{SEGLEN/4*3, SEGLEN/8, 1, 2},
|
||||
{SEGLEN/4*2, SEGLEN/8, -1, 1}
|
||||
|
||||
};
|
||||
uint8_t numOscillators = 3;
|
||||
uint16_t dataSize = sizeof(oscillator) * numOscillators;
|
||||
|
||||
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 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 (it != SEGENV.step) oscillators[i].pos += oscillators[i].dir * oscillators[i].speed;
|
||||
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;
|
||||
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) {
|
||||
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
|
||||
|
||||
if (!SEGENV.allocateData(SEGLEN)) return mode_static(); //allocation failed
|
||||
|
||||
byte* heat = SEGENV.data;
|
||||
|
||||
if (it != SEGENV.step)
|
||||
{
|
||||
// Step 1. Cool down every cell a little
|
||||
for( int i = SEGMENT.start; i < SEGMENT.stop; i++) {
|
||||
_locked[i] = qsub8(_locked[i], random8(0, (((20 + SEGMENT.speed /3) * 10) / SEGLEN) + 2));
|
||||
for (uint16_t i = 0; i < SEGLEN; i++) {
|
||||
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
|
||||
for( int k= SEGMENT.stop -1; k >= SEGMENT.start + 2; k--) {
|
||||
_locked[k] = (_locked[k - 1] + _locked[k - 2] + _locked[k - 2] ) / 3;
|
||||
for (uint16_t k= SEGLEN -1; k > 1; k--) {
|
||||
heat[k] = (heat[k - 1] + heat[k - 2] + heat[k - 2] ) / 3;
|
||||
}
|
||||
|
||||
// Step 3. Randomly ignite new 'sparks' of heat near the bottom
|
||||
if( random8() <= SEGMENT.intensity ) {
|
||||
int y = SEGMENT.start + random8(7);
|
||||
if (y < SEGMENT.stop) _locked[y] = qadd8(_locked[y], random8(160,255) );
|
||||
if (random8() <= SEGMENT.intensity) {
|
||||
uint8_t y = random8(7);
|
||||
if (y < SEGLEN) heat[y] = qadd8(heat[y], random8(160,255));
|
||||
}
|
||||
SEGENV.step = it;
|
||||
}
|
||||
|
||||
// Step 4. Map from heat cells to LED colors
|
||||
for( int j = SEGMENT.start; j < SEGMENT.stop; j++) {
|
||||
CRGB color = ColorFromPalette( currentPalette, min(_locked[j],240), 255, LINEARBLEND);
|
||||
setPixelColor(j, color.red, color.green, color.blue);
|
||||
for (uint16_t j = 0; j < SEGLEN; j++) {
|
||||
CRGB color = ColorFromPalette(currentPalette, min(heat[j],240), 255, LINEARBLEND);
|
||||
setPixelColor(SEGMENT.start + j, color.red, color.green, color.blue);
|
||||
}
|
||||
return FRAMETIME;
|
||||
}
|
||||
@ -1870,18 +1883,25 @@ uint16_t WS2812FX::mode_noise16_4()
|
||||
//based on https://gist.github.com/kriegsman/5408ecd397744ba0393e
|
||||
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;
|
||||
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));
|
||||
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;
|
||||
incrementalColor.nscale8_video( fadeUpAmount);
|
||||
fastled_col += incrementalColor;
|
||||
|
||||
if( fastled_col.red == 255 || fastled_col.green == 255 || fastled_col.blue == 255) {
|
||||
_locked[i] = false;
|
||||
if (fastled_col.red == 255 || fastled_col.green == 255 || fastled_col.blue == 255) {
|
||||
bitWrite(SEGENV.data[index], bitNum, false);
|
||||
}
|
||||
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++)
|
||||
{
|
||||
if ( random8() <= SEGMENT.intensity ) {
|
||||
if (random8() <= SEGMENT.intensity) {
|
||||
for (uint8_t times = 0; times < 5; times++) //attempt to spawn a new pixel 5 times
|
||||
{
|
||||
int i = SEGMENT.start + random16(SEGLEN);
|
||||
if(getPixelColor(i) == 0) {
|
||||
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);
|
||||
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.
|
||||
// adapted from https://www.tweaking4all.com/hardware/arduino/adruino-led-strip-effects/#LEDStripEffectMeteorRain
|
||||
uint16_t WS2812FX::mode_meteor() {
|
||||
if (!SEGENV.allocateData(SEGLEN)) return mode_static(); //allocation failed
|
||||
|
||||
byte* trail = SEGENV.data;
|
||||
|
||||
byte meteorSize= 1+ SEGLEN / 10;
|
||||
uint16_t counter = now * ((SEGMENT.speed >> 2) +8);
|
||||
uint16_t in = counter * SEGLEN >> 16;
|
||||
|
||||
// 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)
|
||||
{
|
||||
byte meteorTrailDecay = 128 + random8(127);
|
||||
_locked[i] = scale8(_locked[i], meteorTrailDecay);
|
||||
setPixelColor(i, color_from_palette(_locked[i], false, true, 255));
|
||||
trail[i] = scale8(trail[i], meteorTrailDecay);
|
||||
setPixelColor(SEGMENT.start + i, color_from_palette(trail[i], false, true, 255));
|
||||
}
|
||||
}
|
||||
|
||||
// draw meteor
|
||||
for(int j = 0; j < meteorSize; j++) {
|
||||
uint16_t index = in + j;
|
||||
if(in + j >= SEGMENT.stop) {
|
||||
index = SEGMENT.start + (in + j - SEGMENT.stop);
|
||||
if(index >= SEGLEN) {
|
||||
index = (in + j - SEGLEN);
|
||||
}
|
||||
|
||||
_locked[index] = 240;
|
||||
setPixelColor(index, color_from_palette(_locked[index], false, true, 255));
|
||||
trail[index] = 240;
|
||||
setPixelColor(SEGMENT.start + index, color_from_palette(trail[index], false, true, 255));
|
||||
}
|
||||
|
||||
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.
|
||||
// adapted from https://www.tweaking4all.com/hardware/arduino/adruino-led-strip-effects/#LEDStripEffectMeteorRain
|
||||
uint16_t WS2812FX::mode_meteor_smooth() {
|
||||
if (!SEGENV.allocateData(SEGLEN)) return mode_static(); //allocation failed
|
||||
|
||||
byte* trail = SEGENV.data;
|
||||
|
||||
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
|
||||
for (uint16_t i = SEGMENT.start; i < SEGMENT.stop; i++) {
|
||||
if (_locked[i] != 0 && random8() <= 255 - SEGMENT.intensity)
|
||||
for (uint16_t i = 0; i < SEGLEN; i++) {
|
||||
if (trail[i] != 0 && random8() <= 255 - SEGMENT.intensity)
|
||||
{
|
||||
int change = 3 - random8(12); //change each time between -8 and +3
|
||||
_locked[i] += change;
|
||||
if (_locked[i] > 245) _locked[i] = 0;
|
||||
if (_locked[i] > 240) _locked[i] = 240;
|
||||
setPixelColor(i, color_from_palette(_locked[i], false, true, 255));
|
||||
trail[i] += change;
|
||||
if (trail[i] > 245) trail[i] = 0;
|
||||
if (trail[i] > 240) trail[i] = 240;
|
||||
setPixelColor(SEGMENT.start + i, color_from_palette(trail[i], false, true, 255));
|
||||
}
|
||||
}
|
||||
|
||||
// draw meteor
|
||||
for(int j = 0; j < meteorSize; j++) {
|
||||
uint16_t index = in + j;
|
||||
if(in + j >= SEGMENT.stop) {
|
||||
index = SEGMENT.start + (in + j - SEGMENT.stop);
|
||||
if(in + j >= SEGLEN) {
|
||||
index = (in + j - SEGLEN);
|
||||
}
|
||||
setPixelColor(index, color_blend(getPixelColor(index), color_from_palette(240, false, true, 255), 48));
|
||||
_locked[index] = 240;
|
||||
setPixelColor(SEGMENT.start + index, color_blend(getPixelColor(SEGMENT.start + index), color_from_palette(240, false, true, 255), 48));
|
||||
trail[index] = 240;
|
||||
}
|
||||
|
||||
SEGENV.step += SEGMENT.speed +1;
|
||||
@ -2035,23 +2065,35 @@ uint16_t WS2812FX::mode_railway()
|
||||
//Water ripple
|
||||
//propagation velocity from speed
|
||||
//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 maxripples = SEGLEN / 4;
|
||||
if (maxripples == 0) return mode_static();
|
||||
uint16_t maxRipples = 1 + (SEGLEN >> 2);
|
||||
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));
|
||||
|
||||
//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 = _locked[storeI];
|
||||
uint16_t ripplestate = ripples[i].state;
|
||||
if (ripplestate)
|
||||
{
|
||||
uint8_t rippledecay = (SEGMENT.speed >> 4) +1; //faster decay if faster propagation
|
||||
uint16_t rippleorigin = (_locked[storeI+1] << 8) + _locked[storeI+2];
|
||||
uint32_t col = color_from_palette(_locked[storeI+3], false, false, 255);
|
||||
uint16_t rippleorigin = ripples[i].pos;
|
||||
uint32_t col = color_from_palette(ripples[i].color, false, false, 255);
|
||||
uint16_t propagation = ((ripplestate/rippledecay -1) * SEGMENT.speed);
|
||||
int16_t propI = propagation >> 8;
|
||||
uint8_t propF = propagation & 0xFF;
|
||||
@ -2061,7 +2103,7 @@ uint16_t WS2812FX::mode_ripple()
|
||||
for (int16_t v = left; v < left +4; v++)
|
||||
{
|
||||
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));
|
||||
}
|
||||
@ -2072,16 +2114,14 @@ uint16_t WS2812FX::mode_ripple()
|
||||
}
|
||||
}
|
||||
ripplestate += rippledecay;
|
||||
_locked[storeI] = (ripplestate > 254) ? 0 : ripplestate;
|
||||
ripples[i].state = (ripplestate > 254) ? 0 : ripplestate;
|
||||
} else //randomly create new wave
|
||||
{
|
||||
if (random16(IBN + 10000) <= SEGMENT.intensity)
|
||||
{
|
||||
_locked[storeI] = 1;
|
||||
uint16_t origin = SEGMENT.start + random16(SEGLEN);
|
||||
_locked[storeI+1] = origin >> 8;
|
||||
_locked[storeI+2] = origin & 0xFF;
|
||||
_locked[storeI+3] = random8(); //color
|
||||
ripples[i].state = 1;
|
||||
ripples[i].pos = SEGMENT.start + random16(SEGLEN);
|
||||
ripples[i].color = 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
|
||||
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
|
||||
//Inspired by https://github.com/avanhanegem/ArduinoCandleEffectNeoPixel
|
||||
//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
|
||||
|
||||
//each needs 64 byte
|
||||
typedef struct particle {
|
||||
CRGB color;
|
||||
uint32_t birth =0;
|
||||
@ -2456,10 +2667,16 @@ typedef struct particle {
|
||||
} star;
|
||||
|
||||
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();
|
||||
|
||||
const uint8_t numStars = 15;
|
||||
static star stars[numStars];
|
||||
star* stars = reinterpret_cast<star*>(SEGENV.data);
|
||||
|
||||
float maxSpeed = 375.0f; // Max velocity
|
||||
float particleIgnition = 250.0f; // How long to "flash"
|
||||
float particleFadeTime = 1500.0f; // Fade out time
|
||||
@ -2552,3 +2769,117 @@ uint16_t WS2812FX::mode_starburst(void) {
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
47
wled00/FX.h
47
wled00/FX.h
@ -91,7 +91,7 @@
|
||||
#define IS_REVERSE ((SEGMENT.options & REVERSE ) == REVERSE )
|
||||
#define IS_SELECTED ((SEGMENT.options & SELECTED) == SELECTED )
|
||||
|
||||
#define MODE_COUNT 90
|
||||
#define MODE_COUNT 96
|
||||
|
||||
#define FX_MODE_STATIC 0
|
||||
#define FX_MODE_BLINK 1
|
||||
@ -183,10 +183,19 @@
|
||||
#define FX_MODE_GLITTER 87
|
||||
#define FX_MODE_CANDLE 88
|
||||
#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 {
|
||||
typedef uint16_t (WS2812FX::*mode_ptr)(void);
|
||||
|
||||
// pre show callback
|
||||
typedef void (*show_callback) (void);
|
||||
|
||||
// segment parameters
|
||||
public:
|
||||
@ -248,9 +257,7 @@ class WS2812FX {
|
||||
return true;
|
||||
}
|
||||
void deallocateData(){
|
||||
if (data) {
|
||||
delete[] data;
|
||||
}
|
||||
delete[] data;
|
||||
data = nullptr;
|
||||
WS2812FX::_usedSegmentData -= _dataLen;
|
||||
_dataLen = 0;
|
||||
@ -352,6 +359,12 @@ class WS2812FX {
|
||||
_mode[FX_MODE_GLITTER] = &WS2812FX::mode_glitter;
|
||||
_mode[FX_MODE_CANDLE] = &WS2812FX::mode_candle;
|
||||
_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;
|
||||
currentPalette = CRGBPalette16(CRGB::Black);
|
||||
@ -359,8 +372,6 @@ class WS2812FX {
|
||||
ablMilliampsMax = 850;
|
||||
currentMilliamps = 0;
|
||||
timebase = 0;
|
||||
_locked = nullptr;
|
||||
_modeUsesLock = false;
|
||||
bus = new NeoPixelWrapper();
|
||||
resetSegments();
|
||||
}
|
||||
@ -377,13 +388,8 @@ class WS2812FX {
|
||||
driverModeCronixie(bool b),
|
||||
setCronixieDigits(byte* d),
|
||||
setCronixieBacklight(bool b),
|
||||
setIndividual(uint16_t i, uint32_t col),
|
||||
setRange(uint16_t i, uint16_t i2, uint32_t col),
|
||||
lock(uint16_t i),
|
||||
lockRange(uint16_t i, uint16_t i2),
|
||||
unlock(uint16_t i),
|
||||
unlockRange(uint16_t i, uint16_t i2),
|
||||
unlockAll(void),
|
||||
setShowCallback(show_callback cb),
|
||||
setTransitionMode(bool t),
|
||||
trigger(void),
|
||||
setSegment(uint8_t n, uint16_t start, uint16_t stop),
|
||||
@ -535,7 +541,13 @@ class WS2812FX {
|
||||
mode_spots_fade(void),
|
||||
mode_glitter(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:
|
||||
@ -554,21 +566,20 @@ class WS2812FX {
|
||||
|
||||
void handle_palette(void);
|
||||
void fill(uint32_t);
|
||||
bool modeUsesLock(uint8_t);
|
||||
|
||||
bool
|
||||
_modeUsesLock,
|
||||
_rgbwMode,
|
||||
_cronixieMode,
|
||||
_cronixieBacklightEnabled,
|
||||
_skipFirstMode,
|
||||
_triggered;
|
||||
|
||||
byte* _locked;
|
||||
byte _cronixieDigits[6];
|
||||
|
||||
mode_ptr _mode[MODE_COUNT]; // SRAM footprint: 4 bytes per element
|
||||
|
||||
show_callback _callback = nullptr;
|
||||
|
||||
// mode helper functions
|
||||
uint16_t
|
||||
blink(uint32_t, uint32_t, bool strobe, bool),
|
||||
@ -577,6 +588,7 @@ class WS2812FX {
|
||||
theater_chase(uint32_t, uint32_t, bool),
|
||||
running_base(bool),
|
||||
larson_scanner(bool),
|
||||
sinelon_base(bool,bool),
|
||||
dissolve(uint32_t),
|
||||
chase(uint32_t, uint32_t, uint32_t, 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",
|
||||
"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",
|
||||
"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"
|
||||
])=====";
|
||||
|
||||
|
||||
|
@ -32,7 +32,7 @@
|
||||
|
||||
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;
|
||||
_rgbwMode = supportWhite;
|
||||
_skipFirstMode = skipFirst;
|
||||
@ -59,13 +59,9 @@ void WS2812FX::init(bool supportWhite, uint16_t countPixels, bool skipFirst, uin
|
||||
|
||||
bus->Begin((NeoPixelType)ty, _lengthRaw);
|
||||
|
||||
delete[] _locked;
|
||||
_locked = new byte[_length];
|
||||
|
||||
_segments[0].start = 0;
|
||||
_segments[0].stop = _usableCount;
|
||||
|
||||
unlockAll();
|
||||
|
||||
setBrightness(_brightness);
|
||||
}
|
||||
|
||||
@ -96,14 +92,6 @@ void WS2812FX::service() {
|
||||
_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) {
|
||||
uint8_t w = (c >> 24);
|
||||
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)
|
||||
{
|
||||
i = i * (_disableNLeds+1);
|
||||
if (_locked[i] && !_modeUsesLock) return;
|
||||
if (IS_REVERSE) i = SEGMENT.stop -1 -i + SEGMENT.start; //reverse just individual segment
|
||||
byte tmpg = g;
|
||||
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
|
||||
|
||||
void WS2812FX::show(void) {
|
||||
if (_callback) _callback();
|
||||
|
||||
//power limit calculation
|
||||
//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
|
||||
@ -273,7 +262,6 @@ void WS2812FX::trigger() {
|
||||
void WS2812FX::setMode(uint8_t segid, uint8_t m) {
|
||||
if (segid >= MAX_NUM_SEGMENTS) return;
|
||||
|
||||
bool anyUsedLock = _modeUsesLock, anyUseLock = false;
|
||||
if (m >= MODE_COUNT) m = MODE_COUNT - 1;
|
||||
|
||||
if (_segments[segid].mode != m)
|
||||
@ -281,13 +269,6 @@ void WS2812FX::setMode(uint8_t segid, uint8_t m) {
|
||||
_segment_runtimes[segid].reset();
|
||||
_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()
|
||||
@ -454,12 +435,7 @@ void WS2812FX::setSegment(uint8_t n, uint16_t i1, uint16_t i2) {
|
||||
if (n >= MAX_NUM_SEGMENTS) return;
|
||||
Segment& seg = _segments[n];
|
||||
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
|
||||
if (i2 <= i1) //disable segment
|
||||
{
|
||||
@ -489,61 +465,20 @@ void WS2812FX::resetSegments() {
|
||||
_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)
|
||||
{
|
||||
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
|
||||
{
|
||||
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;
|
||||
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;
|
||||
_callback = cb;
|
||||
}
|
||||
|
||||
void WS2812FX::setTransitionMode(bool t)
|
||||
|
@ -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>
|
||||
Enable ArduinoOTA: <input type="checkbox" name="AO"><br>
|
||||
<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 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>
|
||||
Server message: <span class="msg"> Response error! </span><hr>
|
||||
<button type="button" onclick="B()">Back</button><button type="submit">Save & Reboot</button>
|
||||
|
@ -10,7 +10,7 @@
|
||||
*/
|
||||
/*
|
||||
* @title Espalexa library
|
||||
* @version 2.4.3
|
||||
* @version 2.4.4
|
||||
* @author Christian Schwinne
|
||||
* @license MIT
|
||||
* @contributors d-999
|
||||
@ -49,7 +49,7 @@
|
||||
#include <WiFiUdp.h>
|
||||
|
||||
#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_DEBUGLN(x) Serial.println (x)
|
||||
#else
|
||||
@ -164,7 +164,7 @@ private:
|
||||
json += "\",\"modelid\":\"" + modelidString(dev->getType());
|
||||
json += "\",\"manufacturername\":\"Philips\",\"productname\":\"E" + String(static_cast<uint8_t>(dev->getType()));
|
||||
json += "\",\"uniqueid\":\"" + String(encodeLightId(deviceId+1));
|
||||
json += "\",\"swversion\":\"espalexa-2.4.3\"}";
|
||||
json += "\",\"swversion\":\"espalexa-2.4.4\"}";
|
||||
|
||||
return json;
|
||||
}
|
||||
@ -188,7 +188,7 @@ private:
|
||||
}
|
||||
res += "\r\nFree Heap: " + (String)ESP.getFreeHeap();
|
||||
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);
|
||||
}
|
||||
#endif
|
||||
@ -298,7 +298,7 @@ private:
|
||||
"SERVER: FreeRTOS/6.0.5, UPnP/1.0, IpBridge/1.17.0\r\n" // _modelName, _modelNumber
|
||||
"hue-bridgeid: "+ escapedMac +"\r\n"
|
||||
"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";
|
||||
|
||||
espalexaUdp.beginPacket(espalexaUdp.remoteIP(), espalexaUdp.remotePort());
|
||||
@ -372,7 +372,7 @@ public:
|
||||
String request = packetBuffer;
|
||||
if(request.indexOf("M-SEARCH") >= 0) {
|
||||
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...");
|
||||
respondToSearch();
|
||||
}
|
||||
@ -584,4 +584,4 @@ public:
|
||||
~Espalexa(){delete devices;} //note: Espalexa is NOT meant to be destructed
|
||||
};
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
@ -130,30 +130,28 @@ uint32_t EspalexaDevice::getRGB()
|
||||
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;
|
||||
default: {
|
||||
|
||||
if( temp <= 66 ){
|
||||
r = 255;
|
||||
g = temp;
|
||||
g = 99.470802 * log(g) - 161.119568;
|
||||
if( temp <= 19){
|
||||
b = 0;
|
||||
} else {
|
||||
b = temp-10;
|
||||
b = 138.517731 * log(b) - 305.044793;
|
||||
}
|
||||
} else {
|
||||
r = temp - 60;
|
||||
r = 329.698727 * pow(r, -0.13320476);
|
||||
g = temp - 60;
|
||||
g = 288.12217 * pow(g, -0.07551485 );
|
||||
b = 255;
|
||||
}
|
||||
if( temp <= 66 ){
|
||||
r = 255;
|
||||
g = temp;
|
||||
g = 99.470802 * log(g) - 161.119568;
|
||||
if( temp <= 19){
|
||||
b = 0;
|
||||
} else {
|
||||
b = temp-10;
|
||||
b = 138.517731 * log(b) - 305.044793;
|
||||
}
|
||||
} else {
|
||||
r = temp - 60;
|
||||
r = 329.698727 * pow(r, -0.13320476);
|
||||
g = temp - 60;
|
||||
g = 288.12217 * pow(g, -0.07551485 );
|
||||
b = 255;
|
||||
}
|
||||
|
||||
rgb[0] = (byte)constrain(r,0.1,255.1);
|
||||
rgb[1] = (byte)constrain(g,0.1,255.1);
|
||||
rgb[2] = (byte)constrain(b,0.1,255.1);
|
||||
|
||||
}
|
||||
rgb[0] = (byte)constrain(r,0.1,255.1);
|
||||
rgb[1] = (byte)constrain(g,0.1,255.1);
|
||||
rgb[2] = (byte)constrain(b,0.1,255.1);
|
||||
}
|
||||
}
|
||||
} else if (_mode == EspalexaColorMode::hs)
|
||||
{
|
||||
@ -232,6 +230,12 @@ uint32_t EspalexaDevice::getRGB()
|
||||
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()
|
||||
{
|
||||
return (getRGB() >> 16) & 0xFF;
|
||||
|
@ -51,6 +51,7 @@ public:
|
||||
uint8_t getR();
|
||||
uint8_t getG();
|
||||
uint8_t getB();
|
||||
uint8_t getW();
|
||||
EspalexaColorMode getColorMode();
|
||||
EspalexaDeviceType getType();
|
||||
|
||||
|
@ -98,7 +98,7 @@
|
||||
|
||||
|
||||
//version code in format yymmddb (b = daily build)
|
||||
#define VERSION 1912312
|
||||
#define VERSION 2001071
|
||||
char versionString[] = "0.9.0-b2";
|
||||
|
||||
|
||||
@ -424,6 +424,7 @@ AsyncMqttClient* mqtt = NULL;
|
||||
void colorFromUint32(uint32_t,bool=false);
|
||||
void serveMessage(AsyncWebServerRequest*,uint16_t,String,String,byte);
|
||||
void handleE131Packet(e131_packet_t*, IPAddress);
|
||||
void handleOverlayDraw();
|
||||
|
||||
#define E131_MAX_UNIVERSE_COUNT 9
|
||||
|
||||
|
@ -200,7 +200,6 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
|
||||
|
||||
if (request->hasArg("OL")){
|
||||
overlayDefault = request->arg("OL").toInt();
|
||||
if (overlayCurrent != overlayDefault) strip.unlockAll();
|
||||
overlayCurrent = overlayDefault;
|
||||
}
|
||||
|
||||
@ -459,29 +458,6 @@ bool handleSet(AsyncWebServerRequest *request, const String& req)
|
||||
pos = req.indexOf("OL=");
|
||||
if (pos > 0) {
|
||||
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
|
||||
|
@ -96,6 +96,7 @@ void wledInit()
|
||||
void beginStrip()
|
||||
{
|
||||
// Initialize NeoPixel Strip and button
|
||||
strip.setShowCallback(handleOverlayDraw);
|
||||
|
||||
#ifdef BTNPIN
|
||||
pinMode(BTNPIN, INPUT_PULLUP);
|
||||
|
@ -79,7 +79,6 @@ void arlsLock(uint32_t timeoutMs)
|
||||
{
|
||||
strip.setPixelColor(i,0,0,0,0);
|
||||
}
|
||||
strip.unlockAll();
|
||||
realtimeActive = true;
|
||||
}
|
||||
realtimeTimeout = millis() + timeoutMs;
|
||||
@ -127,7 +126,6 @@ void handleNotifications()
|
||||
//unlock strip when realtime UDP times out
|
||||
if (realtimeActive && millis() > realtimeTimeout)
|
||||
{
|
||||
//strip.unlockAll();
|
||||
strip.setBrightness(bri);
|
||||
realtimeActive = false;
|
||||
//strip.setMode(effectCurrent);
|
||||
|
@ -1,6 +1,7 @@
|
||||
/*
|
||||
* Used to draw clock overlays over the strip
|
||||
*/
|
||||
|
||||
void initCronixie()
|
||||
{
|
||||
if (overlayCurrent == 3 && !cronixieInit)
|
||||
@ -24,14 +25,8 @@ void handleOverlays()
|
||||
initCronixie();
|
||||
updateLocalTime();
|
||||
checkTimers();
|
||||
switch (overlayCurrent)
|
||||
{
|
||||
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
|
||||
checkCountdown();
|
||||
if (overlayCurrent == 3) _overlayCronixie();//Diamex cronixie clock kit
|
||||
overlayRefreshedTime = millis();
|
||||
}
|
||||
}
|
||||
@ -40,7 +35,6 @@ void handleOverlays()
|
||||
void _overlayAnalogClock()
|
||||
{
|
||||
int overlaySize = overlayMax - overlayMin +1;
|
||||
strip.unlockAll();
|
||||
if (countdownMode)
|
||||
{
|
||||
_overlayAnalogCountdown(); return;
|
||||
@ -73,23 +67,19 @@ void _overlayAnalogClock()
|
||||
{
|
||||
pix = analogClock12pixel + round((overlaySize / 12.0) *i);
|
||||
if (pix > overlayMax) pix -= overlaySize;
|
||||
strip.setIndividual(pix, 0x00FFAA);
|
||||
strip.setPixelColor(pix, 0x00FFAA);
|
||||
}
|
||||
}
|
||||
if (!analogClockSecondsTrail) strip.setIndividual(secondPixel, 0xFF0000);
|
||||
strip.setIndividual(minutePixel, 0x00FF00);
|
||||
strip.setIndividual(hourPixel, 0x0000FF);
|
||||
if (!analogClockSecondsTrail) strip.setPixelColor(secondPixel, 0xFF0000);
|
||||
strip.setPixelColor(minutePixel, 0x00FF00);
|
||||
strip.setPixelColor(hourPixel, 0x0000FF);
|
||||
overlayRefreshMs = 998;
|
||||
}
|
||||
|
||||
|
||||
void _overlayAnalogCountdown()
|
||||
{
|
||||
strip.unlockAll();
|
||||
if (now() >= countdownTime)
|
||||
{
|
||||
checkCountdown();
|
||||
} else
|
||||
if (now() < countdownTime)
|
||||
{
|
||||
long diff = countdownTime - now();
|
||||
double pval = 60;
|
||||
@ -127,3 +117,9 @@ void _overlayAnalogCountdown()
|
||||
}
|
||||
overlayRefreshMs = 998;
|
||||
}
|
||||
|
||||
|
||||
void handleOverlayDraw() {
|
||||
if (overlayCurrent != 1) return; //only analog clock
|
||||
_overlayAnalogClock();
|
||||
}
|
||||
|
@ -145,7 +145,6 @@ void setCronixie()
|
||||
|
||||
void _overlayCronixie()
|
||||
{
|
||||
if (countdownMode) checkCountdown();
|
||||
#ifndef WLED_DISABLE_CRONIXIE
|
||||
byte h = hour(local);
|
||||
byte h0 = h;
|
||||
|
@ -257,7 +257,9 @@ void serializeInfo(JsonObject root)
|
||||
|
||||
JsonObject wifi_info = root.createNestedObject("wifi");
|
||||
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();
|
||||
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
|
Loading…
Reference in New Issue
Block a user