#include <Adafruit_NeoPixel.h>
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
// --- Hardware Setup ---
#define PIN_DATA 3
#define MATRIX_WIDTH 16
#define MATRIX_HEIGHT 16
#define NUM_PIXELS (MATRIX_WIDTH * MATRIX_HEIGHT)
#define LCD_ADDRESS 0x27 // Change if needed (e.g., 0x3F)
#define LCD_COLUMNS 20
#define LCD_ROWS 4
Adafruit_NeoPixel pixels(NUM_PIXELS, PIN_DATA, NEO_GRB + NEO_KHZ800);
LiquidCrystal_I2C lcd(LCD_ADDRESS, LCD_COLUMNS, LCD_ROWS);
// --- Timing ---
#define CHASE_INTERVAL 50
#define RAINBOW_INTERVAL 10
#define SWITCH_INTERVAL 20000
unsigned long lastFrameTime = 0;
unsigned long lastSwitchTime = 0;
// --- Animation State ---
enum Animation { CHASE, RAINBOW, SPARKLE, WIPE, BREATH };
Animation currentAnim = CHASE;
const int NUM_ANIMATIONS = 5;
// --- Setup ---
void setup() {
Serial.begin(9600);
lcd.begin(20, 4);
lcd.backlight();
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("NeoPixel Matrix Ready");
pixels.begin();
pixels.setBrightness(50);
pixels.clear();
pixels.show();
}
// --- Main Loop ---
void loop() {
if (millis() - lastSwitchTime > SWITCH_INTERVAL) {
lastSwitchTime = millis();
switchAnimation();
}
switch (currentAnim) {
case CHASE: updateChase(); break;
case RAINBOW: updateRainbow(); break;
case SPARKLE: updateSparkle(); break;
case WIPE: updateWipe(); break;
case BREATH: updateBreath(); break;
}
}
// --- Animation Switching ---
void switchAnimation() {
currentAnim = static_cast<Animation>((currentAnim + 1) % NUM_ANIMATIONS);
String name = getAnimName(currentAnim);
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Animation:");
lcd.setCursor(0, 1);
lcd.print(name);
Serial.println("Switched to: " + name);
}
// --- Animation Names ---
String getAnimName(Animation a) {
switch (a) {
case CHASE: return "CHASE";
case RAINBOW: return "RAINBOW";
case SPARKLE: return "SPARKLE";
case WIPE: return "WIPE";
case BREATH: return "BREATHING";
default: return "CODE BY ARVIND";
}
}
// --- Pixel Index Helper ---
uint16_t getPixelIndex(int x, int y) {
if (y % 2 == 0) return y * MATRIX_WIDTH + x;
else return y * MATRIX_WIDTH + (MATRIX_WIDTH - 1 - x);
}
// --- Color Wheel ---
uint32_t Wheel(byte pos) {
pos = 255 - pos;
if (pos < 85) return pixels.Color(255 - pos * 3, 0, pos * 3);
if (pos < 170) {
pos -= 85;
return pixels.Color(0, pos * 3, 255 - pos * 3);
}
pos -= 170;
return pixels.Color(pos * 3, 255 - pos * 3, 0);
}
// --- Animations ---
void updateChase() {
static int x = 0, y = 0;
if (millis() - lastFrameTime > CHASE_INTERVAL) {
lastFrameTime = millis();
pixels.setPixelColor(getPixelIndex(x, y), 0);
x++;
if (x >= MATRIX_WIDTH) {
x = 0;
y = (y + 1) % MATRIX_HEIGHT;
}
pixels.setPixelColor(getPixelIndex(x, y), pixels.Color(255, 0, 0));
pixels.show();
}
}
void updateRainbow() {
static uint16_t j = 0;
if (millis() - lastFrameTime > RAINBOW_INTERVAL) {
lastFrameTime = millis();
for (uint16_t i = 0; i < NUM_PIXELS; i++) {
pixels.setPixelColor(i, Wheel((i * 256 / NUM_PIXELS + j) & 255));
}
pixels.show();
j++;
}
}
void updateSparkle() {
static unsigned long lastSparkle = 0;
if (millis() - lastSparkle > 100) {
lastSparkle = millis();
int index = random(NUM_PIXELS);
pixels.setPixelColor(index, pixels.Color(255, 255, 255));
pixels.show();
delay(30);
pixels.setPixelColor(index, 0);
pixels.show();
}
}
void updateWipe() {
static int row = 0;
if (millis() - lastFrameTime > 100) {
lastFrameTime = millis();
for (int x = 0; x < MATRIX_WIDTH; x++) {
pixels.setPixelColor(getPixelIndex(x, row), pixels.Color(0, 255, 0));
}
pixels.show();
row++;
if (row >= MATRIX_HEIGHT) {
row = 0;
pixels.clear();
}
}
}
void updateBreath() {
static int brightness = 0;
static int delta = 5;
if (millis() - lastFrameTime > 30) {
lastFrameTime = millis();
brightness += delta;
if (brightness >= 255 || brightness <= 0) delta = -delta;
pixels.setBrightness(brightness);
for (int i = 0; i < NUM_PIXELS; i++) {
pixels.setPixelColor(i, pixels.Color(0, 0, 255));
}
pixels.show();
}
}
FPS: 0
Power: 0.00W
Power: 0.00W