/*
 * Task5
 * 
 * Demonstrate using an interrupt routine to detect a button press.
 * This code does debounce the button.
 * 
 */

// Pin to read for button value
#define BUTTON_IN 3

// Boolean to show if pin value has changed
// Because this value is changed in the interrupt, must declare as volatile,
// which tells the compiler to always read this from memory, not a register
volatile bool pinChange; // Communicates from interrupt routine

// the setup function runs once when you press reset or power the board
void setup() {
  
  // Pin to read button state, use internal pull-up to avoid external resistor
  // This also emulates TTL inputs that float HI.
  // Use INPUT for normal digital input (emulates CMOS input)
  pinMode(BUTTON_IN, INPUT_PULLUP);

  // initialize serial communication at 9600 bits per second:
  Serial.begin(9600);

  // Write something out to show we are alive
  Serial.println(F("Starting..."));
    
  // Setup the interrupt handler on pin change
  // Must be attached to pin 2 or 3 on Nano
  attachInterrupt(digitalPinToInterrupt(BUTTON_IN), pinChangeRoutine, CHANGE);
}

// the loop function runs over and over again forever
void loop() {

  // Count how many times the button has been pressed
  // static means this is not reinitialized every loop
  static unsigned int nButton=0;

  // Previous button state (to find changes)
  static bool lastState; 

  // Get the logical button value (pressed = true) from debounce routine
  bool buttonValue = buttonState();

  // Has this changed?
  if (lastState != buttonValue) {
    lastState = buttonValue;  // Update last state
    
    // If it is now pressed, increment counter and print result
    if (buttonValue) {
      ++nButton;
 
      Serial.print(F(" The button has been pressed "));
      Serial.print(String(nButton));
      if (nButton==1) {
        Serial.println(F(" time"));
      }
      else {
        Serial.println(F(" times"));
      }
    }
  }
}

// Interrupt handler, this is called via the interrupt handler every 
// time a change is detected on the button pin.  Best to do very little here.
void pinChangeRoutine() {
  pinChange = true;
}

// Debounce routine, returns best guess of button state
bool buttonState() {

  // 8-bit storage used as shift register of past pin values
  static byte pinHistory = 0; 

  // Best guess of debounced button value
  static bool buttonValue = false;  

  // Time (in ms) since Arduino rebooted
  static unsigned long lastTime = 0;
  
  // If no change, just return last value
  if (!pinChange) return buttonValue;

  // Check how long since we last read the pin
  unsigned long currentTime = millis();
  if ((currentTime - lastTime) < 4) return buttonValue;
  lastTime = currentTime;
  
  // Read the pin and push value onto buttonHistory
  pinHistory = (pinHistory<<1);
  if (digitalRead(BUTTON_IN)) bitSet(pinHistory, 0);

  // If the value has been stable for the last 8 x 4ms = 32ms, consider it stable
  // buttonValue reflects whether the button is pressed (true) or not (false)
  if (pinHistory == 0xFF) {
    buttonValue = false;
    pinChange = false;  // Reset this to indicate we are in a stable state
  }
  if (pinHistory == 0x00) {
    buttonValue = true;
    pinChange = false;
  }
  return buttonValue;
}