#include <FastLED.h>
#define WIDTH 10
#define HEIGHT 10
#define NUM_LEDS WIDTH * HEIGHT
#define DATA_PIN 6
CRGB leds[NUM_LEDS];
// Effect.h
#ifndef Effect_h
#define Effect_h
class Effect {
protected:
CRGB *leds;
int width, height;
struct CRGB& pixel(int x, int y);
bool inXRange(int x);
bool inYRange(int y);
void clearLeds();
public:
Effect(CRGB *leds, int width, int height);
virtual void start() {};
};
#endif
Effect::Effect(CRGB *leds, int width, int height) {
this->leds = leds;
this->width = width;
this->height = height;
}
CRGB& Effect::pixel(int x, int y) {
if (inXRange(x) && inYRange(y)) {
return leds[y * width + x];
} else {
// Return a result pixel for out of range coordinates.
static CRGB resultPixel;
return resultPixel;
}
}
bool Effect::inXRange(int x) {
return x >= 0 && x < width;
}
bool Effect::inYRange(int y) {
return y >= 0 && y < height;
}
void Effect::clearLeds() {
for (int i = 0; i < width * height; i++) {
leds[i] = CRGB::Black;
}
}
// TestPattern
class TestPattern : public Effect {
public:
TestPattern(CRGB *leds, int width, int height): Effect(leds, width, height) {}
void start() {
chasePixels(CRGB::Red);
chasePixels(CRGB::Green);
chasePixels(CRGB::Blue);
blockPixels(CRGB::Red);
blockPixels(CRGB::Green);
blockPixels(CRGB::Blue);
for (int i = 0; i < 256; i += 16) {
blockPixels(CRGB::White, i);
}
blockPixels(CRGB::White, 10);
}
void chasePixels(CRGB colour) {
for (int pixel = 0; pixel < width * height; pixel++) {
leds[pixel] = colour;
LEDS.show();
leds[pixel] = CRGB::Black;
}
}
void blockPixels(CRGB color) {
LEDS.showColor(color);
delay(1000);
}
void blockPixels(CRGB color, byte intensity) {
LEDS.showColor(color, intensity);
delay(200);
}
};
// DeadChannel.cpp
#define DELAY 32
class DeadChannel : public Effect {
public:
DeadChannel(CRGB *leds, int width, int height) : Effect(leds, width, height) {}
void start() {
for (int i = 0; i < 5; i++) {
playStatic();
blank();
}
}
void playStatic() {
uint8_t staticLength = random8(24);
for (int staticFrame = 0; staticFrame < staticLength; staticFrame++) {
for (int i = 0; i < width * height; i++) {
leds[i] = CHSV(0, 0, random());
}
LEDS.show();
}
}
void blank() {
clearLeds();
LEDS.show();
delay(32 * random(16));
}
};
// Plasma.cpp
class Plasma : public Effect {
public:
Plasma(CRGB *leds, int width, int height) : Effect(leds, width, height) {}
void start() {
for (uint16_t time = 0, cycles = 0; cycles < 2048; time += 128, cycles++) {
calcFrame(time);
}
}
//sin(distance(x, y, (128 * sin(-t) + 128), (128 * cos(-t) + 128)) / 40.74)s
// v = sin(10 * (x * sin(time / 2) + y * cos(time / 3)) + time)
void calcFrame(int time) {
for (int x = 0; x < width; x++) {
for (int y = 0; y < height; y++) {
int16_t v = 0;
uint16_t wibble = sin8(time);
v += sin16(x * wibble * 8 + time);
v += cos16(y * (128 - wibble) * 4 + time);
v += sin16(y * x * cos8(-time) / 2);
pixel(x, y) = CHSV((v >> 8) + 127, 255, 255);
}
}
LEDS.show();
}
uint8_t sin8(uint16_t x) {
return (sin16(x) >> 8) + 128;
}
uint8_t cos8(uint16_t x) {
return (cos16(x) >> 8) + 128;
}
};
// Twinkle.cpp
#define MAX_TWINKS 25
#define OFFSET 0xB000
typedef struct Twink {
short x;
short y;
} Twink;
class Twinkle : public Effect {
private:
short numTwinks;
bool colour;
bool cycleSaturation;
public:
Twinkle(CRGB *leds, int width, int height, bool colour, bool cycleSaturation) :
Effect(leds, width, height),
colour(colour),
cycleSaturation(cycleSaturation),
numTwinks(0) {
}
void start() {
for (uint16_t frame = 0x0000, iterations = 0; iterations < 2250; frame += 0x20, iterations++) {
for (int i = 0; i < width * height; i++) {
if (leds[i]) {
leds[i].fadeToBlackBy(50);
if (!leds[i]) {
numTwinks--;
}
} else {
if (random(56) == 0) {
numTwinks++;
if (colour) {
if (cycleSaturation) {
uint8_t saturation = (sin16(frame + OFFSET) >> 8) + 128;
leds[i] = CHSV(random(255), saturation, 255);
} else {
leds[i] = CHSV(random(255), random(128, 255), 255);
}
} else {
leds[i] = CRGB::White;
}
}
}
}
LEDS.show();
}
}
};
// Snake.cpp
class Snake : public Effect {
private:
static const byte SNAKE_LENGTH = 16;
enum Direction {
UP, DOWN, LEFT, RIGHT
};
struct Pixel {
uint8_t x;
uint8_t y;
};
CRGB colours[SNAKE_LENGTH];
uint8_t initialHue;
Pixel pixels[SNAKE_LENGTH];
Direction direction;
void newDirection() {
switch (direction) {
case UP:
case DOWN:
direction = random(0, 2) == 1 ? RIGHT : LEFT;
break;
case LEFT:
case RIGHT:
direction = random(0, 2) == 1 ? DOWN : UP;
default:
break;
}
}
void shuffleDown() {
for (byte i = SNAKE_LENGTH - 1; i > 0; i--) {
pixels[i] = pixels[i - 1];
}
}
public:
Snake(CRGB *leds, int width, int height) : Effect(leds, width, height), initialHue(0) {
direction = UP;
for (int i = 0; i < SNAKE_LENGTH; i++) {
pixels[i].x = 0;
pixels[i].y = 0;
}
}
void start() {
clearLeds();
for (int frameNo = 0; frameNo < 1000; frameNo++) {
shuffleDown();
if (random(10) > 6) {
newDirection();
}
switch (direction) {
case UP:
pixels[0].y = (pixels[0].y + 1) % height;
break;
case LEFT:
pixels[0].x = (pixels[0].x + 1) % width;
break;
case DOWN:
pixels[0].y = pixels[0].y == 0 ? height - 1 : pixels[0].y - 1;
break;
case RIGHT:
pixels[0].x = pixels[0].x == 0 ? width - 1 : pixels[0].x - 1;
break;
}
fill_rainbow(colours, SNAKE_LENGTH, initialHue++);
for (byte i = 0; i < SNAKE_LENGTH; i++) {
pixel(pixels[i].x, pixels[i].y) = colours[i] %= (255 - i * (255 / SNAKE_LENGTH));
}
LEDS.show();
delay(30);
clearLeds();
}
}
};
// Life.cpp
const int deltaLen = 36;
class Life : public Effect {
private:
uint8_t density;
uint8_t hue;
uint16_t delta[deltaLen];
public:
Life(CRGB *leds, int width, int height, int density): Effect(leds, width, height), density(density) {}
void seed(uint8_t hue) {
for (int i = 0; i < width * height; i++) {
if (random(255) < density) {
leds[i] = CHSV(hue, 255, 255);
}
}
}
void start() {
for (int iterations = 0; iterations < 20; iterations++) {
uint8_t hue = random(256);
seed(hue);
for (int time = 0; time < random(96, 156); time++) {
for (int x = 0; x < width; x++) {
for (int y = 0; y < height; y++) {
int neighbours = numNeighbours(x, y);
if (pixel(x, y)) {
if (neighbours < 2 || neighbours > 3) {
setChanged(x, y);
}
} else {
if (neighbours == 3) {
setChanged(x, y);
}
}
}
}
updateWithChanges(hue++);
for (int i = 0; i < deltaLen; i++) {
delta[i] = 0;
}
LEDS.show();
}
fadeout();
}
}
/*
x - 1, y + 1|x = x, y + 1|x + 1, y + 1
x - 1, y = y| x |x + 1, y = y
x - 1, y - 1|x = x, y - 1|x + 1, y - 1
*/
int numNeighbours(int x, int y) {
int numNeighbours = 0;
numNeighbours += alive(x - 1, y + 1) ? 1 : 0;
numNeighbours += alive(x, y + 1) ? 1 : 0;
numNeighbours += alive(x + 1, y + 1) ? 1 : 0;
numNeighbours += alive(x - 1, y) ? 1 : 0;
numNeighbours += alive(x + 1, y) ? 1 : 0;
numNeighbours += alive(x - 1, y - 1) ? 1 : 0;
numNeighbours += alive(x, y - 1) ? 1 : 0;
numNeighbours += alive(x + 1, y - 1) ? 1 : 0;
return numNeighbours;
}
bool alive(int x, int y) {
return inXRange(x) && inYRange(y) && pixel(x, y);
}
// xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx
// xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx
void setChanged(int x, int y) {
delta[deltaIndex(x, y)] |= 1 << (x < 16 ? x : x - 16);
}
void updateWithChanges(uint8_t time) {
for (int x = 0; x < width; x++) {
for (int y = 0; y < height; y++) {
if (delta[deltaIndex(x, y)] & (1 << (x < 16 ? x : x - 16))) {
if (pixel(x, y)) {
pixel(x, y) = CRGB::Black;
} else {
pixel(x, y) = CHSV(time, 255, 255);
}
}
}
}
}
int deltaIndex(int x, int y) {
return y * 2 + (x < 16 ? 0 : 1);
}
void fadeout() {
for (int brightness = 0; brightness < 256; brightness++) {
for (int i = 0; i < width * height; i++) {
leds[i]--;
}
LEDS.show();
}
}
};
// Sprite.cpp
#define SPRITE_WIDTH 9
#define SPRITE_HEIGHT 9
static uint8_t heartData[SPRITE_WIDTH * SPRITE_HEIGHT] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00,
0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00,
0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00,
0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
};
static uint8_t ghostData[SPRITE_WIDTH * SPRITE_HEIGHT] = {
0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0x00, 0x00, 0xFF, 0x00, 0x00, 0xFF, 0xFF,
0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00,
0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00,
};
class Sprite : public Effect {
private:
int spriteWidth, spriteHeight;
public:
Sprite(CRGB *leds, int width, int height) : Effect(leds, width, height), spriteWidth(SPRITE_WIDTH), spriteHeight(SPRITE_HEIGHT) {}
void start() {
for (int i = 0; i < 400; i++) {
if (random(100) > 94) {
blit(ghostData, 178, random(0, width - spriteWidth), random(0, height - spriteHeight));
} else {
blit(heartData, 0, random(0, width - spriteWidth), random(0, height - spriteHeight));
}
LEDS.show();
delay(100);
for (int i = 0; i < width * height; i++) {
leds[i].fadeToBlackBy(64);
}
}
}
void blit(uint8_t *sprite, uint8_t hue, int x, int y) {
uint8_t saturation = random(8, 16) * 16;
for (int spx = 0; spx < spriteWidth; spx++) {
for (int spy = 0; spy < spriteHeight; spy++) {
if (inXRange(spx + x) && inYRange(spy + y)) {
uint8_t spritePixel = sprite[spy * spriteHeight + spx];
if (spritePixel) {
pixel(spx + x, spy + y) = CHSV(hue, saturation, spritePixel);
}
}
}
}
}
};
// Boxes.cpp
class Boxes : public Effect {
private:
struct Box {
uint16_t height, width;
uint16_t xPos, yPos;
uint8_t hue;
uint8_t speed;
};
uint16_t hiWidth, hiHeight;
public:
Boxes(CRGB *leds, int width, int height): Effect(leds, width, height) {}
void start() {
Box box;
box.height = 3;
box.width = 3;
box.hue = 100;
box.speed = 2;
int repeat = 20;
hi(box, repeat);
clearLeds();
}
void hi(Box b, int repeat) {
for (int i = 0; i < repeat; i++) {
b.xPos = random(0, 8);
b.yPos = random(0, 8);
int y = b.yPos;
for (uint16_t x = 0; x < width; x += b.speed) {
clearLeds();
for (int y = 0; y <= b.width; y += b.speed) {
renderIfVisible(b.xPos, b.yPos, CHSV((b.hue + random(0, 255)), 255, 255), b.width, b.height);
}
LEDS.show();
delay(50);
}
}
}
void renderIfVisible(uint16_t x, uint16_t y, CHSV chsv, int w, int h) {
if (inXRange(x) && inYRange(y)) {
for (int i = 0; i < w; i++) {
for (int j = 0; j < h; j++) {
pixel(x + i, y + j) += chsv;
}
}
}
}
};
// Bouncy.cpp
class Bouncy : public Effect {
private:
public:
Bouncy(CRGB *leds, int width, int height): Effect(leds, width, height) {}
void start() {
for (int x = 0; x < width; x++) {
for (int y = 0; y < height; y++) {
pixel(x, height - 1 - y) = CRGB::White;
LEDS.show();
int wait = y == 0 ? 100 : sin16(y * 1024) / 1024 * 10;
delay(wait);
pixel(x, height - 1 - y) = CRGB::Black;
LEDS.show();
}
for (int y = 0; y < height; y++) {
pixel(x, y) = CRGB::White;
LEDS.show();
int wait = y == 0 ? 100 : sin16((height - 1 - y) * 1024) / 1024 * 10;
delay(wait);
pixel(x, y) = CRGB::Black;
LEDS.show();
}
}
}
};
// HiRez.cpp
class HiRez : public Effect {
private:
uint16_t lowWidth, lowHeight;
uint8_t hue;
public:
HiRez(CRGB *leds, int width, int height): Effect(leds, width, height),
hue(0),
lowWidth(width / 2),
lowHeight(height / 2)
{}
void start() {
lo();
hi();
}
void lo() {
for (int y = 0; y < width; y += 2) {
for (int x = 0; x < width; x += 2) {
pixel(x, y) = CHSV(hue, 255, 255);
pixel(x + 1, y) = CHSV(hue, 255, 255);
pixel(x, y + 1) = CHSV(hue, 255, 255);
pixel(x + 1, y + 1) = CHSV(hue, 255, 255);
LEDS.show();
delay(128);
pixel(x, y) = CRGB::Black;
pixel(x + 1, y) = CRGB::Black;
pixel(x, y + 1) = CRGB::Black;
pixel(x + 1, y + 1) = CRGB::Black;
}
}
}
void hi() {
for (int y = 0; y < width; y++) {
for (int x = 0; x < width; x++) {
pixel(x, y) = CHSV(hue + y * 25, 255, 255);
LEDS.show();
delay(32);
pixel(x, y) = CRGB::Black;
}
}
}
};
// // // // //
void setup() {
FastLED.addLeds<WS2812B, DATA_PIN, GRB>(leds, NUM_LEDS);
}
void loop() {
doTestPattern();
doDeadChannel();
doPlasma();
doTwinkle();
doSnake();
doLife();
doSprite();
doBoxes();
doBouncy();
doHiRez();
}
void doTestPattern() {
TestPattern testPattern(leds, WIDTH, HEIGHT);
testPattern.start();
}
void doDeadChannel() {
DeadChannel deadChannel(leds, WIDTH, HEIGHT);
deadChannel.start();
}
void doPlasma() {
Plasma plasma(leds, WIDTH, HEIGHT);
plasma.start();
}
void doTwinkle() {
Twinkle twinkle(leds, WIDTH, HEIGHT, true, true);
twinkle.start();
}
void doSnake() {
Snake snake(leds, WIDTH, HEIGHT);
snake.start();
}
void doLife() {
Life life(leds, WIDTH, HEIGHT, 56);
life.start();
}
void doSprite() {
Sprite sprite(leds, WIDTH, HEIGHT);
sprite.start();
}
void doBoxes() {
Boxes boxes(leds, WIDTH, HEIGHT);
boxes.start();
}
void doBouncy() {
Bouncy bouncy(leds, WIDTH, HEIGHT);
bouncy.start();
}
void doHiRez() {
HiRez hiRez(leds, WIDTH, HEIGHT);
hiRez.start();
}