Ziel des Projekts

Für die Arduino-Welt gibt es verschiedene TFT-Displays, die als paralleles LCD-Shield ausgeführt sind. Es wird auf den Arduino UNO gesteckt und nutzt die entsprechenden Pins.
Ziel des Projekts ist es eine Verbindung zwischen dem LCD-Shield und einem ESP32-Wroom herzustellen. Auf dem TFT sollen eine analoge Uhr und die Messdaten von Temperatursensoren dargestellt werden.
Konfiguration des Mikrocontrollers
Pinbelegung am ESP32-Wroom

links | Pin ESP32-Wroom | rechts | Pin ESP32-Wroom |
---|---|---|---|
LCD_RST | 32 | LCD_D2 | 26 |
LCD_CS | 33 | LCD_D3 | 25 |
LCD_RS | 15 | LCD_D4 | 17 (RX2) |
LCD_WR | 4 | LCD_D5 | 16 (TX2) |
LCD_RD | 2 | LCD_D7 | 14 |
GND | LCD_D0 | 23 | |
5V | LCD_D1 | 13 | |
3V3 |
Bibliothek installieren


Steuerdatei User_Setup.h
Die Bibliothek TFT_eSPI verwendet eine Steuerdatei, in der Treiber, Bildschirmmaße in Pixeln und die verwendeten Pins notiert werden.
#define ESP32_PARALLEL
#define ILI9488_DRIVER
#define TFT_WIDTH 320
#define TFT_HEIGHT 480
#define TFT_CS 33
#define TFT_DC 15 // Beschriftung LDC_RS
#define TFT_RST 32
#define TFT_WR 4
#define TFT_RD 2
#define TFT_D0 23
#define TFT_D1 13
#define TFT_D2 26
#define TFT_D3 25
#define TFT_D4 17
#define TFT_D5 16
#define TFT_D6 27
#define TFT_D7 14
// Schriftarten
// Font 2-4: Buchstaben, Zahlen und Satzzeichen, keine Umlaute
// Font 6-8: Zahlen und .,
#define LOAD_GLCD
#define LOAD_FONT2
#define LOAD_FONT4
#define LOAD_FONT6
#define LOAD_FONT7
#define LOAD_FONT8
#define LOAD_GFXFF
#define SMOOTH_FONT
Das Kompilieren führt zu einer Fehlermeldung:
Arduino/libraries/TFT_eSPI/Processors/TFT_eSPI_ESP32.c:114:9: error: 'gpio_input_get' was not declared in this scope;
Im Boardmanager esp32 ab Version 3 wurde die Funktion gpio_input_get' entfernt, in der Version 2 tritt der Fehler nicht auf.
Die Datei Arduino/libraries/TFT_eSPI/Processors/TFT_eSPI_ESP32.c muss ab Zeile 110 geändert werden
#if defined (TFT_PARALLEL_8_BIT)
RD_L;
uint32_t reg; // Read all GPIO pins 0-31
// reg = gpio_input_get(); // Read three times to allow for bus access time
// reg = gpio_input_get();
// reg = gpio_input_get(); // Data should be stable now
RD_H;
Möglicherweise wird das Problem in einer zukünftigen Version behoben, ansonsten muss nach einem Update die Änderung erneut durchgeführt werden.
Außerdem erscheint eine Warnung, dass die Touchpins nicht definiert sind, sie kann ignoriert werden.
Beispielprogramme
Die in den Programmen verwendete Bibliothek U8g2_for_TFT_eSPI kann nicht über die Bibliotheksverwaltung installiert werden, sie muss heruntergeladen
🔗https://github.com/Bodmer/U8g2_for_TFT_eSPI
und mit
Sketch -> Bibliothek einbinden -> zip-Bibliothek hinzufügen
installiert werden.
Grafische Funktionen
#include "TFT_eSPI.h"
TFT_eSPI tft = TFT_eSPI();
// 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
void setup()
{
tft.init();
}
void loop()
{
tft.fillScreen(SCHWARZ);
tft.setTextSize(1);
tft.setCursor(1, 5);
tft.setTextColor(BLAU);
tft.print("Text");
delay(500);
tft.setTextSize(2);
tft.setCursor(1, 20);
tft.setTextColor(ORANGE);
tft.print("Text");
delay(500);
tft.setTextSize(3);
tft.setCursor(1, 40);
tft.setTextColor(GRUEN);
tft.print("Text");
delay(500);
tft.setTextSize(5);
tft.setCursor(1, 70);
tft.setTextColor(ROT);
tft.print("Text");
delay(500);
delay(2000);
// zufällige Pixel
tft.fillScreen(SCHWARZ);
for (int i = 0; i < 700; i++)
{
int PixelX = random(1, tft.width());
int PixelY = random(1, tft.height());
tft.drawPixel(PixelX, PixelY, tft.color565(random(255),random(255),random(255)));
delay(5);
}
delay(2000);
// Linien ziehen
tft.fillScreen(SCHWARZ);
for (int i = 1; i < tft.height(); i+=10)
{
tft.drawLine(1, i, tft.width(), i, ORANGE);
}
delay(2000);
// Kreise vom Mittelpunkt zeichnen
tft.fillScreen(SCHWARZ);
for (int i = 1; i < tft.width() / 2; i+=10)
{
tft.fillCircle(tft.width() / 2, tft.height() / 2, tft.width() / 2 - i, tft.color565(random(255),random(255),random(255)));
delay(50);
}
delay(2000);
// Rechtecke zeichnen
tft.fillScreen(SCHWARZ);
for (int i = 1; i < tft.width(); i+=10)
{
tft.drawRect(tft.width() / 2 - i / 2, tft.height() / 2 - i / 2 , i, i, tft.color565(random(255),random(255),random(255)));
}
delay(2000);
// ausgefüllte Rechtecke zeichnen
tft.fillScreen(SCHWARZ);
for (int i = 1; i < tft.width() / 2; i+=10)
{
tft.fillRect(i, i, i, i, tft.color565(random(ROT),random(GRUEN),random(BLAU)));
delay(50);
}
delay(2000);
// Dreiecke
tft.fillScreen(SCHWARZ);
for (int i = 1; i <tft.width(); i+=10)
{
tft.fillTriangle(i, i, 100, 100, 1, tft.width(), tft.color565(random(255),random(255),random(255)));
delay(50);
}
}
Temperatur und Luftdruck mit BMP280 anzeigen
Der BMP280 wird am ⇒I²C-Bus angeschlossen. Der Sensor arbeitet mit unterschiedlichen HEX-Adressen, sie können mit diesem ⇒Programm ermittelt werden.

Zusätzliche Bibliothek installieren:

Das Programm
#include "TFT_eSPI.h"
#include "U8g2_for_TFT_eSPI.h"
// Objekt für Schriften von U8g2 (u8g2Schriften)
U8g2_for_TFT_eSPI u8g2Schriften;
TFT_eSPI tft = TFT_eSPI();
#include "Adafruit_BMP280.h"
Adafruit_BMP280 bmp;
#define SDA_PIN 21
#define SCL_PIN 22
// 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
void setup()
{
// Sensor starten
Wire.begin(SDA_PIN, SCL_PIN);
bmp.begin(0x77);
tft.init();
tft.setRotation(1);
tft.fillScreen(SCHWARZ);
// Schriften von u8g2 tft zuordnen
u8g2Schriften.begin(tft);
}
void loop()
{
DatenAnzeigen();
delay(10000);
}
void DatenAnzeigen()
{
// Temperatur lesen
String Temperatur = String(bmp.readTemperature());
// replace -> . durch , ersetzen
Temperatur.replace(".", ",");
String Luftdruck = String(bmp.readPressure() / 100);
Luftdruck.replace(".", ",");
// Bereich für die Messwerte löschen
tft.fillRect(1, 40, 300, 150, SCHWARZ);
u8g2Schriften.setCursor(10, 100);
u8g2Schriften.setForegroundColor(BLAU);
u8g2Schriften.setFont(u8g2_font_logisoso42_tf);
u8g2Schriften.print(Luftdruck + " hPa");
u8g2Schriften.setCursor(10, 180);
u8g2Schriften.setForegroundColor(ROT);
u8g2Schriften.print(Temperatur + "°C");
}
Analoge Uhr

Das Programm kann auf vielfältige Art und Weise angepasst werden:
- die Farbe der Zeiger (Zeigerfarbe)
die Farben kannst du dem Kopf des Programms entnehmen - die Farbe des inneren Kreises (Kreisfarbe)
- die Farbe der äußeren Umrandung (Randfarbe)
- Anzeige des Datums (DatumAnzeigen)
true: Datum anzeigen, false: Datum verbergen - Sekundenzeiger vollständig oder nur als Kreis anzeigen (SekundenzeigerKreis)
true: nur den Kreis anzeigen, false: Sekundenzeiger als Linie mit Kreis am Ende der Linie anzeigen - die Anzeige der Stundenmarkierungen 12, 3, 6 und 9 (Ziffernanzeigen)
true: Ziffern anzeigen, false: Ziffern verbergen
#include "WiFi.h"
#include "time.h"
#include "TFT_eSPI.h"
#include "U8g2_for_TFT_eSPI.h"
// Objekt für Schriften von U8g2 (u8g2Schriften)
U8g2_for_TFT_eSPI u8g2Schriften;
TFT_eSPI tft = TFT_eSPI();
// WiFi-Daten
char Router[] = "Router_SSID";
char Passwort[] = "xxxxxxxx";
// Variablen des TFTs (Höhe, Breite, Radius)
const int MitteHoehe = 160;
const int MitteBreite = 160;
const int Radius = 160;
// Multiplikatoren für x- y-Positionen der Stunden, Minuten und Sekunden
float SekundePosX = 0, SekundePosY = 0, MinutePosX = 0, MinutePosY = 0, StundePosX = 0, StundePosY = 0;
float GradSekunden = 0, GradMinuten = 0, GradStunden = 0;
// x- y-Koordinaten für die Anzeige Stunden, Minuten und Sekunden
int SekundenZeigerX = MitteHoehe, SekundenZeigerY = MitteHoehe;
int MinutenZeigerX = MitteHoehe, MinutenZeigerY = MitteHoehe;
int StundenZeigerX = MitteHoehe, StundenZeigerY = MitteHoehe;
// Start wird nur beim ersten Start für den Aufbau des TFTs benötigt
bool Start = true;
unsigned long Zeitmessung = 0;
// Variablen für die Markierungen und Punkte und Striche des Ziffernblatts
float PosX, PosY;
int PunktX, PunktY, PunktX1, PunktX2, PunktY1, PunktY2;
// Variablen für die Zeit
int Stunden, Minuten, Sekunden;
// 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
// Farben innerer Kreis, Randfarbe und Zeigerfarbe
// die Farben der Zeiger können aber auch individuell gesetzt werden
const int Kreisfarbe = SCHWARZ;
const int Zeigerfarbe = WEISS;
const int Randfarbe = BORDEAUX;
// true -> Datum anzeigen
// false -> Datum nicht anzeigen
bool DatumAnzeigen = true;
// true -> Sekundenzeiger nur als Kreis
bool SekundenzeigerKreis = false;
// Ziffern 12 3 6 9 anzeigen/nicht anzeigen
bool Ziffernanzeigen = true;
// 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;
void setup()
{
Serial.begin(9600);
// Schriften von u8g2 tft zuordnen
u8g2Schriften.begin(tft);
// Zeitzone: Parameter für die zu ermittelnde Zeit
configTzTime(Zeitzone, Zeitserver);
WiFi.mode(WIFI_STA);
// WiFi starten
WiFi.begin(Router, Passwort);
Serial.println("------------------------");
while (WiFi.status() != WL_CONNECTED)
{
delay(200);
Serial.print(".");
}
Serial.println();
Serial.print("Verbunden mit ");
Serial.println(Router);
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;
tft.begin();
tft.setRotation(0);
tft.fillScreen(Kreisfarbe);
// 4 Pixel breiter äußerer Rand, Farbe au der Farbpalette wählen
tft.drawCircle(MitteHoehe, MitteBreite, Radius - 1, Randfarbe);
tft.drawCircle(MitteHoehe, MitteBreite, Radius - 2, Randfarbe);
tft.drawCircle(MitteHoehe, MitteBreite, Radius - 3, Randfarbe);
tft.drawCircle(MitteHoehe, MitteBreite, Radius - 4, Randfarbe);
// innere Fläche bis auf den Rand von 4 Pixeln vollständig löschen
// wenn andere Farbe als äußerer Rand gewählt wird ergibt sich ein schmaler Rand
tft.fillCircle(MitteHoehe, MitteBreite, Radius - 4, Kreisfarbe);
/*
alle 30° Linie am Rand als Stundenmarkierung zeichnen
DEG_TO_RAD (= PI/180 = 0.0174532925) -> Winkel in Bogenmaß umrechnen
sin/cos berechnen die x-/y-Kordinaten des Punktes auf der Kreislinie
*/
for (int i = 0; i < 360; i += 30)
{
PosX = cos((i - 90) * DEG_TO_RAD);
PosY = sin((i - 90) * DEG_TO_RAD);
// kurze Linien zeichnen, von 114 bis 100 vom äußeren Rand aus
// Farbe individuell wählbar
int PunktX1 = PosX * 155 + Radius;
int PunktY1 = PosY * 155 + Radius;
int PunktX2 = PosX * 145 + Radius;
int PunktY2 = PosY * 145 + Radius;
tft.drawLine(PunktX1, PunktY1, PunktX2, PunktY2, Zeigerfarbe);
// keine Striche an der Position der Zahlen
if (Ziffernanzeigen)
{
if (PunktX1 == 160 || PunktX1 == 315 || PunktX1 == 5)
{
tft.drawLine(PunktX1, PunktY1, PunktX2, PunktY2, Kreisfarbe);
}
}
}
// alle 6 Grad Punkte als Sekundenmarkierung zeichnen
for (int i = 0; i < 360; i += 6)
{
PosX = cos((i - 90) * DEG_TO_RAD);
PosY = sin((i - 90) * DEG_TO_RAD);
// Positionen der Punkte
// 108 -> Abstand vom Mittelpunkt
PunktX = PosX * 150 + Radius;
PunktY = PosY * 150 + Radius;
tft.drawPixel(PunktX, PunktY, Zeigerfarbe);
}
// Markierung 12 3 6 9
if (Ziffernanzeigen)
{
tft.setTextSize(2);
tft.setTextColor(Zeigerfarbe);
tft.setCursor(145, 15);
tft.print("12");
tft.setCursor(10, 153);
tft.print("9");
tft.setCursor(300, 153);
tft.print("3");
tft.setCursor(155, 300);
tft.print("6");
}
if (DatumAnzeigen)
{
ZeigeDatum();
}
Zeitmessung = millis() + 1000;
}
void loop()
{
// Sekunden weiter zählen
if (Zeitmessung < millis())
{
Zeitmessung += 1000;
Sekunden++;
if (Sekunden == 60)
{
Sekunden = 0;
Minuten++;
// 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);
if (Minuten > 59)
{
Minuten = 0;
Stunden++;
if (Stunden > 23)
{
Stunden = 0;
}
}
}
// Vorausberechnung der x- und y-Koordinaten
// alle 6° eine Sekunde vorwärts
GradSekunden = Sekunden * 6;
// alle 6° eine Minute vorwärts
GradMinuten = Minuten * 6;
// alle 30° eine Stunde vorwärts
// 30 / 3600 = 0.0833333
// sorgt dafür, dass der Stundenzeiger entsprechend
// der Anzahl der Minuten weiter "wandert"
GradStunden = Stunden * 30 + GradMinuten * 0.0833333;
StundePosX = cos((GradStunden - 90) * DEG_TO_RAD);
StundePosY = sin((GradStunden - 90) * DEG_TO_RAD);
MinutePosX = cos((GradMinuten - 90) * DEG_TO_RAD) ;
MinutePosY = sin((GradMinuten - 90) * DEG_TO_RAD);
SekundePosX = cos((GradSekunden - 90) * DEG_TO_RAD);
SekundePosY = sin((GradSekunden - 90) * DEG_TO_RAD);
// nach jeder Minute Minuten-/Stundenzeiger löschen
// oder einmalig beim Start der Anzeige
if (Sekunden == 0 || Start)
{
Start = false;
// Datum anzeigen/nicht anzeigen
if (DatumAnzeigen) ZeigeDatum();
tft.drawLine(StundenZeigerX, StundenZeigerY, MitteHoehe, MitteHoehe + 1, Kreisfarbe);
// 62 Pixel -> Länge des Stundenzeigers
// Mittelpunkt + 1 -> Mittelpunkt soll nicht gelöscht werden
StundenZeigerX = StundePosX * 85 + MitteHoehe + 1;
StundenZeigerY = StundePosY * 85 + MitteHoehe + 1;
tft.drawLine(MinutenZeigerX, MinutenZeigerY, MitteHoehe, MitteHoehe + 1, Kreisfarbe);
// 84 Pixel -> Länge des Minutenzeigers
// Mittelpunkt + 1 -> Mittelpunkt soll nicht gelöscht werden
MinutenZeigerX = MinutePosX * 120 + MitteHoehe;
MinutenZeigerY = MinutePosY * 120 + MitteHoehe + 1;
}
// Sekundenzeiger löschen
if (!SekundenzeigerKreis) tft.drawLine(SekundenZeigerX, SekundenZeigerY, MitteHoehe, MitteHoehe + 1, Kreisfarbe);
// Kreis am Sekundenzeiger löschen, Radius 5
tft.fillCircle(SekundenZeigerX, SekundenZeigerY, 5, Kreisfarbe);
// 85 Pixel -> Länge des Sekundenzeigers
SekundenZeigerX = SekundePosX * 120 + MitteHoehe + 1;
SekundenZeigerY = SekundePosY * 120 + MitteHoehe + 1;
// Zeiger neu zeichnen
// Sekunden Linie nur anzeigen wenn SekundenzeigerKreis false
if (!SekundenzeigerKreis) tft.drawLine(SekundenZeigerX, SekundenZeigerY, MitteHoehe, MitteHoehe + 1, ROT);
// Minuten
tft.drawLine(MinutenZeigerX, MinutenZeigerY, MitteHoehe, MitteHoehe + 1, Zeigerfarbe);
// Stunden
tft.drawLine(StundenZeigerX, StundenZeigerY, MitteHoehe, MitteHoehe + 1, Zeigerfarbe);
// Kreis an der Spitze des Sekundenzeigers, Radius 5
tft.fillCircle(SekundenZeigerX, SekundenZeigerY, 5, ROT);
// Mittelpunkt zeichnen
tft.fillCircle(MitteHoehe, MitteHoehe + 1, 3, Zeigerfarbe);
}
}
void ZeigeDatum()
{
u8g2Schriften.setForegroundColor(GRUEN);
u8g2Schriften.setBackgroundColor(SCHWARZ);
u8g2Schriften.setCursor(10, 380);
u8g2Schriften.setFont(u8g2_font_fub35_tf);
// Bildschirmbereich für das Datum löschen
tft.fillRect(10, 320, tft.width(), tft.height(), SCHWARZ);
// 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.setCursor(10, 450);
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);
}
Analoge Uhr mit Temperatur-/Luftdruckanzeige mit BMP280

#include "WiFi.h"
#include "time.h"
#include "TFT_eSPI.h"
#include "U8g2_for_TFT_eSPI.h"
#include "Adafruit_BMP280.h"
Adafruit_BMP280 bmp;
// Pins anpassen
#define SDA_PIN 21
#define SCL_PIN 22
// Objekt für Schriften von U8g2 (u8g2Schriften)
U8g2_for_TFT_eSPI u8g2Schriften;
TFT_eSPI tft = TFT_eSPI();
// WiFi-Daten
char Router[] = "Router_SSID";
char Passwort[] = "xxxxxxx";
// Variablen des TFTs (Höhe, Breite, Radius)
const int MitteHoehe = 160;
const int MitteBreite = 160;
const int Radius = 160;
// Multiplikatoren für x- y-Positionen der Stunden, Minuten und Sekunden
float SekundePosX = 0, SekundePosY = 0, MinutePosX = 0, MinutePosY = 0, StundePosX = 0, StundePosY = 0;
float GradSekunden = 0, GradMinuten = 0, GradStunden = 0;
// x- y-Koordinaten für die Anzeige Stunden, Minuten und Sekunden
int SekundenZeigerX = MitteHoehe, SekundenZeigerY = MitteHoehe;
int MinutenZeigerX = MitteHoehe, MinutenZeigerY = MitteHoehe;
int StundenZeigerX = MitteHoehe, StundenZeigerY = MitteHoehe;
// Start wird nur beim ersten Start für den Aufbau des TFTs benötigt
bool Start = true;
unsigned long Zeitmessung = 0;
// Variablen für die Markierungen und Punkte und Striche des Ziffernblatts
float PosX, PosY;
int PunktX, PunktY, PunktX1, PunktX2, PunktY1, PunktY2;
// Variablen für die Zeit
int Stunden, Minuten, Sekunden;
// 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
// Farben innerer Kreis, Randfarbe und Zeigerfarbe
// die Farben der Zeiger können aber auch individuell gesetzt werden
const int Kreisfarbe = SCHWARZ;
const int Zeigerfarbe = WEISS;
const int Randfarbe = BORDEAUX;
// true -> Datum anzeigen
// false -> Datum nicht anzeigen
bool DatumAnzeigen = true;
// true -> Sekundenzeiger nur als Kreis
bool SekundenzeigerKreis = false;
// Ziffern 12 3 6 9 anzeigen/nicht anzeigen
bool Ziffernanzeigen = true;
// 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;
void setup()
{
// Wire mit I2C-Pins starten
Wire.begin(SDA_PIN, SCL_PIN);
bmp.begin(0x77);
// bmp.begin(0x76);
Serial.begin(9600);
// Schriften von u8g2 tft zuordnen
u8g2Schriften.begin(tft);
// Zeitzone: Parameter für die zu ermittelnde Zeit
configTzTime(Zeitzone, Zeitserver);
WiFi.mode(WIFI_STA);
// WiFi starten
WiFi.begin(Router, Passwort);
Serial.println("------------------------");
while (WiFi.status() != WL_CONNECTED)
{
delay(200);
Serial.print(".");
}
Serial.println();
Serial.print("Verbunden mit ");
Serial.println(Router);
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;
tft.begin();
tft.setRotation(0);
tft.fillScreen(Kreisfarbe);
// 4 Pixel breiter äußerer Rand, Farbe au der Farbpalette wählen
tft.drawCircle(MitteHoehe, MitteBreite, Radius - 1, Randfarbe);
tft.drawCircle(MitteHoehe, MitteBreite, Radius - 2, Randfarbe);
tft.drawCircle(MitteHoehe, MitteBreite, Radius - 3, Randfarbe);
tft.drawCircle(MitteHoehe, MitteBreite, Radius - 4, Randfarbe);
// innere Fläche bis auf den Rand von 4 Pixeln vollständig löschen
// wenn andere Farbe als äußerer Rand gewählt wird ergibt sich ein schmaler Rand
tft.fillCircle(MitteHoehe, MitteBreite, Radius - 4, Kreisfarbe);
/*
alle 30° Linie am Rand als Stundenmarkierung zeichnen
DEG_TO_RAD (= PI/180 = 0.0174532925) -> Winkel in Bogenmaß umrechnen
sin/cos berechnen die x-/y-Kordinaten des Punktes auf der Kreislinie
*/
for (int i = 0; i < 360; i += 30)
{
PosX = cos((i - 90) * DEG_TO_RAD);
PosY = sin((i - 90) * DEG_TO_RAD);
// kurze Linien zeichnen, von 114 bis 100 vom äußeren Rand aus
// Farbe individuell wählbar
int PunktX1 = PosX * 155 + Radius;
int PunktY1 = PosY * 155 + Radius;
int PunktX2 = PosX * 145 + Radius;
int PunktY2 = PosY * 145 + Radius;
tft.drawLine(PunktX1, PunktY1, PunktX2, PunktY2, Zeigerfarbe);
// keine Striche an der Position der Zahlen
if (Ziffernanzeigen)
{
if (PunktX1 == 160 || PunktX1 == 315 || PunktX1 == 5)
{
tft.drawLine(PunktX1, PunktY1, PunktX2, PunktY2, Kreisfarbe);
}
}
}
// alle 6 Grad Punkte als Sekundenmarkierung zeichnen
for (int i = 0; i < 360; i += 6)
{
PosX = cos((i - 90) * DEG_TO_RAD);
PosY = sin((i - 90) * DEG_TO_RAD);
// Positionen der Punkte
// 108 -> Abstand vom Mittelpunkt
PunktX = PosX * 150 + Radius;
PunktY = PosY * 150 + Radius;
tft.drawPixel(PunktX, PunktY, Zeigerfarbe);
}
// Markierung 12 3 6 9
if (Ziffernanzeigen)
{
tft.setTextSize(2);
tft.setTextColor(Zeigerfarbe);
tft.setCursor(145, 15);
tft.print("12");
tft.setCursor(10, 153);
tft.print("9");
tft.setCursor(300, 153);
tft.print("3");
tft.setCursor(155, 300);
tft.print("6");
}
if (DatumAnzeigen)
{
ZeigeDatum();
}
DatenAnzeigen();
Zeitmessung = millis() + 1000;
}
void loop()
{
// Sekunden weiter zählen
if (Zeitmessung < millis())
{
Zeitmessung += 1000;
Sekunden++;
if (Sekunden == 60)
{
Sekunden = 0;
Minuten++;
// 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);
if (Minuten > 59)
{
Minuten = 0;
Stunden++;
if (Stunden > 23)
{
Stunden = 0;
}
}
}
// Vorausberechnung der x- und y-Koordinaten
// alle 6° eine Sekunde vorwärts
GradSekunden = Sekunden * 6;
// alle 6° eine Minute vorwärts
GradMinuten = Minuten * 6;
// alle 30° eine Stunde vorwärts
// 30 / 3600 = 0.0833333
// sorgt dafür, dass der Stundenzeiger entsprechend
// der Anzahl der Minuten weiter "wandert"
GradStunden = Stunden * 30 + GradMinuten * 0.0833333;
StundePosX = cos((GradStunden - 90) * DEG_TO_RAD);
StundePosY = sin((GradStunden - 90) * DEG_TO_RAD);
MinutePosX = cos((GradMinuten - 90) * DEG_TO_RAD) ;
MinutePosY = sin((GradMinuten - 90) * DEG_TO_RAD);
SekundePosX = cos((GradSekunden - 90) * DEG_TO_RAD);
SekundePosY = sin((GradSekunden - 90) * DEG_TO_RAD);
// nach jeder Minute Minuten-/Stundenzeiger löschen
// oder einmalig beim Start der Anzeige
if (Sekunden == 0 || Start)
{
Start = false;
// Datum anzeigen/nicht anzeigen
if (DatumAnzeigen) ZeigeDatum();
DatenAnzeigen();
tft.drawLine(StundenZeigerX, StundenZeigerY, MitteHoehe, MitteHoehe + 1, Kreisfarbe);
// 62 Pixel -> Länge des Stundenzeigers
// Mittelpunkt + 1 -> Mittelpunkt soll nicht gelöscht werden
StundenZeigerX = StundePosX * 85 + MitteHoehe + 1;
StundenZeigerY = StundePosY * 85 + MitteHoehe + 1;
tft.drawLine(MinutenZeigerX, MinutenZeigerY, MitteHoehe, MitteHoehe + 1, Kreisfarbe);
// 84 Pixel -> Länge des Minutenzeigers
// Mittelpunkt + 1 -> Mittelpunkt soll nicht gelöscht werden
MinutenZeigerX = MinutePosX * 120 + MitteHoehe;
MinutenZeigerY = MinutePosY * 120 + MitteHoehe + 1;
}
// Sekundenzeiger löschen
if (!SekundenzeigerKreis) tft.drawLine(SekundenZeigerX, SekundenZeigerY, MitteHoehe, MitteHoehe + 1, Kreisfarbe);
// Kreis am Sekundenzeiger löschen, Radius 5
tft.fillCircle(SekundenZeigerX, SekundenZeigerY, 5, Kreisfarbe);
// 85 Pixel -> Länge des Sekundenzeigers
SekundenZeigerX = SekundePosX * 120 + MitteHoehe + 1;
SekundenZeigerY = SekundePosY * 120 + MitteHoehe + 1;
// Zeiger neu zeichnen
// Sekunden Linie nur anzeigen wenn SekundenzeigerKreis false
if (!SekundenzeigerKreis) tft.drawLine(SekundenZeigerX, SekundenZeigerY, MitteHoehe, MitteHoehe + 1, ROT);
// Minuten
tft.drawLine(MinutenZeigerX, MinutenZeigerY, MitteHoehe, MitteHoehe + 1, Zeigerfarbe);
// Stunden
tft.drawLine(StundenZeigerX, StundenZeigerY, MitteHoehe, MitteHoehe + 1, Zeigerfarbe);
// Kreis an der Spitze des Sekundenzeigers, Radius 5
tft.fillCircle(SekundenZeigerX, SekundenZeigerY, 5, ROT);
// Mittelpunkt zeichnen
tft.fillCircle(MitteHoehe, MitteHoehe + 1, 3, Zeigerfarbe);
}
}
void ZeigeDatum()
{
u8g2Schriften.setForegroundColor(GRUEN);
u8g2Schriften.setBackgroundColor(SCHWARZ);
u8g2Schriften.setCursor(10, 350);
u8g2Schriften.setFont(u8g2_font_fub20_tf);
// Bildschirmbereich für das Datum löschen
tft.fillRect(10, 320, tft.width(), tft.height(), SCHWARZ);
// 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);
}
void DatenAnzeigen()
{
// Temperatur lesen
String Temperatur = String(bmp.readTemperature());
// replace -> . durch , ersetzen
Temperatur.replace(".", ",");
String Luftdruck = String(bmp.readPressure() / 100);
Luftdruck.replace(".", ",");
u8g2Schriften.setForegroundColor(BLAU);
u8g2Schriften.setBackgroundColor(SCHWARZ);
u8g2Schriften.setCursor(10, 410);
u8g2Schriften.setFont(u8g2_font_logisoso38_tf);
u8g2Schriften.print(Luftdruck + " hPa");
u8g2Schriften.setCursor(10, 470);
u8g2Schriften.setForegroundColor(ROT);
u8g2Schriften.setBackgroundColor(SCHWARZ);
u8g2Schriften.print(Temperatur + "°C");
}
Quellen
- Adafruit Grafik-Bibliothek
- Espressif WiFi-API
- Definition der Farben als HEX-Code
- Bibliothek TFT_eSPI
- Dokumentation zu TFT_eSPI
- u8g2 für TFT_eSPI
- Schriftarten von u8g2
Letzte Aktualisierung: