NTP Abruf ESP8266 Core 2.7.4

Hallo,

ich benötige Eure Unterstützung beim NTP Zeit Abruf auf einem ESP8266 (NodeMCU) mit dem Core 2.7.4., da ich meinen Sketch meiner WordClock aufräume / neu schreibe.

Die Uhrzeit wird in UTC auf einer RTC gespeichert, welche ich in die Systemzeit mit Hilfe der Funktion settimeofday() lade. Dies klappt soweit auch super, allerdings ruft die Funktion immer - egal ob sie vom User oder vom SNTP Intervall aufgerufen wurde Ihre definierte Callback Funktion auf.

Das hat zur Folge, dass wenn ich die Uhrzeit aus der RTC lese und in die Systemzeit schreibe die Callback Funktion (time_is_set) die Systemzeit auch wieder in die RTC schreiben würde - was absolut keinen Sinn macht.

Habt ihr eine Idee wie ich prüfen kann ob die Funktion settimeofday() zum Stellen der Systemzeit mit Hilfe der RTC oder zum Abruf der NTP Zeit gestartet wurde? Die einzige Idee die ich aktuell habe wäre eine globale Variable die ich entsprechend vor dem Stellen der Systemzeit veränder und in der Funktion time_is_set() prüfe.

Ab dem ESP Core =>3.0.0 ist dieses Feature bereits vorhanden, dort kann man in der Callback Funktion über die bool from_sntp abfragen, ob der Abruf vom Intervall kam. Diesen Core kann ich aktuell aber nicht nutzen, da es hier massive Probleme mit der FastLED Lib gibt.

Vielen Dank schonmal für Eure Hilfe :slight_smile: !

Lieben Gruß,
Björn

// ESP8266 Core 2.7.4

#include <ESP8266WiFi.h>
#include <time.h>    // time() ctime()
#include <sys/time.h>   // struct timeval
#include <coredecls.h>    // settimeofday_cb()

// WLAN Konfiguration
#define SSID            ""
#define SSIDPWD         ""

// Zeitzonen Konfiguration
// https://www.gnu.org/software/libc/manual/html_node/TZ-Variable.html
// https://github.com/nayarsystems/posix_tz_db/blob/master/zones.csv
const char *TZstr = "CET-1CEST,M3.5.0,M10.5.0/3";

time_t tnow; // Beinhaltet die Epochzeit

// Nach 10 Sekunden erfolgt die erste NTP Abfrage
uint32_t sntp_startup_delay_MS_rfc_not_less_than_60000 () {
  return 10000;
}

void time_is_set (void) {
  time_t t = time (nullptr);
  Serial.println("--- Callback -> settimeofday() wurde aufgerufen --- ");
  /*
    An dieser Stelle wird die RTC mit Hilfe der Variable t gestellt.
    Die RTC speichert die aktuelle Uhrzeit in UTC
  */
}

void setup() {
  Serial.begin (115200);
  Serial.print("\n\n");

  // Callback Funktion definieren, die aufgerufen wird, wenn
  // settimeofday() erfolgreich ausgeführt wurde.
  settimeofday_cb(time_is_set);

  // Uhrzeit aus der RTC auslesen und in die Systemzeit schreiben
  time_t rtc_time_t = 1635552000; // 30.10.21 00:00:00 Uhr
  timeval tv = { rtc_time_t, 0 };
  settimeofday (&tv, NULL);
  Serial.println("-> RTC Epoch Zeit wurde in die Systemzeit geladen \n");
  yield(); // Zeit schaffen um direkt die Callback Funktion auszuführen

  // Zeitzone und NTP Server definieren
  configTime (TZstr, "pool.ntp.org");

  WiFi.mode (WIFI_STA);
  WiFi.begin (SSID, SSIDPWD);
}

void loop() {
  tnow = time (nullptr); // Aktuelle Epoch Zeit UTC aus der Systemzeit auslesen

  struct tm tm;
  printf ("Lokale Uhrzeit: %s", asctime (localtime_r (&tnow, &tm)));
  printf ("UTC / GMT: %s \n", asctime (gmtime_r (&tnow, &tm)));

  delay(1000); // Dirty Delay - Nur für den Test...
}

Sieh dir doch mal die Beispiele auf der Seite von fips an.
Da kannst du gut vergleichen und evtl. den Fehler erkennen.

Hallo HotSystems,

vielen Dank für deine Antwort :slight_smile: . Auf der Seite von fips kann ich direkt keine Lösung für mein Problem finden. Er arbeitet bei seinen Beispielen ohne eine RTC. Wenn ich mein Sketch ohne die RTC nutze klappt es auch wunderbar mit der Callback Funktion - sie wird ja nur beim NTP Abruf aufgerufen.

Ich habe gerade das NTP Beispiel aus dem Core 3.0.2 nochmal angeschaut und die betreffenden Stellen rauskopiert, die zeigen was ich gerne auch auf dem Core 2.7.4 machen würde.

Ab dem Core 3.0.0 ist es möglich zu prüfen woher der Aufruf der Callback Funktion kam:

void time_is_set(bool from_sntp /* <= this parameter is optional */) {
  // any function is allowed in this callback

  if (from_sntp) {
    Serial.print("SNTP");
  } else {
    Serial.print("USER");
  }

}

Meine Idee war es jetzt diese Prüfung mit einer globalen Variable umzusetzen auf dem Core 2.7.4. Ich habe den Sketch von oben mal mit meiner Idee angepasst:

// ESP8266 Core 2.7.4

#include <ESP8266WiFi.h>
#include <time.h>    // time() ctime()
#include <sys/time.h>   // struct timeval
#include <coredecls.h>    // settimeofday_cb()

// WLAN Konfiguration
#define SSID            ""
#define SSIDPWD         ""

// Zeitzonen Konfiguration
const char *TZstr = "CET-1CEST,M3.5.0,M10.5.0/3";

time_t tnow; // Beinhaltet die Epochzeit

bool from_sntp = true; // <=== NEU

// Nach 10 Sekunden erfolgt die erste NTP Abfrage
uint32_t sntp_startup_delay_MS_rfc_not_less_than_60000 () {
  return 10000;
}

void time_is_set (void) {
  time_t t = time (nullptr);
  Serial.println("--- Callback -> settimeofday() wurde aufgerufen --- ");

  if (from_sntp) { // <== NEU Abfrage woher der Aufruf kam
    /*
      An dieser Stelle wird die RTC mit Hilfe der Variable t gestellt.
    */
  }

  from_sntp = true;
}

void syncSystemzeit(void) {

/*
  Auf false setzen damit die Callback Funktion nicht nochmal
  die gerade abgerufene Zeit aus der RTC erneut in diese schreibt
*/
  from_sntp = false;

  time_t rtc_time_t = 1635552000; // 30.10.21 00:00:00 Uhr
  timeval tv = { rtc_time_t, 0 };
  settimeofday (&tv, NULL);
  Serial.println("-> RTC Epoch Zeit wurde in die Systemzeit geladen \n");
  yield(); // Zeit schaffen um direkt die Callback Funktion auszuführen
}

void setup() {
  Serial.begin (115200);
  Serial.print("\n\n");

  // Callback Funktion definieren, die aufgerufen wird, wenn
  // settimeofday() erfolgreich ausgeführt wurde.
  settimeofday_cb(time_is_set);

  // Uhrzeit aus der RTC auslesen und in die Systemzeit schreiben
  syncSystemzeit();

  // Zeitzone und NTP Server definieren
  configTime (TZstr, "pool.ntp.org");

  WiFi.mode (WIFI_STA);
  WiFi.begin (SSID, SSIDPWD);
}

void loop() {
}

Aber ist die Variante mit der globalen Variable so glücklich?

Lieben Gruß,
Björn

ich verstehe noch nicht ganz was du mit der RTC willst.
Nur wegen den ersten 10 Sekunden bis zur ersten NTP Abfrage?

Hallo noiasca,

nein nicht wegen den erstem 10 Sekunden, die kann ich nach belieben anpassen. Ich hab die RTC mit eingebunden damit ich stets eine aktuelle Zeit vorhalten kann auch wenn der NTP Server nicht erreichbar ist oder ich mal einen Internetausfall habe (oder ganz ohne Internet betreiben möchte).

Deshalb synchronisiere ich immer drei Zeiten. Alle 24 Stunden rufe ich die Zeit beim NTP Server ab und schreibe diese in die RTC und alle 3 Stunden rufe ich die Zeit von der RTC ab und schreibe diese in die Systemzeit.

Und da ich möchte, dass die RTC automatisch beim Abruf der NTP Zeit, durch das Intervall, aktualisiert wird nutze ich die Callbackfunktion time_is_set. Deshalb muss ich unterscheiden können warum die Callbackfunktion aufgerufen wurde - ob dies durch das Intervall oder zum Systemzeit stellen über die RTC erfolgt ist.

Danke für deine Antwort!

Ich kann immer noch nicht nachvollziehen was du vor hast.
Die aktuelle Zeit hast du zwischen den NTP Intervallen mit der Genauigkeit des ESP in now.

mit time holst dir die aktuelle epoche,
mit localtime_r machst dir ein lesbares tm struct

 time(&now);                       // read the current time
  localtime_r(&now, &tm);           // update the structure tm with the current time

Edit:

Um es mit drei Sätzen auf den Punkt zubringen was ich möchte:

Die Funktion settimeofday_cb() setzt einen Callback, der jedes Mal ausgeführt wird, wenn settimeofday() aufgerufen wird. Die SNTP-Synchronisierung ändert die Systemzeit mit settimeofday () - was in Ordnung ist, aber wenn ich settimeofday () manuell ausführe, wird auch die Callback-Funktion aufgerufen. Es gibt keine Möglichkeit, um anzuzeigen, was die Quelle der Callback-Funktion war – Netzwerksynchronisierung oder manuelle Einstellung.


Ich versuch es nochmal anders zu erklären was ich vorhabe.

Mit dem Aufruf von

settimeofday_cb(time_is_set);

definiere ich die Callback-Funktion time_is_set(), die immer aufgerufen wird wenn die NTP Zeit automatisch - bedingt durch das Intervall - erfolgreich abgerufen wird. Somit kann ich kontrollieren, ob der Abruf geklappt hat und gleichzeitig die frisch abgerufene Zeit in die RTC schreiben.

Jetzt lese ich mir aber zwischen den Intervallen, wo die NTP Zeit abgerufen wird, aus der RTC die Epoch Zeit und aktualisiere damit die Systemzeit. Dies mache ich so:

  // Epoch Zeit aus der RTC in Systemzeit einlesen
  time_t rtc_time_t = 1635552000; 
  timeval tv = { rtc_time_t, 0 };
  settimeofday (&tv, NULL); // Epoch Zeit der RTC in die Systemzeit schreiben

Das klappt bis dahin auch alles wunderbar. Allerdings wird hier die Callback-Funktion auch aufgerufen, wenn ich die Zeit manuell aus der RTC in die Systemzeit einlesen. Und das hat zur folge, dass die gerade abgerufene Zeit aus der RTC durch die Callback-Funktion ( time_is_set() ) direkt auch weider in die RTC geschrieben wird.

Deshalb brauche ich eine Unterscheidung, ob Callback-Funktion aufgerufen wird weil das Intervall abgelaufen ist oder weil ich manuell per RTC die Systemzeit aktualisiere.

Da ging mein Gedanke an eine globale Variable die ich vor dem setzen der RTC Zeit in die Systemzeit anpasse, damit die Callback-Funktion mir nicht direkt wieder die vermeidlich gleiche Zeit in die RTC schreibt:

bool from_sntp = true;

void time_is_set (void) {
  time_t t = time (nullptr);
  Serial.println("--- Callback -> settimeofday() wurde aufgerufen --- ");

  if (from_sntp) { // Wenn false dann nicht die RTC beschreiben
    /*
      An dieser Stelle wird die RTC mit Hilfe der Variable t gestellt.
    */
  }

  from_sntp = true;
}

Nochmal vielen Dank für eure Antworten :slight_smile:

was du willst ist schon klar. Meine Fragen gingen in die Richtung warum.

Aber wenn du weiterhin auf deinem Weg bleiben willst:
Was hindert dich daran die Callback Signatur in der Lib zu ändern damit du das hast wie auch in 3.0.0?

Hey noiasca,

sorry dann habe ich dich falsch verstanden. Warum ich das machen möchte ist, dass ich die WordClock auch ohne Netzwerkeinbindung nutzen kann und die Zeit dann nur von der RTC geholt wird. Die Systemzeit aktualisiere ich deshalb öfter über die RTC, da während die FastLED Lib die LEDs beschreibt die Interrupts abschaltet sind und die Systemzeit dann nach einer Weile nicht mehr richtig läuft.

Wenn ich die Lib entsprechend ändere, würde ich dann nicht meine eigene ESP Core Version schaffen? Ich müsste die coredecls.h, time.cpp sowie Änderung an lwip2 vornehmen. Damit wäre der Sketch nicht mehr so einfach weiter zugeben. Deshalb dachte ich an eine Lösung z.B. mit einer globalen Variable.

Vielen Dank für deine Antwort.