#include <FastLED.h>
#include <CircularBuffer.h>
//FASTLED Parameters
#define NUM_LEDS 300
//#define NUM_LEDS 334 // number of LEDs on the underglow led strip
#define LED_PIN 27 // pin the underglow led strip is attached to
//pins for the marquee spot light relay controls
#define MARQUEESPOT_UL_PIN 32 //relay 1
#define MARQUEESPOT_UR_PIN 33 //relay 2
#define MARQUEESPOT_LL_PIN 25 //relay 3
#define MARQUEESPOT_LR_PIN 26 //relay 4
#define ACTLED 2 // Built in ESP LED PIN
//Idle Timer
unsigned long idleTimeout = 1000 * 30; //30 seconds of no step switch button presses.
unsigned long idleTime = 0;
unsigned long startTime = 0;
bool idleMode = 0;
//AnimationChangetimer
unsigned long animationTimeout = 1000 * 240; //time between animation changes
unsigned long animationTime = 0;
unsigned long animationStartTime = 0;
int currentAnimation = 2;
//Rainbow Effect Parameters
uint8_t gHue = 0; // rotating "base color" used by many of the patterns
uint8_t gHueSpeed = 1; // default speed of the rainbow rotation animation
uint8_t BassSpeedBoost = 4; // how much to accelerate the rainbow animation when the bass lights are on
//Marquee Effect Parameters - Not Marquee Spot Lights!
uint8_t MarqueeSegmentSize = 10;
CRGB MarqueePrimaryColors[] = {CRGB::Blue, CRGB::Green, CRGB::Red, CRGB::Yellow}; //must be an even number of colors!
int MarqueeCurrentColor = 0;
CRGB MarqueeValidColors[] = {CRGB::Red, CRGB::Green, CRGB::Blue, CRGB::Amethyst, CRGB::Aqua, CRGB::Gold, CRGB::Fuchsia, CRGB::SpringGreen, CRGB::HotPink, CRGB::Indigo, CRGB::Yellow, CRGB::Lime};
bool MarqueeUseRandomColors = 0;
long MarqueeAnimationDelay = 500; //ms between color changes
uint8_t intLEDBrightness = 50; // variable to hold the brightness level of the underglow led strip
uint8_t lowBrightness = 50; // default low brightness of the underglow led strip
uint8_t highBrightness = 85; // default high brightness of the underglow strip
//RingChase Effect Parameters
CircularBuffer<CRGB, NUM_LEDS> RingChaseBuffer;
uint8_t RingSegmentSize = 10;
uint8_t NumberOfRings = 6;
long RingAnimationDelay = 15;
uint8_t RingBassReverseAmount = 20;
bool RingBufferPrimed = 0;
// holds the status for player 1 and 2 step switch lights
bool P1UpButton = 0;
bool P1LeftButton = 0;
bool P1DownButton = 0;
bool P1RightButton = 0;
bool P2UpButton = 0;
bool P2LeftButton = 0;
bool P2DownButton = 0;
bool P2RightButton = 0;
// holds the status of the marquee spot lights
bool MarqueeSpot_UL = 0;
bool MarqueeSpot_UR = 0;
bool MarqueeSpot_LL = 0;
bool MarqueeSpot_LR = 0;
bool previousCycleBassLights = 0; //used for rising edge trigger of bass light effects.
bool BassLights = 0; // holds the status of the bass lights
// LED arrays for underglow step button highlighting
int P1UpButtonUnderglow[] = { 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31 };
int P1LeftButtonUnderglow[] = { 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85 };
int P1DownButtonUnderglow[] = { 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140 };
int P2UpButtonUnderglow[] = { 288, 289, 290, 291, 292, 293, 294, 295, 296, 297, 298 };
int P2DownButtonUnderglow[] = { 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189 };
int P2RightButtonUnderglow[] = { 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245 };
// Stepmania Data Variables
// (Each byte contains 8 bits, individual lights are tracked with individual bits)
byte p1LEDs = 0; //Tracks P1's lights (SM gameplay buttons 0-7)
byte p2LEDs = 0; //Tracks P2's lights (SM gameplay buttons 0-7)
byte cabLEDs = 0; //Tracks cabinet lighting. 0-3: Marquee lights, 4: P1 Start, 5: P1 Menu, 6: P2 Start, 7: P2 Menu
byte etcLEDs = 0; //Tracks other lights. 0: Bass neon. All other slots unused - if you want to output more lights from SM, here's a good spot.
byte receivedData = 0; //The last byte of data we received from Stepmania
short lightBytePos = 0; //Tracks how many bytes of lighting data we've received this update
CRGB Pad_Underglow_LEDs[NUM_LEDS];
void setup() {
// put your setup code here, to run once:
Serial.begin(115200);
pinMode(MARQUEESPOT_UL_PIN, OUTPUT);
pinMode(MARQUEESPOT_UR_PIN, OUTPUT);
pinMode(MARQUEESPOT_LL_PIN, OUTPUT);
pinMode(MARQUEESPOT_LR_PIN, OUTPUT);
pinMode(ACTLED, OUTPUT);
//setup FastLED
FastLED.addLeds<NEOPIXEL, LED_PIN>(Pad_Underglow_LEDs, NUM_LEDS);
FastLED.setBrightness(255);
//StartupLightTest();
startTime = millis();
}
void loop() {
//draw a base animation
switch (currentAnimation) {
case 0:
HSVRainbow();
EVERY_N_MILLISECONDS(16) {
IncrementHue(); // slowly cycle the "base color" through the rainbow
}
break;
case 1:
Marquee();
break;
case 2:
RingChase();
break;
default:
HSVRainbow();
break;
}
//check for incomming serial data from outfox/stepmania
if (Serial.available() > 0) { //Do we have lights data to receive?
readSerialLightingData();
}
//Done receiving serial data this loop
previousCycleBassLights = BassLights;
BassLights = digitalRead(21);
//overlay display functions
BassGlow();
PadButtonLights();
//process other lights
MarqueeSpots();
//apply and needed updates to the LED strip.
CalculateIdleTime();
CalculateAnimationChangeTimer();
FastLED.show();
}
void CalculateAnimationChangeTimer() {
animationTime = millis();
if (animationTime - animationStartTime >= animationTimeout) { //test whether the period has elapsed
currentAnimation = (currentAnimation + 1) % 3;
animationStartTime = millis();
Serial.print("Changing Animation: ");
Serial.println(currentAnimation);
FastLED.clear(true);
}
}
void CalculateIdleTime() {
idleTime = millis();
if (P1UpButton || P1LeftButton || P1DownButton || P1RightButton ||
P2UpButton || P2LeftButton || P2DownButton || P2RightButton)
{
//if any button lights are activated, reset idle timer.
idleTime = 0;
startTime = millis();
}
//Serial.println(idleTime);
if (idleTime - startTime >= idleTimeout) //test whether the period has elapsed
{
idleMode = 1;
//Serial.println("Idle Mode Activated");
}
else {
idleMode = 0;
//Serial.println("Idle Mode Disabled");
}
}
void BassGlow() {
if (BassLights == 1) {
intLEDBrightness = highBrightness;
} else {
intLEDBrightness = lowBrightness;
}
}
//handle outputing signals to the marquee spots
void MarqueeSpots() {
digitalWrite(MARQUEESPOT_UL_PIN, MarqueeSpot_UL);
digitalWrite(MARQUEESPOT_UR_PIN, MarqueeSpot_UR);
digitalWrite(MARQUEESPOT_LL_PIN, MarqueeSpot_LL);
digitalWrite(MARQUEESPOT_LR_PIN, MarqueeSpot_LR);
}
void IncrementHue() {
gHue += gHueSpeed;
if (BassLights == 1) {
gHue += BassSpeedBoost;
}
}
//read the status of the pad buttons and turn on the associated underglow section
void PadButtonLights() {
//Player 1
if (P1UpButton) {
for (int i = 0; i < 10; i++) {
Pad_Underglow_LEDs[P1UpButtonUnderglow[i]] = CRGB::White;
}
}
if (P1LeftButton) {
for (int i = 0; i < 10; i++) {
Pad_Underglow_LEDs[P1LeftButtonUnderglow[i]] = CRGB::White;
}
}
if (P1DownButton) {
for (int i = 0; i < 10; i++) {
Pad_Underglow_LEDs[P1DownButtonUnderglow[i]] = CRGB::White;
}
}
//Player 2
if (P2UpButton) {
for (int i = 0; i < 10; i++) {
Pad_Underglow_LEDs[P2UpButtonUnderglow[i]] = CRGB::White;
}
}
if (P2RightButton) {
for (int i = 0; i < 10; i++) {
Pad_Underglow_LEDs[P2RightButtonUnderglow[i]] = CRGB::White;
}
}
if (P2DownButton) {
for (int i = 0; i < 10; i++) {
Pad_Underglow_LEDs[P2DownButtonUnderglow[i]] = CRGB::White;
}
}
}
void StartupLightTest() {
//test Marquee Spots
delay(500);
digitalWrite(MARQUEESPOT_UL_PIN, HIGH);
delay(500);
digitalWrite(MARQUEESPOT_UL_PIN, LOW);
digitalWrite(MARQUEESPOT_UR_PIN, HIGH);
delay(500);
digitalWrite(MARQUEESPOT_UR_PIN, LOW);
digitalWrite(MARQUEESPOT_LL_PIN, HIGH);
delay(500);
digitalWrite(MARQUEESPOT_LL_PIN, LOW);
digitalWrite(MARQUEESPOT_LR_PIN, HIGH);
delay(500);
digitalWrite(MARQUEESPOT_LR_PIN, LOW);
delay(500);
digitalWrite(MARQUEESPOT_UL_PIN, HIGH);
digitalWrite(MARQUEESPOT_UR_PIN, HIGH);
digitalWrite(MARQUEESPOT_LL_PIN, HIGH);
digitalWrite(MARQUEESPOT_LR_PIN, HIGH);
delay(500);
digitalWrite(MARQUEESPOT_UL_PIN, LOW);
digitalWrite(MARQUEESPOT_UR_PIN, LOW);
digitalWrite(MARQUEESPOT_LL_PIN, LOW);
digitalWrite(MARQUEESPOT_LR_PIN, LOW);
delay(500);
//test Red
for (int i = 0; i < NUM_LEDS; i++) {
Pad_Underglow_LEDs[i] = CRGB::Red;
FastLED.show();
delay(4);
}
//test Green
for (int i = 0; i < NUM_LEDS; i++) {
Pad_Underglow_LEDs[i] = CRGB::Green;
FastLED.show();
delay(4);
}
//test Blue
for (int i = 0; i < NUM_LEDS; i++) {
Pad_Underglow_LEDs[i] = CRGB::Blue;
FastLED.show();
delay(4);
}
//test White
for (int i = 0; i < NUM_LEDS; i++) {
Pad_Underglow_LEDs[i] = CRGB::White;
FastLED.show();
delay(4);
}
delay(500);
fill_solid(Pad_Underglow_LEDs, NUM_LEDS, CRGB::Black);
FastLED.show();
delay(250);
}