Grundsätzlich ist es keine große Tat die interne RTC mit der Netzzeit zu syncronisieren. Dazu gibt es das Standard-Modul ntptime.py. Dieses Modul enthält eine einzige Funktion: ntptime.settime(). Diese syncronsiert die RTC mit der Netzzeit. Erledigt.
Allerdings wird die RTC damit auf UTC gesetzt. Egal ob man time.time(), rtc.datetime() oder rtc.localtime() aufruft, immer wird UTC zurückgegeben. Deshalb habe ich mit Hilfe von Chatgpt eine Modul time_sync.py erstellt.
Dieses syncronisiert die RTC mit der Netzzeit und stellt sie auf MEZ/MESZ um. Die RTC läuft also mit MEZ/MESZ.
# time_sync.py
#
# RTC + NTP + MEZ/MESZ Zeitverwaltung
# für ESP32 / MicroPython
#
# RTC läuft in LOKALER ZEIT
#
# Hardware: M5Stack ATOM-S3R
# Firmware: UIFlow 2
#
# Die NTP Implementierung in ntptime.py ist sehr rudimentär.
# Sie enthält nur settime().
# ntp.gmtime() wird bei dir() zwar angezeigt, läßt sich aber nicht aufrufen.
# Es funktioniert nur time.gmtime()
#
NAME = 'time_sync.py'
VERSION = '00.00.03'
DATE = '10.05.2026'
AUTHOR = 'Peter Stoeck & ChatGPT'
# ---------------------------------------------------------
# IMPORTS
# ---------------------------------------------------------
import time
import machine
from mod_data import SYS_INFO
# ---------------------------------------------------------
# REGISTER
# ---------------------------------------------------------
def register():
SYS_INFO.register(NAME, VERSION, DATE)
# ---------------------------------------------------------
# NTP VERFÜGBAR?
# ---------------------------------------------------------
try:
import ntptime
HAS_NTP = True
except:
HAS_NTP = False
# ---------------------------------------------------------
# RTC
# ---------------------------------------------------------
rtc = machine.RTC()
# ---------------------------------------------------------
# CONFIG
# ---------------------------------------------------------
# Sync-Intervall in Stunden
SYNC_INTERVAL_HOURS = 6
# intern in Sekunden
NTP_INTERVAL = SYNC_INTERVAL_HOURS * 3600
_last_sync = 0
# ---------------------------------------------------------
# LETZTER SONNTAG IM MONAT
# ---------------------------------------------------------
def _last_sunday(year, month):
for day in range(31, 0, -1):
try:
t = time.mktime((year, month, day, 0, 0, 0, 0, 0))
# Sonntag = 6
if time.localtime(t)[6] == 6:
return day
except:
pass
return 31
# ---------------------------------------------------------
# SOMMERZEIT EU / DE
# ---------------------------------------------------------
def is_dst_utc(year, month, day, hour):
"""
Sommerzeitregel Europa (UTC-basiert)
Beginn:
letzter Sonntag März
01:00 UTC
Ende:
letzter Sonntag Oktober
01:00 UTC
"""
if month < 3 or month > 10:
return False
if month > 3 and month < 10:
return True
start_day = _last_sunday(year, 3)
end_day = _last_sunday(year, 10)
# März
if month == 3:
if day > start_day:
return True
if day < start_day:
return False
# Umschalttag
return hour >= 1
# Oktober
if month == 10:
if day < end_day:
return True
if day > end_day:
return False
# Umschalttag
return hour < 1
return False
# ---------------------------------------------------------
# RTC -> UNIX TIMESTAMP
# ---------------------------------------------------------
def rtc_timestamp():
"""
RTC (lokale Zeit) -> Unix Timestamp
"""
t = rtc.datetime() # (Jahr, Monat, Tag, Wochentag, Stunden, Minuten, Sekunden, Zehntelsekunden)
return time.mktime((
t[0], # year
t[1], # month
t[2], # day
t[4], # hour
t[5], # minute
t[6], # second
0,
0
))
# ---------------------------------------------------------
# NTP SYNC
# ---------------------------------------------------------
def sync_ntp():
global _last_sync
if not HAS_NTP:
print('NTP nicht verfügbar')
return False
try:
# -------------------------------------------------
# RTC vorher sichern
# -------------------------------------------------
rtc_before = rtc_timestamp()
# -------------------------------------------------
# NTP UTC holen
# -------------------------------------------------
ntptime.settime() # setzt RTC auf UTC
# UTC lesen
utc = time.localtime() # gibt Lokale Zeit zurück ?! funktioniert das?
year = utc[0]
month = utc[1]
day = utc[2]
hour = utc[3]
# -------------------------------------------------
# DST bestimmen
# -------------------------------------------------
if is_dst_utc(year, month, day, hour):
offset = 2 # MESZ
else:
offset = 1 # MEZ
# -------------------------------------------------
# Lokalzeit berechnen
# -------------------------------------------------
utc_ts = time.mktime(utc)
local_ts = utc_ts + (offset * 3600)
lt = time.localtime(local_ts)
# -------------------------------------------------
# RTC auf LOKALZEIT setzen
# -------------------------------------------------
rtc.datetime((
lt[0], # Jahr
lt[1], # Monat
lt[2], # Tag
lt[6], # Wochentag
lt[3], # Stunde
lt[4], # Minute
lt[5], # Sekunde
0
))
# -------------------------------------------------
# Abweichung ausgeben
# -------------------------------------------------
rtc_after = rtc_timestamp()
diff = rtc_after - rtc_before
print('NTP sync OK')
if diff >= 0:
print('RTC Abweichung: +{} Sekunden'.format(diff))
else:
print('RTC Abweichung: {} Sekunden'.format(diff))
_last_sync = time.time()
return True
except Exception as e:
print('NTP Fehler:', e)
return False
# ---------------------------------------------------------
# HIGH LEVEL SYNC
# ---------------------------------------------------------
def sync_time(force=False):
global _last_sync
now = time.time()
if not force:
if (now - _last_sync) < NTP_INTERVAL:
return False
return sync_ntp()
# ---------------------------------------------------------
# HELPER
# ---------------------------------------------------------
def now():
return rtc.datetime()
def timestamp():
t = rtc.datetime()
return '{:04d}-{:02d}-{:02d} {:02d}:{:02d}:{:02d}'.format(
t[0], t[1], t[2], t[4], t[5], t[6])
def rtc_date():
t = rtc.datetime()
return '{:04d}-{:02d}-{:02d}'.format(t[0], t[1], t[2])
def rtc_time():
t = rtc.datetime()
return '{:02d}:{:02d}:{:02d}'.format(t[4], t[5], t[6])
Wie funktioniert das Programm?
Der Abschnitt Register ist für die Zeit-Funktionen unwichtig. Er ermöglicht das Registrieren des Moduls im Hauptprogramm. Ich habe diesen Mechanismus entworfen, um die importierten Module und ihre Versionen anzeigen zu lassen. Dazu erfolgt später noch ein Artikel.
Beim Importieren wird zuerst geprüft, ob NTP überhaupt verfügbar ist. Deshalb mus vor dem Import dieses Modules eine Wlan- bzw. Internetverbindung bestehen. Dann wird die RTC erzeugt (time_sync.rtc). Damit ist der Import abgeschlossen.
Die Funktionen _last_sunday(year, month) und is_dst_utc(year, month, day, hour) ermitteln, ob Datum und Zeit zur Sommerzeit gehören.
rtc_timestamp() gibt Datum und Zeit als UNIX-Timestamp zurück.
sync_ntp() führt die eigentliche Syncronisierung durch.
Hier ist auch noch eine Funktionalität eingebaut, die die Abweichung der RTC von der NTP ausgibt. Das habe ich nur eingebaut, damit ich testen kann, wie oft die Syncronisation erforderlich ist. Deshalb wird zuerst die RTC-Zeit gespeichert.
Da die NTP-Syncronisierung nur mit:
ntptime.settime()
funktioniert, muss zuerst die RTC auf die aktuelle UTC gesetzt werden. Dann wird diese ausgelesen und auf die MEZ/MESZ angepasst wieder zurück in die RTC geschrieben.
Schließlich wird die Abweichung ausgegeben.
Das war es im Grunde schon. Aber es gibt noch sync_time(force=False). Diese Funktion sorgt dafür, daß die Syncronisation nicht bei jedem Aufruf, sondern nur im eingestellten Intervall (SYNC_INTERVAL_HOURS am Anfang des Codes) ausgeführt wird.
Für die Nutzung des Ganzen gibt es noch die Helfer-Funktionen :
- now()
- timestamp()
- rtc_date()
- rtc_time()
Mit Hilfe dieser Funktionen können die entsprechenden Strings geholt werden.
Anwendung
Nach dem Import muss ersteinmal alles gestartet werden:
import time_sync
time_sync.sync_time(force=True) # Führt eine Syncronisation durch
print(time_sync.rtc_date()) # Beispiele für die Anwendung
print(time_sync.rtc_time())
Ab und zu, so wie es das Main-Programm hergibt muss dann geprüft werden ob wieder eine Syncronisation durchgeführt werden muss:
time_sync.sync_time()
Damit hat man eine RTC, die zuverlässig mit NTP syncronisiert wird und die MEZ/MESZ zeigt.
Viel Spaß damit.
Du musst eingeloggt sein um eine Antwort hinterlassen zu können.