// https://github.com/Sam-Kearns/Arduino-LED-Chaser/tree/master
// https://www.youtube.com/watch?v=pIHpoEE0R98

/***************************************************************
 * Arduino LED chaser with button control
 ***************************************************************
 * by Sam Kearns
 *
 * This circuit has 6 LEDs and 3 buttons, the LEDs are arranged in a circle
 *
 * The LEDs are connected to PWM outputs and their brightness is continuously adjusted on a sine wave pattern
 * The brightness of each led is 60 degrees out of phase with the next
 * This gives the impression of LED brightness rotating around the circle
 *
 * Button 1 increases the speed of rotation
 * Button 2 decreases the speed of rotation
 * Button 3 reverses the speed of rotation
 */

/***************************************************************
 * SETUP 
 ***************************************************************/
int numButtons = 3;                            // The number of buttons in the circuit
int buttonPin[3] = { 2, 4, 7 };       // An array of the pin numbers these buttons are connected to
boolean buttonState[3] = { false, false, false }; // An array indicating the last known state of each button


int ledPin[] = { 3, 5, 6, 9, 10, 11 };  // An array of the pin numbers these LEDs are connected to

// https://www.arduino.cc/reference/en/language/variables/utilities/sizeof/
// sizeof(); https://forum.arduino.cc/t/how-do-you-check-the-length-of-an-array/88325/7
int numLEDs = sizeof(ledPin) / sizeof(int); // The number of LEDs in the circuit 

// These variables control the sine wave for LED brightness
float angle = 0;                               // The current angle of the sine wave
float pi = 3.141592653589793238462643383279;   // pi constant for calculations
float increment = pi/255;                      // The amount the angle is incremented by each time the LEDs are updated
                                               // (Note there are 255 possible steps of LED brightness)
// These variables control the LED update speed
int loopCounter = 0;                           // Incremented on each iteration of the main loop 
int delayTime = 100;                           // How many iterations of the main loop to wait before updating the LEDS
int delayIncrement = 20;                       // When the faster/slower buttons are pushed, delayTime is changed by this much

void setup() {
    for (int i = 0; i < numButtons; i++) {      // Set the button pins to be inputs
        pinMode(buttonPin[i], INPUT_PULLUP);
    }

    for (int i = 0; i < numLEDs; i++) {         // Set the LED pins to be outputs
        pinMode(ledPin[i], OUTPUT);
    }
}

/***************************************************************
 * MAIN LOOP 
 ***************************************************************/
void loop() {
    if (buttonPressed(0) && delayTime >= delayIncrement) {  // If button 1 has been pressed increase the speed
        if (delayTime - delayIncrement < 0) {
            delayTime = 0;
        } else {
            delayTime -= delayIncrement;
        }
    }

    if (buttonPressed(1)) {                       // If button 2 has been pressed decrease the speed
        delayTime += delayIncrement;
    }

    if (buttonPressed(2)) {                       // If button 3 has been pressed reverse the direction of rotation
        increment = -increment;
    }

    if (loopCounter >= delayTime) {             // If the number of main loop iterations is high enough then change the LEDs
        float phase = 0;
        angle += increment;
        
        for (int i = 0; i < numLEDs; i++) {         // For each LED
            analogWrite(ledPin[i], (128*sin(angle+phase)+128)); // Set the LED brightness using a sin() function
            phase += pi/3;                // After setting each LED we increase the phase by 60 degrees
        }
        loopCounter = 0;                          // Reset the main loop counter
        
    } else {                                    // Otherwise increment the counter
        loopCounter++;
    }
}


/***************************************************************
 * FUNCTIONS 
 ***************************************************************/

/*
 * This function contains logic that ensures we only register each button press once
 * A button must be released and pressed again before it will be noticed again
 */
boolean buttonPressed(int button) {
    if (digitalRead(buttonPin[button]) == HIGH && buttonState[button] == false) {
        // The button has been pressed since we last checked it
        buttonState[button] = true;
        return true;
    } else if (digitalRead(buttonPin[button]) == LOW && buttonState[button] == true) {
        // The button has been released since we last checked it
        buttonState[button] = false;
    }
    return false;
}