RFID-Station für Spiele im KidsLab: Karte an die Disc halten, die Station erkennt sie und leuchtet.
- ESP32-Board mit CircuitPython (Gehäuse: ESP32-C6 Super Mini mit Laderegler, Akku-Pads auf der Unterseite; getestet außerdem: Waveshare ESP32-C6-Zero, Lolin S2 Mini)
- RC522 RFID-Leser (13,56 MHz, ISO14443A)
- 18650-Akku mit direkt angelöteten Kabeln, in zwei Klammern im Unterteil
- WS2812B-LED-Streifen (10 mm)
- KCD1-Wippschalter (Einbauöffnung 19,2 × 13 mm) in der Rückwand
- 4× M3-Gewindeeinsatz + M3×8 Senkkopfschraube
18650 (+) ── Schalter ──┬── BAT+ (Pad unten am C6 Super Mini)
└── LED-Streifen +
18650 (–) ──────────────┬── BAT– (Pad unten am C6 Super Mini)
└── LED-Streifen GND
- RC522 an 3V3 des ESP-Boards
- Der Schalter trennt den Akku ganz ab: Geladen wird über USB-C am Board nur, wenn er an ist
- Akku: Die Kabel direkt an die Zelle löten (Pole kurz anrauen, heißer Kolben, schnell arbeiten) und die Zelle von oben in die zwei Klammern drücken. Sie liegt quer auf dem Boden, Platz ist für Zellen bis etwa 75 mm.
- C6 Super Mini: Es gibt keinen Halter mehr, das Board wird mit Heißkleber fixiert, USB-C mittig in die Öffnung der unteren Wand. Die Öffnung ist 11 mm breit und reicht vom Boden bis unter die LED-Nut, damit das Board auch mit den Akku-Kabeln an den Pads unten noch passt. BAT+/BAT– vor dem Einkleben anlöten.
- Schalter: Er sitzt von hinten versenkt in einer 3 mm tiefen Mulde, damit er beim Aufhängen möglichst wenig aufliegt, und wird von hinten eingeklipst.
- Aufhängen: Oben sind zwei flache Haken in der Rückenebene, z. B. für zwei Nägel oder Schrauben (Schaft bis 5 mm). Alternativ geht eine Kordel durch das Loch oben.
RC522 und LEDs (code.py, Board über TAGGED_BOARD in settings.toml wählen):
| Bauteil | Lolin S2 Mini | ESP32-C6 Super Mini |
|---|---|---|
| RC522 SDA | IO12 | GPIO0 |
| RC522 SCK | IO7 | GPIO1 |
| RC522 MOSI | IO11 | GPIO2 |
| RC522 MISO | IO9 | GPIO3 |
| RC522 RST | IO5 | GPIO4 |
| RC522 3.3V | 3V3 | 3V3 |
| LED-Streifen DIN (25 LEDs im Kreis) | IO40 | GPIO20 |
Am C6 Super Mini liegen die RC522-Pins nebeneinander auf der Leiste mit TX/RX; auf der anderen Leiste liegen GND, 3V3 und GPIO20 (LEDs) direkt nebeneinander. GPIO8 (eingebaute RGB-LED), GPIO9 (BOOT) und GPIO15 (Status-LED) frei lassen. Die eingebaute RGB-LED schaltet code.py aus. In die DIN-Leitung am besten 330 Ω, am Streifenanfang einen Elko (100–470 µF) zwischen + und GND.
kartentest.py ist für den ESP32-C6-Zero (SDA IO0, SCK IO1, MOSI IO2, MISO IO3, RST IO4).
circuitpython/: Firmware und Hilfsskriptecode.py: Spielstation: Regenbogen beim Start, dann zeigt der LED-Ring den Zustand vom Server, Karten-Scans gehen an den Servernet.py: minimaler HTTP-Client (Klartext-HTTP, JSON) für den Serversettings.toml.example: Vorlage fürsettings.tomlauf dem Board (WLAN, Board-Typ, Box-Nummer, Server, Token)rc522.py: minimaler RC522-Treiberkartentest.py: zeigt UID und Kartentyp jeder erkannten Karteupload.py: Dateien über die serielle REPL aufs Board laden (für Boards ohne USB-Laufwerk)log_serial.py: serielle Ausgabe mitschreiben
server/: Spielserver (Python, nur Standardbibliothek) mit Webansichtstatic/index.html, läuft als Docker-Container auf fangulus.kidslab.dedocker-compose.yml,.github/workflows/build.yml: Image auf GHCR, Betrieb mit Traefik wie bei kidslab.defreecad/: parametrisches Gehäusedisc_case.py: in FreeCAD ausführen, erzeugtDiscCase.FCStd, STLs, 3MFs und Vorschaubildergroup_3mf.py: baut die Bambu-Studio-3MF (Deckel + Logo als ein Objekt, Logo auf Filament 2)DiscCase_Druck_Bambu.3mf: druckfertig für Bambu Lab (X1C-Profil, Unterteil + zweifarbiger Deckel von Station 1)DiscCase_Unterteil.stl: nur das UnterteilDiscCase_Front_1_Bambu.3mf…DiscCase_Front_5_Bambu.3mf: nur der Deckel je Station („FANGULUS“ + Nummer 1–5), Logo auf Filament 2DiscCase_Prusa_Front_1_2.3mf,_3_4.3mf,_5.3mf: PrusaSlicer-2.9-Projekte für die Prusa MK4 ohne MMU, zwei Fronten je Platte. Logo auf Extruder 1 (schwarz), Deckel auf Extruder 2; der Drucker hält per M600 je einmal in Schicht 1, 2 und 3 zum Filamentwechsel an, danach läuft der Rest ohne Eingriff. Erzeugt vonprusa_3mf.pyausprusa_mk4.ini(MK4 Input Shaper, 0.20mm SPEED, Generic PLA). Nicht mit PrusaSlicer 3.0 alpha öffnen.PixelifySans-Bold.ttf: Schrift für alle Texte (Pixelify Sans, statische Bold-Instanz, SIL Open Font License)
Karten sind Spieler, Teams sind Farben, die 5 Stationen sind die „Türme“.
- Teams: Eine neue Karte kommt beim ersten Scan an irgendeiner Station ins Team mit den wenigsten Leuten (bei Gleichstand zufällig). Die Station blinkt 3× in der Teamfarbe. Pro Team dürfen höchstens Stationen − 2 Leute sein, also 3 bei 5 Stationen. Weitere Karten blinken 2× weiß.
- Start: Die erste Karte an einer Station startet das Spiel und erobert gleich diese Station (Autostart, in der Spielleitung abschaltbar, dann startet das Spiel per Knopf).
- Erobern: Im laufenden Spiel wechselt eine Station mit einer Karte des anderen Teams die Farbe, die Farbe läuft einmal um den Ring. Danach ist sie für die Sperrzeit (Standard 20 s) geschützt, ein Versuch flackert nur.
- Punkte: Erst wenn keine Station mehr neutral ist, bekommt jedes Team pro Sekunde 1 Punkt pro Station. Hält ein Team alle Stationen, zählt es doppelt, und alle Stationen pulsieren.
- Ende: Nach der Laufzeit (Standard 10 min) leuchten alle Stationen in der Siegerfarbe.
Anzeige am Ring: Lobby = weiß atmend, neutral = weißer Lauf, erobert = Lauf in Teamfarbe (langsam und gedimmt während der Sperre), kein Server = erste LED blinkt orange, kein WLAN = ganzer Ring blinkt gelb. Beim Start leuchtet der Ring nach dem Regenbogen gedimmt gelb, solange die Station ein WLAN sucht, und blinkt einmal grün, wenn sie verbunden ist.
Die Webansicht unter https://fangulus.kidslab.de zeigt die Stationen, die Restzeit und die Punkte. Sie sagt Ereignisse an („Station 1 ist jetzt rot“), dafür einmal „Ton an“ drücken. Unter „Spielleitung“ werden Laufzeit, Sperrzeit, Teamzahl und Autostart eingestellt, außerdem gibt es Start, Stop und Neues Spiel. Dafür ist das Passwort TAGGED_ADMIN nötig.
Die Stationen sprechen Klartext-HTTP (Port 80) mit ?token=TAGGED_TOKEN, die Webansicht läuft über HTTPS. Ein Push auf main, der server/ ändert, baut das Image ghcr.io/kidslabde/tagged:latest. Ausgeliefert wird von Hand: auf dem Server in /opt/tagged (dort liegen docker-compose.yml und .env mit TAGGED_TOKEN=... und TAGGED_ADMIN=...) docker compose pull && docker compose up -d.
Lokal testen:
TAGGED_TOKEN=test uv run --no-project python server/tagged_server.py --port 8090Dann auf den Boards TAGGED_SERVER = "<IP des Laptops>:8090" setzen.
Der S2 Mini taucht mit CircuitPython als Laufwerk CIRCUITPY auf: code.py, net.py und rc522.py einfach draufkopieren, dazu eine settings.toml nach der Vorlage settings.toml.example. Alle Boxen haben dieselbe code.py, nur TAGGED_BOX_ID (und ggf. TAGGED_BOARD) unterscheidet sich. Die Helligkeit des LED-Rings stellt TAGGED_BRIGHTNESS in Prozent ein (Standard 25). Hängt der LED-Streifen an einem anderen Pin als in der Tabelle, setzt TAGGED_LED_PIN die GPIO-Nummer (Box 2 hat ihn an GPIO14). Weitere WLANs kommen als TAGGED_WIFI_SSID_2/TAGGED_WIFI_PASSWORD_2, _3 usw. dazu; die Station nimmt das erste bekannte Netz in Reichweite, CIRCUITPY_WIFI_SSID zuerst.
Für Boards ohne USB-Laufwerk (z. B. ESP32-C6):
cd circuitpython
uv run --no-project --with pyserial python upload.py /dev/ttyACM0 code.py net.py rc522.py settings.toml