// Experiments with Fastled beatsin16, beatsin88
// Based on code from https://forum.arduino.cc/t/fastled-beatsin16-just-one-way/1085303/4?u=davex
// modified to be mostly non-blocking
// Wokwi simulation at https://wokwi.com/projects/355578772690767873
#include <FastLED.h>
#define NUM_LEDS 40
#define LED_PIN 6
//CRGB leds[NUM_LEDS];
CRGBArray <NUM_LEDS> leds;
uint8_t data[ NUM_LEDS];
const float BPMf = 20; // BPM in floating point
const uint32_t BPM88 = BPMf * 256; // Q8.8 fixed point
const uint16_t BPM = BPMf; // integer
void setup() {
delay(300);
FastLED.addLeds<WS2812B, LED_PIN, GRB>(leds, NUM_LEDS).setCorrection(TypicalLEDStrip);
FastLED.setBrightness(255);
// FastLED.setMaxPowerInVoltsAndMilliamps(5, 1000);
randomSeed(analogRead(A0));
Serial.begin(115200);
Serial.print("ms seqVal period.\n0 0 0");
}
void loop() {
static uint32_t loopcount = 0;
static uint32_t fastcount = 0;
static uint32_t now = 0;
static uint32_t lastnow = -1;
static int seqVar = -1;
// use millis to update sequence:
static uint32_t lastseq = -1;
static uint32_t period2 = 0L; // X seconds
now = millis(); loopcount++;
if ( now - lastseq >= period2) { //change sequence
lastseq += period2;
seqVar++;
if (seqVar > 8) seqVar = 0;
//seqVar = 7; // override
period2 = random(10, 20) * 1000L; // X seconds
// report on sequences:
Serial.print(". loops: ");
Serial.print(loopcount);
Serial.print(", LED updates: ");
Serial.print(fastcount);
Serial.print(" ");
Serial.print("\n");
Serial.print(now);
Serial.print(" ");
Serial.print(seqVar);
Serial.print(" ");
Serial.print(period2);
loopcount = 0; fastcount = 0;
}
if (now - lastnow >= 4 ) { // updated LEDs only once per Xms
lastnow = now;
fastcount++;
switch (seqVar) {
case 0:
//uint32_t period2 = .1 * 60000L; // X minutes
//for ( uint32_t tStart = millis(); (millis() - tStart) < period2; )
movingDotred();
break;
case 1:
//uint32_t period2 = .2 * 60000L; // X minutes
//for ( uint32_t tStart = millis(); (millis() - tStart) < period2; )
movingDotwhite();
break;
case 2:
//uint32_t period2 = .2 * 60000L; // X minutes
//for ( uint32_t tStart = millis(); (millis() - tStart) < period2; )
movingDotblue();
break;
case 3:
//uint32_t period3 = .3 * 60000L; // X minutes
//for ( uint32_t tStart = millis(); (millis() - tStart) < period2; )
phaseBeat();
break;
case 4:
//uint32_t period3 = .3 * 60000L; // X minutes
//for ( uint32_t tStart = millis(); (millis() - tStart) < period2; )
redwhitebluestripeswithglitter();
break;
case 5:
//uint32_t period3 = .3 * 60000L; // X minutes
//for ( uint32_t tStart = millis(); (millis() - tStart) < period2; )
RWBFadeInandOut();
break;
case 6:
//uint32_t period3 = .3 * 60000L; // X minutes
//for ( uint32_t tStart = millis(); (millis() - tStart) < period2; )
NewKITT(0xff, 0, 0, 35, .1, 50);
NewKITT(0, 0, 0xff, 35, .1, 50);
NewKITT(0xff, 0xff, 0xff, 35, .1, 50);
break;
case 7:
//uint32_t period3 = .3 * 60000L; // X minutes
//for ( uint32_t tStart = millis(); (millis() - tStart) < period2; )
theaterChase(0xff, 0, 0, 70);
//theaterChase(0xff, 0xff, 0xff, 70);
//theaterChase(0, 0, 0xff, 70);
break;
case 8:
outsideinmeteors();
break;
default:
;
}
}
}
void movingDotred() {
const uint32_t interval = 60000UL / BPMf / 2; // = (60000ms/min)/20BPM/2
static unsigned long lastOffsetTime = -interval; // make an immediate change with -interval initialization trick
static uint32_t t0 = 0; // timebase
static uint16_t phase = 1 * 32768 / 2; // which segment of sine
if (millis() - lastOffsetTime >= interval) {
lastOffsetTime += interval;
t0 = millis() ; // update timebase
}
uint16_t sinBeat = beatsin88(BPM88, 0, NUM_LEDS - 1, t0, phase);
leds[sinBeat] = CRGB::Red;
fadeToBlackBy(leds, NUM_LEDS, 3);
FastLED.show();
}
void movingDotwhite() {
uint16_t sinBeat = beatsin16(20, 0, NUM_LEDS - 1, 0, 0);
leds[sinBeat] = CRGB::White;
fadeToBlackBy(leds, NUM_LEDS, 1);
FastLED.show();
}
void movingDotblue() {
uint16_t sinBeat = beatsin16(20, 0, NUM_LEDS - 1, 0, 0);
leds[sinBeat] = CRGB::Blue;
fadeToBlackBy(leds, NUM_LEDS, 1);
FastLED.show();
}
void phaseBeat() {
// uint8_t sinBeat = beatsin8(15, 0, NUM_LEDS - 1, 0, 0);
// uint8_t sinBeat2 = beatsin8(15, 0, NUM_LEDS - 1, 0, 85);
// uint8_t sinBeat3 = beatsin8(15, 0, NUM_LEDS - 1, 0, 170);
// If you notice that your pattern is missing out certain LEDs, you
// will need to use the higher resolution beatsin16 instead. In this
// case remove the 3 lines above and replace them with the following:
uint16_t sinBeat = beatsin16(15, 0, NUM_LEDS - 1, 0, 0);
uint16_t sinBeat2 = beatsin16(15, 0, NUM_LEDS - 1, 0, 21845);
uint16_t sinBeat3 = beatsin16(15, 0, NUM_LEDS - 1, 0, 43690);
leds[sinBeat] = CRGB::Blue;
leds[sinBeat2] = CRGB::Red;
leds[sinBeat3] = CRGB::White;
fadeToBlackBy(leds, NUM_LEDS, 10);
FastLED.show();
}
void redwhitebluestripeswithglitter() {
static uint32_t last = 0 ;
const uint32_t interval = 20;
if (millis() - last < interval) return;
last = millis();
fill_data_array();
render_data_with_palette();
//add_glitter();
FastLED.show();
//FastLED.delay(20);
}
void fill_data_array()
{
static uint8_t startValue = 0;
startValue = startValue + 2;
uint8_t value = startValue;
for ( int i = 0; i < NUM_LEDS; i++) {
data[i] = triwave8( value); // convert value to an up-and-down wave
value += 3;
}
}
CRGBPalette16 gPalette (
CRGB::Black, CRGB::Black,
CRGB::Red, CRGB::Red, CRGB::Red, CRGB::Red,
CRGB::White, CRGB::White, CRGB::White, CRGB::White,
CRGB::Blue, CRGB::Blue, CRGB::Blue, CRGB::Blue,
CRGB::Black, CRGB::Black
);
void render_data_with_palette()
{
for ( int i = 0; i < NUM_LEDS; i++) {
leds[i] = ColorFromPalette( gPalette, data[i], 255, LINEARBLEND);
}
}
void add_glitter()
{
int chance_of_glitter = 10; // percent of the time that we add glitter
int number_of_glitters = 10; // number of glitter sparkles to add
int r = random8(100);
if ( r < chance_of_glitter ) {
for ( int j = 0; j < number_of_glitters; j++) {
int pos = random16( NUM_LEDS);
leds[pos] = CRGB::White; // very bright glitter
}
}
}
void RWBFadeInandOut() { //blocking
FadeInOut(0xff, 0x00, 0x00); // red
FadeInOut(0xff, 0xff, 0xff); // white
FadeInOut(0x00, 0x00, 0xff); // blue
}
void setAll(byte red, byte white, byte blue) {
for (int i = 0; i < NUM_LEDS; i++ ) {
setPixel(i, red, white, blue);
}
FastLED.show();
}
void setPixel(int Pixel, byte red, byte white, byte blue) {
leds[Pixel].r = red;
leds[Pixel].g = white;
leds[Pixel].b = blue;
}
void FadeInOut(byte red, byte green, byte blue) { //blocking
float r, g, b;
for (int k = 0; k < 256; k = k + 5) {
r = (k / 256.0) * red;
g = (k / 256.0) * green;
b = (k / 256.0) * blue;
setAll(r, g, b);
FastLED.show();
}
for (int k = 255; k >= 0; k = k - 5) {
r = (k / 256.0) * red;
g = (k / 256.0) * green;
b = (k / 256.0) * blue;
setAll(r, g, b);
FastLED.show();
}
}
void NewKITT(byte red, byte green, byte blue, int EyeSize, int SpeedDelay, int ReturnDelay) {
RightToLeft(red, green, blue, EyeSize, SpeedDelay, ReturnDelay);
LeftToRight(red, green, blue, EyeSize, SpeedDelay, ReturnDelay);
OutsideToCenter(red, green, blue, EyeSize, SpeedDelay, ReturnDelay);
CenterToOutside(red, green, blue, EyeSize, SpeedDelay, ReturnDelay);
// LeftToRight(red, green, blue, EyeSize, SpeedDelay, ReturnDelay);
// RightToLeft(red, green, blue, EyeSize, SpeedDelay, ReturnDelay);
// OutsideToCenter(red, green, blue, EyeSize, SpeedDelay, ReturnDelay);
// CenterToOutside(red, green, blue, EyeSize, SpeedDelay, ReturnDelay);
}
void CenterToOutside(byte red, byte green, byte blue, int EyeSize, int SpeedDelay, int ReturnDelay) {
for (int i = ((NUM_LEDS - EyeSize) / 2); i >= 0; i--) {
setAll(0, 0, 0);
setPixel(i, red / 10, green / 10, blue / 10);
for (int j = 1; j <= EyeSize; j++) {
setPixel(i + j, red, green, blue);
}
setPixel(i + EyeSize + 1, red / 10, green / 10, blue / 10);
setPixel(NUM_LEDS - i, red / 10, green / 10, blue / 10);
for (int j = 1; j <= EyeSize; j++) {
setPixel(NUM_LEDS - i - j, red, green, blue);
}
setPixel(NUM_LEDS - i - EyeSize - 1, red / 10, green / 10, blue / 10);
FastLED.show();
delay(SpeedDelay);
}
delay(ReturnDelay);
}
void OutsideToCenter(byte red, byte green, byte blue, int EyeSize, int SpeedDelay, int ReturnDelay) {
for (int i = 0; i <= ((NUM_LEDS - EyeSize) / 2); i++) {
setAll(0, 0, 0);
setPixel(i, red / 10, green / 10, blue / 10);
for (int j = 1; j <= EyeSize; j++) {
setPixel(i + j, red, green, blue);
}
setPixel(i + EyeSize + 1, red / 10, green / 10, blue / 10);
setPixel(NUM_LEDS - i, red / 10, green / 10, blue / 10);
for (int j = 1; j <= EyeSize; j++) {
setPixel(NUM_LEDS - i - j, red, green, blue);
}
setPixel(NUM_LEDS - i - EyeSize - 1, red / 10, green / 10, blue / 10);
FastLED.show();
delay(SpeedDelay);
}
delay(ReturnDelay);
}
void LeftToRight(byte red, byte green, byte blue, int EyeSize, int SpeedDelay, int ReturnDelay) {
for (int i = 0; i < NUM_LEDS - EyeSize - 2; i++) {
setAll(0, 0, 0);
setPixel(i, red / 10, green / 10, blue / 10);
for (int j = 1; j <= EyeSize; j++) {
setPixel(i + j, red, green, blue);
}
setPixel(i + EyeSize + 1, red / 10, green / 10, blue / 10);
FastLED.show();
delay(SpeedDelay);
}
delay(ReturnDelay);
}
void RightToLeft(byte red, byte green, byte blue, int EyeSize, int SpeedDelay, int ReturnDelay) {
for (int i = NUM_LEDS - EyeSize - 2; i > 0; i--) {
setAll(0, 0, 0);
setPixel(i, red / 10, green / 10, blue / 10);
for (int j = 1; j <= EyeSize; j++) {
setPixel(i + j, red, green, blue);
}
setPixel(i + EyeSize + 1, red / 10, green / 10, blue / 10);
FastLED.show();
delay(SpeedDelay);
}
delay(ReturnDelay);
}
void theaterChase(byte red, byte green, byte blue, int SpeedDelay) {
// https://www.tweaking4all.com/hardware/arduino/adruino-led-strip-effects/#LEDStripEffectTheatreChase
// mod to be non-blocking
static int j, i, q;
static int last = 0;
const int phaselen = 3;
if (millis() - last < abs(SpeedDelay)) return;
last = millis();
// for (j = 0; j < 10; j++) { //do 10 cycles of chasing
// for ( q = 0; q < phaselen; q++) { // phase
for (int i = 0; i < NUM_LEDS; i = i + phaselen) {
setPixel(i + q, 0, 0, 0); //turn every third pixel off
}
if (SpeedDelay > 0) {
q++;
if (q >= phaselen) {
q = q - phaselen;
}
} else {
q--;
if ( q < 0 )q = phaselen;
}
for ( i = 0; i < NUM_LEDS; i = i + phaselen) {
setPixel(i + q, red, green, blue); //turn every third pixel on
}
FastLED.show();
// delay(SpeedDelay);
// }
// }
}
void outsideinmeteors() {
int myVar = random(0, 3);
switch (myVar) {
case 0:
red();
break;
case 1:
blue();
break;
case 2:
white();
}
}
void red() {
//static uint8_t hue;
for (int i = 0; i < NUM_LEDS / 2; i++) {
// fade everything out
leds.fadeToBlackBy(20);
// let's set an led value
leds[i] = CRGB(250, 0, 0);
// now, let's first 20 leds to the top 20 leds,
leds(NUM_LEDS / 2, NUM_LEDS - 1) = leds(NUM_LEDS / 2 - 1, 0);
FastLED.delay(random (2, 30));
}
}
void blue() {
//static uint8_t hue;
for (int i = 0; i < NUM_LEDS / 2; i++) {
// fade everything out
leds.fadeToBlackBy(20);
// let's set an led value
leds[i] = CRGB(0, 0, 250);
// now, let's first 20 leds to the top 20 leds,
leds(NUM_LEDS / 2, NUM_LEDS - 1) = leds(NUM_LEDS / 2 - 1, 0);
FastLED.delay(random(2, 30));
}
}
void white() {
//static uint8_t hue;
for (int i = 0; i < NUM_LEDS / 2; i++) {
// fade everything out
leds.fadeToBlackBy(20);
// let's set an led value
leds[i] = CRGB(250, 250, 250);
// now, let's first 20 leds to the top 20 leds,
leds(NUM_LEDS / 2, NUM_LEDS - 1) = leds(NUM_LEDS / 2 - 1, 0);
FastLED.delay(random(2, 30));
}
}