Mega2560 + Ethernet + DS1307 + LCD ==> Mettre à l'heure le RTC par NTP

Bonjour,
Voila 2 jours que je galère pour mettre le DS1307 à la bonne date/heure, j'ai bien trouvé des programmes ca et là mais la plupart date un peu, les librairies ayant évoluées, je dois corriger les erreurs avant de les utiliser, mais en plus certains ne fonctionnent pas, d'autres mettent une date complètement erronée...

Bref, quelqu'un aurait il un programme qui marche avec la version 1.5.5 de l'éditeur Arduino ?
L'idée n'est pas de faire une pendule, mais de corriger la dérive du RTC par le NTP, je construis un projet qui nécessite une date et une heure relativement juste, avec changement d'heure été hiver automatique.

Merci d'avance pour toute suggestion.

ça peut peut-être t'aider....

http://forum.arduino.cc/index.php?topic=77849.0

Merci pour ta réponse, mais j'ai déjà testé et ça ne va pas, d'ailleurs le tropic date de 2011, et je n'arrive pas a trouver quelques choses de récent. Mais merci quand même d'avoir poster.

Essaye avec ça :
(le code de l'autre topic remis à jour pour arduino 1.xx)

/* 
 Permet de mettre une horloge ds1307 en synchronisation avec l'heure NTP
 Par le biais d'une application Arduino-Processing
 */

/*

 Comporte des parties de code écrites par :
- Maurice Ribble
  4-17-2008
  http://www.glacialwanderer.com/hobbyrobotics
  Pour le ds1307
 
 - X. HINAULT
   01/2010 
   http://www.mon-club-elec.fr
   Pour le LCD
 
 */
 
#include "Wire.h"
#define DS1307_I2C_ADDRESS 0x68

// Convert normal decimal numbers to binary coded decimal
byte decToBcd(byte val)
{
  return ( (val/10*16) + (val%10) );
}

// Convert binary coded decimal to normal decimal numbers
byte bcdToDec(byte val)
{
  return ( (val/16*10) + (val%16) );
}

// Stops the DS1307, but it has the side effect of setting seconds to 0
// Probably only want to use this for testing
/*void stopDs1307()
 {
 Wire.beginTransmission(DS1307_I2C_ADDRESS);
 Wire.send(0);
 Wire.send(0x80);
 Wire.endTransmission();
 }*/

// 1) Sets the date and time on the ds1307
// 2) Starts the clock
// 3) Sets hour mode to 24 hour clock
// Assumes you're passing in valid numbers
void setDateDs1307(byte second,        // 0-59
byte minute,        // 0-59
byte hour,          // 1-23
byte dayOfWeek,     // 1-7
byte dayOfMonth,    // 1-28/29/30/31
byte month,         // 1-12
byte year)          // 0-99
{
  Wire.beginTransmission(DS1307_I2C_ADDRESS);
  Wire.write(0);
  Wire.write(decToBcd(second));    // 0 to bit 7 starts the clock
  Wire.write(decToBcd(minute));
  Wire.write(decToBcd(hour));      // If you want 12 hour am/pm you need to set
  // bit 6 (also need to change readDateDs1307)
  Wire.write(decToBcd(dayOfWeek));
  Wire.write(decToBcd(dayOfMonth));
  Wire.write(decToBcd(month));
  Wire.write(decToBcd(year));
  Wire.endTransmission();
}

// Gets the date and time from the ds1307
void getDateDs1307(
byte *second,
byte *minute,
byte *hour,
byte *dayOfWeek,
byte *dayOfMonth,
byte *month,
byte *year)
{
  // Reset the register pointer
  Wire.beginTransmission(DS1307_I2C_ADDRESS);
  Wire.write(0);
  Wire.endTransmission();

  Wire.requestFrom(DS1307_I2C_ADDRESS, 7);

  // A few of these need masks because certain bits are control bits
  *second     = bcdToDec(Wire.read() & 0x7f);
  *minute     = bcdToDec(Wire.read());
  *hour       = bcdToDec(Wire.read() & 0x3f);  // Need to change this if 12 hour am/pm
  *dayOfWeek  = bcdToDec(Wire.read());
  *dayOfMonth = bcdToDec(Wire.read());
  *month      = bcdToDec(Wire.read());
  *year       = bcdToDec(Wire.read());
}


#include <LiquidCrystal.h> 

const int RS=2; //declaration constante de broche
const int E=3; //declaration constante de broche
const int D4=4; //declaration constante de broche
const int D5=5; //declaration constante de broche
const int D6=6; //declaration constante de broche
const int D7=7; //declaration constante de broche 
LiquidCrystal lcd(RS, E, D4, D5, D6, D7);

int octetReception=0; // variable de stockage des valeurs reçues sur le port Série (ASCII)
char caractereRecu=0; // variable pour stockage caractère recu
int compt=0; // variable comptage caractères reçus

String chaineReception=""; // déclare un objet String vide pour reception chaine
int oldSecond;

void setup() {
  byte second, minute, hour, dayOfWeek, dayOfMonth, month, year;
  Wire.begin(); // initialise 1wire

  Serial.begin(115200); // Initialise la communication série

  // lit les valeurs de l'horloge pour les replacer si elle est déjà à l'heure
  getDateDs1307(&second, &minute, &hour, &dayOfWeek, &dayOfMonth, &month, &year);
  if ((year)== NULL){ // si l'année = NULL
  // preinitialise l'horloge mets les valeurs lue précédement (à 0 si premier allumage)
  setDateDs1307(int(second), int(minute), int(hour), int(dayOfWeek), int(dayOfMonth), int(month), int(year));
  }

  //***** LCD *****//
  lcd.begin(20,4); // Initialise le LCD avec 20 colonnes x 4 lignes
  delay(10); // pause rapide pour laisser temps initialisation
  lcd.print("initialisation") ; // affiche la chaîne texte - message de test
  lcd.setCursor(0, 1) ;
  lcd.print("de l'horloge") ;
  delay(2000); // pause de 2 secondes
  lcd.clear(); // // efface écran et met le curseur en haut à gauche
  delay(100); // pour laisser temps effacer écran

  //**** liaison pour l'heure NTP ****//
  Serial.println('A'); // envoi un "top" à Processing
  while (millis()<5000) {  // attente pendant 5 secondes d'une liaison série (ne fonctionne pas en dessous)
    while (Serial.available()>0) { // tant qu'un octet est dans la liaison série

      octetReception=Serial.read(); // Lit le 1er octet reçu et le met dans la variable      

      if (octetReception==13) { // si l'octet reçu est le retour chariot (CR ou 13)

        // converti la chaine en entier pour les valeurs de temps
        second = int((chaineReception.charAt(0)-48)*10)+int(chaineReception.charAt(1)-48) ;
        minute = int((chaineReception.charAt(2)-48)*10)+int(chaineReception.charAt(3)-48);
        hour = int((chaineReception.charAt(4)-48)*10)+int(chaineReception.charAt(5)-48);
        dayOfWeek = 1;
        dayOfMonth = int((chaineReception.charAt(6)-48)*10)+int(chaineReception.charAt(7)-48);
        month = int((chaineReception.charAt(8)-48)*10)+int(chaineReception.charAt(9)-48);
        year = int((chaineReception.charAt(12)-48)*10)+int(chaineReception.charAt(13)-48); //int((chaineReception.charAt(10)-48)*1000)+int((chaineReception.charAt(11)-48)*100)+

        //mets le DS1307 en syncronisation avec l'horloge de l'ordinateur
        setDateDs1307(int(second), int(minute), int(hour), int(dayOfWeek), int(dayOfMonth), int(month), int(year));

        chaineReception=""; //RAZ de la chaine de réception
        break; // sort de la boucle while
      }
      else { // si le caractère reçu n'est pas un saut de ligne
        caractereRecu=char(octetReception); // convertit l'octet reçu en caractère
        chaineReception=chaineReception+caractereRecu; // ajoute le caratère à la chaine
      }
    }
  }
}

void loop(){ 

  byte second, minute, hour, dayOfWeek, dayOfMonth, month, year;

  getDateDs1307(&second, &minute, &hour, &dayOfWeek, &dayOfMonth, &month, &year);

  if(second!=oldSecond){  // change l'affichage uniquement au changement de seconde
    Serial.print(hour, DEC);
    Serial.print(":");
    Serial.print(minute, DEC);
    Serial.print(":");
    Serial.print(second, DEC);
    Serial.print("  ");
    Serial.print(month, DEC);
    Serial.print("/");
    Serial.print(dayOfMonth, DEC);
    Serial.print("/");
    Serial.print(year, DEC);
    Serial.print("  Day_of_week:");
    Serial.println(dayOfWeek, DEC);

    lcd.home(); 
    if (int(hour)<=9)lcd.print(0);
    lcd.print(int(hour));
    lcd.print (":");
    if (int(minute)<=9)lcd.print(0);
    lcd.print(int(minute));
    lcd.print (":");
    if (int(second)<=9)lcd.print(0);
    lcd.print(int(second));
    lcd.setCursor (0,1);
    if (int(dayOfMonth)<=9)lcd.print(0);
    lcd.print(int(dayOfMonth));
    lcd.print ("/");
    if (int(month)<=9)lcd.print(0);
    lcd.print(int(month));
    lcd.print ("/20");
    lcd.print(int(year));

  }
  oldSecond=second;  // réinitialise oldSecond

}

Merci pour ton aide, mais ça aussi j'ai essayé le problème principal que j'ai rencontré avec ce script c'est qu'il faut lancer un autre script pour mettre à l'heure, mais je n'ai pas sus comment le lancer :blush:

ça fonctionne avec Processing :

Même système qu'arduino, mais sur ton pc....

Ça c'est une super info !
La ce soir j'ai tout éteint je m'y remet d?s demain et je reviens poster pour les résultats, si ça marche ça pourrait en intéresser d'autre dans le même besoin.
Merci beaucoup.

Bonjour,
Alors j'ai fais le test, et effectivement l'horloge se met à jour avec l'heure que le PC à envoyé par le script. Bien sur il faut modifier le port 0 par le numéro dans la liste trouvé par "Processing" au bas de l'écran 0 étant le premier, pour moi le 3ème était donc le 2.

Mais, même si ca marche très bien, je voudrai un système autonome, qui aille chercher tout seul sur le net ou en local l'heure et remette le DS1307 à l'heure au rythme d'une fois par semaine par exemple (plus ou moins si nécessaire)

Je continu donc mes investigations, mais si vous avez la moindre proposition je suis preneur.
De même si je trouve soit directement, soit en le créant un script qui marche, je le déposerai ici.

Merci beaucoup pour votre aide.

Là tu cherches du tout fait...et tu trouveras pas.
Avec l'exemple que je t'ai donné, tu peux adapter à ce que tu veux.

Bonjour

Si je comprends bien, tu as un shield ethernet sur ton arduino, et souhaites recaler régulièrement l'horloge de ton DS1307 par procédure NTP via internet.

Si c'est ça, je peux t'aider car j'ai déjà fait et ça fonctionne bien.

Tu as déjà dans le code ci-dessus les fonctions qu permettent de lire ou d'écrire l'heure du DS1307.
Sur le site arduino, tu as un exemple d'interrogation NTP que tu peux reprendre.
L'interrogation NTP te retourne l'heure UTC, exprimée en secondes écoulées depuis le 01/01/1900 00:00, en binaire sur 4 octets.
Ton dernier problème est donc de passer ce format au format étendu pour ton DS1307.
Si c'est bien ça, alors je devrais pouvoir te mettre à disposition des bouts de code qu'il te suffira d'assembler.

Dans mon cas, j'utilise aussi la NVRAM du DS1307 pour stocker des informations complémentaires :

  • la dernière date/heure lue du DS1307, pour détecter les coupures électriques, au redémarrage de l'arduino
  • la date/heure de dernière synchronisation NTP, pour gérer le délai entre les demandes de synchronisation, sans être influencé par les coupures électriques.
    Et accessoirement, cela me permet aussi de détecter, au démarrage de l'arduino, que la pile du DS1307 est morte (quand je ne retrouve pas en NVRAM les données attendues).

Concernant l'heure locale : après plusieurs versions de code, j'en suis arrivé à la conclusion que le plus simple est de caler le DS1307 sur l'heure UTC, et de convertir en heure locale uniquement au moment des restitutions (LCD, page WEB, message sur moniteur série, etc.)

Bonjour,
Tout d'abord merci à tous de m'aider, je viens d'y repasser la journée et cette fois je tiens quelques choses qui fonctionne.
Mais je suis quand même intéressé par ton code et ton aide Bricoleau, puisque tu as déjà été confronté au problème.
Pour la mise en situation, j'ai un PC qui tourne en serveur donc allumé 24/24 et que j'ai configuré pour qu'il me fournisse du NTP, comme ca si la box est HS (si, si c'est déjà arrivé) j'ai quand même mon réseau local qui fonctionne et donc le NTP de dispo.

Voici donc le code qui m'a permit de tester, bien entendu tout n'est pas de moi, et je suis parti du code proposé par Jean-François et dans lequel j'ai ajouté de quoi obtenir le NTP et mettre à jour le RTC.

/* 
 Permet de mettre une horloge ds1307 en synchronisation avec l'heure NTP
 Par le biais d'une application Arduino-Processing
*/

/*

 Comporte des parties de code écrites par :
- Maurice Ribble
  4-17-2008
  http://www.glacialwanderer.com/hobbyrobotics
  Pour le ds1307
 
 - X. HINAULT
   01/2010 
   http://www.mon-club-elec.fr
   Pour le LCD

 - Rendu autonome : 01/2014
   Par jean-Luc NAPOLITANO
   Note : Cela sous entend d'avoir (attention aux broches communes) :
          - Un shield W5100 et une connexion reseau valide
          - Un shield afficheur LCD
          - Un shield RTC DS1307 (attention broches 4-5 sur Uno, 20-21 sur Mega2560)
          - Un Aduino (Uno, Mega etc)
 
*/

#include <Time.h>
#include <Wire.h>
#define DS1307_I2C_ADDRESS 0x68

// Pour l'accès au reseau
#include <SPI.h>
#include <Ethernet.h>
#include <EthernetUdp.h>
byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED }; // Utilisez votre MAC address (pas obligé d'en changer)
byte ip[] = { 192, 168, 0, 177 };                    // Pas de DHCP, utilisation d'une IP compatible au reseau local (plus simple)
byte subnet[] = { 255, 255, 255, 0 };                // Masque de sous-reseau
byte gateway[] = { 192, 168, 0, 254 };               // internet access via router (votre box généralement)

//**** liaison pour l'heure NTP ****//
unsigned int localPort = 8888; // Port local pour écouter les paquets UDP
IPAddress timeServer(192, 168, 0, 1); // PC local à paramétrer en serveur NTP ou un serveur NTP sur le net
const int NTP_PACKET_SIZE = 48; // L'horodatage NTP se trouve dans les 48 premiers octets du message
byte packetBuffer[ NTP_PACKET_SIZE]; // Tampon pour maintenir les paquets entrants et sortants

bool maj_heure = false; // Pour la mise à l'heure automatique
bool maj_manu = false;  // Pour la mise à l'heure manuelle

// Un exemple UDP pour envoyer et recevoir des paquets UDP
EthernetUDP Udp;
const long timeZoneOffset = -3600L; // Réglez-le sur le décalage en secondes à votre heure locale
//char isodate[25]; // L'heure actuelle au format ISO est stocké ici


// Convert normal decimal numbers to binary coded decimal
byte decToBcd(byte val)
{
  return ( (val/10*16) + (val%10) );
}

// Convert binary coded decimal to normal decimal numbers
byte bcdToDec(byte val)
{
  return ( (val/16*10) + (val%16) );
}

// Stops the DS1307, but it has the side effect of setting seconds to 0
// Probably only want to use this for testing
/*void stopDs1307()
 {
   Wire.beginTransmission(DS1307_I2C_ADDRESS);
   Wire.send(0);
   Wire.send(0x80);
   Wire.endTransmission();
 }
*/

// 1) Sets the date and time on the ds1307
// 2) Starts the clock
// 3) Sets hour mode to 24 hour clock
// Assumes you're passing in valid numbers
void setDateDs1307(byte second,        // 0-59
byte minute,        // 0-59
byte hour,          // 1-23
byte dayOfWeek,     // 1-7
byte dayOfMonth,    // 1-28/29/30/31
byte month,         // 1-12
byte year)          // 0-99
{
  Wire.beginTransmission(DS1307_I2C_ADDRESS);
  Wire.write(0);
  Wire.write(decToBcd(second));    // 0 to bit 7 starts the clock
  Wire.write(decToBcd(minute));
  Wire.write(decToBcd(hour));      // If you want 12 hour am/pm you need to set
  // bit 6 (also need to change readDateDs1307)
  Wire.write(decToBcd(dayOfWeek));
  Wire.write(decToBcd(dayOfMonth));
  Wire.write(decToBcd(month));
  Wire.write(decToBcd(year));
  Wire.endTransmission();
}

// Gets the date and time from the ds1307
void getDateDs1307(
byte *second,
byte *minute,
byte *hour,
byte *dayOfWeek,
byte *dayOfMonth,
byte *month,
byte *year)
{
  // Reset the register pointer
  Wire.beginTransmission(DS1307_I2C_ADDRESS);
  Wire.write(0);
  Wire.endTransmission();

  Wire.requestFrom(DS1307_I2C_ADDRESS, 7);

  // A few of these need masks because certain bits are control bits
  *second     = bcdToDec(Wire.read() & 0x7f);
  *minute     = bcdToDec(Wire.read());
  *hour       = bcdToDec(Wire.read() & 0x3f);  // Need to change this if 12 hour am/pm
  *dayOfWeek  = bcdToDec(Wire.read());
  *dayOfMonth = bcdToDec(Wire.read());
  *month      = bcdToDec(Wire.read());
  *year       = bcdToDec(Wire.read());
}


#include <LiquidCrystal.h> 

const int RS=8; //declaration constante de broche
const int E=9; //declaration constante de broche
const int D4=3; //declaration constante de broche
const int D5=5; //declaration constante de broche
const int D6=6; //declaration constante de broche
const int D7=7; //declaration constante de broche 
LiquidCrystal lcd(RS, E, D4, D5, D6, D7);

int octetReception=0;   // variable de stockage des valeurs reçues sur le port Série (ASCII)
char caractereRecu=0;   // variable pour stockage caractère recu
//int compt=0; // variable comptage caractères reçus

String chaineReception=""; // déclare un objet String vide pour reception chaine
int oldSecond;

void setup() {
  byte second, minute, hour, dayOfWeek, dayOfMonth, month, year;
  Wire.begin(); // initialise 1wire

  Serial.begin(115200); // Initialise la communication série

  // lit les valeurs de l'horloge pour les replacer si elle est déjà à l'heure
  getDateDs1307(&second, &minute, &hour, &dayOfWeek, &dayOfMonth, &month, &year);
  if ((year)== NULL){ // si l'année = NULL
    // preinitialise l'horloge mets les valeurs lue précédement (à 0 si premier allumage)
    setDateDs1307(int(second), int(minute), int(hour), int(dayOfWeek), int(dayOfMonth), int(month), int(year));
  }

  //***** LCD *****//
  lcd.begin(20,4); // Initialise le LCD avec 20 colonnes x 4 lignes (à changer en 16/2 en fonction de l'afficheur)
  delay(10); // pause rapide pour laisser temps initialisation
  lcd.print("initialisation") ; // affiche la chaîne texte - message de test
  lcd.setCursor(0, 1) ;
  lcd.print("de l'horloge") ;
  delay(2000); // pause de 2 secondes
  lcd.clear(); // efface écran et met le curseur en haut à gauche
  delay(100);  // pour laisser temps effacer écran

  // Connexion au reseau
  Ethernet.begin(mac, ip);
  Udp.begin(localPort);
  
  Serial.println("waiting for sync");

//  ***************** La remise à l'heure par script n'est plus utilisée *****************
//  Enlever les REMs pour une utilisation ponctuelle (1ère mise en route par exemple)
//  Serial.println('A'); // envoi un "top" à Processing
//  while (millis()<5000) {  // attente pendant 5 secondes d'une liaison série (ne fonctionne pas en dessous)
//    while (Serial.available()>0) { // tant qu'un octet est dans la liaison série
//
//      octetReception = Serial.read(); // Lit le 1er octet reçu et le met dans la variable      
//
//      if (octetReception == 13) { // si l'octet reçu est le retour chariot (CR ou 13)
//
//        // converti la chaine en entier pour les valeurs de temps
//        second = int((chaineReception.charAt(0)-48)*10)+int(chaineReception.charAt(1)-48) ;
//        minute = int((chaineReception.charAt(2)-48)*10)+int(chaineReception.charAt(3)-48);
//        hour = int((chaineReception.charAt(4)-48)*10)+int(chaineReception.charAt(5)-48);
//        dayOfWeek = 1;
//        dayOfMonth = int((chaineReception.charAt(6)-48)*10)+int(chaineReception.charAt(7)-48);
//        month = int((chaineReception.charAt(8)-48)*10)+int(chaineReception.charAt(9)-48);
//        year = int((chaineReception.charAt(12)-48)*10)+int(chaineReception.charAt(13)-48); //int((chaineReception.charAt(10)-48)*1000)+int((chaineReception.charAt(11)-48)*100)+
//
//        // mets le DS1307 en syncronisation avec l'horloge de l'ordinateur
//        setDateDs1307(int(second), int(minute), int(hour), int(dayOfWeek), int(dayOfMonth), int(month), int(year));
//
//        chaineReception=""; //RAZ de la chaine de réception
//        break; // sort de la boucle while
//      }
//      else { // si le caractère reçu n'est pas un saut de ligne
//        caractereRecu=char(octetReception); // convertit l'octet reçu en caractère
//        chaineReception=chaineReception+caractereRecu; // ajoute le caratère à la chaine
//      }
//    }
//  }
}

Pas assez de place dans le précédent post, voici donc la suite :

void loop(){ 

  byte second, minute, hour, dayOfWeek, dayOfMonth, month, year;
  byte tmp_second, tmp_minute, tmp_hour;

  getDateDs1307(&second, &minute, &hour, &dayOfWeek, &dayOfMonth, &month, &year);

  if(second != oldSecond){  // change l'affichage uniquement au changement de seconde
    Serial.print(hour, DEC);
    Serial.print(":");
    Serial.print(minute, DEC);
    Serial.print(":");
    Serial.print(second, DEC);
    Serial.print("  ");
    Serial.print(month, DEC);
    Serial.print("/");
    Serial.print(dayOfMonth, DEC);
    Serial.print("/");
    Serial.print(year, DEC);
    Serial.print("  Day_of_week:");
    Serial.println(dayOfWeek, DEC);

    lcd.home(); 
    if (int(hour)<=9)lcd.print(0);
    lcd.print(int(hour));
    lcd.print (":");
    if (int(minute)<=9)lcd.print(0);
    lcd.print(int(minute));
    lcd.print (":");
    if (int(second)<=9)lcd.print(0);
    lcd.print(int(second));
    lcd.setCursor (0,1);
    if (int(dayOfMonth)<=9)lcd.print(0);
    lcd.print(int(dayOfMonth));
    lcd.print ("/");
    if (int(month)<=9)lcd.print(0);
    lcd.print(int(month));
    lcd.print ("/20");
    lcd.print(int(year));

  }
  oldSecond=second;  // réinitialise oldSecond
  
  // Pour la mise à l'heure NTP automatique 1 fois par semaine
  // Par exemple le dimanche à 3 heures
  // Pour la prise en compte changement d'heure été/hiver immediate (ou presque)
  // Modifiez ces paramètres à vos besoins.
  if ((dayOfWeek == 1 && hour == 3 && maj_heure == false) || maj_manu == true)
  {
    Serial.println("Mise a l'heure du RTC par NTP en cours...");
    maj_heure = true; // Pour ne faire qu'une fois le dimanche à 3 heures
    maj_manu = false; // Pour ne faire qu'une fois
    time_t dh = getNtpTime();
    Serial.print("Reponse : ");
    Serial.println(dh);

    // Ecrit les heure, minute et second:
    Serial.print("Donne en clair : ");       
    // Ecrit l'heure (86400 egale aux secondes par jour)
    tmp_hour = (dh  % 86400L) / 3600;
    Serial.print(sDigits(tmp_hour,2)); 
    Serial.print(':');
    // Ecrit les minutes (3600 egale aux secondes par minute)
    tmp_minute = (dh  % 3600) / 60;
    Serial.print(sDigits(tmp_minute,2));
    Serial.print(':');
    // Ecrit les secondes
    tmp_second = dh %60;
    Serial.println(sDigits(tmp_second,2));

    // mets le DS1307 en syncronisation avec l'horloge de l'ordinateur
    setDateDs1307(int(tmp_second), int(tmp_minute), int(tmp_hour), int(dayOfWeek), int(dayOfMonth), int(month), int(year));

  } else if (dayOfWeek == 1 && hour > 3 && maj_heure == true) // Pour réinitialiser et attendre le dimanche d'après
  {
    maj_heure = false;
  }
  // Pour la mise à l'heure NTP manuelle
  if (Serial.available())
  {
    octetReception = Serial.read();
    caractereRecu = char(octetReception); // convertit l'octet reçu en caractère
    if (caractereRecu == 'm' || caractereRecu == 'M') // M ou m = demande de mise à l'heure manuelle
    {
      Serial.println("Mise a l'heure manuelle du RTC par le NTP.");
      maj_manu = true;
    }
  }
}

/*-------- NTP code ----------*/

String sDigits(byte digit, byte format) {
  String nbr = String(digit);
  nbr.trim();
  nbr = "0000" + nbr;
  nbr = nbr.substring(nbr.length() - format);
  return nbr;
}

unsigned long getNtpTime() {
  Serial.println("Requete NTP sur Serveur local.");
  sendNTPpacket(timeServer); // Envoyer un paquet au serveur NTP
  // Attendre pour voir si une réponse est disponible
  delay(1000);
  if ( Udp.parsePacket() ) {
    // Nous avons reçu un paquet et allons lire les données qu'il contient
    Udp.read(packetBuffer,NTP_PACKET_SIZE); // Lire le paquet dans la mémoire tampon

    // l'horodatage commence à l'octet 40 du paquet reçu et est de quatre octets,
    // ou de deux mots, de longues. Tout d'abord, extraire les deux mots:

    unsigned long highWord = word(packetBuffer[40], packetBuffer[41]);
    unsigned long lowWord = word(packetBuffer[42], packetBuffer[43]);
    // Combiner les quatre octets (deux mots) dans un entier long 
    // c'est le temps NTP (en secondes depuis le 1er janvier 1900):
    unsigned long secsSince1900 = highWord << 16 | lowWord;
    
    // Maintenant convertir le temps NTP dans le temps Unix (qui commence le 1 janv. 1970).
    // En secondes, ce qui fait 2208988800:
    const unsigned long seventyYears = 2208988800UL;
    unsigned long epoch = secsSince1900 - seventyYears;
    return epoch - timeZoneOffset;
  }
  Serial.println("Serveur local ne repond pas.");
  return 0; // Retourne 0 en cas d'impossibilité d'obtenir le temps
}

// Envoyer une requête NTP au serveur de temps à l'adresse indiquée
unsigned long sendNTPpacket(IPAddress& address) {
  // Réglez tous les octets de la mémoire tampon à 0
  memset(packetBuffer, 0, NTP_PACKET_SIZE);
  // Initialiser les valeurs nécessaires pour former la demande NTP
  // (voir URL ci-dessus pour plus de détails sur les paquets)
  packetBuffer[0] = 0b11100011; // LI, Version, Mode
  packetBuffer[1] = 0; // Strate, ou type d'horloge
  packetBuffer[2] = 6; // Intervalle d'interrogation
  packetBuffer[3] = 0xEC; // Pairs d'horloge de précision
  // 8 octets de zéro pour Délai & Dispersion
  packetBuffer[12] = 49;
  packetBuffer[13] = 0x4E;
  packetBuffer[14] = 49;
  packetBuffer[15] = 52;

  // Tous les champs NTP ont reçu des valeurs,
  // maintenant vous pouvez envoyer un paquet demandant un horodatage:                 
  Udp.beginPacket(address, 123); // Les demandes NTP se font sur le port 123
  Udp.write(packetBuffer,NTP_PACKET_SIZE);
  Udp.endPacket();
}

Pour compléter ton programme :

Voici un exemple opérationnel d'interrogation NTP pour arduino équipé d'un shield ethernet, qui te retourne en quelques lignes de code la date et l'heure locale de France métropolitaine, dans des variables directement exploitables pour (par exemple) mettre à jour ton DS1307.
Avec gestion automatique des changements d'heure d'été/hiver

Les quelques lignes intéressantes :

void mafonction()
  IPAddress ip_serveur_NTP(132, 163, 4, 101);
  uint32_t timestamp;
  uint8_t annee,mois,jour,joursem,heure,minute,seconde;
  const uint32_t delai_max_attente_en_ms = 5000;

  DemanderHeureNTP(ip_serveur_NTP);
  timestamp = EcouterHeureNTP(delai_max_attente_en_ms);
  if (timestamp != 0)
  {
    timestamp = timestamp - 3155673600UL; //ramène le zéro à l'an 2000
    timestamp = CalculerTimestampLocalFrance(timestamp);//UTC + 1 + heures d'été
    DecomposerTimestamp(timestamp, &annee, &mois, &jour, &joursem, &heure, &minute, &seconde);
    //à partir de là les variables annee,mois,jour,etc. sont à l'heure de France métropolitaine
...

Code complet en pièce jointe.

ArduinoNTP.cpp (5.97 KB)

Bonjour,
Merci beaucoup pour la réponse Bricoleau, ton code me plait vraiment, je le trouve même beaucoup moins lourd que le miens, bon le mien gère l'afficheur, quand il faut déclencher la requête etc... Mais tout de même pour la partie NTP c'est super bien et concis.
Tu as été plus loin dans la conversion Timestamp puisque tout y est, moi je me suis contenté de récupérer seulement l'heure en me disant que de toute façon le RTC ne pouvait pas dériver de plus de quelques secondes par semaines, mais en y repensant, c'est mieux car au moins même lors de la première mise à jour (changement de module, pile...) tout est parfait.
Je vais donc si tu le permet m'en inspirer pour mon projet.

Je ne sais pas si je l'ai déjà dis, mon projet consiste en la régulation d'une vanne 4 voies et de l'accélérateur (circulateur) pour mon chauffage, avec prise de température de l'eau de départ, extérieur, intérieur, retour... Je voudrai pouvoir par le réseau en http (domotique) mettre en service/arrêter le chauffage, modifier les paramètres de températures (jour/nuit/absence...) etc... Je teste d'abord tous les besoins avant de développer le projet final.

Maintenant je m'attaque à la carte SD qui contiendra les pages web et la config (en cas de redémarrage)

Merci pour tout, et connaissant mon projet, si vous avez des suggestions je suis preneur.

Bonjour

Bien sûr que tu peux reprendre le code, il est là pour ça. :wink:

Pour la suite, et personnellement, je te conseillerais plutôt :

  • d'utiliser l'eeprom de l'arduino pour stocker ta config (plus fiable que la SD) : il te faudra juste prévoir un programme spécifique qui permet son chargement dans l'eeprom.
  • d'utiliser la mémoire flash (PROGMEM) pour stocker toute l'information statique de tes pages WEB. Sauf si vraiment tu veux faire autre chose que quelques pages html assez simples (m'enfin t'attends pas à avoir un serveur de type LAMP).
  • de conserver en RAM les derniers relevés des données dynamiques que tu veux restituer dans tes pages WEB.
  • de réserver la SD uniquement à l'enregistrement de données sous forme de log, pour exploitation ultérieure.

Si c'est à ta portée, tu pourras ultérieurement ajouter un serveur UDP sur ton arduino. C'est assez simple à faire et là tu t'ouvres des portes vers plein d'autres possibilités, mais il y aura aussi du code à produire sur le partenaire à l'autre bout de la ligne, et là c'est potentiellement une autre paire de manche.
Côté arduino, tu développes des fonctions accessibles à distance via UDP, pour par exemple :

  • passer en commande manuelle
  • modifier les paramètres
  • récupérer tout ou partie de la log
    Après côté distant, c'est libre cours à ton imagination pour exploiter ses fonctions disponibles :
  • développer un programme de type "console à distance" sur ton PC
  • développer une appli android pour ta tablette (plus sexy que des pages HTML renvoyées par un mini serveur web arduino)
  • monter un vrai serveur web de type LAMP sur un raspberry pi (très tendance chez les geek)
  • interfacer ton arduino avec le reste de tes équipements domotiques (ça aussi)
    ...

Merci beaucoup Bricoleau pour toutes ces infos, coté programmation, rien ne me fais peur, à la base je suis développeur informatique depuis le début des années 80, pas plus de soucis avec l'assembleur qu'avec beaucoup d'autres langages d'ailleurs, je ne veux pas me gonfler, mais juste me situer.
L'Arduino et autre RPI je m'y suis mis que dernièrement (fin décembre) c'est pourquoi je galère autant, pas encore la maitrise du sujet. Ce qui m'y a mit, c'est que mon système de régulation existant est vieillissant et analogique. Et donc, à mon avis, peu économique en consommation fuel.

Tous tes conseils me semblent tellement pertinent que je vais en tenir compte, tu peux en être sure.
Pour l'UDP ca parait aussi très intéressant, si tu as un bout de code pour me mettre sur la voie, je t'en serais très reconnaissant.
Mon idée, connaissant peu les possibilités du matériel, était de faire une simple page web découpée en 3 parties. Une sur la SD qui serait l'entête propre en html, css, des images éventuelles légère en taille etc. Une partie centrale fournie sous forme de formulaire contenant les paramètres courant issus des variables utilisées donc dans la ram et une troisième partie pour finir proprement la page web. Les enregistrements via le formulaire se faisant dans un fichier sur la SD mais sur ce point, tes recommandations m'avait orientées sur un enregistrement dans l'eeprom. Mais l'UDP peut effectivement ouvrir d'autres champs de possibilité, alors pourquoi pas...

J'ai déjà monté l'électronique, fais et testé la partie régulation en me basant sur "La loi d'eau" comme l'appellent les chauffagistes.
Une fois que j'aurai fini de tester tous ces bouts de programme, j'écrirai le programme final en me basant sur l'ensemble.
Ce n'est pas forcement la bonne méthode, mais elle m'apporte au fur et à mesure des connaissances sur ce monde de l'Arduino.

J'ai fais pareil pour le RPI (encore que c'était plus facile avec linux) et maintenant j'en ai un qui tourne pour s'occuper de mon imprimante 3D (Leapfrog Creatr 2 heads) spécialement pour ca. Je lui envoi le fichier à imprimer (serveur Repetier + mes améliorations) et je peux même éteindre mon micro et suivre si tout se passe bien depuis mon iPad devant ma télé à l'autre bout de la maison (webcam aussi).

Bon je suis bavard, mais aussi très passionné tu me pardonneras.

Merci pour tout, vraiment merci beaucoup.
@+ Jean-Luc

Je viens de tester ton programme et je rencontre un soucis avec le serveur NTP, je n'arrive pas à me connecter "Pas de reponse serveur NTP" alors que lorsque je ping depuis ma machine, je le trouve bien :
ping -a 132.163.4.101
Reponse :
Envoi d'une requête 'ping' sur time-a.timefreq.bldrdoc.gov [132.163.4.101] avec 32 octets de données :
Réponse de 132.163.4.101 : octets=32 temps=147 ms TTL=41
Réponse de 132.163.4.101 : octets=32 temps=139 ms TTL=41
Réponse de 132.163.4.101 : octets=32 temps=147 ms TTL=41
Réponse de 132.163.4.101 : octets=32 temps=147 ms TTL=41

Statistiques Ping pour 132.163.4.101:
Paquets : envoyés = 4, reçus = 4, perdus = 0 (perte 0%),
Durée approximative des boucles en millisecondes :
Minimum = 139ms, Maximum = 147ms, Moyenne = 145ms

As-tu une idée du pourquoi ???
J'ai essayé plusieurs autres IP externes c'est pareil.

bizarre : le prog tel que fournit marche bien chez moi.
C'est bien celui-là que tu utilises extactement sans avoir rien changé d'autre que les adresses ip et mac de ton arduino ?

Testé sur une arduino Ethernet (une vraie). Sur le coup, j'avais constaté qu'il perd une synchro de temps en temps, mais dans 90% des cas la réponse est OK.

Pas pris le temps de creuser l'affaire pour savoir si c'est problème réseau ou soft.
Techniquement, le soft pourrait indiquer "pas de réponse NTP" si le timestamp lu est à zéro.
Du coup je me suis demandé si je n'avais pas de temps en temps un pb de lecture de trame retour.
Elle fait 48 octets. Peut-être que le udp.read() se déclenche alors que les 48 octets ne sont pas encore reçus.
Et comme on va lire le timestamp dans les derniers octets...
Je vais essayer de regarder ça.

Si tu ajoutes une petite attente (genre 1 seconde) entre la demande NTP et la première écoute, cela ne fait rien de mieux ?

Bonjour,
J'ai copier/coller le code sans aucune modification.
Jai ajouté un delay entre la demande et l'écoute de 1 à 5 seconde (5 essais) rien n'y fait, pas de connexion ntp.
Pas non plus en changeant juste l'ip des serveur ntp. C'est a n'y rien comprendre, surtout si chez toi ca marche.
J'ai repris une partie de ton code et l'ai adapté pour interroger mon serveur ntp et là ca marche (reseau local)
Pas les bonnes dates (windows donne en ntp le timestamp courant) mais je ne m'explique pas pourquoi je ne me connecte pas aux serveurs distants. J'ai vu sur certains site listant les serveur ntp que la plupart demande qu'on leur envoi un mail signifiant l'utilisation, est-ce pour qu'ils autorise ?
J'attend tes conclusions, d'avance merci.

Chez moi, sur mon réseau local, les adresses IP sont toutes en 192.168.0.xxxx
J'ai utilisé la 192.168.0.14 pour mon arduino Ethernet.
C'est l'adresse qui figure dans mon code, utilisée dans la fonction Ethernet.begin(...).

Si tu l'as laissée telle quelle, et qu'elle n'est pas compatible avec ton masque de sous-réseau, ta box ne doit probablement pas la transcoder correctement, et donc elle ne t'envoie pas les trames retour.
Après, je ne suis pas certain de cette hypothèse, car je ne suis pas super calé en réseaux.