Ein interaktiver GeoCache für den Verstehbahnhof in Fürstenberg/Havel. Besucher:innen helfen Jim Knopf, seine Lok Emma von Fürstenberg zurück zur Augsburger Puppenkiste zu bringen — indem sie QR-Codes am Verstehbahnhof scannen und dabei auf einem LED-Streifen zusehen, wie Emma Bahnhof für Bahnhof nach Hause rollt.
Jim Knopf, Lukas der Lokomotivführer und die kleine Lok Emma sind auf dem Heimweg nach Augsburg gestrandet. Das Schloss an Emmas Feuerbüchse klemmt, der Goldstein ist verloren, und Emma ist zu schwer zum Anschieben. Ohne Hilfe kommen die drei nicht weiter.
An der Strecke vor dem Exponat flackert das Lagerfeuer (LED-Streifen im Attract-Modus) — es lockt Besucher:innen an. Wer den Start-QR-Code scannt, startet das Abenteuer: Emma muss über vier Stationen wieder fit gemacht werden. Am Ende rollt sie in Augsburg ein.
Die Spieler:innen begegnen pro Bahnhof zwei QR-Codes:
- Aufgaben-QR — irgendwo am Verstehbahnhof versteckt (am Lok-Bild, am Repair-Café-Plakat, am Goldstein, an der Lore). Beim Scannen wird die Aufgabe am aktuellen Bahnhof als erledigt markiert und die Webseite sagt: "Geh zurück zum Start-QR".
- Start-QR — neben Emma am LED-Streifen. Dasselbe physische Exponat, dieselbe URL. Beim ersten Scan startet das Spiel, bei jedem weiteren Scan wird der nächste Bahnhof freigeschaltet (vorausgesetzt die Aufgabe wurde gelöst). Wird der Start-QR zu früh gescannt, sagt die Webseite "du bist zu früh zurück".
Idle (Lagerfeuer)
└─ Start-QR → Fürstenberg, Attract endet, Pendelei 0↔1 beginnt
└─ Lok-QR → task_done
└─ Start-QR → Bahnhof Berlin öffnet sich, Pendelei 1↔2
└─ Repair-Café-QR → task_done
└─ Start-QR → Bahnhof Leipzig öffnet sich
└─ Goldstein-QR → task_done
└─ Start-QR → Nürnberg öffnet sich
└─ Lore-QR → task_done
└─ Start-QR → Augsburg + Finale
Am Finale darf die Gruppe sich mit Namen + Gruß ins Logbuch eintragen. Nach dem Timeout (standardmäßig 5 min nach Gewinn, 15 min während des Spiels) fällt alles zurück auf Idle.
Die fünf Bahnhöfe auf dem LED-Streifen: Fürstenberg → Berlin →
Leipzig → Nürnberg → Augsburg. Sie sind gleichmäßig über den
Streifen verteilt, die Firmware rechnet die LED-Positionen automatisch
aus NEOPIXEL_COUNT aus.
┌──────────────┐ ┌──────────────────┐ ┌────────────┐
│ Smartphone │ scan QR │ verstehbahnhof. │ GET │ D1 Mini │
│ Besucher:in │ ────────▶ │ kidslab.de │ ◀──────── │ Lite │
│ │ │ (Flask+Gunicorn)│ /state │ (Firmware)│
└──────────────┘ └─────────┬────────┘ └─────┬──────┘
│ │
▼ ▼
┌──────────────┐ ┌─────────────────┐
│ state.json + │ │ NeoPixel-Streif │
│ log/*.log │ │ (Feuer, Zug, │
│ (Volumes) │ │ Waggons) │
└──────────────┘ └─────────────────┘
- Webseite (
webseite/): Flask-App mit Gunicorn, rendert Story- Seiten pro QR-Scan, verwaltet den zentralen Spielstand, schreibt Logs und Logbuch-Einträge. Läuft hinter Traefik unterverstehbahnhof.kidslab.de. - Firmware (
firmware/): PlatformIO-Projekt für den Wemos D1 Mini Lite (ESP8285). Pollt alle 10 s bzw. am Pendel-Wendepunkt die/state-API und animiert den NeoPixel-Streifen. - QR-Codes (
qr-codes/): Fünf druckbare PNGs für die Stationen.
- Wemos D1 Mini Lite (ESP8285, 1 MB Flash)
- NeoPixel-LED-Streifen (WS2812B), empfohlen 30+ LEDs
- Anschluss:
NeoPixel D1 Mini VCC (+5V) 5VGND GNDDIN (Data) D2(GPIO4) - Bei Flackern: 470 Ω in die Data-Leitung, 1000 µF Elko am Streifen- Anfang zwischen 5V und GND. Streifen >30 LEDs → externes 5V-Netzteil.
- Siehe
firmware/README.mdfür Details.
| Zustand | Animation |
|---|---|
Idle (station=-1) |
Fire2012-Feuer auf dem ganzen Streifen (schwarz→rot→gelb→weiß), alle 1-3 s ein heller Feuerwerks-Burst an zufälliger Position, der ausfadet |
Spielstart (-1 → 0) |
Einmaliger Transition-Flash (weiß → gelb → rot, ~300 ms) über den ganzen Streifen |
Pendelei (station >= 0, < 4) |
Erreichte Bahnhöfe grün, offene rot, befahrene Strecke dim weiß. Emma (blau) + 3 Waggons pendeln zwischen aktuellem und nächstem Bahnhof (kehren vor dem roten Bahnhof um) |
Bahnhof öffnet sich (N → N+1) |
Neuer Bahnhof blinkt 3× rot, fadet aus, fadet grün ein (~1.8 s, blockierend). Danach normale Pendelei |
Finale (station=4) |
Öffnungs-Animation + Ankunfts-Party (6× Ganz-Streifen-Grün-Flash) |
Server-Timeout (N → -1) |
Firmware merkt es beim nächsten Poll, fällt in Idle (Feuer+Feuerwerk) zurück |
API-Polling:
- Im Idle: alle 10 s (
ATTRACT_POLL_MS) - Im Spiel: nur am Wendepunkt der Pendelei (vorm nächsten roten Bahnhof)
state.json({station, task_done, last_activity}) — persistent imstate-Volume- Start-QR hat vier Reaktionen:
started/advanced/pending/arrived - Aufgaben-QRs setzen nur
task_done=trueam passenden Bahnhof /resetzeigt Bestätigungs-Seite,/reset/confirmsetzt zurück/logbook(POST) nimmt Logbuch-Einträge an, speichert inlog/logbook.jsonl- Strukturiertes Logging in
log/YYYY-MM-DD.log(und parallel auf stdout fürdocker compose logs)
| Env-Variable | Default | Bedeutung |
|---|---|---|
STATE_DIR |
/app/state |
Ort der state.json |
LOG_DIR |
/app/log |
Ort der Log-Dateien + logbook.jsonl |
INACTIVITY_TIMEOUT_MIN |
15 |
Timeout in Minuten während des Spiels |
ARRIVED_TIMEOUT_MIN |
5 |
Timeout in Minuten nach dem Finale |
Alle Werte stehen direkt in compose.yml. Zum Ändern Wert anpassen
und docker compose up -d neu aufrufen — Rebuild nicht nötig.
Firmware-Config: firmware/src/config.h (gitignored), Vorlage in
firmware/src/config.h.example. Die wichtigsten Einstellungen:
| Define | Default | Bedeutung |
|---|---|---|
WIFI_SSID / WIFI_PASSWORD |
— | WLAN-Zugang |
API_URL |
http://verstehbahnhof.kidslab.de/state |
/state-Endpunkt, plain HTTP über Traefik-Sonderroute |
API_TIMEOUT_MS |
1500 |
HTTP-Timeout |
NEOPIXEL_PIN |
D2 |
Data-Pin |
NEOPIXEL_COUNT |
30 |
LEDs insgesamt |
NEOPIXEL_BRIGHTNESS |
80 |
0..255 |
TRAIN_FRAME_MS |
18 |
Pendel-Geschwindigkeit (ms/LED) |
NUM_WAGONS |
3 |
Waggons hinter der Lok |
ATTRACT_POLL_MS |
10000 |
Idle-Poll-Intervall |
FIRE_COOLING / FIRE_SPARKING |
55 / 120 |
Fire2012-Tuning |
Auf dem Server:
git pull
docker compose up -d --buildTraefik-Labels sind in compose.yml drin, das externe proxy-Netz
muss existieren und Traefik im selben Netz laufen. Die Domain
verstehbahnhof.kidslab.de muss auf den Host zeigen.
/state wird zusätzlich über plain HTTP ausgeliefert (ohne
HTTPS-Redirect), damit der ESP8266 sich keinen BearSSL-Handshake
zumuten muss. Alle anderen Pfade werden auf HTTPS umgeleitet.
Firmware flashen:
cd firmware
cp src/config.h.example src/config.h # einmalig, dann editieren
pio run -t upload
pio device monitor # optional: live-logDie Logs liegen per Bind-Mount direkt neben compose.yml im Ordner
logs/ — da kann man sie ohne docker compose exec lesen und backuppen:
# Tageslog live mitlesen
tail -f logs/$(date +%F).log
# Alle Logbuch-Einträge
cat logs/logbook.jsonl | jq .
# Spielstand ansehen (steckt in einem Named Volume, daher via exec)
docker compose exec webseite cat /app/state/state.json
# State komplett zurücksetzen (vor Event, Testlauf, etc.)
docker compose exec webseite rm -f /app/state/state.jsoncompose.yml Docker Compose + Traefik-Labels
webseite/ Flask-Server
server.py Game-Logik, Routes, Logging
Dockerfile Gunicorn-Runtime
templates/ Jinja2-Templates pro State/Route
firmware/ PlatformIO-Projekt für D1 Mini Lite
src/main.cpp Fire2012, Pendelei, Öffnungs-Animationen
src/config.h.example Pins, WLAN-Platzhalter, Tuning
platformio.ini Board + Libraries
qr-codes/ Druckbare QR-Codes für die 5 Stationen
README.md ← du bist hier
TESTING.md URLs, curl-Flows, Debug-Tipps
Der Verstehbahnhof in Fürstenberg/Havel ist ein Maker- und Lernort für Kinder und Jugendliche rund um Technik, Programmieren und Eisenbahn. Dieses Projekt ist ein kleiner GeoCache-Beitrag für Besucher:innen des Verstehbahnhofs.
tbd