#include <LiquidCrystal_I2C.h>

const uint8_t colonnes = 20;
const uint8_t lignes   = 4;

LiquidCrystal_I2C lcd( 0x27, colonnes, lignes );


void setup()
{
  lcd.init();
  lcd.backlight();
}


void loop()
{
  // temps actuel
  const uint32_t temps = millis();

  // temporisation pour le défilement du texte
  static uint32_t temporisation_defilement = temps;

  // interval du défilement, en millisecondes
  static const uint16_t interval_defilement = 50;

  // s'il est temps de déplacer le texte
  if ( temps - temporisation_defilement >= interval_defilement )
  {
    temporisation_defilement = temps; // réinitialiser pour la prochaine temporisation
    
    static const char    texte[]        = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";                         // le texte à afficher
    static const uint8_t longueur_texte = min( strlen( texte ), colonnes ); // limiter la longueur du texte à la taille horizontale de l'afficheur
    static const uint8_t position_x_max = colonnes - longueur_texte;         // la position horizontale maximale
    static const uint8_t position_y_max = lignes - 1;                        // la position verticale maximale
    static int8_t        position_x     = 0;                                 // position horizontale du curseur
    static int8_t        position_y     = 0;                                 // position verticale du curseur
    static int8_t        direction_x    = 1;                                 // direction horizontale ( 0 = aucune, 1 = droite, -1 = gauche )
    static int8_t        direction_y    = 0;                                 // direction verticale ( 0 = aucune, 1 = bas, -1 = haut )
    static bool          debut          = true;

    // 
    if ( debut == true )
    {
      lcd.setCursor( position_x, position_y );
      lcd.print( texte );
      debut = false;
    }
    else
    {
      // si le texte se déplace vers la droite et a atteint le bord droit
      if ( direction_x == 1 && position_x == position_x_max )
      {
        direction_x =  0; // arrêter le déplacement vers la droite
        direction_y =  1; // commencer le déplacement vers le bas
      }

      // sinon, si le texte se déplace vers le bas et a atteint le bord bas
      else if ( direction_y == 1 && position_y == position_y_max )
      {
        direction_y =  0; // arrêter le déplacement vers le bas
        direction_x = -1; // commencer le déplacement vers la gauche
      }

      // sinon, si le texte se déplace vers la gauche et a atteint le bord gauche
      else if ( direction_x == -1 && position_x == 0 )
      {
        direction_x =  0; // arrêter le déplacement vers la gauche
        direction_y = -1; // commencer le déplacement vers le haut
      }

      // sinon, si le texte se déplace vers le haut et a atteint le bord haut
      else if ( direction_y == -1 && position_y == 0 )
      {
        direction_y =  0; // arrêter le déplacement vers le haut
        direction_x =  1; // commencer le déplacement vers la droite
      }


      // and temps, should it move right, if possible ?
      if ( direction_x == 1 && position_x < position_x_max )
      {
        lcd.setCursor( position_x++, position_y ); // go to the old position, and set next position, one to the right of the old texteing
        lcd.print( ' ' );        // erase the debut character of the old texteing
        lcd.print( texte );        // print the new texteing
      }

      // or should it move left, if possible ?
      else if ( direction_x == -1 && position_x > 0 )
      {
        lcd.setCursor( --position_x, position_y ); // set next position, one to the left of the old texteing, and go there
        lcd.print( texte );        // print the new texteing
        lcd.print( ' ' );        // erase the last character of the old texteing
      }

      // or should it move up or down ?
      else if ( direction_y != 0 )
      {
        uint8_t longueur = longueur_texte;
        lcd.setCursor( position_x, position_y );                      // go to the old position
        do lcd.print( ' ' ); while ( --longueur );       // erase all characters of the old texteing
        lcd.setCursor( position_x, direction_y == 1 ? ++position_y : --position_y ); // set next position, one up or down, and go there
        lcd.print( texte );                           // print the new texteing
      }
    }
  }
}