
Ziel des Projekts
Passend zu jedem Tag sollen die anstehenden Termine auf dem TFT angezeigt werden. Die Daten befinden sich auf einer SD-Karte und werden täglich um Mitternacht aktualisiert. Zusätzlich werden Datum und Uhrzeit angezeigt.


Die Hardware

Für diese Projekt wird ein CYD (Cheap Yellow Display) verwendet. Auf der Rückseite des TFTs mit 2,8-Zoll und 320×240 Pixeln befinden sich ein ESP32-Wroom, ein SD-Karten-Modul, eine RGB-LED und Anschlussmöglichkeiten für Peripheriegeräte mithilfe von JST-Steckern.
Es gibt mehrere Versionen des CYD: ⇒ESP32-2432S028 und ESP32-JC2432W328C. Sie unterscheiden sich im verwendeten Grafiktreiber:
- mit Mikro-USB-Anschluss (ESP32-2432S028)
Grafiktreiber ⇒ILI9341 - mit USB-C-Anschluss (ESP32-2432S028 und ESP32-JC2432W328)
Grafiktreiber ⇒ST7789
Konfiguration des Mikrocontrollers
Der Datensatz auf der SD-Karte
Die Daten müssen im Format tt.mm./Name des Eintrags vorliegen. Der Dateiname ist zwingend Kalender.txt. Wenn Tag oder Monat einstellig sind, muss eine 0 vorangestellt werden. Die Reihenfolge der Einträge darf beliebig sein. Es werden maximal vier Einträge für den jeweiligen Tag auf dem Display dargestellt. Für jeden Eintrag muss eine neue Zeile erstellt werden.
Das gilt auch für mehrere Termine an einem Tag.
Die Datei wird nur einmal um Mitternacht neu gelesen, daher kann die SD-Karte zwischendurch herausgenommen und neu beschrieben werden.
Als Trennzeichen zwischen Datum und Kalendereintrag dient der /.
Aufbau der Datei
19.07./Theresa Geburtstag
20.07./10:15 Zahnarzt
21.07./Thomas Geburtstag
22.07./10:30 PhysioKalenderdaten schreiben
Mit diesem Programm kannst du Daten auf die SD-Karte schreiben, sie wird automatisch als Kalender.txt gespeichert. Wenn du eine neue Datei erstellen willst, musst du in Zeile 52 die // entfernen.

Du musst nach jeder Zeile die Eingabe-Taste drücken.
Zum Speichern musst du nach der letzten Zeile die Taste # drücken.
Anschließend wird die Datei angezeigt.
So sieht es dann aus:

#include "SdFat.h"
SdFs SD;
// 3 = FAT32
#define SD_FAT_TYPE 3
// SPI-Geschwindigkeit
#define SPI_SPEED SD_SCK_MHZ(4)
/*
Pinbelegung:
CIPO -> 19
COPI -> 23
SCK -> 18
CS -> 5
*/
// CSPin der SD-Karte
int CSPin = 5;
bool Schreiben = false;
void setup()
{
// Bezeichner für die Datei
File Datei;
Serial.begin(9600);
// auf serielle Verbindung warten
while (!Serial);
delay(1000);
/*
SD-Karte mit Angabe des Datenpins starten
wenn die Intialisierung fehlschlägt
- keine SD-Karte vorhanden
- falsche Pinbelegung
-> es wird eine Fehlermeldung angezeigt
*/
if (!SD.begin(CSPin, SPI_SPEED))
{
Serial.println("Initialisierung fehlgeschlagen!");
}
else Serial.println("Initialisierung abgeschlossen");
// char-Array für den Dateinamen erstellen
char Dateiname[20] = "Kalender.txt";
if (SD.exists("Kalender.txt"))
{
SD.remove("Kalender.txt");
}
String DatenSchreiben;
/*
O_CREAT -> Datei erstellen, wenn sie nicht existiert
O_WRITE -> in die Datei schreiben
O_AT_END -> Startposition zum Schreiben an das Ende der Datei setzen
*/
Datei.open(Dateiname, O_CREAT | O_WRITE | O_AT_END);
// Daten in Datei schreiben
Serial.println("Daten eingeben und Eingabe mit # beenden.");
Serial.println("Format: tt.mm./Eintrag:");
Serial.println("Beispiel: 02.07./Thomas Geburtstag");
// solange kein # eingegeben wurde und Schreiben false ist
while (DatenSchreiben != "#" && !Schreiben)
{
while (Serial.available() > 0)
{
DatenSchreiben = Serial.readStringUntil('\n');
Serial.println(DatenSchreiben);
// wenn das erste eingegebene Zeichen # ist
// -> Schreiben auf true setzen -> while verlassen
if (DatenSchreiben.startsWith("#")) Schreiben = true;
// ansonsten Datensatz in Datei schreiben
else Datei.print(DatenSchreiben + "\n");
}
}
// Datei schließen
Datei.close();
Serial.println("\nDatei " + String(Dateiname) + " erfolgreich geschrieben!");
// Datei Zahlen.txt öffnen
Serial.println("Zeige " + String(Dateiname));
if (Datei.open(Dateiname, O_RDONLY))
{
while (Datei.available())
{
// ... werden sie gelesen und im Seriellen Monitor ausgegeben
Serial.write(Datei.read());
}
}
// wenn die Datei Kalender.txt nicht existiert ...
else
{
// char-Array Dateiname muss in String umgewandelt werden
Serial.print("Datei " + String(Dateiname) + " nicht gefunden!");
}
// Datei schließen
Datei.close();
}
void loop()
{
// bleibt leer, das Programm läuft nur einmal
}Natürlich kannst du die Datei auch mit einem beliebigen Texteditor erstellen oder bearbeiten. Weil das Durchsuchen der Datei einige Zeit in Anspruch nimmt, empfiehlt es sich abgelaufene Termine zu löschen.
Das Programm
Vorbemerkungen
- Das Programm verwendet die Schriftarten von ⇒u8g2. Wenn du größere Schriftarten verwenden willst, musst du ausprobieren, ob die Einträge noch auf das Display passen.
Einige Beispiele:
14pt: u8g2_font_luRS14_tf
16pt: u8g2_fnt_logisoso16_tf
18pt: u8g2_font_luBS18_tf
20pt: u8g2_font_fub20_tf
22pt: u8g2_font_logisoso22_tf
24pt: u8g2_font_helvB24_tf
26pt: u8g2_font_logisoso26_tf
28pt: u8g2_font_logisoso28_tf
Die Schriften werden mit setFont(Name_der_Schrift) definiert. - Vordergrund- und Hintergrundfarbe können aus der Liste im Kopf des Programms in den entsprechenden Variablen getrennt für den Bereich von Datum und Zeit und für den Bereich der Termine festgelegt werden:
Farben Datum/Zeit:
int HintergrundFarbeDatum = SCHWARZ;
int VordergrundFarbeDatum = WEISS;
Farben Termine:
int HintergrundFarbeTermine = SCHWARZ;
int VordergrundFarbeTermine = WEISS; - Es können höchsten 50 Datensätze gelesen werden. Wenn du mehr benötigst, musst du den Wert der Variablen DatenMax erhöhen.
- Bis zu 90 Sekunden versucht das Programm eine Verbindung zum Zeitserver aufzubauen und das korrekte Datum und die aktuelle Zeit zu holen.
Gelingt das nicht, wird das Programm beendet.
Beim nächsten Start antwortet der Zeitserver zumeist in kurzer Zeit.
Benötigte Bibliotheken
Je nach Art des USB-Anschlusses wird ein anderer Grafik-Treiber benötigt:

oder:


Wenn du zuvor die Bibliothek SdFat installiert hast kann es zu Konflikten kommen. In diesem Fall musst du sie über die Bibliotheksverwaltung deinstallieren und stattdessen den Adafruit Fork installieren.

Einbindung der Bibliotheken und setup-Teil
Grafiktreiber ST7789
Für den ESP-JC2432W328 gibt es eine Besonderheit:
Der Wert der Variablen TFT_BL muss auf 27 gesetzt werden.
#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;
}Grafiktreiber 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;
}Beim Start des Programms zeigen die Meldungen, ob die SD-Karte gestartet wurde, die Datei Kalender.txt gelesen werden konnte und ob Datum und Zeit korrekt sind.

Der loop-Teil
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();
}
}
}
}Die Funktion ZeigeDatum()
Das Datum wird beim Start des Programms und danach nur noch beim Wechsel des Datums um Mitternacht angezeigt.
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);
}Die Funktion KalenderDurchsuchen()
Die Funktion wird einmalig beim Start des Programms und danach nur noch zum Datumswechsel um Mitternacht aufgerufen. Für den Vergleich des aktuellen Datums mit einem Eintrag auf der SD-Karte wird der String Datum aus der vom Zeitserver übermittelten Zeit zusammen gesetzt und dann mit dem ersten Teil des getrennten Strings (DatumEintrag) verglichen. Der zweite Teil des getrennten Strings (KalenderEintrag) wird auf dem Display angezeigt.
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!");
}
}Quellen
- Adafruit Grafik-Bibliothek
- Informationen zu ESP32-2432S028R auf github
- Espressif WiFi-API
- Definition der Farben als HEX-Code
- Schriftarten von u8g2
Letzte Aktualisierung: