/*
--------------- by JediRick -----------------
boolean (8 bit) - [true/false]
byte (8 bit) - [0-255] unsigned number
char (8 bit) - [-128 to 127] signed number
unsigned char (8 bit) - [-128 to 127] signed number
word (16 bit) - [0-65535] unsigned number
unsigned int (16 bit) - [0-65535] unsigned number
int (16 bit) - [-32768 to 32767] signed number
unsigned long (32 bit) - [0-4,294,967,295] unsigned number usually for millis
long (32 bit) - [-2,147,483,648 to 2,147,483,647] signed number
float (32 bit) - [-3.4028235E38 to 3.4028235E38] signed number
uint8_t (8 bit) - [0-255] unsigned number
int8_t (8 bit) - [-127 - 127] signed number
uint16_t (16 bit) - [0-65,535] unsigned number
int16_t (16 bit) - [-32,768 - 32,767] signed number
uint32_t (32 bit) - [0-4,294,967,295] unsigned number
int32_t (32 bit) - [-2,147,483,648 - 2,147,483,647] signed number
uint64_t (64 bit) - [0-18,446,744,073,709,551,615] unsigned number
int64_t (64 bit) - [−9,223,372,036,854,775,808 - 9,223,372,036,854,775,807] signed number
--------------------------------------------
*/
uint8_t redPins[6] = {2, 5, 8, 11, 14, 17};
uint8_t greenPins[6] = {3, 6, 9, 12, 15, 18};
uint8_t bluePins[6] = {4, 7, 10, 13, 16, 19};
uint8_t blackPins[6] = {20, 21, 22, 26, 27, 28};
// Animation state
uint8_t currentAnimation = 0; // 0: Sparkle, 1: Color Wipe, 2: Theater Chase, 3: Rainbow Chase, 4: Cylon, 5: White Twinkle
unsigned long lastAnimationSwitchMs = 0;
const unsigned long animationDurationMs = 7000; // switch every 7 seconds
bool randomCycleMode = true; // true: button 0 enables random timer cycling; false: button 1 locks a fixed animation
// Button state (GPIO 0 and 1)
const uint8_t buttonPins[2] = {0, 1};
const unsigned long debounceMs = 25;
bool buttonStable[2] = {true, true}; // using INPUT_PULLUP, HIGH=not pressed
bool buttonLastRead[2] = {true, true};
unsigned long buttonLastChangeMs[2] = {0, 0};
bool pollButtonPressed(uint8_t idx) {
bool reading = digitalRead(buttonPins[idx]);
if (reading != buttonLastRead[idx]) {
buttonLastChangeMs[idx] = millis();
buttonLastRead[idx] = reading;
}
if ((millis() - buttonLastChangeMs[idx]) > debounceMs) {
if (reading != buttonStable[idx]) {
buttonStable[idx] = reading;
// Active-low press detection
if (buttonStable[idx] == LOW) return true;
}
}
return false;
}
// Utility helpers
void setAllColorsHigh() {
for (uint8_t i = 0; i <= 5; i++) {
digitalWrite(redPins[i], HIGH);
digitalWrite(greenPins[i], HIGH);
digitalWrite(bluePins[i], HIGH);
}
}
void setAllBlackLow() {
for (uint8_t i = 0; i <= 5; i++) {
digitalWrite(blackPins[i], LOW);
}
}
void activateLayer(uint8_t layerIdx) {
for (uint8_t i = 0; i <= 5; i++) {
digitalWrite(blackPins[i], (i == layerIdx) ? HIGH : LOW);
}
}
void setColorAt(uint8_t colorIdx, uint8_t ledIdx, uint8_t level) {
// colorIdx: 0=R, 1=G, 2=B | level: LOW=on (sink), HIGH=off
if (colorIdx == 0) digitalWrite(redPins[ledIdx], level);
else if (colorIdx == 1) digitalWrite(greenPins[ledIdx], level);
else digitalWrite(bluePins[ledIdx], level);
}
void setRGBAt(uint8_t ledIdx, bool rOn, bool gOn, bool bOn) {
digitalWrite(redPins[ledIdx], rOn ? LOW : HIGH);
digitalWrite(greenPins[ledIdx], gOn ? LOW : HIGH);
digitalWrite(bluePins[ledIdx], bOn ? LOW : HIGH);
}
void setup() {
delay(1000);
Serial1.begin(115200);
while (!Serial1) {
delay(100); // wait for serial port to connect. Needed for native USB
}
Serial1.println("Hello, Serial1 Monitor!");
for (uint8_t i = 0; i <= 5; i++) {
pinMode(redPins[i], OUTPUT);
pinMode(greenPins[i], OUTPUT);
pinMode(bluePins[i], OUTPUT);
pinMode(blackPins[i], OUTPUT);
}
// Buttons with internal pull-ups (active-low)
pinMode(buttonPins[0], INPUT_PULLUP);
pinMode(buttonPins[1], INPUT_PULLUP);
setAllColorsHigh();
setAllBlackLow();
Serial1.println("Startup: random mode enabled; timer-based switching active.");
}
void loop() {
// Switch animations on a timer only in random cycle mode
unsigned long nowMs = millis();
if (randomCycleMode && (nowMs - lastAnimationSwitchMs >= animationDurationMs)) {
currentAnimation = random(6);
lastAnimationSwitchMs = nowMs;
}
// Button handling (manual override)
if (pollButtonPressed(0)) { // Enable random timed animations
randomCycleMode = true;
lastAnimationSwitchMs = millis();
Serial1.print("Button 0: random mode enabled; switching every ");
Serial1.print(animationDurationMs);
Serial1.println(" ms.");
}
if (pollButtonPressed(1)) { // Set/advance permanent fixed animation
if (randomCycleMode) {
randomCycleMode = false;
currentAnimation = 0; // first press: set animation 0 permanently
Serial1.println("Button 1: fixed mode set. Animation = 0");
} else {
currentAnimation = (currentAnimation + 1) % 6; // subsequent presses: next animation
Serial1.print("Button 1: fixed mode next. Animation = ");
Serial1.println(currentAnimation);
}
lastAnimationSwitchMs = millis();
}
// Run current animation frame
if (currentAnimation == 0) {
// Sparkle
for (uint8_t n = 0; n < 8; n++) {
uint8_t layer = random(6);
uint8_t color = random(3);
uint8_t idx = random(6);
activateLayer(layer);
setColorAt(color, idx, LOW);
delay(30);
setColorAt(color, idx, HIGH);
}
} else if (currentAnimation == 1) {
// Color wipe across indices for each color
for (uint8_t color = 0; color < 3; color++) {
for (uint8_t i = 0; i <= 5; i++) {
activateLayer(i);
setColorAt(color, i, LOW);
delay(70);
setColorAt(color, i, HIGH);
}
}
} else {
// Theater chase style: light every 3rd index per phase
uint8_t color = random(3);
for (uint8_t phase = 0; phase < 3; phase++) {
for (uint8_t i = 0; i <= 5; i++) {
activateLayer(i);
for (uint8_t j = 0; j <= 5; j++) {
bool on = ((j + phase) % 3) == 0;
setColorAt(color, j, on ? LOW : HIGH);
}
delay(60);
for (uint8_t j = 0; j <= 5; j++) setColorAt(color, j, HIGH);
}
}
}
// Additional animations
if (currentAnimation == 3) {
// Rainbow Chase: rotate a rainbow palette across indices for each layer
static uint8_t offset = 0;
const uint8_t steps = 12; // multiple frames per loop
for (uint8_t s = 0; s < steps; s++) {
for (uint8_t layer = 0; layer <= 5; layer++) {
activateLayer(layer);
for (uint8_t j = 0; j <= 5; j++) {
uint8_t k = (j + offset) % 6;
// Palette: R, G, B, Y, C, M
if (k == 0) setRGBAt(j, true, false, false);
else if (k == 1) setRGBAt(j, false, true, false);
else if (k == 2) setRGBAt(j, false, false, true);
else if (k == 3) setRGBAt(j, true, true, false);
else if (k == 4) setRGBAt(j, false, true, true);
else setRGBAt(j, true, false, true);
}
delay(40);
for (uint8_t j = 0; j <= 5; j++) setRGBAt(j, false, false, false);
}
offset = (offset + 1) % 6;
}
} else if (currentAnimation == 4) {
// Cylon scanner on a single layer with a random color
uint8_t layer = random(6);
uint8_t color = random(3);
activateLayer(layer);
for (int8_t j = 0; j <= 5; j++) {
setColorAt(color, j, LOW);
delay(70);
setColorAt(color, j, HIGH);
}
for (int8_t j = 4; j >= 1; j--) {
setColorAt(color, j, LOW);
delay(70);
setColorAt(color, j, HIGH);
}
} else if (currentAnimation == 5) {
// White twinkle bursts across random layers and indices
for (uint8_t n = 0; n < 10; n++) {
uint8_t layer = random(6);
uint8_t idx = random(6);
activateLayer(layer);
setRGBAt(idx, true, true, true);
delay(50);
setRGBAt(idx, false, false, false);
delay(20);
}
}
}
void selectBlack() {
uint8_t pinRandoBlack = random(6);
digitalWrite(blackPins[pinRandoBlack], HIGH);
pinRandoBlack = random(6);
digitalWrite(blackPins[pinRandoBlack], LOW);
}
void lightLED() {
uint8_t pinRando = random(20);
digitalWrite(pinRando, LOW);
pinRando = random(20);
digitalWrite(pinRando, HIGH);
}
void generateLights() {
uint8_t countRando = random(1,5);
for (uint8_t w = 0; w <= countRando; w++) {
selectBlack();
lightLED();
}
}