/*
 * Gray_ButtonsAndSwitches
 * Code by: Gray Mack
 * Published at: N/A
 * Description: A few switches, button, ws2812 8 pixel strip, piezo element or micro speaker
 * Why I made this:
 * Just playing around, quick demo of switches and activities
 * 
 * License: MIT License
 *          https://choosealicense.com/licenses/mit/
 * Created: 11/14/2022
 * Board: Arduino Uno or compatible
 * Select Board: Arduino Uno
 * Processor: ATMEGA328p
 * 
 * Connections:
 *  connect 3 toggle switches to 2,3,4 (use internal pullups) the other end to ground
 *  connect button to 6 (use internal pullups) the other end of switched so ground
 *  lighted safety toggle switch- This switch is tricky since it only has 3 leads
 *  one end to +5v which is led annode and switch input
 *  and the switch lead to pin 5 (use physical pull down 10k resistor)
 *  the switches LED should already have a resistor inside the switch, connect through pin 7
 *  pin 8 goes to WS2812 pixels. We should have no problem powering 8 through the regulator 5v supply. Ensure wires do not come out while powered which can damage pixels
 *  connect mini 16ohm speaker/piezo element to pin 9   (and the other lead to GND)
 * 
 * 
 * 11/14/2022 initial code creation
 * //2022
 * //2022
 * //2022 
 */

// ----[ included libraries ]--------------------------------
#include <FastLED.h> // LED pixel library for neopixels
#include <Bounce2.h> // button debounce library v2.71   https://github.com/thomasfredericks/Bounce2/


// ----[ configuration ]------------------------------------
#define SerialBaud 9600
#define LED_COLOR_ORDER GRB
#define LED_CHIPSET WS2812

// ----[ pin definitions ]-----------------------------------
const int PIN_TOGGLE_ACC1 = 2;
const int PIN_TOGGLE_ACC2 = 3;
const int PIN_TOGGLE_ACC3 = 4;
const int PIN_TOGGLE_MAIN_SYSTEM = 5;
const int PIN_START_BUTTON = 6;
const int PIN_MAIN_SYSTEM_LED = 7;
const int PIN_PIXELS = 8;
const int PIN_SPEAKER = 9;
// LED_BUILTIN is internally defined as 13

// ----[ constants ]-----------------------------------------
const int NUM_LEDS = 8; // How many leds in your strip
const int LED_BRIGHTNESS = 220; // use 60 on real LEDs but need brighter for simulator
const int MAIN_SYSTEM_LED_ON = LOW;
const int MAIN_SYSTEM_LED_OFF = HIGH;

// ----[ predeclarations ]-----------------------------------


// ----[ global variables ]----------------------------------
CRGB leds[NUM_LEDS]; // Define the array of leds for FastLED
//Button2 ToggleAcc1, ToggleAcc2, ToggleAcc3;
//Button2 ToggleMainSystem;
//Button2 ButtonStart;

Bounce2::Button ToggleAcc1 = Bounce2::Button();
Bounce2::Button ToggleAcc2 = Bounce2::Button();
Bounce2::Button ToggleAcc3 = Bounce2::Button();
Bounce2::Button ToggleMainSystem =  Bounce2::Button();
Bounce2::Button ButtonStart = Bounce2::Button();
int oldSwitchValue;


// ----[ code ]----------------------------------------------

void setup() {
  Serial.begin(9600);
  Serial.println("\nArduino reset");

  ToggleAcc1.attach(PIN_TOGGLE_ACC1, INPUT_PULLUP);
  ToggleAcc1.setPressedState(LOW);
  ToggleAcc1.interval(100);
  ToggleAcc2.attach(PIN_TOGGLE_ACC2, INPUT_PULLUP);
  ToggleAcc2.setPressedState(LOW);
  ToggleAcc2.interval(100);
  ToggleAcc3.attach(PIN_TOGGLE_ACC3, INPUT_PULLUP);
  ToggleAcc3.setPressedState(LOW);
  ToggleAcc3.interval(100);
  ToggleMainSystem.attach(PIN_TOGGLE_MAIN_SYSTEM, INPUT);
  ToggleMainSystem.setPressedState(HIGH);  // active low, needs external pulldown because of LED in switch
  ToggleMainSystem.interval(100);
  ButtonStart.attach(PIN_START_BUTTON, INPUT_PULLUP);
  ButtonStart.setPressedState(LOW);
  ButtonStart.interval(20);
  
  pinMode(PIN_MAIN_SYSTEM_LED, OUTPUT);
  digitalWrite(PIN_MAIN_SYSTEM_LED, MAIN_SYSTEM_LED_ON);

  FastLED.addLeds<LED_CHIPSET, PIN_PIXELS, LED_COLOR_ORDER>(leds, NUM_LEDS);
  FastLED.clear();
  
  FastLED.setMaxPowerInVoltsAndMilliamps(5, 200);
  FastLED.setBrightness(LED_BRIGHTNESS);
  FastLED.show();
  
  pinMode(PIN_SPEAKER, OUTPUT);
  digitalWrite(PIN_SPEAKER, LOW); // caution mini speaker is a low ohm load, do not wright HIGH, tone command seems ok to use. Recommend piezo element instead.

  pinMode(LED_BUILTIN, OUTPUT);
  digitalWrite(LED_BUILTIN, LOW);
}

void loop() {
  ToggleAcc1.update();
  ToggleAcc2.update();
  ToggleAcc3.update();
  ToggleMainSystem.update();
  ButtonStart.update();
  
  if(ToggleAcc1.changed() && ToggleAcc1.isPressed())
    Serial.println("ACC1 on");
  if(ToggleAcc2.changed() && ToggleAcc2.isPressed())
    Serial.println("ACC2 on");
  if(ToggleAcc3.changed() && ToggleAcc3.isPressed())
    Serial.println("ACC3 on");
  if(ToggleMainSystem.changed() && ToggleMainSystem.isPressed())
    Serial.println("Main System on");
    
  int switchValue = (ToggleAcc1.isPressed()? 1 : 0) +
                    (ToggleAcc2.isPressed()? 2 : 0) +
                    (ToggleAcc3.isPressed()? 4 : 0);
  if(switchValue != oldSwitchValue)
  {
    oldSwitchValue = switchValue;
    Serial.print("switchValue: ");
    Serial.println(switchValue);
  }

  bool animating = false;
  if(ToggleMainSystem.isPressed())
  {
    switch(switchValue)
    {
      case 0: 
        break;
      case 1: 
        lightEffect1();
        animating = true;
        break;
      case 2:
        lightEffect2();
        animating = true;
        break;
      case 3:
        lightEffect3();
        animating = true;
        break;
      case 4:
        lightEffect4();
        animating = true;
        break;
      case 5:
        lightEffect5();
        animating = true;
        break;
      case 6:
        lightEffect6();
        animating = true;
        break;
      case 7:
        lightEffect7();
        animating = true;
        break;
    }
  }
  if(!animating)
  {
    lightsOff();
  }

  BlinkLed();

  if(ToggleMainSystem.isPressed() && ButtonStart.isPressed())
  {
    if(switchValue == 7)
      soundEffect2();
    else
      soundEffect3();
  }
  if(ButtonStart.released() || ToggleMainSystem.released())
  {
     noTone(PIN_SPEAKER);
  }
}

// ----[ support functions ]---------------------------------
void BlinkLed()
{
  static int systemLedState = MAIN_SYSTEM_LED_OFF;
  if(ToggleMainSystem.isPressed())
  {
      EVERY_N_MILLISECONDS(500)
      {
        systemLedState = !systemLedState;
      }
  }
  else
  {
    systemLedState = MAIN_SYSTEM_LED_OFF;
  }
  digitalWrite(PIN_MAIN_SYSTEM_LED, systemLedState);
}

void soundEffect3()
{ // up/down frequency
  const int lowTone = 31; // 31 is the minimum tone value
  const int highTone = 100;
  const int changeAmount = 6;
  const int delayAmount = 50;
  static int toneVal = lowTone;
  static bool soundUp = true;
  
  EVERY_N_MILLISECONDS(delayAmount)
  {
    tone(PIN_SPEAKER, toneVal);
    if(soundUp)
    {
      toneVal+=changeAmount;
      if(toneVal > highTone)
      {
        toneVal = highTone;
        soundUp = false;
      }
    }
    else // sound down
    {
      toneVal-=changeAmount;
      if(toneVal < lowTone)
      {
        toneVal = lowTone;
        soundUp = true;
      }
    }
  }
}

void soundEffect2()
{
  const int lowTone = 100;
  const int highTone = 800;
  const int changeAmount = 4;
  const int delayAmount = 50;
  static int toneVal = lowTone;
  static bool toggle = false;
  EVERY_N_MILLISECONDS(delayAmount)
  {
    if(toggle)
      tone(PIN_SPEAKER, toneVal);
    else
      tone(PIN_SPEAKER, highTone-toneVal);
    toggle = !toggle;
    toneVal += changeAmount;
    if(toneVal > highTone)
      toneVal = lowTone;
  }
}

void lightsOff()
{
  FastLED.clear();
  FastLED.show();
}

void lightEffect1() // red/blue/red/blue/red/blue
{
  const int delayAmount = 1000/2;
  static int counter = 0;
  EVERY_N_MILLISECONDS(delayAmount)
  {
    FastLED.clear();
    counter++;
    if(counter%2==0)
    {
      leds[0] = CRGB::Red;
    }
    else
    {
      leds[7] = CRGB::Blue;
    }
    FastLED.show();
  }
}

void lightEffect2() // back forth green
{
  const int delayAmount = 1000/8;
  static int counter = 0;
  static int cdirection = 1;
  EVERY_N_MILLISECONDS(delayAmount)
  {
    FastLED.clear();
    counter+=cdirection;
    if(counter >= NUM_LEDS) 
    {
      cdirection = -1;
      counter+=cdirection;
    }
    if(counter < 0)
    {
      cdirection = 1;
      counter+=cdirection;
    }
    leds[counter] = CRGB::Green;
    FastLED.show();
  }  
}

void lightEffect3() // center to outside on both sides
{
  const int delayAmount = 1000/2;
  static int counter = 0;
  EVERY_N_MILLISECONDS(delayAmount)
  {
    FastLED.clear();
    for(int i=counter; i<NUM_LEDS; i=i+2)
    {
      leds[i] = CRGB::Purple;
    }
    FastLED.show();
    counter = (counter == 0) ? 1 : 0;
  }
}

void lightEffect4() // center to outside on both sides
{
  const int delayAmount = 1000/6;
  static int counter = 0;
  EVERY_N_MILLISECONDS(delayAmount)
  {
    FastLED.clear();
    for(int i=0; i<counter%6; i++)
    {
      leds[3+i] = CRGB::Orange;
      leds[4-i] = CRGB::Orange;
    }
    FastLED.show();
    counter++;
  }
}

void lightEffect5() // rainbow
{
  const int delayAmount = 1000/20;
  const int changeAmount = 4;
  static byte counter = 0;
  EVERY_N_MILLISECONDS(delayAmount)
  {
    for(int i=0; i<NUM_LEDS; i++)
      leds[i] = CHSV((counter+i*20)%256, 255, 255);
    FastLED.show();
    counter+=changeAmount;
  }
}

void lightEffect6() // not implemented yet
{
  FastLED.clear();
  leds[0] = CRGB::Cyan;
  FastLED.show();
}

void lightEffect7() // random
{
  const int delayAmount = 10;
  EVERY_N_MILLISECONDS(delayAmount)
  {
    FastLED.clear();
    int i = random(NUM_LEDS);
    CRGB rcolor;
    switch(random(3))
    {
      case 0: rcolor = CRGB::Red; break;
      case 1: rcolor = CRGB::Green; break;
      case 2: rcolor = CRGB::Blue; break;
    }
    leds[i] = rcolor;
    FastLED.show();
  }
}

// good alarm sounds 1, 4, 5, 22
// sound 9 is like rocket sound