Time.h mit NTP Server synchronisieren

Hallo,
Der Time NTP Beispielcode funktioniert nicht, braucht die UdpBytewise.h und insgesamt scheint der Code mit den lib's nicht mehr zusammenzupassen.

Auch wird mit einem Delay(1000) auf eine mögliche Antwort gewartet, wenn man ein größeres Projekt hat und der Atmel noch was anderes zu tun hat geht sowas gar nicht.

Ich habe nun den Code etwas abgändert braucht keine UdpBytewise und läuft mit den aktuellen lib's
Mein oberste Prioität war: Wenn der Timeserver aus welche Gründen auch immer nicht erreichbar ist, darf die Funktion den Atmel nicht blockieren.

Hier mein pseudo Code incl. Sommer/Winterzeitumstellung (den Code habe ich von jurs "geklaut") :slight_smile:

#include <Ethernet.h>
#include <EthernetUdp.h>
#include <Time.h>

byte timeServer[]  = { 64,90,182,55 };   // time.nist.gov

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

EthernetUDP udp;

unsigned int localPort = 8888;
unsigned long startMillis;
boolean udpwait=true;
byte trying=0;

void setup() {
Ethernet.begin(mac,ip,dnsServer,gateway,subnet);

setSyncProvider(getNtpTime);
setSyncInterval(3600);   

}

/*-------- utility code from jurs ----------*/
boolean summertime_EU(int year, byte month, byte day, byte hour, byte tzHours)
// European Daylight Savings Time calculation by "jurs" for German Arduino Forum
// input parameters: "normal time" for year, month, day, hour and tzHours (0=UTC, 1=MEZ)
// return value: returns true during Daylight Saving Time, false otherwise
{
  if (month<3 || month>10) return false; // keine Sommerzeit in Jan, Feb, Nov, Dez
  if (month>3 && month<10) return true; // Sommerzeit in Apr, Mai, Jun, Jul, Aug, Sep
  if (month==3 && (hour + 24 * day)>=(1 + tzHours + 24*(31 - (5 * year /4 + 4) % 7)) || month==10 && (hour + 24 * day)<(1 + tzHours + 24*(31 - (5 * year /4 + 1) % 7)))
    return true;
  else
    return false;
}


unsigned long getNtpTime()
{
  if (udpwait) {
        sendNTPpacket(timeServer); // send an NTP packet to a time server
        udpwait=false;
        trying=0;
       // Serial.println("udp start");
        startMillis = millis();
             }


  if( millis() - startMillis > 1000)  // wait one second for the response without any delays
  {
      startMillis = millis();
     // Serial.println("udp wait");
     trying++;
   if (trying > 5) udpwait=true;  // no response up to 6 second - try again
     int datain = udp.parsePacket();
    if ( datain )
    {
      //  Serial.println("Data incomming");
    
      udp.read(packetBuffer,NTP_PACKET_SIZE);  // read the packet into the buffer

      //the timestamp starts at byte 40 of the received packet and is four bytes,
      // or two words, long. First, esxtract the two words:
      unsigned long highWord = word(packetBuffer[40], packetBuffer[41]);
      unsigned long lowWord = word(packetBuffer[42], packetBuffer[43]);
      // combine the four bytes (two words) into a long integer
      // this is NTP time (seconds since Jan 1 1900):
      unsigned long secsSince1900 = highWord << 16 | lowWord;
      // now convert NTP time into Arduino Time format:
      // Time starts on Jan 1 1970. In seconds, that's 2208988800:
      const unsigned long seventyYears = 2208988800UL;
      // subtract seventy years:
      unsigned long epoch = secsSince1900 - seventyYears+3600; // +3600 too MEZ;
      
                tmElements_t pretime;
        breakTime(epoch,pretime);
        int real_year=pretime.Year+1970;

        if (summertime_EU(real_year,pretime.Month,pretime.Day,pretime.Hour,1)) epoch+=3600;
            
           udpwait=true;
      return epoch;
    }
  }
  return 0;   // return 0 if unable to get the time
}

// send an NTP request to the time server at the given address
unsigned long sendNTPpacket(byte *address)
{
  // set all bytes in the buffer to 0
  memset(packetBuffer, 0, NTP_PACKET_SIZE);
  // Initialize values needed to form NTP request
  // (see URL above for details on the packets)
  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;

  // all NTP fields have been given values, now
  // you can send a packet requesting a timestamp:
 
  udp.beginPacket(address,123);
  udp.write( packetBuffer,NTP_PACKET_SIZE);
  udp.endPacket();
  udp.flush();
}

Kritik ausdrücklich erwünscht, wenn ich einen Fehler drin habe oder was zu umständlich gemacht habe.
Und es wird wohl was zu finden sein, bin ja auch noch quasi Anfänger.

Laufen tut der Code jedenfalls, ist nun Teil von meiner Wetterstation mit Webinterface.
Dort habe ich den DCF Empfänger entfernt, macht auch keinen Sinn wenn der Arduino eh im Internet hängt

Hallo,

ich habe diesen "alten" Beitrag ausgegraben weil er gut als Grundlage dient für das was ich brauche.

Ich möchte die aktuelle Uhrzeit (UTC) im Unix-Time Format bei einem NTP-Server abfragen und in einer Variablen "unsigned long ntpUnixTime" haben.
Das Ganze aber ohne Verwendung der Time-Library.

Hab leider keine Idee wie und wo ich den Code von "rudirabbit" ändern muß.

Kann mir bitte jemand eine Einstiegshilfe geben oder einen Link mit einem Beispiel dessen was ich suche?

Viele Dank
Peter

rudirabbit:
Hallo,
Der Time NTP Beispielcode funktioniert nicht, braucht die UdpBytewise.h und insgesamt scheint der Code mit den lib's nicht mehr zusammenzupassen.

Auch wird mit einem Delay(1000) auf eine mögliche Antwort gewartet, wenn man ein größeres Projekt hat und der Atmel noch was anderes zu tun hat geht sowas gar nicht.

Ich habe nun den Code etwas abgändert braucht keine UdpBytewise und läuft mit den aktuellen lib's
Mein oberste Prioität war: Wenn der Timeserver aus welche Gründen auch immer nicht erreichbar ist, darf die Funktion den Atmel nicht blockieren.

Hier mein pseudo Code incl. Sommer/Winterzeitumstellung (den Code habe ich von jurs "geklaut") :slight_smile:

#include <Ethernet.h>

#include <EthernetUdp.h>
#include <Time.h>

byte timeServer[]  = { 64,90,182,55 };   // time.nist.gov

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

EthernetUDP udp;

unsigned int localPort = 8888;
unsigned long startMillis;
boolean udpwait=true;
byte trying=0;

void setup() {
Ethernet.begin(mac,ip,dnsServer,gateway,subnet);

setSyncProvider(getNtpTime);
setSyncInterval(3600);

}

/-------- utility code from jurs ----------/
boolean summertime_EU(int year, byte month, byte day, byte hour, byte tzHours)
// European Daylight Savings Time calculation by "jurs" for German Arduino Forum
// input parameters: "normal time" for year, month, day, hour and tzHours (0=UTC, 1=MEZ)
// return value: returns true during Daylight Saving Time, false otherwise
{
 if (month<3 || month>10) return false; // keine Sommerzeit in Jan, Feb, Nov, Dez
 if (month>3 && month<10) return true; // Sommerzeit in Apr, Mai, Jun, Jul, Aug, Sep
 if (month==3 && (hour + 24 * day)>=(1 + tzHours + 24*(31 - (5 * year /4 + 4) % 7)) || month==10 && (hour + 24 * day)<(1 + tzHours + 24*(31 - (5 * year /4 + 1) % 7)))
   return true;
 else
   return false;
}

unsigned long getNtpTime()
{
 if (udpwait) {
       sendNTPpacket(timeServer); // send an NTP packet to a time server
       udpwait=false;
       trying=0;
      // Serial.println("udp start");
       startMillis = millis();
            }

if( millis() - startMillis > 1000)  // wait one second for the response without any delays
 {
     startMillis = millis();
    // Serial.println("udp wait");
    trying++;
  if (trying > 5) udpwait=true;  // no response up to 6 second - try again
    int datain = udp.parsePacket();
   if ( datain )
   {
     //  Serial.println("Data incomming");
   
     udp.read(packetBuffer,NTP_PACKET_SIZE);  // read the packet into the buffer

//the timestamp starts at byte 40 of the received packet and is four bytes,
     // or two words, long. First, esxtract the two words:
     unsigned long highWord = word(packetBuffer[40], packetBuffer[41]);
     unsigned long lowWord = word(packetBuffer[42], packetBuffer[43]);
     // combine the four bytes (two words) into a long integer
     // this is NTP time (seconds since Jan 1 1900):
     unsigned long secsSince1900 = highWord << 16 | lowWord;
     // now convert NTP time into Arduino Time format:
     // Time starts on Jan 1 1970. In seconds, that's 2208988800:
     const unsigned long seventyYears = 2208988800UL;
     // subtract seventy years:
     unsigned long epoch = secsSince1900 - seventyYears+3600; // +3600 too MEZ;
     
               tmElements_t pretime;
       breakTime(epoch,pretime);
       int real_year=pretime.Year+1970;

if (summertime_EU(real_year,pretime.Month,pretime.Day,pretime.Hour,1)) epoch+=3600;
           
          udpwait=true;
     return epoch;
   }
 }
 return 0;   // return 0 if unable to get the time
}

// send an NTP request to the time server at the given address
unsigned long sendNTPpacket(byte *address)
{
 // set all bytes in the buffer to 0
 memset(packetBuffer, 0, NTP_PACKET_SIZE);
 // Initialize values needed to form NTP request
 // (see URL above for details on the packets)
 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;

// all NTP fields have been given values, now
 // you can send a packet requesting a timestamp:

udp.beginPacket(address,123);
 udp.write( packetBuffer,NTP_PACKET_SIZE);
 udp.endPacket();
 udp.flush();
}




Kritik ausdrücklich erwünscht, wenn ich einen Fehler drin habe oder was zu umständlich gemacht habe.
Und es wird wohl was zu finden sein, bin ja auch noch quasi Anfänger.

Laufen tut der Code jedenfalls, ist nun Teil von meiner Wetterstation mit Webinterface.
Dort habe ich den DCF Empfänger entfernt, macht auch keinen Sinn wenn der Arduino eh im Internet hängt

Wenn du Time nicht verwenden willst, must du getNtpTime() selbst anstossen.
Diese Funktion gibt die Uhrzeit in unsigned long zurück.

Es sind noch einige Dinge im Code drin die du nicht brauchst, weil für mein Projekt gedacht.
Außerdem nutze ich noch einige Funktionen die in der Time stecken z.b breakTime das müsste man von dort übernehmen.

Wenn Jurs keinen spontanen Code parat hat, schaue ich morgen nach.

Warum willst du die Time die nicht nutzen ?

peter_de:
Ich möchte die aktuelle Uhrzeit (UTC) im Unix-Time Format bei einem NTP-Server abfragen und in einer Variablen "unsigned long ntpUnixTime" haben.
Das Ganze aber ohne Verwendung der Time-Library.

Hab leider keine Idee wie und wo ich den Code von "rudirabbit" ändern muß.

Wo ist dabei das Problem?
Du rufst die Zeit ab mit:

unsigned long ntpUnixTime=getNtpTime();

Und wenn Du nicht UTC+1 sondern UTC von der Funktion geliefert bekommen möchtest, dann änderst Du innerhalb der Funktion die Zeile:

unsigned long epoch = secsSince1900 - seventyYears+3600; // +3600 too MEZ;

so ab, dass Du das "+3600" genau so wegläßt wie die Sommerzeitumstellung.

Dann entfernst Du das include für die Time-Library und alle Bezüge auf Funktionen in dieser Library.

Danke schon mal für die Tipps,

jurs:
Wo ist dabei das Problem?
...

Ich habe bereits umfangreichen, funktionierenden Code auf einem MEGA2560 laufen.
Dieser Code verwendet bereits die Time-Library. Die aktuelle Uhrzeit bekommt diese Time-Library derzeit aus anderer Quelle, nicht per NTP.

Ich will nun parallel dazu eine Zeit per NTP-Zeitserver empfangen und erst mal Erfahrungen damit sammeln bevor ich die bereits laufende Time-Library damit beaufschlage. Deshalb hätte ich diese Zeit vom NTP-Zeitserver gerne in einer eigenen Variablen im UNIX-Zeitformat.

Ich habe bisher den Code von @rudirabbit in mein bestehendes Programm eingefügt.
Natürlich alle Teile an die entsprechenden Stellen (hoffe ich).

Ich habe eine globale Variable "unsigned long ntpUnixTime" definiert.

Die Zeilen:

setSyncProvider(getNtpTime);
setSyncInterval(3600);

habe ich auskommentiert.

Über:ntpUnixTime=getNtpTime();
will ich dann die Zeit abrufen. Aber dabei hängt sich mein MEGA mit Ethernet Shield auf. (Ausgabe auf den Serial-Monitor stoppt).

Möglicherweise habe ich beim kopieren und anpassen des Codes von @rudirabbit irgendwas übersehen oder falsch gemacht.

Ich habe beim Start des Ethernet keinen DNS-Server angegeben. Vielleicht ist das schon der Fehler, obwohl ich meine wenn ich die IP des NTP-Servers im Klartext (64.90.182.55) angebe keine Namensauflösung brauche?

Viel Grüße
Peter

Bist du sicher das 64.90.182.55 online ist ?

Pingen kann ich diese Adresse nicht erfolgreich, ist natürlich nicht immer ein zuverlässiger Test.

Oder definiere den DNS und versuche es über z.b. pool.ntp.org hat zur Zeit 31.25.153.77 inne.

Hallo,

ich habe jetzt auch den DNS-Server eingetragen. - kein Erfolg

Dann habe ich die Adresse des NTP-Servers auf die Adresse abgeändert die ich per ping von pool.ntp.org erhalten habe. - kein Erfolg.

Habe mir dann ein paar Ausgaben zum Serial-Monitor in den Code geschrieben.

Demnach wird die Zeile "udp.write( packetBuffer,NTP_PACKET_SIZE);" noch ausgeführt.
Die danach folgende Zeile "udp.endPacket();" schickt dann meinen MEGA ins Nirvana.

Ich suche selbst mal noch weiter nach Gründen für dieses verhalten, wäre aber auch froh über den einen oder anderen Tipp zur Fehlersuche.

Gruß
Peter

Kleines Update.

ich habe im setup() eine Zeile eingefügt. udp.begin(localPort);
dann habe ich als NTP-Server die IP von "ptbtime1.ptb.de" eingetragen.

Und jetzt läuft es - nix mehr Absturz.

Antwort: 1416437514 ergibt umgerechnet 19.11.2014 - 23:51:54

Wenn ich jetzt aber nicht "byte timeServer[] = { 192, 53, 103, 108};" für "ptbtime1.ptb.de" schreiben will sondern "ptbtime1.ptb.de" über den DNS-Server auflösen will, wie muss ich das dann schreiben?

Gruß
Peter

Hallo mal nä frage welche bits muss man auslesen um die bruchteile der sekunde zu erhalten?

McCurtis:
Hallo mal nä frage welche bits muss man auslesen um die bruchteile der sekunde zu erhalten?

Die Antwort kannst Du dort nachlesen, wo Du quasi dieselbe Frage doppelt gestellt und bereits Antwort erhalten hast:
http://forum.arduino.cc/index.php?topic=350873.0

How to use this forum: http://forum.arduino.cc/index.php/topic,148850.0.html

13. Forum etiquette
Don't cross-post!