Ter­min­ka­len­der mit ESP32 mit 2,8 Zoll TFT

Gra­fik­trei­ber ILI9341

#include "WiFi.h"
#include "time.h"
#include "U8g2_for_Adafruit_GFX.h"
#include "SdFat.h"
#include "Adafruit_ILI9341.h"

// SPI-Pins TFT
#define TFT_BL   21
#define TFT_CS   15 
#define TFT_DC    2
#define TFT_MISO 12
#define TFT_MOSI 13
#define TFT_SCLK 14
#define TFT_RST  -1

// Objekt tft der Bibliothek Adafruit_ILI9341 erstellen
Adafruit_ILI9341 tft = Adafruit_ILI9341(TFT_CS, TFT_DC, TFT_MOSI, TFT_SCLK, TFT_RST, TFT_MISO);

// Objekt SD der Bibliothek SdFat erstellen
SdFat SD;    

// 3 = FAT32
#define SD_FAT_TYPE 3

// SPI-Geschwindigkeit
#define SPI_SPEED SD_SCK_MHZ(4)

// CSPin der SD-Karte
int CSPin = 5;

// WiFi-Daten
char Router[] = "Router_SSID";
char Passwort[] = "xxxxxxxx";

// Variablen für die Zeit
int Stunden, Minuten, Sekunden;

// Objekt u8g2Schriften
U8G2_FOR_ADAFRUIT_GFX u8g2Schriften;

// Farben
#define SCHWARZ     0x0000
#define WEISS       0xFFFF
#define BLAU        0x001F
#define ROT         0xF800
#define GRUEN       0x07E0
#define CYAN        0x07FF
#define MAGENTA     0xF81F
#define GELB        0xFFE0
#define BRAUN       0x9A60
#define GRAU        0x7BEF
#define GRUENGELB   0xB7E0
#define DUNKELCYAN  0x03EF
#define ORANGE      0xFDA0
#define PINK        0xFE19
#define BORDEAUX    0xA000
#define HELLBLAU    0x867D
#define VIOLETT     0x915C
#define SILBER      0xC618
#define GOLD        0xFEA0

// farbliche Gestaltung
// schwarz/weiß
int HintergrundFarbeDatum = SCHWARZ;
int VordergrundFarbeDatum = WEISS;
int HintergrundFarbeTermine = SCHWARZ;
int VordergrundFarbeTermine = WEISS;

// farbig
// int HintergrundFarbeDatum = SILBER;
// int VordergrundFarbeDatum = BLAU;
// int HintergrundFarbeTermine = BLAU;
// int VordergrundFarbeTermine = WEISS;

// NTP-Server aus dem Pool
#define Zeitserver "de.pool.ntp.org"

/*
  Liste der Zeitzonen
  https://github.com/nayarsystems/posix_tz_db/blob/master/zones.csv
  Zeitzone CET = Central European Time -1 -> 1 Stunde zurück
  CEST = Central European Summer Time von
  M3 = März, 5.0 = Sonntag 5. Woche, 02 = 2 Uhr
  bis M10 = Oktober, 5.0 = Sonntag 5. Woche 03 = 3 Uhr
*/
#define Zeitzone "CET-1CEST,M3.5.0/02,M10.5.0/03"

// time_t enthält die Anzahl der Sekunden seit dem 1.1.1970 0 Uhr
time_t aktuelleZeit;

/* 
  Struktur tm
  tm_hour -> Stunde: 0 bis 23
  tm_min -> Minuten: 0 bis 59
  tm_sec -> Sekunden 0 bis 59
  tm_mday -> Tag 1 bis 31
  tm_mon -> Monat: 0 (Januar) bis 11 (Dezember)
  tm_year -> Jahre seit 1900
  tm_yday -> vergangene Tage seit 1. Januar des Jahres
  tm_isdst -> Wert > 0 = Sommerzeit (dst = daylight saving time)
*/
tm Zeit;

unsigned long Zeitmessung = 0;  

// Anzahl der Datensätze
#define DatenMax 50

// Array für gelesene Zeile aus Datei Kalender.txt
String DatenLesen[DatenMax];

// nach Datum und Beschreibung getrenntes Array
String DatumEintrag[DatenMax];
String KalenderEintrag[DatenMax];

// Bezeichner für die Datei
File Datei;

// char-Array für den Dateinamen erstellen
char Dateiname[20] = "Kalender.txt";

void setup() 
{
  // Schriften von u8g2 tft zuordnen
  u8g2Schriften.begin(tft); 

  Serial.begin(9600);

  // Zeitzone: Parameter für die zu ermittelnde Zeit
  configTzTime(Zeitzone, Zeitserver);

  // WiFi starten
  WiFi.mode(WIFI_STA);
  WiFi.begin(Router, Passwort);

  Serial.println("------------------------");

  while (WiFi.status() != WL_CONNECTED) 
  {
    delay(200);
    Serial.print(".");
  }
  
  Serial.println();
  Serial.print("Verbunden mit ");
  Serial.println(WiFi.SSID());
  Serial.print("IP über DHCP: ");
  Serial.println(WiFi.localIP());

  // Zeit holen
  time(&aktuelleZeit);

  // localtime_r -> Zeit in die lokale Zeitzone setzen
  localtime_r(&aktuelleZeit, &Zeit);

  // Zeit in Stunden, Minuten und Sekunden
  Stunden = Zeit.tm_hour, Minuten = Zeit.tm_min, Sekunden = Zeit.tm_sec;

  // Hintergrundbeleuchtung einschalten
  pinMode(TFT_BL, OUTPUT);
  digitalWrite(TFT_BL, HIGH);

  // tft starten, Farben invertieren
  tft.begin();
  tft.invertDisplay(0);

  // Rotation anpassen
  tft.setRotation(1);

  // HintergrundFarbeer Hintergrund
  tft.fillScreen(HintergrundFarbeTermine);
  tft.setTextSize(2);
  tft.setTextColor(VordergrundFarbeTermine);
  tft.setCursor(1, 20);

  /*
    SD-Karte mit Angabe des CSPins und  der SPI-Geschwindigkeit starten
    wenn die Intialisierung fehlschlägt
    - keine SD-Karte vorhanden
    - falsche Pinbelegung
    -> es wird eine Fehlermeldung angezeigt
  */
  if (!SD.begin(CSPin, SPI_SPEED)) 
  {
    tft.println("Start der SD-Karte");
    tft.print("fehlgeschlagen!");
    Serial.println("Start der SD-Karte fehlgeschlagen!");
    Serial.println("Programm angehalten ...");

    // Programm anhalten
    while(1);;
  } 

  else 
  {
    Serial.println("SD-Karte gestartet ...");
    tft.print("SD-Karte gestartet!");
    tft.setCursor(1, 50);
    tft.print("auf Zeitserver warten ...");
  }

  // tft.fillScreen(HintergrundFarbe);

 // wenn die Datei existiert
  if (SD.exists(Dateiname)) 
  {
    // Datei Kalender.txt öffnen
    Serial.println("-------------------------");
    Serial.println("Zeige " + String(Dateiname));
    
    Datei.open(Dateiname, O_RDONLY);

    int Trenner;
    
    for (int i = 0; i < DatenMax; i++) 
    {
      // Zeile bis zum return ('\n') lesen
      DatenLesen[i] = Datei.readStringUntil('\n');

      // Position des Trennzeichens
      Trenner = DatenLesen[i].indexOf("/");

      // Leerzeichen entfernen
      DatenLesen[i].trim();

      // Zeile am Komma in Datum und Kalendereintrag trennnen
      DatumEintrag[i] = DatenLesen[i].substring(0, Trenner);
      KalenderEintrag[i] = DatenLesen[i].substring(Trenner + 1, DatenLesen[i].length());
        
      // Abbruch, wenn kein Datensatz nehr vorhanden
      if (DatenLesen[i] == "") break;
      Serial.println(DatumEintrag[i]);
      Serial.println(KalenderEintrag[i]);
    }
    
    // Datei schließen
    Datei.close(); 
  }

  // Datei existiert nicht
  else
  {
    Serial.println("Datei " + String(Dateiname) + " nicht gefunden!");
    Serial.println("Programm angehalten ...");

    // Programm beenden
    while(1);
  }

  // beim Start entspricht das Datum der Unixtime: 1.1.1970
  // Datum/Kalender sollen erst angezeigt werden, wenn das Datum korrekt ist
  String Jahr = String(Zeit.tm_year + 1900); 
  int Zaehler = 0;

  // String Jahr nach "1970" durchsuchen
  int Suche = Jahr.indexOf("1970");         

  Serial.println("-------------------------"); 
  Serial.println("Datum und Zeit holen (maximal 90 Sekunden)...");
  
  // solange die Suche nicht erfolgreich ist
  while (Suche != -1) 
  {
    // aktuelle Zeit holen
    time(&aktuelleZeit);

    // localtime_r -> Zeit in die lokale Zeitzone setzen
    localtime_r(&aktuelleZeit, &Zeit);
    Jahr = String(Zeit.tm_year + 1900); 
    
    // String Jahr nach "1970" durchsuchen
    Suche = Jahr.indexOf("1970");

    // Zeit in Stunden, Minuten und Sekunden
    Stunden = int(Zeit.tm_hour), Minuten = int(Zeit.tm_min), Sekunden = int(Zeit.tm_sec);
    delay(1000);
    Zaehler ++;

    if (Zaehler >= 90)
    {
      Serial.println();
      Serial.println("Datum und Zeit konnte innerhalb von " + String(Zaehler) + " Sekunden nicht geholt werden");
      Serial.println("Programm wird beendet");

      // Programm beenden
      while(1);
    }

    Serial.print(".");
  }   

  Serial.println();

  // Datum/Zeit erfolgreich synchronisiert
  if (Suche == -1) 
  {
    Serial.println("-------------------------");
    Serial.println("Datum/Zeit erfolgreich synchronisiert ...");
    
    if (Zeit.tm_mday < 10) Serial.print("0");
    Serial.print(Zeit.tm_mday);
    Serial.print(".");

    // Monat: führende 0 ergänzen
    if (Zeit.tm_mon < 9) Serial.print("0");
    
    // Zählung beginnt mit 0 -> +1
    Serial.print(Zeit.tm_mon + 1);
    Serial.print(".");

    // Anzahl Jahre seit 1900
    Serial.println(Zeit.tm_year + 1900);

    if (Zeit.tm_hour < 10) Serial.print("0");
    Serial.print(Zeit.tm_hour);
    Serial.print(":");
      
    if (Zeit.tm_min < 10) Serial.print("0");
    Serial.println(Zeit.tm_min);
    Serial.println("-------------------------");

    ZeigeDatum();

    // beim Start -> Kalender nach Terminen durchsuchen
    Serial.println("Kalender nach Terminen durchsuchen ...");
    KalenderDurchsuchen();
    
    // Zeit anzeigen
    tft.fillRect(1, 30, tft.width(), 45, HintergrundFarbeDatum);
    u8g2Schriften.setFont(u8g2_font_logisoso28_tf);   
    u8g2Schriften.setForegroundColor(VordergrundFarbeDatum);
    u8g2Schriften.setBackgroundColor(HintergrundFarbeDatum);
    u8g2Schriften.setCursor(1, 70);

    if (Zeit.tm_hour < 10) 
    {
      u8g2Schriften.print("0");
      Serial.print("0");
    }
    u8g2Schriften.print(Zeit.tm_hour);
    u8g2Schriften.print(":");
    Serial.print(Zeit.tm_hour);
    Serial.print(":");
      
    if (Zeit.tm_min < 10) 
    {
      u8g2Schriften.print("0");
      Serial.print("0");
    }

    u8g2Schriften.print(Zeit.tm_min);
    Serial.print(Zeit.tm_min);
    Serial.println(" Uhr");

    tft.drawLine(1, 80, tft.width(), 80, VordergrundFarbeDatum);
  }
  
  Zeitmessung = millis() + 1000; 
}

void loop() 
{
  // Sekunden weiter zählen
  if (Zeitmessung < millis()) 
  {
    Zeitmessung += 1000;
    Sekunden ++;

    if (Sekunden == 60) 
    { 
      Sekunden = 0; 
    
      // Zeit jede Minute mit Zeitserver synchronisieren
      // aktuelle Zeit holen
      time(&aktuelleZeit);

      // localtime_r -> Zeit in die lokale Zeitzone setzen
      localtime_r(&aktuelleZeit, &Zeit);

      // Zeit in Stunden, Minuten und Sekunden
      Stunden = int(Zeit.tm_hour), Minuten = int(Zeit.tm_min), Sekunden = int(Zeit.tm_sec);

      tft.fillRect(1, 30, tft.width(), 45, HintergrundFarbeDatum);
      u8g2Schriften.setFont(u8g2_font_logisoso28_tf);   
      u8g2Schriften.setForegroundColor(VordergrundFarbeDatum);
      u8g2Schriften.setBackgroundColor(HintergrundFarbeDatum);
      u8g2Schriften.setCursor(1, 70);

      if (Zeit.tm_hour < 10) 
      {
        u8g2Schriften.print("0");
        Serial.print("0");
      }
      u8g2Schriften.print(Zeit.tm_hour);
      u8g2Schriften.print(":");
      Serial.print(Zeit.tm_hour);
      Serial.print(":");
      
      if (Zeit.tm_min < 10) 
      {
        u8g2Schriften.print("0");
        Serial.print("0");
      }

      u8g2Schriften.print(Zeit.tm_min);
      Serial.print(Zeit.tm_min);

      Serial.println(" Uhr");

      // tft.drawLine(1, 80, tft.width(), 80, VordergrundFarbe);

      // Mitternacht 
      // -> Wechsel des Datums anzeigen
      // -> Kalender durchsuchen
      if (Stunden == 0 && Minuten == 0) 
      {
          ZeigeDatum();
          KalenderDurchsuchen();
      }
    }
  }
}

void ZeigeDatum()
{
  tft.fillRect(1, 1, tft.width(), 79, HintergrundFarbeDatum);
  u8g2Schriften.setFont(u8g2_font_logisoso20_tf);   
  u8g2Schriften.setForegroundColor(VordergrundFarbeDatum);
  u8g2Schriften.setBackgroundColor(HintergrundFarbeDatum);
  
  u8g2Schriften.setCursor(1, 25);

  // Wochentag anzeigen
  switch (Zeit.tm_wday) 
  {
    case 0:
      u8g2Schriften.print("Sonntag");
      break;
    case 1:
      u8g2Schriften.print("Montag");
      break;
    case 2:
      u8g2Schriften.print("Dienstag");
      break;
    case 3:
      u8g2Schriften.print("Mittwoch");
      break;
    case 4:
      u8g2Schriften.print("Donnerstag");
      break;
    case 5:
      u8g2Schriften.print("Freitag");
      break;
    case 6:
      u8g2Schriften.print("Samstag");
      break;
  }

  u8g2Schriften.print(", ");
      
  if (Zeit.tm_mday < 10) u8g2Schriften.print("0");
  u8g2Schriften.print(Zeit.tm_mday);
  u8g2Schriften.print(".");

  // Monat: führende 0 ergänzen
  if (Zeit.tm_mon < 9) u8g2Schriften.print("0");
    
  // Zählung beginnt mit 0 -> +1
  u8g2Schriften.print(Zeit.tm_mon + 1);
  u8g2Schriften.print(".");

  // Anzahl Jahre seit 1900
  u8g2Schriften.print(Zeit.tm_year + 1900);

  tft.drawLine(1, 80, tft.width(), 80, VordergrundFarbeDatum);
}

void KalenderDurchsuchen()
{
  /*
    Datum = String für das aktuelle Datum
    Treffer = true wenn es einen oder mehrere Treffer beim Datum gibt
    Termin[] = zum aktuellen Datum gefundene Einträge
    Abstand = Abstand in Pixeln zwischen mehreren Einträgen
    Trenner = Position des Trennzeichens (/)
    AnzahlDaten = Anzahl aller Daten in der Datei
    AnzahlTreffer = Anzahl der zum aktuellen Datum passenden Einträge
  */
  String Datum;
  bool Treffer = false;
  String Termin[DatenMax];
  int Abstand;
  int Trenner;
  int AnzahlTreffer;
  int AnzahlDaten;
  
  // Datei lesen
  if (Datei.open(Dateiname, O_RDONLY)) 
  {
    while (Datei.available()) 
    {
       for (int i = 0; i < DatenMax; i++) 
       {
        // Zeile bis zum return ('\n') lesen
        DatenLesen[i] = Datei.readStringUntil('\n');

        // Position des Trennzeichens
        Trenner = DatenLesen[i].indexOf("/");

        // Leerzeichen entfernen
        DatenLesen[i].trim();

        // Zeile am Komma in Datum und Kalendereintrag trennnen
        DatumEintrag[i] = DatenLesen[i].substring(0, Trenner);
        KalenderEintrag[i] = DatenLesen[i].substring(Trenner + 1, DatenLesen[i].length());

        // Anzahl der Einträge feststellen
        AnzahlDaten = i;

        Serial.println(DatumEintrag[i]);
        Serial.println(KalenderEintrag[i]);

        // Abbruch, wenn kein Datensatz nehr vorhanden
        if (DatenLesen[i] == "") break;
      }
    }
  }

  // Datei schließen
  Datei.close(); 

  // Datum zusammensetzen: TT.MM.
  // bei einstelligen Tag/Monat 0 davor setzen
  if (Zeit.tm_mday < 10) Datum = "0" + String(Zeit.tm_mday);
  else Datum = String(Zeit.tm_mday);
  Datum += ".";
  
  if (Zeit.tm_mon < 10) Datum += "0" + String(Zeit.tm_mon + 1);
  else Datum += String(Zeit.tm_mon + 1);
  
  Datum += ".";
  
  AnzahlTreffer = 0;

  // Einträge nach aktuellen Datum durchsuchen
  for (int i = 0; i <= AnzahlDaten; i++)
  {
    // wenn das aktuelle Element des Arrays mit dem Datum übereinstimmt
    if (DatumEintrag[i] == Datum) 
    {
      // Übereinstimmung gefunden
      Treffer = true;
      AnzahlTreffer ++;

      // dem String KalenderEintrag das aktuelle Elent des Arrays hinzufügen
      Termin[AnzahlTreffer] = KalenderEintrag[i];
      Serial.println(Termin[AnzahlTreffer]);
    }
  }

  tft.fillRect(1, 80, tft.width(), tft.height(), HintergrundFarbeTermine);
  u8g2Schriften.setForegroundColor(VordergrundFarbeTermine);   
  u8g2Schriften.setBackgroundColor(HintergrundFarbeTermine);

  // Schriftgröße 12
  // u8g2Schriften.setFont(u8g2_font_luBS12_tf);

  // Schriftgröße 14
  u8g2Schriften.setFont(u8g2_font_helvB14_tf);

  // Schriftgröße 18
  // u8g2Schriften.setFont(u8g2_font_luRS18_tf);  
  
  // Schriftgröße 20
  // u8g2Schriften.setFont(u8g2_font_logisoso20_tf);  
  
  // Schriftgröße 24
  // u8g2Schriften.setFont(u8g2_font_helvB24_tf);  

  // wenn es einen/mehrere Treffer gab (Treffer = true)
  // Anzahl der Treffer
  Serial.println(String(AnzahlTreffer) + " Treffer");
  if (Treffer)
  {
    // Einträge anzeigen
    for(int i = 0; i <= AnzahlTreffer; i++)
    {
      u8g2Schriften.setCursor(1, 80 + Abstand);
      u8g2Schriften.print(Termin[i]);

      // je nach Schriftgröße kann der Abstand angepasst werden
      Abstand += 30;
    }
  }

  // keine Treffer
  else 
  {  
    u8g2Schriften.setCursor(1, 120);
    u8g2Schriften.print("Keine Termine heute!"); 
  }
}

Gra­fik­trei­ber ST7789

#include "WiFi.h"
#include "time.h"
#include "U8g2_for_Adafruit_GFX.h"
#include "SdFat.h"
#include "Adafruit_ST7789.h"   

// SPI-Pins TFT
#define TFT_BL   21
#define TFT_CS   15 
#define TFT_DC    2
#define TFT_MISO 12
#define TFT_MOSI 13
#define TFT_SCLK 14
#define TFT_RST  -1

// Objekt tft der Bibliothek Adafruit_ST7789 erstellen
Adafruit_ST7789 tft = Adafruit_ST7789(TFT_CS, TFT_DC, TFT_MOSI, TFT_SCLK, TFT_RST);

// Objekt SD der Bibliothek SdFat erstellen
SdFat SD;    

// 3 = FAT32
#define SD_FAT_TYPE 3

// SPI-Geschwindigkeit
#define SPI_SPEED SD_SCK_MHZ(4)

// CSPin der SD-Karte
int CSPin = 5;

// WiFi-Daten
char Router[] = "Router_SSID";
char Passwort[] = "xxxxxxxx";

// Variablen für die Zeit
int Stunden, Minuten, Sekunden;

// Objekt u8g2Schriften
U8G2_FOR_ADAFRUIT_GFX u8g2Schriften;

// Farben
#define SCHWARZ     0x0000
#define WEISS       0xFFFF
#define BLAU        0x001F
#define ROT         0xF800
#define GRUEN       0x07E0
#define CYAN        0x07FF
#define MAGENTA     0xF81F
#define GELB        0xFFE0
#define BRAUN       0x9A60
#define GRAU        0x7BEF
#define GRUENGELB   0xB7E0
#define DUNKELCYAN  0x03EF
#define ORANGE      0xFDA0
#define PINK        0xFE19
#define BORDEAUX    0xA000
#define HELLBLAU    0x867D
#define VIOLETT     0x915C
#define SILBER      0xC618
#define GOLD        0xFEA0

// farbliche Gestaltung
// schwarz/weiß
int HintergrundFarbeDatum = SCHWARZ;
int VordergrundFarbeDatum = WEISS;
int HintergrundFarbeTermine = SCHWARZ;
int VordergrundFarbeTermine = WEISS;

// farbig
// int HintergrundFarbeDatum = SILBER;
// int VordergrundFarbeDatum = BLAU;
// int HintergrundFarbeTermine = BLAU;
// int VordergrundFarbeTermine = WEISS;

// NTP-Server aus dem Pool
#define Zeitserver "de.pool.ntp.org"

/*
  Liste der Zeitzonen
  https://github.com/nayarsystems/posix_tz_db/blob/master/zones.csv
  Zeitzone CET = Central European Time -1 -> 1 Stunde zurück
  CEST = Central European Summer Time von
  M3 = März, 5.0 = Sonntag 5. Woche, 02 = 2 Uhr
  bis M10 = Oktober, 5.0 = Sonntag 5. Woche 03 = 3 Uhr
*/
#define Zeitzone "CET-1CEST,M3.5.0/02,M10.5.0/03"

// time_t enthält die Anzahl der Sekunden seit dem 1.1.1970 0 Uhr
time_t aktuelleZeit;

/* 
  Struktur tm
  tm_hour -> Stunde: 0 bis 23
  tm_min -> Minuten: 0 bis 59
  tm_sec -> Sekunden 0 bis 59
  tm_mday -> Tag 1 bis 31
  tm_mon -> Monat: 0 (Januar) bis 11 (Dezember)
  tm_year -> Jahre seit 1900
  tm_yday -> vergangene Tage seit 1. Januar des Jahres
  tm_isdst -> Wert > 0 = Sommerzeit (dst = daylight saving time)
*/
tm Zeit;

unsigned long Zeitmessung = 0;  

// Anzahl der Datensätze
#define DatenMax 50

// Array für gelesene Zeile aus Datei Kalender.txt
String DatenLesen[DatenMax];

// nach Datum und Beschreibung getrenntes Array
String DatumEintrag[DatenMax];
String KalenderEintrag[DatenMax];

// Bezeichner für die Datei
File Datei;

// char-Array für den Dateinamen erstellen
char Dateiname[20] = "Kalender.txt";

void setup() 
{
  // Schriften von u8g2 tft zuordnen
  u8g2Schriften.begin(tft); 

  Serial.begin(9600);

  // Zeitzone: Parameter für die zu ermittelnde Zeit
  configTzTime(Zeitzone, Zeitserver);

  // WiFi starten
  WiFi.mode(WIFI_STA);
  WiFi.begin(Router, Passwort);

  Serial.println("------------------------");

  while (WiFi.status() != WL_CONNECTED) 
  {
    delay(200);
    Serial.print(".");
  }
  
  Serial.println();
  Serial.print("Verbunden mit ");
  Serial.println(WiFi.SSID());
  Serial.print("IP über DHCP: ");
  Serial.println(WiFi.localIP());

  // Zeit holen
  time(&aktuelleZeit);

  // localtime_r -> Zeit in die lokale Zeitzone setzen
  localtime_r(&aktuelleZeit, &Zeit);

  // Zeit in Stunden, Minuten und Sekunden
  Stunden = Zeit.tm_hour, Minuten = Zeit.tm_min, Sekunden = Zeit.tm_sec;

   // Hintergrundbeleuchtung einschalten
  pinMode(TFT_BL, OUTPUT);
  digitalWrite(TFT_BL, HIGH);

  // tft starten, Farben invertieren
  tft.init(240, 320);
  tft.invertDisplay(0);

  // Rotation anpassen
  tft.setRotation(1);

  // HintergrundFarbeer Hintergrund
  tft.fillScreen(HintergrundFarbeTermine);
  tft.setTextSize(2);
  tft.setTextColor(VordergrundFarbeTermine);
  tft.setCursor(1, 20);

  /*
    SD-Karte mit Angabe des CSPins und  der SPI-Geschwindigkeit starten
    wenn die Intialisierung fehlschlägt
    - keine SD-Karte vorhanden
    - falsche Pinbelegung
    -> es wird eine Fehlermeldung angezeigt
  */
  if (!SD.begin(CSPin, SPI_SPEED)) 
  {
    tft.println("Start der SD-Karte");
    tft.print("fehlgeschlagen!");
    Serial.println("Start der SD-Karte fehlgeschlagen!");
    Serial.println("Programm angehalten ...");

    // Programm anhalten
    while(1);;
  } 

  else 
  {
    Serial.println("SD-Karte gestartet ...");
    tft.print("SD-Karte gestartet!");
    tft.setCursor(1, 50);
    tft.print("auf Zeitserver warten ...");
  }

  // tft.fillScreen(HintergrundFarbe);

 // wenn die Datei existiert
  if (SD.exists(Dateiname)) 
  {
    // Datei Kalender.txt öffnen
    Serial.println("-------------------------");
    Serial.println("Zeige " + String(Dateiname));
    
    Datei.open(Dateiname, O_RDONLY);

    int Trenner;
    
    for (int i = 0; i < DatenMax; i++) 
    {
      // Zeile bis zum return ('\n') lesen
      DatenLesen[i] = Datei.readStringUntil('\n');

      // Position des Trennzeichens
      Trenner = DatenLesen[i].indexOf("/");

      // Leerzeichen entfernen
      DatenLesen[i].trim();

      // Zeile am Komma in Datum und Kalendereintrag trennnen
      DatumEintrag[i] = DatenLesen[i].substring(0, Trenner);
      KalenderEintrag[i] = DatenLesen[i].substring(Trenner + 1, DatenLesen[i].length());
        
      // Abbruch, wenn kein Datensatz nehr vorhanden
      if (DatenLesen[i] == "") break;
      Serial.println(DatumEintrag[i]);
      Serial.println(KalenderEintrag[i]);
    }
    
    // Datei schließen
    Datei.close(); 
  }

  // Datei existiert nicht
  else
  {
    Serial.println("Datei " + String(Dateiname) + " nicht gefunden!");
    Serial.println("Programm angehalten ...");

    // Programm beenden
    while(1);
  }

  // beim Start entspricht das Datum der Unixtime: 1.1.1970
  // Datum/Kalender sollen erst angezeigt werden, wenn das Datum korrekt ist
  String Jahr = String(Zeit.tm_year + 1900); 
  int Zaehler = 0;

  // String Jahr nach "1970" durchsuchen
  int Suche = Jahr.indexOf("1970");         

  Serial.println("-------------------------"); 
  Serial.println("Datum und Zeit holen (maximal 90 Sekunden)...");
  
  // solange die Suche nicht erfolgreich ist
  while (Suche != -1) 
  {
    // aktuelle Zeit holen
    time(&aktuelleZeit);

    // localtime_r -> Zeit in die lokale Zeitzone setzen
    localtime_r(&aktuelleZeit, &Zeit);
    Jahr = String(Zeit.tm_year + 1900); 
    
    // String Jahr nach "1970" durchsuchen
    Suche = Jahr.indexOf("1970");

    // Zeit in Stunden, Minuten und Sekunden
    Stunden = int(Zeit.tm_hour), Minuten = int(Zeit.tm_min), Sekunden = int(Zeit.tm_sec);
    delay(1000);
    Zaehler ++;

    if (Zaehler >= 90)
    {
      Serial.println();
      Serial.println("Datum und Zeit konnte innerhalb von " + String(Zaehler) + " Sekunden nicht geholt werden");
      Serial.println("Programm wird beendet");

      // Programm beenden
      while(1);
    }

    Serial.print(".");
  }   

  Serial.println();

  // Datum/Zeit erfolgreich synchronisiert
  if (Suche == -1) 
  {
    Serial.println("-------------------------");
    Serial.println("Datum/Zeit erfolgreich synchronisiert ...");
    
    if (Zeit.tm_mday < 10) Serial.print("0");
    Serial.print(Zeit.tm_mday);
    Serial.print(".");

    // Monat: führende 0 ergänzen
    if (Zeit.tm_mon < 9) Serial.print("0");
    
    // Zählung beginnt mit 0 -> +1
    Serial.print(Zeit.tm_mon + 1);
    Serial.print(".");

    // Anzahl Jahre seit 1900
    Serial.println(Zeit.tm_year + 1900);

    if (Zeit.tm_hour < 10) Serial.print("0");
    Serial.print(Zeit.tm_hour);
    Serial.print(":");
      
    if (Zeit.tm_min < 10) Serial.print("0");
    Serial.println(Zeit.tm_min);
    Serial.println("-------------------------");

    ZeigeDatum();

    // beim Start -> Kalender nach Terminen durchsuchen
    Serial.println("Kalender nach Terminen durchsuchen ...");
    KalenderDurchsuchen();
    
    // Zeit anzeigen
    tft.fillRect(1, 30, tft.width(), 45, HintergrundFarbeDatum);
    u8g2Schriften.setFont(u8g2_font_logisoso28_tf);   
    u8g2Schriften.setForegroundColor(VordergrundFarbeDatum);
    u8g2Schriften.setBackgroundColor(HintergrundFarbeDatum);
    u8g2Schriften.setCursor(1, 70);

    if (Zeit.tm_hour < 10) 
    {
      u8g2Schriften.print("0");
      Serial.print("0");
    }
    u8g2Schriften.print(Zeit.tm_hour);
    u8g2Schriften.print(":");
    Serial.print(Zeit.tm_hour);
    Serial.print(":");
      
    if (Zeit.tm_min < 10) 
    {
      u8g2Schriften.print("0");
      Serial.print("0");
    }

    u8g2Schriften.print(Zeit.tm_min);
    Serial.print(Zeit.tm_min);
    Serial.println(" Uhr");

    tft.drawLine(1, 80, tft.width(), 80, VordergrundFarbeDatum);
  }
  
  Zeitmessung = millis() + 1000; 
}

void loop() 
{
  // Sekunden weiter zählen
  if (Zeitmessung < millis()) 
  {
    Zeitmessung += 1000;
    Sekunden ++;

    if (Sekunden == 60) 
    { 
      Sekunden = 0; 
    
      // Zeit jede Minute mit Zeitserver synchronisieren
      // aktuelle Zeit holen
      time(&aktuelleZeit);

      // localtime_r -> Zeit in die lokale Zeitzone setzen
      localtime_r(&aktuelleZeit, &Zeit);

      // Zeit in Stunden, Minuten und Sekunden
      Stunden = int(Zeit.tm_hour), Minuten = int(Zeit.tm_min), Sekunden = int(Zeit.tm_sec);

      tft.fillRect(1, 30, tft.width(), 45, HintergrundFarbeDatum);
      u8g2Schriften.setFont(u8g2_font_logisoso28_tf);   
      u8g2Schriften.setForegroundColor(VordergrundFarbeDatum);
      u8g2Schriften.setBackgroundColor(HintergrundFarbeDatum);
      u8g2Schriften.setCursor(1, 70);

      if (Zeit.tm_hour < 10) 
      {
        u8g2Schriften.print("0");
        Serial.print("0");
      }
      u8g2Schriften.print(Zeit.tm_hour);
      u8g2Schriften.print(":");
      Serial.print(Zeit.tm_hour);
      Serial.print(":");
      
      if (Zeit.tm_min < 10) 
      {
        u8g2Schriften.print("0");
        Serial.print("0");
      }

      u8g2Schriften.print(Zeit.tm_min);
      Serial.print(Zeit.tm_min);

      Serial.println(" Uhr");

      // tft.drawLine(1, 80, tft.width(), 80, VordergrundFarbe);

      // Mitternacht 
      // -> Wechsel des Datums anzeigen
      // -> Kalender durchsuchen
      if (Stunden == 0 && Minuten == 0) 
      {
          ZeigeDatum();
          KalenderDurchsuchen();
      }
    }
  }
}

void ZeigeDatum()
{
  tft.fillRect(1, 1, tft.width(), 79, HintergrundFarbeDatum);
  u8g2Schriften.setFont(u8g2_font_logisoso20_tf);   
  u8g2Schriften.setForegroundColor(VordergrundFarbeDatum);
  u8g2Schriften.setBackgroundColor(HintergrundFarbeDatum);
  
  u8g2Schriften.setCursor(1, 25);

  // Wochentag anzeigen
  switch (Zeit.tm_wday) 
  {
    case 0:
      u8g2Schriften.print("Sonntag");
      break;
    case 1:
      u8g2Schriften.print("Montag");
      break;
    case 2:
      u8g2Schriften.print("Dienstag");
      break;
    case 3:
      u8g2Schriften.print("Mittwoch");
      break;
    case 4:
      u8g2Schriften.print("Donnerstag");
      break;
    case 5:
      u8g2Schriften.print("Freitag");
      break;
    case 6:
      u8g2Schriften.print("Samstag");
      break;
  }

  u8g2Schriften.print(", ");
      
  if (Zeit.tm_mday < 10) u8g2Schriften.print("0");
  u8g2Schriften.print(Zeit.tm_mday);
  u8g2Schriften.print(".");

  // Monat: führende 0 ergänzen
  if (Zeit.tm_mon < 9) u8g2Schriften.print("0");
    
  // Zählung beginnt mit 0 -> +1
  u8g2Schriften.print(Zeit.tm_mon + 1);
  u8g2Schriften.print(".");

  // Anzahl Jahre seit 1900
  u8g2Schriften.print(Zeit.tm_year + 1900);

  tft.drawLine(1, 80, tft.width(), 80, VordergrundFarbeDatum);

}

void KalenderDurchsuchen()
{
  /*
    Datum = String für das aktuelle Datum
    Treffer = true wenn es einen oder mehrere Treffer beim Datum gibt
    Termin[] = zum aktuellen Datum gefundene Einträge
    Abstand = Abstand in Pixeln zwischen mehreren Einträgen
    Trenner = Position des Trennzeichens (/)
    AnzahlDaten = Anzahl aller Daten in der Datei
    AnzahlTreffer = Anzahl der zum aktuellen Datum passenden Einträge
  */
  String Datum;
  bool Treffer = false;
  String Termin[DatenMax];
  int Abstand;
  int Trenner;
  int AnzahlTreffer;
  int AnzahlDaten;
  
  // Datei lesen
  if (Datei.open(Dateiname, O_RDONLY)) 
  {
    while (Datei.available()) 
    {
       for (int i = 0; i < DatenMax; i++) 
       {
        // Zeile bis zum return ('\n') lesen
        DatenLesen[i] = Datei.readStringUntil('\n');

        // Position des Trennzeichens
        Trenner = DatenLesen[i].indexOf("/");

        // Leerzeichen entfernen
        DatenLesen[i].trim();

        // Zeile am Komma in Datum und Kalendereintrag trennnen
        DatumEintrag[i] = DatenLesen[i].substring(0, Trenner);
        KalenderEintrag[i] = DatenLesen[i].substring(Trenner + 1, DatenLesen[i].length());

        // Anzahl der Einträge feststellen
        AnzahlDaten = i;

        Serial.println(DatumEintrag[i]);
        Serial.println(KalenderEintrag[i]);

        // Abbruch, wenn kein Datensatz nehr vorhanden
        if (DatenLesen[i] == "") break;
      }
    }
  }

  // Datei schließen
  Datei.close(); 

  // Datum zusammensetzen: TT.MM.
  // bei einstelligen Tag/Monat 0 davor setzen
  if (Zeit.tm_mday < 10) Datum = "0" + String(Zeit.tm_mday);
  else Datum = String(Zeit.tm_mday);
  Datum += ".";
  
  if (Zeit.tm_mon < 10) Datum += "0" + String(Zeit.tm_mon + 1);
  else Datum += String(Zeit.tm_mon + 1);
  
  Datum += ".";
  
  AnzahlTreffer = 0;

  // Einträge nach aktuellen Datum durchsuchen
  for (int i = 0; i <= AnzahlDaten; i++)
  {
    // wenn das aktuelle Element des Arrays mit dem Datum übereinstimmt
    if (DatumEintrag[i] == Datum) 
    {
      // Übereinstimmung gefunden
      Treffer = true;
      AnzahlTreffer ++;

      // dem String KalenderEintrag das aktuelle Elent des Arrays hinzufügen
      Termin[AnzahlTreffer] = KalenderEintrag[i];
      Serial.println(Termin[AnzahlTreffer]);
    }
  }

  tft.fillRect(1, 80, tft.width(), tft.height(), HintergrundFarbeTermine);
  u8g2Schriften.setForegroundColor(VordergrundFarbeTermine);   
  u8g2Schriften.setBackgroundColor(HintergrundFarbeTermine);

  // Schriftgröße 12
  // u8g2Schriften.setFont(u8g2_font_luBS12_tf);

  // Schriftgröße 14
  u8g2Schriften.setFont(u8g2_font_helvB14_tf);

  // Schriftgröße 18
  // u8g2Schriften.setFont(u8g2_font_luRS18_tf);  
  
  // Schriftgröße 20
  // u8g2Schriften.setFont(u8g2_font_logisoso20_tf);  
  
  // Schriftgröße 24
  // u8g2Schriften.setFont(u8g2_font_helvB24_tf);  

  // wenn es einen/mehrere Treffer gab (Treffer = true)
  // Anzahl der Treffer
  Serial.println(String(AnzahlTreffer) + " Treffer");
  if (Treffer)
  {
    // Einträge anzeigen
    for(int i = 0; i <= AnzahlTreffer; i++)
    {
      u8g2Schriften.setCursor(1, 80 + Abstand);
      u8g2Schriften.print(Termin[i]);

      // je nach Schriftgröße kann der Abstand angepasst werden
      Abstand += 30;
    }
  }

  // keine Treffer
  else 
  {  
    u8g2Schriften.setCursor(1, 120);
    u8g2Schriften.print("Keine Termine heute!"); 
  }
}

Letzte Aktualisierung: Aug. 12, 2025 @ 14:43