TM1637-Modul mit Anzeige der Zeit

TM1637-LED-Modul

Mehrstellige 7-Segment-Anzeigen direkt per Schieberegister zum Anzeigen von Daten zu nutzen, bedeutet auf dem Breadboard einen ziemlichen Kabelsalat. Praktischerweise gibt es fertige Module (oft als digital tube bezeichnet) mit vier- oder achtstelligen Anzeigen und den Chips TM1637 oder MAX7219, die mit vier Anschlüssen auskommen. Die vierstelligen TM1637-Module können meist in der Mitte einen Doppelpunkt darstellen und sind primär für Zeitanzeigen gedacht, man kann sie aber auch z.B. als Temperaturanzeige mit Minuszeichen und Gradsymbol nutzen. Es gibt auch Module mit Dezimalpunkten nach den einzelnen Ziffern und anderen Farben als dem standardmäßigen LED-Rot.

Bei deutschen Elektronikversendern ist ein einfaches TM1637-Modul ab Preisen zwischen 1 und 2 € zu bekommen, die achtstelligen Anzeigen mit MAX7219-Chip sind etwas teurer. [1]Je nach Händler können die Preise auch deutlich höher liegen – ein Preisvergleich kann sich lohnen. Bestellungen, Service, auch Reklamationen ohne Sprachbarriere, dazu der hiesige Standort … Continue reading

Das TM1637-Modul

LED-Modul mit TM1637
LED-Modul mit TM1637 (Vorder- und Rückansicht)

Das Modul hat vier Anschlüsse, die auf der Rückseite beschriftet sind. Von oben nach unten sind das:

  • CLK – Taktung der Register
  • DIO – Daten
  • VCC – Versorgungsspannung (3,3 – 5,25 V)
  • GND – Masse

CLK und DIO werden jeweils mit einem beliebigen digitalen Output-Pin verbunden (ich nutze hier GPIO 33 für CLK und GPIO 32 für DIO).

Bibliothek TM1637

Für TM1637-Module gibt es diverse Bibliotheken zum Einbinden in die Arduino-IDE. Ich habe mich für die Bibliothek TM1637 entschieden (Kurzvorstellung im Arduino Playground), weil man damit neben Ziffern und Hexadezimalwerten auch recht einfach andere Zeichen darstellen kann. Die Bibliothek ist am einfachsten über die Bibliotheksverwaltung der Arduino-IDE zu installieren – man findet sie unter dem Namen »TM1637 by Avishay Orpaz«.
Will man sie lieber manuell installieren, geht das wie folgt. (Beide Installationswege werden ausführlich im Beitrag zur Bibliotheksverwaltung der Arduino-IDE erläutert.)

  • Download des ZIP-Archivs von Github
  • Entpacken der ZIP-Datei im libraries-Ordner der Arduino-IDE und Umbenennen des entpackten Ordners in TM1637

Die Bibliothek stellt u.a. folgende Funktionen bereit, deren jeweilige Parameter in der Datei TM1637Display.h dokumentiert sind:

  • setBrightness – stellt die Helligkeit in 8 Stufen (0-7) ein und schaltet die Anzeige an oder aus
  • showNumberDec – Anzeige einer Dezimalzahl mit oder ohne führende Nullen
  • showNumberDecEx – Anzeige einer Dezimalzahl mit Dezimalpunkten oder Doppelpunkt
  • showNumberHexEx – Anzeige einer Hexadezimalzahl mit Dezimalpunkten oder Doppelpunkt
  • setSegments – zeigt beliebige Zeichen an, indem einzelne Segmente des Displays gesetzt werden

Anzeige der Zeit eines NTP-Zeitservers

TM1637-Modul am ESP32
TM1637-Modul am ESP32

Der ESP32 besitzt (wie andere Mikrocontroller auch) keine Batterie (Akku) zur Erhaltung der internen Zeit – beim Einschalten starten die Zeitfunktionen mit dem Wert 0 Sekunden, was dem 1. Januar 1970 0:00 Uhr entspricht. Um z.B. für die Protokollierung von Daten die tatsächliche Zeit zu ermitteln, kann der ESP32 einen NTP-Zeitserver im Internet abfragen – hierzu müssen (mindestens vorübergehend) die WLAN-Funktionen aktiviert werden. Das Programm zeigt dann im Wechsel auf dem Display Uhrzeit (mit Doppelpunkt) und Datum (Tag und Monat) an.

Als Zeitserver wird häufig die Adresse pool.ntp.org oder de.pool.ntp.org verwendet. Wer seinen WLAN-Router als Zeitserver für das lokale Netz konfiguriert hat, sollte den eintragen; bei mir ist es die lokale Adresse fritz.box. [2]Eine Anleitung zur Einrichtung der FRITZ!Box als lokaler NTP-Server findet man z.B. beim Hersteller AVM. Sinnvolle Einträge für Zeitserver im deutschsprachigen Raum sind

  • de.pool.ntp.org (Zeitserver aus dem deutschen NTP-Pool)
  • at.pool.ntp.org (Zeitserver aus dem östereichischen NTP-Pool)
  • ch.pool.ntp.org (Zeitserver in der Schweiz)
  • ptbtime1.ptb.de (Zeitserver der Physikalisch-Technischen Bundesanstalt PTB in Braunschweig; ebenso ptbtime2 und ptbtime3)
  • europe.pool.ntp.org (europäische Zeitserver)
  • pool.ntp.org (eine Adresse aus dem weltweiten Pool von NTP-Servern)

Allgemeine Informationen zu Zeitservern und dem verwendeten Protokoll NTP (network time protocol) liefern u.a. Wikipedia und zeitserver.de.

Im unten folgenden Programm orientiert sich der Teil zum Setzen der Zeit am Beispielprogramm SimpleTime aus der Arduino-IDE (Menü Datei → Beispiele → ESP32 → Time → SimpleTime). [3]Per Web-Suche findet man auch andere (meiner Meinung nach umständlichere) Beispiele zur Kommunikation mit NTP-Servern, bei denen erst noch eine NTP-Client-Bibliothek installiert werden muss. Ein … Continue reading

Zeitzonenautomatik

Das Setzen der Zeitzone vereinfacht die Abfrage des NTP-Servers per configTime(). Standardmäßig erwartet die Funktion als ersten Parameter die Zeitdifferenz der eigenen Zeitzone zu UTC (koordinierte Weltzeit; entspricht der Zeitzone GMT) in Sekunden – für die hiesige Zeitzone CET = UTC + 1 Stunde also den Wert 3600. Zweiter Parameter ist eine weitere Zeitdifferenz – je nach Normal- oder Sommerzeit 0 oder 3600. (CET steht für Central European Time = MEZ = Mitteleuropäische Zeit.)

Verwendet man die hier gezeigte automatische Anpassung der Zeitzone mit der Funktion setenv(), lässt man die beiden ersten Parameter von configTime() bei 0 und spart sich die Rechnerei. Für die deutschsprachigen Länder lautet die korrekte Zeitzonenangabe wie folgt:[4]Eine Liste der Zeitzonen inkl. Angaben zur Sommerzeit findet sich auf github.com/nayarsystems/posix_tz_db/blob/master/zones.csv. Den Hinweis darauf habe ich auf fipsok.de gefunden.

#define TIMEZONE "CET-1CEST,M3.5.0/02,M10.5.0/03"
  [...]
configTime(0, 0, ntp_server);
// vor Trennung des WLANs einmal getLocalTime()
// aufrufen; sonst wird die Zeit nicht übernommen
getLocalTime(&tdata);
setenv("TZ", TIMEZONE, 1);  // Zeitzone einstellen

Oder etwas kürzer mit configTzTime() statt configTime() und setenv():

#define TIMEZONE "CET-1CEST,M3.5.0/02,M10.5.0/03"
  [...]
configTzTime(TIMEZONE, ntp_server);
// vor Trennung des WLANs einmal getLocalTime()
// aufrufen; sonst wird die Zeit nicht übernommen
getLocalTime(&tdata);

Anmerkung zu configTime()

Mir ist es nicht gelungen, die Zeit vom NTP-Server zu übernehmen, ohne vor dem Abschalten der WLAN-Funktionalität einmal die Funktion getLocalTime() aufzurufen. Es ist egal, ob man – wie hier – einen nicht ausgewerteten Aufruf der Funktion macht oder wie im Arduino-Beispielprogramm die Daten z.B. in den seriellen Monitor ausgibt. Wenn man configTime() ohne anschließendes getLocalTime() aufruft, wird die Zeit nicht gesetzt, sondern startet bei Null (01.01.1970 0:00:00 Uhr). Das hat mich einige Zeit zur Fehlersuche gekostet; den Grund konnte ich nicht finden und ich würde mich freuen, wenn es jemand weiß und mir per Kommentar mitteilt.

Das hier gezeigte Programm nutzt aus Bequemlichkeit führende Nullen zur Darstellung von Zeit und Datum. Will man Leerzeichen verwenden (um z.B. in der Datumsdarstellung die einstelligen Monate nur mit einer Ziffer anzuzeigen), muss man die Zeichen des Displays einzeln setzen. Wie das geht, zeigt das zweite Programm weiter unten.

WLAN-Name (SSID; service set identifier) und Passwort müssen zur Anmeldung an das eigene WLAN angepasst werden. Auch der Name des Zeitservers muss ggf. statt des lokalen Routers auf einen Server im Internet gesetzt werden.

  1. /* Zeit von einem NTP-Server holen und auf dem TM1637-LED-Modul anzeigen
  2.  *
  3.  * 2019-07-17 Heiko (unsinnsbasis.de)
  4.  */
  5.  
  6. #include <WiFi.h>
  7. #include "time.h"
  8. #include <TM1637Display.h>
  9.  
  10. const char* ssid = "Deine_WLAN_SSID";
  11. const char* password = "Dein_WLAN_Passwort";
  12. const char* ntp_server = "fritz.box"; // oder (de/at/ch).pool.ntp.org
  13.  
  14. // lokale Zeitzone definieren
  15. // https://github.com/nayarsystems/posix_tz_db/blob/master/zones.csv
  16. #define TIMEZONE "CET-1CEST,M3.5.0/02,M10.5.0/03"
  17.  
  18. #define CLK_PIN 33
  19. #define DIO_PIN 32
  20. TM1637Display display(CLK_PIN, DIO_PIN);  // Datenstruktur für Display
  21.  
  22. void setup() {
  23.   struct tm tdata;
  24.  
  25.   Serial.begin(115200);
  26.  
  27.   // mit dem WLAN verbinden; nach Ermitteln der Zeit kann die
  28.   // Verbindung wieder getrennt werden
  29.   Serial.printf("Verbindung herstellen mit %s ", ssid);
  30.   WiFi.begin(ssid, password);
  31.   while (WiFi.status() != WL_CONNECTED) {
  32.       delay(500);
  33.       Serial.print(".");
  34.   }
  35.   Serial.println(" Verbunden!");
  36.  
  37.   // Zeit vom NTP-Server holen
  38.   configTime(0, 0, ntp_server);
  39.   // vor Trennung des WLANs einmal getLocalTime()
  40.   // aufrufen; sonst wird die Zeit nicht übernommen
  41.   getLocalTime(&tdata);
  42.   setenv("TZ", TIMEZONE, 1);  // Zeitzone einstellen
  43.  
  44.   // WLAN trennen
  45.   WiFi.disconnect(true);
  46.   WiFi.mode(WIFI_OFF);
  47.  
  48.   // Display auf mittl. Helligkeit einstellen
  49.   display.setBrightness(3);
  50. }
  51.  
  52.  
  53. void loop() {
  54.   struct tm tdata;
  55.  
  56.   if (!getLocalTime(&tdata)) {
  57.     Serial.println("Fehler beim Ermitteln der Zeit");
  58.     delay (10000);
  59.   } else {
  60.     // Zeit als Zahl HHMM mit Doppelpunkt in der Mitte und führenden Nullen
  61.     // (sonst werden Zeiten in der ersten Stunde als xx statt 00:xx angezeigt)
  62.     display.showNumberDecEx((tdata.tm_hour)*100 + tdata.tm_min, 0b01000000, true);
  63.     delay(2000);
  64.     // Datum als Zahl TTMM mit führender Null
  65.     display.showNumberDec(tdata.tm_mday*100 + tdata.tm_mon+1, true);
  66.     delay(1000);
  67.   }
  68. }

Buchstaben und andere Zeichen als Laufschrift

7-Segment-Anzeige
7-Segment-Anzeige mit Beschriftung der Pins und Segmente (Vorlage: Fritzing)

Neben Zahlen kann man mit einer 7-Segment-LED auch eine ganze Menge Buchstaben und ein paar Sonderzeichen anzeigen. Hierzu muss man die einzelnen Segmente passend setzen. Die Position der Segmente und Berechnung der Zeichenwerte hatte ich ausführlich im Beitrag zu den 7-Segment-Anzeigen erläutert. Hier nur ein kurzes Beispiel: um ein kleines „o” anzuzeigen, müssen die Segmente C, D, E und G angeschaltet sein; als Zeichencode ergibt sich 0b01011100 = 22 + 23 + 24 + 26 = 4 + 8 + 16 + 64 = 92 = 0x5C.

Im Programm ist für jedes darstellbare Zeichen der entsprechende Hexadezimalwert kodiert. Da die TM1637-Bibliothek passende Konstanten bereitstellt (SEG_ASEG_G), könnte man die Werte auch durch Oder-Verknüpfung der Segment-Werte darstellen, z.B.:

code_o_lower = SEG_C | SEG_D | SEG_E | SEG_G;

Das folgende Programm ergänzt die TM1637-Bibliothek um Funktionen zur Zeichendarstellung: [5]Sauberer wäre es natürlich, die Klasse display um Methoden zu erweitern, die diesen Funktionen entsprechen. Vielleicht später einmal… ;-)

  • encodeChar() liefert zu einem Zeichen den 7-Segment-Code zurück. Nicht darstellbare Zeichen erzeugen ein Leerzeichen.
  • encodeString() übersetzt eine Zeichenkette in 7-Segment-Codes. Parameter sind:
    • (Zeiger auf den) Text
    • ein Zeiger auf ein Array, das die codierten Zeichen aufnimmt
    • Textlänge; Default: Displaygröße (4 Zeichen)
  • scrollText() zeigt den übergebenen Text als Laufschrift an. Es werden immer so viele Zeichen dargestellt, wie auf das Display passen. Nach einer einstellbaren Pause wandert die Anzeige eine Zeichenposition weiter. Texte mit kürzerer Länge als die Displaygröße werden mit Leerzeichen aufgefüllt. Parameter:
    • (Zeiger auf den) Text
    • Pause in Millisekunden, bis die Anzeige ein Zeichen vorrückt; Default: 300 ms

Das vierstellige Display ist etwas schmal für Laufschrift – das Programm sollte sich aber leicht an die achtstelligen MAX7219-Displays anpassen lassen, wenn die Bibliothek eine ähnliche Methode wie setSegments() als Unterbau zur Verfügung stellt.

  1. /* Text auf TM1637 als Laufschrift anzeigen
  2.  *
  3.  * 2019-07-20 Heiko (unsinnsbasis.de)
  4.  */
  5.  
  6. #include <TM1637Display.h>
  7.  
  8. #define DISPLAY_SIZE 4  // vereinfacht Portierung auf größere Displays
  9. #define CLK_PIN 33
  10. #define DIO_PIN 32
  11. TM1637Display display(CLK_PIN, DIO_PIN);  // Datenstruktur für Display
  12.  
  13. // Codes für alle auf einer 7-Segment-Anzeige darstellbaren Zeichen
  14. const uint8_t charToSegment[] =
  15.                 // G F E D C B A
  16.                 // HEX digits 0-9, A-F
  17.     { 0x3f,     // 0 1 1 1 1 1 1 - 0 (and uppercase o)
  18.       0x06,     // 0 0 0 0 1 1 0 - 1 (and uppercase i, lowercase l)
  19.       0x5b,     // 1 0 1 1 0 1 1 - 2
  20.       0x4f,     // 1 0 0 1 1 1 1 - 3
  21.       0x66,     // 1 1 0 0 1 1 0 - 4
  22.       0x6d,     // 1 1 0 1 1 0 1 - 5 (and uppercase s)
  23.       0x7d,     // 1 1 1 1 1 0 1 - 6
  24.       0x07,     // 0 0 0 0 1 1 1 - 7
  25.       0x7f,     // 1 1 1 1 1 1 1 - 8 (and uppercase b)
  26.       0x6f,     // 1 1 0 1 1 1 1 - 9
  27.       0x77,     // 1 1 1 0 1 1 1 - A
  28.       0x7c,     // 1 1 1 1 1 0 0 - b
  29.       0x39,     // 0 1 1 1 0 0 1 - C
  30.       0x5e,     // 1 0 1 1 1 1 0 - d
  31.       0x79,     // 1 1 1 1 0 0 1 - E
  32.       0x71,     // 1 1 1 0 0 0 1 - F
  33.                 // additional characters
  34.                 // for words like: Lo, Hi, PUSH (PU5H), PULL, UP...
  35.       0x7f,     // 1 1 1 1 1 1 1 - B (and digit 8)
  36.       0x58,     // 1 0 1 1 0 0 0 - c
  37.       0x76,     // 1 1 1 0 1 1 0 - H
  38.       0x74,     // 1 1 1 0 1 0 0 - h
  39.       0x06,     // 0 0 0 0 1 1 0 - I (and digit 1, lowercase l)
  40.       0x04,     // 0 0 0 0 1 0 0 - i
  41.       0x0e,     // 0 0 0 1 1 1 0 - J
  42.       0x38,     // 0 1 1 1 0 0 0 - L
  43.       0x06,     // 0 0 0 0 1 1 0 - l (and digit 1, uppercase I)
  44.       0x54,     // 1 0 1 0 1 0 0 - n
  45.       0x3f,     // 0 1 1 1 1 1 1 - O (and digit 0)
  46.       0x5c,     // 1 0 1 1 1 0 0 - o
  47.       0x73,     // 1 1 1 0 0 1 1 - P
  48.       0x67,     // 1 1 0 0 1 1 1 - q
  49.       0x50,     // 1 0 1 0 0 0 0 - r
  50.       0x6d,     // 1 1 0 1 1 0 1 - S (and digit 5)
  51.       0x78,     // 1 1 1 1 0 0 0 - t
  52.       0x3e,     // 0 1 1 1 1 1 0 - U
  53.       0x1c,     // 0 0 1 1 1 0 0 - u
  54.       0x6e,     // 1 1 0 1 1 1 0 - y
  55.       0x40,     // 1 0 0 0 0 0 0 - minus
  56.       0x08,     // 0 0 0 1 0 0 0 - underscore
  57.       0x00      // 0 0 0 0 0 0 0 - space
  58.     };
  59. // alle darstellbaren Zeichen (in gleicher Reihenfolge wie oben)
  60. const char *sevenSegChars = "0123456789AbCdEFBcHhIiJLlnOoPqrStUuy-_ ";
  61. // Das Gradsymbol (°) muss extra codiert werden, weil es nicht Standard-ASCII
  62. // ist, sondern UTF-codiert zwei Ausgabezeichen erzeugen würde
  63. const uint8_t codeDegree     = 0x63;  // 1 1 0 0 0 1 1
  64. const uint8_t codeMinus      = 0x40;  // 1 0 0 0 0 0 0
  65. const uint8_t codeUnderscore = 0x08;  // 0 0 0 1 0 0 0
  66. const uint8_t codeBlank      = 0x00;  // 0 0 0 0 0 0 0
  67.  
  68. // encodeChar liefert zu einem Zeichen den 7-Segment-Code zurück.
  69. // Nicht darstellbare Zeichen erzeugen ein Leerzeichen.
  70. //
  71. uint8_t encodeChar(char ch) {
  72.   char *pos;
  73.  
  74.   pos = strchr(sevenSegChars, ch);
  75.   if (pos == NULL) {
  76.     return codeBlank;
  77.   } else {
  78.     return charToSegment[pos - sevenSegChars];
  79.   }
  80. }
  81.  
  82. // Eine Zeichenkette wird in 7-Segment-Codes übersetzt.
  83. // Parameter:
  84. // - Text
  85. // - Referenz auf das Ziel zum Speichern der codierten Zeichen
  86. // - Textlänge (Default: Displaygröße (4))
  87. //
  88. void encodeString(char *src, uint8_t *tgt, int len=DISPLAY_SIZE) {
  89.   int i;
  90.   for (i=0; i<len && i<strlen(src); i++) {
  91.     tgt[i] = encodeChar(src[i]);
  92.   }
  93. }
  94.  
  95. // scrollText erzeugt eine Laufschrift des Textes. Es werden immer so
  96. // viele Zeichen dargestellt wie auf das Display passen. Nach einer
  97. // einstellbaren Pause wandert die Anzeige eine Position weiter.
  98. // Texte mit kürzerer Länge als die Displaygröße werden mit Leerzeichen
  99. // aufgefüllt.
  100. // Parameter:
  101. // - Text
  102. // - Pause in Millisekunden bis zum Anzeigen des nächsten Zeichens
  103. //
  104. void scrollText(char *text, int pause=300) {
  105.   uint8_t codes[DISPLAY_SIZE];
  106.   int i;
  107.  
  108.   if (strlen(text) < DISPLAY_SIZE) {
  109.     encodeString(text, codes, strlen(text));
  110.     // überschüssige Segmente mit Leerzeichen auffüllen
  111.     for (i=strlen(text); i<DISPLAY_SIZE; i++) {
  112.       codes[i] = codeBlank;
  113.     }
  114.     display.setSegments(codes, DISPLAY_SIZE);
  115.     delay(pause);
  116.   } else {
  117.     for (i=0; i<strlen(text)-DISPLAY_SIZE+1; i++) {
  118.       encodeString(text+i, codes, DISPLAY_SIZE);
  119.       display.setSegments(codes, DISPLAY_SIZE);
  120.       delay(pause);
  121.     }
  122.   }
  123. }
  124.  
  125.  
  126. void setup() {
  127.   // Display auf mittl. Helligkeit einstellen
  128.   display.setBrightness(3);
  129. }
  130.  
  131.  
  132. void loop() {
  133.   uint8_t segments[DISPLAY_SIZE];
  134.  
  135.   scrollText("thiS codE iS Funny");
  136.   display.clear();
  137.   delay(1000);
  138.   // Text mit ein paar nicht darstellbaren Zeichen
  139.   scrollText("THIS CODE RunS FAStEr but hAS BLANKS", 200);
  140.   display.clear();
  141.   delay(1000);
  142.   scrollText("thiS runS too FASt to rEAd", 80);
  143.   display.clear();
  144.   delay(1000);
  145.   // kurzer Text
  146.   scrollText("Hi", 1000);
  147.   display.clear();
  148.   delay(1000);
  149.   scrollText("0123456789AbCdEFBcHhIiJLlnOoPqrStUuy");
  150.   display.clear();
  151.   delay(1000);
  152.   // einzelne Segmente mit Zeichen belegen
  153.   segments[0] = codeDegree;
  154.   segments[1] = codeMinus;
  155.   segments[2] = codeBlank;
  156.   segments[3] = codeUnderscore;
  157.   display.setSegments(segments);
  158.   delay(1000);
  159.   display.clear();
  160.   delay(1000);
  161. }

Material und Datenblatt

Die Material-Liste ist kurz. Neben dem ESP32 habe ich verwendet:

  • 1 TM1637-Modul
  • 1 kleines Breadboard
  • 4 Dupont- oder Jumperkabel mit Male/Female-Anschlüssen (Stecker und Buchse)
    (wenn man das Modul ohne Breadboard direkt mit den ESP32-Pins verbindet, benötigt man Female/Female-Kabel)

TM1637 Datenblatt

Fußnoten

Fußnoten
1 Je nach Händler können die Preise auch deutlich höher liegen – ein Preisvergleich kann sich lohnen.
Bestellungen, Service, auch Reklamationen ohne Sprachbarriere, dazu der hiesige Standort mit zusätzlichen Arbeitsplätzen sowie Steuern und Zoll, rechtliche Auflagen wie RoHS und Richtlinien zur Entsorgung von Elektroschrott, Gewährleistung usw. erklären die höheren Preise im Vergleich zu einer Direktbestellung in China über die großen Handelsplattformen. Angeboten werden häufig die gleichen Artikel aus China, für die man dort aufgrund der subventionierten, lächerlich kleinen Versandkosten ohne Mindestbestellwert auch in kleinen Stückzahlen teilweise nur halb so viel bezahlt wie hier, oft noch weniger.
TM1637-Module findet man bspw. schon für ca. 50 Cent plus Versandkosten – dafür muss man mehrwöchige Lieferzeiten einplanen; Reklamationen sind aufwendig, Rücksendungen lohnen eher nicht. Auch Dokumentationen wie Datenblätter muss man selbst suchen; von „vernünftigen” Rechnungen mit Angabe der Mehrwertsteuer ganz zu schweigen. Außerdem wird auf größere Bestellungen aus dem Nicht-EU-Ausland ab 22 € Einfuhrumsatzsteuer fällig und man muss sie beim Zoll abholen – zumindest theoretisch. In der Praxis wird das oft umgangen, indem ein geringerer Warenwert auf der Sendung angegeben wird (und eine Rechnung liegt ja nicht bei). Trotzdem sind die kleineren Preise bei privaten Direktbestellungen natürlich verlockend. Ich will hier für keine der Optionen Werbung machen, sondern nur ein paar Hinweise zur Ursache der Preisunterschiede geben.
2 Eine Anleitung zur Einrichtung der FRITZ!Box als lokaler NTP-Server findet man z.B. beim Hersteller AVM.
3 Per Web-Suche findet man auch andere (meiner Meinung nach umständlichere) Beispiele zur Kommunikation mit NTP-Servern, bei denen erst noch eine NTP-Client-Bibliothek installiert werden muss. Ein solches Beispiel hat Random Nerd Tutorials.
4 Eine Liste der Zeitzonen inkl. Angaben zur Sommerzeit findet sich auf github.com/nayarsystems/posix_tz_db/blob/master/zones.csv. Den Hinweis darauf habe ich auf fipsok.de gefunden.
5 Sauberer wäre es natürlich, die Klasse display um Methoden zu erweitern, die diesen Funktionen entsprechen. Vielleicht später einmal… ;-)

Kommentar hinterlassen

Deine E-Mail-Adresse wird nicht veröffentlicht.