So it has been a hot minute since I have made a post – been dealing with the ongoing madness that is trying to find a job.
The code for this project was based on the FastLED library, and modified from the demoreel example. Demoreel is a cool little snippet of Arduino C that really showcases how versatile the FastLED library really is. The only problem is that it is meant for a string of LEDs. As you can see from the pictures, Altared Space is more appropriately described as a 2d array of LEDs:
Another important point to make is that I am not really a coder. I’ve managed to pick up some C, as well as HTML and CSS, and some useful scraps of Javascript and Python, but my degree is chemistry, not computer science, and as such, my coding skills are not really the sharpest (although they get increasingly better with every project I do!). So after about 13 straight hours of breakthroughs, setbacks, frustration, and eyestrain, I managed to come up with this:
#include
#define num_Led_total 600
#define num_Led 50 //number of LED per strand
#define num_Strand_long 12 //number of strands
#define FRAMES_PER_SECOND 60
#define COOLING 75
#define SPARKING 95
bool gReverseDirection = false;
CRGB south [num_Strand_long * num_Led]; //define LED array
CRGBPalette16 gPal;
CRGBPalette16 gPal1;
volatile int a, b, c, d, e, f, g, h, k, l;
uint8_t gHue = 0;
uint8_t gBrightness = 128;
uint8_t gCurrentPatternNumber = 0;
uint8_t m = 0;
void rainbow() {
for (a = 0; a < 12; a++) {
fill_rainbow(south, num_Led, gHue, 5);
FastLED[a].showLeds();
}
}
void water() {
for (a = 0; a < 12; a++) {
fill_solid(south, num_Led, CRGB::Black);
// Array of temperature readings at each simulation cell
static byte heat[num_Led / 2][12];
// Step 1. Cool down every cell a little
for ( int i = 0; i = 2; k–) {
heat[k][a] = (heat[k – 1][a] + heat[k – 2][a] + heat[k – 2][a] ) / 3;
}
// Step 3. Randomly ignite new ‘sparks’ of heat near the bottom
if ( random8() < SPARKING ) {
int y = random8(7);
heat[y][a] = qadd8( heat[y][a], random8(160, 255) );
}
// Step 4. Map from heat cells to LED colors
for ( int j = 0; j < num_Led / 2; j++) {
// Scale the heat value from 0-255 down to 0-240
// for best results with color palettes.
byte colorindex = scale8( heat[j][a], 240);
CRGB color = ColorFromPalette( gPal1, colorindex);
int pixelnumber;
if ( gReverseDirection ) {
pixelnumber = (num_Led / 2 – 1) – j;
} else {
pixelnumber = j;
}
south[pixelnumber] = color;
south[num_Led – pixelnumber] = color;
}
FastLED[a].showLeds();
delay(5);
}
}
void Fire() {
for (a = 0; a < 12; a++) {
fill_solid(south, num_Led, CRGB::Black);
// Array of temperature readings at each simulation cell
static byte heat[num_Led / 2][12];
// Step 1. Cool down every cell a little
for ( int i = 0; i = 2; k–) {
heat[k][a] = (heat[k – 1][a] + heat[k – 2][a] + heat[k – 2][a] ) / 3;
}
// Step 3. Randomly ignite new ‘sparks’ of heat near the bottom
if ( random8() < SPARKING ) {
int y = random8(7);
heat[y][a] = qadd8( heat[y][a], random8(160, 255) );
}
// Step 4. Map from heat cells to LED colors
for ( int j = 0; j < num_Led / 2; j++) {
// Scale the heat value from 0-255 down to 0-240
// for best results with color palettes.
byte colorindex = scale8( heat[j][a], 240);
CRGB color = ColorFromPalette( gPal, colorindex);
int pixelnumber;
if ( gReverseDirection ) {
pixelnumber = (num_Led / 2 – 1) – j;
} else {
pixelnumber = j;
}
south[pixelnumber] = color;
south[num_Led – pixelnumber] = color;
}
FastLED[a].showLeds();
delay(5);
}
}
void rainbowWaterfall() {
for (a = 0; a < 12; a++) {
m = m + a * 3;
fill_rainbow(south, num_Led, m, 5);
FastLED[a].showLeds();
}
}
void addGlitter( fract8 chanceOfGlitter) {
for (a = 0; a < 12; a++) {
if ( random8() < chanceOfGlitter) {
b = random16(num_Led – a);
south[ b + random(a) ] += CRGB::White;
}
FastLED[a].showLeds();
}
}
void fillAll() {
for (a = 0; a < 12; a++) {
fill_solid(south, num_Led, CHSV(gHue, 255, 255));
FastLED[a].showLeds();
delay(75);
}
delay(5000);
}
void dissolve() {
for (int i = 0; i < 6; i++) {
for (a = 0; a < 12; a++) {
for (int j = 0; j = 1; c /= 1.2) {
for (a = 0; a < 12; a++) {
FastLED[a].showLeds(c);
}
}
}
void filldissolve() {
fillAll();
dissolve();
}
void rainbowSparkle () {
rainbow();
addGlitter(20);
}
void solidRainbow() {
for (a = 0; a < 12; a++) {
fill_solid(south, num_Led, CHSV(gHue, 255, 255));
FastLED[a].showLeds();
}
}
void solidRainGlit() {
solidRainbow();
addGlitter(20);
}
void rainbowScroll() {
for (a = 0; a < 12; a++) {
m = m + a;
fill_solid(south, num_Led, CHSV(m, 255, 255));
FastLED[a].showLeds();
}
}
void sinelon() {
for (a = 0; a < 12; a++) {
fadeToBlackBy( south, num_Led, 20);
int pos = beatsin16(13, 0, num_Led);
south[pos] += CHSV( gHue, 255, 192);
FastLED[a].showLeds();
}
}
void juggle() {
for (a = 0; a < 12; a++) {
fadeToBlackBy( south, num_Led, 10);
byte dothue = 0;
for ( int i = 0; i < 8; i++) {
south[beatsin16(i + 7, 0, num_Led)] |= CHSV(dothue, 200, 255);
dothue += 32;
}
FastLED[a].showLeds();
delay(5);
}
}
void confetti () {
for (a = 0; a < 12; a++) {
fadeToBlackBy( south, num_Led, 10);
int pos = random16(num_Led);
south[pos] += CHSV( gHue + random8(64), 200, 255);
FastLED[a].showLeds();
delay(5);
}
}
typedef void(*SimplePatternList[])();
SimplePatternList gPatterns = {filldissolve, Fire, sinelon, juggle, rainbowWaterfall, rainbow, rainbowSparkle, confetti, water, solidRainbow, solidRainGlit, rainbowScroll};
#define ARRAY_SIZE(A) (sizeof(A) / sizeof((A)[0]))
void nextPattern() {
gCurrentPatternNumber = random(ARRAY_SIZE(gPatterns));
}
void setup() {
FastLED.addLeds(south, num_Led); //define LED objects
FastLED.addLeds(south, num_Led);
FastLED.addLeds(south, num_Led);
FastLED.addLeds(south, num_Led);
FastLED.addLeds(south, num_Led);
FastLED.addLeds(south, num_Led);
FastLED.addLeds(south, num_Led);
FastLED.addLeds(south, num_Led);
FastLED.addLeds(south, num_Led);
FastLED.addLeds(south, num_Led);
FastLED.addLeds(south, num_Led);
FastLED.addLeds(south, num_Led);
gPal = CRGBPalette16( CRGB::Black, CRGB::Red, CRGB::Yellow, CRGB::White);
gPal1 = CRGBPalette16( CRGB::Black, CRGB::Blue, CRGB::Aqua, CRGB::White);
}
void loop() {
random16_add_entropy( random());
m = gHue;
gPatterns[gCurrentPatternNumber]();
EVERY_N_MILLISECONDS( 12 ) {
gHue++;
}
EVERY_N_SECONDS( 60 ) {
nextPattern();
}
}
One of the consequences of me being an amateur when it comes to programming is that I am notoriously bad at commenting my code. However, this particular example should be fairly easy to follow, as most of the variables are pretty explicitly named for their purpose. I am also notoriously bad at documenting my work, so unfortunately, there are no good videos that I know of that demonstrate the animations. Which is sad, because they were pretty cool. A few months down the line, we ended up reusing the LEDs, code, and controllers for another project, which I may be able to find some video of. As promised, the next series of posts will detail my work on The Quacken!