11. Januar 2021 · Kommentare deaktiviert für Herzlich Willkommen auf meiner Website! · Kategorien: Allgemein

Diese Website ist ein rein privates Projekt. Es verfolgt keinerlei kommerzielle Ziele. Deshalb werden von mir auch keine persönlichen Daten gespeichert. Deshalb kann sich auch niemand auf dieser Website anmelden und Kommentare hinterlassen. Der Provider (1blu.de) speichert die Verbindungsdaten für einen begrenzten Zeitraum.

Dieser Blog dient mir dazu meine Projekte zu dokumentieren und meine Erfahrungen damit festzuhalten. Er ist im Internet frei zugänglich, damit auch Andere von meinen Erfahrungen profitieren können. Schließlich muss nicht jeder das Rad selbst erfinden.

Mein Beiträge sind keine Bauanleitungen! Es besteht kein Anspruch darauf, dass die vorgestellten Projekte bei einem Nachbau funktionieren! Sollten dabei Schäden entstehen, so ist dafür der Nachbauer verantwortlich. Schließlich sollte jeder wissen was er tut. Ich schließe jede Haftung aus.

Die Beiträge können nur als Anregung angesehen werden.

Wenn Du mir etwas mitteilen möchtest, so schicke mir unter peter@peters-bastelkiste.de eine Email.

Viel Spaß beim Stöbern.

Peter

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

Ich habe bei Chatgpt die Belastbarkeit und den Spannungsabfall bei 1 Ampere erfragt:

  • mm2 : Imax – Inorm – Uloss
  • 0,14 : 2,5-3 A – 2 A – 1,27 V
  • 0,25 : 4-6 A – 4 A – 0,071 V
  • 0,5 : 8-10 A – 6-8 A – 0,036 V

Für Hin und Rücklauf muß der Spannungswert verdoppelt werden.


Wemos 18650 shield V3

Hier geht es um die Befestigung des o.a. Shields. Es wurden 2×5 mm selbstschneidende Schrauben für Kunststoff verwendet. Für die konkrete Anwendung in einem 3D-gedruckten Gehäuse sind nur die vier Eckenkonstrukte erforderlich. Die Verbindungen dazwischen waren nur für die Tests nötig.

Die Übersichtsmaße:

Die Konstruktion der Halter ist hier zu sehen:

Der Versuch ein kleines „Rohr“ stehen zu lassen, das in das Loch auf der Leiterplatte hineinragt und durch das dann die Schraube gedreht wurde scheiterte leider daran, das diese dünne Struktur nicht gedruckt wurde.

Deshalb habe ich mich für diese Konstruktion entschieden:

So sieht des Testmodul aus (Onshape).

Bei selbstentworfenen Gehäusen, die im 3D-Druck gefertigt werden ist es möglich die Bauteile mit selbstschneidenden Schrauben zu befestigen. Das ist wesentlich einfacher und preiswerter als das einschmelzen von Gewindeeinsätzen aus Messing. Das selbstschneidende Konzept ist sicher nicht für Teile geeignet, die häufig ein- und ausgebaut werden müssen. Für Teile, die nur zu Reparaturzwecken ausgebaut werden müssen ist es aber m.E. Erste Wahl.

Ich habe mir aus China entsprechende Schrauben mit Innensechkant in den Größen 2×5, 2×8, 3×5, 3×8 und 3×10 schicken lassen. Nun wollte ich gerne wissen wie groß die Bohrungen für diese Schrauben sein müssen. Dazu habe ich ein kleines Testteil mir Onshape entworfen:

Die obere Reihe ist für die 2 mm Schrauben und die untere für die 3 mm Ausführung.

Das sieht dann so aus:

Zu meinem Erstaunen ließen sich die jeweiligen Schrauben in alle 5 Löcher hinein drehen, allerdings mit unterschiedlichem Kraftaufwand.
Für die 2 mm-Schrauben erscheinem mir die drei rechten Löcher geeignet zu sein (1,8 1,9 und 2,0 mm). Das erstaunt, weil die Schrauben doch 2 mm Außendurchmesser haben. Ich hatte bei einem früheren Test festgestellt, das Löcher, also Innendurchmesser beim 3D-Druck etwas kleiner ausfallen. Deshalb hat das Gewinde noch etwas Fleisch zum festhalten. Ich würde aber 1,8 oder 1,9 mm empfehlen, das fühlt sich stabil an.

Bei den 3 mm Schrauben fühlten sich die beiden rechten Löcher (2,7 und 2,8 mm) gut an. Die anderen gingen auch, aber die Kraft zum eindrehen ist schon erheblich.

Die oben angegeben Bohrungsmaße beziehen sich auf die in der Zeichnung eingetragenen Werte, nicht auf die tatsächlich beim Druck entstandenen.

In 3D-Druck-Gehäusen wird meist ein Durchbruch für USB-Buchsen, z.B. Zum Laden von Akkus oder zur Kommunikation mit anderen Geräten benötigt. Ich habe mir verschiedene USB-C-Buchsen für die Gehäusemontage aus China schicken lassen und dann die erforderlichen Durchbrüche durch Messen und Probieren ermittelt.

Die Überschriften zu den verschiedenen Teilen klingen sonderbar, aber ich habe einfach die Überschrift oder Teile davon von der Aliexpress Webseite übernommen. So kann man sie ggf. einfach wiederfinden.


Typ-C Weibliche 2Pin Typ-C Buchse Stecker Wasserdichte Jack

Diese Buchse ist wohl am einfachsten einzubauen: Bohrung mit 12mm Durchmesser.


USB 3,1 Typ C Micro USB 2,0 mit Schalttafel einbau Schraube

Hier ein Bild davon:

Ich habe die Buchse vermessen und einen Probeentwurf mit Onshape angefertigt. Der funktionierte schon recht gut. Hier sie Skizze von Onshape:

Der ovale Innenteil ist 1,5mm hoch, der restliche Teil (Wand) 3mm.

Allerdings kollidierten die Schraubenköpfe von Flachkopf-Kreuzzschlitzschrauben mit einigen Steckern. Die Köpfe von Schlitzschrauben waren ca. 0,5mm kleiner und passten besser.

Allerdings wären Senkschrauben eleganter. Deshalb habe ich die Bohrungen für die Schrauben testweise angefast.45° und 1mm. Dann ist der Durchmesser oben 5,5mm und das entspricht den gemessenen Größen der Senkschraubenköpfe:

Perfekt mit M3x6 Kreuzschlitz-Senkkopfschrauben! Die Schraubenköpfe schließen bündig ab.


Ich habe die Maße für den Einbau des Nokia-Displays in ein 3D gedrucktes Gehäuse ermittelt.

Um die Maße zu ermitteln habe ich einige Muster gebaut. Die sahen so aus:

Die 3mm Stifte passen in die Bohrungen in der Platine (3,2mm)

Das Display liegt nicht symmetrisch zwischen den Befestigungslöchern. Es ist etwas nach oben versetzt. Mit den obigen Maßen passt es aber.

Bei meinem Hameg Funktionsgenerator funktionierte nach einigen Jahren die Frequenzeinstetllung nicht mehr. Recherche im Internet und Nachfrage bei Chatgpt erbrachte, dass es ein bekanntes Problem ist, daß der Drehimpulsgeber (DIG) nach einigen Jahren defekt wird.

Nach einigem Zögern habe ich dann das Modul zerlegt. Da Alles mit Allem verbunden ist, sei es durch Drähte oder Verlötung der Platinen, habe ich es nicht geschafft das Teil so zu zerlegen, daß ich den DIG auslöten konnte.

Da der Drehknopf extrem fest saß war viel Kraft nötig ihn abzubekommen. Später zeigte sich, dass es sich um einen Spannzangenknopf handelt. Durch die starke Kraftausübung hatte sich das Gehäuse des DIG etwas gelockert. Es ist auf das Unterteil, das mit der Platine verlötet ist augesetzt und mit 4 Laschen geklemmt.

Ich habe dann diese Laschen soweit hoch gebogen, dass das Gehäuse sich entfernen ließ. Darin befand sich eine Scheibe mit Leiterbahnen, die auf einer Kunststoffachse sitzt. Diese Achse ist mit Fett geschmiert. Dieses Fett hatte sich auf die Scheibe gesetzt und so die Kontaktgabe verhindert. Nachdem ich die Scheibe gereinigt und wieder eingebaut hatte funktionierte der Funktionsgenerator wieder.

Im Unterteil befinden sich 3 Federkontakte mit je 2 Kontakten, die auf die Scheibe drücken und bei Kontakt mit den Leiterbahnen die Inpulse ausgeben.

Letztendlich ist die Reparatur relativ einfach:

  1. Die zwei Knöpfe für Amplitude und Offset abziehen.
  2. Die Kappe des Knopfes für die Frequenzeinstellung entfernen. Die Schraube dahinter etwas locher drehen und den Knopf abziehen. Geht dann ganz leicht!
  3. Boden und Deckel abnehmen. Dazu je eine Schraube an der Rückseite heraus schrauben.
  4. Die drei Schrauben in der Frontplatine von hinten abschrauben. Eine sitzt oben und 2 unten (oder umgekehrt). Die habe ich am Ende nicht wieder eingedreht, weil mir das zu kompliziert war und ihr Fehlen keinen wesentlichen Nachteil darstellt.
  5. Schließlich die 4 Schrauben an den Seitenteilen herausdrehen.
  6. Frontplatte abziehen. Geht etwas schwer.
  7. Nun ist der DIG zugänglich.
  8. Vorsichtig mit einem Schraubendreher die 4 Laschen aufbiegen.
  9. Das Oberteil abnehmen. Die Scheibe heraus nehmen und reinigen.
  10. In umgekehrter Reihenfolge wieder zusammenbauen.

Das war’s!

Ich habe die Idee ein Messgerät zu bauen mit dem ich Kondensatoren vermessen kann. Zur Kapazitätsmessung habe ich mich für die Aufladung eines Kondensators mit Konstantstrom entschieden, weil so die Kapazität in einem linearen Verhältnis zur Aufladezeit steht.

Die folgende Schaltung hat sich dafür als geeignet erwiesen (in der Simulation mit LTSpice):

Die Werte der Widerstände sind sehr kritisch. Wenn sie nicht exakt gleich sind, Verläuft der Strom über die Ladezeit nicht mehr konstant, sondern steigt an oder fällt ab. Wie groß der Einfluß ist habe ich nicht untersucht. Aus einem früheren Versuch habe ich aber in Erinnerung das schon kleine Abweichungen sich kräftig bemerkbar machen. Ganz wichtig ist, das die Summe von R3 und R6 exakt der von R2 und R1 entspricht!

Die Simulation zeigt sehr schön, dass der Strom (hier 20µA – blaue Linie) horizontal verläuft, bis der Ausgang des OPV an seine Grenze stößt. Dementsprechend linear verläuft der Spannungsanstieg (grüne Linie).

Der hier ausgewählte OPV ist nicht zwingend. Ich werde den Versuchsaufbau mit einem OPA186 oder OPA2186 bauen. Den MAX44244 habe ich als Ersatztyp gefunden.


Perfekt! Dann gehen wir Schritt für Schritt vor, um Windows wieder bootfähig zu machen, ohne die Daten zu löschen. Ich erkläre es für Windows 10. Du brauchst dafür ein Windows-Installationsmedium (USB-Stick oder DVD).


Schritt 1: Windows-Installationsmedium vorbereiten

  1. Lade das Windows 10 Media Creation Tool herunter: Microsoft Downloadseite
  2. Erstelle damit einen bootfähigen USB-Stick (mindestens 8 GB).

Schritt 2: Vom Installationsmedium booten

  1. Rechner vom USB-Stick starten (Boot-Menü oder BIOS/UEFI wählen).
  2. Sprache und Tastatur auswählen, dann „Computerreparaturoptionen“„Problembehandlung“„Eingabeaufforderung“.

Schritt 3: Bootloader reparieren

Im Eingabeaufforderungs-Fenster folgende Befehle ausführen:

  1. Bootpartition markieren:
<code>
diskpart 
list disk 
select disk 0 (deine Windows-Festplatte auswählen) 
list partition 
select partition 1 (sda1 – System Reserved) 
active (Partition als aktiv markieren) 
exit
</code>

2. Bootloader reparieren:

<code>
bootrec /fixmbr 
bootrec /fixboot 
bootrec /scanos 
bootrec /rebuildbcd
</code>

3. Optional:

Wenn bootrec /fixboot Zugriff verweigert, kann man folgendes versuchen:

 <code>
bootsect /nt60 SYS /mbr
</code>

    Schritt 4: Neustart

    • USB-Stick entfernen, Rechner neu starten.
    • Windows sollte nun wieder booten.

    Schritt 5: Backup machen

    Sobald Windows wieder startet:

    • Am besten ein vollständiges Image von sda2 erstellen (z. B. mit Macrium Reflect Free oder Acronis True Image).
    • So hast du ein sicheres Backup, bevor du erneut Linux probierst.

    Wenn du willst, kann ich dir danach auch zeigen, wie du Linux parallel installieren kannst, ohne den Windows-Boot zu zerstören.

    Willst du, dass ich das vorbereite?

    Dieses Rezept habe ich nicht mehr ausprobiert weil ich GRUB zum Laufen bekommen habe und so wieder auf das Windowssystem zugreifen konnte.