Skip to content

Technik Blog

Programmieren | Arduino | ESP32 | MicroPython | Python | Raspberry Pi | Raspberry Pi Pico

Menu
  • Smarthome
  • Arduino
  • ESP32 & Co.
  • Raspberry Pi & Pico
  • Solo Mining
  • Über mich
  • Deutsch
  • English
Menu

Arduino und DS3231: Relais jeden Tag automatisch zu variablen Uhrzeiten schalten

Posted on 23. Februar 202523. Februar 2025 by Stefan Draeger

Im Beitrag Arduino und DS3231: Relais jeden Tag automatisch zur gleichen Uhrzeit schalten habe ich dir gezeigt, wie du ein Relais zu einem fest definierten Zeitpunkt aktivierst. Aufbauend auf diesem Wissen erkläre ich dir heute, wie du das System erweiterst, um an mehreren Zeitpunkten pro Tag automatische Schaltvorgänge durchzuführen.

Arduino und DS3231: Relais jeden Tag automatisch zu variablen Uhrzeiten schalten
Dieses Video auf YouTube ansehen.

Hinweis: Die Idee zu dieser Erweiterung entstand aus einem Wunsch eines Lesers meines Blogs, der sich eine Mehrfachschaltung des Relais gewünscht hat. Wenn auch du Anregungen, Fragen oder Wünsche hast, melde dich gerne per E-Mail oder schreibe mir in den Kommentaren.

Inhaltsverzeichnis

  • Rückblick – Aufbau der Schaltung und benötigte Ressourcen
  • Erweitern des bestehenden Codes
    • Schritt 1 – Strukturen zum ablegen der Zeiten anpassen
    • Schritt 2 – Anpassen der Funktion zum auslesen der aktuellen Uhrzeit von der RTC DS3231
    • Schritt 3 – prüfen aller gespeicherten Zeiten
    • fertiges Projekt
  • Ausblick – Relais zu variablen Uhrzeiten schalten

Rückblick – Aufbau der Schaltung und benötigte Ressourcen

Für den Aufbau der Schaltung benötigt man:

  • einen Arduino Nano V3*
  • ein USB-Datenkabel*
  • eine RTC DS3231*
  • ein 2fach Relais-Modul*
  • ein 20×2 LC-Display mit I2C Schnittstelle*
  • diverse Breadboardkabel*
  • ein 400 Pin Breadboard*
Schaltung - Maker Nano mit DS3231 - RelaisModul und LC-Display
Schaltung – Maker Nano mit DS3231 – RelaisModul und LC-Display

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!

zeitgesteuerte Schaltung mit dem Arduino UNO R3, RTC und LCD-Display
zeitgesteuerte Schaltung mit dem Arduino UNO R3, RTC und LCD-Display

Für die Schaltung verwende ich den Maker Nano welchen ich dir im Beitrag Vorstellung des Maker Nano von Cytron vorgestellt habe. Dieser hat die Abmaße des Arduino Nano bietet aber ein paar nette zusätzliche Features.

Erweitern des bestehenden Codes

Wie bereits erwähnt, habe ich das kleine Projekt bereits vorgestellt. Nun gehe ich auf die notwendige kleine Änderung ein, die es ermöglicht, pro Tag mehrere Schaltvorgänge umzusetzen.

Schritt 1 – Strukturen zum ablegen der Zeiten anpassen

Da das Projekt nun darauf abzielt, das Relais täglich zu festen Uhrzeiten zu steuern, ist es nicht mehr notwendig, auch das Datum zu berücksichtigen. Ursprünglich war die Struktur so aufgebaut, dass sie an einem bestimmten Datum und zu einer bestimmten Uhrzeit aktivierte – das Datum war also ein entscheidendes Kriterium. Jetzt, wo die Schaltvorgänge jeden Tag erfolgen, genügt es, nur die Uhrzeit zu prüfen. Das Datumsfeld kann daher entfernt werden, was den Code übersichtlicher und zielgerichteter macht.

// Struktur für einen RTC-Zeitstempel.
// Speichert Datum und Zeit als Strings, wie sie z.B. von einer Real-Time Clock (RTC) geliefert werden.
struct RTC_Zeitstempel {
  String datum;  // Datum als String, z.B. "2025-02-23"
  String zeit;   // Zeit als String, z.B. "12:34:56"
};

// Struktur für einen allgemeinen Zeitstempel.
// Enthält zwei Zeitangaben: eine für das Einschalten (ON) und eine für das Ausschalten (OFF) des Relais.
struct Zeitstempel {
  String zeitON;  // Zeitpunkt, zu dem das Relais eingeschaltet werden soll
  String zeitOFF; // Zeitpunkt, zu dem das Relais ausgeschaltet werden soll
};

// Struktur zur Darstellung eines Relais.
// Speichert Informationen über das Relais und die zugehörigen Schaltzeitpunkte.
struct Relais {
  int digitalPin;             // Digitaler Pin, an den das Relais angeschlossen ist
  String desc;                // Beschreibung oder Name des Relais
  int numZeitstempel;         // Anzahl der aktuell gesetzten Zeitstempel im Array
  Zeitstempel zeitstempel[10]; // Array, das bis zu 10 Zeitstempel (Schaltvorgänge) aufnehmen kann
};

Schritt 2 – Anpassen der Funktion zum auslesen der aktuellen Uhrzeit von der RTC DS3231

Die Struktur zum speichern des aktuellen Zeitstempels habe ich umbenannt, daher musste im Code dieses an den entsprechenden Stellen angepasst werden.

void loop() {
  ...
  //bleibt leer
  RTC_Zeitstempel zeitstempel = readRtc();
  …
}

//auslesen der Daten von der RealtimeClock
RTC_Zeitstempel readRtc() {
  ...
}

void checkCurrentTimestamp(Relais relais, RTC_Zeitstempel zeitstempel) {
  ...
}

Schritt 3 – prüfen aller gespeicherten Zeiten

Die Funktion checkCurrentTimestamp dient dazu den aktuellen Zeitstempel gegen die gespeicherten Zeiten für die beiden Relais zu prüfen.

// Funktion zum Überprüfen, ob der aktuelle RTC-Zeitstempel einem
// der definierten Schaltzeitpunkte des Relais entspricht.
void checkCurrentTimestamp(Relais relais, RTC_Zeitstempel zeitstempel) {
  
  // Schleife über alle gesetzten Zeitstempel für dieses Relais
  for (int index = 0; index < relais.numZeitstempel; index++) {
    // Aktuellen Zeitstempel (mit On- und Off-Zeit) aus dem Array lesen
    Zeitstempel relaisZeitstempel = relais.zeitstempel[index];

    // Überprüfen, ob die aktuelle Zeit mit der Einschaltzeit übereinstimmt
    if (zeitstempel.zeit == relaisZeitstempel.zeitON) {
      // Ausgabe an den seriellen Monitor, dass das Relais aktiviert wird
      Serial.println("aktivieren");
      // Relais aktivieren
      digitalWrite(relais.digitalPin, LOW);
      
    // Überprüfen, ob die aktuelle Zeit mit der Ausschaltzeit übereinstimmt
    } else if (zeitstempel.zeit == relaisZeitstempel.zeitOFF) {
      // Ausgabe an den seriellen Monitor, dass das Relais deaktiviert wird
      Serial.println("deaktivieren");
      // Relais deaktivieren
      digitalWrite(relais.digitalPin, HIGH);
    }
  }
}

fertiges Projekt

Hier kannst du das fertige Programm als ZIP-Datei herunterladen.

Quellcode
/*
 * Autor: Stefan Draeger
 * Webseite: https://draeger-it.blog
 * Blogbeitrag: https://draeger-it.blog/arduino-und-ds3231-relais-jeden-tag-automatisch-zu-variablen-uhrzeiten-schalten/
 *
 * Benötigte Bauteile:
 *  - Arduino Nano V3
 *  - USB-Datenkabel
 *  - RTC DS3231
 *  - 2fach Relais-Modul
 *  - 20x2 LC-Display mit I2C Schnittstelle
 *  - diverse Breadboardkabel
 *  - 400 Pin Breadboard
 */

// Bibliothek zum Ansteuern des LCD-Displays via I2C
#include <LiquidCrystal_I2C.h>
// Bibliothek für die Kommunikation mit der RTC DS3231
#include <Wire.h>
// Bibliothek zur Kommunikation mit dem Bluetooth Modul über SoftwareSerial
#include <SoftwareSerial.h>

// I2C Adresse des RTC DS3231
#define RTC_I2C_ADDRESS 0x68

// Struktur zur Speicherung eines RTC-Zeitstempels
// Enthält Datum und Uhrzeit als Strings
struct RTC_Zeitstempel {
  String datum; // Datum, z.B. "23.02.2025"
  String zeit;  // Uhrzeit, z.B. "12:34:56"
};

// Struktur zur Speicherung eines Schalt-Zeitstempels für ein Relais
// Definiert, wann das Relais eingeschaltet (zeitON) und wann es ausgeschaltet (zeitOFF) werden soll
struct Zeitstempel {
  String zeitON;  // Zeitpunkt zum Aktivieren des Relais
  String zeitOFF; // Zeitpunkt zum Deaktivieren des Relais
};

// Struktur zur Beschreibung eines Relais und seiner Schaltzeitpunkte
struct Relais {
  int digitalPin;             // Der digitale Pin, an dem das Relais angeschlossen ist
  String desc;                // Beschreibung oder Name des Relais
  int numZeitstempel;         // Anzahl der definierten Zeitstempel im Array
  Zeitstempel zeitstempel[10]; // Array zur Speicherung von bis zu 10 Schalt-Zeitstempeln
};

// Initialisierung des LCD-Displays via I2C
// Adresse: 0x27, 20 Zeichen pro Zeile, 2 Zeilen
LiquidCrystal_I2C lcd(0x27, 20, 2);

// Puffer zum Speichern von seriellen Eingaben
char linebuf[30] = {};
bool readData = false;

// Definition des ersten Relais (Relais #1)
// Enthält 3 Schalt-Zeitstempel
Relais relais1 = {
  9,                      // Digitaler Pin 9
  "Relais #1",            // Beschreibung
  3,                      // Anzahl der Zeitstempel
  { 
    { "12:48:00", "12:48:10" },
    { "12:48:25", "12:48:30" },
    { "12:48:40", "12:49:00" }
  }
};

// Definition des zweiten Relais (Relais #2)
// Enthält 1 Schalt-Zeitstempel
Relais relais2 = {
  8,                      // Digitaler Pin 8
  "Relais #2",            // Beschreibung
  1,                      // Anzahl der Zeitstempel
  { { "12:48:00", "12:49:00" } }
};

void setup() {
  // Start der seriellen Kommunikation
  Serial.begin(9600);
  
  // Initialisierung des LCD-Displays
  lcd.init();
  // Aktivierung der Hintergrundbeleuchtung des Displays
  lcd.backlight();

  // Setzt die digitalen Pins der Relais als Ausgang
  pinMode(relais1.digitalPin, OUTPUT);
  pinMode(relais2.digitalPin, OUTPUT);

  // Setzt beide Relais initial auf HIGH (in diesem Fall deaktiviert)
  digitalWrite(relais1.digitalPin, HIGH);
  digitalWrite(relais2.digitalPin, HIGH);
}

void loop() {
  // Lese Daten von der seriellen Schnittstelle
  readDataFromSerial();
  
  // Wenn Daten empfangen wurden, verarbeite den Zeitstempel
  if (readData) {
    String timestamp = linebuf;
    // Das Datum wird als 10 Zeichen (inkl. Punkte) erwartet
    String datum = timestamp.substring(0, 10);
    String tag = datum.substring(0, 2);
    String monat = datum.substring(3, 5);
    String jahr = datum.substring(6, 10);

    // Die Uhrzeit beginnt ab dem 11. Zeichen des Zeitstempels
    String uhrzeit = timestamp.substring(11);
    String stunde = uhrzeit.substring(0, 2);
    String minute = uhrzeit.substring(3, 5);
    String sekunde = uhrzeit.substring(6);

    // Setzt den RTC-Zeitstempel entsprechend der empfangenen Werte
    rtcWriteTimestamp(stunde.toInt(), minute.toInt(), sekunde.toInt(), tag.toInt(), monat.toInt(), jahr.toInt());
  }

  // Lese den aktuellen Zeitstempel von der RTC
  RTC_Zeitstempel zeitstempel = readRtc();

  // Anzeige des Datums auf der ersten Zeile des LCD-Displays
  lcd.setCursor(0, 0);
  lcd.print(zeitstempel.datum);
  // Anzeige der Uhrzeit auf der zweiten Zeile des LCD-Displays
  lcd.setCursor(0, 1);
  lcd.print(zeitstempel.zeit);

  // Überprüfung der aktuellen Uhrzeit mit den definierten Schalt-Zeitstempeln der Relais
  checkCurrentTimestamp(relais1, zeitstempel);
  checkCurrentTimestamp(relais2, zeitstempel);

  // Kurze Pause zur Entlastung
  delay(500);
}

// Funktion zur Überprüfung, ob der aktuelle RTC-Zeitstempel
// mit einem der Schalt-Zeitstempel eines Relais übereinstimmt
void checkCurrentTimestamp(Relais relais, RTC_Zeitstempel zeitstempel) {
  // Schleife durch alle definierten Zeitstempel für das gegebene Relais
  for (int index = 0; index < relais.numZeitstempel; index++) {
    // Lese den aktuellen Schalt-Zeitstempel aus dem Array
    Zeitstempel relaisZeitstempel = relais.zeitstempel[index];

    // Falls die aktuelle Uhrzeit dem Einschaltzeitpunkt entspricht:
    if (zeitstempel.zeit == relaisZeitstempel.zeitON) {
      Serial.println("aktivieren");
      // Relais aktivieren (in diesem Fall wird LOW gesetzt, um zu schalten)
      digitalWrite(relais.digitalPin, LOW);
    } 
    // Falls die aktuelle Uhrzeit dem Ausschaltzeitpunkt entspricht:
    else if (zeitstempel.zeit == relaisZeitstempel.zeitOFF) {
      Serial.println("deaktivieren");
      // Relais deaktivieren (HIGH schaltet das Relais aus)
      digitalWrite(relais.digitalPin, HIGH);
    }
  }
}

// Funktion zum Einlesen von Daten von der seriellen Schnittstelle
void readDataFromSerial() {
  // Zähler für die empfangenen Zeichen
  byte counter = 0;
  readData = false;

  // Prüfen, ob Daten verfügbar sind
  if (Serial.available() > 0) {
    delay(250); // Kurze Verzögerung, um sicherzustellen, dass alle Daten empfangen wurden
    readData = true;
    // Solange Daten von der seriellen Schnittstelle verfügbar sind:
    while (Serial.available()) {
      // Lese ein Zeichen aus der seriellen Schnittstelle
      char c = Serial.read();
      // Füge das Zeichen in den Puffer ein, solange es nicht das Zeilenumbruch-Zeichen ist
      if (c != '\n') {
        linebuf[counter] = c;
        // Stelle sicher, dass der Puffer nicht überläuft
        if (counter < sizeof(linebuf) - 1) {
          counter++;
        }
      }
    }
    // Ausgabe des eingelesenen Puffers zur Kontrolle
    Serial.println(linebuf);
  }
}

// Funktion zum Schreiben eines Zeitstempels in den RTC DS3231
// Übergabe: Stunde, Minute, Sekunde, Tag, Monat, Jahr
void rtcWriteTimestamp(int stunde, int minute, int sekunde, int tag, int monat, int jahr) {
  Wire.beginTransmission(RTC_I2C_ADDRESS);
  Wire.write(0);  // Setzt den internen Zeiger des RTC auf 0 (Beginn des Registers)
  // Schreibe die Zeitdaten in BCD-Format in die RTC
  Wire.write(decToBcd(sekunde));
  Wire.write(decToBcd(minute));
  Wire.write(decToBcd(stunde));

  Wire.write(decToBcd(0));  // Wochentag wird hier nicht berücksichtigt
  Wire.write(decToBcd(tag));
  Wire.write(decToBcd(monat));
  // Das Jahr wird als Offset zu 2000 gespeichert
  Wire.write(decToBcd(jahr - 2000));

  Wire.endTransmission();
}

// Funktion zum Auslesen der aktuellen Zeit vom RTC DS3231
RTC_Zeitstempel readRtc() {
  // Aufbau der Verbindung zum RTC
  Wire.beginTransmission(RTC_I2C_ADDRESS);
  Wire.write(0);  // Setzt den internen Zeiger auf 0
  Wire.endTransmission();
  // Fordert 7 Bytes an: Sekunden, Minuten, Stunde, Wochentag, Tag, Monat, Jahr
  Wire.requestFrom(RTC_I2C_ADDRESS, 7);

  int sekunde = bcdToDec(Wire.read() & 0x7f);  // Sekunde (Bit 7 wird maskiert)
  int minute  = bcdToDec(Wire.read());
  int stunde  = bcdToDec(Wire.read() & 0x3f);    // Stunde (nur 24-Stunden Format)
  int wochentag = bcdToDec(Wire.read());         // Wochentag (wird nicht weiter genutzt)
  int tag     = bcdToDec(Wire.read());
  int monat   = bcdToDec(Wire.read());
  int jahr    = bcdToDec(Wire.read()) + 2000;      // Jahr als Offset zu 2000

  // Formatierung des Datums als "TT.MM.JJJJ"
  char datum[30];
  sprintf(datum, "%02d.%02d.%4d", tag, monat, jahr);

  // Formatierung der Uhrzeit als "HH:MM:SS"
  char zeit[30];
  sprintf(zeit, "%02d:%02d:%02d", stunde, minute, sekunde);

  // Rückgabe des Zeitstempels als Struktur
  return { datum, zeit };
}

// Hilfsfunktion zum Konvertieren von Dezimal in BCD (Binary Coded Decimal)
byte decToBcd(byte val) {
  return ((val / 10 * 16) + (val % 10));
}

// Hilfsfunktion zum Konvertieren von BCD in Dezimal
byte bcdToDec(byte val) {
  return ((val / 16 * 10) + (val % 16));
}
Programm: Relais jeden Tag automatisch zu variablen Uhrzeiten am Arduino schaltenHerunterladen

Ausblick – Relais zu variablen Uhrzeiten schalten

Nachdem in diesem Projektkommentar auf Wunsch eines Lesers bereits die Funktionalität um mehrere Schaltzeitpunkte erweitert wurde, steht nun der nächste Schritt an: der Umstieg auf den ESP32. Mit dem integrierten WiFi bietet der ESP32 enorme Vorteile, denn zukünftig sollen die Schaltzeiten flexibel und benutzerfreundlich über eine eigene Webseite konfigurierbar sein. Außerdem wird die aktuelle Uhrzeit nicht mehr ausschließlich von einer RTC ermittelt, sondern direkt von einem NTP-Zeitserver aus dem Internet bezogen – für eine präzise und wartungsfreie Zeitsynchronisation.

Diese Neuerungen machen das Relaissteuerungs-Projekt noch flexibler und vielseitiger. Ich freue mich darauf, in den kommenden Beiträgen mehr über die Umsetzung und die erweiterten Möglichkeiten zu berichten. Bleib also gespannt!

Schreibe einen Kommentar Antworten abbrechen

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert

Fragen oder Feedback?

Du hast eine Idee, brauchst Hilfe oder möchtest Feedback loswerden?
Support-Ticket erstellen

Newsletter abonnieren

Bleib auf dem Laufenden: Erhalte regelmäßig Updates zu neuen Projekten, Tutorials und Tipps rund um Arduino, ESP32 und mehr – direkt in dein Postfach.

Jetzt Newsletter abonnieren

Unterstütze meinen Blog

Wenn dir meine Inhalte gefallen, freue ich mich über deine Unterstützung auf Tipeee.
So hilfst du mit, den Blog am Leben zu halten und neue Beiträge zu ermöglichen.

draeger-it.blog auf Tipeee unterstützen

Vielen Dank für deinen Support!
– Stefan Draeger

Kategorien

Tools

  • Unix-Zeitstempel-Rechner
  • ASCII Tabelle
  • Spannung, Strom, Widerstand und Leistung berechnen
  • Widerstandsrechner
  • 8×8 LED Matrix Tool
  • 8×16 LED Matrix Modul von Keyestudio
  • 16×16 LED Matrix – Generator

Links

Blogverzeichnis Bloggerei.de TopBlogs.de das Original - Blogverzeichnis | Blog Top Liste Blogverzeichnis trusted-blogs.com

Stefan Draeger
Königsberger Str. 13
38364 Schöningen

Tel.: 01778501273
E-Mail: info@draeger-it.blog

Folge mir auf

  • Impressum
  • Datenschutzerklärung
  • Disclaimer
  • Cookie-Richtlinie (EU)
©2025 Technik Blog | Built using WordPress and Responsive Blogily theme by Superb
Cookie-Zustimmung verwalten
Wir verwenden Technologien wie Cookies, um Geräteinformationen zu speichern und/oder darauf zuzugreifen. Wir tun dies, um das Surferlebnis zu verbessern und um personalisierte Werbung anzuzeigen. Wenn Sie diesen Technologien zustimmen, können wir Daten wie das Surfverhalten oder eindeutige IDs auf dieser Website verarbeiten. Wenn Sie Ihre Zustimmung nicht erteilen oder zurückziehen, können bestimmte Funktionen beeinträchtigt werden.
Funktional Immer aktiv
Die technische Speicherung oder der Zugang ist unbedingt erforderlich für den rechtmäßigen Zweck, die Nutzung eines bestimmten Dienstes zu ermöglichen, der vom Teilnehmer oder Nutzer ausdrücklich gewünscht wird, oder für den alleinigen Zweck, die Übertragung einer Nachricht über ein elektronisches Kommunikationsnetz durchzuführen.
Vorlieben
Die technische Speicherung oder der Zugriff ist für den rechtmäßigen Zweck der Speicherung von Präferenzen erforderlich, die nicht vom Abonnenten oder Benutzer angefordert wurden.
Statistiken
Die technische Speicherung oder der Zugriff, der ausschließlich zu statistischen Zwecken erfolgt. Die technische Speicherung oder der Zugriff, der ausschließlich zu anonymen statistischen Zwecken verwendet wird. Ohne eine Vorladung, die freiwillige Zustimmung deines Internetdienstanbieters oder zusätzliche Aufzeichnungen von Dritten können die zu diesem Zweck gespeicherten oder abgerufenen Informationen allein in der Regel nicht dazu verwendet werden, dich zu identifizieren.
Marketing
Die technische Speicherung oder der Zugriff ist erforderlich, um Nutzerprofile zu erstellen, um Werbung zu versenden oder um den Nutzer auf einer Website oder über mehrere Websites hinweg zu ähnlichen Marketingzwecken zu verfolgen.
Optionen verwalten Dienste verwalten Verwalten von {vendor_count}-Lieferanten Lese mehr über diese Zwecke
Einstellungen anzeigen
{title} {title} {title}