Arduino UNO R4 Projekt: Weltzeituhr mit LCD Keypad Shield
Wie du das LCD Keypad Shield programmierst, habe ich dir bereits im Beitrag LCD Keypad Shield für Arduino: Einsteigerfreundliches Display mit Tastensteuerung ausführlich erläutert. In diesem Beitrag soll es nun darum gehen, wie du dir eine Weltzeituhr am Arduino UNO R4 programmierst. Der Arduino UNO R4 verfügt über einen ESP32 Chip und mit dem Formfaktor des alten UNO R3 ist dieser bestens geeignet für dieses Projekt. https://youtu.be/uCFm0KZOZh8 Der Aufbau einer Schaltung ist recht einfach, denn das LCD Keypad Shield wird lediglich auf den Mikrocontroller gesteckt.
Das LCD Display verfüg über zwei Zeilen mit je 16 Zeichen, das ist für die Anzeige von Datum, Uhrzeit und der Zeitzone etwas wenig, daher habe ich das auf den Tag, die Uhrzeit und die Zeitzone in Kurzform komprimiert.
Benötigte Ressourcen für dieses kleine Projekt
Wenn du das kleine Projekt nachbauen möchtest, dann benötigst du: - einen Arduino UNO R4 WiFi*, - ein USB-C Datenkabel*, - ein LCD Keypad Shield* Hinweis von mir: Die mit einem Sternchen (*) markierten Links sind Affiliate-Links. Wenn du über diese Links einkaufst, erhalte ich eine kleine Provision, die dazu beiträgt, diesen Blog zu unterstützen. Der Preis für dich bleibt dabei unverändert. Vielen Dank für deine Unterstützung!
NTP Timeserver für die Abfrage von Zeiten
Es gibt im Internet einige NTP Timeserver welche für dieses Projekt verwendet werden können, für dieses Projekt verwende ich ptbtime1.ptb.de von der Physikalisch-technische Bundesanstalt. Wir müssen lediglich für die jeweilige Zeitzone noch Stunden dazu oder abzählen. Liste mit Zeitzonen und TimeOffsets Nachfolgend gebe ich dir eine Tabelle, aus welcher du entnehmen kannst, welchen Offset du für die Zeit verwenden musst. Rechenbeispiele: - für Pacific Standard Time (PST) musst du von der aktuellen Uhrzeit -8h rechnen, - für Japan Standard Time (JST) musst du von der aktuellen Uhrzeit +9h rechnen KontinentZeitzoneUTC ZeitNordamerikaEastern Standard Time (EST)UTC-5Central Standard Time (CST)UTC-6Mountain Standard Time (MST)UTC-7Pacific Standard Time (PST)UTC-8Alaska Standard Time (AKST)UTC-9Hawaii-Aleutian Standard Time (HAST)UTC-10SüdamerikaBrasília Time (BRT)UTC-3Argentina Standard Time (ART)UTC-3Chile Standard Time (CLT)UTC-4Venezuela Standard Time (VET)UTC-4:30EuropaGreenwich Mean Time (GMT)UTC+0Central European Time (CET)UTC+1Eastern European Time (EET)UTC+2British Summer Time (BST)UTC+1AfrikaWest Africa Time (WAT)UTC+1Central Africa Time (CAT)UTC+2East Africa Time (EAT)UTC+3South Africa Standard Time (SAT)UTC+2AsienIndian Standard Time (IST)UTC+5:30China Standard Time (CST)UTC+8Japan Standard Time (JST)UTC+9Australian Eastern Standard Time (AEST)UTC+10Australian Central Standard Time (ACST)UTC+9:30Australian Western Standard Time (AWST)UTC+8OzeanienNew Zealand Standard Time (NZST)UTC+12Fiji Standard Time (FST)UTC+12Tonga Standard Time (TST)UTC+13
Programmieren einer Weltzeituhr mit einem NTP Zeitserver
Wie du dich mit einem Mikrocontroller zu einem WiFi Netzwerk verbindest, habe ich dir bereits in einigen Beiträgen ausführlich erläutert. Jedoch nehme ich dich in der nachfolgenden Schritt-für-Schritt-Anleitung an die Hand, sodass du alle Informationen in diesem Beitrag findest. Schritt 1 - Installation der benötigten Bibliotheken Für die Abfrage der Zeit aus dem Internet benötigen wir eine Bibliothek, ich verwende hier NTPClient welche als ZIP-Datei vom GitHub Repository arduino-libraries/NTPClient heruntergeladen werden kann. Schritt 2 - Aufbau einer WiFi Verbindung Im ersten richtigen Schritt programmieren wir die WiFi Verbindung, dieses ist recht einfach und mit wenigen Zeilen Code erledigt. //Bibliotheken zum aufbau einer WiFi Verbindung #include #include //Die Zugangsdaten zum WiFi-Netzwerk const char *ssid = "*****"; const char *password = "*****"; void setup() { //beginn der seriellen Kommunikation Serial.begin(115200); //Aufbau der WiFi Verbindung WiFi.begin(ssid, password); //solange noch keine Verbindung hergestellt wurde, soll //eine Pause von 500ms eingelegt werden, //ein Punkt auf der seriellen Schnittstelle ausgegeben werden while (WiFi.status() != WL_CONNECTED) { delay(500); Serial.print("."); } //Wenn die Verbindung erfolgreich aufgebaut wurde, dann //wird der nchfolgende Text angezeigt. Serial.print("Erfolgreich zu "); Serial.print(ssid); Serial.println(" verbunden!"); } void loop() { //bleibt leer } Wenn die Verbindung zum Netzwerk hergestellt wurde, dann erscheint auf der seriellen Schnittstelle die Ausgabe vom Text "Erfolgreich zu verbunden!". Schritt 3 - Abfrage einer Zeit von einem NTPServer Wie erwähnt möchte ich die Daten vom Zeitserver der Physikalisch-technische Bundesanstalt abrufen. Die Adresse von diesem ist ptbtime1.ptb.de. //Bibliotheken zum aufbau einer WiFi Verbindung #include #include //Die Zugangsdaten zum WiFi-Netzwerk const char *ssid = "*****"; const char *password = "*****"; void setup() { //beginn der seriellen Kommunikation Serial.begin(115200); //Aufbau der WiFi Verbindung WiFi.begin(ssid, password); //solange noch keine Verbindung hergestellt wurde, soll //eine Pause von 500ms eingelegt werden, //ein Punkt auf der seriellen Schnittstelle ausgegeben werden while (WiFi.status() != WL_CONNECTED) { delay(500); Serial.print("."); } //Wenn die Verbindung erfolgreich aufgebaut wurde, dann //wird der nchfolgende Text angezeigt. Serial.print("Erfolgreich zu "); Serial.print(ssid); Serial.println(" verbunden!"); } void loop() { //bleibt leer } Die Ausgabe auf der seriellen Schnittstelle ist wiefolgt: - erste Zeile, der Hinweis das die WiFi-Verbindung aufgebaut wurde, - zweite bis n-te Zeile, die Uhrzeit vom NTPServer
Wenn noch keine Verbindung zum Server hergestellt wurde, dann kann auch einmal die Uhrzeit mit 01:00:00 geliefert werden. Hier muss man lediglich ein paar Sekunden warten. In der Grafik ist ersichtlich, dass die Uhrzeit um eine Stunde verschoben ist, bedingt durch die europäische Sommerzeit. Um dies anzupassen, wird die Funktion setTimeOffset am Objekt timeClient aufgerufen, welches in der setup-Funktion initialisiert wird. Dabei wird der Funktion einmalig ein Parameter von 7200 Sekunden übergeben, was zwei Stunden entspricht. timeClient.setTimeOffset(7200); Schritt 4 - Ausgeben der Daten auf dem LCD Display Das LCD Display hat maximal 16 Zeichen für je zwei Zeilen zur Verfügung, anders als bei OLED Displays kann man hier nicht durch die Auswahl einer kleineren Schriftart für mehr Platz sorgen. Man muss quasi Abstriche machen. Damit wir mit dem LCD Display kommunizieren können, importieren wir die LiquidCrystal Bibliothek welche bei der Arduino IDE 2.x dabei ist. //Bibliotheken zum aufbau einer WiFi Verbindung #include #include //Bibliothek zum kommunizieren mit einem Zeitserver #include //Bibliothek zum kommunizieren mit dem LCD Display #include //Objekt vom Typ LiquidCrystal initialisieren LiquidCrystal lcd(8, 9, 4, 5, 6, 7); //Die Zugangsdaten zum WiFi-Netzwerk const char *ssid = "*****"; const char *password = "******"; WiFiUDP ntpUDP; NTPClient timeClient(ntpUDP, "ptbtime1.ptb.de", 3600, 60000); void setup() { //beginn der seriellen Kommunikation Serial.begin(115200); //Aufbau der WiFi Verbindung WiFi.begin(ssid, password); //solange noch keine Verbindung hergestellt wurde, soll //eine Pause von 500ms eingelegt werden, //ein Punkt auf der seriellen Schnittstelle ausgegeben werden while (WiFi.status() != WL_CONNECTED) { delay(500); Serial.print("."); } //Wenn die Verbindung erfolgreich aufgebaut wurde, dann //wird der nchfolgende Text angezeigt. Serial.print("Erfolgreich zu "); Serial.print(ssid); Serial.println(" verbunden!"); //beginn der Kommunikation mit dem Zeitserver timeClient.begin(); //Zeitoffset von 1h für die europäische Sommerzeit timeClient.setTimeOffset(7200); //Das verbaute LCD Display hat 16 Zeichen mit zwei Zeilen. //Die Bibliothek kann für viele weitere verwendet werden, daher müssen //diese Daten übergeben werden. lcd.begin(16, 2); } void loop() { //aktualisieren der Daten timeClient.update(); //Ausgeben eines formatierten Zeitstempels String formattedTime = timeClient.getFormattedTime(); Serial.println(formattedTime); //Daten im LCD Display leeren lcd.clear(); //Anzeigen der Uhrzeit lcd.print(formattedTime); //eine Pause von 1 Sekunde delay(1000); } Die Uhrzeit wird nun nicht nur auf der seriellen Schnittstelle ausgegeben, sondern auch auf dem LCD Display.
Schritt 5 - Anzeigen von unterschiedlichen Weltzeiten auf dem Display Im nächsten Schritt wollen wir jetzt eine Liste von Weltzeiten anlegen und diese nacheinander anzeigen lassen. In der zweiten noch leeren Zeile möchte ich die Zeitzone und die Differenz anzeigen lassen. Weltzeituhr am Arduino UNO R4 WiFi mit dem LCD Keypad ShieldHerunterladen Am einfachsten geht dieses mit einem MenuItem welches nachfolgende Eigenschaften hat: struct MenuItem { double timeOffset; //die Zeit welche abgezogen/addiert werden soll String timezone; //die Bezeichnung der Zeitzone }; Von diesem neuen Datentyp MenuItem können wir uns nun beliebig viele anlegen und in ein Array ablegen. MenuItem item1 = { 1, "CEST (UTC +2)" }; //Central European Summer Time (CEST) MenuItem item2 = { 0, "GMT (UTC +0)" }; //Greenwich Mean Time (GMT) MenuItem item3 = { -4, "CLT (UTC -4)" }; //Chile Standard Time (CLT) MenuItem item4 = { 12, "NZST (UTC +12)" }; //New Zealand Standard Time (NZST) MenuItem item5 = { 9.5, "ACST (UTC +9:30)" }; //Australian Central Standard Time (ACST) MenuItem item6 = { -10, "HAST (UTC -10)" }; //Hawaii-Aleutian Standard Time (HAST) const int NUM_MENUITEMS = 6; MenuItem menu = { item1, item2, item3, item4, item5, item6 }; Wir entnehmen ein MenuItem mit dem aktuellen index aus dem Array. Im Anschluss berechnen wir den Offset. Dieser Offset wird in Sekunden übergeben (1h = 3600 Sekunden). Die nächsten Zeilen dienen dann lediglich um den Zeitstempel wie zuvor zu laden und auf dem Display anzuzeigen. MenuItem item = menu; double timeOffset = item.timeOffset * 3600; timeClient.setTimeOffset(timeOffset); //aktualisieren der Daten timeClient.update(); //Ausgeben eines formatierten Zeitstempels String formattedTime = timeClient.getFormattedTime(); Serial.println(formattedTime); //Daten im LCD Display leeren lcd.clear(); //Anzeigen der Uhrzeit lcd.print(formattedTime); //Anzeigen der Zeitzone lcd.setCursor(0, 1); lcd.print(item.timezone); Die Weltzeiten sollen immer jeweils für 5 Sekunden angezeigt werden, dafür nutze ich eine bekannte Lösung mit einer Pause ohne Delay. Da ich auf die Werte im Array mit der Variable index zugreife erhöhe ich diese damit alle 5 Sekunden jedoch läuft das Programm im Hintergrund weiter. long lastAction = -1; const long PAUSE = 3000; //Alle 5 Sekunden die Zeitzone wechseln long currentMillis = millis(); if (lastAction + PAUSE < currentMillis) { lastAction = currentMillis; if (index < NUM_MENUITEMS-1) { index++; } else { index = 0; } }
Arbeiten mit dem Timestamp
Von der Bibliothek erhalten wir mit der Funktion getEpochTime() einen Zeitstempel in Sekunden. Dieser repräsentiert die vergangenen Sekunden seit dem 01.01.1970. Aus diesem können wir zusätzlich auch das Datum berechnen und somit auf dem Display zusätzlich das Datum anzeigen. Ich habe hier bei der Uhrzeit die Sekunden abgeschnitten und das Jahr auf zweistellig gekürzt.
Das fertige Programm kannst du nachfolgend herunterladen: Anzeige von Datum & Uhrzeit von verschiedenen Zeitzonen am LCD Keypad Shield mit dem Arduino UNO R4 WiFiHerunterladen // Funktion zur Umwandlung eines Unix-Timestamps in ein Datum String formatiereDatum(unsigned long timestamp) { unsigned long days = timestamp / 86400; // Tage seit dem Unix-Epoch unsigned long years = 1970; // Beginn des Unix-Epoch unsigned long remainingDays = days; // Jahr berechnen while (remainingDays >= 365) { if (istSchaltjahr(years)) { remainingDays -= 366; } else { remainingDays -= 365; } years++; } // Monat und Tag berechnen unsigned long months = 0; unsigned long monthLength = 0; while (remainingDays > monthLength) { monthLength = tageImMonat(months, years); remainingDays -= monthLength; months++; } return zweistellig(remainingDays + 1) + "." + zweistellig(months + 1) + "." + String(years % 100); } // Funktion zur Überprüfung, ob ein Jahr ein Schaltjahr ist bool istSchaltjahr(unsigned long year) { return (year % 4 == 0 && year % 100 != 0) || (year % 400 == 0); } // Funktion zur Rückgabe der Anzahl der Tage in einem Monat unsigned long tageImMonat(unsigned long month, unsigned long year) { if (month == 1) { // Februar if (istSchaltjahr(year)) { return 29; } else { return 28; } } else if (month == 3 || month == 5 || month == 8 || month == 10) { // April, Juni, September, November return 30; } else { return 31; } } // Funktion zur Umwandlung eines Unix-Timestamps in eine Uhrzeit String formatiereUhrzeit(unsigned long timestamp) { unsigned long minutes = (timestamp % 3600) / 60; unsigned long hours = (timestamp % 86400) / 3600; return zweistellig(hours) + ":" + zweistellig(minutes); } // Funktion zur Formatierung von Zahlen auf zwei Stellen String zweistellig(unsigned long zahl) { if (zahl < 10) { return "0" + String(zahl); } else { return String(zahl); } } Read the full article














