Horloge TRÈS précise pilotée par GPS avec Arduino Nano

Si ce n'est pas top secret autant le mettre en pièce jointe ici dans un message.

Architecture_compare.pdf (78.2 KB)

1 Like

Merci Jacques !

Roland

j'ai retravaillé et clarifié mes histoires de performance et d'organisation du programme.
Voilà le résultat.
perf_mon.pdf (155.2 KB)
prenez-en de la graine.

Super projet et merci pour ton partage. Je commence à explorer ce système d'horloge pour avoir un backup d'heure exacte pour du positionnement avec un sextant. :+1:

Bonjour Roland et vous tous,

Encore une passion de plus ! Comme toi, l’horlogerie m’est une passion mais pas aussi poussée que toi pour y rajouter l’éphéméride etc…
Et tous cas très beau travail d’évolution du début du post vers l’affichage final, une beauté ! (Comme d’ab)
J’ai réalisé il y a quelques temps déjà une horloge pilotée par gps et carillonnant comme la tour de Westminster (sons mp3)
Si quelqu’un serait intéressé, je posterai ? :slight_smile:

Bon week-end à vous tous.

L’Arsène.

Merci Roland,
Tes projets sont top.
J'ai adapté ton projet pour avoir UTC et Locator radio, étant radio amateur et skipper voilier, j'avais besoin d'une heure UTC précise pour le sextant et le IARU Locator, plus coordonnées décimales et MMSS.


Bon week end,

1 Like

Bonjour

Tres beau projet que j'ai mis en oeuvre en l'adaptant sur un MEGA2560 en attendant de recevoir le seeduino.

J'aurai juste une remarque. Pourquoi faire apparaitre l'UTC lorsque que l'on est en AUTO ?

J'ai sur l'affichage AUTO et UTC+1 alors que nous sommes à UTC + 2

Thierry

Bonjour l'Arsène !
Oui, poste si tu veux bien, ça augmentera notre culture générale
(pour ma part je n'ai pas encore fait de projet avec "du son dedans")

Amicalement, Roland

Bonjour Roland,

Avant de poster ce programme, je souhaiterai modifier la façon de changer l'heure d'été à l'heure d'hiver et inversement selon ta façon écrite comme suit:

//En France métropolitaine :
  //Passage de l'heure d'hiver à l'heure d'été le dernier dimanche de mars à 1h00 UTC (à 2h00 locales il est 3h00)
  //Passage de l'heure d'été à l'heure d'hiver le dernier dimanche d'octobre à 1h00 UTC (à 3h00 locales il est 2h00)
  if (moisUTC == 3)
    {
     byte derDimancheMars = 31 - ((5 + anUTC + (anUTC >> 2)) % 7);
     return jourUTC > derDimancheMars || (jourUTC == derDimancheMars && heureUTC != 0);
    }
  if (moisUTC == 10)
    {
     byte derDimancheOct = 31 - ((2 + anUTC + (anUTC >> 2)) % 7);
     return jourUTC < derDimancheOct || (jourUTC == derDimancheOct && heureUTC == 0);
    }
  return 3 < moisUTC && moisUTC < 10;  // true d'avril à septembre inclus
}

Le résultat est il correct ? J'utilise l'algorithme de Mike KEITH mais me donne uniquement le jour de la semaine !

L'Arsène.

@mirage47 et @wondersea

En rajoutant la gestion des 1/4 d'heures pour l'heure UTC, j'ai oublié de modifier la ligne :

lcd.setCursor(19, 2); if (decal_DST == 1) lcd.write(byte(2)); else lcd.print(" ");

en

lcd.setCursor(19, 2); if (decal_DST == 3600) lcd.write(byte(2)); else lcd.print(" ");

qui permet l'affichage d'un "S" blanc sur fond noir pour indiquer qu'on est en heure d'été.
Ça indiquera donc toujours encore UTC+1 mais avec le "S" pour l'heure d'été en plus.
Je viens de faire la correction dans mon post initial.

Sinon rien n'interdit, dans la fonction void ecrit_lcd_UTC() de faire un test sur la variable "decal_DST" et d'ajouter 1 au décalage UTC si decal_DST = 3600 ou de ne rien afficher !

Roland

1 Like

Pour ma part l'algorithme (qui n'est pas de moi, mais de Bricoleau) fonctionne correctement.
Attention pour l'année de la faire passer à la fonction sous la forme "24" et non pas "2024" !

Roland

Informations issus des données recueillis par GPS (librairie "TinyGPS++.h") ne me donnent pas le jour de la semaine.
De quelle façon l'obtient tu ?

L'Arsène.

Dans ma première version avec le nano c'est la bibliothèque Time de Paul Stoffregen qui me donne le jour de la semaine avec "weekday()"

Donc avec ça déclaré au début du sketch :
const char* JourSem[7] = {"Dimanche"," Lundi "," Mardi ","Mercredi"," Jeudi ","Vendredi"," Samedi "};

et la commande lcd.print(JourSem[weekday()-1]); pour l'affichage ça le fait.

Avec le montage à base de l'ESP32 l'approche est différente, j'utilise les fonctionnalités intégrées à
struct tm qui est une structure de données contenant les différents éléments du temps et de l'heure, et aussi du jour de la semaine.
Au début du sketch je déclare donc une structure que je nomme loc (tu peux l'appeler autrement)
struct tm loc;

Cette structure contient ensuite différents éléments comme "loc.tm_wday" (jour de la semaine), "loc.tm_mday" (jour du mois), "loc.tm_hour" (heure), etc...

Pour afficher la date avec le jour sur l'écran la commande est :
spr_date.drawString(String(JourSem[loc.tm_wday])+" "+String(loc.tm_mday)+" "+String(Mois[loc.tm_mon])+" "+String(loc.tm_year+1900),position x,position y); (sachant que la commande "spr_date.drawString..." fait partie de la bibliothèque d'affichage TFT-eSPI.

Roland

Merci Roland,
A chaque différents de tes posts j'en apprend beaucoup et te remercie. :wink:
Du fait que j'utilise un DS 3231 pour les valeurs de l'heure, j’utilise la bibliothèque RtcDS3231.h le récepteur GPS ne servant qu'à la mise à l'heure de celui-ci lors de la mise sous tension puis le récepteur n'est plus alimenté. J'ai fait des recherche cette bibliothèque ne calcule pas je jour de la semaine. Je vais mettre en parallèle la bibliothèque Time de Paul Stoffregen et faire des essais de récupération du jour de la semaine puis appliquer tes écrits énumérés en post 30.

Bonne journée.
L'Arsène.

Bonjour

Merci pour ce retour

La modif est donc aussi a faire avec le soft SEEDUINO

Thierry

Comme m’a demandé Roland dans le Post # 29, voici le contenu de la réalisation de mon horloge comportant un carillon imitant celui de la tour de Westminster en UK.

Comme Roland, les horloges m’ont toujours impressionnées que ce soit des mécaniques (murales ou de clocher) comme des électroniques.

Dans les années 1970 une société nommée HEATKIT (USA) avait dans son catalogue un kit d’une horloge avec de maxi afficheurs et un carillon (une usine à gaz basée sur un générateur d’une fréquence puis un diviseur d’une octave et demie et dont les 5 sons sont un accord de 4 notes) que j’ai réalisé. A chaque coupure d’alimentation il fallait régler l’heure ce qui me convenait plus lorsque j’ai réalisé mes premiers montages décodant les signaux du DCF77 puis les informations de la date et heure via internet (toujours beaucoup utilisé dans les forums) et finalement par les informations via les satellites du GPS et c’est cette dernière méthode qui est retenu pour le montage avec un afficheur à maxi segments et un carillon issu des enregistrements MP3 du montage HEATKIT.
Vous avez tenu le coup de me lire jusque-là, bravo !

La pièce maitresse est un Arduino Nano gérant, le récepteur GPS, un RTC DS3231, pour l’affichage un CI HT16K33 (élément formidable ayant 16 X 8 E/S programmable et les sortie géré en PWM ce qui élimine les résistances en série dans les leds et la luminosité étant réglable) et enfin un lecteur MP3 bien connu, le DFPlayer Mini MP3. Ce dernier est gourmand en mA (prévoyez une alimentation supplémentaire à la connexion USB) et comporte un petit ampli BF pouvant être connecter directement à un HP mais est de qualité médiocre et un petit ampli BF (et même stéréo) branché sur les connections BF du module améliore franchement cette qualité.

La partie de l’affichage m’a pris du temps pour le passage de l’heure d’hiver et d’été avec la bibliothèque Time ! Merci à Roland pour sa précieuse aide.

Mais c’est surtout avec la lecture des fichier MP3 qui m’a pris du temps à la finalisation du programme. Apparent (ou j’ai des lacunes) dans le programme Arduino on ne peut pas gérer deux liaisons séries avec les modules utilisés ! Pour contourner ce problème, j’ai après que le RTC soit informé de la date et heure, l’alimentation du récepteur GPS est coupée ce qui arrête l’activité sur la connexion série de celui-ci et laissant ainsi seul le lecteur MP3 accessible au programme.



Les différentes bandes sonores du carillon sont enregistrées sur une Mini SD au format MP3 et numérotées de 1 à 15 qui elles sont dans un fichier nommé MP3. Ecoutez l’échantillon joint au Post. Il est au format .txt pour le transfert, renommez le .mp3 pour l'écouter.
0001,1 h.txt (316,1 Ko)

Concernant un nouvel afficheur doté du fameux HT 16K33 fonctionnant sur le port SDA, SCL est cours de réalisation.

Bonne bidouille aux mélomanes So british !

Et le programme ....

#include <RtcDateTime.h>                                // Librarie RTC pour DS3231      Ne pas déplacer cette librairie !!!
#include <RtcDS3231.h>                                  // Librarie RTC pour DS3231      Ne pas déplacer cette librairie !!!
#include <Wire.h>                                       // Librarie I2C
#include "TinyGPS++.h"                                  // Librairie pour TinyGPS
#include "Adafruit_GFX.h"                               // Librairie pour les HT 16K33
#include "Adafruit_LEDBackpack.h"                       // Librairie pour l'affichage
#include "SoftwareSerial.h"                             // Insertion de la bibliothèque serie logicielle
#include <DFRobotDFPlayerMini.h>                        // Pour la gestion du Mini Player
#include <TimeLib.h>                                    // Pour les conversions de temps au format unix et réciproquement et passe heure été/hiver

Adafruit_7segment matrix1 = Adafruit_7segment();        // Activation de l'affichage pour les heures et minutes

// Déclaration des variables pour le GPS
   boolean drawDots;
   char data;
   double latitude;
   double longitude;
   double alt; //altitude
   double vitesse;
   unsigned long nbre_sat;

  //  int h;
   int h1;
   int mn;
   int mn_car;
   int mn_uni;
   int mn_dix; 
   int s1 = 0;
   int etat_s;
   int compt_GPS = 0;
   int volume;
   boolean flag_ete_hiver;
   boolean flag_carrillon;
   boolean flag_GPS;

//---------------------------------- Déclaration des I/O ---------------------------------------------------


   int Alim_GPS = 6;                              // Pour contrôle de l'alimention du GPS     

// Création de l'objet Mini Player
SoftwareSerial mySoftwareSerial(7, 8);  // TX, RX
DFRobotDFPlayerMini myDFPlayer ;  

// Création de l'objet GPS
TinyGPSPlus gps;
SoftwareSerial GPS(3, 2); // RX TX// Création de l'objet GPS pour la liaison série
                            // entre l'Arduino et le module GPS

   tmElements_t UTC;                      // Déclaration de la structure contenant les éléments de TimeLib

RtcDS3231<TwoWire> rtcObject(Wire);
// Par défaut SDA = A4  pour arduino Nano
//            SCL = A5  


void setup() {

  Serial.begin(115200); 

  pinMode(Alim_GPS, OUTPUT);                      
  digitalWrite(Alim_GPS, HIGH);                    // On met sous tension le module GPS 

  GPS.begin(9600);                                 // Initialisation de la liaison série du GPS pour reception données

  rtcObject.Begin();                               // Initialisation du RTC

  //matrix1.begin(0x70);                           // Adresse I2C pour le HT 16K33 de l'affichage les heures et minutes
                                                   // A décommenter lorsque l'afficheur sera branché via SDA SCL !
  Serial.println("matrix begin ok");               // Pour info de l'évolution du porogramme

   volume = 30;                                    // Niveau sonnore pour le mini player
}

void loop() {

       while (GPS.available() & (compt_GPS <= 5)) {     // Tant que la trame des infos du GPS sont validés et 5 rotations pour des infos complets
        data = GPS.read();                              // On lit les infos 
        gps.encode(data);                               // On décode les infos 

        if (gps.location.isUpdated()){                  // Si les valeurs sont OK
          compt_GPS ++; 
          
          latitude = gps.location.lat();                // Les valeurs sont les suivantes ************ Ne pas enlever ces lignes ci dessous  
          longitude = gps.location.lng();
          alt = gps.altitude.meters();
          vitesse = gps.speed.kmph();
          nbre_sat = gps.satellites.value();
          
          UTC.Day = (gps.date.day());                   // En les modifiant dans les int simpliste
          UTC.Month = (gps.date.month());
          UTC.Year = (gps.date.year())-1970;
          UTC.Hour = (gps.time.hour())+1;               // Heure de Paris                     
          UTC.Minute = (gps.time.minute());
          UTC.Second = (gps.time.second());

          Serial.print("Les valeurs GPS: ");
          Serial.print(UTC.Hour);
          Serial.print(":");
          Serial.print(UTC.Minute);
          Serial.print("  ");
          Serial.print(UTC.Day);
          Serial.print("-");
          Serial.print(UTC.Month);
          Serial.print("-");
          Serial.println(UTC.Year); 

          Serial.println("Ecriture dans le RTC");         // Pour info du déroulement du programme

          RtcDateTime currentTime = RtcDateTime(UTC.Year, UTC.Month, UTC.Day, UTC.Hour, UTC.Minute, UTC.Second); // Ecritures des valeurs dans DS 3231  
          rtcObject.SetDateTime(currentTime);             // Configurer le RTC avec les valeurs issues du GPS
                            
          
          Serial.println("Ecriture dans le RTC faite");   // Pour info du déroulement du programme
          


        }  // Fin du if....
  }        // Fin du while

        if (compt_GPS == 5){                             // Si les 5 rotations pour le GPS sont passées
          
           digitalWrite(Alim_GPS, LOW);                  // La réception par GPS est terminée, on met hors tension le module GPS

       if(flag_GPS == 0){                                // On initialise le Mini player maintenant, le récepteur GPS hors tension !
            mySoftwareSerial.begin(9600) ;
             myDFPlayer.begin(mySoftwareSerial) ;        // Apparement la gestion de 2 liaisons séries ne sont pas admis ! 
           flag_GPS = 1;
           }

          
           RtcDateTime currentTime = rtcObject.GetDateTime();     // On va lire l'heure du RTC 

           UTC.Year = currentTime.Year();                         // Les valeurs à afficher sont maintenant issus du DS 3231
           UTC.Month = currentTime.Month();                       
           UTC.Day = currentTime.Day();                          
           UTC.Hour = currentTime.Hour();
           UTC.Minute = currentTime.Minute();
           UTC.Second = currentTime.Second();

           if(Heure_ete(UTC.Year-30, UTC.Month, UTC.Day, UTC.Hour)){
             flag_ete_hiver = 1;
             Serial.println("Heure d'été");
             UTC.Hour = UTC.Hour + 1;
            } else { flag_ete_hiver = 0;
             Serial.println("Heure d'hiver");

           }
           Serial.print("Les valeurs RTC: ");                 // Pour afficher les valeurs du RTC
           Serial.print(UTC.Hour);
           Serial.print(":");
           Serial.print(UTC.Minute);
           Serial.print("  ");
           Serial.print(UTC.Day);
           Serial.print("-");
           Serial.print(UTC.Month);
           Serial.print("-");
           Serial.println(UTC.Year+18);

          if(UTC.Hour >= 13){                                   // On affiche les cycles de 12 heures seulement 
             h1 = (UTC.Hour - 12);
            } else { h1 = UTC.Hour;
              }

           Serial.print("Valeurs affichées: ");                // Pour les valeurs à afficher
           Serial.print(h1);
           Serial.print(":");
           Serial.println(UTC.Minute);

       } // Fin du  if (compt_GPS == 5){ 


     //-------------------------------------  Pour l'affichage des clignotement du double point secondes  --------------------------------
    
   if(s1 != UTC.Second){                                // Si s1 est différent que s, donc 1s seconde est de plus
     s1 = UTC.Second;                                   // On ajuste la variable s1 à la variable s      
     etat_s =! etat_s ;                                 // On fait un Flip Flop  
     affichage();                                       // On modifie le clignotement du double point de secondes
     delay(500);
     etat_s =! etat_s ;                                 // On refait un Flip Flop
     affichage();
          }  

   if(UTC.Minute == 0 && h1 >= 1 && flag_carrillon == LOW){
     carrillon_00mn();
     flag_carrillon = HIGH;
     mn_car = UTC.Minute;
   }

   if(UTC.Minute == 15 && flag_carrillon == LOW){
     carrillon_15mn();
     flag_carrillon = HIGH;
     mn_car = UTC.Minute;
   }

   if(UTC.Minute == 30 && flag_carrillon == LOW){
     carrillon_30mn();
     flag_carrillon = HIGH;
     mn_car = UTC.Minute;
   }

   if(UTC.Minute == 45 && flag_carrillon == LOW){
     
     carrillon_45mn();
     flag_carrillon = HIGH;
     mn_car = UTC.Minute;
   }


   if((flag_carrillon == HIGH) && (mn_car != UTC.Minute)){    // Entre carrillonnage on remet le flag à LOW
     flag_carrillon = LOW;
   }
       

}   // Fin du loop

bool Heure_ete(byte anUTC, byte moisUTC, byte jourUTC, byte heureUTC)
{
  //En France métropolitaine :
  //Passage de l'heure d'hiver à l'heure d'été le dernier dimanche de mars à 1h00 UTC (à 2h00 locales il est 3h00)
  //Passage de l'heure d'été à l'heure d'hiver le dernier dimanche d'octobre à 1h00 UTC (à 3h00 locales il est 2h00)
  if (moisUTC == 3) 
    {
     byte derDimancheMars = 31 - ((5 + anUTC + (anUTC >> 2)) % 7);
     return jourUTC > derDimancheMars || (jourUTC == derDimancheMars && heureUTC != 0);
    }
  if (moisUTC == 10) {
     byte derDimancheOct = 31 - ((2 + anUTC + (anUTC >> 2)) % 7);
     return jourUTC < derDimancheOct || (jourUTC == derDimancheOct && heureUTC == 0);
    }
  return 3 < moisUTC && moisUTC < 10;  // true d'avril à septembre inclus
}


//------------------------------------ Affichage des heures, minutes ----------------------------------------

   void affichage() {
    
     matrix1.clear();
     matrix1.setBrightness(8);                        // Intensité lumineuse de l'affichage   

     if(h1 >= 10){
        matrix1.writeDigitNum(3,2);                   // On affiche sur le digit 3 les dixaines des heures
       } else {matrix1.writeDigitNum(3,4);            // Si il fait moins de 10h00 on affiche rien sur le digit 1 les dixaines des heures
        }
     matrix1.writeDigitNum(2,h1-((h1/10)*10));        // On affiche sur le digit 2 les unités des heures 
     matrix1.writeDigitNum(1,mn/10);                  // On affiche sur le digit 1 les dixaines des minutes
     matrix1.writeDigitNum(0,mn-((mn/10)*10));        // On affiche sur le digit 0 les unités des minutes
     matrix1.writeDigitNum(4,etat_s);                 // On affiche sur le digit 3 les impulsions des secondes    
     matrix1.writeDisplay();                          // On demande au HT 16K33 d'afficher 

   }                  // Fin du void affichage


      // ------------------------------------------------ Informations pour le carillon ------------------------------------

   void carrillon_00mn() {            // Heure pleine

   Serial.println("Carrillon Heure");

  switch (h1) {                       // h1 = 1 à 12
   case 1:   
        myDFPlayer.setTimeOut(500) ;
        myDFPlayer.volume(volume) ;  // Fixe le volume son 
        myDFPlayer.play(h1);         // Joue le MP3 0001.mp3  
          
        break;

   case 2:   
        myDFPlayer.setTimeOut(500) ;
        myDFPlayer.volume(volume) ; // Fixe le volume son   
        myDFPlayer.play(h1);        // Joue le MP3 0002.mp3   etc....
   
       break;

    case 3:   
        myDFPlayer.setTimeOut(500) ;
        myDFPlayer.volume(volume) ; 
        myDFPlayer.play(h1);         
   
       break;

    case 4:   
        myDFPlayer.setTimeOut(500) ;
        myDFPlayer.volume(volume) ; 
        myDFPlayer.play(h1);         
   
       break;

    case 5:   
        myDFPlayer.setTimeOut(500) ;
        myDFPlayer.volume(volume) ;
        myDFPlayer.play(h1);        
   
       break;

    case 6:   
        myDFPlayer.setTimeOut(500) ;
        myDFPlayer.volume(volume) ;  
        myDFPlayer.play(h1);        
   
       break;

    case 7:   
        myDFPlayer.setTimeOut(500) ;
        myDFPlayer.volume(volume) ; 
        myDFPlayer.play(h1);        
   
       break;

    case 8:   
        myDFPlayer.setTimeOut(500) ;
        myDFPlayer.volume(volume) ; 
        myDFPlayer.play(h1);       
   
       break;

    case 9:   
        myDFPlayer.setTimeOut(500) ;
        myDFPlayer.volume(volume) ; 
        myDFPlayer.play(h1);        
   
       break;

    case 10:   
        myDFPlayer.setTimeOut(500) ;
        myDFPlayer.volume(volume) ; 

        myDFPlayer.play(h1);        
   
       break;

    case 11:   
        myDFPlayer.setTimeOut(500) ;
        myDFPlayer.volume(volume) ; 
        myDFPlayer.play(h1);        
   
       break;

    case 12:   
        myDFPlayer.setTimeOut(500) ;
        myDFPlayer.volume(volume) ; 
        myDFPlayer.play(h1);         
   
       break;

    }     // Fin du switch (h1)   Heures pleines
   }      // Fin du s/prg carrillon 00mn
   
   
   void carrillon_15mn() { 
    myDFPlayer.setTimeOut(500) ;
    myDFPlayer.volume(volume) ;                   
    myDFPlayer.play(13);                          // joue le MP3 du 1/4 heure      

   }        // Fin du s/prg carrillon 15mn 


   void carrillon_30mn() { 
    myDFPlayer.setTimeOut(500) ;
    myDFPlayer.volume(volume) ;                    
    myDFPlayer.play(14);                         // joue le MP3 du 1/2 heure  
  
   }        // Fin du s/prg carrillon 30mn


   void carrillon_45mn() {
    myDFPlayer.setTimeOut(500) ;
    myDFPlayer.volume(volume) ;  
    myDFPlayer.play(15);                          // joue le MP3 du 3/4 heure 
     
   }        // Fin du s/prg carrillon 45mn 

L'Arsène.

@rollmops67 excellent partage, merci a toi :nerd_face:

je voulais me faire une horloge aussi, juste histoire de l'offrir a quelques proches. j'avais l'intension d'utiliser une RTC, mais ça devient completement absurde quand je vois comment ce topic est documenté... je vais surement utiliser un module GPS :yum:

Un petit passage juste pour dire bonjour, j'étais en vacances quelques semaines !
Il y a encore de la vie et des idées ici !
Ah, Heatkit, Tandy, souvenirs souvenirs...

Roland