Created
January 7, 2022 20:51
-
-
Save Lukelectro/d7849672654de212e27e0f82257c0f35 to your computer and use it in GitHub Desktop.
Showreel for 241 pixel nested LED-rings, based on FastLED's showreel100, includes a modified Fire2012
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 | |
// FastLED "100-lines-of-code" demo reel, showing just a few | |
// of the kinds of animation patterns you can quickly and easily | |
// compose using FastLED. | |
// | |
// This example also shows one easy way to define multiple | |
// animations patterns and have them automatically rotate. | |
// | |
// -Mark Kriegsman, December 2014 | |
// Modified for 241 pixel circulair LED-matrix / nested LED rings. Quite a few todo's left but here is a gist. | |
// - Lucas, Januari 2022 (No longer 100 lines...) | |
#if defined(FASTLED_VERSION) && (FASTLED_VERSION < 3001000) | |
#warning "Requires FastLED 3.1 or later; check github for latest code." | |
#endif | |
#define DATA_PIN 6 | |
//#define CLK_PIN 4 | |
#define LED_TYPE WS2812B | |
#define COLOR_ORDER GRB | |
#define NUM_LEDS 241 | |
CRGB leds[NUM_LEDS]; | |
#define BRIGHTNESS 64 | |
#define FRAMES_PER_SECOND 120 | |
const int numrings = 9; | |
const int ledPos[numrings][2] = { // positions of the leds for each ring. note: start with 0 | |
{ 0, 59}, //60 | |
{60, 107}, //48 | |
{108, 147}, //40 | |
{148, 179}, //32 | |
{180, 203}, //24 | |
{204, 219}, //16 | |
{220, 231}, //12 | |
{232, 239}, //8 | |
{240, 240}// 1 | |
}; | |
const int numinring[numrings] = {60, 48, 40, 32, 24, 16, 12, 8, 1}; | |
void setup() { | |
delay(3000); // 3 second delay for recovery | |
// tell FastLED about the LED strip configuration | |
FastLED.addLeds<LED_TYPE, DATA_PIN, COLOR_ORDER>(leds, NUM_LEDS).setCorrection(TypicalLEDStrip); | |
//FastLED.addLeds<LED_TYPE,DATA_PIN,CLK_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[])(); | |
//SimplePatternList gPatterns = { rainbow, rainbowWithGlitter, confetti, sinelon, juggle, bpm }; // all preexisting (Not all these work out nicely on non-lineair led things) | |
//SimplePatternList gPatterns = { ring_fire2012, ring_circles, ring_firework, ring_knight, ring_radar, ring_radar_reverse, ring_rainbow, ring_rainbow_2, ring_rgb, ring_cyclo}; // all new | |
SimplePatternList gPatterns = { ring_fire2012, ring_circles, ring_firework, ring_knight, ring_radar, ring_radar_reverse, ring_rainbow, ring_rainbow_2, ring_rgb, ring_cyclo, confetti, sinelon, juggle, bpm}; // all nice on 241 led ring thing | |
//SimplePatternList gPatterns = { ring_cyclo }; //ring_rgb}; // current test(s) | |
uint8_t gCurrentPatternNumber = 0; // Index number of which pattern is current | |
uint8_t gHue = 0; // rotating "base color" used by many of the patterns | |
void loop() | |
{ | |
// Call the current pattern function once, updating the 'leds' array | |
gPatterns[gCurrentPatternNumber](); | |
// 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); | |
// do some periodic updates | |
EVERY_N_MILLISECONDS( 20 ) { | |
gHue++; // slowly cycle the "base color" through the rainbow | |
} | |
EVERY_N_SECONDS( 20 ) { | |
nextPattern(); // change patterns periodically | |
fill_solid(leds, NUM_LEDS, CRGB::Black); // clear screen before changing to next | |
} | |
} | |
#define ARRAY_SIZE(A) (sizeof(A) / sizeof((A)[0])) | |
void nextPattern() | |
{ | |
// add one to the current pattern number, and wrap around at the end | |
gCurrentPatternNumber = (gCurrentPatternNumber + 1) % ARRAY_SIZE( gPatterns); | |
} | |
void rainbow() | |
{ | |
// FastLED's built-in rainbow generator | |
fill_rainbow( leds, NUM_LEDS, gHue, 7); // pointer to first, number to fill, initial color, delta color | |
} | |
void ring_rainbow() // rainbow around circle | |
{ | |
// FastLED's built-in rainbow generator | |
for (int i = 0; i < numrings; i++) { //i<numrings | |
fill_rainbow( &leds[ledPos[i][0]], numinring[i], gHue, 255 / numinring[i]); // pointer to first, number to fill, initial color, delta color | |
} | |
} | |
void ring_rgb() // TODO: hues are a bit too pastel-y. Still looks nice but should be a variant. And another one with pure RGB and saturated mix colors. | |
{ | |
static uint8_t j; | |
j++; | |
for (int i = 0; i < numrings; i++) { //i<numrings | |
fill_solid( &leds[ledPos[i][0]], numinring[i], CHSV(gHue + ((i + j) * 72), 200, 255)); // pointer to first, number to fill, color to fill with | |
} | |
FastLED.delay(800); //TODO: make less flashy / slwoly fade from one hue to the next instead of flashing over | |
} | |
void ring_rainbow_2() // rainbow from center to outside | |
{ | |
for (int j = 0; j < 255; j++) { | |
for (int i = 0; i < numrings; i++) { //i<numrings | |
fill_solid( &leds[ledPos[i][0]], numinring[i], CHSV(gHue + ((i + j) * 12), 255, 255)); // pointer to first, number to fill, color to fill with | |
} | |
} | |
} | |
void ring_radar() | |
{ | |
for (int j = 60; j > 0; j--) { | |
fadeToBlackBy(leds, NUM_LEDS, 64); | |
for (int ring = 0; ring < numrings; ring++) { | |
leds[ (j * numinring[ring]) / 60 + ledPos[ring][0]] = CRGB::Green; // startposition plus (j/60)-th ratio of largest ring. | |
} | |
FastLED.delay(25); | |
} | |
} | |
void ring_radar_reverse() | |
{ | |
for (int j = 0; j < 60; j++) { | |
fadeToBlackBy(leds, NUM_LEDS, 64); | |
for (int i = 0; i < numrings; i++) { //i<numrings | |
leds[ (j * numinring[i]) / 60 + ledPos[i][0]] = CRGB::Green; | |
} | |
FastLED.delay(25); | |
} | |
} | |
void ring_knight() | |
{ | |
static int j; | |
if (j >= 60) j = 0; else j++; | |
for (int i = 0; i < numrings; i++) { //i<numrings | |
fadeToBlackBy(leds, NUM_LEDS, 128); | |
leds[ledPos[i][0] + ((j * numinring[i]) / 60)] = CRGB::Red; | |
FastLED.delay(50); | |
} | |
for (int i = numrings - 2; i >= 0; i--) { | |
fadeToBlackBy(leds, NUM_LEDS, 128); | |
leds[ledPos[i][0] + (((j + 30) % 60) * numinring[i]) / 60] = CRGB::Red; | |
FastLED.delay(50); | |
} | |
for (int i = 1; i < numrings; i++) { //i<numrings | |
fadeToBlackBy(leds, NUM_LEDS, 128); | |
leds[ledPos[i][0] + (((j + 30) % 60) * numinring[i]) / 60] = CRGB::Red; | |
FastLED.delay(50); | |
} | |
for (int i = numrings - 2; i >= 0; i--) { | |
fadeToBlackBy(leds, NUM_LEDS, 128); | |
leds[ledPos[i][0] + ((j * numinring[i]) / 60) ] = CRGB::Red; | |
FastLED.delay(50); | |
} | |
} | |
void ring_firework() | |
{ | |
// firework goes up from random start position, then when it reaches the center it goes off with a random effect in random hues or silver/gold crackling. | |
// TODO: hues are often too pastel-y | |
CRGB up = CRGB::DarkGoldenrod; /* color of rocket*/ | |
byte r_hue = random8(); /*random for hue of other parts*/ | |
CHSV mid = CHSV( r_hue, 200, 255); /* middle part of effect */ | |
CHSV stream = CHSV((r_hue + 30) % 255, 200, 255); /* color of streamer part of effect */ | |
CHSV point = CHSV((r_hue - 127) % 255, 200, 255); /* color of end part of effect */ | |
CRGB crackle = CRGB::Silver; /* color of crackling */ | |
if (random8() < 127) crackle = CRGB::Gold; /* sometimes cracling is gold*/ | |
int up_pos = random8(59); // random start position from outer ring | |
byte effect, r_effect = random8(); /* pick which effect */ | |
// make some effects more likely then others: | |
if (r_effect < 127) effect = 0; else // half the time: flower | |
if (r_effect < 152) effect = 1; else // 25/255 : small/failed flower | |
if (r_effect < 177) effect = 3; else effect = 2; // 25/255 : crackling and resting 78/255: rings | |
/* go up */ | |
for (int i = 0; i < numrings; i++) { //i<numrings | |
fadeToBlackBy(leds, NUM_LEDS, 200); | |
leds[ledPos[i][0] + up_pos * numinring[i] / 60] = up; | |
FastLED.delay(20 + i * 6); // so it seems to slow down / height ilusion | |
} | |
/* go off*/ | |
switch (effect) { | |
default: | |
case 0: | |
// effect: Flower/peony/coconut/whatever the pyro's call it. 8 streamers | |
for (int i = numrings; i >= 0; i--) { //i<numrings | |
fadeToBlackBy(leds, NUM_LEDS, 50); | |
//inner few rings solid, then streamers | |
if (i > 6) { | |
fill_solid( &leds[ledPos[i][0]], numinring[i], mid); // pointer to first, number to fill, color to fill with (can be CHSV(gHue+((i+j)*12), 255, 255)) | |
} | |
else if (i != 0) | |
{ | |
for (int j = 0; j < numinring[i]; j += (numinring[i] / 7)) | |
leds[ledPos[i][0] + j] = stream; | |
} | |
else { | |
leds[ledPos[i][0] + 0] = point; | |
leds[ledPos[i][0] + 7] = point; | |
leds[ledPos[i][0] + 8 + 7] = point; | |
leds[ledPos[i][0] + 7 + 8 + 7] = point; | |
leds[ledPos[i][0] + 8 + 7 + 8 + 7] = point; | |
leds[ledPos[i][0] + 7 + 8 + 7 + 8 + 8] = point; | |
leds[ledPos[i][0] + 8 + 7 + 8 + 7 + 8 + 7] = point; | |
leds[ledPos[i][0] + 7 + 8 + 7 + 8 + 7 + 8 + 8] = point; | |
} | |
FastLED.delay(40 - i * 4); | |
} | |
break; | |
case 1: | |
// effect: smaller flower, "failed firework" | |
for (int i = numrings; i >= 0; i--) { //i<numrings | |
fadeToBlackBy(leds, NUM_LEDS, 50); | |
//inner few rings solid, then streamers | |
if (i > 6) { | |
fill_solid( &leds[ledPos[i][0]], numinring[i], mid); // pointer to first, number to fill, color to fill with (can be CHSV(gHue+((i+j)*12), 255, 255)) | |
} | |
else | |
{ | |
for (int j = 0; j < numinring[i]; j += (numinring[i] / 5)) | |
{ | |
leds[ledPos[i][0] + j] = stream; | |
if (i == 0) leds[ledPos[i][0] + j] = point; | |
} | |
} | |
FastLED.delay(40 - i * 4); | |
} | |
break; | |
case 2: | |
//effect: rings | |
for (int i = numrings - 1; i >= 0; i--) { | |
fadeToBlackBy(leds, NUM_LEDS, 50); | |
fill_solid( &leds[ledPos[i][0]], numinring[i], stream); | |
FastLED.delay(20); | |
} | |
for (int i = 0; i < 30 ; i++) { /* extinguish, else outer rings linger too long */ | |
fadeToBlackBy(leds, NUM_LEDS, 70); | |
FastLED.delay(20); | |
} | |
break; | |
case 3: | |
//cracling | |
for (int i = 0; i < 90; i++) { | |
fadeToBlackBy( leds, NUM_LEDS, 3); | |
int pos = random16(NUM_LEDS); | |
leds[pos] += crackle; | |
} | |
for (int i = 0; i < 20; i++) { | |
fadeToBlackBy( leds, NUM_LEDS, 3); | |
int pos = random16(NUM_LEDS); | |
leds[pos] += crackle; | |
FastLED.delay(1); | |
} | |
break; | |
} | |
// extinguish | |
for (int i = 0; i < 120; i++) { | |
fadeToBlackBy(leds, NUM_LEDS, 5); | |
FastLED.delay(10); | |
} | |
} | |
void ring_circles() | |
{ | |
CHSV color; | |
color = CHSV(gHue, 200, 255); | |
for (int i = 0; i < numrings; i++) { //i<numrings | |
fadeToBlackBy(leds, NUM_LEDS, 150); | |
fill_solid( &leds[ledPos[i][0]], numinring[i], color); | |
FastLED.delay(80); | |
} | |
for (int i = numrings - 2; i >= 0; i--) { | |
fadeToBlackBy(leds, NUM_LEDS, 150); | |
fill_solid( &leds[ledPos[i][0]], numinring[i], color); | |
FastLED.delay(80); | |
} | |
} | |
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 ring_cyclo() | |
{ | |
static int pos[6]; // position of dots | |
static bool dir[6] = { true,false,true,false,true,false }; // directions of movement of dot(s) | |
for (int i = 0 ; i < 6; i++) { | |
(pos[i] < numinring[i]-1) ? pos[i]++ : pos[i] = 0; /* else just increment untill full circle */ | |
} | |
fadeToBlackBy(leds, NUM_LEDS, 64); | |
for (int ring = 0; ring < 6; ring++) { | |
if (dir[ring]) { | |
leds[ pos[ring] + ledPos[ring][0]] = CHSV(gHue+ring * 32, 200, 255); | |
} else { | |
leds[ ledPos[ring][1] - pos[ring] ] = CHSV(gHue+ring * 32, 200, 255); | |
} | |
} | |
FastLED.delay(35); // todo: delay should be adapted to size of ring? Now dot travels at constant linear velocity but angular velocity increases on smaller circles. | |
} | |
void ring_fire2012() | |
{ | |
#define FLAMES 4 | |
// Array of temperature readings at each simulation cell | |
static byte heat[numrings][FLAMES]; | |
//static uint8_t heat[FLAMES][numrings]; | |
CRGB ledcolors[numrings][FLAMES]; | |
// COOLING: How much does the air cool as it rises? | |
// Less cooling = taller flames. More cooling = shorter flames. | |
// Default 50, suggested range 20-100 | |
#define COOLING 70 | |
// SPARKING: What chance (out of 255) is there that a new spark will be lit? | |
// Higher chance = more roaring fire. Lower chance = more flickery fire. | |
// Default 120, suggested range 50-200. | |
#define SPARKING 120 | |
for (int flame = 0; flame < FLAMES; flame++) { | |
// Step 1. Cool down every cell a little | |
for ( int i = 0; i < numrings; i++) { | |
heat[i][flame] = qsub8( heat[i][flame], random8(0, ((COOLING * 10) / numrings) + 2)); | |
} | |
// Step 2. Heat from each cell drifts 'up' and diffuses a little | |
for ( int k = numrings - 1; k >= 2; k--) { | |
heat[k][flame] = (heat[k - 1][flame] + heat[k - 2][flame] + heat[k - 2][flame] ) / 3; | |
} | |
// Step 3. Randomly ignite new 'sparks' of heat at/near the bottom | |
if ( random8() < SPARKING ) { | |
int y = random8(2); | |
heat[y][flame] = qadd8( heat[y][flame], random8(160, 255) ); | |
} | |
// Step 4. Map from heat cells to LED colors | |
for ( int j = 0; j < numrings; j++) { | |
ledcolors[j][flame] = HeatColor( heat[j][flame]); | |
} | |
} | |
//step 5: display on LED rings | |
for (int ring = 0; ring < numrings; ring++) { | |
/* Flame 0 */ | |
leds[ledPos[ring][0]] = ledcolors[ring][0]; | |
if (ring < 7) { // wider flame at base | |
leds[ledPos[ring][0] + 1] = ledcolors[ring + 1][0]; | |
leds[(ledPos[ring][1])] = ledcolors[ring + 1][0]; | |
} | |
if (ring < 5) { | |
leds[ledPos[ring][0] + 2] = ledcolors[ring + 2][0]; | |
leds[(ledPos[ring][1] - 1)] = ledcolors[ring + 3][0]; | |
} | |
if (ring < 3) { | |
leds[ledPos[ring][0] + 3] = ledcolors[ring + 3][0]; | |
leds[(ledPos[ring][1] - 2)] = ledcolors[ring + 4][0]; | |
} | |
/* Flame 1 */ | |
leds[ledPos[ring][0] + numinring[ring] / 4] = ledcolors[ring][1]; | |
if (ring < 7) { // wider flame at base | |
leds[ledPos[ring][0] + numinring[ring] / 4 + 1] = ledcolors[ring + 1][1]; | |
leds[(ledPos[ring][0]) + numinring[ring] / 4 - 1] = ledcolors[ring + 1][1]; | |
} | |
if (ring < 5) { | |
leds[ledPos[ring][0] + numinring[ring] / 4 + 2] = ledcolors[ring + 2][1]; | |
leds[(ledPos[ring][0] + numinring[ring] / 4 - 2)] = ledcolors[ring + 3][1]; | |
} | |
if (ring < 3) { | |
leds[ledPos[ring][0] + numinring[ring] / 4 + 3] = ledcolors[ring + 3][1]; | |
leds[(ledPos[ring][0] + numinring[ring] / 4 - 3)] = ledcolors[ring + 4][1]; | |
} | |
/* Flame 2 */ | |
leds[ledPos[ring][0] + numinring[ring] / 2] = ledcolors[ring][1]; | |
if (ring < 7) { // wider flame at base | |
leds[ledPos[ring][0] + numinring[ring] / 2 + 1] = ledcolors[ring + 1][1]; | |
leds[(ledPos[ring][0]) + numinring[ring] / 2 - 1] = ledcolors[ring + 1][1]; | |
} | |
if (ring < 5) { | |
leds[ledPos[ring][0] + numinring[ring] / 2 + 2] = ledcolors[ring + 2][1]; | |
leds[(ledPos[ring][0] + numinring[ring] / 2 - 2)] = ledcolors[ring + 3][1]; | |
} | |
if (ring < 3) { | |
leds[ledPos[ring][0] + numinring[ring] / 2 + 3] = ledcolors[ring + 3][1]; | |
leds[(ledPos[ring][0] + numinring[ring] / 2 - 3)] = ledcolors[ring + 4][1]; | |
} | |
/* Flame 3 */ | |
leds[ledPos[ring][1] - numinring[ring] / 4] = ledcolors[ring][1]; | |
if (ring < 7) { // wider flame at base | |
leds[ledPos[ring][1] - numinring[ring] / 4 + 1] = ledcolors[ring + 1][1]; | |
leds[(ledPos[ring][1]) - numinring[ring] / 4 - 1] = ledcolors[ring + 1][1]; | |
} | |
if (ring < 5) { | |
leds[ledPos[ring][1] - numinring[ring] / 4 + 2] = ledcolors[ring + 2][1]; | |
leds[(ledPos[ring][1] - numinring[ring] / 4 - 2)] = ledcolors[ring + 3][1]; | |
} | |
if (ring < 3) { | |
leds[ledPos[ring][1] - numinring[ring] / 4 + 3] = ledcolors[ring + 3][1]; | |
leds[(ledPos[ring][1] - numinring[ring] / 4 - 3)] = ledcolors[ring + 4][1]; | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment