NTP-Uhr


Network Time Protokoll (NTP) – Die Atomuhr des Internets

NTP ist die Abkürzung von Network Time Protokoll. Der Client, in unserem Fall die Uhr bzw. der auf der Platine verbaute ESP8266, holt sich die aktuelle Zeit von einem Zeitserver aus dem Internet. Bei guter Anbindung ist damit eine Ganggenauigkeit von 10 Millisekunden möglich.

Der oben erwähnte Mikrocontroller (ESP8266) ist also das Herzstück der Uhr. Dazu noch ein Display für die Stunden- bzw. Minutenanzeige und ein LED-Pixelring für die Anzeige der Sekunden. In der Vergangenheit hätte die Herstellung des LED-Ringes einen großen Aufwand an Bohr- und Lötarbeiten bedeutet. Aber Dank NEOPIXEL reichen bereits 3 Drähte, um die 60 LEDs anzusteuern.

Die Einkaufsliste ist kurz

• 1 WEMOS D1 mini => 1,50€
• 1 MAX7219 Dot Matrix Modul (4-stellig) => Rot 2,30€ oder Grün 3,30€
• 1 WS2812B 60er LED-Pixelring => 9,60€ (aufpassen, es gibt sie preiswerter, aber dann kommen 4 Teile, die man zum Ring löten muss)
• 1 Fotowiderstand LDR 5516 => (aus der Bastelkiste oder 50 Teile mit 5 verschiedenen Typen für 1,21€)
• 1 Widerstand 10K => je nach LDR-Typ andere Werte möglich (aus der Bastelkiste oder kleines Sortiment kaufen)

Also mit 15-20€ muss man rechnen. Die Preise können sich aber ändern.
Bauteil-Sammlung

Maße in mm
LED-Ring: ⌀ Innen 156, ⌀ Außen 172, Höhe Platine 1,7 + LED 1,7 = 3,4 dazu 4 Bohrungen ⌀ 2 im Abstand von 15 LEDs
Dotmatrix: 132 x 32, 14,5 hoch, Bohrung ⌀ 3,5
WEMOS: 34,5 x 25,6, 5,3 hoch, Bohrung ⌀ 2,5
Wenn man von den 14,5 mm Höhe der Dotmatrix die rund 3,5 mm des LED-Rings abzieht, kommt man auf 11 mm Abstandshalter zu befestigen des LED-Rings. Für den WEMOS reichen 3 mm (Auf der Unterseite ist noch ein Chip) oder besser 6 mm, da ist mehr Platz für die Verdrahtung auf der Unterseite.

© 3/2021


Die NTP-Uhr wird zur Wetterstation

Ich konnte es mir nicht verkneifen und habe noch einen BME280 hinzugefügt. Damit wird die Uhr zur Wetterstation, denn es stehen Temperatur, Luftfeuchte und Luftdruck zur Verfügung.
Um den Luftdruck auf Meereshöhe (DruckNN=NormalNull) zu erhalten, addiert man einfach 1 hPa je 8m Höhe dazu oder verwendet die etwas genauere Formel:
DruckNN = ((Druck) / pow((1 - ((float)(alititude in m)) / 44330), 5.255));
Mit einer zusätzlichen Variable im Programmcode merkt man sich jede halbe Stunde den Luftdruck und vergleicht ihn mit dem aktuellen Luftdruck.
if(min % 30 == 0) merkeDruck = (merkeDruck + aktuellerDruck) / 2;
Somit kann man zusätzliche Infos wie steigend↑, fallend↓ und gleichbleibend→ ausgeben.
© 4/2021


Der erste Prototyp

Zum BME280 (ca. 4,50€) kam noch ein SGP30 (Luftgütesensor ca. 5,50€).
Beide Sensoren werden über den I2C-Bus ausgelesen. Damit sind die Sensoren im Grunde genommen parallel geschalten.
Man kann dem SGP30 die Werte vom BME280 (Temperatur, Luftfeuchte) zuführen.
Damit erzielt man eine höhere Genauigkeit bei der Berechnung der Luftgüte.
Der SGP30 färbt mittels seiner Werte den Sekundenzeiger entsprechend der Luftgüte von grün über gelb zu rot.

Sensoren

BME280-Sensor
BME280-Sensor

SGP30-Sensor
SGP30-Sensor



Leider werden die Farben des Sekundenzeigers im Video nicht so optimal dargestellt, wie sie in Wirklichkeit sind.
Ich habe aller 15s eine rote LED, aller 5 s eine blaue LED. Alle anderen sind grün.
Da man dies per Software einstellen kann, kann man beliebige Farben verwenden.

Hauptplatine

Der direkt auf der WEMOS-Platine verlötete Fotowiderstand regelt die Helligkeit der Dot-Matrix und des LED-Pixelringes.

Wemos D1
Wemos D1 mit LDR5516 (Fotowiderstand)

Pixelring

Auch die Art und Weise des Sekundenzeigers kann man per Software ändern. So füllt sich beispielsweise von 22 Uhr bis 6 Uhr der Sekundenkreis nicht mehr, sondern es leuchtet immer nur die aktuelle Sekunde. Damit verringert sich die Helligkeit in den Nachtstunden zusätzlich.

WS2812B
WS2812B LED-Pixelring Ausschnitt
WS2812B Detail
WS2812B LED-Pixelring Detail
Software

In der Soft- bzw. Firmware habe ich ein Webinterface integriert, welches man über die IP-Adresse der Uhr erreichen kann.
Dort sind detaillierte Infos und Sensorwerte enthalten.

Weiteres folgt wahrscheinlich nicht mehr, da dazu weitere Hardwareänderungen notwendig wären.
Da man Softwareänderungen über die OTA-Schnittstelle (Over The Air) übertragen kann, wird es in Zukunft sicher noch ein bisschen Codeoptimierung geben.

Am Ende mein Dank an alle Entwickler, die auf GitHub all die Bibliotheken zur Verfügung stellen und damit vielen bei der Entwicklung eigener Ideen unterstützen.
© 5/2021


Update Preise:

Seit 1. Juli 2021 wird in Fernost die deutsche Einfuhrumsatzsteuer während des Bestellprozesses angezeigt und mit berechnet. Auf die obigen Preise sind also jeweils 19% aufzuschlagen. Des Weiteren werden immer öfter Versandkosten berechnet bzw. im Artikelpreis versteckt. Was am Ende nicht relevant ist, da auch Versandkosten steuerpflichtig sind.
© 7/2021


Serienfertigung für den

Die erste Kleinserie ist fertig und wird vom Weihnachtsmann ausgeliefert 🙂
Jede Uhr hat einen anderen Geburtstags/Terminkalender so das die Berechnungszeit der nächsten Termine unterschiedlich lang dauert. Deshalb der etwas asynchrone Bootvorgang.
Falls die Beschenkten die Bedienungsanleitung mal nicht zur Hand haben sollten, könnt ihr auch hier nachlesen.

Preisupdate:

Mittlerweile sind die Chip- und Versandkosten stark gestiegen. Die Beschaffungskosten des Luftgütesensors (SGP30) haben sich fast verdoppelt. Er kostet jetzt fast 10€.  Auch der Preis für den Temperatursensor (BME280) ist stark gestiegen. Beide Sensor-Chips werden von Bosch hergestellt. Andere Bauteile, wie der LED-Pixelring, sind nur moderat gestiegen.

Unnützes Wissen:

Im Mai 2021 bekam die ESP8266-Entwicklungsumgebung ein lang ersehntes Update. Es wurde die Zeit- bzw. Datumsberechnung von 32 auf 64-Bit umgestellt. Vor dieser Umstellung, wäre die Uhr am 19.01.2038 stehen geblieben bzw. hätte sinnlose Zeiten angezeigt. Die 2.147.483.647 (231-1) Sekunden seit dem 01.01.1970 wären vorbei und es käme zum 32-Bit-Ganzzahl-Zählerüberlauf.

Aber dies war nicht die einzige Gefahr. Am 7. Februar 2036 um 06:28:16 Uhr UTC wäre der Zähler des Zeitsynchronisations-Protokolls NTP (Network Time Protocol) übergelaufen. Jedoch wurde mit Einführung des NTP-Protokolls Version 4 das Problem behoben (im Jahr 2010).

Aber keine Angst, mit der 64-Bit-Umstellung ist man jetzt auf der ganz sicheren Seite. Damit sind Berechnungen bis zu 292 Milliarden Jahren möglich (Das Universum ist zurzeit ca. 15 Milliarden Jahre alt). Selbstverständlich erinnert euch die Uhr an diese IT-technisch wichtigen Ereignisse.
© 12/2021


Source code snippet:

Es gibt nur zwei bewegliche Tage/Feiertage, die man mit etwas höheren Aufwand berechnen muss.

#define secDay 86400 // 1 Tag = 86.400,003 Atomsekunden, auf Grund
                     // ständiger Schwankungen der Erdrotation
                     // alle 2-5 Jahre eine Schaltsekunde

// Ostersonntag
int getOstern(int jahr) {
  int k, m, s, a, d, r, og, sz, oe, os, tag;
  k = jahr / 100;
  m = 15 + (3 * k + 3) / 4 - (8 * k + 13) / 25;
  s = 2 - (3 * k + 3) / 4;
  a = jahr % 19;
  d = (19 * a + m) % 30;
  r = d / 29 + (d / 28 - d / 29) * a / 11;
  og = 21 + d - r;
  sz = 7 - ((jahr + jahr / 4 + s) % 7);
  oe = 7 - ((og - sz) % 7);
  os = og + oe;
  if (os <= 31) {
    tag = time2sek(jahr, 3, os, 0, 0, 0, 0);
  } else {
    tag = time2sek(jahr, 4, os - 31, 0, 0, 0, 0);
  }
  if (debug) {
    Serial.print(F("getOstern: ")); Serial.println(DateFormatter::format("%A %d.%m.%Y", tag));
  }
  return tag;
}

// Erster Advent
int getErsterAdvent(int jahr) {
  int heiligabend = time2sek(jahr, 12, 24);
  int wt = DateFormatter::format("%w", heiligabend).toInt();
  int tage = wt + 21;
  int tag = heiligabend - (tage * secDay);
  if (debug) {
    Serial.print(F("getErsterAdvent: ")); Serial.println(DateFormatter::format("%A %d.%m.%Y", tag));
  }
  return tag;
}

// Umrechnung Zeit in Sekunden
uint64_t time2sek(int i_year, byte b_month, byte b_day, byte b_hour = 0, byte b_min = 0, byte b_sec = 0, byte b_dst = 0) {
  struct tm zeit;
  zeit.tm_year = i_year - 1900;
  zeit.tm_mon = b_month - 1;
  zeit.tm_mday = b_day;
  zeit.tm_hour = b_hour;
  zeit.tm_min = b_min;
  zeit.tm_sec = b_sec;
  zeit.tm_isdst = b_dst;
  return mktime(&zeit);
}

Weitere bewegliche Feiertage orientieren sich alle an Ostersonntag oder dem 1. Advent.
Fastnacht = Ostersonntag – 47
Himmelfahrt = Ostersonntag + 39
Pfingstsonntag = Ostersonntag + 49
Buß- und Bettag = 1. Advent – 11

Schaltjahrberechnung:

Der Perfektion geschuldet, hier die Berechnung der Schaltjahre, obwohl für dieses Jahrhundert eigentlich nur noch die Regel gilt, alle Jahre die durch vier teilbar sind, sind Schaltjahre.

bool isSchaltjahr(int jahr) {
  // Alle Jahre, die durch 4 teilbar sind, sind ein Schaltjahr.
  // Es sei denn, das Jahr ist durch 100 teilbar, dann ist es kein Schaltjahr.
  // Aber, wenn das Jahr durch 400 teilbar ist, dann ist es doch wieder ein Schaltjahr.

  if ((jahr % 400) == 0) {
    return true;
  } else if ((jahr % 100) == 0) {
    return false;
  } else if ((jahr % 4) == 0) {
    return true;
  } else {
    return false;
  }
  //return (jahr % 4 == 0 && jahr % 100 != 0 || jahr % 400 == 0); // Kurzform
}

© 3/2024


Die nächste Kleinserie

Wegen ein paar runder Geburtstage, habe ich die nächsten drei hardwaregleichen Uhren zusammengelötet.

Preisupdate:

Die Preise sind gegenüber 2021 wieder gefallen.
• 1 Wemos D1 MINI V3.0 => 2,50€
• 1 WS2812B 60er LED-Pixelring => 9,50€
• 1 MAX7219 Dot Matrix (4-stellig, grün) => 4,50€
• 1 SGP30 => 7€
• 1 BME280 => 3€

Bedienungsanleitung überarbeitet.

Die Uhr verbraucht ca. 1,8 Watt. Daraus ergibt sich ein Jahresverbrauch von ca. 16 kWh.
16 kWh x 0,31 €/kWh = 4,96 € Jahreskosten Stromverbrauch.

© 3/2025