Merge pull request #2 from Aircoookie/master

Update repository
pull/575/head
srg74 2020-01-06 21:01:36 -05:00 zatwierdzone przez GitHub
commit 19f6fd2295
Nie znaleziono w bazie danych klucza dla tego podpisu
ID klucza GPG: 4AEE18F83AFDEB23
14 zmienionych plików z 491 dodań i 234 usunięć

Wyświetl plik

@ -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;
}

Wyświetl plik

@ -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"
])=====";

Wyświetl plik

@ -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)

Wyświetl plik

@ -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>

Wyświetl plik

@ -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

Wyświetl plik

@ -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;

Wyświetl plik

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

Wyświetl plik

@ -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

Wyświetl plik

@ -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

Wyświetl plik

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

Wyświetl plik

@ -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);

Wyświetl plik

@ -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();
}

Wyświetl plik

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

Wyświetl plik

@ -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