#define USE_OCTOWS2811
#include <OctoWS2811.h>  // Extension de la librairie FASTLED pour utiliser plusieurs (ici 2) bandes de LEDs à la fois
#include <FastLED.h>

#define INPUT_CAPTEUR A4
#define NUM_LEDS 8
#define NUM_STRIPS 2
#define NUM_COLUMNS 100  // Nombre de colonnes affichées
#define vitesse_min 180  // Vitesse minimale de 180°/s

#define condition_debut_tour ((!Xbool and (val > magnXzero)))  // Vaut true si on vient de passer par le début du tour

//  Bitmap des caractères dans l'ordre ASCII, commence par "!", 5 octets par caractère :
const uint8_t alphabet[] = {
  0b00000000,  //
  0b00000000,  //
  0b01011111,  //  # #####
  0b00000000,  //
  0b00000000,  //

  0b00000000,  //
  0b00000111,  //      ###
  0b00000000,  //
  0b00000111,  //      ###
  0b00000000,  //

  0b00010100,  //    # #
  0b01111111,  //  #######
  0b00010100,  //    # #
  0b01111111,  //  #######
  0b00010100,  //    # #

  0b00100100,  //   #  #
  0b00101010,  //   # # #
  0b01111111,  //  #######
  0b00101010,  //   # # #
  0b00010010,  //    #  #

  0b00100011,  //   #   ##
  0b00010011,  //    #  ##
  0b00001000,  //     #
  0b01100100,  //  ##  #
  0b01100010,  //  ##   #

  0b00110110,  //   ## ##
  0b01001001,  //  #  #  #
  0b01010101,  //  # # # #
  0b00100010,  //   #   #
  0b01010000,  //  # #

  0b00000000,  //
  0b00000101,  //      # #
  0b00000011,  //       ##
  0b00000000,  //
  0b00000000,  //

  0b00000000,  //
  0b00011100,  //    ###
  0b00100010,  //   #   #
  0b01000001,  //  #     #
  0b00000000,  //

  0b00000000,  //
  0b01000001,  //  #     #
  0b00100010,  //   #   #
  0b00011100,  //    ###
  0b00000000,  //

  0b00001000,  //     #
  0b00101010,  //   # # #
  0b00011100,  //    ###
  0b00101010,  //   # # #
  0b00001000,  //     #

  0b00001000,  //     #
  0b00001000,  //     #
  0b00111110,  //   #####
  0b00001000,  //     #
  0b00001000,  //     #

  0b00000000,  //
  0b01010000,  //  # #
  0b00110000,  //   ##
  0b00000000,  //
  0b00000000,  //

  0b00001000,  //     #
  0b00001000,  //     #
  0b00001000,  //     #
  0b00001000,  //     #
  0b00001000,  //     #

  0b00000000,  //
  0b01100000,  //  ##
  0b01100000,  //  ##
  0b00000000,  //
  0b00000000,  //

  0b00100000,  //   #
  0b00010000,  //    #
  0b00001000,  //     #
  0b00000100,  //      #
  0b00000010,  //       #

  0b00111110,  //   #####
  0b01010001,  //  # #   #
  0b01001001,  //  #  #  #
  0b01000101,  //  #   # #
  0b00111110,  //   #####

  0b00000000,  //
  0b01000010,  //  #    #
  0b01111111,  //  #######
  0b01000000,  //  #
  0b00000000,  //

  0b01000010,  //  #    #
  0b01100001,  //  ##    #
  0b01010001,  //  # #   #
  0b01001001,  //  #  #  #
  0b01000110,  //  #   ##

  0b00100001,  //   #    #
  0b01000001,  //  #     #
  0b01000101,  //  #   # #
  0b01001011,  //  #  # ##
  0b00110001,  //   ##   #

  0b00011000,  //    ##
  0b00010100,  //    # #
  0b00010010,  //    #  #
  0b01111111,  //  #######
  0b00010000,  //    #

  0b00100111,  //   #  ###
  0b01000101,  //  #   # #
  0b01000101,  //  #   # #
  0b01000101,  //  #   # #
  0b00111001,  //   ###  #

  0b00111100,  //   ####
  0b01001010,  //  #  # #
  0b01001001,  //  #  #  #
  0b01001001,  //  #  #  #
  0b00110000,  //   ##

  0b00000001,  //        #
  0b01110001,  //  ###   #
  0b00001001,  //     #  #
  0b00000101,  //      # #
  0b00000011,  //       ##

  0b00110110,  //   ## ##
  0b01001001,  //  #  #  #
  0b01001001,  //  #  #  #
  0b01001001,  //  #  #  #
  0b00110110,  //   ## ##

  0b00000110,  //      ##
  0b01001001,  //  #  #  #
  0b01001001,  //  #  #  #
  0b00101001,  //   # #  #
  0b00011110,  //    ####

  0b00000000,  //
  0b00110110,  //   ## ##
  0b00110110,  //   ## ##
  0b00000000,  //
  0b00000000,  //

  0b00000000,  //
  0b01010110,  //  # # ##
  0b00110110,  //   ## ##
  0b00000000,  //
  0b00000000,  //

  0b00000000,  //
  0b00001000,  //     #
  0b00010100,  //    # #
  0b00100010,  //   #   #
  0b01000001,  //  #     #

  0b00010100,  //    # #
  0b00010100,  //    # #
  0b00010100,  //    # #
  0b00010100,  //    # #
  0b00010100,  //    # #

  0b01000001,  //  #     #
  0b00100010,  //   #   #
  0b00010100,  //    # #
  0b00001000,  //     #
  0b00000000,  //

  0b00000010,  //       #
  0b00000001,  //        #
  0b01010001,  //  # #   #
  0b00001001,  //     #  #
  0b00000110,  //      ##

  0b00110010,  //   ##  #
  0b01001001,  //  #  #  #
  0b01111001,  //  ####  #
  0b01000001,  //  #     #
  0b00111110,  //   #####

  0b01111110,  //  ######
  0b00010001,  //    #   #
  0b00010001,  //    #   #
  0b00010001,  //    #   #
  0b01111110,  //  ######

  0b01111111,  //  #######
  0b01001001,  //  #  #  #
  0b01001001,  //  #  #  #
  0b01001001,  //  #  #  #
  0b00110110,  //   ## ##

  0b00111110,  //   #####
  0b01000001,  //  #     #
  0b01000001,  //  #     #
  0b01000001,  //  #     #
  0b00100010,  //   #   #

  0b01111111,  //  #######
  0b01000001,  //  #     #
  0b01000001,  //  #     #
  0b00100010,  //   #   #
  0b00011100,  //    ###

  0b01111111,  //  #######
  0b01001001,  //  #  #  #
  0b01001001,  //  #  #  #
  0b01001001,  //  #  #  #
  0b01000001,  //  #     #

  0b01111111,  //  #######
  0b00001001,  //     #  #
  0b00001001,  //     #  #
  0b00000001,  //        #
  0b00000001,  //        #

  0b00111110,  //   #####
  0b01000001,  //  #     #
  0b01000001,  //  #     #
  0b01010001,  //  # #   #
  0b00110010,  //   ##  #

  0b01111111,  //  #######
  0b00001000,  //     #
  0b00001000,  //     #
  0b00001000,  //     #
  0b01111111,  //  #######

  0b00000000,  //
  0b01000001,  //  #     #
  0b01111111,  //  #######
  0b01000001,  //  #     #
  0b00000000,  //

  0b00100000,  //   #
  0b01000000,  //  #
  0b01000001,  //  #     #
  0b00111111,  //   ######
  0b00000001,  //        #

  0b01111111,  //  #######
  0b00001000,  //     #
  0b00010100,  //    # #
  0b00100010,  //   #   #
  0b01000001,  //  #     #

  0b01111111,  //  #######
  0b01000000,  //  #
  0b01000000,  //  #
  0b01000000,  //  #
  0b01000000,  //  #

  0b01111111,  //  #######
  0b00000010,  //       #
  0b00000100,  //      #
  0b00000010,  //       #
  0b01111111,  //  #######

  0b01111111,  //  #######
  0b00000100,  //      #
  0b00001000,  //     #
  0b00010000,  //    #
  0b01111111,  //  #######

  0b00111110,  //   #####
  0b01000001,  //  #     #
  0b01000001,  //  #     #
  0b01000001,  //  #     #
  0b00111110,  //   #####

  0b01111111,  //  #######
  0b00001001,  //     #  #
  0b00001001,  //     #  #
  0b00001001,  //     #  #
  0b00000110,  //      ##

  0b00111110,  //   #####
  0b01000001,  //  #     #
  0b01010001,  //  # #   #
  0b00100001,  //   #    #
  0b01011110,  //  # ####

  0b01111111,  //  #######
  0b00001001,  //     #  #
  0b00011001,  //    ##  #
  0b00101001,  //   # #  #
  0b01000110,  //  #   ##

  0b01000110,  //  #   ##
  0b01001001,  //  #  #  #
  0b01001001,  //  #  #  #
  0b01001001,  //  #  #  #
  0b00110001,  //   ##   #

  0b00000001,  //        #
  0b00000001,  //        #
  0b01111111,  //  #######
  0b00000001,  //        #
  0b00000001,  //        #

  0b00111111,  //   ######
  0b01000000,  //  #
  0b01000000,  //  #
  0b01000000,  //  #
  0b00111111,  //   ######

  0b00011111,  //    #####
  0b00100000,  //   #
  0b01000000,  //  #
  0b00100000,  //   #
  0b00011111,  //    #####

  0b01111111,  //  #######
  0b00100000,  //   #
  0b00011000,  //    ##
  0b00100000,  //   #
  0b01111111,  //  #######

  0b01100011,  //  ##   ##
  0b00010100,  //    # #
  0b00001000,  //     #
  0b00010100,  //    # #
  0b01100011,  //  ##   ##

  0b00000011,  //       ##
  0b00000100,  //      #
  0b01111000,  //  ####
  0b00000100,  //      #
  0b00000011,  //       ##

  0b01100001,  //  ##    #
  0b01010001,  //  # #   #
  0b01001001,  //  #  #  #
  0b01000101,  //  #   # #
  0b01000011,  //  #    ##

  0b00000000,  //
  0b00000000,  //
  0b01111111,  //  #######
  0b01000001,  //  #     #
  0b01000001,  //  #     #

  0b00000010,  //       #
  0b00000100,  //      #
  0b00001000,  //     #
  0b00010000,  //    #
  0b00100000,  //   #

  0b01000001,  //  #     #
  0b01000001,  //  #     #
  0b01111111,  //  #######
  0b00000000,  //
  0b00000000,  //

  0b00000100,  //      #
  0b00000010,  //       #
  0b00000001,  //        #
  0b00000010,  //       #
  0b00000100,  //      #

  0b01000000,  //  #
  0b01000000,  //  #
  0b01000000,  //  #
  0b01000000,  //  #
  0b01000000,  //  #

  0b00000000,  //
  0b00000001,  //        #
  0b00000010,  //       #
  0b00000100,  //      #
  0b00000000,  //

  0b00100000,  //   #
  0b01010100,  //  # # #
  0b01010100,  //  # # #
  0b01010100,  //  # # #
  0b01111000,  //  ####

  0b01111111,  //  #######
  0b01001000,  //  #  #
  0b01000100,  //  #   #
  0b01000100,  //  #   #
  0b00111000,  //   ###

  0b00111000,  //   ###
  0b01000100,  //  #   #
  0b01000100,  //  #   #
  0b01000100,  //  #   #
  0b00100000,  //   #

  0b00111000,  //   ###
  0b01000100,  //  #   #
  0b01000100,  //  #   #
  0b01001000,  //  #  #
  0b01111111,  //  #######

  0b00111000,  //   ###
  0b01010100,  //  # # #
  0b01010100,  //  # # #
  0b01010100,  //  # # #
  0b00011000,  //    ##

  0b00001000,  //     #
  0b01111110,  //  ######
  0b00001001,  //     #  #
  0b00000001,  //        #
  0b00000010,  //       #

  0b00001000,  //     #
  0b00010100,  //    # #
  0b01010100,  //  # # #
  0b01010100,  //  # # #
  0b00111100,  //   ####

  0b01111111,  //  #######
  0b00001000,  //     #
  0b00000100,  //      #
  0b00000100,  //      #
  0b01111000,  //  ####

  0b00000000,  //
  0b01000100,  //  #   #
  0b01111101,  //  ##### #
  0b01000000,  //  #
  0b00000000,  //

  0b00100000,  //   #
  0b01000000,  //  #
  0b01000100,  //  #   #
  0b00111101,  //   #### #
  0b00000000,  //

  0b00000000,  //
  0b01111111,  //  #######
  0b00010000,  //    #
  0b00101000,  //   # #
  0b01000100,  //  #   #

  0b00000000,  //
  0b01000001,  //  #     #
  0b01111111,  //  #######
  0b01000000,  //  #
  0b00000000,  //

  0b01111100,  //  #####
  0b00000100,  //      #
  0b00011000,  //    ##
  0b00000100,  //      #
  0b01111000,  //  ####

  0b01111100,  //  #####
  0b00001000,  //     #
  0b00000100,  //      #
  0b00000100,  //      #
  0b01111000,  //  ####

  0b00111000,  //   ###
  0b01000100,  //  #   #
  0b01000100,  //  #   #
  0b01000100,  //  #   #
  0b00111000,  //   ###

  0b01111100,  //  #####
  0b00010100,  //    # #
  0b00010100,  //    # #
  0b00010100,  //    # #
  0b00001000,  //     #

  0b00001000,  //     #
  0b00010100,  //    # #
  0b00010100,  //    # #
  0b00011000,  //    ##
  0b01111100,  //  #####

  0b01111100,  //  #####
  0b00001000,  //     #
  0b00000100,  //      #
  0b00000100,  //      #
  0b00001000,  //     #

  0b01001000,  //  #  #
  0b01010100,  //  # # #
  0b01010100,  //  # # #
  0b01010100,  //  # # #
  0b00100000,  //   #

  0b00000100,  //      #
  0b00111111,  //   ######
  0b01000100,  //  #   #
  0b01000000,  //  #
  0b00100000,  //   #

  0b00111100,  //   ####
  0b01000000,  //  #
  0b01000000,  //  #
  0b00100000,  //   #
  0b01111100,  //  #####

  0b00011100,  //    ###
  0b00100000,  //   #
  0b01000000,  //  #
  0b00100000,  //   #
  0b00011100,  //    ###

  0b00111100,  //   ####
  0b01000000,  //  #
  0b00110000,  //   ##
  0b01000000,  //  #
  0b00111100,  //   ####

  0b01000100,  //  #   #
  0b00101000,  //   # #
  0b00010000,  //    #
  0b00101000,  //   # #
  0b01000100,  //  #   #

  0b00001100,  //     ##
  0b01010000,  //  # #
  0b01010000,  //  # #
  0b01010000,  //  # #
  0b00111100,  //   ####

  0b01000100,  //  #   #
  0b01100100,  //  ##  #
  0b01010100,  //  # # #
  0b01001100,  //  #  ##
  0b01000100,  //  #   #

  0b00000000,  //
  0b00001000,  //     #
  0b00110110,  //   ## ##
  0b01000001,  //  #     #
  0b00000000,  //

  0b00000000,  //
  0b00000000,  //
  0b01111111,  //  #######
  0b00000000,  //
  0b00000000,  //

  0b00000000,  //
  0b01000001,  //  #     #
  0b00110110,  //   ## ##
  0b00001000,  //     #
  0b00000000,  //
};

CRGB matrice[NUM_COLUMNS][NUM_LEDS];
CRGB leds[NUM_STRIPS * NUM_LEDS];

char message[] = "<3<3<3  GMM  <3<3<3";  // Message à afficher
const int nb_tours_cumul = 5;            //Nombre de tours sur lesquels on moyenne la vitesse de rotation
int tour_actuel = 0;
long vitesse, periode, intervalle, cumul_periodes, temps_tours[nb_tours_cumul];
long t1, t2 = 0, t0, t4;
bool retard = false;
int val, magnXmax, magnXmin, magnXzero;  // Les valeurs lues par le capteur
bool Xbool;


void setup() {
  delay(500);
  //Serial.begin(9600);

  FastLED.addLeds<OCTOWS2811>(leds, NUM_LEDS);
  FastLED.setBrightness(64);
  FastLED.setMaxRefreshRate(0, false);  // Suppression de la limite de fréquence de rafraichissement par défaut

  //matrice[1][0] = CRGB::Green; // Repères pour tester la syncro des LEDs
  //matrice[51][0] = CRGB::Blue;

  string_to_matrix(message);  // On initialise la matrice avec notre message
  memcpy(leds, matrice[0], NUM_LEDS * 3);
  memcpy(&leds[NUM_LEDS], matrice[NUM_COLUMNS / 2], NUM_LEDS * 3);

  // Étalonnage des capteurs magnétiques pendant 3sec :
  t0 = millis();
  for (int i = 0; i < NUM_LEDS * 2; i++) {
    leds[i] = CRGB::Red;
  }
  FastLED.show();
  while (millis() - t0 < 3000) {
    //val = analogRead(INPUT_CAPTEUR);        // On enregistre les valeurs min et max du capteurs pour définir un seuil entre les deux
    //magnXmax = max(magnXmax, val);          // (non utilisé dans la version de démonstration)
    //magnXmin = min(magnXmin, val);
  }
  //magnXzero = (magnXmax + magnXmin) / 2;
  for (int i = 0; i < NUM_LEDS * 2; i++) {
    leds[i] = CRGB::Black;
  }
  FastLED.show();
  magnXzero = 650;
  delay(3000);

  mesure_vitesse();
  for (int i = 0; i < nb_tours_cumul; i++) {
    temps_tours[i] = periode;
  }
  cumul_periodes = periode * nb_tours_cumul;
}


void string_to_matrix(char chaine[]) {  // Inscription d'une chaine de caractères dans 'matrice' sous format RGB avec un dégradé de couleurs
  int i = 0;
  for (int c = 0; c < strlen(chaine); c++) {
    if (chaine[c] != ' ') {                                                                      // Si c'est un espace on saute juste une ligne dans matrice
      if ((int)chaine[c] < 33 or (int) chaine[c] > 125) { chaine[c] = '#'; }                     // Si le caractère n'est pas pris en compte dans notre liste on le remplace par '#'
      for (int j = 0; j < 5; j++) {                                                              // Chaque caractère est représenté par 5 colonnes de 8 pixels de haut
        for (int k = 0; k < 8; k++) {                                                            // On se sert de l'octet fourni par alphabet comme un masque
          if (!!((1 << k) & alphabet[5 * ((int)chaine[c] - 33) + j])) {                          // (int) chaine[c] donne le code ASCII, et le tableau alphabet commence avec '!' (code ASCII 33)
            matrice[i][k] = ColorFromPalette(RainbowColors_p, (i)*255 / 100, 255, LINEARBLEND);  // On laisse le pixel inchangé si le bit concerné vaut 0, sinon on l'éclaire en blanc
          }
        }
        if (i >= NUM_COLUMNS) { break; }  // On s'arrête quand il n'y a plus de place dans la matrice
        i++;
      }
      if (i >= NUM_COLUMNS) { break; }
    }
    if (i >= NUM_COLUMNS) { break; }
    i++;
  }
}


void mesure_vitesse() {  // Mesure de la vitesse sur un seul tour
  val = analogRead(INPUT_CAPTEUR);
  while (!condition_debut_tour) {  // On attend la détection du début du tour une première fois...
    Xbool = val > magnXzero;
    val = analogRead(INPUT_CAPTEUR);
  }
  t0 = micros();                   // ...On lance le chrono...
  while (!condition_debut_tour) {  // ...Puis on attend un 2eme passage au debut du tour
    Xbool = val > magnXzero;
    val = analogRead(INPUT_CAPTEUR);
  }
  Xbool = val > magnXzero;
  periode = micros() - t0;
  vitesse = 1000000 * 360 / periode;
}


long update_vitesse_cumul() {                                               // Retourne le temps d'un tour moyenné sur les n=nb_tours_cumul derniers tours
  if (micros() - t0 < 50000) { return (cumul_periodes / nb_tours_cumul); }  // On retourne la periode inchangée si la fonction est appelée trop vite (détection parasite)
  cumul_periodes -= temps_tours[tour_actuel];                               // On stocke l'historique des n derniers tours dans la liste temps_tours[]
  if (abs(micros() - t0 - periode) < 0.4 * periode) {
    // On traite le cas où la différence de mesure est trop différent des précédents (cas où un tour n'a pas été compté par exemple)
    temps_tours[tour_actuel] = micros() - t0;
  }
  temps_tours[tour_actuel] = micros() - t0;
  t0 = micros();
  cumul_periodes += temps_tours[tour_actuel];
  tour_actuel++;
  if (tour_actuel == nb_tours_cumul) { tour_actuel = 0; }
  return (cumul_periodes / nb_tours_cumul);  // On retourne la somme de la liste temps_tours[] divisée par sa longueur
}


void loop() {
  mesure_vitesse();
  t0 = micros();
  while (vitesse > vitesse_min) {  // Condition d'allumage du dispositif, une fois qu'il tourne assez vite
    t4 = micros();
    intervalle = periode / NUM_COLUMNS;  // Temps d'affichage de chaque colonne en microsecondes (de l'ordre de 300 a haute frequence)

    for (int currentLED = 0; currentLED < NUM_COLUMNS; currentLED++) {  // On effectue un tour complet avant de mettre a jour la vitesse de rotation
      t1 = micros();

      memcpy(leds, matrice[currentLED], NUM_LEDS * 3);                                               // On copie le contenu de la colonne courante dans la liste leds qui est envoyée aux LEDs
      memcpy(&leds[NUM_LEDS], matrice[(currentLED + NUM_COLUMNS / 2) % NUM_COLUMNS], NUM_LEDS * 3);  // La 2e colonne de LEDs est à l'opposé de la 1ere

      if (retard) {  // Si le programme ne peut plus suivre la vitesse d'actualisation on affiche une LED en rouge
        leds[7] = CRGB::Red;
      } else {
        leds[7] = CRGB::Black;
      }
      FastLED.show();

      t2 = micros();
      retard = intervalle + t1 - t2 < 0;  // Cas où les instructions dépassent l'intervalle de temps entre deux actualisations

      do {  // Tant que l'intervalle de temps n'est pas finie, on cherche à voir si le seuil de début de tour est franchi
        val = analogRead(INPUT_CAPTEUR);
        if (condition_debut_tour) {
          periode = update_vitesse_cumul();
          vitesse = 1000000 * 360 / periode;
        }
        Xbool = val > magnXzero;

      } while (micros() < t1 + intervalle);
    }

    while (micros() - t4 < periode) {}  // On attend la fin du tour si jamais il n'est pas fini
  }
}
