// Bibliothèque u8g2 pour dessiner sur un écran OLED
#include <U8g2lib.h>

// affichage final, 128x128px [tampon de page, taille = 128 octets], connexion HW I2C
U8G2_SH1107_128X128_1_HW_I2C u8g2(U8G2_R0);
// Connexion I2C de l'écran OLED et de l'Arduino UNO
// +5V > +5V // GND > GND // SCL (horloge série) > A5 ou SCL // SDA (données série) > A4 ou SDA

#include <LiquidCrystal_I2C.h>      // Bibliothèque pour l'écran LCD fonctionnant avec I2C.
LiquidCrystal_I2C lcd(0x27, 20, 4); // Définitions des paramètres de lcd 2004 à l'adresse 0x27.

int center_x = 64; // affichage x au centre, 64px pour l'affichage 128x128px
int center_y = 64; // affichage au centre y, 64px pour l'affichage 128x128px

//long TempsEcoule, Milliseconde, Seconde, Minute, Heure, Jour; // Variable de temps.
unsigned long currentTime = 0;    // Variable utilisée pour récupérer la valeur de millis()
unsigned long previousTime = 0;   // Variable utilisée pour stocker la valeur de millis() pour le prochain "loop"

// valeurs du temps de départ
long TempsEcoule, Milliseconde;
long time_hours = 9;
long time_minutes = 20;
long time_seconds = 10;

const int buzzer = 13;    // Buzzer relié à la broche 13
const int btn_StartStop = 8; // START/STOP, BP activé à l'état bas
const int btn_Reset = 9;     // RESET, BP activé à l'état bas
bool etat_btn_StartStop, etat_btn_Reset, etat_BoutonAppuyer; // Variable qu'on passe à 1 après avoir presser un bouton afin d'éviter l'effet rebond
bool etat_ChronoOnOFF, etat_btnDraw[8];  // Variable qui détermine l'état de marche du chronomètre et la fonction Mute.
char Chrono[20];

void setup(void) { // Initialisation
  u8g2.begin();  // début de la bibliothèque u8g2
  lcd.init(); lcd.backlight();  // Initialisation de l'écran LCD, et active le rétroéclairage de l'écran LCD
  printCenter("Chronometre", 0);        // écran de démarrage
  Fn_Display();
  pinMode(btn_StartStop, INPUT_PULLUP); // Assigne le mode ENTREE et résistance PULL UP
  pinMode(btn_Reset, INPUT_PULLUP);     // Assigne le mode ENTREE et résistance PULL UP
  pinMode(buzzer, OUTPUT); // Assigne le mode SORTIE à la broche 13.
  analogClock();
}
void loop(void) {
  Fn_Scan_Buttons(); // Fonction de balayage de l'état de tous les boutons poussoirs
  Fn_Chronometre();  // Fonction de gestion du temps qui passe lorsque le chrono est à ON
} // fin de loop()
// fonction perso beep pour Émettre une tonalité de x kHz pendant x ms
void beep() {
  tone(buzzer, 2500, 100);  // Émet une tonalité de 2,5kHz pendant 100 ms
}
void tick() {  // fonction perso pour Émettre une très brèvetonalité de 5kHz pendant 1 ms
  tone(buzzer, 5000, 1);   // Émet un son de 5 kHz pendant 1 ms
}
void Fn_Scan_Buttons() { //balayage de l'état de tous les boutons poussoirs
  etat_btn_StartStop = digitalRead(btn_StartStop); // État Bouton Start/Stop
  etat_btn_Reset     = digitalRead(btn_Reset);     // État Bouton Start/Stop

  if (etat_btn_StartStop == LOW && etat_BoutonAppuyer == 0) { // Test pression bouton START/STOP
    etat_BoutonAppuyer = 1;  beep(); delay(500);   // Evite l'effet rebond
    etat_ChronoOnOFF = !etat_ChronoOnOFF;                     // Inverse l'état du chronomètre
    while (etat_btn_StartStop)etat_btn_StartStop = digitalRead(btn_StartStop); // fonction anti rebond
  }
  if (etat_btn_Reset == LOW && etat_ChronoOnOFF == 0 && etat_BoutonAppuyer == 0) { // Test pression bouton RESET
    etat_BoutonAppuyer = 1; beep();  // Evite l'effet rebond
    time_seconds = 0; time_minutes = 0; time_hours = 0;     // Remet toutes les variables de temps à 0.
    analogClock();  // Mise à zéro du contenu de l'afficheur oled-sh1107
    Fn_Display();   // Mise à zéro du contenu de l'écran LCD
  }
}
void Fn_Chronometre() {// Fonction pricipale de la gestion de l'horloge du chrono
  // Incrémentation du compteur de chrono en utilisant la fonction millis()
  currentTime = millis();                    // Le temps courant = l'horloge interne en ms.
  TempsEcoule = currentTime - previousTime;  // Le temps écoulée = au temps courant - le temps précédant
  previousTime = millis();                   // nouveau temps précédant = l'horloge interne en ms.

  // Fonction compteur d'incrémentation des variables Seconde, Minute, Heure
  if (etat_ChronoOnOFF == 1) {                 // Si Etat_ChronoOnOff = true exécute le code entre {}.
    Milliseconde = Milliseconde + TempsEcoule; // Incrémentation des millisecondes
    if (Milliseconde > 999) {                  // Si Milliseconde est supérieure à 999... (1000ms)
      Milliseconde = Milliseconde - 1000;      // ...Remise à 0 de la variable Milliseconde
      time_seconds++;   // Incrémentation de 1 seconde toutes les 1000 Millisecondes
      Fn_Display();     // Fonction principale d'Affichage en temps réel.
      tick();           // Émet un tic toutes les secondes
      analogClock();    // Fonction principale d'Affichage en temps réel.
    }
    if (time_seconds > 59) {  // Si Seconde est supérieure à 59... (60sec)
      time_seconds = 0;       // ...Remise à 0 de la variable Seconde
      beep();                 // Émet un beep par minute
      time_minutes++;         // Incrémentation de 1 Minute toutes les 60 Secondes
    }
    if (time_minutes > 59) {  // Si Minute est supérieure à 59... (60 minutes)
      time_minutes = 0;       // ...Remise à 0 de la variable Minute
      time_hours++;           // Incrémentation de 1 Heure toutes les 60 Minutes
    }
  }
  if (etat_btn_StartStop == HIGH && etat_btn_Reset == HIGH) {
    etat_BoutonAppuyer = 0;   // réinitialise la variable "etat_BoutonAppuyer" à "0" cequi évite l'effet rebond
  }
}
// fonction perso pour centrer le texte à afficher.
void printCenter(String text, int ligne) {
  int len = text.length();                     // défini la longeur de la variable text
  lcd.setCursor((20 - len) / 2, ligne);  // positionne le curseur au centre de la ligne choisie et soustrait la longeur du text à afficher
  lcd.print(text);                         // affiche le text centré
}
void Fn_Display() { // rafraîchi l'Affichage en temps réel toutes les secondes.
  sprintf(Chrono, "%02ld:%02ld:%02ld", time_hours, time_minutes, time_seconds ); //Formate l'affichage.
  printCenter(Chrono, 1);  // Affiche le temps écoulé.
}
void analogClock() {
  u8g2.firstPage(); // sélectionnez la première page de l'affichage (la page mesure 128x8px), puisque nous utilisons la méthode de dessin de page de la bibliothèque u8g2
  do { // dessiner
    // dessinez le cercle central pour couvrir la partie centrale des aiguilles
    u8g2.setColorIndex(0); // couleur noir
    u8g2.drawDisc(center_x, center_y, 4);
    u8g2.setColorIndex(1); // couleur blanche
    u8g2.drawCircle(center_x, center_y, 4);
    // dessine l'arrière-plan - cercle plein écran, points pour les secondes,
    //  grandes graduations, nombres
    draw_background();
    draw_hand_thin(time_seconds * 6, 56, 24); // aiguille des secondes
    // dessinez les aiguilles avec des angles basés sur la valeur du temps
    draw_hand_bold(time_minutes * 6, 48, 15, 2); // aiguille des minutes
    draw_hand_bold(time_hours * 30 + (time_minutes / 2), 32, 15, 2); // aiguille des heures
  }
  while ( u8g2.nextPage() ); // parcourir toutes les pages jusqu'à ce que l'affichage soit entièrement mis à jour
}

// dessine l'arrière-plan -
// cercle plein écran, points pour les secondes, grandes graduations, nombres
void draw_background() {
  float xpos, ypos, xpos2, ypos2; // Variables Flottantes
  u8g2.drawCircle(center_x, center_y, 63, U8G2_DRAW_ALL);// dessiner un cercle en plein écran
  // dessine 60 points (pixels) autour du cercle, un pour chaque minute/seconde
  for (int i = 0; i < 60; i++) { // dessine 60 pixels autour du cercle
    xpos = round(center_x + sin(radians(i * 6)) * 60); // calculer x pos en fonction de l'angle et du rayon
    ypos = round(center_y - cos(radians(i * 6)) * 60); // calculer y pos en fonction de l'angle et du rayon
    u8g2.drawPixel(xpos, ypos); // dessine un pixel blanc sur la position xpos et ypos
  }
  // dessine de grandes graduations
    for (int i = 0; i < 12; i++) {
    // dessiner des graduations uniquement pour certains nombres, 
    // laisser un espace vide pour 12, 3, 6 et 9
    if ((i % 3) != 0) {
      xpos = round(center_x + sin(radians(i * 30)) * 54); // calculer x pos en fonction de l'angle et du rayon
      ypos = round(center_y - cos(radians(i * 30)) * 54); // calculer y pos en fonction de l'angle et du rayon
      xpos2 = round(center_x + sin(radians(i * 30)) * 46); // calculer x pos en fonction de l'angle et du rayon
      ypos2 = round(center_y - cos(radians(i * 30)) * 46); // calculer y pos en fonction de l'angle et du rayon
      u8g2.drawLine(xpos, ypos, xpos2, ypos2); // tracer une ligne pour une graduation
    }
  }
  
  // dessine les étiquettes 12, 3, 6 et 9
      u8g2.setFont(u8g2_font_8x13B_mn); // définir la police u8g2
    u8g2.drawStr(57, 20, "12");
    u8g2.drawStr(112, 69, "3");
    u8g2.drawStr(61, 120, "6");
    u8g2.drawStr(9, 69, "9");  
}
// dessiner une ligne fine = Aiguille des seconde s
void draw_hand_thin (int hand_angle, int hand_lenght_long, int hand_legth_short) {
  float xpos, ypos;
  float xpos2, ypos2;

  // calculer la position de départ et de fin de l'aiguille seconde
  xpos = round(center_x + sin(radians(hand_angle)) * hand_lenght_long); // calculer x pos en fonction de l'angle et du rayon
  ypos = round(center_y - cos(radians(hand_angle)) * hand_lenght_long); // calculer y pos en fonction de l'angle et du rayon
  xpos2 = round(center_x + sin(radians(hand_angle + 180)) * hand_legth_short); // calculer x pos en fonction de l'angle et du rayon
  ypos2 = round(center_y - cos(radians(hand_angle + 180)) * hand_legth_short); // calculer y pos en fonction de l'angle et du rayon

  u8g2.drawLine(xpos, ypos, xpos2, ypos2); // dessiner la ligne principale
  u8g2.setDrawColor(0); // couleur noire
  u8g2.drawDisc(xpos2, ypos2, 3); // dessine un petit cercle noir rempli
  u8g2.setDrawColor(1); // couleur blanche
  u8g2.drawCircle(xpos2, ypos2, 3, U8G2_DRAW_ALL); // dessine un petit contour de cercle blanc
}
// dessiner une aiguille en gras = aiguille des minutes et aiguille des heures
void draw_hand_bold (int hand_angle, int hand_lenght_long, int hand_legth_short, int hand_dot_size) {
  float xpos, ypos;
  float xpos2, ypos2;
  float tri_xoff, tri_yoff;

  // calculer les positions des deux cercles
  xpos = round(center_x + sin(radians(hand_angle)) * hand_lenght_long); // calculer x pos en fonction de l'angle et du rayon
  ypos = round(center_y - cos(radians(hand_angle)) * hand_lenght_long); // calculer y pos en fonction de l'angle et du rayon
  xpos2 = round(center_x + sin(radians(hand_angle)) * hand_legth_short); // calculer x pos en fonction de l'angle et du rayon
  ypos2 = round(center_y - cos(radians(hand_angle)) * hand_legth_short); // calculer y pos en fonction de l'angle et du rayon

  tri_xoff = round( sin(radians(hand_angle + 90)) * hand_dot_size);
  tri_yoff = round(-cos(radians(hand_angle + 90)) * hand_dot_size);

  u8g2.drawLine(center_x, center_y, xpos2, ypos2); // tracer la ligne d'un cercle vers le centre
  u8g2.drawDisc(xpos, ypos, hand_dot_size); // dessine un cercle blanc rempli
  u8g2.drawDisc(xpos2, ypos2, hand_dot_size); // dessine un cercle blanc rempli

  // deux triangles remplis sont utilisés pour dessiner un rectangle pivoté entre deux cercles
  u8g2.drawTriangle(xpos + tri_xoff, ypos + tri_yoff,
                    xpos - tri_xoff, ypos - tri_yoff,
                    xpos2 + tri_xoff, ypos2 + tri_yoff);

  u8g2.drawTriangle(xpos2 + tri_xoff, ypos2 + tri_yoff,
                    xpos2 - tri_xoff, ypos2 - tri_yoff,
                    xpos - tri_xoff, ypos - tri_yoff);
}
Loading
grove-oled-sh1107