// Four PWM-enabled light channels, intended for colors const int LED_PWM1 = 6; // white const int LED_PWM2 = 9; // red const int LED_PWM3 = 10; // blue const int LED_PWM4 = 11; // TBD // Two digital light channels, intended for effects const int LED_FX1 = 8; // twinkle const int LED_FX2 = 12; // color-change // Input for button const int IN_CONTROL = 7; // Internal LED for debugging const int LED_INT = 13; // Control lines for spectrum analyzer const int SPECTRUM_RESET = 5; const int SPECTRUM_STROBE = 4; // Input lines for spectrum analyzer const int SPECTRUM_LEFT = 0; const int SPECTRUM_RIGHT = 1; // TODO: Better enums const int MODE_AUTO = 0; const int MODE_MUSIC = 1; const int MODE_SNAKE = 2; const int MODE_SWAP = 3; const int MODE_ALL = 4; const int MODE_TWINKLE = 5; const int NUM_MODES = 6; const int NUM_PWM_MODES = 4; const int NUM_SPECIAL_MODES = 2; const int TIME_PER_MODE = 20; const int PWM_FADEIN = 1; const int PWM_FADEOUT = 2; const int PWM_FADEINOUT = 3; const int PWM_BRIEFINOUT = 4; const int PWM_TWINKLE = 5; const int FX_ON = 1; const int FX_BLINK = 2; const int MODES[4][18] = { /* { pwm 1 type, pwm 1 factor, pwm 1 offset, pwm 2 type, pwm 2 factor, pwm 2 offset, pwm 3 type, pwm 3 factor, pwm 3 offset, pwm 4 type, pwm 4 factor, pwm 4 offset, fx 1 type, fx 1 factor, fx 1 offset, fx 2 type, fx 2 factor, fx 2 offset} */ // MODE_SNAKE { PWM_BRIEFINOUT, 10, 0, PWM_BRIEFINOUT, 10, 64, PWM_BRIEFINOUT, 10, 128, PWM_BRIEFINOUT, 10, 192, FX_ON, 10, 0, FX_BLINK, 30, 0 }, // MODE_SWAP { PWM_FADEINOUT, 20, 0, PWM_FADEINOUT, 20, 0, PWM_FADEINOUT, 20, 128, PWM_FADEINOUT, 20, 128, FX_BLINK, 60, 0, FX_BLINK, 60, 128 }, // MODE_ALL { PWM_FADEINOUT, 30, 0, PWM_FADEINOUT, 30, 0, PWM_FADEINOUT, 30, 0, PWM_FADEINOUT, 30, 0, FX_BLINK, 150, 0, FX_BLINK, 150, 128 }, // MODE_TWINKLE { PWM_TWINKLE, 20, 4, PWM_TWINKLE, 20, 8, PWM_TWINKLE, 20, 12, PWM_TWINKLE, 20, 16, FX_BLINK, 60, 0, FX_BLINK, 90, 0 } }; const int MAX_PWM_DELTA = 32; const int GLOBAL_SLICE_TIME = 10; const float DC_BIAS = 60.0; const int NOISE_FLOOR = 50; const int SILENCE_TIME_TO_LOCK = 5000; const int GAIN_TIME_TO_ADJUST = 1250; const int MAX_ORGAN_DELTA = 3; // Global application state int controlState = 0; int controlState_old = 0; unsigned long time = 0; unsigned long timeslice = 0; int mode = 0; int effectiveMode = 0; int pwm1 = 0; int pwm2 = 0; int pwm3 = 0; int pwm4 = 0; int spectrum[7]; int reducedSpectrum[4]; float gain = 0.25; int maxAudio = 0; unsigned long gainTimestamp = 0; int silent = 0; unsigned long silenceTimestamp = 0; int silenceLock = 1; void setup() { // Initialize output pins for LEDs pinMode(LED_PWM1, OUTPUT); pinMode(LED_PWM2, OUTPUT); pinMode(LED_PWM3, OUTPUT); pinMode(LED_PWM4, OUTPUT); pinMode(LED_FX1, OUTPUT); pinMode(LED_FX2, OUTPUT); pinMode(LED_INT, OUTPUT); // Initialize input pin for button pinMode(IN_CONTROL, INPUT); // Initialize spectrum analyzer pinMode(SPECTRUM_RESET, OUTPUT); pinMode(SPECTRUM_STROBE, OUTPUT); digitalWrite(SPECTRUM_STROBE, LOW); delay(1); digitalWrite(SPECTRUM_RESET, HIGH); delay(1); digitalWrite(SPECTRUM_STROBE, HIGH); delay(1); digitalWrite(SPECTRUM_STROBE, LOW); delay(1); digitalWrite(SPECTRUM_RESET, LOW); delay(5); for (int i = 0; i < 7; i++) { spectrum[i] = 0; } time = millis(); mode = MODE_AUTO; controlState = LOW; controlState_old = LOW; Serial.begin(9600); } void loop() { unsigned long timeNow = millis(); timeslice = timeNow - time; time = timeNow; // Calculate blink for internal mode LED int slicetime = time % 1000; int slicephase = slicetime / 100; if ( (slicephase % 2 == 0) && ( slicephase / 2 <= mode) ) { digitalWrite(LED_INT, HIGH); } else { digitalWrite(LED_INT, LOW); } // Update effective mode if (mode == MODE_AUTO) { if (silenceLock == 1 && silent == 0) { effectiveMode = MODE_MUSIC; } else if (silenceLock == 1) { effectiveMode = ((time / 1000) / TIME_PER_MODE) % NUM_PWM_MODES + NUM_SPECIAL_MODES; } } int localMode = effectiveMode; if (mode == MODE_AUTO) { localMode -= 2; } // Read spectrum; readSpectrum(); int fx1 = 0; int fx2 = 0; // Handle light control for programmatic mode if (effectiveMode != MODE_MUSIC) { pwm1 = constrain(constrain(getPWM(MODES[localMode][0], MODES[localMode][1], MODES[localMode][2]), pwm1 - MAX_PWM_DELTA, pwm1 + MAX_PWM_DELTA), 0, 255); pwm2 = constrain(constrain(getPWM(MODES[localMode][3], MODES[localMode][4], MODES[localMode][5]), pwm2 - MAX_PWM_DELTA, pwm2 + MAX_PWM_DELTA), 0, 255); pwm3 = constrain(constrain(getPWM(MODES[localMode][6], MODES[localMode][7], MODES[localMode][8]), pwm3 - MAX_PWM_DELTA, pwm3 + MAX_PWM_DELTA), 0, 255); pwm4 = constrain(constrain(getPWM(MODES[localMode][9], MODES[localMode][10], MODES[localMode][11]), pwm4 - MAX_PWM_DELTA, pwm4 + MAX_PWM_DELTA), 0, 255); fx1 = getFX(MODES[localMode][12], MODES[localMode][13], MODES[localMode][14]); fx2 = getFX(MODES[localMode][15], MODES[localMode][16], MODES[localMode][17]); } else { pwm1 = constrain(constrain(reducedSpectrum[0], pwm1 - MAX_ORGAN_DELTA, reducedSpectrum[0] + MAX_PWM_DELTA), 0, 255); pwm2 = constrain(constrain(reducedSpectrum[1], pwm2 - MAX_ORGAN_DELTA, reducedSpectrum[1] + MAX_PWM_DELTA), 0, 255); pwm3 = constrain(constrain(reducedSpectrum[2], pwm3 - MAX_ORGAN_DELTA, reducedSpectrum[2] + MAX_PWM_DELTA), 0, 255); pwm4 = constrain(constrain(reducedSpectrum[3], pwm4 - MAX_ORGAN_DELTA, reducedSpectrum[3] + MAX_PWM_DELTA), 0, 255); if ( reducedSpectrum[0] + reducedSpectrum[1] + reducedSpectrum[2] + reducedSpectrum[3] > 180) { fx1 = HIGH; } else { fx1 = LOW; } if ( max(max(max(reducedSpectrum[0], reducedSpectrum[1]), reducedSpectrum[2]), reducedSpectrum[3]) > 152) { fx2 = HIGH; } else { fx2 = LOW; } } analogWrite(LED_PWM1, pwm1); analogWrite(LED_PWM2, pwm2); analogWrite(LED_PWM3, pwm3); analogWrite(LED_PWM4, pwm4); digitalWrite(LED_FX1, fx1); digitalWrite(LED_FX2, fx2); // Check for manual mode change controlState = digitalRead(IN_CONTROL); if ( (controlState == HIGH) && (controlState_old == LOW) ) { //Serial.println("Button pressed"); mode = (mode + 1) % NUM_MODES; effectiveMode = mode; } controlState_old = controlState; // Delay and end loop delay(GLOBAL_SLICE_TIME); } int getPWM(int pwmtype, int timefactor, int offset) { int slicetime = (time / timefactor + offset) % 256; int pwm = 0; switch (pwmtype) { case PWM_FADEIN: { pwm = slicetime; break; } case PWM_FADEOUT: { pwm = 0 - slicetime; break; } case PWM_FADEINOUT: { if (slicetime < 128) { pwm = 2 * slicetime; } else { pwm = 256 - (2 * (slicetime - 128)); } break; } case PWM_BRIEFINOUT: { if ( (slicetime > 64) && (slicetime <= 128) ) { pwm = 4 * (slicetime - 64); } else if ( (slicetime > 128) && (slicetime <= 190) ) { pwm = 256 - (4 * (slicetime - 128)); } break; } case PWM_TWINKLE: { if ( (slicetime / 8) % 2 == 0) { pwm = 255; } else { pwm = 0; } break; } } return pwm; } int getFX(int fxtype, int timefactor, int offset) { int slicetime = (time / timefactor + offset) % 256; int fx = LOW; switch (fxtype) { case (FX_ON): { fx = HIGH; } case (FX_BLINK): { if (slicetime < 128) { fx = HIGH; } } } return fx; } int calculateSpectrumValue(int value) { float fValue = ((float) value - DC_BIAS) * gain; return constrain((int) fValue, 0, 1023); } // Read 7 band equalizer. void readSpectrum() { int dbg = 0; int time = millis(); if (time % 250 < 5) { dbg = 1; } // Band 0 = Lowest Frequencies. for(int i = 0; i < 7; i++) { //Read each channel twice and take the average spectrum[i] = ( analogRead(SPECTRUM_LEFT) + analogRead(SPECTRUM_LEFT) + analogRead(SPECTRUM_RIGHT) + analogRead(SPECTRUM_RIGHT) ) >> 2; digitalWrite(SPECTRUM_STROBE, HIGH); digitalWrite(SPECTRUM_STROBE, LOW); } reducedSpectrum[0] = calculateSpectrumValue((spectrum[0] + spectrum[1]) >> 1); reducedSpectrum[1] = calculateSpectrumValue(spectrum[2]); reducedSpectrum[2] = calculateSpectrumValue((spectrum[3] + spectrum[4]) >> 1); reducedSpectrum[3] = calculateSpectrumValue((spectrum[5] + spectrum[6]) >> 1); int isSilent = 0; int totalSound = 0; for (int i = 0; i < 4; i++) { maxAudio = max(maxAudio, reducedSpectrum[i]); totalSound += reducedSpectrum[i]; } if (totalSound < NOISE_FLOOR) { isSilent = 1; } if (silent == 0 && isSilent == 1) { //Serial.println("DETECTED SILENCE"); silent = 1; silenceTimestamp = time; silenceLock = 0; } if (silent == 1 && isSilent == 0) { //Serial.println("DETECTED SOUND"); silent = 0; silenceTimestamp = time; silenceLock = 0; } if (silenceLock == 0 && (time - silenceTimestamp) > SILENCE_TIME_TO_LOCK) { //Serial.println("LOCKING"); silenceLock = 1; if (silent == 1) { maxAudio = 255; } } if (silent == 0 && silenceLock == 1 && (time - gainTimestamp) > GAIN_TIME_TO_ADJUST) { float newGain = 128.0 / (float)maxAudio; if (newGain != gain) { //Serial.println(gain); gain = 0.8 * gain + 0.2 * newGain; gainTimestamp = time; maxAudio *= 0.75; } } }