Go Down

Topic: TimeNTP ESP8266 (Read 838 times) previous topic - next topic

LOOIIS

Jun 13, 2018, 08:57 pm Last Edit: Jun 13, 2018, 09:04 pm by LOOIIS
Hallo Experten, :)
habe mir im Programm Arduino IDE unter Bibliotheksverwalter die Bibliothek  "Time by Michael Margolis Version 1.5.0" heruntergeladen.
Möchte nämlich mit meinem ESP8266 die Internetzeit aus einem NTP-Server auslesen.
Das Programm "TimeNTP_ESP8266" funktioniert leider nicht :( : Der NTP antwortet nicht. Habs auch mit einer anderen IP-Adresse versucht. (129, 6, 15, 28 - time.nist.gov NTP server)
Die Verbindung zu meinem AP funktioniert (habe es mit einem anderen Sketch getestet).
Hat jemand von euch den passenden Tipp?
Wie wird der "local port" für UDP Pakete bestimmt?

Code: [Select]
/*
 * Time_NTP.pde
 * Example showing time sync to NTP time source
 *
 * This sketch uses the ESP8266WiFi library
 */
 
#include <TimeLib.h>
#include <ESP8266WiFi.h>
#include <WiFiUdp.h>

const char ssid[] = "abc";  //  your network SSID (name)
const char pass[] = "123";       // your network password

// NTP Servers:
IPAddress timeServer(132, 163, 4, 101); // time-a.timefreq.bldrdoc.gov
// IPAddress timeServer(132, 163, 4, 102); // time-b.timefreq.bldrdoc.gov
// IPAddress timeServer(132, 163, 4, 103); // time-c.timefreq.bldrdoc.gov


const int timeZone = 1;     // Central European Time
//const int timeZone = -5;  // Eastern Standard Time (USA)
//const int timeZone = -4;  // Eastern Daylight Time (USA)
//const int timeZone = -8;  // Pacific Standard Time (USA)
//const int timeZone = -7;  // Pacific Daylight Time (USA)


WiFiUDP Udp;
unsigned int localPort = 8888;  // local port to listen for UDP packets

void setup()
{
  Serial.begin(9600);
  while (!Serial) ; // Needed for Leonardo only
  delay(250);
  Serial.println("TimeNTP Example");
  Serial.print("Connecting to ");
  Serial.println(ssid);
  WiFi.begin(ssid, pass);
 
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }

 
  Serial.print("IP number assigned by DHCP is ");
  Serial.println(WiFi.localIP());
  Serial.println("Starting UDP");
  Udp.begin(localPort);
  Serial.print("Local port: ");
  Serial.println(Udp.localPort());
  Serial.println("waiting for sync");
  setSyncProvider(getNtpTime);
}

time_t prevDisplay = 0; // when the digital clock was displayed

void loop()

  if (timeStatus() != timeNotSet) {
    if (now() != prevDisplay) { //update the display only if time has changed
      prevDisplay = now();
      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 for digital clock display: prints preceding colon and leading 0
  Serial.print(":");
  if(digits < 10)
    Serial.print('0');
  Serial.print(digits);
}

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

const int NTP_PACKET_SIZE = 48; // NTP time is in the first 48 bytes of message
byte packetBuffer[NTP_PACKET_SIZE]; //buffer to hold incoming & outgoing packets

time_t getNtpTime()
{
  while (Udp.parsePacket() > 0) ; // discard any previously received packets
  Serial.println("Transmit NTP Request");
  sendNTPpacket(timeServer);
  uint32_t beginWait = millis();
  while (millis() - beginWait < 1500) {
    int size = Udp.parsePacket();
    if (size >= NTP_PACKET_SIZE) {
      Serial.println("Receive NTP Response");
      Udp.read(packetBuffer, NTP_PACKET_SIZE);  // read packet into the buffer
      unsigned long secsSince1900;
      // convert four bytes starting at location 40 to a long integer
      secsSince1900 =  (unsigned long)packetBuffer[40] << 24;
      secsSince1900 |= (unsigned long)packetBuffer[41] << 16;
      secsSince1900 |= (unsigned long)packetBuffer[42] << 8;
      secsSince1900 |= (unsigned long)packetBuffer[43];
      return secsSince1900 - 2208988800UL + timeZone * SECS_PER_HOUR;
    }
  }
  Serial.println("No NTP Response :-(");
  return 0; // return 0 if unable to get the time
}

// send an NTP request to the time server at the given address
void sendNTPpacket(IPAddress &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); //NTP requests are to port 123
  Udp.write(packetBuffer, NTP_PACKET_SIZE);
  Udp.endPacket();
}

amithlon

#1
Jun 13, 2018, 09:14 pm Last Edit: Jun 13, 2018, 09:15 pm by amithlon
Hallo,

als Hinweis weil es offenbar wenig bekannt ist:
Im ESP8266 SDK ist ein Port der Posix time.h enthalten.
Als Beispiel für die Nutzung mal dieser Link:
https://arduino.stackexchange.com/questions/42922/get-hour-with-ctime-time-library-with-esp8266
Infos sind etwas spärlich, die aus der ArduinoIDE unterstützten Funktionen und Strukturen findet man hier:
https://github.com/esp8266/Arduino/blob/master/tools/sdk/libc/xtensa-lx106-elf/include/time.h
Vorteile: kaum Programmieraufwand, nur einbinden und mit

configTime(timezone * 3600, dst * 0, "pool.ntp.org", "time.nist.gov");

im Setup() initalisieren. Standardeinstellung ist 1x pro Stunde aktualisieren, ansonsten läuft eine Softwareuhr.
Außerdem wird die Laufzeitinfo der Timeserver berücksichtigt, die Uhr wird also sehr genau gesetzt.

Gruß aus Berlin
Michael

noiasca

#2
Jun 13, 2018, 09:16 pm Last Edit: Jun 13, 2018, 09:17 pm by noiasca
hier hab ich mal damit gekämpft:
https://forum.arduino.cc/index.php?topic=525378.0

im Beitrag #16 gibts einen Link auf einen funktionierenden Sketch.


edit: die Sache von amithlon muss ich mir mal ansehen...
DE: Wie man Fragen postet:
1. was hat man (Sketch und Hardware)
2. was SOLL es machen
3. was macht es: IST (Fehlverhalten, Fehlermeldungen, Serial.Output ...)
4. Eine Frage stellen bzw. erklären was man erwartet

Rentner

Hallo,

für den Fall das Du eine Fritzbox hast , die hat einen eigenen  NTP Server eingebaut.

Aber die Lösung von Michael ist sicher noch einfacher ob die mit einer Fritzbox geht müsste man mal ausprobieren.

Heinz

Derfips

Hallo,

als Hinweis weil es offenbar wenig bekannt ist:
Im ESP8266 SDK ist ein Port der Posix time.h enthalten.

Und seit ein paar Wochen wird es in den Esp WiFi Beispielen verwendet.

https://github.com/esp8266/Arduino/blob/master/libraries/ESP8266WiFi/examples/BearSSL_Validation/BearSSL_Validation.ino

Ich habe aber auch noch nicht damit gearbeitet!

Gruß Fips
Meine Esp8266 & ESP32 Projekte => https://fipsok.de

amithlon

Hallo,

Und seit ein paar Wochen wird es in den Esp WiFi Beispielen verwendet.
Ich habe aber auch noch nicht damit gearbeitet!
der Witz ist, daß die Extension mit der Lib schon seit rund 2 Jahren beim Arduino drin ist, ich bin nur durch einen Zufall im Espressif-Forum drüber gestolpert.
Ich nutze es hier inzwischen auf allen ESP die NTP-Zeit holen und habe seitdem keinerlei Probleme mehr, die Uhren gehen einfach immer richtig. ;-)

Es geht auch problemlos mit dem Fritzbox-NTP, eben fritz.box oder die IP eintragen, nutzt mein Bekannter so.

Gruß aus Berlin
Michael

noiasca

@amithlon... super Tipp!

nur das
configTime(timezone * 3600, dst * 0, "pool.ntp.org", "time.nist.gov");

muss meines Erachtens
Code: [Select]

configTime(timezone * 3600, dst, "pool.ntp.org", "time.nist.gov");

sein, denn sonst können die Sekunden in dst ja keinen Einfluss auf die Zeit nehmen.

DE: Wie man Fragen postet:
1. was hat man (Sketch und Hardware)
2. was SOLL es machen
3. was macht es: IST (Fehlverhalten, Fehlermeldungen, Serial.Output ...)
4. Eine Frage stellen bzw. erklären was man erwartet

hannoverarduino

Hi noiasca,
irgendwie bin ich anderer Meinung als Du. Wie wäre es mit

Code: [Select]

configTime(timezone * 3600, dst * 3600, "pool.ntp.org", "time.nist.gov");


dst = 1 * 3600 -> + 1h
dst = 0 * 3600 -> + 0h

...oder bin ich jetzt auf einem völlig falschen Dampfer? Ich teste es auf jeden gleich mal.

Gruß Olaf

hannoverarduino

Hallo nochmals,

es machts wie erwartet, muss aber trotzdem nicht richtig sein. Mir hat sich zur Sekunde noch nicht erschlossen, ob und wann die zeitabhängige Berücksichtigung der Sommerzeit überhaupt stattfindet. Ich vermute in der time.h Library anhand der Zeitzone. Mit der habe ich mich aber noch genauso wenig beschäftigt wie mit dieser genialen ESP Funktion mit wenigen Zeilen Code die Zeit per NTP verfügbar zu haben.
Mein Kurztest basiert auf den hier bereits von amithlon verlinkten Sketch https://arduino.stackexchange.com/questions/42922/get-hour-with-ctime-time-library-with-esp8266

Gruß
Olaf

noiasca

so kann man es auch machen klar. Mir gings nur darum dass *0 ziemlich sinnfrei ist.

imho zählt er den Sekundenoffset nur dazu/weg. Echtes DST muss man selber dazugeben.
DE: Wie man Fragen postet:
1. was hat man (Sketch und Hardware)
2. was SOLL es machen
3. was macht es: IST (Fehlverhalten, Fehlermeldungen, Serial.Output ...)
4. Eine Frage stellen bzw. erklären was man erwartet

hannoverarduino

Hallo,

die Variable dst aus dem obigen Beispiel hat im Sinne der Sommerzeitumstellung vermutlich überhaupt keine sinnvolle Auswirkung auf die korrekte Berechnung der Sommer- oder Winterzeit.
Es gibt offensichtlich ein Flag im C++ mit Namen tm_isdst (is day light saving time), dieses Flag bleibt hierkonstant auf 0, egal ob dst 0 oder 1 ist.

Auszug aus meinem bereinigten Sketch anbei. Vielleicht hat hier jemand eine Idee wie ich ohne großen Aufwand die Sommer- Winterzeitumstellung über die vorhandenen Libraries hier elegant erledigen kann.


Code: [Select]


#include <ESP8266WiFi.h>
#include <time.h>

const char* ssid                  =  "";   // Your network SSID (name)
const char* password              =  "";// Your network password
int timezone                      =  1;            // GMT + 1
int dst                           =  1;            // Summer time, true

void setup() {
.
.
int timezone                   =  1;            // GMT + 1
int dst                           =  1;            // Summer time, true
.
.
}

void loop() {
  time_t now = time(nullptr);
  Serial.print("ctime ");
  Serial.println(ctime(&now));

  /***  Structure (http://www.cplusplus.com/reference/ctime/gmtime/)  ***/
  // time_t now;
  struct tm * timeinfo;
  time(&now);
  timeinfo = gmtime(&now);                        
  Serial.print("gmtime ");
  Serial.print(timeinfo->tm_hour);
  Serial.print(":");
  Serial.print(timeinfo->tm_min);
  Serial.print(":");
  Serial.print(timeinfo->tm_sec);

  Serial.print("  ");
  Serial.print(timeinfo->tm_mday);
  Serial.print(".");
  Serial.print(timeinfo->tm_mon + 1);
  Serial.print(".");
  Serial.println(timeinfo->tm_year+1900);

  Serial.print("Day of week: ");                   // Sunday = 0
  Serial.print(timeinfo->tm_wday);

  
  Serial.print(" DST: ");
  Serial.println(timeinfo->tm_isdst);    // Day light saving
  delay(1000);

}                                                  // End of void loop()



Ansonsten muss ich mal meine uralten Unterlagen sichten....
https://electronicfreakblog.wordpress.com/2014/03/06/die-zeit-im-sommer-und-im-winter/

amithlon

Hallo,

ich bin das Sommerzeit-Problem auch noch nicht angegangen, allerdings mehr wegen eines logischen Problems:
Die Lib selbst scheint da nichts anzubieten. Libs, Srpite, Funktionen usw., die aus dem Datum feststellen, ob Sommer- oder Winterzeit ist, gibt es ja wie Sand am Meer.

Also müßte man beim Aufruf von configTime() erstmal die Daten holen, dann testen, ob Sommer/Winter und dann configTime() mit dem passenden Wert nochmal aufrufen?

Gruß aus Berlin
Michael

Tommy56

Man kann auch intern alles in UTC bearbeiten (und vor allem abspeichern wenn gebraucht) und nur für die Anzeige auf die lokale Sommer-/Winterzeit umrechnen. z.B. mit der Funktion von Jurs.

Das hat z.B. beim Speichern in einer Datenbank den Vortil, dass es keine doppelten Zeitstempel bzw. Löcher an den Umstellungstagen gibt.

Gruß Tommy
"Wer den schnellen Erfolg sucht, sollte nicht programmieren, sondern Holz hacken." (Quelle unbekannt)

hannoverarduino

Hallo nochmals,
alle meine gestrigen Recherchen und Untersuchungen bestätigen, dass die Variable dst in der configTime() Funktion nur ein Dummy ist.

Daher macht im Nachinein die Multiplikation von dst mit 0 vielleicht doch Sinn, obwohl man das vermutlich auch anders hätte lösen können. Könnte man aber zumindest als Hinweis interpretieren: "Hey Leute, da tut sich noch nichts"
Code: [Select]
configTime(tzHours * 3600, [b]dst * 0[/b], "0.europe.pool.ntp.org", "pool.ntp.org", "time.nist.gov");

Der Weg den ich jetzt gegangen bin unterscheideet sich nicht großartig von obigem Beispiel. Im Setup() erst einmal den Zeitstempel holen. Dann in der Loop() mit Hilfe der Funktion von Jurs feststellen ob das dst Flag gesetzt ist oder nicht und nachfolgend entscheiden welcher Wert hinzuaddiert wird.
Der Code Snippet wie folgt:
Code: [Select]
    dst = summertime_EU(year, month, mday, hour, tzHours);
    Serial.print("DST-Flag: ");
    Serial.println(dst);
    if (dst == 1) {
      configTime(tzHours * 3600, [b]dst * 3600[/b], "0.europe.pool.ntp.org", "pool.ntp.org", "time.nist.gov");// fritz.box
    }
    else
      configTime(tzHours * 3600, [b]dst * 3600[/b], "0.europe.pool.ntp.org", "pool.ntp.org", "time.nist.gov");// fritz.box

Dieser Teil ds Codes wird in einer Heartbeat-Routine sekündlich durchlaufen. Nur falls der Author der configTime() irgendwann mal ein Update mit erweitertem Funktionsumfang bereitstellt, füttere ich die configTime() jetzt bereits mit dem dst Flag. Ginge sonst durchaus einfacher.

Die Fritzbox stellt übrigens tatsächlich einen NTP-Server bereit. Funktioniert prinzipiell auch hier mit der time Library. Gibt bei mir allerdings nur eine falsche Uhrzeit heraus. Das habe ich jetzt jedoch noch nicht weiter untersucht, gibt ja genügend andere Optionen.

Vielen Dank allen für die Hinweise!

amithlon

Hallo,

ich würde mir da wohl eher Zeit/Datum aus der passenden Struktur holen und nur zur Stundenwechsel testen und einmalig korrigieren. Ich vermute mal, daß der Aufruf von configTime() alles neu initialisiert und damit auch ein holen der Zeit vom NTP auslöst. Das muß aber nicht unbedingt alle Sekunde passieren.

Gruß aus Berlin
Michael

Go Up