#include <FastLED.h>
#include "ButtonConfig.h"
#include "Debounce.h"
#include <EEPROM.h>
#define patternpos 0 //indicate which address to save EEPROM data too. Set here to allow rotation of saved storage address for write cycle variation
#define colorpos 1
//*********Pin Numbers**************
#define LED_PIN1 6 // //Dataoutput pin to LED strips - A0 on custom PCB
#define ButtonPin 2 // button to connect push button to. Use Input_Pullup and connect other end to ground.
// **********Button Info*************
// Byte using a 1 to represent button has a pull-up so is normally high,
// A 0 means buton has pull-down or is not in use
// Button order as defined in ButtonsUsed term
#define ButtonsUsed 0b00000001
Debouncer btns(ButtonsUsed);
#define PushButton 0x01 //button masks
#define buttonchecktime 3 //check button every 2-5 ms with needing 8 consecutive in Config that gives bounce time of 16-40 ms
// *********LED strip Info***********
#define LED_TYPE WS2812
#define COLOR_ORDER GRB
#define VOLTS 5
#define MAX_MA 1500
const uint16_t NUM_LEDS=100;
CRGBArray <NUM_LEDS> leds;
CRGBPalette16 gCurrentPalette;
CRGBPalette16 gTargetPalette;
//********Adjustable Settings************
#define TWINKLE_SPEED 5 // Overall twinkle speed. // 0 (VERY slow) to 8 (VERY fast). // 4, 5, and 6 are recommended, default is 4.
#define TWINKLE_DENSITY 5 // Overall twinkle density.// 0 (NONE lit) to 8 (ALL lit at once). // Default is 5.
#define CYCLE_SPEED 10//How often in seconds to change colors during 'cycle' program
#define SECONDS_PER_PALETTE 100 // How often to change color palettes.
#define MINUTES_PER_PATTERN 3
#define BRIGHTNESS 80
#define FRAMES_PER_SECOND 120
#define CHASE_CYCLES 5
#define CHASE_CHANGE_RATE 4
#define MIN_CHASE_SPEED 375//ms delay for chase. Higher = slower chase speed
uint8_t CHASE_ADJUST = MIN_CHASE_SPEED/CHASE_CYCLES;
const fract8 GlitterChance=60; //0-255 Higher=increase chance of glitter
const uint8_t BlendChangeRate=12; //1-48, 24 meaningful - How fast palette blend changes are done
uint8_t CurrentPattern;
uint8_t gHue = 0; // "base color" used by some of the patterns
bool SolidOn=false;
bool LightsOn=true;
CRGB RotateSolids[] = {CRGB::FairyLight, CRGB::Red, CRGB::Green, ((CRGB::FairyLight & 0xFEFEFE) / 2), CRGB::White};
const uint8_t NumToRotate = sizeof(RotateSolids) / sizeof(RotateSolids[0]);
uint8_t colorindex;
#define Program(x) void (x)(CRGBSet& ledsX, uint16_t NUM_LEDSX)
Program(cycle);
Program(twinkle);
Program(juggle);
Program(confetti);
Program(rainbowWithGlitter);
Program(theaterChase);
typedef Program(*PatternList[]);
PatternList gPatterns = {theaterChase,juggle, confetti, cycle};
const uint8_t numberOfPatterns = sizeof(gPatterns) / sizeof(gPatterns[0]);
void setup() {
Serial.begin(9600);
Serial.print("Pattern Number= "); Serial.println(numberOfPatterns );
pinMode(ButtonPin, INPUT_PULLUP);
// tell FastLED about the LED strip configuration
FastLED.addLeds<LED_TYPE,LED_PIN1,COLOR_ORDER>(leds, NUM_LEDS).setCorrection(TypicalLEDStrip);
FastLED.setMaxPowerInVoltsAndMilliamps( VOLTS, MAX_MA);
FastLED.setBrightness(BRIGHTNESS); // set master brightness control
FastLED.setTemperature(Tungsten40W);
CurrentPattern = EEPROM[patternpos];
if (CurrentPattern == numberOfPatterns) {
SolidOn=true;
colorindex=EEPROM[colorpos];
Rotate();
}
else if (CurrentPattern > numberOfPatterns){
CurrentPattern=0;
}
chooseNextColorPalette(gCurrentPalette);
gTargetPalette=gCurrentPalette;
}
void loop(){
EVERY_N_MILLISECONDS_I(buttoncheck, buttonchecktime){
uint32_t ms = millis();
btns.ButtonProcess(digitalRead(ButtonPin),ms);
if (btns.ButtonShortPress(PushButton)) {
nextPattern();
Serial.println(CurrentPattern);
}
if (btns.ButtonHeld(PushButton)){
(LightsOn) ? FastLED.clear(true) : Rotate(); //If On, then Turn Lights Off, otherwise turn on Solid
LightsOn = !LightsOn; //Toggle Lights each time ButtonHeld
Serial.println(LightsOn);
}
if (btns.ButtonDblClick(PushButton) && LightsOn && SolidOn){
Rotate();
}
}
if (LightsOn && !SolidOn){
EVERY_N_MILLISECONDS_I(loop, 1000/FRAMES_PER_SECOND) {// insert a delay to keep the framerate modest
gPatterns[CurrentPattern](leds, NUM_LEDS);// Call the current pattern function once, updating the 'leds' array
FastLED.show(); // send the 'leds' array out to the actual LED strip
}
EVERY_N_SECONDS_I(palette, SECONDS_PER_PALETTE ) { chooseNextColorPalette( gTargetPalette );}
EVERY_N_MILLISECONDS_I(blend, 10 ) {nblendPaletteTowardPalette( gCurrentPalette, gTargetPalette, BlendChangeRate);}
//EVERY_N_SECONDS_I(patternchange, MINUTES_PER_PATTERN){CurrentPattern=random8(0,numberOfPatterns); Serial.println(CurrentPattern);}
}
}
void Rotate(){
FastLED.showColor(RotateSolids[colorindex]);
EEPROM[colorpos]=colorindex; //Save current index only when color printed.
colorindex=addmod8(colorindex,1,NumToRotate); //advance to next index done after showColor() for next change to allow initial setting of color index from EEPROM
}
void nextPattern(){
CurrentPattern = addmod8(CurrentPattern,1,numberOfPatterns+1);
EEPROM[patternpos]= CurrentPattern;
if (CurrentPattern == numberOfPatterns){ //We should now go to solid pattern
SolidOn = true;
Rotate();
}
else {SolidOn=false;}
}
void rainbow(CRGBSet& ledsX, uint16_t NUM_LEDSX)
{
// FastLED's built-in rainbow generator
fill_rainbow( ledsX, NUM_LEDSX, gHue, 7);
}
void rainbowWithGlitter(CRGBSet& ledsX, uint16_t NUM_LEDSX)
{
// built-in FastLED rainbow, plus some random sparkly glitter
rainbow(ledsX, NUM_LEDSX);
addGlitter(GlitterChance, ledsX, NUM_LEDSX);
}
void addGlitter(fract8 chanceOfGlitter, CRGBSet& ledsX, uint16_t NUM_LEDSX)
{
if( random8() < chanceOfGlitter) {
ledsX[ random16(NUM_LEDSX) ] = CRGB::White;
}
}
void confetti(CRGBSet& ledsX, uint16_t NUM_LEDSX)
{
// random colored speckles that blink in and fade smoothly
fadeToBlackBy( ledsX, NUM_LEDSX, 10);
int pos = random16(NUM_LEDSX);
ledsX[pos] = gCurrentPalette[random8(16)];
}
void sinelon(CRGBSet& ledsX, uint16_t NUM_LEDSX)
{
// a colored dot sweeping back and forth, with fading trails
fadeToBlackBy( ledsX, NUM_LEDSX, 10);
int pos = beatsin16( 13, 0, NUM_LEDSX-1 );
int color = random8(15);
ledsX[pos] = ColorFromPalette(gCurrentPalette, color, BRIGHTNESS, NOBLEND);
}
void bpm(CRGBSet& ledsX, uint16_t NUM_LEDSX)
{
// colored stripes pulsing at a defined Beats-Per-Minute (BPM)
uint8_t BeatsPerMinute = 62;
CRGBPalette16 palette = PartyColors_p;
uint8_t beat = beatsin8( BeatsPerMinute, 64, 255);
for( int i = 0; i < NUM_LEDSX; i++) { //9948
ledsX[i] = ColorFromPalette(palette, gHue+(i*2), beat-gHue+(i*10));
}
}
void juggle(CRGBSet& ledsX, uint16_t NUM_LEDSX)
{ // colored dots, weaving in and out of sync with each other
static uint8_t juggle_num=random8(3,12);
EVERY_N_SECONDS_I(jugglechange, 6){juggle_num=random8(3,12);}
fadeToBlackBy( ledsX, NUM_LEDSX, 20);
uint8_t dothue = 0;
for( int i = 0; i < juggle_num; i++) {
ledsX[beatsin16( i+6, 0, NUM_LEDSX-1 )] |= CHSV(dothue, 200, 255);
dothue += 256/juggle_num;
}
}
void theaterChase(CRGBSet& ledsX, uint16_t NUM_LEDSX) {
static uint16_t CHASE_SPEED=MIN_CHASE_SPEED;
static uint8_t index;
static uint8_t color;
uint32_t ms = millis();
static uint32_t chasetime=0;
EVERY_N_SECONDS_I(chasecolor, 10){color = TestNextColor(color);Serial.println(color);}
EVERY_N_SECONDS_I(chasespeed, 2){CHASE_SPEED=submod16(CHASE_SPEED,CHASE_ADJUST,MIN_CHASE_SPEED);}
if (ms-chasetime >= CHASE_SPEED){
index = addmod8(index, 1, 3);
for (uint16_t i = 0; i < NUM_LEDSX; i += 3) { //third pixel on
ledsX[i+index]= gCurrentPalette[color]; // turn every third pixel on
ledsX[i +index+1] = CRGB::Black;
if (!(index==0 && i==0)){ledsX[i+index-1] = CRGB::Black;} // turn every third pixel off
chasetime=ms;
}
}
}
void cycle (CRGBSet& ledsX, uint16_t NUM_LEDSX) {
static uint8_t count=1;
static bool countup=true;
static uint8_t color;
EVERY_N_SECONDS_I(CYCLECOLOR, 5){ color=random8(16);}
fadeToBlackBy( ledsX, NUM_LEDSX, 15);//Fade previous LEDs
ledsX[count] = gCurrentPalette[color]; //set next LED to hue
if (count ==0 || count==NUM_LEDSX){countup=!countup;}
(countup) ? count++ : count--;
}
uint8_t TestNextColor(uint8_t color)
{//check if next color in palette matches and keep advancing to ensure color changes every time
uint8_t test=addmod8(color,1,16);//advance test by 1 to check next color in palette
CRGB nowcolor = gCurrentPalette[color];
CRGB nextcolor = gCurrentPalette[test];
while(nowcolor==nextcolor ){
test=addmod8(test,1,16);
nextcolor = gCurrentPalette[test];
}
return color=test;
}
uint16_t submod16(uint16_t a, uint16_t b, uint16_t m){
a -= b;
while( a <= 0) a += m;
return a;
}