In diesem Beitrag zeige ich dir, wie du eine flexible Timer-Schaltung mit der Real-Time-Clock DS1302 und einem OLED-Display, das über ein Drehreglermodul gesteuert wird, programmierst. Die Schaltung ist mit Arduino-kompatiblen Mikrocontrollern nutzbar und bietet dir die Möglichkeit, individuelle Alarme bequem und flexibel einzustellen. In einem früheren Beitrag habe ich bereits erläutert, wie du das OLED-Display und den Drehregler in der Arduino IDE programmierst, um feste Zeiten zu setzen. Hier gehen wir nun einen Schritt weiter und gestalten eine flexiblere Lösung, die sich ideal für Projekte eignet, bei denen sich die Alarmzeiten regelmäßig anpassen lassen.
Mit dieser Schaltung lassen sich tägliche und wiederkehrende Alarme intuitiv und ohne großen Programmieraufwand festlegen – perfekt für DIY-Projekte, die eine dynamische Zeitsteuerung erfordern.
Die in dieser Schaltung verwendeten Module, die RTC DS1302 und das OLED-Display mit Drehregler, habe ich bereits in früheren Beiträgen auf diesem Blog vorgestellt und verschiedene Anwendungsbeispiele gezeigt:
- DS1302 RTC mit SPI-Schnittstelle: So programmierst du die Echtzeituhr am Arduino
- OLED Display und Drehregler: Programmieren in der Arduino IDE
Inhaltsverzeichnis
- Wie soll die Timer-Schaltung am Arduino funktionieren?
- Aufbau der Schaltung – RTC DS1302 mit OLED-Display Modul am Arduino Mikrocontroller
- Programmieren in der Arduino IDE
- Schritt 1 – installieren der benötigten Bibliotheken für die RTC, OLED-Display & Drehregler
- Schritt 2 – Definieren der Pins & Initialisieren der Objekte
- Schritt 3 – Felder & Variablen definieren
- Schritt 4 – Funktion setup
- Schritt 5 – Funktion zum zeichnen des Hintergrundes
- Schritt 6 – Funktion zum anzeigen des aktuellen Datums und der Uhrzeit auf dem Display
- Schritt 7 – Funktion zum anzeigen des eingestellten Weckers
- Schritt 8 – Funktion zum zeichnen eines Rechtecks zum anzeigen der Bearbeitung
- Schritt 9 – Funktion zum Anzeigen des Textes “!! Alarm !!” auf dem Display
- Schritt 10 – Funktion zum aktivieren des Alarms und ausgeben eines Tones
- Schritt 11 – Funktion loop zum behandeln der Aktionen am Drehrehregler und Taster
- Fertiges Programm – RTC DS1302 Timer-Schaltung mit OLED-Display am Arduino
- Erweiterung um ein Relais Modul
Wie soll die Timer-Schaltung am Arduino funktionieren?
Die Timer-Schaltung basiert auf einer RTC DS1302, die als Zeitgeber dient und an den Arduino angeschlossen ist. Über einen Drehregler am OLED-Display lässt sich die gewünschte Alarmzeit einstellen. Zu dieser eingestellten Zeit gibt die Schaltung ein Signal an einen kleinen Piezo-Buzzer aus, der den Alarm akustisch meldet. Alternativ lässt sich der Buzzer durch ein Relais ersetzen, um zu bestimmten Zeiten andere Geräte automatisch zu schalten.
Aufbau der Schaltung – RTC DS1302 mit OLED-Display Modul am Arduino Mikrocontroller
Beginnen wir mit dem Aufbau der Schaltung: Ich habe sie kompakt auf einem 400-Pin-Breadboard realisiert. Da sowohl das OLED-Modul als auch die RTC 5V benötigen, ist das Breadboard unverzichtbar, weil der Mikrocontroller nur über einen 3,3V- und einen 5V-VCC-Ausgang verfügt.
Für den Aufbau benötigst du:
- einen Arduino kompatiblen Mikrocontroller,
- einen Arduino Nano V3*, oder
- einen Maker Nano
- ein passendes USB-Datenkabel*,
- eine RTC DS1302*,
- einen Piezo Buzzer*,
- ein OLED-Display Modul mit Drehregler*
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!
Falls du das hier verwendete OLED-Display-Modul mit integriertem Drehregler nicht zur Verfügung hast, kannst du die Bauteile auch separat anschließen. Achte jedoch später im Code darauf, dass dieses Modul den Treiberchip SH11X verwendet und nicht den gängigeren SSD1306.
Programmieren in der Arduino IDE
Schritt 1 – installieren der benötigten Bibliotheken für die RTC, OLED-Display & Drehregler
Die RTC DS1302, der Drehregler und das OLED-Display erfordern zusätzliche Bibliotheken, die wir zunächst installieren, bevor wir mit der Programmierung beginnen.
Zur Steuerung der RTC DS1302 nutzen wir die VirtuabotixRTC-Bibliothek, die als ZIP-Datei im GitHub-Repository von chrisfryer78/ArduinoRTClibrary verfügbar ist.
Für den Rotary Encoder gibt es verschiedene Bibliotheken. Ich habe gute Erfahrungen mit der Version von CarlosSiles67 gemacht, die du unter CarlosSiles67/Rotary herunterladen kannst.
Das OLED-Display wird durch den Treiberchip SH1106 betrieben, weshalb wir eine spezielle Bibliothek benötigen. Diese findest du entweder im GitHub-Repository von Adafruit oder direkt im Bibliotheksverwalter der Arduino IDE unter dem Namen „Adafruit SH110x“.
Schritt 2 – Definieren der Pins & Initialisieren der Objekte
Im ersten Schritt im Code laden wir die benötigten Bibliotheken und definieren die Pins welche mit dem Mikrocontroller verbunden sind.
#include <virtuabotixRTC.h> #include <rotary.h> #include <SPI.h> #include <Wire.h> #include <Adafruit_GFX.h> #include <Adafruit_SH110X.h>
Für das kleine Programm verwende ich drei Schriftarten wobei eine davon die default ist, andere coole Schriftarten findest du auch unter der Adresse https://learn.adafruit.com/adafruit-gfx-graphics-library/using-fonts.
#include <Fonts/FreeSans18pt7b.h> #include <Fonts/FreeSerif12pt7b.h>
Definieren der Pins vom Drehregler, den Tasten, RTC und des Piezo Buzzers.
#define pushPin 9 #define traPin 10 #define trbPin 11 #define confirmPin 5 #define backPin 4 #define clkPin 6 #define datPin 7 #define rstPin 8 #define buzzerPin 13
Initialisieren der Objekte
virtuabotixRTC rtcDS1302(clkPin, datPin, rstPin); Rotary rotary = Rotary(traPin, trbPin, pushPin); #define i2c_Address 0x3c Adafruit_SH1106G display = Adafruit_SH1106G(128, 64, &Wire, -1);
Schritt 3 – Felder & Variablen definieren
Für das speichern der Zustände benötigen wir einige Felder.
//Konstante für die Pause zwischen dem umschalten der Zeit //wir zeigen auf dem Display lediglich Minutengenau an //daher ist der Takt 1 Minute (60000 Millisekunden) const unsigned long PAUSE = 60000; //Feld zum speichern des Zeitstempels der letzten ausführung unsigned long lastAction = -1; //Feld zum speichern des Zeitstempels wann der Alarm das //letzte Mal ertönt hat. Damit wird ein Konstanter Ton unterbunden. unsigned long lastActionAlarm = -1; const int PAUSE_ALARM = 500; //Zeit für den Wecker int stunden = 12; int minuten = 34; //Felder zum zwischenspeichern der Werte //diese werden später in die "echten" Felder //überschrieben int tempStunden = stunden; int tempMinuten = minuten; //Felder zum speichern ob der Alarm aktiviert ist, //ob gerade die Zeit eingestellt wird bool setAlarm = false; bool isAlarmActive = false; bool isAlarmDeactivated = false; //Welcher Bestandteil der Zeit (Stunde oder Minute) //soll bearbeitet werden int alarmTimeIndex = 0;
Schritt 4 – Funktion setup
Die Funktion setup wird einmalig beim starten des Mikrocontrollers ausgeführt. Hier konfigurieren wir die Pins einmalig.
//Diese Funktion wird einmalig beim Starten / neustarten //des Mikrocontrollers ausgeführt. void setup() { //beginn der seriellen Kommunikation mit 9600 baud Serial.begin(9600); //Pin des Taster BACK als Eingang definieren pinMode(backPin, INPUT); //Pin des Taster CONFIRM als Eingang definieren pinMode(confirmPin, INPUT); //Pin des Piezo Buzzers als Ausgang definieren pinMode(buzzerPin, OUTPUT); //Initialisieren des Displays display.begin(i2c_Address, true); //leeren des Display display.clearDisplay(); //Zeichnen des Hintergrundes drawBackground(); //Anzeigen der aktuellen Uhrzeit updateTime(); //Anzeigen des Weckers displayTime(stunden, minuten); }
Schritt 5 – Funktion zum zeichnen des Hintergrundes
Für den Hintergrund zeichnen wir einmal ein abgerundetes Rechteck sowie eine Linie.
//Funktion zum zeichnen des Hintergrundes void drawBackground() { //Ein abgerundetes Rechteck zeichnen display.drawRoundRect(2, 2, 125, 62, 5, SH110X_WHITE); //Eine Linie zeichnen display.drawFastHLine(2, 20, 125, SH110X_WHITE); }
Schritt 6 – Funktion zum anzeigen des aktuellen Datums und der Uhrzeit auf dem Display
Mit der Funktion updateTime wird die Uhrzeit und das Datum welches oben im Display befindet aktualisiert.
//Funktion zum anzeigen des aktuellen Datums und der Uhrzeit void updateTime() { //ein gefülltes, schwarzes Rechteck über die gezeichneten Felder //für das Datum und die Uhrzeit zeichnen display.fillRect(4, 4, 120, 16, SH110X_BLACK); //die Felder der RTC aktualisieren rtcDS1302.updateTime(); //Auslesen und Formatieren der Uhrzeit und des Datums char zeit[12]; sprintf(zeit, "%02d:%02d", rtcDS1302.hours, rtcDS1302.minutes); char datum[8]; sprintf(datum, "%02d.%02d.%02d", rtcDS1302.dayofmonth, rtcDS1302.month, rtcDS1302.year - 2000); //Ausgeben der Daten auf der seriellen Schnittstelle Serial.print(datum); Serial.print(" "); Serial.println(zeit); //Font auf default setzen //(später verwenden wir bestimmte Schriftarten, daher müssen wir dieses hier zurücksetzen) display.setFont(NULL); //Textgröße 1 display.setTextSize(1); //Schriftfarbe Weiß display.setTextColor(SH110X_WHITE); //setzen des Cursors display.setCursor(10, 8); //schreiben des Datums an die Cursorposition display.println(datum); //setzen des Cursors display.setCursor(90, 8); //schreiben der Uhrzeit an die Cursorposition display.println(zeit); //Absenden der Daten an das Display //dieser Aufruf ist sehr wichtig, ohne diesen werden keine Daten angezeigt display.display(); }
Schritt 7 – Funktion zum anzeigen des eingestellten Weckers
Die eingestellte Zeit des Weckers wird mittig im Display angezeigt.
//Funktion zum Anzeigen des Weckers void displayTime(int stunden, int minuten) { //Wenn der Modus zum editieren des Alarms nicht aktiv ist, dann... if (!setAlarm) { //Zeichnen von gefüllten, schwarten Rechtecken display.fillRect(70, 55, 40, 3, SH110X_BLACK); display.fillRect(20, 55, 40, 3, SH110X_BLACK); } //ein schwarzes, gefülltes Rechteck über die Felder //für Stunden & Minuten zeichnen display.fillRect(20, 22, 40, 30, SH110X_BLACK); display.fillRect(70, 22, 40, 30, SH110X_BLACK); //setzen der Schriftart display.setFont(&FreeSans18pt7b); //setzen der Schriftgröße 1 display.setTextSize(1); //setzen der Schriftfarbe Weiß display.setTextColor(SH110X_WHITE); //Wenn der Wert der Stunden kleiner 10 ist, //dann wird der Text um eine Stelle eingerückt display.setCursor(stunden < 10 ? 40 : 20, 51); display.println(stunden); display.setCursor(60, 51); display.println(":"); //Formatieren der Minuten mit führender Null char minutes[2]; sprintf(minutes, "%02d", minuten); display.setCursor(70, 51); display.println(minutes); display.display(); }
Schritt 8 – Funktion zum zeichnen eines Rechtecks zum anzeigen der Bearbeitung
Wenn der Wecker eingestellt wird, dann soll unter dem zzt. eingestellten Wert (Stunde oder Minute) ein kleines, gefülltes Rechteck gezeichnet werden.
//Funktion zum zeichnen eines kleinen weißen Rechtecks //wenn der Wecker eingestellt wird, anhand dieses Rechtecks //kann der Benutzer erkennen welcher Bestandteil der Zeit gerade //bearbeitet wird. void changeAlarm() { if (alarmTimeIndex == 0) { display.fillRect(20, 55, 40, 3, SH110X_WHITE); display.fillRect(70, 55, 40, 3, SH110X_BLACK); } else if (alarmTimeIndex == 1) { display.fillRect(70, 55, 40, 3, SH110X_WHITE); display.fillRect(20, 55, 40, 3, SH110X_BLACK); } display.display(); }
Schritt 9 – Funktion zum Anzeigen des Textes “!! Alarm !!” auf dem Display
Wenn der Alarm aktiviert ist, dann soll der Text “!! Alarm !!” auf dem Display angezeigt werden.
//Funktion zum anzeigen des Textes "!! Alarm !!" auf dem Display void showAlarmMsg() { display.fillRect(5, 23, 118, 40, SH110X_BLACK); display.setFont(&FreeSerif12pt7b); display.setTextSize(1); display.setTextColor(SH110X_WHITE); display.setCursor(12, 50); display.println("!! Alarm !!"); display.display(); }
Schritt 10 – Funktion zum aktivieren des Alarms und ausgeben eines Tones
Wenn der Zeitpunkt des Alarms ereicht wurde, dann soll ein Ton ausgegeben werden.
//Funktion zum aktivieren des Alarms und abspielen eines Tones void playAlarm() { isAlarmActive = true; //ein Ton mit der Frequenz von 450 Hz und der Dauer von 125 ms. //abspielen tone(buzzerPin, 450, 125); }
Schritt 11 – Funktion loop zum behandeln der Aktionen am Drehrehregler und Taster
In der Funktion loop werden die Aktionen am Drehregler und den Taster behandelt. Diese sind je nach aktivierten Modus verschieden.
//Diese Funktion wird fortwährend ausgeführt void loop() { //speichern der aktuellen Millisekunden seitdem der Mikrocontroller //gestartet wurde unsigned long currentMillis = millis(); //Wenn der Zeitpunkt der nächsten Ausführung erreicht wurde, dann... if ((lastAction + PAUSE) < currentMillis) { //abspeichern der aktuellen Millisekunden lastAction = currentMillis; //aktualisieren der Zeit auf dem Display updateTime(); //prüfen ob der Alarm aktiviert werden soll rtcDS1302.updateTime(); int currentStunden = rtcDS1302.hours; int currentMinuten = rtcDS1302.minutes; if ((currentStunden == stunden && currentMinuten == minuten)) { isAlarmActive = true; showAlarmMsg(); } } //Wenn der Alarm aktiviert ist, dann soll dieser Piepen und kein Dauerton sein //daher werden zwischen zwei Tönen eine kleine Pause eingelegt if ((lastActionAlarm + PAUSE_ALARM) < currentMillis && isAlarmActive) { lastActionAlarm = currentMillis; tone(buzzerPin, 600, 100); } //auslesen der aktuellen Drehrichtung des Drehreglers / Rotary Encoder char currentPosition = rotary.process(); //Wenn der Drehregler im Uhrzeigersinn gedreht wird und der Wecker eingestellt wird, dann... if (currentPosition == rotary.clockwise() && setAlarm) { //Nur wenn der Wert der Stunden größer 0 ist einen abziehen if (alarmTimeIndex == 0 && tempStunden > 0) { tempStunden = tempStunden - 1; } else if (alarmTimeIndex == 1 && tempMinuten > 0) { tempMinuten = tempMinuten - 1; } //einen Ton abspielen playKeyTone(); //anzeigen des neuen Wertes displayTime(tempStunden, tempMinuten); } else if (currentPosition == rotary.counterClockwise() && setAlarm) { //Wenn die Drehrichtung gegen den Uhrzeigersinn ist und der Wecker eingestellt wird, dann... if (alarmTimeIndex == 0 && tempStunden < 23) { tempStunden = tempStunden + 1; } else if (alarmTimeIndex == 1 && tempMinuten < 59) { tempMinuten = tempMinuten + 1; } playKeyTone(); displayTime(tempStunden, tempMinuten); } //Der mittlere Taster des Drehreglers dient zum deaktivieren des Alarms UND //zum wechseln zwischen Stunde & Minute if (rotary.buttonPressedReleased(20)) { //Wenn der Alarm aktiviert ist, dann... if (isAlarmActive) { isAlarmActive = false; display.fillRect(5, 23, 118, 40, SH110X_BLACK); displayTime(stunden, minuten); //An diesem Punkt wird die Funktion verlassen! return; } //Wenn der Wecker nicht eingestellt wird, dann if (!setAlarm) { //Wecker wird eingestellt setAlarm = true; changeAlarm(); playKeyTone(); } else { //Wechseln zwischen den Feldern Stunde & Minute alarmTimeIndex = alarmTimeIndex == 0 ? 1 : 0; changeAlarm(); playKeyTone(); } } //Wenn die BACK Taste betätigt wird und der Wecker eingestellt wird, dann... if ((digitalRead(backPin) == LOW) && setAlarm) { //überschreiben der gewählten Zeiten mit den vorherigen Werten tempStunden = stunden; tempMinuten = minuten; //deaktivieren des Modus zum einstellen des Weckers setAlarm = false; //eine kleine Pause von 150 ms. um den Taster zu entprellen delay(150); //anzeigen der Stunden & Minuten auf dem Display displayTime(stunden, minuten); //abspielen eines Tones playKeyTone(); } else if ((digitalRead(backPin) == LOW) && !setAlarm) { showAlarmMsg(); } //Wenn die Taste CONFIRM betätigt wird und der Wecker eingestellt wird, dann... if ((digitalRead(confirmPin) == LOW) && setAlarm) { //überschreiben der Werte für Stunde & Minute mit den gewählten Einstellungen stunden = tempStunden; minuten = tempMinuten; setAlarm = false; delay(150); displayTime(stunden, minuten); playKeyTone(); } }
Fertiges Programm – RTC DS1302 Timer-Schaltung mit OLED-Display am Arduino
Kompletter Quellcode des Projektes
// Importieren der benötigten Bibliotheken // Bibliothek zum einfachen auslesen / setzen // eines Zeitstempels einer RTC vom Typ DS1302 #include <virtuabotixRTC.h> // Bibliothek zum auslesen der Drehrichtung am // Drehregler / Rotary Encoder #include <rotary.h> // Für das OLED-Display mit Treiberchip SH110X // wird die Adafruit Bibliothek verwendet #include <SPI.h> #include <Wire.h> #include <Adafruit_GFX.h> #include <Adafruit_SH110X.h> // Auf dem Display werden zwei Schriftarten verwendet // unter https://learn.adafruit.com/adafruit-gfx-graphics-library/using-fonts // finden sich weitere verwendbare Schriften und Größen #include <Fonts/FreeSans18pt7b.h> #include <Fonts/FreeSerif12pt7b.h> // Der Drehregler benötigt drei Pins. #define pushPin 9 //mittlerer Taster #define traPin 10 #define trbPin 11 // die beiden Taster CONFIRM & BACK #define confirmPin 5 #define backPin 4 // RTC DS1302 #define clkPin 6 #define datPin 7 #define rstPin 8 //Piezo Buzzer #define buzzerPin 13 //Objekt für die RTC initialisieren virtuabotixRTC rtcDS1302(clkPin, datPin, rstPin); //Objekt für den Drehregler / Rotary Encoder //mit den Pins initialisieren Rotary rotary = Rotary(traPin, trbPin, pushPin); //Initialisieren der OLED Anzeige //die default Adresse ist 0x3c //Es wird hier ein Treiberchip SH110X verwendet, solltest //du eine SSD1306 verwenden so musst du hier eine //andere Bibliothek verwenden. #define i2c_Address 0x3c Adafruit_SH1106G display = Adafruit_SH1106G(128, 64, &Wire, -1); //Konstante für die Pause zwischen dem umschalten der Zeit //wir zeigen auf dem Display lediglich Minutengenau an //daher ist der Takt 1 Minute (60000 Millisekunden) const unsigned long PAUSE = 60000; //Feld zum speichern des Zeitstempels der letzten ausführung unsigned long lastAction = -1; //Feld zum speichern des Zeitstempels wann der Alarm das //letzte Mal ertönt hat. Damit wird ein Konstanter Ton unterbunden. unsigned long lastActionAlarm = -1; const int PAUSE_ALARM = 500; //Zeit für den Wecker int stunden = 12; int minuten = 34; //Felder zum zwischenspeichern der Werte //diese werden später in die "echten" Felder //überschrieben int tempStunden = stunden; int tempMinuten = minuten; //Felder zum speichern ob der Alarm aktiviert ist, //ob gerade die Zeit eingestellt wird bool setAlarm = false; bool isAlarmActive = false; bool isAlarmDeactivated = false; //Welcher Bestandteil der Zeit (Stunde oder Minute) //soll bearbeitet werden int alarmTimeIndex = 0; //Diese Funktion wird einmalig beim Starten / neustarten //des Mikrocontrollers ausgeführt. void setup() { //beginn der seriellen Kommunikation mit 9600 baud Serial.begin(9600); //Pin des Taster BACK als Eingang definieren pinMode(backPin, INPUT); //Pin des Taster CONFIRM als Eingang definieren pinMode(confirmPin, INPUT); //Pin des Piezo Buzzers als Ausgang definieren pinMode(buzzerPin, OUTPUT); //Initialisieren des Displays display.begin(i2c_Address, true); //leeren des Display display.clearDisplay(); //Zeichnen des Hintergrundes drawBackground(); //Anzeigen der aktuellen Uhrzeit updateTime(); //Anzeigen des Weckers displayTime(stunden, minuten); } void playKeyTone() { tone(buzzerPin, 600, 75); } //Funktion zum zeichnen des Hintergrundes void drawBackground() { //Ein abgerundetes Rechteck zeichnen display.drawRoundRect(2, 2, 125, 62, 5, SH110X_WHITE); //Eine Linie zeichnen display.drawFastHLine(2, 20, 125, SH110X_WHITE); } //Funktion zum anzeigen des aktuellen Datums und der Uhrzeit void updateTime() { //ein gefülltes, schwarzes Rechteck über die gezeichneten Felder //für das Datum und die Uhrzeit zeichnen display.fillRect(4, 4, 120, 16, SH110X_BLACK); //die Felder der RTC aktualisieren rtcDS1302.updateTime(); //Auslesen und Formatieren der Uhrzeit und des Datums char zeit[12]; sprintf(zeit, "%02d:%02d", rtcDS1302.hours, rtcDS1302.minutes); char datum[8]; sprintf(datum, "%02d.%02d.%02d", rtcDS1302.dayofmonth, rtcDS1302.month, rtcDS1302.year - 2000); //Ausgeben der Daten auf der seriellen Schnittstelle Serial.print(datum); Serial.print(" "); Serial.println(zeit); //Font auf default setzen //(später verwenden wir bestimmte Schriftarten, daher müssen wir dieses hier zurücksetzen) display.setFont(NULL); //Textgröße 1 display.setTextSize(1); //Schriftfarbe Weiß display.setTextColor(SH110X_WHITE); //setzen des Cursors display.setCursor(10, 8); //schreiben des Datums an die Cursorposition display.println(datum); //setzen des Cursors display.setCursor(90, 8); //schreiben der Uhrzeit an die Cursorposition display.println(zeit); //Absenden der Daten an das Display //dieser Aufruf ist sehr wichtig, ohne diesen werden keine Daten angezeigt display.display(); } //Funktion zum Anzeigen des Weckers void displayTime(int stunden, int minuten) { //Wenn der Modus zum editieren des Alarms nicht aktiv ist, dann... if (!setAlarm) { //Zeichnen von gefüllten, schwarten Rechtecken display.fillRect(70, 55, 40, 3, SH110X_BLACK); display.fillRect(20, 55, 40, 3, SH110X_BLACK); } //ein schwarzes, gefülltes Rechteck über die Felder //für Stunden & Minuten zeichnen display.fillRect(20, 22, 40, 30, SH110X_BLACK); display.fillRect(70, 22, 40, 30, SH110X_BLACK); //setzen der Schriftart display.setFont(&FreeSans18pt7b); //setzen der Schriftgröße 1 display.setTextSize(1); //setzen der Schriftfarbe Weiß display.setTextColor(SH110X_WHITE); //Wenn der Wert der Stunden kleiner 10 ist, //dann wird der Text um eine Stelle eingerückt display.setCursor(stunden < 10 ? 40 : 20, 51); display.println(stunden); display.setCursor(60, 51); display.println(":"); //Formatieren der Minuten mit führender Null char minutes[2]; sprintf(minutes, "%02d", minuten); display.setCursor(70, 51); display.println(minutes); display.display(); } //Funktion zum zeichnen eines kleinen weißen Rechtecks //wenn der Wecker eingestellt wird, anhand dieses Rechtecks //kann der Benutzer erkennen welcher Bestandteil der Zeit gerade //bearbeitet wird. void changeAlarm() { if (alarmTimeIndex == 0) { display.fillRect(20, 55, 40, 3, SH110X_WHITE); display.fillRect(70, 55, 40, 3, SH110X_BLACK); } else if (alarmTimeIndex == 1) { display.fillRect(70, 55, 40, 3, SH110X_WHITE); display.fillRect(20, 55, 40, 3, SH110X_BLACK); } display.display(); } //Funktion zum anzeigen des Textes "!! Alarm !!" auf dem Display void showAlarmMsg() { display.fillRect(5, 23, 118, 40, SH110X_BLACK); display.setFont(&FreeSerif12pt7b); display.setTextSize(1); display.setTextColor(SH110X_WHITE); display.setCursor(12, 50); display.println("!! Alarm !!"); display.display(); } //Funktion zum aktivieren des Alarms und abspielen eines Tones void playAlarm() { isAlarmActive = true; //ein Ton mit der Frequenz von 450 Hz und der Dauer von 125 ms. //abspielen tone(buzzerPin, 450, 125); } //Diese Funktion wird fortwährend ausgeführt void loop() { //speichern der aktuellen Millisekunden seitdem der Mikrocontroller //gestartet wurde unsigned long currentMillis = millis(); //Wenn der Zeitpunkt der nächsten Ausführung erreicht wurde, dann... if ((lastAction + PAUSE) < currentMillis) { //abspeichern der aktuellen Millisekunden lastAction = currentMillis; //aktualisieren der Zeit auf dem Display updateTime(); //prüfen ob der Alarm aktiviert werden soll rtcDS1302.updateTime(); int currentStunden = rtcDS1302.hours; int currentMinuten = rtcDS1302.minutes; if ((currentStunden == stunden && currentMinuten == minuten)) { isAlarmActive = true; showAlarmMsg(); } } //Wenn der Alarm aktiviert ist, dann soll dieser Piepen und kein Dauerton sein //daher werden zwischen zwei Tönen eine kleine Pause eingelegt if ((lastActionAlarm + PAUSE_ALARM) < currentMillis && isAlarmActive) { lastActionAlarm = currentMillis; tone(buzzerPin, 600, 100); } //auslesen der aktuellen Drehrichtung des Drehreglers / Rotary Encoder char currentPosition = rotary.process(); //Wenn der Drehregler im Uhrzeigersinn gedreht wird und der Wecker eingestellt wird, dann... if (currentPosition == rotary.clockwise() && setAlarm) { //Nur wenn der Wert der Stunden größer 0 ist einen abziehen if (alarmTimeIndex == 0 && tempStunden > 0) { tempStunden = tempStunden - 1; } else if (alarmTimeIndex == 1 && tempMinuten > 0) { tempMinuten = tempMinuten - 1; } //einen Ton abspielen playKeyTone(); //anzeigen des neuen Wertes displayTime(tempStunden, tempMinuten); } else if (currentPosition == rotary.counterClockwise() && setAlarm) { //Wenn die Drehrichtung gegen den Uhrzeigersinn ist und der Wecker eingestellt wird, dann... if (alarmTimeIndex == 0 && tempStunden < 23) { tempStunden = tempStunden + 1; } else if (alarmTimeIndex == 1 && tempMinuten < 59) { tempMinuten = tempMinuten + 1; } playKeyTone(); displayTime(tempStunden, tempMinuten); } //Der mittlere Taster des Drehreglers dient zum deaktivieren des Alarms UND //zum wechseln zwischen Stunde & Minute if (rotary.buttonPressedReleased(20)) { //Wenn der Alarm aktiviert ist, dann... if (isAlarmActive) { isAlarmActive = false; display.fillRect(5, 23, 118, 40, SH110X_BLACK); displayTime(stunden, minuten); //An diesem Punkt wird die Funktion verlassen! return; } //Wenn der Wecker nicht eingestellt wird, dann if (!setAlarm) { //Wecker wird eingestellt setAlarm = true; changeAlarm(); playKeyTone(); } else { //Wechseln zwischen den Feldern Stunde & Minute alarmTimeIndex = alarmTimeIndex == 0 ? 1 : 0; changeAlarm(); playKeyTone(); } } //Wenn die BACK Taste betätigt wird und der Wecker eingestellt wird, dann... if ((digitalRead(backPin) == LOW) && setAlarm) { //überschreiben der gewählten Zeiten mit den vorherigen Werten tempStunden = stunden; tempMinuten = minuten; //deaktivieren des Modus zum einstellen des Weckers setAlarm = false; //eine kleine Pause von 150 ms. um den Taster zu entprellen delay(150); //anzeigen der Stunden & Minuten auf dem Display displayTime(stunden, minuten); //abspielen eines Tones playKeyTone(); } else if ((digitalRead(backPin) == LOW) && !setAlarm) { showAlarmMsg(); } //Wenn die Taste CONFIRM betätigt wird und der Wecker eingestellt wird, dann... if ((digitalRead(confirmPin) == LOW) && setAlarm) { //überschreiben der Werte für Stunde & Minute mit den gewählten Einstellungen stunden = tempStunden; minuten = tempMinuten; setAlarm = false; delay(150); displayTime(stunden, minuten); playKeyTone(); } }
Erweiterung um ein Relais Modul
Du kannst diese Schaltung auch recht einfach um ein Relais Modul erweitern, dazu schließt du dieses an eines der freien digitalen Pins an und musst lediglich in der Funktion wo der Alarm aktiviert wird, den Pin vom Relais auf LOW setzen. Wenn der Alarm wieder deaktiviert wird, musst du diesen Pin auf HIGH setzen.
Quellcode für die erweiterte Schaltung mit einem Relais modul
// Importieren der benötigten Bibliotheken // Bibliothek zum einfachen auslesen / setzen // eines Zeitstempels einer RTC vom Typ DS1302 #include <virtuabotixRTC.h> // Bibliothek zum auslesen der Drehrichtung am // Drehregler / Rotary Encoder #include <rotary.h> // Für das OLED-Display mit Treiberchip SH110X // wird die Adafruit Bibliothek verwendet #include <SPI.h> #include <Wire.h> #include <Adafruit_GFX.h> #include <Adafruit_SH110X.h> // Auf dem Display werden zwei Schriftarten verwendet // unter https://learn.adafruit.com/adafruit-gfx-graphics-library/using-fonts // finden sich weitere verwendbare Schriften und Größen #include <Fonts/FreeSans18pt7b.h> #include <Fonts/FreeSerif12pt7b.h> // Der Drehregler benötigt drei Pins. #define pushPin 9 //mittlerer Taster #define traPin 10 #define trbPin 11 // die beiden Taster CONFIRM & BACK #define confirmPin 5 #define backPin 4 // RTC DS1302 #define clkPin 6 #define datPin 7 #define rstPin 8 //Piezo Buzzer #define buzzerPin 13 //Relais #define relaisPin 12 //Objekt für die RTC initialisieren virtuabotixRTC rtcDS1302(clkPin, datPin, rstPin); //Objekt für den Drehregler / Rotary Encoder //mit den Pins initialisieren Rotary rotary = Rotary(traPin, trbPin, pushPin); //Initialisieren der OLED Anzeige //die default Adresse ist 0x3c //Es wird hier ein Treiberchip SH110X verwendet, solltest //du eine SSD1306 verwenden so musst du hier eine //andere Bibliothek verwenden. #define i2c_Address 0x3c Adafruit_SH1106G display = Adafruit_SH1106G(128, 64, &Wire, -1); //Konstante für die Pause zwischen dem umschalten der Zeit //wir zeigen auf dem Display lediglich Minutengenau an //daher ist der Takt 1 Minute (60000 Millisekunden) const unsigned long PAUSE = 60000; //Feld zum speichern des Zeitstempels der letzten ausführung unsigned long lastAction = -1; //Feld zum speichern des Zeitstempels wann der Alarm das //letzte Mal ertönt hat. Damit wird ein Konstanter Ton unterbunden. unsigned long lastActionAlarm = -1; const int PAUSE_ALARM = 500; //Zeit für den Wecker int stunden = 12; int minuten = 34; //Felder zum zwischenspeichern der Werte //diese werden später in die "echten" Felder //überschrieben int tempStunden = stunden; int tempMinuten = minuten; //Felder zum speichern ob der Alarm aktiviert ist, //ob gerade die Zeit eingestellt wird bool setAlarm = false; bool isAlarmActive = false; bool isAlarmDeactivated = false; //Welcher Bestandteil der Zeit (Stunde oder Minute) //soll bearbeitet werden int alarmTimeIndex = 0; //Diese Funktion wird einmalig beim Starten / neustarten //des Mikrocontrollers ausgeführt. void setup() { //beginn der seriellen Kommunikation mit 9600 baud Serial.begin(9600); //Pin des Taster BACK als Eingang definieren pinMode(backPin, INPUT); //Pin des Taster CONFIRM als Eingang definieren pinMode(confirmPin, INPUT); //Pin des Piezo Buzzers als Ausgang definieren pinMode(buzzerPin, OUTPUT); //Pin des Relais als Ausgang definieren pinMode(relaisPin, OUTPUT); //Initialisieren des Displays display.begin(i2c_Address, true); //leeren des Display display.clearDisplay(); //Zeichnen des Hintergrundes drawBackground(); //Anzeigen der aktuellen Uhrzeit updateTime(); //Anzeigen des Weckers displayTime(stunden, minuten); } void playKeyTone() { tone(buzzerPin, 600, 75); } //Funktion zum zeichnen des Hintergrundes void drawBackground() { //Ein abgerundetes Rechteck zeichnen display.drawRoundRect(2, 2, 125, 62, 5, SH110X_WHITE); //Eine Linie zeichnen display.drawFastHLine(2, 20, 125, SH110X_WHITE); } //Funktion zum anzeigen des aktuellen Datums und der Uhrzeit void updateTime() { //ein gefülltes, schwarzes Rechteck über die gezeichneten Felder //für das Datum und die Uhrzeit zeichnen display.fillRect(4, 4, 120, 16, SH110X_BLACK); //die Felder der RTC aktualisieren rtcDS1302.updateTime(); //Auslesen und Formatieren der Uhrzeit und des Datums char zeit[12]; sprintf(zeit, "%02d:%02d", rtcDS1302.hours, rtcDS1302.minutes); char datum[8]; sprintf(datum, "%02d.%02d.%02d", rtcDS1302.dayofmonth, rtcDS1302.month, rtcDS1302.year - 2000); //Ausgeben der Daten auf der seriellen Schnittstelle Serial.print(datum); Serial.print(" "); Serial.println(zeit); //Font auf default setzen //(später verwenden wir bestimmte Schriftarten, daher müssen wir dieses hier zurücksetzen) display.setFont(NULL); //Textgröße 1 display.setTextSize(1); //Schriftfarbe Weiß display.setTextColor(SH110X_WHITE); //setzen des Cursors display.setCursor(10, 8); //schreiben des Datums an die Cursorposition display.println(datum); //setzen des Cursors display.setCursor(90, 8); //schreiben der Uhrzeit an die Cursorposition display.println(zeit); //Absenden der Daten an das Display //dieser Aufruf ist sehr wichtig, ohne diesen werden keine Daten angezeigt display.display(); } //Funktion zum Anzeigen des Weckers void displayTime(int stunden, int minuten) { //Wenn der Modus zum editieren des Alarms nicht aktiv ist, dann... if (!setAlarm) { //Zeichnen von gefüllten, schwarten Rechtecken display.fillRect(70, 55, 40, 3, SH110X_BLACK); display.fillRect(20, 55, 40, 3, SH110X_BLACK); } //ein schwarzes, gefülltes Rechteck über die Felder //für Stunden & Minuten zeichnen display.fillRect(20, 22, 40, 30, SH110X_BLACK); display.fillRect(70, 22, 40, 30, SH110X_BLACK); //setzen der Schriftart display.setFont(&FreeSans18pt7b); //setzen der Schriftgröße 1 display.setTextSize(1); //setzen der Schriftfarbe Weiß display.setTextColor(SH110X_WHITE); //Wenn der Wert der Stunden kleiner 10 ist, //dann wird der Text um eine Stelle eingerückt display.setCursor(stunden < 10 ? 40 : 20, 51); display.println(stunden); display.setCursor(60, 51); display.println(":"); //Formatieren der Minuten mit führender Null char minutes[2]; sprintf(minutes, "%02d", minuten); display.setCursor(70, 51); display.println(minutes); display.display(); } //Funktion zum zeichnen eines kleinen weißen Rechtecks //wenn der Wecker eingestellt wird, anhand dieses Rechtecks //kann der Benutzer erkennen welcher Bestandteil der Zeit gerade //bearbeitet wird. void changeAlarm() { if (alarmTimeIndex == 0) { display.fillRect(20, 55, 40, 3, SH110X_WHITE); display.fillRect(70, 55, 40, 3, SH110X_BLACK); } else if (alarmTimeIndex == 1) { display.fillRect(70, 55, 40, 3, SH110X_WHITE); display.fillRect(20, 55, 40, 3, SH110X_BLACK); } display.display(); } //Funktion zum anzeigen des Textes "!! Alarm !!" auf dem Display void showAlarmMsg() { display.fillRect(5, 23, 118, 40, SH110X_BLACK); display.setFont(&FreeSerif12pt7b); display.setTextSize(1); display.setTextColor(SH110X_WHITE); display.setCursor(12, 50); display.println("!! Alarm !!"); display.display(); } //Diese Funktion wird fortwährend ausgeführt void loop() { //speichern der aktuellen Millisekunden seitdem der Mikrocontroller //gestartet wurde unsigned long currentMillis = millis(); //Wenn der Zeitpunkt der nächsten Ausführung erreicht wurde, dann... if ((lastAction + PAUSE) < currentMillis) { //abspeichern der aktuellen Millisekunden lastAction = currentMillis; //aktualisieren der Zeit auf dem Display updateTime(); //prüfen ob der Alarm aktiviert werden soll rtcDS1302.updateTime(); int currentStunden = rtcDS1302.hours; int currentMinuten = rtcDS1302.minutes; if ((currentStunden == stunden && currentMinuten == minuten)) { isAlarmActive = true; digitalWrite(relaisPin, LOW); showAlarmMsg(); } } //auslesen der aktuellen Drehrichtung des Drehreglers / Rotary Encoder char currentPosition = rotary.process(); //Wenn der Drehregler im Uhrzeigersinn gedreht wird und der Wecker eingestellt wird, dann... if (currentPosition == rotary.clockwise() && setAlarm) { //Nur wenn der Wert der Stunden größer 0 ist einen abziehen if (alarmTimeIndex == 0 && tempStunden > 0) { tempStunden = tempStunden - 1; } else if (alarmTimeIndex == 1 && tempMinuten > 0) { tempMinuten = tempMinuten - 1; } //einen Ton abspielen playKeyTone(); //anzeigen des neuen Wertes displayTime(tempStunden, tempMinuten); } else if (currentPosition == rotary.counterClockwise() && setAlarm) { //Wenn die Drehrichtung gegen den Uhrzeigersinn ist und der Wecker eingestellt wird, dann... if (alarmTimeIndex == 0 && tempStunden < 23) { tempStunden = tempStunden + 1; } else if (alarmTimeIndex == 1 && tempMinuten < 59) { tempMinuten = tempMinuten + 1; } playKeyTone(); displayTime(tempStunden, tempMinuten); } //Der mittlere Taster des Drehreglers dient zum deaktivieren des Alarms UND //zum wechseln zwischen Stunde & Minute if (rotary.buttonPressedReleased(20)) { //Wenn der Alarm aktiviert ist, dann... if (isAlarmActive) { isAlarmActive = false; digitalWrite(relaisPin, HIGH); display.fillRect(5, 23, 118, 40, SH110X_BLACK); displayTime(stunden, minuten); //An diesem Punkt wird die Funktion verlassen! return; } //Wenn der Wecker nicht eingestellt wird, dann if (!setAlarm) { //Wecker wird eingestellt setAlarm = true; changeAlarm(); playKeyTone(); } else { //Wechseln zwischen den Feldern Stunde & Minute alarmTimeIndex = alarmTimeIndex == 0 ? 1 : 0; changeAlarm(); playKeyTone(); } } //Wenn die BACK Taste betätigt wird und der Wecker eingestellt wird, dann... if ((digitalRead(backPin) == LOW) && setAlarm) { //überschreiben der gewählten Zeiten mit den vorherigen Werten tempStunden = stunden; tempMinuten = minuten; //deaktivieren des Modus zum einstellen des Weckers setAlarm = false; //eine kleine Pause von 150 ms. um den Taster zu entprellen delay(150); //anzeigen der Stunden & Minuten auf dem Display displayTime(stunden, minuten); //abspielen eines Tones playKeyTone(); } else if ((digitalRead(backPin) == LOW) && !setAlarm) { showAlarmMsg(); } //Wenn die Taste CONFIRM betätigt wird und der Wecker eingestellt wird, dann... if ((digitalRead(confirmPin) == LOW) && setAlarm) { //überschreiben der Werte für Stunde & Minute mit den gewählten Einstellungen stunden = tempStunden; minuten = tempMinuten; setAlarm = false; delay(150); displayTime(stunden, minuten); playKeyTone(); } }