Created
August 19, 2021 17:04
-
-
Save sensboston/6d70266e9f36068a5eafe64aa0443912 to your computer and use it in GitHub Desktop.
TalkiePi_LED_strip.ino
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <FastLED.h> | |
FASTLED_USING_NAMESPACE | |
//#define _DEBUG | |
#define ARRAY_SIZE(A) (sizeof(A) / sizeof((A)[0])) | |
#define DATA_PIN 3 | |
#define LED_TYPE WS2811 | |
#define COLOR_ORDER GRB | |
#define NUM_LEDS 64 | |
#define BRIGHTNESS 96 | |
#define FRAMES_PER_SECOND 120 | |
#define BUTTON_PIN 10 | |
bool isPressed = false; | |
bool buttonState = true; | |
CRGB leds[NUM_LEDS]; | |
void setup() | |
{ | |
Serial.begin(115200); | |
pinMode(BUTTON_PIN, INPUT); | |
// tell FastLED about the LED strip configuration | |
FastLED.addLeds<LED_TYPE,DATA_PIN,COLOR_ORDER>(leds, NUM_LEDS).setCorrection(TypicalLEDStrip); | |
// set master brightness control | |
FastLED.setBrightness(BRIGHTNESS); | |
} | |
// List of patterns to cycle through. Each is defined as a separate function below. | |
typedef void (*SimplePatternList[])(); | |
#ifdef ALL_EFFECTS | |
SimplePatternList gPatterns = { | |
rainbow, // 0 | |
rainbowWithGlitter, // 1 | |
confetti, // 2 | |
sinelon, // 3 | |
juggle, // 4 | |
bpm, // 5 | |
rainbowSripe, // 6 // uses palette | |
rainbowSripeBlend, // 7 | |
purpleAndGreen, // 8 | |
totallyRandom, // 9 | |
blackAndWhiteStriped, // 10 | |
blackAndWhiteStripedBlend, // 11 | |
clouds, // 12 | |
party, // 13 | |
redWhiteBlue, // 14 | |
redWhiteBlueBlend, // 15 | |
discostrobe, // 16 // new one | |
}; | |
#else | |
SimplePatternList gPatterns = { | |
confetti, // 2 | |
juggle, // 4 | |
rainbowSripeBlend, // 7 // uses palette | |
clouds, // 12 | |
redWhiteBlueBlend, // 15 | |
}; | |
#endif | |
uint8_t gCurrentPatternNumber = 0; // Index number of which pattern is current | |
uint8_t gHue = 0; // rotating "base color" used by many of the patterns | |
uint8_t startIndex = 0; | |
CRGBPalette16 currentPalette; | |
TBlendType currentBlending; | |
void loop() | |
{ | |
isPressed = (bool) digitalRead(BUTTON_PIN); | |
if (isPressed != buttonState) | |
{ | |
#ifdef _DEBUG | |
Serial.println(isPressed ? "Button pressed" : "Button released"); | |
#endif | |
buttonState = isPressed; | |
if (isPressed) | |
{ | |
nextPattern(); | |
} | |
else | |
{ | |
FastLED.clear(); | |
FastLED.show(); | |
} | |
} | |
if (isPressed) | |
{ | |
// Call the current pattern function once, updating the 'leds' array | |
gPatterns[gCurrentPatternNumber](); | |
#ifdef ALL_EFFECTS | |
if (gCurrentPatternNumber > 5 && gCurrentPatternNumber < 16) | |
#else | |
if (gCurrentPatternNumber > 1) | |
#endif | |
{ | |
startIndex++; | |
FillLEDsFromPaletteColors(startIndex); | |
} | |
else if (gCurrentPatternNumber == 0) startIndex = 0; | |
// send the 'leds' array out to the actual LED strip | |
FastLED.show(); | |
// insert a delay to keep the framerate modest | |
FastLED.delay(1000/FRAMES_PER_SECOND); | |
// slowly cycle the "base color" through the rainbow | |
EVERY_N_MILLISECONDS( 20 ) { gHue++; } | |
} | |
} | |
void nextPattern() | |
{ | |
// add one to the current pattern number, and wrap around at the end | |
gCurrentPatternNumber = (gCurrentPatternNumber + 1) % ARRAY_SIZE( gPatterns); | |
#ifdef _DEBUG | |
Serial.print("Current pattern: "); Serial.println(gCurrentPatternNumber); | |
#endif | |
} | |
void rainbow() | |
{ | |
// FastLED's built-in rainbow generator | |
fill_rainbow( leds, NUM_LEDS, gHue, 7); | |
} | |
void rainbowWithGlitter() | |
{ | |
// built-in FastLED rainbow, plus some random sparkly glitter | |
rainbow(); | |
addGlitter(80); | |
} | |
void addGlitter( fract8 chanceOfGlitter) | |
{ | |
if( random8() < chanceOfGlitter) { | |
leds[ random16(NUM_LEDS) ] += CRGB::White; | |
} | |
} | |
void confetti() | |
{ | |
// random colored speckles that blink in and fade smoothly | |
fadeToBlackBy( leds, NUM_LEDS, 10); | |
int pos = random16(NUM_LEDS); | |
leds[pos] += CHSV( gHue + random8(64), 200, 255); | |
} | |
void sinelon() | |
{ | |
// a colored dot sweeping back and forth, with fading trails | |
fadeToBlackBy( leds, NUM_LEDS, 20); | |
int pos = beatsin16( 13, 0, NUM_LEDS-1 ); | |
leds[pos] += CHSV( gHue, 255, 192); | |
} | |
void bpm() | |
{ | |
// 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_LEDS; i++) { //9948 | |
leds[i] = ColorFromPalette(palette, gHue+(i*2), beat-gHue+(i*10)); | |
} | |
} | |
void juggle() { | |
// eight colored dots, weaving in and out of sync with each other | |
fadeToBlackBy( leds, NUM_LEDS, 20); | |
byte dothue = 0; | |
for( int i = 0; i < 8; i++) { | |
leds[beatsin16( i+7, 0, NUM_LEDS-1 )] |= CHSV(dothue, 200, 255); | |
dothue += 32; | |
} | |
} | |
void rainbowSripe() | |
{ | |
currentPalette = RainbowStripeColors_p; | |
currentBlending = NOBLEND; | |
} | |
void rainbowSripeBlend() | |
{ | |
currentPalette = RainbowStripeColors_p; | |
currentBlending = LINEARBLEND; | |
} | |
void purpleAndGreen() | |
{ | |
CRGB purple = CHSV(HUE_PURPLE, 255, 255); | |
CRGB green = CHSV(HUE_GREEN, 255, 255); | |
CRGB black = CRGB::Black; | |
currentPalette = CRGBPalette16(green, green, black, black, | |
purple, purple, black, black, | |
green, green, black, black, | |
purple, purple, black, black); | |
currentBlending = LINEARBLEND; | |
} | |
void totallyRandom() | |
{ | |
for (int i = 0; i < 16; i++) { | |
currentPalette[i] = CHSV(random8(), 255, random8()); | |
} | |
currentBlending = LINEARBLEND; | |
} | |
void blackAndWhiteStriped() | |
{ | |
// 'black out' all 16 palette entries... | |
fill_solid(currentPalette, 16, CRGB::Black); | |
// and set every fourth one to white. | |
currentPalette[0] = CRGB::White; | |
currentPalette[4] = CRGB::White; | |
currentPalette[8] = CRGB::White; | |
currentPalette[12] = CRGB::White; | |
currentBlending = NOBLEND; | |
} | |
void blackAndWhiteStripedBlend() | |
{ | |
blackAndWhiteStriped(); | |
currentBlending = LINEARBLEND; | |
} | |
void clouds() | |
{ | |
currentPalette = CloudColors_p; | |
currentBlending = LINEARBLEND; | |
} | |
void party() | |
{ | |
currentPalette = PartyColors_p; | |
currentBlending = LINEARBLEND; | |
} | |
const TProgmemPalette16 myRedWhiteBluePalette_p PROGMEM = | |
{ | |
CRGB::Red, | |
CRGB::Gray, // 'white' is too bright compared to red and blue | |
CRGB::Blue, | |
CRGB::Black, | |
CRGB::Red, | |
CRGB::Gray, | |
CRGB::Blue, | |
CRGB::Black, | |
CRGB::Red, | |
CRGB::Red, | |
CRGB::Gray, | |
CRGB::Gray, | |
CRGB::Blue, | |
CRGB::Blue, | |
CRGB::Black, | |
CRGB::Black | |
}; | |
void redWhiteBlue() | |
{ | |
currentPalette = myRedWhiteBluePalette_p; | |
currentBlending = NOBLEND; | |
} | |
void redWhiteBlueBlend() | |
{ | |
currentPalette = myRedWhiteBluePalette_p; | |
currentBlending = LINEARBLEND; | |
} | |
void FillLEDsFromPaletteColors(uint8_t colorIndex) | |
{ | |
for (int i = 0; i < NUM_LEDS; i++) | |
{ | |
leds[i] = ColorFromPalette(currentPalette, colorIndex, BRIGHTNESS, currentBlending); | |
colorIndex += 3; | |
} | |
} | |
// New stuff from Mark | |
#define ZOOMING_BEATS_PER_MINUTE 61 | |
// define some shorthands for the Halloween colors | |
#define PURP 0x6611FF | |
#define ORAN 0xFF6600 | |
#define GREN 0x00FF11 | |
#define WHIT 0xCCCCCC | |
// set up a new 16-color palette with the Halloween colors | |
const CRGBPalette16 HalloweenColors_p | |
( | |
PURP, PURP, PURP, PURP, | |
ORAN, ORAN, ORAN, ORAN, | |
PURP, PURP, PURP, PURP, | |
GREN, GREN, GREN, WHIT | |
); | |
// use the Halloween color palette | |
CRGBPalette16 gCurrentPalette( HalloweenColors_p ); | |
#define BLENDING NOBLEND | |
void discostrobe() | |
{ | |
// First, we black out all the LEDs | |
fill_solid( leds, NUM_LEDS, CRGB::Black); | |
// To achive the strobe effect, we actually only draw lit pixels | |
// every Nth frame (e.g. every 4th frame). | |
// sStrobePhase is a counter that runs from zero to kStrobeCycleLength-1, | |
// and then resets to zero. | |
const uint8_t kStrobeCycleLength = 4; // light every Nth frame | |
static uint8_t sStrobePhase = 0; | |
sStrobePhase = sStrobePhase + 1; | |
if( sStrobePhase >= kStrobeCycleLength ) { | |
sStrobePhase = 0; | |
} | |
// We only draw lit pixels when we're in strobe phase zero; | |
// in all the other phases we leave the LEDs all black. | |
if( sStrobePhase == 0 ) { | |
// The dash spacing cycles from 4 to 9 and back, 8x/min (about every 7.5 sec) | |
uint8_t dashperiod= beatsin8( 8/*cycles per minute*/, 4,10); | |
// The width of the dashes is a fraction of the dashperiod, with a minimum of one pixel | |
uint8_t dashwidth = (dashperiod / 4) + 1; | |
// The distance that the dashes move each cycles varies | |
// between 1 pixel/cycle and half-the-dashperiod/cycle. | |
// At the maximum speed, it's impossible to visually distinguish | |
// whether the dashes are moving left or right, and the code takes | |
// advantage of that moment to reverse the direction of the dashes. | |
// So it looks like they're speeding up faster and faster to the | |
// right, and then they start slowing down, but as they do it becomes | |
// visible that they're no longer moving right; they've been | |
// moving left. Easier to see than t o explain. | |
// | |
// The dashes zoom back and forth at a speed that 'goes well' with | |
// most dance music, a little faster than 120 Beats Per Minute. You | |
// can adjust this for faster or slower 'zooming' back and forth. | |
uint8_t zoomBPM = ZOOMING_BEATS_PER_MINUTE; | |
int8_t dashmotionspeed = beatsin8( (zoomBPM /2), 1,dashperiod); | |
// This is where we reverse the direction under cover of high speed | |
// visual aliasing. | |
if( dashmotionspeed >= (dashperiod/2)) { | |
dashmotionspeed = 0 - (dashperiod - dashmotionspeed ); | |
} | |
// The hueShift controls how much the hue of each dash varies from | |
// the adjacent dash. If hueShift is zero, all the dashes are the | |
// same color. If hueShift is 128, alterating dashes will be two | |
// different colors. And if hueShift is range of 10..40, the | |
// dashes will make rainbows. | |
// Initially, I just had hueShift cycle from 0..130 using beatsin8. | |
// It looked great with very low values, and with high values, but | |
// a bit 'busy' in the middle, which I didnt like. | |
// uint8_t hueShift = beatsin8(2,0,130); | |
// | |
// So instead I layered in a bunch of 'cubic easings' | |
// (see http://easings.net/#easeInOutCubic ) | |
// so that the resultant wave cycle spends a great deal of time | |
// "at the bottom" (solid color dashes), and at the top ("two | |
// color stripes"), and makes quick transitions between them. | |
uint8_t cycle = beat8(2); // two cycles per minute | |
uint8_t easedcycle = ease8InOutCubic( ease8InOutCubic( cycle)); | |
uint8_t wavecycle = cubicwave8( easedcycle); | |
uint8_t hueShift = scale8( wavecycle,130); | |
// Each frame of the animation can be repeated multiple times. | |
// This slows down the apparent motion, and gives a more static | |
// strobe effect. After experimentation, I set the default to 1. | |
uint8_t strobesPerPosition = 1; // try 1..4 | |
// Now that all the parameters for this frame are calculated, | |
// we call the 'worker' function that does the next part of the work. | |
discoWorker( dashperiod, dashwidth, dashmotionspeed, strobesPerPosition, hueShift); | |
} | |
} | |
// discoWorker updates the positions of the dashes, and calls the draw function | |
// | |
void discoWorker( | |
uint8_t dashperiod, uint8_t dashwidth, int8_t dashmotionspeed, | |
uint8_t stroberepeats, | |
uint8_t huedelta) | |
{ | |
static uint8_t sRepeatCounter = 0; | |
static int8_t sStartPosition = 0; | |
static uint8_t sStartHue = 0; | |
// Always keep the hue shifting a little | |
sStartHue += 1; | |
// Increment the strobe repeat counter, and | |
// move the dash starting position when needed. | |
sRepeatCounter = sRepeatCounter + 1; | |
if( sRepeatCounter>= stroberepeats) { | |
sRepeatCounter = 0; | |
sStartPosition = sStartPosition + dashmotionspeed; | |
// These adjustments take care of making sure that the | |
// starting hue is adjusted to keep the apparent color of | |
// each dash the same, even when the state position wraps around. | |
if( sStartPosition >= dashperiod ) { | |
while( sStartPosition >= dashperiod) { sStartPosition -= dashperiod; } | |
sStartHue -= huedelta; | |
} else if( sStartPosition < 0) { | |
while( sStartPosition < 0) { sStartPosition += dashperiod; } | |
sStartHue += huedelta; | |
} | |
} | |
// draw dashes with full brightness (value), and somewhat | |
// desaturated (whitened) so that the LEDs actually throw more light. | |
const uint8_t kSaturation = 208; | |
const uint8_t kValue = 255; | |
// call the function that actually just draws the dashes now | |
drawRainbowDashes( sStartPosition, NUM_LEDS-1, | |
dashperiod, dashwidth, | |
sStartHue, huedelta, | |
kSaturation, kValue); | |
} | |
// drawRainbowDashes - draw rainbow-colored 'dashes' of light along the led strip: | |
// starting from 'startpos', up to and including 'lastpos' | |
// with a given 'period' and 'width' | |
// starting from a given hue, which changes for each successive dash by a 'huedelta' | |
// at a given saturation and value. | |
// | |
// period = 5, width = 2 would be _ _ _ X X _ _ _ Y Y _ _ _ Z Z _ _ _ A A _ _ _ | |
// \-------/ \-/ | |
// period 5 width 2 | |
// | |
static void drawRainbowDashes( | |
uint8_t startpos, uint16_t lastpos, uint8_t period, uint8_t width, | |
uint8_t huestart, uint8_t huedelta, uint8_t saturation, uint8_t value) | |
{ | |
uint8_t hue = huestart; | |
for( uint16_t i = startpos; i <= lastpos; i += period) { | |
// Switched from HSV color wheel to color palette | |
// Was: CRGB color = CHSV( hue, saturation, value); | |
CRGB color = ColorFromPalette( gCurrentPalette, hue, value, BLENDING); | |
// draw one dash | |
uint16_t pos = i; | |
for( uint8_t w = 0; w < width; w++) { | |
leds[ pos ] = color; | |
pos++; | |
if( pos >= NUM_LEDS) { | |
break; | |
} | |
} | |
hue += huedelta; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment