!!! Achtung: Thonny aktualisiert die RTC selbsttätig im Hintergrund und setzt sie auf UTC !!!

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.

04. Mai 2026 · Kommentieren · Kategorien: Allgemein, M5Stack · Tags:

Welche Möglichkeiten gibt es das Tab5 mit einer externen Stromquelle zu betreiben?

Der Akku am Tab5 ragt hinten ziemlich klobig heraus. Deshalb suche ich nach einer alternativen Stromversorgung.

ACHTUNG – Das hier Dargelegte sind nur erste Überlegungen zu dem Thema. Ich habe es noch nicht getestet!

PLEASE NOTE – The information provided here is only a preliminary discussion of the topic. I haven’t tested it yet!


Im Schaltplan ist der Anschluß für den M5Bus dargestellt. Hier gibt es SYS_EXT5V und SYS_IN.

Im Übersichtsplan taucht SYS_EXT5V nicht auf. SYS_IN ist aber als alternative Stromversorgung auszumachen:

SYS_IN ist mit RS485, EXT und BUS verbunden. Damit kann der Tab5 z.B. über RS485 mit Strom versorgt werden. Die Anschlüsse am M5BUS sind im ersten Bild dargestellt. Mit EXT ist die 2×5 polige externe Buchsenleiste an der unteren Seite des Gehäuses, links neben der SD-Karte gemeint.

Hier der RS485-Anschluß:

Hier ist der Ausschnitt aus dem Schaltplan in dem SYS_IN verarbeitet wird:

SYS_IN geht also nur auf den Spannungswandler, der daraus 5 Volt macht. Ein folgender Powerselector schaltet diese Spannung dann auf die interne 5Volt-Versorgung durch:

Der Spannungswandler hat lt. Datenblatt folgende Eigenschaften:

  • FEATURES
  • Wide 3.8V to 55V Operating Input Range
  • 250mΩ Internal Power MOSFET
  • Up to 2MHz Programmable Switching Frequency
  • 140μA Quiescent Current
  • Ceramic Capacitor Stable
  • Internal Soft-Start
  • Up to 95% Efficiency
  • Output Adjustable from 0.8V to 52V
  • Available in 3×3 10-Pin QFN and SOIC8 with Exposed Pad Package
  • Der max. Strom beträgt 3.2 A