OpenSpeed is a free, open-source system for measuring the scale speed and physical length of model trains — using an IR sensor module and a Windows desktop app. OpenSpeed ist ein kostenloses Open-Source-System zur Messung der Maßstabsgeschwindigkeit und Länge von Modellzügen – mit einem IR-Sensor-Modul und einer Windows-Desktop-App.
Two IR sensors on a straight section of track — at a distance you choose and configure in the firmware — measure both speed and train length without any modifications to the locomotive.
Zwei IR-Sensoren auf einem geraden Gleisabschnitt – in einem frei wählbaren, in der Firmware konfigurierten Abstand – messen Geschwindigkeit und Zuglänge ohne Eingriff in die Lokomotive.
Whichever sensor fires first becomes the start sensor. Direction is detected automatically — no manual configuration needed.Der Sensor, der zuerst auslöst, wird zum Startsensor. Die Richtung wird automatisch erkannt.
Transit time between sensors is measured in milliseconds. speed = SENSOR_DISTANCE_M ÷ time × 3.6 gives km/h.Die Durchfahrtszeit wird in ms gemessen. Geschwindigkeit = SENSOR_DISTANCE_M ÷ Zeit × 3,6 ergibt km/h.
Blockage duration × speed = physical length in cm. Result is published via REST after a 1-second debounce.Sperrzeit × Geschwindigkeit = physische Länge in cm. Ergebnis nach 1 s Entprellzeit per REST verfügbar.
speed_kmh value by the configured scale factor (e.g. ×87 for H0) to produce the prototype speed. Scale conversion happens entirely in software.
Die Desktop-App multipliziert den rohen speed_kmh-Wert mit dem Maßstabsfaktor (z. B. ×87 für H0). Die Umrechnung erfolgt ausschließlich in der Software.
Two measurement modes, real-time results, and full DCC integration — all in one application.
Zwei Messmodi, Echtzeitergebnisse und vollständige DCC-Integration – in einer Anwendung.
Automatically sweeps every DCC speed step from a configurable start to the maximum, measuring a forward and backward pass for each step. Results appear on a live chart as they arrive.Misst automatisch jeden DCC-Fahrstufenschritt vorwärts und rückwärts. Ergebnisse erscheinen sofort live im Diagramm.
Measures the physical length of any train in centimetres. Forward and backward passes are averaged for accuracy.Misst die physische Länge eines Zuges in Zentimetern. Vorwärts- und Rückwärtsfahrt werden gemittelt.
Forward (blue) and backward (orange) passes shown with a legend. X axis fixed to 0–128 speed steps; Y auto-expands if speeds exceed 200 km/h.Vorwärts (blau) und rückwärts (orange) mit Legende. X-Achse auf 0–128 Fahrstufen, Y expandiert automatisch.
Export all measured speed steps to a formatted .xlsx spreadsheet with one click. Pre-named with decoder address and timestamp.Alle Fahrstufen mit einem Klick in eine .xlsx-Tabelle exportieren, vorbenannt mit Decoderadresse und Zeitstempel.
Switch themes via the sun/moon button. The chart adapts automatically. The chosen theme persists across restarts.Wechsel per Sonne/Mond-Schaltfläche. Das Diagramm passt sich an. Die Einstellung bleibt nach Neustart erhalten.
Switch language at runtime from the header dropdown — no restart required.Umschalten im laufenden Betrieb – kein Neustart erforderlich.
The sensor module is built from off-the-shelf components costing under €20. The Z21 is your existing DCC command station.
Das Sensormodul besteht aus Standard-Komponenten für unter 20 €. Die Z21 ist Ihre vorhandene DCC-Zentrale.
| ComponentBauteil | QtyAnz. | NotesHinweise |
|---|---|---|
| ESP32 DevKit v1 | 1× | Any 38-pin variant with GPIO 6 & 7 availableBeliebige 38-Pin-Variante mit GPIO 6 & 7 |
| IR Sensor (TCRT5000) | 2× | Reflection module with digital OUT, active LOWReflexionsmodul mit digitalem OUT, aktiv LOW |
| Mounting rail / crossbarMontageschienenhalter | 1× | Fix sensors at your chosen distance and set SENSOR_DISTANCE_M in firmwareSensoren im selbst gewählten Abstand fixieren und SENSOR_DISTANCE_M setzen |
| 5 V USB power supply5-V-USB-Netzteil | 1× | Powers the ESP32; sensors fed from its 3.3 V pinVersorgt den ESP32; Sensoren am 3,3-V-Pin |
| Wi-Fi router / AP | 1× | Same LAN segment as the Windows PC and Z21Gleiches LAN-Segment wie Windows-PC und Z21 |
OpenSpeed uses the Z21's proprietary LAN protocol, which is specific to Roco/Fleischmann hardware. OpenSpeed kommuniziert über das proprietäre LAN-Protokoll der Z21, das Roco/Fleischmann-spezifisch ist.
| Operating SystemBetriebssystem | Windows 10 / 11 (64-bit) |
| RuntimeLaufzeit | .NET 8 Desktop Runtime |
| NetworkNetzwerk | PC, ESP32, and Z21 on the same LAN subnet PC, ESP32 und Z21 im selben LAN-Subnetz |
| CV3 / CV4 | Must be 0 — no ramp Muss 0 sein – keine Rampe |
| Track sectionGleisabschnitt | Straight, min. 1.5 m run-up before sensor 1 Gerade, mind. 1,5 m Anlaufstrecke vor Sensor 1 |
The sensors use the ESP32's internal pull-up resistors. The sensor output pulls the GPIO pin LOW when a train is detected. No external resistors are needed.
Die Sensoren nutzen die internen Pull-up-Widerstände des ESP32. Der Sensor-Ausgang zieht den GPIO-Pin auf LOW, wenn ein Zug erkannt wird. Externe Widerstände sind nicht erforderlich.
SENSOR 1 (GPIO 6) SENSOR 2 (GPIO 7)
Entry / Einfahrt Exit / Ausfahrt
┌──────────────┐ ┌──────────────┐
│ TCRT5000 │ │ TCRT5000 │
│ Module │ │ Module │
│ │ │ │
VCC (3.3V) ┤ VCC OUT ├─────────────────┤ OUT VCC ├─── VCC (3.3V)
GND ┤ GND │ │ GND ├─── GND
└──────┬───────┘ └───────┬──────┘
│ OUT (active LOW) (active LOW) OUT │
│ │
────────────┼──────────────────────────────────────┼────────────
↓ ↓
┌───────────────────────────────────────────────────────────────┐
│ ESP32 DevKit │
│ │
│ GPIO 6 ◄──── Sensor 1 OUT Sensor 2 OUT ────► GPIO 7 │
│ 3V3 ──── to both sensor VCC pins │
│ GND ──── to both sensor GND pins │
│ │
│ USB Micro/C ──────────────────────────────► 5V USB Charger │
│ Wi-Fi ────────────────────────────────────► LAN Router/AP │
└───────────────────────────────────────────────────────────────┘
◄──────────── SENSOR_DISTANCE_M (e.g. 0.49 m) ────────────►
(set this constant in the firmware to match your actual installation)
Sensors face downward from a crossbar — detecting reflections from the
locomotive roof / wagon tops as the train passes underneath.
The firmware constant SENSOR_DISTANCE_M tells the ESP32 how far apart the sensors are. You decide the distance based on your layout — then measure it precisely and enter the exact value before flashing.
SENSOR_DISTANCE_M and re-flash.Die Firmware-Konstante SENSOR_DISTANCE_M teilt dem ESP32 den Abstand zwischen den Sensoren mit. Sie wählen den Abstand passend zu Ihrer Anlage – dann genau messen und den exakten Wert vor dem Flashen eintragen.
SENSOR_DISTANCE_M aktualisieren und neu flashen.| ESP32 Pin | Connects toVerbindet mit | Wire colorKabelfarbe |
|---|---|---|
| 3V3 | Both sensor VCC pinsBeide Sensor-VCC-Pins | ● RedRot |
| GND | Both sensor GND pinsBeide Sensor-GND-Pins | ● BlackSchwarz |
| GPIO 6 | Sensor 1 OUT — entry sensor Sensor 1 OUT – Einfahrtsensor | ● GreenGrün |
| GPIO 7 | Sensor 2 OUT — exit sensor Sensor 2 OUT – Ausfahrtsensor | ● Yellow / OrangeGelb / Orange |
| USB | 5 V USB power supply5-V-USB-Netzteil | — |
INPUT_PULLUP on both GPIO pins. The sensor's OUT pin should be open-collector / open-drain and pull to GND when a train is detected — as TCRT5000 modules do.
Die Firmware setzt beide GPIO-Pins auf INPUT_PULLUP. Der OUT-Pin muss Open-Collector/Open-Drain sein und bei Erkennung auf GND ziehen – wie TCRT5000-Module es tun.
Mount the sensors on a crossbar over a straight section of track so they point downward toward passing rolling stock.
Crossbar / arch above the track
┌─────────────────────────────────────────┐
│ [Sensor 1 / GPIO 6] [Sensor 2 / GPIO 7]│
│ ↓ ↓ │
══╪══════════╪════════════════════╪══════════╪══ ← Track
│ Detection zone Detection zone │
└─────────────────────────────────────────┘
◄── SENSOR_DISTANCE_M (your value) ──►
Sensoren an einem Querbalken über einem geraden Gleisabschnitt montieren, sodass sie nach unten auf das Rollmaterial zeigen.
Querbalken / Bogen über dem Gleis
┌─────────────────────────────────────────┐
│ [Sensor 1 / GPIO 6] [Sensor 2 / GPIO 7]│
│ ↓ ↓ │
══╪══════════╪════════════════════╪══════════╪══ ← Gleis
│ Detektionszone Detektionszone │
└─────────────────────────────────────────┘
◄── SENSOR_DISTANCE_M (your value) ──►
The sketch lives in Arduino Sketch/OpenSpeed/OpenSpeed.ino. You only need to edit two lines before flashing.
Der Sketch befindet sich in Arduino Sketch/OpenSpeed/OpenSpeed.ino. Vor dem Flashen müssen nur zwei Zeilen angepasst werden.
Prerequisites
Download from arduino.cc. In Board Manager install esp32 by Espressif Systems (version 2.x or 3.x).
In Library Manager install:
• ESPAsyncWebServer by me-no-dev
• AsyncTCP by me-no-dev
Open Arduino Sketch/OpenSpeed/OpenSpeed.ino from the repository root.
Voraussetzungen
Von arduino.cc herunterladen. Im Board-Manager esp32 von Espressif Systems installieren (Version 2.x oder 3.x).
Im Bibliotheks-Manager installieren:
• ESPAsyncWebServer von me-no-dev
• AsyncTCP von me-no-dev
Arduino Sketch/OpenSpeed/OpenSpeed.ino aus dem Repository-Stammverzeichnis öffnen.
Edit & Flash
Edit the two Wi-Fi constants at the top of the sketch:
const char* WIFI_SSID = "YourNetworkName";
const char* WIFI_PASS = "YourPassword";This is the most important configuration step. Measure the distance between your two sensor centres, then set:
const float SENSOR_DISTANCE_M = 0.49f;If the value is wrong, all speed and length measurements will be systematically incorrect with no error message.
You can also change GPIO pins here if your wiring differs:
const int SENSOR1_PIN = 6;
const int SENSOR2_PIN = 7;Tools → Board → ESP32 Dev Module. Select the COM port. Click Upload ▶.
Open Serial Monitor at 115200 baud. After "Connected!" the assigned IP prints on one line, e.g. 192.168.1.42. Enter this as Speed Sensor Address in OpenSpeed.
Anpassen & Flashen
Die zwei WLAN-Konstanten am Anfang des Sketches anpassen:
const char* WIFI_SSID = "IhrNetzwerkname";
const char* WIFI_PASS = "IhrPasswort";Dies ist der wichtigste Konfigurationsschritt. Den Abstand zwischen den Sensormittelpunkten messen und setzen:
const float SENSOR_DISTANCE_M = 0.49f;Ist der Wert falsch, sind alle Messungen systematisch falsch – ohne Fehlermeldung.
GPIO-Pins können hier ebenfalls angepasst werden:
const int SENSOR1_PIN = 6;
const int SENSOR2_PIN = 7;Werkzeuge → Board → ESP32 Dev Module. COM-Port auswählen. Upload ▶ klicken.
Serial Monitor auf 115200 Baud öffnen. Nach "Connected!" wird die IP angezeigt, z. B. 192.168.1.42. Diese als Speed Sensor Address in OpenSpeed eintragen.
OpenSpeed runs on Windows 10/11. No installer needed — unzip and run.
OpenSpeed läuft auf Windows 10/11. Kein Installer – entzippen und starten.
Installation
Download and install the Windows Desktop Runtime 8.x from dotnet.microsoft.com if not already installed.
Download the latest release from GitHub Releases and extract the ZIP to any folder.
Double-click OpenSpeed.UI.exe. Settings are saved to %USERPROFILE%\OpenSpeed\Settings.json.
Installation
Die Windows Desktop Runtime 8.x von dotnet.microsoft.com herunterladen und installieren.
Aktuelle Version von GitHub Releases herunterladen und die ZIP-Datei entpacken.
Doppelklick auf OpenSpeed.UI.exe. Einstellungen werden unter %USERPROFILE%\OpenSpeed\Settings.json gespeichert.
First-Run Configuration
Type the IP address of your Z21 in the Z21 Address field. The status dot turns green when the connection is established.
Enter the IP address of the ESP32 from the Serial Monitor output.
Set Decoder Address, DCC Speed Mode (14 / 28 / 128), and Scale (e.g. 87 for H0). CV3 and CV4 on the decoder must be 0.
Place the locomotive before Sensor 1 in the forward direction. Click Start Speed Measurement. The app drives it through all configured speed steps automatically.
Ersteinrichtung
IP-Adresse der Z21 in das Feld Z21 Address eingeben. Der Status-Punkt wird grün, sobald die Verbindung steht.
IP-Adresse des ESP32 aus dem Serial-Monitor-Output eingeben.
Decoder-Adresse, DCC-Fahrstufenmodus (14 / 28 / 128) und Maßstab (z. B. 87 für H0) einstellen. CV3 und CV4 müssen 0 sein.
Lokomotive vor Sensor 1 in Vorwärtsrichtung aufstellen. Geschwindigkeitsmessung starten klicken. Die App fährt die Lok automatisch durch alle Fahrstufen.
The firmware serves three endpoints on port 80 via ESPAsyncWebServer. All return JSON — you can call them in a browser for debugging.
Die Firmware stellt drei Endpunkte auf Port 80 via ESPAsyncWebServer bereit. Alle antworten mit JSON und können zum Debuggen im Browser aufgerufen werden.
{
"id": 3178432491,
"train_length_cm": 31.247,
"speed_kmh": 2.940
}
The desktop app polls this every 250 ms and detects a new measurement by comparing the id field. The id is a random 32-bit number (not a counter).
Die Desktop-App ruft diesen Endpunkt alle 250 ms ab. Eine neue Messung wird durch Vergleich des id-Felds erkannt. Die id ist eine zufällige 32-Bit-Zahl.
{ "status": "reset" }
Called before every measurement pass. Resets the state machine to WAITING_FOR_MEASUREMENT and clears all timing variables.
Wird vor jedem Messdurchlauf aufgerufen. Setzt den Zustandsautomaten auf WAITING_FOR_MEASUREMENT und löscht alle Zeitvariablen.
{ "status": 0 }
Polled once per second by the ReachabilityMonitor to drive the connection status dot. A successful HTTP response means the sensor is reachable regardless of the status value.
Wird vom ReachabilityMonitor einmal pro Sekunde abgerufen, um die Verbindungsanzeige zu steuern.
http://<sensor-ip>/status in a browser. If you see {"status":0} the firmware is running and reachable.
Im Browser http://<sensor-ip>/status öffnen. Erscheint {"status":0}, läuft die Firmware und ist erreichbar.