Merge branch 'master' into master

This commit is contained in:
Aircoookie 2020-01-03 23:19:40 +01:00 committed by GitHub
commit 4af7ccd84c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 511 additions and 263 deletions

View File

@ -41,6 +41,8 @@ lib_deps_external =
IRremoteESP8266@2.5.5 IRremoteESP8266@2.5.5
#Time@1.5 #Time@1.5
#Timezone@1.2.1 #Timezone@1.2.1
#For use SSD1306 0.91" OLED display uncomment following
#U8g2@~2.27.2
[common:esp8266] [common:esp8266]
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
@ -214,4 +216,4 @@ build_flags =
-D WLED_ENABLE_5CH_LEDS -D WLED_ENABLE_5CH_LEDS
lib_deps = lib_deps =
${common.lib_deps_external} ${common.lib_deps_external}

View File

@ -4,5 +4,5 @@ This code uses Aircookie's WLED software. It has a premade file for user modific
To install: To install:
Add the enties in the WLED00 file to the top of the same file from Aircoookies WLED. Add the entries in the WLED00 file to the top of the same file from Aircoookies WLED.
Replace the WLED06_usermod.ino file in Aircoookies WLED folder. Replace the WLED06_usermod.ino file in Aircoookies WLED folder.

View File

@ -1,7 +1,7 @@
#include <U8x8lib.h> // from https://github.com/olikraus/u8g2/ #include <U8x8lib.h> // from https://github.com/olikraus/u8g2/
//The SCL and SDA pins are defined here. //The SCL and SDA pins are defined here.
//Lolin32 boards use SCL=4 SDA=5 //Lolin32 boards use SCL=5 SDA=4
#define U8X8_PIN_SCL 5 #define U8X8_PIN_SCL 5
#define U8X8_PIN_SDA 4 #define U8X8_PIN_SDA 4

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) //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;
} }
@ -414,7 +416,6 @@ uint16_t WS2812FX::mode_theater_chase(void) {
* Inspired by the Adafruit examples. * Inspired by the Adafruit examples.
*/ */
uint16_t WS2812FX::mode_theater_chase_rainbow(void) { uint16_t WS2812FX::mode_theater_chase_rainbow(void) {
SEGENV.step = (SEGENV.step + 1) & 0xFF;
return theater_chase(color_wheel(SEGENV.step), SEGCOLOR(1), false); return theater_chase(color_wheel(SEGENV.step), SEGCOLOR(1), false);
} }
@ -1294,20 +1295,38 @@ uint16_t WS2812FX::mode_icu(void) {
*/ */
uint16_t WS2812FX::mode_tricolor_wipe(void) uint16_t WS2812FX::mode_tricolor_wipe(void)
{ {
if(SEGENV.step < SEGLEN) { uint32_t cycleTime = 1000 + (255 - SEGMENT.speed)*200;
uint32_t led_offset = SEGENV.step; uint32_t perc = now % cycleTime;
setPixelColor(SEGMENT.start + led_offset, SEGCOLOR(0)); uint16_t prog = (perc * 65535) / cycleTime;
} else if (SEGENV.step < SEGLEN*2) { uint16_t ledIndex = (prog * SEGLEN * 3) >> 16;
uint32_t led_offset = SEGENV.step - SEGLEN; uint16_t ledOffset = ledIndex;
setPixelColor(SEGMENT.start + led_offset, SEGCOLOR(1));
} else for (uint16_t i = SEGMENT.start; i < SEGMENT.stop; i++)
{ {
uint32_t led_offset = SEGENV.step - SEGLEN*2; setPixelColor(i, color_from_palette(i, true, PALETTE_SOLID_WRAP, 2));
setPixelColor(SEGMENT.start + led_offset, color_from_palette(SEGMENT.start + led_offset, true, PALETTE_SOLID_WRAP, 2)); }
if(ledIndex < SEGLEN) { //wipe from 0 to 1
for (uint16_t i = SEGMENT.start; i < SEGMENT.stop; i++)
{
setPixelColor(i, (i - SEGMENT.start > ledOffset)? SEGCOLOR(0) : SEGCOLOR(1));
}
} else if (ledIndex < SEGLEN*2) { //wipe from 1 to 2
ledOffset = ledIndex - SEGLEN;
for (uint16_t i = SEGMENT.start +ledOffset +1; i < SEGMENT.stop; i++)
{
setPixelColor(i, SEGCOLOR(1));
}
} else //wipe from 2 to 0
{
ledOffset = ledIndex - SEGLEN*2;
for (uint16_t i = SEGMENT.start; i <= SEGMENT.start +ledOffset; i++)
{
setPixelColor(i, SEGCOLOR(0));
}
} }
SEGENV.step = (SEGENV.step + 1) % (SEGLEN * 3); return FRAMETIME;
return SPEED_FORMULA_L;
} }
@ -1318,14 +1337,17 @@ uint16_t WS2812FX::mode_tricolor_wipe(void)
*/ */
uint16_t WS2812FX::mode_tricolor_fade(void) uint16_t WS2812FX::mode_tricolor_fade(void)
{ {
uint16_t counter = now * ((SEGMENT.speed >> 3) +1);
uint32_t prog = (counter * 768) >> 16;
uint32_t color1 = 0, color2 = 0; uint32_t color1 = 0, color2 = 0;
byte stage = 0; byte stage = 0;
if(SEGENV.step < 256) { if(prog < 256) {
color1 = SEGCOLOR(0); color1 = SEGCOLOR(0);
color2 = SEGCOLOR(1); color2 = SEGCOLOR(1);
stage = 0; stage = 0;
} else if(SEGENV.step < 512) { } else if(prog < 512) {
color1 = SEGCOLOR(1); color1 = SEGCOLOR(1);
color2 = SEGCOLOR(2); color2 = SEGCOLOR(2);
stage = 1; stage = 1;
@ -1335,7 +1357,7 @@ uint16_t WS2812FX::mode_tricolor_fade(void)
stage = 2; stage = 2;
} }
byte stp = SEGENV.step % 256; byte stp = prog; // % 256
uint32_t color = 0; uint32_t color = 0;
for(uint16_t i=SEGMENT.start; i < SEGMENT.stop; i++) { for(uint16_t i=SEGMENT.start; i < SEGMENT.stop; i++) {
if (stage == 2) { if (stage == 2) {
@ -1432,17 +1454,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);
@ -1459,9 +1488,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);
} }
@ -1631,30 +1660,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;
} }
@ -1835,18 +1868,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);
@ -1863,13 +1903,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
} }
@ -1903,29 +1945,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;
@ -1936,29 +1982,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;
@ -2000,23 +2050,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;
@ -2026,7 +2088,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));
} }
@ -2037,16 +2099,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
} }
} }
} }
@ -2567,3 +2627,247 @@ uint16_t WS2812FX::mode_candle()
return FRAMETIME; return FRAMETIME;
} }
/*
/ Fireworks in starburst effect
/ based on the video: https://www.reddit.com/r/arduino/comments/c3sd46/i_made_this_fireworks_effect_for_my_led_strips/
/ Speed sets frequency of new starbursts, intensity is the intensity of the burst
*/
#define STARBURST_MAX_FRAG 12
//each needs 64 byte
typedef struct particle {
CRGB color;
uint32_t birth =0;
uint32_t last =0;
float vel =0;
uint16_t pos =-1;
float fragment[STARBURST_MAX_FRAG];
} 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();
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
for (int j = 0; j < numStars; j++)
{
// speed to adjust chance of a burst, max is nearly always.
if (random8((144-(SEGMENT.speed >> 1))) == 0 && stars[j].birth == 0)
{
// Pick a random color and location.
uint16_t startPos = random16(SEGLEN-1);
float multiplier = (float)(random8())/255.0 * 1.0;
stars[j].color = col_to_crgb(color_wheel(random8()));
stars[j].pos = startPos;
stars[j].vel = maxSpeed * (float)(random8())/255.0 * multiplier;
stars[j].birth = it;
stars[j].last = it;
// more fragments means larger burst effect
int num = random8(3,6 + (SEGMENT.intensity >> 5));
for (int i=0; i < STARBURST_MAX_FRAG; i++) {
if (i < num) stars[j].fragment[i] = startPos;
else stars[j].fragment[i] = -1;
}
}
}
fill(SEGCOLOR(1));
for (int j=0; j<numStars; j++)
{
if (stars[j].birth != 0) {
float dt = (it-stars[j].last)/1000.0;
for (int i=0; i < STARBURST_MAX_FRAG; i++) {
int var = i >> 1;
if (stars[j].fragment[i] > 0) {
//all fragments travel right, will be mirrored on other side
stars[j].fragment[i] += stars[j].vel * dt * (float)var/3.0;
}
}
stars[j].last = it;
stars[j].vel -= 3*stars[j].vel*dt;
}
CRGB c = stars[j].color;
// If the star is brand new, it flashes white briefly.
// Otherwise it just fades over time.
float fade = 0.0f;
float age = it-stars[j].birth;
if (age < particleIgnition) {
c = col_to_crgb(color_blend(WHITE, crgb_to_col(c), 254.5f*((age / particleIgnition))));
} else {
// Figure out how much to fade and shrink the star based on
// its age relative to its lifetime
if (age > particleIgnition + particleFadeTime) {
fade = 1.0f; // Black hole, all faded out
stars[j].birth = 0;
c = col_to_crgb(SEGCOLOR(1));
} else {
age -= particleIgnition;
fade = (age / particleFadeTime); // Fading star
byte f = 254.5f*fade;
c = col_to_crgb(color_blend(crgb_to_col(c), SEGCOLOR(1), f));
}
}
float particleSize = (1.0 - fade) * 2;
for (uint8_t index=0; index < STARBURST_MAX_FRAG*2; index++) {
bool mirrored = index & 0x1;
uint8_t i = index >> 1;
if (stars[j].fragment[i] > 0) {
float loc = stars[j].fragment[i];
if (mirrored) loc -= (loc-stars[j].pos)*2;
int start = loc - particleSize;
int end = loc + particleSize;
if (start < 0) start = 0;
if (start == end) end++;
if (end > SEGLEN) end = SEGLEN;
for (int p = start; p < end; p++) {
setPixelColor(SEGMENT.start+p, c.r, c.g, c.b);
}
}
}
}
return FRAMETIME;
}
/*
* Exploding fireworks effect
* adapted from: http://www.anirama.com/1000leds/1d-fireworks/
*/
//each needs 12 byte
typedef struct Spark {
float pos;
float vel;
uint16_t col;
uint8_t colIndex;
} spark;
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

@ -44,10 +44,17 @@
#define WLED_FPS 42 #define WLED_FPS 42
#define FRAMETIME (1000/WLED_FPS) #define FRAMETIME (1000/WLED_FPS)
/* each segment uses 37 bytes of SRAM memory, so if you're application fails because of /* each segment uses 52 bytes of SRAM memory, so if you're application fails because of
insufficient memory, decreasing MAX_NUM_SEGMENTS may help */ insufficient memory, decreasing MAX_NUM_SEGMENTS may help */
#define MAX_NUM_SEGMENTS 10 #define MAX_NUM_SEGMENTS 10
/* How much data bytes all segments combined may allocate */
#ifdef ESP8266
#define MAX_SEGMENT_DATA 2048
#else
#define MAX_SEGMENT_DATA 8192
#endif
#define NUM_COLORS 3 /* number of colors per segment */ #define NUM_COLORS 3 /* number of colors per segment */
#define SEGMENT _segments[_segment_index] #define SEGMENT _segments[_segment_index]
#define SEGCOLOR(x) gamma32(_segments[_segment_index].colors[x]) #define SEGCOLOR(x) gamma32(_segments[_segment_index].colors[x])
@ -84,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 94 #define MODE_COUNT 96
#define FX_MODE_STATIC 0 #define FX_MODE_STATIC 0
#define FX_MODE_BLINK 1 #define FX_MODE_BLINK 1
@ -175,15 +182,20 @@
#define FX_MODE_SPOTS_FADE 86 #define FX_MODE_SPOTS_FADE 86
#define FX_MODE_GLITTER 87 #define FX_MODE_GLITTER 87
#define FX_MODE_CANDLE 88 #define FX_MODE_CANDLE 88
#define FX_MODE_BOUNCINGBALLS 89 #define FX_MODE_STARBURST 89
#define FX_MODE_SINELON 90 #define FX_MODE_EXPLODING_FIREWORKS 90
#define FX_MODE_SINELON_DUAL 91 #define FX_MODE_BOUNCINGBALLS 91
#define FX_MODE_SINELON_RAINBOW 92 #define FX_MODE_SINELON 92
#define FX_MODE_POPCORN 93 #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:
@ -226,13 +238,33 @@ class WS2812FX {
} segment; } segment;
// segment runtime parameters // segment runtime parameters
typedef struct Segment_runtime { // 16 bytes typedef struct Segment_runtime { // 28 bytes
unsigned long next_time; unsigned long next_time;
uint32_t step; uint32_t step;
uint32_t call; uint32_t call;
uint16_t aux0; uint16_t aux0;
uint16_t aux1; uint16_t aux1;
void reset(){next_time = 0; step = 0; call = 0; aux0 = 0; aux1 = 0;}; byte* data = nullptr;
bool allocateData(uint16_t len){
if (data && _dataLen == len) return true; //already allocated
deallocateData();
if (WS2812FX::_usedSegmentData + len > MAX_SEGMENT_DATA) return false; //not enough memory
data = new (std::nothrow) byte[len];
if (!data) return false; //allocation failed
WS2812FX::_usedSegmentData += len;
_dataLen = len;
memset(data, 0, len);
return true;
}
void deallocateData(){
delete[] data;
data = nullptr;
WS2812FX::_usedSegmentData -= _dataLen;
_dataLen = 0;
}
void reset(){next_time = 0; step = 0; call = 0; aux0 = 0; aux1 = 0; deallocateData();}
private:
uint16_t _dataLen = 0;
} segment_runtime; } segment_runtime;
WS2812FX() { WS2812FX() {
@ -324,13 +356,15 @@ class WS2812FX {
_mode[FX_MODE_TRI_STATIC_PATTERN] = &WS2812FX::mode_tri_static_pattern; _mode[FX_MODE_TRI_STATIC_PATTERN] = &WS2812FX::mode_tri_static_pattern;
_mode[FX_MODE_SPOTS] = &WS2812FX::mode_spots; _mode[FX_MODE_SPOTS] = &WS2812FX::mode_spots;
_mode[FX_MODE_SPOTS_FADE] = &WS2812FX::mode_spots_fade; _mode[FX_MODE_SPOTS_FADE] = &WS2812FX::mode_spots_fade;
_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_BouncingBalls; _mode[FX_MODE_BOUNCINGBALLS] = &WS2812FX::mode_BouncingBalls;
_mode[FX_MODE_SINELON] = &WS2812FX::mode_sinelon; _mode[FX_MODE_SINELON] = &WS2812FX::mode_sinelon;
_mode[FX_MODE_SINELON_DUAL] = &WS2812FX::mode_sinelon_dual; _mode[FX_MODE_SINELON_DUAL] = &WS2812FX::mode_sinelon_dual;
_mode[FX_MODE_SINELON_RAINBOW] = &WS2812FX::mode_sinelon_rainbow; _mode[FX_MODE_SINELON_RAINBOW] = &WS2812FX::mode_sinelon_rainbow;
_mode[FX_MODE_POPCORN] = &WS2812FX::mode_popcorn; _mode[FX_MODE_POPCORN] = &WS2812FX::mode_popcorn;
_mode[FX_MODE_GLITTER] = &WS2812FX::mode_glitter;
_mode[FX_MODE_CANDLE] = &WS2812FX::mode_candle;
_brightness = DEFAULT_BRIGHTNESS; _brightness = DEFAULT_BRIGHTNESS;
currentPalette = CRGBPalette16(CRGB::Black); currentPalette = CRGBPalette16(CRGB::Black);
@ -338,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();
} }
@ -356,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),
@ -512,13 +539,15 @@ class WS2812FX {
mode_tri_static_pattern(void), mode_tri_static_pattern(void),
mode_spots(void), mode_spots(void),
mode_spots_fade(void), mode_spots_fade(void),
mode_glitter(void),
mode_candle(void),
mode_starburst(void),
mode_exploding_fireworks(void),
mode_BouncingBalls(void), mode_BouncingBalls(void),
mode_sinelon(void), mode_sinelon(void),
mode_sinelon_dual(void), mode_sinelon_dual(void),
mode_sinelon_rainbow(void), mode_sinelon_rainbow(void),
mode_popcorn(void), mode_popcorn(void);
mode_glitter(void),
mode_candle(void);
private: private:
@ -533,24 +562,24 @@ class WS2812FX {
uint16_t _length, _lengthRaw, _usableCount; uint16_t _length, _lengthRaw, _usableCount;
uint16_t _rand16seed; uint16_t _rand16seed;
uint8_t _brightness; uint8_t _brightness;
static uint16_t _usedSegmentData;
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),
@ -580,7 +609,8 @@ class WS2812FX {
// start, stop, speed, intensity, palette, mode, options, 3 unused bytes (group, spacing, opacity), color[] // start, stop, speed, intensity, palette, mode, options, 3 unused bytes (group, spacing, opacity), color[]
{ 0, 7, DEFAULT_SPEED, 128, 0, DEFAULT_MODE, NO_OPTIONS, 1, 0, 255, {DEFAULT_COLOR}} { 0, 7, DEFAULT_SPEED, 128, 0, DEFAULT_MODE, NO_OPTIONS, 1, 0, 255, {DEFAULT_COLOR}}
}; };
segment_runtime _segment_runtimes[MAX_NUM_SEGMENTS]; // SRAM footprint: 16 bytes per element segment_runtime _segment_runtimes[MAX_NUM_SEGMENTS]; // SRAM footprint: 28 bytes per element
friend class Segment_runtime;
}; };
@ -592,10 +622,10 @@ const char JSON_mode_names[] PROGMEM = R"=====([
"Chase Rainbow","Chase Flash","Chase Flash Rnd","Rainbow Runner","Colorful","Traffic Light","Sweep Random","Running 2","Red & Blue","Stream", "Chase Rainbow","Chase Flash","Chase Flash Rnd","Rainbow Runner","Colorful","Traffic Light","Sweep Random","Running 2","Red & Blue","Stream",
"Scanner","Lighthouse","Fireworks","Rain","Merry Christmas","Fire Flicker","Gradient","Loading","Police","Police All", "Scanner","Lighthouse","Fireworks","Rain","Merry Christmas","Fire Flicker","Gradient","Loading","Police","Police All",
"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","Bouncing Balls", "Twinklefox","Twinklecat","Halloween Eyes","Solid Pattern","Solid Pattern Tri","Spots","Spots Fade","Glitter","Candle","Fireworks Starburst",
"Sinelon","Sinelon Dual","Sinelon Rainbow","Popcorn" "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
{ {
@ -473,7 +449,7 @@ void WS2812FX::setSegment(uint8_t n, uint16_t i1, uint16_t i2) {
void WS2812FX::resetSegments() { void WS2812FX::resetSegments() {
memset(_segments, 0, sizeof(_segments)); memset(_segments, 0, sizeof(_segments));
memset(_segment_runtimes, 0, sizeof(_segment_runtimes)); //memset(_segment_runtimes, 0, sizeof(_segment_runtimes));
_segment_index = 0; _segment_index = 0;
_segments[0].mode = DEFAULT_MODE; _segments[0].mode = DEFAULT_MODE;
_segments[0].colors[0] = DEFAULT_COLOR; _segments[0].colors[0] = DEFAULT_COLOR;
@ -484,64 +460,25 @@ void WS2812FX::resetSegments() {
for (uint16_t i = 1; i < MAX_NUM_SEGMENTS; i++) for (uint16_t i = 1; i < MAX_NUM_SEGMENTS; i++)
{ {
_segments[i].colors[0] = color_wheel(i*51); _segments[i].colors[0] = color_wheel(i*51);
_segment_runtimes[i].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)
@ -862,3 +799,5 @@ uint32_t WS2812FX::gamma32(uint32_t color)
b = gammaT[b]; b = gammaT[b];
return ((w << 24) | (r << 16) | (g << 8) | (b)); return ((w << 24) | (r << 16) | (g << 8) | (b));
} }
uint16_t WS2812FX::_usedSegmentData = 0;

View File

@ -130,26 +130,25 @@ public:
_pGrbw = new NeoPixelBrightnessBus<PIXELFEATURE4,PIXELMETHOD>(countPixels, LEDPIN); _pGrbw = new NeoPixelBrightnessBus<PIXELFEATURE4,PIXELMETHOD>(countPixels, LEDPIN);
#endif #endif
_pGrbw->Begin(); _pGrbw->Begin();
break;
#ifdef WLED_USE_ANALOG_LEDS
#ifdef WLED_USE_ANALOG_LEDS pinMode(WPIN, OUTPUT);
//init PWM pins - PINs 5,12,13,15 are used with Magic Home LED Controller #ifdef WLED_USE_5CH_LEDS
pinMode(RPIN, OUTPUT); pinMode(W2PIN, OUTPUT);
pinMode(GPIN, OUTPUT);
pinMode(BPIN, OUTPUT);
switch (_type) {
case NeoPixelType_Grb: break;
#ifdef WLED_USE_5CH_LEDS
case NeoPixelType_Grbw: pinMode(WPIN, OUTPUT); pinMode(W2PIN, OUTPUT); break;
#else
case NeoPixelType_Grbw: pinMode(WPIN, OUTPUT); break;
#endif
}
analogWriteRange(255); //same range as one RGB channel
analogWriteFreq(880); //PWM frequency proven as good for LEDs
#endif #endif
#endif
break;
} }
#ifdef WLED_USE_ANALOG_LEDS
//init PWM pins - PINs 5,12,13,15 are used with Magic Home LED Controller
pinMode(RPIN, OUTPUT);
pinMode(GPIN, OUTPUT);
pinMode(BPIN, OUTPUT);
analogWriteRange(255); //same range as one RGB channel
analogWriteFreq(880); //PWM frequency proven as good for LEDs
#endif
} }
#ifdef WLED_USE_ANALOG_LEDS #ifdef WLED_USE_ANALOG_LEDS

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

@ -98,7 +98,7 @@
//version code in format yymmddb (b = daily build) //version code in format yymmddb (b = daily build)
#define VERSION 1912232 #define VERSION 2001031
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
@ -622,6 +598,10 @@ bool handleSet(AsyncWebServerRequest *request, const String& req)
//cronixie //cronixie
#ifndef WLED_DISABLE_CRONIXIE #ifndef WLED_DISABLE_CRONIXIE
//mode, 1 countdown
pos = req.indexOf("NM=");
if (pos > 0) countdownMode = (req.charAt(pos+3) != '0');
pos = req.indexOf("NX="); //sets digits to code pos = req.indexOf("NX="); //sets digits to code
if (pos > 0) { if (pos > 0) {
strlcpy(cronixieDisplay, req.substring(pos + 3, pos + 9).c_str(), 6); strlcpy(cronixieDisplay, req.substring(pos + 3, pos + 9).c_str(), 6);
@ -636,9 +616,6 @@ bool handleSet(AsyncWebServerRequest *request, const String& req)
overlayRefreshedTime = 0; overlayRefreshedTime = 0;
} }
#endif #endif
//mode, 1 countdown
pos = req.indexOf("NM=");
if (pos > 0) countdownMode = (req.charAt(pos+3) != '0');
pos = req.indexOf("U0="); //user var 0 pos = req.indexOf("U0="); //user var 0
if (pos > 0) { if (pos > 0) {

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

@ -163,13 +163,16 @@ void setCountdown()
//returns true if countdown just over //returns true if countdown just over
bool checkCountdown() bool checkCountdown()
{ {
long diff = countdownTime - now(); unsigned long n = now();
local = abs(diff); local = countdownTime - n;
if (diff <0 && !countdownOverTriggered) if (n > countdownTime) {
{ local = n - countdownTime;
if (macroCountdown != 0) applyMacro(macroCountdown); if (!countdownOverTriggered)
countdownOverTriggered = true; {
return true; if (macroCountdown != 0) applyMacro(macroCountdown);
countdownOverTriggered = true;
return true;
}
} }
return false; return false;
} }

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,9 +145,7 @@ 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;
byte m = minute(local); byte m = minute(local);

View File

@ -243,7 +243,7 @@ void serializeInfo(JsonObject root)
leds_pin.add(LEDPIN); leds_pin.add(LEDPIN);
leds["pwr"] = strip.currentMilliamps; leds["pwr"] = strip.currentMilliamps;
leds["maxpwr"] = strip.ablMilliampsMax; leds["maxpwr"] = (strip.currentMilliamps)? strip.ablMilliampsMax : 0;
leds["maxseg"] = strip.getMaxSegments(); leds["maxseg"] = strip.getMaxSegments();
leds["seglock"] = false; //will be used in the future to prevent modifications to segment config leds["seglock"] = false; //will be used in the future to prevent modifications to segment config