/* programme pour gérer le bouton poussoir par interruptions 
*/


#define BUTTON_MIN_DELAY 10 // delai minimum entre 2 événements buffer (pour éliminer les rebonds)
#define BUTTON_BUFFER_SIZE 16 // taille du buffer des événements bouton

const byte ledPin = 3; //broche de la led
const byte buttonPin = 2; // broche du bouton poussoir

struct buttonBuffer { // type buttonBuffer
  unsigned long buf[BUTTON_BUFFER_SIZE]; // tableau des événements
  byte pos; // position courante dans le buffer pour l'écriture
  byte rpos; // position de lecture
} button;

void buttonInit(byte value) {
  /* fonction initialisation
   *  créé un événement à t=0 pour avoir toujours un précédent
   */
  button.buf[0] = value & 1; // événement à t = 0, value doit valoir 0 ou 1
  button.pos = 1; // position d'écriture dans le buffer
  button.rpos = 0; // position de lecture dans le buffer
}

void buttonChange() {
  /* fonction appelée par à chaque appui du bouton
   */
  unsigned long m = millis(); // m est le nombre de millisecondes depuis l'allumage de la carte remis à zéro tous les 50 jours
  byte value = digitalRead(buttonPin); // lit l'état du bouton
  if ((m - button.buf[(button.pos + BUTTON_BUFFER_SIZE -1) % BUTTON_BUFFER_SIZE] > BUTTON_MIN_DELAY) // ne garde l'événement que s'il est postérieur au délai minimum
      && (value != (byte)(button.buf[(button.pos + BUTTON_BUFFER_SIZE -1) % BUTTON_BUFFER_SIZE] & 1))) { // et différent du précédent
    button.buf[button.pos] = (millis() & 0xFFFFFFFE) | value; // utilise le bit de poids faible pour l'état du bouton poussoir 
    button.pos = (button.pos + 1) % BUTTON_BUFFER_SIZE; // incrémente la position d'écriture modulo la taille du buffer
  }
}

bool buttonNext(byte *value,unsigned long *t) {
  if (button.pos != button.rpos) { // si tout n'est pas lu
    *value = (byte) (button.buf[button.rpos] & 1); // value est l'état du bouton
    *t = button.buf[button.rpos] & 0xFFFFFFFE; // t est "l'heure" de l'événement
    button.rpos = (button.rpos + 1) % BUTTON_BUFFER_SIZE; // incrémente la position de lecture modulo la taille du buffer
    return true;
  } else {
    return false;
  }
}

void setup() {
  Serial.begin(115200); // initialise la liaison série 
  
  pinMode(ledPin, OUTPUT); // initialise la broche led en écriture
  pinMode(buttonPin, INPUT_PULLUP); // initialise la broche bouton en lecture avec activation de la résistance pullup
  buttonInit(digitalRead(buttonPin)); // initialise le buffer
  attachInterrupt(digitalPinToInterrupt(buttonPin), buttonChange, CHANGE); // branche l'interruption du bouton sur la fonction buttonChange
  
}

void loop() {
  byte value; // valeur du bouton
  unsigned long t; // "heure" du bouton
  
  if (buttonNext(&value,&t)) { // si il y a un nouvel événement
    Serial.print(value); // envoie les infos au moniteur série
    Serial.print(" ");
    Serial.println(t);
    digitalWrite(ledPin,1-value); // envoie l'état du bouton sur la led
  }
}
