Skip to content

Technik Blog

IoT, Smart Home & Mikrocontroller einfach erklärt

Menu
  • Smarthome
  • Gartenautomation
  • Mikrocontroller
    • Arduino
    • ESP32 & Co.
    • Calliope Mini
    • Raspberry Pi & Pico
  • Solo Mining
  • Über mich
Menu

ICE-Tacho mit ESP32: Zuggeschwindigkeit aus dem WIFIonICE auf einem runden Display anzeigen

Veröffentlicht am 1. November 20261. November 2026 von Stefan Draeger

Wie schnell fährt der ICE gerade wirklich? Genau diese Frage kam mir während einer Zugfahrt. Passenderweise hatte ich das runde CrowPanel ESP32 Display mit 1,28 Zoll und 240 × 240 Pixeln dabei. Die Form des Displays bietet sich für einen Tachometer geradezu an – und statt einen simulierten Wert anzuzeigen, wollte ich die tatsächliche Geschwindigkeit des Zuges verwenden.

ICE-Tacho mit ESP32
ICE-Tacho mit ESP32

Die Geschwindigkeit stellt das ICE Portal im Bordnetz bereit. Was zunächst nach einem kleinen ESP32-Projekt aussah, wurde dadurch allerdings technisch interessanter als gedacht: Der ESP32 erreichte den Server, scheiterte aber beim TLS-Handshake. Die funktionierende Lösung besteht deshalb aus einer kleinen Java-Bridge auf dem Notebook und MicroPython auf dem CrowPanel. Das Ergebnis ist ein analoger Tacho mit einer Skala bis 300 km/h, roter Nadel und zusätzlicher digitaler Geschwindigkeitsanzeige.

Inhaltsverzeichnis

  • Hardware und Aufbau des Projekts
  • Die aktuelle ICE-Geschwindigkeit aus dem WIFIonICE
  • Warum ich die API nicht direkt auf dem ESP32 abrufe
  • Architektur: Java als Bridge zwischen ICE Portal und ESP32
  • Software-Voraussetzungen und Java-Projekt bauen
  • Java-Projekt mit Maven und jSerialComm
  • MicroPython und das Problem mit dem 115-KB-Framebuffer
  • Analoger ICE-Tacho mit Skala bis 300 km/h
  • Geschwindigkeit seriell in MicroPython empfangen
  • Reihenfolge beim Starten des Prototyps
  • Mögliche Erweiterungen
  • Komplettes Projekt herunterladen
  • Fazit

Hardware und Aufbau des Projekts

Für den Prototyp bleibt der Aufbau bewusst einfach. Das CrowPanel ist per USB mit dem Notebook verbunden. Gleichzeitig befindet sich das Notebook im WLAN WIFIonICE. Weitere Sensoren oder ein GPS-Modul werden nicht benötigt.

KomponenteAufgabe
CrowPanel ESP32 Display 1.28″Rundes 240 × 240 Display mit ESP32-C3
Notebook / PCRuft die ICE-Portal-API per HTTPS ab
USB-KabelSerielle Verbindung zwischen Java und ESP32
WIFIonICEZugriff auf das lokale ICE Portal
MicroPythonEmpfängt die Geschwindigkeit und zeichnet den Tacho

Das Display ist das CrowPanel ESP32 Display 1.28inch. Auf meinem Board läuft MicroPython 1.29.0 auf einem ESP32-C3.

rundes Elecrow Crowpanel mit 1,28" OLED
rundes Elecrow Crowpanel mit 1,28″ OLED
Rückseite des runden runden Mikrocontrollers mit Sensoren und Aktoren
Rückseite des runden runden Mikrocontrollers mit Sensoren und Aktoren
ESP32-C3 Chip auf dem runden Elecrow Crowpanel
ESP32-C3 Chip auf dem runden Elecrow Crowpanel

Die aktuelle ICE-Geschwindigkeit aus dem WIFIonICE

Im Bordnetz des ICE ist unter iceportal.de eine Status-API erreichbar. Für dieses Projekt ist der folgende Endpunkt interessant:

Hinweis zum ICE Portal: Der hier verwendete API-Endpunkt ist nur aus dem Bordnetz des Zuges erreichbar. Das Notebook muss dafür mit WIFIonICE verbunden sein; außerhalb des Zuges lässt sich die Adresse in der Regel nicht aufrufen. Die Schnittstelle gehört zum ICE Portal und ist kein Bestandteil dieses Projekts. Aufbau, Endpunkte und verfügbare Felder können sich deshalb künftig ändern oder zeitweise nicht verfügbar sein.

https://iceportal.de/api1/rs/status

Während meiner Fahrt lieferte die API beispielsweise folgende Antwort:

{
    "bapServiceStatus": "PAUSED",
    "connection": true,
    "serviceLevel": "AVAILABLE_SERVICE",
    "gpsStatus": "VALID",
    "internet": "HIGH",
    "latitude": 50.8749156667,
    "longitude": 9.5873923333,
    "series": "401",
    "speed": 220.6,
    "trainType": "ICE",
    "tzn": "ICE0157",
    "vzn": 995,
    "wagonClass": "FIRST"
}
JSON Response vom iceportal
JSON Response vom iceportal

Für den ersten Tacho interessiert mich nur das Feld speed. Der Wert 220.6 entspricht dabei 220,6 km/h. Die API liefert aber bereits deutlich mehr Informationen, die sich später für weitere Ansichten nutzen lassen.

Warum ich die API nicht direkt auf dem ESP32 abrufe

Mein erster Ansatz war naheliegend: ESP32 mit WIFIonICE verbinden, Status-API aufrufen und den Wert direkt darstellen. Das WLAN selbst funktionierte problemlos. Auch die Namensauflösung war erfolgreich; iceportal.de wurde im Zug auf die lokale Adresse 172.18.1.110 aufgelöst.

Ein Zugriff über HTTP auf Port 80 erreichte den Server ebenfalls, wurde aber konsequent auf HTTPS umgeleitet:

HTTP/1.1 301 Moved Permanently
Location: https://iceportal.de/api1/rs/status

Beim HTTPS-Zugriff brach MicroPython den TLS-Handshake dagegen mit folgendem Fehler ab:

MBEDTLS_ERR_SSL_FATAL_ALERT_MESSAGE

Ein Gegencheck mit dem Arduino-ESP32-Core endete an derselben Stelle. Der Fehler war damit nicht auf meinen MicroPython-Code beschränkt:

HTTP Code: -1
HTTP Fehler: connection refused
TLS Error Code: -30592
TLS Error: SSL - A fatal alert message was received from our peer

Ein kleiner Test der beim Arduino-Build aktivierten mbedTLS-Optionen zeigte außerdem:

TLS 1.2: JA
TLS 1.3: NEIN
TLS 1.3 Ephemeral KEX: NEIN
X25519: JA
secp256r1 / P-256: JA

Auf meinem Windows-Notebook funktionierte derselbe API-Aufruf dagegen mit TLS 1.3 und lieferte HTTP 200. Für den Prototyp wollte ich deshalb nicht erst einen eigenen ESP-IDF- oder MicroPython-Build erstellen. Da das Notebook ohnehin per USB mit dem CrowPanel verbunden ist, übernimmt es einfach den HTTPS-Teil.

Architektur: Java als Bridge zwischen ICE Portal und ESP32

Damit ergibt sich für den Prototyp eine klare Aufgabenteilung:

Java ruft die API ab und extrahiert den Wert speed. Statt das komplette JSON an den ESP32 zu schicken, übertrage ich nur eine einzelne Textzeile wie 220.6. Das hält das serielle Protokoll klein und die MicroPython-Seite muss kein JSON mehr parsen.

Software-Voraussetzungen und Java-Projekt bauen

Bevor die Java-Bridge gestartet werden kann, müssen auf dem Notebook einige Werkzeuge vorhanden sein. Getestet habe ich das Projekt unter Windows mit OpenJDK / Eclipse Temurin 11 und Maven. Für die MicroPython-Seite verwende ich Thonny zum Kopieren der Dateien auf das CrowPanel.

SoftwareVerwendung
OpenJDK / Eclipse Temurin 11Ausführen und Kompilieren der Java-Bridge
Apache MavenAbhängigkeiten laden und das Java-Projekt bauen
jSerialComm 2.11.4Serielle Kommunikation mit dem ESP32-C3; wird über Maven eingebunden
ThonnyMicroPython-Dateien auf das CrowPanel übertragen und testen
MicroPython 1.29.0Getestete Firmware auf meinem ESP32-C3

Das Maven-Projekt ist auf Java 11 ausgelegt. Deshalb steht in meiner pom.xml auch das entsprechende Compiler-Release:

<properties>
    <maven.compiler.release>11</maven.compiler.release>
</properties>

Im Verzeichnis des Java-Projekts wird anschließend das Projekt inklusive der benötigten Abhängigkeiten gebaut:

mvn clean package

Nach einem erfolgreichen Build liegen die kompilierten Klassen im Verzeichnis target/classes. Die Bibliothek jSerialComm wird dabei automatisch aus dem Maven-Repository geladen. Erst danach lässt sich die Bridge mit dem weiter unten gezeigten java -cp-Befehl starten.

Java-Projekt mit Maven und jSerialComm

Für die serielle Verbindung verwende ich jSerialComm. Im Maven-Projekt reicht dafür die folgende Dependency:

<dependency>
    <groupId>com.fazecast</groupId>
    <artifactId>jSerialComm</artifactId>
    <version>2.11.4</version>
</dependency>

Die Java-Anwendung öffnet den seriellen Port mit 115200 Baud. Wichtig war bei meinem ESP32-C3, DTR und RTS zu deaktivieren. Beim ersten Versuch wurde das Board sonst beim Öffnen des Ports zurückgesetzt.

serialPort.setComPortParameters(
        115200,
        8,
        SerialPort.ONE_STOP_BIT,
        SerialPort.NO_PARITY
);

serialPort.setFlowControl(
        SerialPort.FLOW_CONTROL_DISABLED
);

serialPort.clearDTR();
serialPort.clearRTS();

Nach dem Öffnen des Ports wartet die Anwendung einige Sekunden, damit main.py auf dem ESP32 sicher gestartet ist. Danach wird die ICE-API regelmäßig abgefragt.

Für dieses Projekt reicht es, das Feld speed aus der Antwort zu extrahieren und anschließend als eine Zeile zu senden:

private static final Pattern SPEED_PATTERN =
        Pattern.compile(
                "\"speed\"\\s*:\\s*([0-9]+(?:\\.[0-9]+)?)"
        );

private static void sendSpeed(
        OutputStream serialOut,
        double speed
) throws Exception {

    String message = String.format(
            java.util.Locale.US,
            "%.1f%n",
            speed
    );

    serialOut.write(
            message.getBytes(StandardCharsets.US_ASCII)
    );

    serialOut.flush();
}

In der Konsole sieht das anschließend beispielsweise so aus:

HTTP: 200
-> ESP32: 218.6 km/h

HTTP: 200
-> ESP32: 220.4 km/h

HTTP: 200
-> ESP32: 222.1 km/h

MicroPython und das Problem mit dem 115-KB-Framebuffer

Auf der Displayseite kam noch ein zweites Problem hinzu. Der originale GC9A01-Treiber legt einen kompletten RGB565-Framebuffer für alle 240 × 240 Pixel an. Dafür werden auf einen Schlag 115.200 Byte benötigt:

240 x 240 x 2 Byte = 115.200 Byte

Obwohl MicroPython noch deutlich mehr als 115 KB freien Heap meldete, konnte dieser große zusammenhängende Block auf meinem ESP32-C3 nicht reserviert werden:

MemoryError: memory allocation failed, allocating 115200 bytes

Die funktionierende Lösung ist ein Teil-Framebuffer. Statt das komplette Display im RAM zu halten, verwende ich nur 240 × 80 Pixel. Das reduziert den benötigten Speicher auf 38.400 Byte. Der Bildschirm wird in drei horizontale Bereiche aufgeteilt und nacheinander aktualisiert.

FramebufferSpeicherbedarfErgebnis
240 × 240 Pixel115.200 ByteMemoryError auf meinem ESP32-C3
240 × 80 Pixel38.400 Byteläuft stabil

Dafür erbt meine Klasse weiterhin von framebuf.FrameBuffer, verwendet aber nur die reduzierte Höhe. Die bewährten Methoden für Initialisierung und SPI-Zugriff kommen weiterhin aus dem vorhandenen Elecrow-Treiber.

class LCDPartial(framebuf.FrameBuffer):

    WIDTH = 240
    BAND_HEIGHT = 80

    write_cmd = LCD_1inch28.write_cmd
    write_data = LCD_1inch28.write_data
    init_display = LCD_1inch28.init_display
    setWindows = LCD_1inch28.setWindows
    write_text = LCD_1inch28.write_text

    def __init__(self):
        self.width = self.WIDTH
        self.height = self.BAND_HEIGHT

        self.buffer = bytearray(
            self.width * self.height * 2
        )

        super().__init__(
            self.buffer,
            self.width,
            self.height,
            framebuf.RGB565
        )

Analoger ICE-Tacho mit Skala bis 300 km/h

Nachdem Kommunikation und Speicherproblem gelöst waren, konnte ich mich endlich der eigentlichen Anzeige widmen. Der Tacho besitzt eine weiße Skala von 0 bis 300 km/h, Markierungen im Abstand von 10 km/h und Beschriftungen bei 0, 50, 100, 150, 200, 250 und 300 km/h. Die aktuelle Geschwindigkeit wird über eine rote Tachonadel dargestellt.

rundes CrowPanel von Elecrow mit Geschwindigkeit des ICE
rundes CrowPanel von Elecrow mit Geschwindigkeit des ICE

Die Position auf der Skala ergibt sich direkt aus dem Verhältnis zwischen aktueller und maximaler Geschwindigkeit:

MAX_SPEED = 300
ARC_START = 150
ARC_SWEEP = 240

progress = speed / MAX_SPEED
angle = ARC_START + ARC_SWEEP * progress

Aus dem Winkel berechne ich die Position der Nadelspitze mit Sinus und Kosinus:

def polar_point(angle_deg, radius):
    rad = math.radians(angle_deg)

    x = int(
        CENTER_X +
        math.cos(rad) * radius
    )

    y = int(
        CENTER_Y +
        math.sin(rad) * radius
    )

    return x, y

Zusätzlich befindet sich unten mittig zwischen den Enden der Skala eine kleine digitale Anzeige. So liefert die Nadel einen schnellen visuellen Eindruck, während der exakte Wert weiterhin direkt ablesbar bleibt.

Geschwindigkeit seriell in MicroPython empfangen

Da Java nur noch eine Zahl pro Zeile sendet, ist der Empfang auf dem ESP32 sehr kompakt. Eine Zeile wie 220.6 wird direkt in einen Float umgewandelt und anschließend neu gezeichnet:

while True:
    try:
        line = sys.stdin.readline()

        if not line:
            continue

        line = line.strip()

        if not line:
            continue

        speed = float(line)

        render_speedometer(
            lcd,
            speed
        )

    except ValueError:
        pass

Reihenfolge beim Starten des Prototyps

Beim Testen ist wichtig, dass Thonny und Java nicht gleichzeitig denselben COM-Port öffnen können. Deshalb starte ich das Projekt in dieser Reihenfolge:

  1. main.py und display_partial.py auf dem ESP32 speichern.
  2. CrowPanel resetten und prüfen, ob die Tachoanzeige startet.
  3. Thonny vom seriellen Port trennen oder schließen.
  4. Java-Bridge auf demselben COM-Port starten.
  5. Java ruft die ICE-API ab und sendet die Geschwindigkeit an MicroPython.

In meinem Test war der MicroPython-Port COM10. Der Java-Aufruf sah entsprechend so aus:

java -cp "target\classes;$env:USERPROFILE\.m2\repository\com\fazecast\jSerialComm\2.11.4\jSerialComm-2.11.4.jar" de.draegerit.IceSerialBridge COM10

Mögliche Erweiterungen

Für die erste Version reicht mir die Geschwindigkeit. Die API liefert aber bereits weitere Daten wie Zugtyp, Zugnummer, Baureihe, GPS-Status, Internetqualität und Position. Daraus könnte später ein kleines ICE-Dashboard mit mehreren Ansichten entstehen.

  • Zugnummer und Baureihe anzeigen
  • GPS-Status und Internetqualität darstellen
  • Position ausgeben
  • nächsten Halt und Verspätung ergänzen
  • Touchscreen zum Wechseln zwischen mehreren Ansichten nutzen
  • Java-Bridge später durch einen eigenen TLS-1.3-fähigen ESP32-Build ersetzen

Komplettes Projekt herunterladen

Damit du das Projekt nicht aus den einzelnen Codeblöcken zusammensetzen musst, stelle ich dir die vollständigen Projektdateien als ZIP-Archiv zur Verfügung. Enthalten sind die Java-Bridge für den Zugriff auf das ICE Portal sowie die MicroPython-Dateien für das CrowPanel ESP32 Display inklusive des speichersparenden Teil-Framebuffers und der Tachoanzeige.

ice-tacho-esp32-wifionice-projekt.zipHerunterladen

Nach dem Entpacken müssen gegebenenfalls noch der verwendete COM-Port und die lokale Umgebung angepasst werden. Auf dem CrowPanel werden die MicroPython-Dateien gespeichert, während die Java-Anwendung auf dem Notebook läuft. Wichtig ist außerdem, Thonny vor dem Start der Java-Bridge vom seriellen Port zu trennen, da nicht beide Programme gleichzeitig auf denselben COM-Port zugreifen können.

Fazit

Aus der einfachen Idee, die aktuelle ICE-Geschwindigkeit auf einem runden Display darzustellen, wurde ein Projekt mit mehreren interessanten technischen Problemen. Das WLAN und die lokale API waren schnell gefunden, der TLS-Handshake auf dem ESP32 machte den direkten Zugriff aber zunächst unbrauchbar. Mit der Java-Bridge lässt sich das sauber umgehen, ohne für den Prototyp gleich eine eigene ESP-IDF-Firmware bauen zu müssen.

Auch auf der Displayseite war der direkte Weg nicht der beste: Der vollständige 240 × 240 RGB565-Framebuffer benötigt 115.200 Byte am Stück und ließ sich unter meiner MicroPython-Konfiguration nicht zuverlässig reservieren. Der kleinere 240 × 80 Teil-Framebuffer läuft dagegen stabil und reicht aus, um den kompletten Tacho in drei Bereichen zu zeichnen.

Das Ergebnis ist genau das, was ich unterwegs haben wollte: Während der Fahrt zeigt mir das kleine runde CrowPanel die reale Geschwindigkeit des ICE auf einer analogen Skala bis 300 km/h an – inklusive roter Tachonadel und digitalem Wert.

Transparenzhinweis: Dieser Beitrag wurde mit Unterstützung künstlicher Intelligenz erstellt und redaktionell geprüft. Mehr zur Nutzung von KI

Letzte Aktualisierung am: 01. November 2026

Foto von Stefan Draeger
Über den Autor

Stefan Draeger — Entwickler & Tech-Blogger

Ich zeige praxisnah, wie du Projekte mit Arduino, ESP32 und Smarthome-Komponenten umsetzt – Schritt für Schritt, mit Code und Schaltplänen.

Mehr Artikel von Stefan →

Schreibe einen Kommentar Antwort abbrechen

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

Kategorien

Kontakt & Hilfe

Fragen oder Feedback?

Du hast eine Idee, brauchst Hilfe oder möchtest Feedback loswerden? Erstelle einfach ein Support-Ticket.

Support-Ticket erstellen
Keine Beiträge verpassen

Newsletter abonnieren

Erhalte regelmäßig Updates zu neuen Projekten, Tutorials und Tipps rund um Arduino, ESP32, Smarthome und IoT direkt in dein Postfach.

Newsletter abonnieren
Videos & Tutorials

Draeger-IT auf YouTube

Entdecke meine Videos zu Arduino, ESP32, Raspberry Pi, Smarthome und spannenden Elektronikprojekten – mit Tutorials, Tests und Praxisbeispielen.

YouTube-Kanal besuchen
Freiwillige Unterstützung

Unterstütze meinen Blog

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

Auf Tipeee unterstützen

Vielen Dank für deinen Support!
– Stefan Draeger

Kostenlose Online-Werkzeuge

Tools

Praktische Generatoren, Rechner und Hilfsmittel für deine Elektronik- und Entwicklungsprojekte.

Generatoren & Konverter

  • QR-Code Generator
  • Passwort Generator
  • PNG zu WebP konvertieren
  • Bilder online komprimieren
  • Code online formatieren

Rechner & Tabellen

  • Unix-Zeitstempel-Rechner
  • ASCII-Tabelle
  • ASCII Code umrechnen – Text, Dezimal, Hex & Binär
  • Spannung, Strom, Widerstand und Leistung berechnen
  • Widerstandsrechner

LED-Matrix

  • 8×8 LED Matrix Tool
  • 8×16 LED Matrix Modul von Keyestudio
  • 16×16 LED Matrix Generator
Kontakt

Anschrift & Kontaktdaten

Stefan Draeger

Königsberger Str. 13
38364 Schöningen

Telefon 01556 5432686

E-Mail info@draeger-it.blog

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

Folge mir auf

Weitere Projekte, Videos und Neuigkeiten findest du auch auf meinen Social-Media-Kanälen.

  • f Facebook
  • in LinkedIn
  • ▶ YouTube
  • ♪ TikTok
  • P Pinterest
  • ◎ Instagram
Rechtliche Informationen

Rechtliches

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