Heure d'été / heure d'hiver automatique avec DS1307

Bonjour,
Tu trouveras ci-joint les fichiers suivants:

  • la librairie DCF77 (Dcf77.zip)
  • exemple date et heure sur l'interface série (L08_06.zip)
  • exemple horloge DCF77 avec afficheur LCD (L08_07.zip)
    J'avais également une version sans librairie mais qui fonctionnait aussi bien sur les impulsions positives ou négatives des signaux DCF (automatique) mais je ne l'ai pas encore retrouvé (plupart peut être)
    @+

Dcf77.zip (8.56 KB)

L08_06.zip (593 Bytes)

L08_07.zip (748 Bytes)

Bon là j'ai de quoi tester, je vais donc mettre tout ça en route et je vous tiens au courant de l'avancement !

Yep, du sport en perspective pour la semaine. Je vais commencer par tester sur un montage à part. Dès que ce sera concluant je modifierai mes autres programmes.

Bonjour,
Le message transmis par la saucisse Francfort est composé de 58 bits. Regarde la signification de tous ces bits (recherche DCF77 sur ton ami).
le bit 16 : prévient qu'il va y avoir un changement été/hiver ou hiver/été
le bit 17 : activé durant l'heure d'été
le bit 18 : activé durant l'heure d'hiver
@+

icare:
Bonjour,
Le message transmis par la saucisse Francfort est composé de 58 bits. Regarde la signification de tous ces bits (recherche DCF77 sur ton ami).
le bit 16 : prévient qu'il va y avoir un changement été/hiver ou hiver/été
le bit 17 : activé durant l'heure d'été
le bit 18 : activé durant l'heure d'hiver
@+

En gros pas besoin de tester tous les dimanches !

juste à analyser ces 3 bits :stuck_out_tongue:

J'ai une grande semaine en perspective

Me revoilà avec de bonnes nouvelles !!!

Alors j'ai finalement réussi, après quand même 4 heures à m'arracher les cheveux ! :stuck_out_tongue:

La difficulté n'est pas là où l'on pourrait l'imaginer. Le plus difficile c'est de calé correctement l'antenne pour obtenir une bonne réception de la trame.

J'ai donc un programme qui fonctionne parfaitement:

#include "DCF77.h"
#include "Time.h"

#define DCF_PIN 2	         // Connection pin to DCF 77 device
#define DCF_INTERRUPT 0		 // Interrupt number associated with pin

time_t time;
DCF77 DCF = DCF77(DCF_PIN,DCF_INTERRUPT);


void setup() {
  Serial.begin(9600); 
  DCF.Start();
  Serial.println("Waiting for DCF77 time ... ");
  Serial.println("It will take at least 2 minutes until a first update can be processed.");

digitalWrite(2, HIGH);       // turn on pullup resistors

}

void loop() {
  delay(1000);
  time_t DCFtime = DCF.getTime(); // Check if new DCF77 time is available
  if (DCFtime!=0)
  {
    Serial.println("Time is updated");
    setTime(DCFtime);
  }	
  digitalClockDisplay();  
}

void digitalClockDisplay(){
  // digital clock display of the time
  Serial.print(hour());
  printDigits(minute());
  printDigits(second());
  Serial.print(" ");
  Serial.print(day());
  Serial.print(" ");
  Serial.print(month());
  Serial.print(" ");
  Serial.print(year()); 
  Serial.println(); 
}

void printDigits(int digits){
  // utility function for digital clock display: prints preceding colon and leading 0
  Serial.print(":");
  if(digits < 10)
    Serial.print('0');
  Serial.print(digits);
}

Maintenant j'ai l'heure qui s'affiche dans le terminal. Je vais donc greffer ce bout de code sur mon programme principal.

Comment faire pour mettre à l'heure périodiquement le DS1307 en utilisant la trame que je reçois du DCF77 ?

Dois-je utiliser que le DCF77 au risque un jour de perdre la trame ou vraiment mettre à l'heure une fois par jour le DS1307 ?

Bonjour,

gunsman76:
J'ai donc un programme qui fonctionne parfaitement:
Maintenant j'ai l'heure qui s'affiche dans le terminal. Je vais donc greffer ce bout de code sur mon programme principal.
Comment faire pour mettre à l'heure périodiquement le DS1307 en utilisant la trame que je reçois du DCF77 ?
Dois-je utiliser que le DCF77 au risque un jour de perdre la trame ou vraiment mettre à l'heure une fois par jour le DS1307 ?

Bonne nouvelle si ça fonctionne.
Personnellement j'utiliserai un DS1307 avec mise à jour journalière, la réception du signal DCF77 est parfois délicate en fonction des conditions météorologique, de la période de la journée (meilleur réception la nuit) et de la position de l'antenne (cadre ferrite perpendiculaire à la direction de l'émetteur).
@+

Pareil. Je remets à l'heure le DS1307 une fois par nuit, à 3h du matin, pour prendre en compte le changement d'heure éventuel.
En dehors de ça le récepteur DCF77 n'est pas alimenté. Je coupe son alim dès que j'ai reçu une trame complète et je la rallume le lendemain.

Je vais peut-être dire une conneri* mais l’élément principal pour la stabilité du DS1307 c'est le quartz qui me semble-t-il est à la même fréquence que les quartz d'horlogerie.

Et si on remplaçait le quartz chinois taillé avec un silex par un récupéré sur une montre HS ?
Je vois venir certains : à condition bien sûr que ce ne soit pas le quartz qui soit HS :grin:

Tu peux m'expliquer en 2/3 mots comment faire pour synchroniser le ds1307 avec le dcf77 ?

Le DS1307 à température ambiante dérive d'à peu près 1min/mois.
2 solutions pour une plus grande précision:
-Remplacer le quartz par un DS32KHZ (un quartz compensé en température)
-Utiliser un DS3231 à la place

Bonsoir,
Il faut 1 minute pour récupérer tous les bits de la trame DCF77.
Si tu veux vraiment être puriste, il faut calculer le temps pour d'extraire les données de la trame et les mettre dans les variables puis y ajouter le temps de mise à jour des registres du DS1307.
Tu me diras ce n'est pas facile (quelques secondes devant l'éternité) :grin:
J'avais fait des essais en faisant tourner en parallèle un affichage direct du DCF77 et l'heure du DS1307. Par contre je ne me souvient plus de l'ecart =(
Actuellement, je fais les mises à jour rapide en ajoutant 1 seconde.
@+

gunsman76:
Tu peux m'expliquer en 2/3 mots comment faire pour synchroniser le ds1307 avec le dcf77 ?

Extrait de mon code. C'est la fonction setRTCTime() qui fait le boulot, appelée selon certaines conditions.

#include "Funkuhr.h"
#include <Wire.h>
#include "RTClib.h"

void setup() {                	

   Wire.begin();
    RTC.begin();
}

/*
* Fonction utilisée lors de la synchro avec l'heure reçue par DCF77
*/
void setRTCTime(Dcf77Time dt){
	DateTime d(dt.year, dt.month, dt.day, dt.hour, dt.min, dt.sec); 
	RTC.adjust(d);
}


void syncRTC(){
	long now = millis();
	//synchro heure si DCF77 synchronisé et délai de synchro max écoulé
	if(lastRTCSync == 0 || now-lastRTCSync > RTC_SYNC_PERIOD){
		#ifdef DEBUG_SERIAL 
			Serial.print("Tentative de synchro RTC. DCF synced?: ");
			Serial.println(dcf.synced());
		#endif
	  if(dcf.synced()){
		  struct Dcf77Time dt = { 0 };
		  dcf.getTime(dt);
		  setRTCTime(dt);
		  lastRTCSync = now; 	
		  #ifdef DEBUG_SERIAL 
			Serial.print("Horloge RTC Synchronisee a: ");
			Serial.print(dt.hour);
			Serial.print(" ");
			Serial.println(dt.min);
		  #endif
	  if(dt.hour >= 3 && dt.hour < 6){
			  //si on vient de synchroniser l'horloge et qu'il est plus de 3h du matin
			  //on endort l'horloge jusqu'à une prochaine détection de mouvement
			  sleepMode = 2;
		  }			  
	  }
  }  
}

J'en parle un peu sur mon blog ici: L'horloge RTC - BreizhMakers
et là: Le récepteur DCF77 - BreizhMakers

patg_:

gunsman76:
Tu peux m'expliquer en 2/3 mots comment faire pour synchroniser le ds1307 avec le dcf77 ?

Extrait de mon code. C'est la fonction setRTCTime() qui fait le boulot, appelée selon certaines conditions.

#include "Funkuhr.h"

#include <Wire.h>
#include "RTClib.h"

void setup() {

Wire.begin();
   RTC.begin();
}

/*

  • Fonction utilisée lors de la synchro avec l'heure reçue par DCF77
    */
    void setRTCTime(Dcf77Time dt){
    DateTime d(dt.year, dt.month, dt.day, dt.hour, dt.min, dt.sec);
    RTC.adjust(d);
    }

void syncRTC(){
long now = millis();
//synchro heure si DCF77 synchronisé et délai de synchro max écoulé
if(lastRTCSync == 0 || now-lastRTCSync > RTC_SYNC_PERIOD){
#ifdef DEBUG_SERIAL
Serial.print("Tentative de synchro RTC. DCF synced?: ");
Serial.println(dcf.synced());
#endif
 if(dcf.synced()){
 struct Dcf77Time dt = { 0 };
 dcf.getTime(dt);
 setRTCTime(dt);
 lastRTCSync = now;
 #ifdef DEBUG_SERIAL
Serial.print("Horloge RTC Synchronisee a: ");
Serial.print(dt.hour);
Serial.print(" ");
Serial.println(dt.min);
 #endif
 if(dt.hour >= 3 && dt.hour < 6){
 //si on vient de synchroniser l'horloge et qu'il est plus de 3h du matin
 //on endort l'horloge jusqu'à une prochaine détection de mouvement
 sleepMode = 2;
 }  
 }
 }  
}

Merci je fais des essais demain !

Bon j'ai un petit soucis...

J'essaye de greffer sur le programme la librairie RTClib. Dès que j'essaye de compiler j'ai une erreur :

In file included from DCFOK.ino:3:
/Users/gunsman76/Documents/Arduino/libraries/RTClibB/RTClib.h:17: error: expected unqualified-id before '/' token
/Users/gunsman76/Documents/Arduino/libraries/RTClibB/RTClib.h:17: error: expected )' before '/' token /Users/gunsman76/Documents/Arduino/libraries/RTClibB/RTClib.h:17: error: expected )' before '/' token
/Users/gunsman76/Documents/Arduino/libraries/RTClibB/RTClib.h:17: error: expected `)' before '/' token

Alors que tous mes autres programmes utilisant cette librairie fonctionnent parfaitement. J'ai essayé de relancer tout, mais rien à faire. Il y a une incompatibilité entre le DCF et le DS1307 ?

Ou j'ai raté un truc... :~

Et je me demande quand même si c'est vraiment une bonne solution ce DCF77.

regarder :

Cycle: 2 Pulse :1
Cycle: 173 Pulse :139
Cycle: 944 Pulse :1
Cycle: 2 Pulse :2
Cycle: 50 Pulse :250
Cycle: 1000 Pulse :153
Cycle: 692 Pulse :1
Cycle: 354 Pulse :68
Cycle: 70 Pulse :1
Cycle: 887 Pulse :230
Cycle: 281 Pulse :0
Cycle: 2 Pulse :1
Cycle: 2 Pulse :0
Cycle: 2 Pulse :17

C'est vraiment très très instable...

Je pense que malgré la relative simplicité de la chose, une modification bi annuel de l'heure par commande serait quand même la meilleure des solutions.

Bon alors après 2 heures d'essai à capter le signal dans tous les sens, aujourd'hui vu la météo (ciel couvert) j'ai finalement abandonné l'idée du DCF77. S'il faut attendre un ciel clair, que le DCF soit correctement placé etc... ce n'est pas vraiment gérable et je comprends donc pourquoi peu de personnes l'utilise.

Je repars donc sur l'idée de prendre une DS1307 de bonne qualité (je vais éviter les super offre de la baie...) et mettre à jour l'heure en fonction de la saison.

Bonjour,

Je reviens sur ce forum, après l'avoir un peu "délaissé", car j'ai passé beaucoup plus de temps sur la mise au point de mon imprimante 3D et sur la mise en place d'un Fab Lab que sur arduino.
Mais chaque chose nécessitant son temps, je ne vais pas tarder à me remettre à l'arduino, et à des projets, notamment la construction de petits robots, maintenant que je peux fabriquer facilement diverses pièces.

Pour en venir au sujet du topic :
Pour mon système de domotique DOMOWEB 2012 - Mon site perso : Guy SINNIG, j'avais le même problème. Comme le système est installé dans le local technique,qui se trouve dans le sous-sol, il aurait été compliqué d'utiliser un DCF77, car il faut une antenne pour capter le signal teuton.
Aussi à chaque "reset" je recale l'horloge en NTP et j'ai "bidouillé" un bout de code pour automatiser le passage à l'heure d'été ou d'hiver.
Je vous livre les fonctions utilisées pour cela.
N'étant pas informaticien, c'est loin d'être optimisé.

// --------------- début de la fonction envoi requête NTP  ----------    

unsigned long NTP() // retourne le nombre de sec. depuis 1 Jan 1970. (Unix time)
{ 
  EthernetUDP Udp;
 // IPAddress timeServer(192, 43, 244, 18); // time.nist.gov NTP server - PB -
 // IPAddress timeServer(130, 149, 17, 21); // ntps1-0.cs.tu-berlin.de
  IPAddress timeServer(194, 2, 0, 28); // ntp0.oleane.net
 // IPAddress timeServer(195, 220, 194, 193); // ntp.sophia.cnrs.fr
  
  const int NTP_PACKET_SIZE= 48; // NTP time stamp is in the first 48 bytes of the message
  byte packetBuffer[NTP_PACKET_SIZE]; //buffer to hold incoming and outgoing packets 
  memset(packetBuffer, 0, NTP_PACKET_SIZE); //initialise toute les "case" du tableau à 0
  
  //construction de la requete
  packetBuffer[0] = 0b11100011;   // LI, Version, Mode
  packetBuffer[1] = 0;     // Stratum, or type of clock
  packetBuffer[2] = 6;     // Polling Interval
  packetBuffer[3] = 0xEC;  // Peer Clock Precision
  // 8 bytes of zero for Root Delay & Root Dispersion
  packetBuffer[12]  = 49; 
  packetBuffer[13]  = 0x4E;
  packetBuffer[14]  = 49;
  packetBuffer[15]  = 52;

  Udp.begin(8888);		    
  Udp.beginPacket(timeServer, 123); //NTP requests are to port 123
  Udp.write(packetBuffer,NTP_PACKET_SIZE);
  Udp.endPacket(); 

  delay(1000);  
  if ( Udp.parsePacket() ) 
  {  
    Udp.read(packetBuffer,NTP_PACKET_SIZE);
    Udp.stop();

    const unsigned long seventyYears = 2208988800UL; 
    unsigned long highWord = word(packetBuffer[40], packetBuffer[41]);
    unsigned long lowWord = word(packetBuffer[42], packetBuffer[43]);
    unsigned long secsSince1900 = highWord << 16 | lowWord;
    
    return secsSince1900 - seventyYears;
  }
    else
  {  
    lcd.clear(); // efface LCD
    lcd.setCursor(0,0); // positionne curseur colonne 1, ligne 1
    lcd.print("Pb NTP");
  }
  return 0;
}

// --------------- fin de la fonction envoi requête NTP  ---------- 

// --------------- début de la fonction fonction recherche NTP et mise à jour RTC ---------- 

void print_NTP_Time(unsigned long epoch) // fonction utilitaire, affiche l'heure UTC
{
  lcd.clear(); // efface LCD
  lcd.setCursor(0,0); // positionne curseur colonne 1, ligne 1
  if (epoch == 0)
  {
    lcd.print("Defaut NTP");
  }
  else
  {
    calculDLS();
    lcd.print("NTP = ");
    lcd.print(((epoch  % 86400L) / 3600) + 1 + DLS);
  //  Serial.print(':');
    lcd.print(':');
    if ( ((epoch % 3600) / 60) < 10 ) // In the first 10 minutes of each hour, we'll want a leading '0'
    {
      lcd.print('0');
    }
    lcd.print((epoch  % 3600) / 60);
    lcd.print(':');  
    if ( (epoch % 60) < 10 ) // In the first 10 seconds of each minute, we'll want a leading '0'
    {
      lcd.print('0');
    }
      lcd.print(epoch %60);
      RTC.stop(); // Arret du module RTC pour mise à jour des registre interne !!! important, il faut toujours arréter le RTC avant une mise à jour de l'heure.
      RTC.set(DS1307_SEC,(epoch %60)); // Réglage des secondes
      RTC.set(DS1307_MIN,((epoch  % 3600) / 60)); // Réglage des minutes
      RTC.set(DS1307_HR,(((epoch  % 86400L) / 3600) + 1 + DLS)); // Réglage des heures
      RTC.start(); // Re-mise en marche du module RTC
  }
}
// --------------- fin de la fonction fonction recherche NTP et mise à jour RTC ----------

// --------------- début de la fonction calcul DLS ----------

  void calculDLS() // fonction qui calcule DLS (Day Light Saving : heure d'hiver DLS = 0 , d'heure d'été DLS = 1)
  {
  if ((mois > 3) && (mois < 10) || ((mois == 3) && (date > 24) && (((date-jour) > 24) || (jour == 7))) || ((mois == 10) && ((date < 25) || (((date-jour) < 25) && (jour != 7)))))
  { 
    DLS =1; 
  }
  else
  {
    DLS = 0;
  }
  }
// --------------- fin de la fonction calcul DLS ----------

N'hésitez pas à commenter. Je suis preneur de toute critique, surtout si elle est constructive.

@+

Merci pour ton aide.

Le problème c'est justement que le module que j'utilise n'est pas raccordé au réseau... Et donc il me faut une solution qui ajoute une heure ou enlève une heure automatiquement.

Donc l'idée d'ajouter une heure avec mise en mémoire de l'information en rom, est je pense la meilleure des idées.

Je ne me souvenais plus que tu n'étais pas connecté.
Effectivement tu as la solution d'une table en EEPROM ou tu rentres les dates pour les x prochaines années.
Mais tu peux aussi calculer le passage de l'heure d'hiver ou d'été comme je l'ai fait.
Reste posée la question de la dérive de l'horloge.

@+

Pour la dérive, le fait d'avoir pris une horloge de qualité m'a permis d'avoir une très très faible dérive, je dois avoir 1 seconde maxi sur 3 mois.

Donc le problème reste posé, comment déclencher une action à 13h en heure hiver ou 13h en heure d'été sans connection internet et sans DCF77 ?

Dur dur.

if (now.dayOfWeek() == 2 && now.hour() == 6 && now.minute() == 20 && now.second() == 59) {
  digitalWrite (relais,HIGH);
  lcd.setCursor(0,1);
  lcd.print("declenche action");
  delay (1000);
  digitalWrite (relais,LOW);
  lcd.clear();

Il faudrait que mettre en eprom hiver / été avec une ligne de commande (+1 et -1 à l'heure courante)

Ensuite dans la ligne de commande ajouter la valeur de l'eprom à "now.hour()" donc now.hour + (eprom) pour avoir la mise en route de la commande à l'heure demandé.

Ou alors je me plante complètement et se serait mieux de modifier l'heure du DS1307 ?

gunsman76:
Merci pour ton aide.

Le problème c'est justement que le module que j'utilise n'est pas raccordé au réseau... Et donc il me faut une solution qui ajoute une heure ou enlève une heure automatiquement.

Donc l'idée d'ajouter une heure avec mise en mémoire de l'information en rom, est je pense la meilleure des idées.

Bonjour gunsman76
Intrinsequement il n'y a pas de bon ou mauvais DS1307, il y a simplement un bonne ou mauvaise base de temps (le quartz)
voir AN 58