/*
**********************************************************************
* Stereo VU Meter for 1 or 2 LED rings or strips build by ericBcreator
* Designed to be used with an Arduino UNO, Nano or compatible device.
**********************************************************************
* Last updated 20171105 by ericBcreator
*
* This code is free for personal use, not for commercial purposes.
* Please leave this header intact.
*
* contact: [email protected]
**********************************************************************
*/

//#define DEBUG                                             // for debugging

//
// include the NeoPixel library:
//

#include "Adafruit_NeoPixel.h"

//
// uncomment the definition for the connected strip or ring(s)
//

//#define led_ring_60
//#define led_strip_60
//#define led_strip_30
#define led_2_rings_24
//#define led_2_rings_30
//#define led_strip_200
//#define led_strip_144

//
// important setting: using potentiometer sensor values or not
// This setting has to be set right or the script will not work correctly:
// - set this to true if using potentiometers
// - set this to false if not using potentiometers
//

const int useSensorValues = true;                         

//
// setup pins
//

int leftPin = A0, rightPin = A1;                          // left audio in on analog 0, right on analog 1
int brightnessPin = A4, sensitivityPin = A5;              // potentiometers for brightness and sensitivity on analog 4 and 5
int stripPin = 5;                                         // DIN of leds on digital pin 6
int showPeaksPin = 7;                                     // switch to toggle peaks on or off on digital pin 7
int momentarySwitch = false;                              // set false for an on/off toggle switch

//
// setup variables for the number of leds and led strip or 2 rings
//

#if defined (led_ring_60)
  //settings for a 60 led ring

  int stripNumOfLeds = 60;                                  // the total number of leds
  uint32_t stripColor[31];                                  // half of the number of leds + 1
  int displayMiddleLed = false;                             // display the middle led (blue). set to true for one strip, false for two strips or rings
  int splitStrip = true;                                    // set to true when using 2 strips or rings, false for one strip
  int middleOffset = 0;                                     // offset for the middle led when using one strip
  int startupAnimationDelay = 6;                            // delay for the startup animation
  int orangeLimitAmount = 0;                                // limit the amount of green of the middle LEDs to make them more orange
  int swapLeftRight = false;                                // swap the left and right input values or not
  
  int dropDelay = 4;                                        // hold time before dropping the leds
  float dropFactor = .92;                                   // value for dropping the leds
  
  int peakTimeNoDropDelay = 250;                            // peak hold time when not dropping the peaks (set droppingPeak true or false)
  int peakTimeFirstDropDelay = 130;                         // peak hold time when dropping the first peak
  int peakTimeDropDelay = 7;                                // peak hold time when dropping the rest of the peaks
  float peakDropFactor = .93;                               // value for dropping the peaks
  int droppingPeakFade = false;                             // display the dropping peak fading to black or not
  
  int bouncingPeaksNumOfLeds = 6;                           // how many leds to bounce up (max)
  int bouncingPeakDelay = 4;                                // delay between peak bounce updates
  int bouncingPeakCounterInc = 10;                          // increase counter for each bounce update. note: it uses a 0-180 sin function for the bouncing

#elif defined (led_strip_60)
  //settings for a 60 led ring

  int stripNumOfLeds = 60;                                  // the total number of leds
  uint32_t stripColor[31];                                  // half of the number of leds + 1
  int displayMiddleLed = true;                              // display the middle led (blue). set to true for one strip, false for two strips or rings
  int splitStrip = false;                                   // set to true when using 2 strips or rings, false for one strip
  int middleOffset = 1;                                     // offset for the middle led when using one strip
  int startupAnimationDelay = 6;                            // delay for the startup animation
  int orangeLimitAmount = 0;                                // limit the amount of green of the middle LEDs to make them more orange
  int swapLeftRight = false;                                // swap the left and right input values or not
  
  int dropDelay = 4;                                        // hold time before dropping the leds
  float dropFactor = .92;                                   // value for dropping the leds
  
  int peakTimeNoDropDelay = 250;                            // peak hold time when not dropping the peaks (set droppingPeak true or false)
  int peakTimeFirstDropDelay = 130;                         // peak hold time when dropping the first peak
  int peakTimeDropDelay = 7;                                // peak hold time when dropping the rest of the peaks
  float peakDropFactor = .93;                               // value for dropping the peaks
  int droppingPeakFade = false;                             // display the dropping peak fading to black or not
  
  int bouncingPeaksNumOfLeds = 6;                           // how many leds to bounce up (max)
  int bouncingPeakDelay = 4;                                // delay between peak bounce updates
  int bouncingPeakCounterInc = 10;                          // increase counter for each bounce update. note: it uses a 0-180 sin function for the bouncing

#elif defined (led_strip_30)
  //settings for a 30 led strip

  int stripNumOfLeds = 30;                                  // the total number of leds
  uint32_t stripColor[16];                                  // half of the number of leds + 1
  int displayMiddleLed = true;                              // display the middle led (blue). set to true for one strip, false for two strips or rings
  int splitStrip = false;                                   // set to true when using 2 strips or rings, false for one strip
  int middleOffset = 1;                                     // offset for the middle led when using one strip
  int startupAnimationDelay = 10;                           // delay for the startup animation
  int orangeLimitAmount = 0;                                // limit the amount of green of the middle LEDs to make them more orange
  int swapLeftRight = false;                                // swap the left and right input values or not
  
  int dropDelay = 10;                                       // hold time before dropping the leds
  float dropFactor = .9;                                    // value for dropping the leds
  
  int peakTimeNoDropDelay = 250;                            // peak hold time when not dropping the peaks (set droppingPeak true or false)
  int peakTimeFirstDropDelay = 150;                         // peak hold time when dropping the first peak
  int peakTimeDropDelay = 15;                               // peak hold time when dropping the rest of the peaks
  float peakDropFactor = .94;                               // value for dropping the peaks
  int droppingPeakFade = false;                             // display the dropping peak fading to black or not
  
  int bouncingPeaksNumOfLeds = 3;                           // how many leds to bounce up (max)
  int bouncingPeakDelay = 4;                                // delay between peak bounce updates
  int bouncingPeakCounterInc = 9;                           // increase counter for each bounce update. note: it uses a 0-180 sin function for the bouncing

#elif defined (led_2_rings_24)
  //settings for 2 24 led rings

  int stripNumOfLeds = 48;
  uint32_t stripColor[25];
  int displayMiddleLed = false;
  int splitStrip = true;
  int middleOffset = 0;
  int startupAnimationDelay = 5;
  int orangeLimitAmount = 0;
  int swapLeftRight = false;
  
  int dropDelay = 2;
  float dropFactor = .96;
  
  int peakTimeNoDropDelay = 250;
  int peakTimeFirstDropDelay = 100;
  int peakTimeDropDelay = 10;
  float peakDropFactor = .94;
  int droppingPeakFade = false;
  
  int bouncingPeaksNumOfLeds = 3;
  int bouncingPeakDelay = 4;
  int bouncingPeakCounterInc = 9;

#elif defined(led_2_rings_30)
  //settings for 2 30 led rings

  int stripNumOfLeds = 60;
  uint32_t stripColor[31];
  int displayMiddleLed = false;
  int splitStrip = true;
  int middleOffset = 0;
  int startupAnimationDelay = 5;
  int orangeLimitAmount = 0;
  int swapLeftRight = false;
  
  int dropDelay = 2;
  float dropFactor = .96;
  
  int peakTimeNoDropDelay = 250;
  int peakTimeFirstDropDelay = 100;
  int peakTimeDropDelay = 10;
  float peakDropFactor = .94;
  int droppingPeakFade = false;
  
  int bouncingPeaksNumOfLeds = 3;
  int bouncingPeakDelay = 4;
  int bouncingPeakCounterInc = 9;

#elif defined (led_strip_200)
  //settings for a 200 led strip

  int stripNumOfLeds = 200;
  uint32_t stripColor[101];
  int displayMiddleLed = false;
  int splitStrip = true;
  int middleOffset = 0;
  int startupAnimationDelay = 1;
  int orangeLimitAmount = 0;
  int swapLeftRight = false;
  
  int dropDelay = 10;
  float dropFactor = .96;
  
  int peakTimeNoDropDelay = 250;
  int peakTimeFirstDropDelay = 100;
  int peakTimeDropDelay = 30;
  float peakDropFactor = .99;
  int droppingPeakFade = false;
  
  int bouncingPeaksNumOfLeds = 8;
  int bouncingPeakDelay = 4;
  int bouncingPeakCounterInc = 9;

#elif defined (led_strip_144)
  //settings for a 200 led strip

  int stripNumOfLeds = 145;
  uint32_t stripColor[73];
  int displayMiddleLed = true;
  int splitStrip = false;
  int middleOffset = 1;
  int startupAnimationDelay = 1;
  int orangeLimitAmount = 0;
  int swapLeftRight = false;
  
  int dropDelay = 10;
  float dropFactor = .85;
  
  int peakTimeNoDropDelay = 250;
  int peakTimeFirstDropDelay = 100;
  int peakTimeDropDelay = 5;
  float peakDropFactor = .94;
  int droppingPeakFade = false;
  
  int bouncingPeaksNumOfLeds = 10;
  int bouncingPeakDelay = 2;
  int bouncingPeakCounterInc = 10;
#endif

//
// setup other variables, user editable
//

// basic settings
int minValue = 10;                                        // min analog input value
int maxValue = 350;                                       // max analog input value (0-1023 equals 0-5V)
int sensitivityValue = 128;                               // 0 - 255, initial value (value read from the potentiometer if useSensorValues = true)
int maxSensitivity = 2 * 255;                             // let the 'volume' go up to 200%!
int ledBrightness = 30;                                   // 0 - 255, initial value (value read from the potentiometer if useSensorValues = true)
int sensorDeviationBrightness = 1;                        // eliminate fluctuating values
int overflowDelay = 20;                                   // overflow hold time

// peak settings
int displayPeaks = true;                                  // value will be set by the switch if useSensorValues = true
int droppingPeak = true;                                  // display dropping peaks or not. note: displayPeaks has to be true 
int bouncingPeaks = true;                                 // display bouncing peaks or not. note: displayPeaks has to be true 

//
// initialize other variables needed for the sketch
//

int numOfSegments = stripNumOfLeds / 2;
int halfNumOfSegments = numOfSegments / 2; 
int stripMiddle = stripNumOfLeds / 2;
int maxDisplaySegments = stripMiddle - 1;
float sensitivityFactor;

int brightnessValue, prevBrightnessValue;
float ledFactor, ledFactor_div_numOfSegments;

int leftValue = 0, rightValue = 0, maxReadValue = 0;
int leftAnalogValue = 0, rightAnalogValue = 0;

int prevLeftValue = 0, prevRightValue = 0;
int prevLeftAnalogValue = 0, prevRightAnalogValue = 0;

int i, j;
int dropLeft, dropRight;
int leftDropTime, rightDropTime;

int leftPeak = 0, rightPeak = 0;
int leftPeakTime = 0, rightPeakTime = 0;
int leftFirstPeak = true, rightFirstPeak = true;
int readShowPeaksPin, prevReadShowPeaksPin;

uint32_t stripMiddleColor, stripOverflowColor, stripHoldColor;

int leftPeakBouncing = false, rightPeakBouncing = false;
int leftPeakBounce = 0, rightPeakBounce = 0;
int prevLeftPeakBounce = 0, prevRightPeakBounce = 0;
int leftPeakBounceCounter = 0, rightPeakBounceCounter = 0;
int leftPeakBounceDelayCounter = 0, rightPeakBounceDelayCounter = 0;

//
// initialize the strip or rings
//

Adafruit_NeoPixel strip = Adafruit_NeoPixel(stripNumOfLeds, stripPin, NEO_GRB + NEO_KHZ800);

//
// setup
//

void setup() {
  #ifdef DEBUG
    Serial.begin(9600);
  #endif
  
  pinMode(showPeaksPin, INPUT);  

  strip.begin();
  
  setStripColors();
  startupAnimation();
  
  if (useSensorValues) 
    setInitialDisplayPeaks();
  else
    setSensitivityFactor();
}

//
// main loop
//

void loop() {
  if (useSensorValues) 
    readSensorValues();
  
  readValues();
  drawValues();

  if (displayPeaks) {
    getPeaks();
    drawPeaks();
  }

  storePrevValues();
}

// 
// functions
//

void setInitialDisplayPeaks() {
  readShowPeaksPin = digitalRead(showPeaksPin);
  if (readShowPeaksPin == HIGH)
    displayPeaks = false;
  else
    displayPeaks = true;
  prevReadShowPeaksPin = readShowPeaksPin;
}

void readSensorValues() {
  readShowPeaksPin = digitalRead(showPeaksPin);

  if (momentarySwitch) {
    if (readShowPeaksPin == LOW && prevReadShowPeaksPin == HIGH) {
      if (displayPeaks == true) {
        displayPeaks = false;
        clearLeftPeak();
        clearRightPeak();        
        if (momentarySwitch)
          while (digitalRead(showPeaksPin) == LOW) {}
      }
      else {
        displayPeaks = true;
      }
    }
  } 
  else {
    if (readShowPeaksPin == LOW && prevReadShowPeaksPin == HIGH) 
      displayPeaks = true;
    else if (readShowPeaksPin == HIGH && prevReadShowPeaksPin == LOW) {
      displayPeaks = false;
      clearLeftPeak();
      clearRightPeak();        
    }
  }
  prevReadShowPeaksPin = readShowPeaksPin;
  
  brightnessValue = analogRead(brightnessPin);
  brightnessValue = map(brightnessValue, 0, 1023, 0, 255);
  
  if (abs(brightnessValue - prevBrightnessValue) > sensorDeviationBrightness) {
    ledBrightness = brightnessValue;
    setStripColors();
    prevBrightnessValue = brightnessValue;
  }

  sensitivityValue = analogRead(sensitivityPin);
  sensitivityValue = map(sensitivityValue, 0, 1023, 0, 255);
  setSensitivityFactor();
}

void setSensitivityFactor() {
  //sensitivityValue_div_numOfSegments = sensitivityValue / numOfSegments;
  sensitivityFactor = ((float) sensitivityValue / 255 * (float) maxSensitivity / 255);
}

void readValues() {
  leftAnalogValue = analogRead(leftPin);
  rightAnalogValue = analogRead(rightPin);

  if (swapLeftRight) {
    int tempValue = leftAnalogValue;
    leftAnalogValue = rightAnalogValue;
    rightAnalogValue = tempValue;
  }

  if (leftAnalogValue < prevLeftAnalogValue) {
    leftDropTime++;
    if (leftDropTime > dropDelay) {
      leftAnalogValue = prevLeftAnalogValue * dropFactor;
      leftDropTime = 0;
    }
    else
      leftAnalogValue = prevLeftAnalogValue;
  }
   
  if (rightAnalogValue < prevRightAnalogValue) {
    rightDropTime++;
    if (rightDropTime > dropDelay) {
      rightAnalogValue = prevRightAnalogValue * dropFactor;
      rightDropTime = 0;
    }
    else
      rightAnalogValue = prevRightAnalogValue;
  }

  #ifdef DEBUG
    Serial.print(leftAnalogValue);
    Serial.print(" ");
    Serial.println(rightAnalogValue);
  #endif  

  // map values  
  leftValue = map(leftAnalogValue * sensitivityFactor, minValue, maxValue, 0, maxDisplaySegments);
  rightValue = map(rightAnalogValue * sensitivityFactor, minValue, maxValue, 0, maxDisplaySegments);

  if (leftValue > maxDisplaySegments) {
      leftValue = maxDisplaySegments;
      drawOverflow();
  }
  
  if (rightValue > maxDisplaySegments) {
      rightValue = maxDisplaySegments;
      drawOverflow();
  }
}

void storePrevValues() {
  prevLeftAnalogValue = leftAnalogValue;
  prevRightAnalogValue = rightAnalogValue;

  prevLeftValue = leftValue;
  prevRightValue = rightValue;
}

void getPeaks() {
  if (leftValue > leftPeak) {
    leftPeak = leftValue;
    leftPeakTime = 0;
    leftFirstPeak = true;

    if (bouncingPeaks) {
      leftPeakBouncing = true;
      leftPeakBounceCounter = 0;
      leftPeakBounceDelayCounter = 0;
    }
  }
  else {
    leftPeakTime++;
    if (droppingPeak) {
      if (leftFirstPeak) {
        if (leftPeakTime > peakTimeFirstDropDelay) {
          clearLeftPeak();
          leftFirstPeak = false;
        }
      }
      else {
        if (leftPeakTime > peakTimeDropDelay) {
          clearLeftPeak();
        }
      }
    }
    else {
      if (leftPeakTime > peakTimeNoDropDelay) {
        clearLeftPeak();
      }
    }
  }

  if (leftPeakBouncing) {
    if (leftFirstPeak) {
      leftPeakBounceDelayCounter++;
      if (leftPeakBounceDelayCounter >= bouncingPeakDelay) {
        leftPeakBounceDelayCounter = 0;
        leftPeakBounceCounter += bouncingPeakCounterInc;
  
        if (leftPeakBounceCounter >= 180) {
          clearLeftBouncePeak();
          clearLeftBounce();
        }
        else {        
          leftPeakBounce = min((sin(leftPeakBounceCounter * 0.0174532925) * bouncingPeaksNumOfLeds), (maxDisplaySegments - leftPeak));
          if (leftPeakBounce != prevLeftPeakBounce) {
            clearLeftBouncePeak();
          }
          prevLeftPeakBounce = leftPeakBounce;
        }
      }
    }
  }

  if (rightValue > rightPeak) {
    rightPeak = rightValue;
    rightPeakTime = 0;
    rightFirstPeak = true;

    if (bouncingPeaks) {
      rightPeakBouncing = true;
      rightPeakBounceCounter = 0;
      rightPeakBounceDelayCounter = 0;
    }
  }
  else {
    rightPeakTime++;
    if (droppingPeak) {
      if (rightFirstPeak) {
        if (rightPeakTime > peakTimeFirstDropDelay) {
          clearRightPeak();
          rightFirstPeak = false;
        }
      }
      else {
        if (rightPeakTime > peakTimeDropDelay)
          clearRightPeak();
      }
    }
    else {
      if (rightPeakTime > peakTimeNoDropDelay)
        clearRightPeak();
    }
  }

  if (rightPeakBouncing) {
    if (rightFirstPeak) {
      rightPeakBounceDelayCounter++;
      if (rightPeakBounceDelayCounter >= bouncingPeakDelay) {
        rightPeakBounceDelayCounter = 0;
        rightPeakBounceCounter += bouncingPeakCounterInc;
  
        if (rightPeakBounceCounter >= 180) {
          clearRightBouncePeak();
          clearRightBounce();
        }
        else {        
          rightPeakBounce = min((sin(rightPeakBounceCounter * 0.0174532925) * bouncingPeaksNumOfLeds), (maxDisplaySegments - rightPeak));
          if (rightPeakBounce != prevRightPeakBounce) {
            clearRightBouncePeak();
          }
          prevRightPeakBounce = rightPeakBounce;
        }
      }
    }
  }
}

void drawValues() {
  if (splitStrip) {
    for (i = middleOffset; i < leftValue; i++)
      strip.setPixelColor(i, stripColor[i]);
  
    for (i = prevLeftValue; i > leftValue; i--)
      strip.setPixelColor(i, 0);
  
    for (i = middleOffset; i < rightValue; i++)
      strip.setPixelColor(stripMiddle + i, stripColor[i]);
  
    for (i = prevRightValue; i > rightValue; i--)
      strip.setPixelColor(stripMiddle + i, 0);
  }
  else {
    for (i = middleOffset; i < leftValue; i++)
      strip.setPixelColor(stripMiddle + i, stripColor[i]);
  
    for (i = prevLeftValue; i > leftValue; i--)
      strip.setPixelColor(stripMiddle + i, 0);
  
    for (i = middleOffset; i < rightValue; i++)
      strip.setPixelColor(stripMiddle - i, stripColor[i]);
  
    for (i = prevRightValue; i > rightValue; i--)
      strip.setPixelColor(stripMiddle - i, 0);
  }

  if (displayMiddleLed) strip.setPixelColor(stripMiddle, stripMiddleColor);
  strip.show();
}

void drawPeaks() {
  if (leftPeak > 0) {
    if (droppingPeakFade && leftPeakBouncing == false)
      stripHoldColor = strip.Color(max(1, (255 * leftPeak * ledFactor_div_numOfSegments)), 0, 0);
    else
      stripHoldColor = stripColor[numOfSegments];

    if (splitStrip)
      strip.setPixelColor((leftPeak + leftPeakBounce), stripHoldColor);
    else
      strip.setPixelColor(stripMiddle + (leftPeak + leftPeakBounce), stripHoldColor);
  } 
  
  if (rightPeak > 0) {
    if (droppingPeakFade && rightPeakBouncing == false)
      stripHoldColor = strip.Color(max(1, (255 * rightPeak * ledFactor_div_numOfSegments)), 0, 0);    
    else
      stripHoldColor = stripColor[numOfSegments];

    if (splitStrip)
      strip.setPixelColor(stripMiddle + rightPeak + prevRightPeakBounce, stripHoldColor);
    else 
      strip.setPixelColor(stripMiddle - (rightPeak + prevRightPeakBounce), stripHoldColor);
  }

  if (leftPeak > 0 || rightPeak > 0)
    strip.show();    
}

void clearLeftPeak() {
  if (splitStrip)
    strip.setPixelColor((leftPeak + prevLeftPeakBounce), 0);
  else
    strip.setPixelColor(stripMiddle + (leftPeak + prevLeftPeakBounce), 0);

  if (droppingPeak)
    leftPeak = leftPeak * peakDropFactor;
  else
    leftPeak = 0;
  leftPeakTime = 0;
}

void clearLeftBounce() {
  leftPeakBouncing = false;
  leftPeakBounceCounter = 0;
  leftPeakBounce = 0;
  prevLeftPeakBounce = 0;
}

void clearLeftBouncePeak() {
  if (splitStrip)
    strip.setPixelColor((leftPeak + prevLeftPeakBounce), 0);
  else
    strip.setPixelColor(stripMiddle + (leftPeak + prevLeftPeakBounce), 0);
}

void clearRightPeak() {
  if (splitStrip)
    strip.setPixelColor(stripMiddle + rightPeak + prevRightPeakBounce, 0);
  else 
    strip.setPixelColor(stripMiddle - (rightPeak + prevRightPeakBounce), 0);
  
  if (droppingPeak)
    rightPeak = rightPeak * peakDropFactor;
  else
    rightPeak = 0;
  rightPeakTime = 0;
}

void clearRightBounce() {
  rightPeakBouncing = false;
  rightPeakBounceCounter = 0;
  rightPeakBounce = 0;
  prevRightPeakBounce = 0;
}

void clearRightBouncePeak() {
  if (splitStrip)
    strip.setPixelColor((stripMiddle + rightPeak + prevRightPeakBounce), 0);
  else
    strip.setPixelColor(stripMiddle - (rightPeak + prevRightPeakBounce), 0);
}

void drawOverflow() {
  for (i = 0; i <= numOfSegments; i++) {
    strip.setPixelColor(stripMiddle + i, stripOverflowColor);
    strip.setPixelColor(stripMiddle - i, stripOverflowColor);
  }
  strip.show();
  delay(overflowDelay);

  for (i = 0; i <= numOfSegments; i++) {
    strip.setPixelColor(stripMiddle + i, 0);
    strip.setPixelColor(stripMiddle - i, 0);
  }
  strip.show();
}

void setStripColors() {
  int orangeLimit;
  ledFactor = (float)ledBrightness / 255;

  float orangeFactor = orangeLimitAmount / halfNumOfSegments;
  ledFactor_div_numOfSegments = ledFactor / numOfSegments;
  
  stripOverflowColor = strip.Color(min(255, 255 * ledFactor * 1.5), 0, 0);
  stripMiddleColor = strip.Color(0, 0, 255 * ledFactor);

  stripColor[0] = strip.Color(0, 255 * ledFactor, 0);
  for (i = 1; i <= numOfSegments; i++) {
    if (i <= halfNumOfSegments)
      orangeLimit = (i * orangeFactor);
    else
      orangeLimit = ((numOfSegments - i) * orangeFactor);

    stripColor[i] = strip.Color((255 * i * ledFactor_div_numOfSegments), ((255 - orangeLimit) * (numOfSegments - i) * ledFactor_div_numOfSegments), 0); 
  }

  stripHoldColor = stripColor[numOfSegments];
}

void startupAnimation() {  
  for (j = 0; j < 2; j++) {
    for (i = 0; i <= numOfSegments; i++) {
      strip.setPixelColor(stripMiddle - i, stripColor[i]);
      strip.setPixelColor(stripMiddle + i, stripColor[i]);
      strip.show();
      delay(startupAnimationDelay);
    }
    
    for (i = 0; i <= numOfSegments; i++) {
      strip.setPixelColor(stripMiddle + i, 0);
      strip.setPixelColor(stripMiddle - i, 0);
      strip.show();
      delay(startupAnimationDelay);
    }
  }
}