Stabile Offline/Online Uhr

Hi zusammen,

mit dem folgenden Sketch möchte ich erreichen, dass ich dauerhaft (ohne externes RTC-Modul) eine nahezu perfekte Uhrzeit aufrechterhalte, d.h. auch wenn das WLAN mal zwischendurch abgeschaltet wird.

#include <WiFi.h>
#include <ESP32Time.h>
#include <time.h>

ESP32Time rtc; // Realtime-Clock
const char* ntpServer = "pool.ntp.org";
const long  gmtOffset_sec = 3600;
const int   daylightOffset_sec = 3600;

unsigned long nextUpdate = 20000;
unsigned long millisMerker = 0;
unsigned long connTimer = 0;
unsigned long connDauer = 5000;
boolean timeSet = false;
boolean online = false;

void setup() {
  // put your setup code here, to run once:
  Serial.begin(115200);
  // NTP-Uhrzeit initialisierung
  configTime(gmtOffset_sec, daylightOffset_sec, ntpServer);
}

void loop() {
  if (millis() - millisMerker > nextUpdate) { // Alle 20 Sekunden Uhrzeit abgleichen 
    if (WiFi.status() != WL_CONNECTED) {
      Serial.println("Neuer Verbindungsversuch:");
      connTimer = millis();
      WiFi.mode(WIFI_STA);
      WiFi.disconnect();
      WiFi.begin("MeineSSID", "meinPasswort");
      while (WiFi.status() != WL_CONNECTED && (millis() - connTimer < connDauer)) {
        Serial.print(".");
        delay(500);
      }    
    }
    if (WiFi.status() == WL_CONNECTED) {
      timeupdate_online();
      online=true;
    } else {
      online=false;
    }
    millisMerker = millis();  
  }
  if (timeSet) { // Prüfen, ob es überhaupt schon eine Uhrzeit gibt (egal ob On- oder Offline)
    if (online) { // Falls der letzte Online-Uhrzeitabgleich geklappt hat....
      Serial.println("Online:" + rtc.getTime("%A, %B %d %Y %H:%M:%S"));   // (String) returns time with specified format 
    } else  { // Ansonsten kennzeichnen, dass die ESP RTC-Clock gerade "Offline" läuft
      Serial.println("Offline:" + rtc.getTime("%A, %B %d %Y %H:%M:%S"));   // (String) returns time with specified format 
    }
  } else {
    Serial.println("Uhrzeit wurde noch nicht gesetzt!");
  }
  delay(1000); // Delay nur für dieses Beispiel...geht natürlich auch über millis();
}

void timeupdate_online() {
  struct tm timeinfo;
  if(!getLocalTime(&timeinfo)){
      Serial.print("Zeit konnte nicht online abgerufen werden");
    return;
  } 

  uint16_t currentYear = timeinfo.tm_year+1900;
  if ((int)currentYear > 1970) {
    Serial.println("Uhrzeitableich Online erfolgt mit:");
    Serial.println(&timeinfo, "%A, %B %d %Y %H:%M:%S");
    rtc.setTimeStruct(timeinfo); 
    timeSet=true;
    online=true;
  } else {
    Serial.print("Kein Online-Abgleich...");
  }
}

Da sollte doch soweit prinzipiell in Ordnung sein, oder?
Das "Delay" und die Seriellen Ausgaben sind natürlich erstmal nur für mich.
Auch die timeSet / online -booleans sind nur zu Debug-Zwecken drin.

Was ich nicht schnalle und Grund dieses Posts ist:
Ab und an passiert es, dass auf einmal die Uhrzeit nicht mehr stimmt (manchmal sogar um eine Stunde nach geht).

Kann das evtl. an der verwendeten Library "time.h" liegen? Beim googeln bin ich schon desöfteren über eine ander Library (NTPTime.h) gestolpert. Wäre die besser / "Stabiler"?

Und welcher Zeitserver ist der beste?

Danke schon mal für eure Tipps.

LG

Auch in meinem Radiowecker verwende ich im ESP32 die Beispiele von Fips.
Da stört es nicht, wenn zwischendurch mal das WiFi oder Internet ausfällt.
Die Uhrzeit geht dann auch noch genau genug.

Was verstehst Du darunter?
Wieviel darf der Fehler sein?
Wenn die Versorgungsspannung ausfällt, muß nach der Wiederherstellung der Versorgung die Zeit auch ohne Netz richtig sein? oder ist es zulässig keine Zeit bis zum ersten Verbindung mit NTP Server zu haben?

Grüße Uwe

glaube ich nicht, dass die das eigentliche Problem ist.
Was macht die unbekannt

#include <ESP32Time.h>

?
dieses 1h offset kenn ich vom ESP8266 dann, wenn der ESP Core nicht zum Beispiel stimmt / zusammenkopierter Code.
Mein ESP32 Code sollte mit dem angegeben Core funktionieren:
https://werner.rothschopf.net/microcontroller/202103_arduino_esp32_ntp_en.htm

Versuch mal exakt das ESP NTP Beispiel aus der IDE - fällt das auch um?

Das wäre ja das für mich passende Beispiel:

Was ich da nicht ganz begreife:
Fips hat einen Array mit verschiedenen NTPServern

const char* const PROGMEM ntpServer[] = {"fritz.box", "de.pool.ntp.org", "at.pool.ntp.org", "ch.pool.ntp.org", "ptbtime1.ptb.de", "europe.pool.ntp.org"};

In der Funktion "getTime()" ruft er aber nur die "de.pool.ntp.org" auf:

bool getTime() {                                                   // Zeitzone einstellen https://github.com/nayarsystems/posix_tz_db/blob/master/zones.csv
  configTzTime("CET-1CEST,M3.5.0/02,M10.5.0/03", ntpServer[1]);    // deinen NTP Server einstellen (von 0 - 5 aus obiger Liste)
  if (!getLocalTime(&tm)) return false;
  return true;
}

OK, wahrscheinlich hat der den Array als "Sammlung", dass man sich eben einen aussucht.
Aber da fängts schon an: Welchen nehme ich?

In meinem aktuellen Beispiel nehme ich ja den hier:

pool.ntp.org

Wäre es besser, hier einen Deutschen Server anzusteuern, oder? Auf was genau wirkt sich die Auswahl der Servers aus? Die Zeitzone kann es ja nicht sein, denn die setzt FIPS ja über diese Zeile hier:

configTzTime("CET-1CEST,M3.5.0/02,M10.5.0/03", ntpServer[1]);

Ich mach das ja so in meinem Beispielcode

configTime(gmtOffset_sec, daylightOffset_sec, ntpServer);

Daher frage ich mich an der Stelle:
Gibt es ein besser oder schlecher für eine der beiden config-Varianten?

Jedenfalls bekomme am Ende auch die korrekte deutsche Uhrzeit raus.

Nur eben passiert es manchmal (nach Tagen oder Wochen) das sich die Zeit auf einmal verstellt (z.B um eine ganze Stunde oder 45 Minuten) und dann auch so bleibt...obwohl ich doch alle 20 Sekunden wieder neu die Zeit vom NTP hole.

Ansonsten kann ich prinzipiell was die NTP-Abfrage anbetrifft keinen großen Unterschied zwischen Fips' Lösung und meiner finden (klar, das Abfrage-Intervall ist anders).

Was ich noch mache, ist eben die Verwendung der ESP32 RTC, da ich ja auch offline weiter Uhrwerken will.

Dafür verwende ich diese Library:

#include <ESP32Time.h>

Keine Ahnung, ob die evtl. der Übertäter ist.

Aber wie könnte man denn ohne diese Library die Zeit auf dem ESP32 weiterführen, wenn das WLAN mal aussetzt (zum Beispiel über Nacht)?

Ich hab übrigens ein ähnliches Problem beim Googeln gefunden:

Der Fred-Ersteller schreibt ja auch

"But some days, let's say every 30 days, I get wrong time data from the library. Sometimes 5 minutes difference, sometimes 30 minutes. And although I have configured a refresh every 10 minutes, the time remains wrong for hours, sometimes only until a hard reset."

Aber die Lösung konnte ich damit auch nicht wirklich finden.

Mir würde es schon reichen, wenn die Uhr z.B. bei WLAN Ausfall von mehreren Tagen dann immer noch auf ca. 5 Minuten genau ist. Aber in meinem Fall hat sich die Uhrzeot ja verstellt, obwohl ich alle 20 Sekunden per NTP synchronisiere - und das schnall ich einfach nicht.

Das kuck ich mir mal an. Ist halt immer doof, weil der Fehler (zumindest mit meinem Code) manchmal erst nach Tagen auftritt. Da kann man dann nicht mal schnell kucken sondern muss wieder Tage lang warten ;-(

LG

Deine fritzBox.
Ist lokaler Traffic - fertig.
Wenn Dir Deine Abfragen Amok laufen, freut sich der externe Zeitserver, das er nicht behelligt wird.
:wink:
Ich selbst habe sogar einen Proxy/Firewall zu stehen, den ich seit Jahren nicht anfasse, der sich seine Zeit von der FB holt und den ich abfrage - das funktioniert supi. Auch für Geräte, die garnicht nach draussen telefonieren dürfen....

1 Like

Hoffentlich nicht von einem öffentlich erreichbaren NTP-Server. Da bist Du bei den Intervallen schnell auf der Blacklist.

Gruß Tommy

Bei pool.ntp.org sind 20sek durchaus ok. Aber Recht hast - denn die Empfehlung lautet 5 Abfragen / Std.
Aber eben auch mit den entsprechenden Nebenwirkungen.

@basementmedia Zum lesen wie deren pool funktioniert und was zu beachten ist: pool.ntp.org: Wie benutze ich den NTP Pool?

Wen Du eine Fitz Box hast nimm die als NTP, und damit übergehst die Backlisten.

Ja, wobei 1 pro Stunde durchaus für normale Anwendungen genügt.
Ich betreibe intern 2 eigene NTP-Server (FRitzbox und einen Raspi). Alle ESP greifen auf die beiden zu.

Gruß Tommy

Ach schau - noch einer :wink: - Und dann schaut man mich schief an, wenn ich spreche: ich bin nicht allein...

Ich habe hier Infrastruktur, die nicht nach draussen gehen darf/soll - da hat sich bewährt, das selbst die PC's sich da ihre Zeit abholen.
Wenn ich doch irgendwann ESP machen sollte, bekommt die Microwelle als erstes einen zugang zum timeserver :wink:

Wo habe ich Dich deswegen schief angeschaut?
Beide fragen >= 1 Stunde ab.

Gruß Tommy

Nein - flasch verstadnen - alles gut, War n en versuchter Joke :wink:

1 Like

ok :wink:

Gruß Tommy

Ich hab mir jetzt mal deinen Link angeschaut @noiasca

https://werner.rothschopf.net/microcontroller/202103_arduino_esp32_ntp_en.htm

Funktioniert prima, damit geh ich mal ins Rennen.

Könnte man es auch so konfigurieren, dass - falls jemand keine Fritzbox hat - dann alternativ ein anderer Pool gewählt wird? Wenn ja, wie müsste sowas im Code aussehen?

Und noch was anderes @noiasca
Bei deinem Beispiel muss ja zumindest am Anfang an eine Internetverbindung bestehen, damit die Start-Uhrzeit korrekt gesetzt wird. Ich hab dann mal nach ein paar Sekunden (nachdem die Uhrzeit erfolgreich gesetzt wurde) das WLAN ausgeschaltet und siehe da: die Uhrzeit wurde trotzdem weitergezählt. D.h. ab dann springt quasi die interne RTC des ESP32 ein, oder?

Somit bräuchte ich diese andere Library (ESP32Time.h) anscheinend gar nicht.

Aber:
Da ich ja auch möchte, dass man die Uhr ganz offline nutzen kann, möchte ich die Startuhrzeit auch wahlweise manuell setzen können (z.B. von einem Handy an den ESP übermitteln.
Wenn die Uhr dann ab und zu etwas WLAN ab bekommt, soll sie sich aktualisieren, wenn nicht, aber eben trotzdem noch weiterlaufen.

Somit lautet die Frage: Wie kann man die Initial-Uhrzeit manuell setzen?

Folgendes habe ich probiert (ich zeig nur mal den entscheidenden Teil, die Setup()):

void setup() {
 Serial.begin(115200);
 Serial.println("\nNTP TZ DST - bare minimum");

 configTime(0, 0, MY_NTP_SERVER); // 0, 0 because we will use TZ in the next line
 setenv("TZ", MY_TZ, 1); // Set environment variable with your time zone
 tzset();

 tm.tm_year = 2022 - 1900;
 tm.tm_mon = 2;
 tm.tm_mday = 11;
 tm.tm_hour = 19;
 tm.tm_min = 56;
 tm.tm_sec = 0;
 tm.tm_wday = 5;
 time_t t = mktime(&tm);
 struct timeval now = { .tv_sec = t };
 settimeofday(&now, NULL);

// Zu Testzwecken kein WLAN, später dann ja
 /*
 // start network
 WiFi.persistent(false);
 WiFi.mode(WIFI_STA);
 WiFi.begin(STASSID, STAPSK);
 while (WiFi.status() != WL_CONNECTED) {
  delay(200);
  Serial.print ( "." );
 }
 Serial.println("\nWiFi connected");
 */
}

Sieht ganz gut aus, die Uhrzeit wird gesetzt und läuft durch.

Aber: Wenn jetzt NIE eine WLAN-Verbindung und damit NIE ein Abgleich mit dem NTP stattfinden würde, würde die Uhr wahrscheinlich nach und nach Sekunden verlieren, korrekt?

Würde die Zeitumstellung (Sommer/Winterzeit) trotzdem funktionieren?

Mal als ehrliche Frage: Warum willst Du unbedingt auf ein externes RTC-Modul (DS32321) verzichten? Das kostet doch nur wenig. Das kannst Du bei erfolgreicher NTP-Abfrage aktualisieren und ohne Erfolg liefert es Dir lange Zeit eine stabile Uhrzeit. (Nur am Rande - man kann den EEPROM auf dem Modul gegen einen FRAM austauschen und hat dann nebenbei einen persistenten Speicher mit vielen Schreibzyklen).

Gruß Tommy

Ja, wäre bestimmt eine gute Lösung. In diesem Fall würde mich trotzdem rein technisch die pure ESP32 Lösung interessieren.

Annahme JA und JA.

ich sage so ... die interne Zeit läuft durch den RTC des ESP, sie wird nur im hinterlegten Rhyhtmus zusätzlich mit SNTP gesynct.

1 Like

Holt sich der ESP die IP-Adresse via DHCP?
Wenn Du einen gut gepflegten DHCP-Server hast, übermittelt der auch die passende Adresse des Zeitservers.
Ansonsten ist - zumindest auf den Heimroutern - der Timeserver aktiv und gleichzeitig sind die DHCP-Server. Das heisst, Deine Router/Gateway-Adresse wäre Dein Zeitserver.

Dann wäre die Uhr nicht gestellt und hätte irgend eine Zeit vom 1.1.1970.

Gruß Tommy