Ich habe die letzten beiden Tage wieder etwas gebastelt. Die Idee, dass ich meine Termine für heute aus meiner Org-Agenda in der Wohnung permanent im Blick habe. Wenn ich auch noch die Termine für morgen, übermorgen und meine Todo-Liste abrufen kann, wäre das für mich perfekt.
So ungefähr stelle ich mir das Endergebnis vor ☺️
Da mich auch gereizt hat, einmal mit einem ESP32 etwas rumzuspielen, habe ich mir einfach zwei “Cheap Yellow Displays” (ESP32-2432S028R) bestellt.
Gemini war auch der Meinung, dass es mir alles vibecoden kann. Ich war neugierig, wie gut es wirklich funktionieren wird. Und ob ich meine Ideen und Vorstellungen vernünftig in Prompts formulieren kann.
🛑 ACHTUNG 🛑
Der folgende Quellcode wurde mit Google Gemini erstellt und von mir lediglich gesichtet und an einigen Stellen modifiziert. Du kannst den Code gerne verwenden und verändern. Aber frag mich bitte nicht um Support.
Geplanter Ablauf
Damit die Daten auf dem ESP32 angezeigt werden, habe ich folgenden Ablauf bzw. Weg der Daten geplant.
- Die Agenda wird von mir wie gehabt in Emacs gepflegt. Meine Computer sollen die Agenda mittels syncthing auf meinen Raspberry Pi übertragen.
Ein Hook in meiner
init.elsorgt zusätzlich dafür, dass beim Speichern sofort die Daten auf dem Pi aktualisiert werden. - Auf dem Pi soll eine lokale Emacs-Installation regelmäßig die Agenda auslesen und als JSON bereitstellen. Es sollen die Termine für heute, morgen und übermorgen sowie die TODO-Liste ausgelesen werden. Termine und TODOs folgen der chronologischen bzw. der Sortierung in der Agenda.
- Der ESP32 soll beim Start eine Verbindung zum Wifi aufbauen, die aktuelle Uhrzeit ermitteln und dann die JSON-Datei einlesen und darstellen.
- Der ESP32 aktualisiert alle fünf Minuten die Daten, in dem er die JSON-Datei vom Pi abruft.
Weitere Anforderungen an den ESP32
Ich habe noch zusätzlich diese Anforderungen an den ESP32 gehabt:
- Der ESP32 soll zwischen 22 und 6 Uhr das Display dimmen
- Ich kann über den Touchscreen per Tippen auf die linke oder rechte Seite zwischen den Seiten “HEUTE”, “MORGEN”, “UEBERMORGEN” und “TODOS” wechseln.
- Nach fünf Minuten springt der ESP32 immer auf die Seite “HEUTE” zurück
- In der Titelleiste soll immer die aktuelle Uhrzeit stehen. Sie wird jede Minute aktualisiert
- Auf der “HEUTE”-Seite soll das Datum stehen
- Es gibt eine Fußzeile. Über das Symbol “*” kann ich eine Seite mit Themes aufrufen.
- Die Themes können über das Touchscreen durchgeblättert und aktiviert werden.
- Auf der Theme-Seite kann über das Symbol “<-” zurück zu “HEUTE” gewechselt werden.
- Tipp-Animation beim Wechsel der Seite. Die Buchstaben sollen alle nacheinander erscheinen.
- Retro-Look wie C64
- Optional: Einen FadeOut- FadeIN-Mechanismus beim Refresh der angezeigten Seite (ist aktuell nicht in Verwendung)
Benötigte Hardware
Damit das alles auch funktioniert, benötigen wir ein paar Hardwarekomponenten.
ESP32 mit Display (Cheap Yellow Display)
Es gibt sogenannte “Cheap Yellow Display”-Module. Das ist ein ESP32, der gleich ein integriertes 2,8"-Touchdisplay hat. Ein Gerät kostet ca. 15 Euro. Ich habe gleich einen Doppelpack bestellt. Es gibt Varianten mit Mikro-USB oder USB-C. Ich habe gleich USB-C genommen.
Raspberry Pi
Damit die JSON-Datei immer für den ESP32 verfügbar ist, benötigen wir ein immer verfügbares System. Ich habe dafür einen Raspberry Pi genutzt, der hier eh bereits als kleiner Homeserver diverse Dinge erledigt.
Computer mit Emacs
Klar… wir benötigen einen Computer, damit wir darauf Emacs nutzen können.
Netzteil
Für den Betrieb des ESP32 benötigen wir ein einfaches USB-Netzteil. Fünf Watt sollten locker ausreichen. Bei meinen ESP32 war das passende USB-C-Kabel bereits dabei und ich hatte in einer Schublade noch alte, einfache Netzteile.
Umsetzung
Bereitstellung einer JSON-Datei aus der Agenda
Die JSON-Datei mit den Einträgen kann Emacs für uns erzeugen. Wir nutzen dafür auf dem Pi eine separate Emacs-Installation, die per Cronjob regelmäßig aufgerufen wird. Die Ausgabe ist eine agenda.json. Diese Datei muss in einem Ordner liegen, die von einem Webserver ausgeliefert werden kann. Ich habe dafür bei mir per Docker einfach nginx genutzt.
;; cyd-export.el - Automatischer Export für das ESP32-Display
(require 'json)
(require 'org)
(require 'org-agenda)
(require 'subr-x)
;; --- HIER DIE PFADE AUF DEM PI ANPASSEN ---
(setq org-agenda-files '("/home/benutzer/Agenda.org"))
(defvar cyd-json-zielpfad "/home/benutzer/html/agenda.json")
;; ------------------------------------------
(defun bereinige-esp32-text (text)
(let* ((s1 (replace-regexp-in-string "[ \t]+:[[:alnum:]_@]+:[ \t]*$" "" text))
(s2 (replace-regexp-in-string "ä" "ae" s1 t t))
(s3 (replace-regexp-in-string "ö" "oe" s2 t t))
(s4 (replace-regexp-in-string "ü" "ue" s3 t t))
(s5 (replace-regexp-in-string "Ä" "AE" s4 t t))
(s6 (replace-regexp-in-string "Ö" "OE" s5 t t))
(s7 (replace-regexp-in-string "Ü" "UE" s6 t t))
(s8 (replace-regexp-in-string "ß" "ss" s7 t t))
(s9 (replace-regexp-in-string "[^[:ascii:]]" "" s8)))
(string-trim s9)))
(defun hole-termine-fuer-tag (tage-in-zukunft)
(let* ((zeit (time-add (current-time) (days-to-time tage-in-zukunft)))
(datum (list (nth 4 (decode-time zeit))
(nth 3 (decode-time zeit))
(nth 5 (decode-time zeit))))
(tages-termine nil))
(dolist (datei (org-agenda-files))
;; Sicherstellen, dass wir nur echte Org-Dateien prüfen
(when (string-match "\\.org$" datei)
(let ((eintraege (org-agenda-get-day-entries datei datum :timestamp)))
(dolist (eintrag eintraege)
(let ((text (get-text-property 0 'txt eintrag))
(uhrzeit (get-text-property 0 'time eintrag)))
(when text
(let* ((sauberer-text (bereinige-esp32-text (substring-no-properties text)))
(anzeige-text (if (and (stringp uhrzeit) (not (string-empty-p uhrzeit)))
(concat (string-trim (substring-no-properties uhrzeit)) " " sauberer-text)
sauberer-text)))
(push (list (cons "titel" anzeige-text)) tages-termine))))))))
tages-termine))
(defun starte-cyd-export-prozess ()
(let ((meine-todos nil)
(heute (hole-termine-fuer-tag 0))
(morgen (hole-termine-fuer-tag 1))
(uebermorgen (hole-termine-fuer-tag 2)))
;; TODOs aus den konfigurierten Agenda-Dateien sammeln
(org-map-entries
(lambda ()
(let ((titel (org-get-heading t t t t)))
(push (list (cons "titel" (bereinige-esp32-text titel))) meine-todos)))
"/+TODO" 'agenda)
;; NEU: Wir drehen alle Listen mit 'nreverse' um, damit die Reihenfolge
;; exakt deiner Org-Datei und der chronologischen Uhrzeit entspricht.
(let ((json-daten (list (cons "todos" (nreverse meine-todos))
(cons "heute" (nreverse heute))
(cons "morgen" (nreverse morgen))
(cons "uebermorgen" (nreverse uebermorgen)))))
(with-temp-file cyd-json-zielpfad
(insert (json-encode json-daten))))))
;; Das Skript sofort ausführen, wenn es geladen wird
(starte-cyd-export-prozess)
So sieht dann meine JSON z. B. aus. Ich habe im Beispiel Zeilenumbrüche hinzugefügt. In der Datei selber fehlen sie.
{
"todos": [
{
"titel": "Unbeaufsichtigte Updates einrichten"
},
{
"titel": "Vorbereitung CoS"
},
{
"titel": "Vorbereitung DoIP"
},
{
"titel": "Balkonkasten schick machen"
},
{
"titel": "For all Mankind vom FTP laden"
},
{
"titel": "Keller aufraeumen"
},
{
"titel": "Emacs-Agenda per Caldav syncen"
},
{
"titel": "Prots default pruefen"
},
{
"titel": "Fett und oel fuer 3D Drucker besorgen"
},
{
"titel": "Groesseren V-Server bestellen und Dienste migrieren"
}
],
"heute": [
{
"titel": "Pfingsten"
}
],
"morgen": [
{
"titel": "Pfingsten"
},
{
"titel": "19:30-22:00 Spielrunde DoIP"
}
],
"uebermorgen": null
}
Cronjob einrichten
Um den Export zu automatisieren, habe ich einen Cronjob eingerichtet. Dafür einfach crontab -e aufrufen. Und folgenden Eintrag hinzufügen (natürlich den Pfad für die cyd-export.el anpassen). Der Cronjob läuft in meinem Beispiel immer um 0:00, 6:00, 12:00 und 18:00 Uhr. Für meine Zwecke ist das ausreichend, da ich noch beim Speichern in Emacs einen Hook nutze, der die JSON auch erzeugt. Wichtig ist die Aktualisierung um 0:00 Uhr. Sonst sind in der JSON nicht die Daten korrekt für “HEUTE”, “MORGEN”, und “UEBERMORGEN”.
0 */6 * * * /usr/bin/emacs --batch -l /home/benutzer/cyd-export.el
JSON-Datei per Webserver bereitstellen
Damit die Datei für den ESP32 erreichbar ist, muss diese per Webserver ausgeliefert werden. Ich habe bereits auf dem Pi einen nginx-Server im Betrieb. Daher habe ich diesen einfach genommen.
Benutzer vorbereiten
Bei mir unter Tumbleweed muss der Benutzer noch der Gruppe dialout hinzugefügt werden. Ich habe sonst keinen Zugriff auf den USB-Port zum Flashen.
WICHTIG: Diese Änderung wird erst übernommen, wenn der Benutzer einmal ab- und wieder angemeldet ist.
sudo usermod -aG dialout $USER
ESP32 programmieren
Arduino IDE vorbereiten
Für die Entwicklung habe ich die Arduino IDE über Flathub installiert. Das erschien mir am einfachsten.
Pakete installieren
In der IDE müssen wir für unser Programm einige Pakete vorab installieren. Das erfolgt über den Librarymanager.
Arduinojsonvon Benoit Blanchon Mit dieser Bibliothek verarbeiten wir die JSON-Datei.TFT-eSPIvon Bodmer Diese Bibliothek steuert das TFT-Display an.XPT2046_Touchscreenvon Paul Stoffregen Damit können wir die Touchfähigkeit des Screens nutzen
Board hinzufügen
Damit die Arduino IDE das Board auch ansprechen kann, installieren wir über den
Boards Managedas Paketesp32.
User_setup.hanpassenDamit das Display überhaupt später funktioniert, müssen wir die Datei
User_Setup.hanpassen. Die Datei liegt normalerweise unter~/Arduino/libraries/TFT_eSPI/. Ich habe folgenden Inhalt:#define USER_SETUP_INFO "User_Setup" #define ILI9341_2_DRIVER #define TFT_WIDTH 240 #define TFT_HEIGHT 320 #define TFT_BL 21 #define TFT_BACKLIGHT_ON HIGH #define TFT_MISO 12 #define TFT_MOSI 13 #define TFT_SCLK 14 #define TFT_CS 15 #define TFT_DC 2 #define TFT_RST -1 #define LOAD_GLCD #define LOAD_FONT2 #define LOAD_FONT4 #define SPI_FREQUENCY 55000000
Quellcode
Gemini und ich haben nach einigen Iterationen dann diesen finalen Code entwickelt. Ich werde ihn nicht weiter erklären. Ich verstehe ihn. Und die wichtigen Stellen sind kommentiert. Der Code setzt die Anforderungen um, die ich oben beschrieben habe. Zum Ausprobieren müssen die Daten für das WIFI und die URL zum JSON angepasst werden.
#include <WiFi.h>
#include <HTTPClient.h>
#include <ArduinoJson.h>
#include <TFT_eSPI.h>
#include <SPI.h>
#include <XPT2046_Touchscreen.h>
#include "time.h"
// --- HIER DEINE DATEN EINTRAGEN ---
const char* ssid = "WIFINAME";
const char* password = "WIFI-PASSWORT";
const char* json_url = "http://url/agenda.json"; // Pfad auf dem Pi
// ----------------------------------
// C64-spezifische Farbwerte
#define C64_DARK_BLUE 0x0011
#define C64_LIGHT_BLUE 0xA51F
#define DARKMODE_BG TFT_BLACK
#define DARKMODE_TEXT TFT_CYAN
// Anschlüsse für Touch auf CYD-Board
#define XPT2046_IRQ 36
#define XPT2046_MOSI 32
#define XPT2046_MISO 39
#define XPT2046_CLK 25
#define XPT2046_CS 33
SPIClass touchSPI(VSPI);
XPT2046_Touchscreen ts(XPT2046_CS, XPT2046_IRQ);
TFT_eSPI tft = TFT_eSPI();
int aktuelleAnsicht = 0;
int aktuelleSeite = 0; // NEU: Merkt sich, auf welcher Unterseite wir sind
String aktuelleDaten = "";
unsigned long letzterAbruf = 0;
bool isDarkMode = false;
int letzteMinute = -1;
// Helligkeit
const int MIN_HELLIGKEIT = 5;
const int MAX_HELLIGKEIT = 150;
int aktuelleHelligkeit = MAX_HELLIGKEIT;
// --- THEMES ---
struct Theme {
String name;
uint16_t bgColor;
uint16_t textColor;
};
const Theme themes[] = {
{ "C64 BASIC", 0x0011, 0xA51F }, // Klassisches C64 Blau
{ "MAINFRAME", TFT_BLACK, TFT_GREEN }, // Grüner Phosphor
{ "BERNSTEIN", TFT_BLACK, 0xFD20 }, // Warmes Terminal-Orange
{ "NORTON DOS", TFT_BLUE, TFT_YELLOW }, // Blau mit kräftigem Gelb
{ "DARKMODE", TFT_BLACK, TFT_CYAN }, // Schwarz mit Cyan
{ "DOS", TFT_BLACK, TFT_LIGHTGREY} // DOS
};
const int anzahlThemes = 6;
int aktuellesTheme = 0; // Startet mit C64
bool imThemeMenue = false; // Status: Sind wir auf der Agenda oder im Menü?
void setup() {
Serial.begin(115200);
pinMode(21, OUTPUT);
tft.init();
tft.invertDisplay(true);
tft.setRotation(1);
tft.setTextSize(2);
tft.fillScreen(C64_DARK_BLUE);
touchSPI.begin(XPT2046_CLK, XPT2046_MISO, XPT2046_MOSI, XPT2046_CS);
ts.begin(touchSPI);
ts.setRotation(1);
tft.setTextColor(C64_LIGHT_BLUE, C64_DARK_BLUE);
tft.drawString("COMMODORE 64 BASIC V2", 10, 10, 1);
tft.drawString("VERBINDE W-LAN...", 10, 30, 1);
// NEU: Dem ESP32 einen eigenen Namen für den Router geben
WiFi.setHostname("skynet-agenda");
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) { delay(500); }
// Automatische Sommer-/Winterzeit für Deutschland (MEZ/MESZ)
configTzTime("CET-1CEST,M3.5.0,M10.5.0/3", "pool.ntp.org");
datenAbrufen();
bildschirmZeichnen();
}
void loop() {
// 1. Automatische Aktualisierung & Auto-Rücksprung alle 5 Min.
if (millis() - letzterAbruf > 300000) {
//fadeAus(); // Falls du das Dimm-Feature beim Laden nutzen willst
datenAbrufen();
aktuelleAnsicht = 0; // Zurück zur Kategorie "HEUTE"
aktuelleSeite = 0; // Zurück auf die erste Seite
bildschirmZeichnen();
//fadeEin();
letzterAbruf = millis();
}
// 2. Uhrzeit-Ecke minütlich aktualisieren
struct tm timeinfo;
if (getLocalTime(&timeinfo)) {
if (timeinfo.tm_min != letzteMinute) {
zeichneKopfzeile();
}
}
// 3. Touch-Steuerung mit Ghost-Touch-Filter
if (ts.touched()) {
TS_Point p = ts.getPoint();
// Nur echte Berührungen (Z-Wert) durchlassen
if (p.z > 500) {
// A. Zuerst prüfen: Wurde die untere rechte Ecke (Stern / Zurück) getippt?
if (p.x > 2500 && p.y > 2500) {
imThemeMenue = !imThemeMenue; // Menü auf oder zu
bildschirmZeichnen();
delay(300);
return; // Bricht ab und startet die loop() sofort frisch
}
// B. Was passiert bei einem normalen Tipp auf den Bildschirm?
if (imThemeMenue) {
// Im Menü: Ein Tipp irgendwo anders schaltet das Theme durch (Live-Vorschau)
aktuellesTheme++;
if (aktuellesTheme >= anzahlThemes) aktuellesTheme = 0;
} else {
// In der Agenda: Deine originale Logik zum Blättern nach Links oder Rechts
if (p.x < 1500 || p.x > 2500) {
JsonDocument doc;
int maxSeitenAktuell = 1;
bool datenGelesen = false;
// Kurz nachschauen, wie viele Seiten die aktuelle Ansicht hat
if (aktuelleDaten != "" && !deserializeJson(doc, aktuelleDaten)) {
datenGelesen = true;
String jsonKeys[] = {"heute", "morgen", "uebermorgen", "todos"};
int anzahlElemente = doc[jsonKeys[aktuelleAnsicht]].size();
maxSeitenAktuell = (anzahlElemente == 0) ? 1 : ((anzahlElemente - 1) / 9) + 1;
}
if (p.x < 1500) {
// Nach Links tippen (zurück)
if (aktuelleSeite > 0) {
aktuelleSeite--;
} else {
aktuelleAnsicht--;
if (aktuelleAnsicht < 0) aktuelleAnsicht = 3;
if (datenGelesen) {
String jsonKeys[] = {"heute", "morgen", "uebermorgen", "todos"};
int anzahlNeu = doc[jsonKeys[aktuelleAnsicht]].size();
aktuelleSeite = (anzahlNeu == 0) ? 0 : ((anzahlNeu - 1) / 9);
} else {
aktuelleSeite = 0;
}
}
} else if (p.x > 2500) {
// Nach Rechts tippen (vorwärts)
aktuelleSeite++;
if (aktuelleSeite >= maxSeitenAktuell) {
aktuelleSeite = 0;
aktuelleAnsicht++;
if (aktuelleAnsicht > 3) aktuelleAnsicht = 0;
}
}
}
}
bildschirmZeichnen();
delay(300);
}
}
}
void zeichneKopfzeile() {
uint16_t bgColor = themes[aktuellesTheme].bgColor;
uint16_t textColor = themes[aktuellesTheme].textColor;
tft.setTextColor(textColor, bgColor);
tft.setTextSize(2);
struct tm timeinfo;
if (getLocalTime(&timeinfo)) {
// 1. Hintergrundbeleuchtung dimmen (Passiert immer im Hintergrund)
if (timeinfo.tm_hour >= 22 || timeinfo.tm_hour < 6) {
aktuelleHelligkeit = MIN_HELLIGKEIT;
} else {
aktuelleHelligkeit = MAX_HELLIGKEIT;
}
// 2. Zeitstempel je nach Ansicht formatieren
char timeString[20];
if (aktuelleAnsicht == 0) {
// Auf der "Heute"-Seite: Datum und Uhrzeit (z.B. "23.05.2026 14:38")
strftime(timeString, sizeof(timeString), "%d.%m.%Y %H:%M", &timeinfo);
} else {
// Auf allen anderen Seiten: Nur die Uhrzeit (z.B. "14:38")
strftime(timeString, sizeof(timeString), "%H:%M", &timeinfo);
}
// Text rechtsbündig an Position 310 ausgeben
tft.drawRightString(timeString, 310, 10, 1);
letzteMinute = timeinfo.tm_min;
}
}
void datenAbrufen() {
if (WiFi.status() == WL_CONNECTED) {
HTTPClient http;
http.begin(json_url);
int httpCode = http.GET();
if (httpCode == 200) {
aktuelleDaten = http.getString();
}
http.end();
}
}
void bildschirmZeichnen() {
uint16_t bgColor = themes[aktuellesTheme].bgColor;
uint16_t textColor = themes[aktuellesTheme].textColor;
tft.fillScreen(bgColor);
tft.setTextColor(textColor, bgColor);
tft.setTextSize(2);
tft.setTextWrap(false); // Tippanimation erzeugt sonst Zeilenumbrüche
zeichneKopfzeile();
if (imThemeMenue) {
// --- THEME-MENÜ ZEICHNEN ---
tft.drawString("THEMES", 10, 10, 1);
tft.drawLine(10, 28, 310, 28, textColor);
int yPosition = 40;
for (int i = 0; i < anzahlThemes; i++) {
// Setzt einen fetten Pfeil vor das aktive Theme
String zeile = (i == aktuellesTheme) ? "> " : "- ";
zeile += themes[i].name;
tft.setCursor(10, yPosition);
tft.print(zeile); // Wir printen im Menü sofort (ohne Delay), damit der Live-Wechsel flüssig ist
yPosition += 18;
}
} else {
String kategorien[] = { "HEUTE", "MORGEN", "UEBERMORGEN", "OFFENE TODOs" };
String headerTitel = kategorien[aktuelleAnsicht];
if (aktuelleDaten == "") {
tft.drawString(headerTitel, 10, 10, 1);
tft.drawLine(10, 28, 310, 28, textColor);
tft.drawString("WARTE AUF DATEN...", 10, 40, 1);
} else {
JsonDocument doc;
DeserializationError fehler = deserializeJson(doc, aktuelleDaten);
if (!fehler) {
String jsonKeys[] = { "heute", "morgen", "uebermorgen", "todos" };
JsonArray liste = doc[jsonKeys[aktuelleAnsicht]];
int totalItems = liste.size();
int maxSeiten = (totalItems == 0) ? 1 : ((totalItems - 1) / 9) + 1;
if (aktuelleSeite >= maxSeiten) aktuelleSeite = maxSeiten - 1;
if (maxSeiten > 1) {
headerTitel += " (" + String(aktuelleSeite + 1) + "/" + String(maxSeiten) + ")";
}
tft.drawString(headerTitel, 10, 10, 1);
tft.drawLine(10, 28, 310, 28, textColor);
int startIdx = aktuelleSeite * 9;
int endIdx = startIdx + 9;
int index = 0;
int yPosition = 40;
for (JsonObject eintrag : liste) {
if (index >= startIdx && index < endIdx) {
String text = eintrag["titel"];
text.toUpperCase();
// NEU: Die Länge etwas schärfer abschneiden (23 statt 25),
// damit es inklusive dem "- " exakt in die 320 Pixel der Breite passt.
if (text.length() > 23) {
text = text.substring(0, 20) + "...";
}
tft.setCursor(10, yPosition);
String zeile = "- " + text;
for (int i = 0; i < zeile.length(); i++) {
tft.print(zeile[i]);
delay(5);
}
yPosition += 18;
}
index++;
}
} else {
tft.drawString(headerTitel, 10, 10, 1);
tft.drawLine(10, 28, 310, 28, textColor);
tft.drawString("FEHLER BEIM LESEN", 10, 40, 1);
}
}
}
tft.drawString("READY.", 10, 210, 1);
tft.fillRect(100, 210, 15, 15, textColor);
// Dynamischer Button unten rechts
if (imThemeMenue) {
tft.drawString("<-", 290, 210, 1); // Zurück-Pfeil im Menü
} else {
tft.drawString("*", 300, 210, 1); // Stern in der Agenda
}
}
void fadeAus() {
// Dimmt das Display in kleinen Schritten von 200 auf 0 (aus)
for (int i = aktuelleHelligkeit; i >= 0; i -= 5) {
analogWrite(21, i);
delay(15); // Je höher der Delay, desto langsamer das Dimmen
}
}
void fadeEin() {
// Erhellt das Display sanft wieder auf unseren Zielwert 200
for (int i = 0; i <= aktuelleHelligkeit; i += 5) {
analogWrite(21, i);
delay(15);
}
}
Flashen
Den Quellcode mit Arduino einfach kompilieren und dann flashen. Danach sollte die Platine neustarten, sich mit dem WLAN verbinden und die Daten anzeigen.
Gehäuse
Da ich einen 3D-Drucker habe, konnte ich mir ein passendes Gehäuse über Makerworld suchen. Ich musste lediglich die Öffnung für die Anschlüsse etwas vergrößern. Aber dieses Gehäuse gefällt mir am besten und hat gut funktioniert. Es werden lediglich vier Schrauben M3x12 benötigt.
Desk stand for XTouch with ESP32 CYD JC2432W328 - Free 3D Print Model - Maker…
Fazit
Wenn alles geklappt hat, dann leuchtet das Display jetzt in nettem C64-Blau und zeigt die heutigen Termine an. Über einen Tipp auf die rechte Seite des Touchscreens blättere ich eine Seite weiter. Links entsprechend zurück.

Abbildung 1: Agendadisplay mit den heutigen Terminen

Abbildung 2: Agendadisplay im fertigen Gehäuse