19.08.2025 - Fachartikel

Basteln mit BCS: Löten, Coden, Stempeln

In diesem Beitrag erfahren Sie, wie aus einer Handvoll Elektronikkomponenten, einer guten Portion Bastelfreude und der neuen REST-API zur Anwesenheitserfassung ein RFID-Zeiterfassungsterminal für Projektron BCS entsteht. Und falls Sie nicht selbst zum Lötkolben greifen möchten: Im letzten Abschnitt stellen wir Ihnen eine fertige, professionelle Lösung in einem eleganten Gehäuse vor – individualisierbar und optimal auf Projektron BCS abgestimmt.

Zeiterfassung über Terminals mit Projektron BCS: flexibel und individuell anpassbar

Mitarbeitende, die ihren Arbeitstag vor dem Bildschirm verbringen und mit Aufgaben, Tickets und Terminen jonglieren, profitieren von der intuitiven, digitalen Zeiterfassung über die Weboberfläche von Projektron BCS. Aber was ist mit den Kolleginnen und Kollegen, die nicht ständig mit Maus und Tastatur unterwegs sind – sondern mit Werkzeug, Wischmopp, Kochlöffel oder Kundenkontakt? Menschen, die täglich in der Produktion, in der Werkstatt, am Prüfstand, im Verkaufsraum, in der Kantine oder im Reinigungsdienst dafür sorgen, dass der Laden läuft, ohne direkt ins Projektgeschäft eingebunden zu sein?

Auch sie unterliegen der Pflicht zur Arbeitszeiterfassung – doch die Anmeldung am PC ist hier oft alles andere als praktikabel. Genau für solche Einsatzbereiche bietet die Zeiterfassung über ein Terminal beim Betreten des Betriebsgeländes eine komfortable und effiziente Alternative.

Solche Zeiterfassungsterminals sind in der Praxis meist an den Eingängen oder in den Treppenhäusern von Firmengebäuden montiert. Damit können sich Mitarbeitende – ganz im Stil der guten alten Stechuhr – bequem ein- und ausstempeln. Zur eindeutigen Identifikation bekommt jeder Mitarbeiter ein persönliches RFID-Token oder eine RFID-Karte, die im Idealfall sogar noch weitere Funktionen übernimmt: etwa fürs Schließsystem oder das Bezahlen in der Kantine. Und wer weiß – vielleicht entdeckt auch die eine oder der andere PC-Nutzer den Komfort dieser Variante für sich. 

Was ist RFID (Radio Frequency Identification)?

Eine Technologie zur berührungslosen Identifikation, bei der Daten auf einem kleinen Chip (Tag) gespeichert und per Funk von einem Lesegerät ausgelesen werden.

Projektron BCS ermöglichte bisher die Integration von Zeiterfassungsterminals per CSV-Schnittstelle – eine Lösung, die bei mehreren unserer Kunden erfolgreich im Einsatz ist.

Löten, Coden, Stempeln

In diesem Blog-Beitrag gehen wir einen neuen Weg: 

Statt bei der Schnittstelle zu beginnen, starten wir direkt an der Basis – bei der Hardware (Löten). Im Mittelpunkt steht beim Softwareteil dieses Projektes die neue REST-API zur Anwesenheitserfassung, die seit BCS-Version 25.2 verfügbar ist (Coden). Sie ermöglicht eine nahtlose transaktionsorientierte Integration von Terminals in Projektron BCS – ganz ohne Umwege. Die Arbeitszeiten fließen direkt in Projektron BCS ein – für eine unternehmensweit einheitliche und transparente Auswertung (Stempeln).

Ein besonderer Vorteil: Die API ermöglicht nicht nur die Buchung von Zeiten, sondern auch eine direkte Rückmeldung am Gerät. So erfahren Mitarbeitende sofort, ob ihre Buchung erfolgreich war – oder ob sie versehentlich versuchen, die Zeit zu verbiegen. Zum Beispiel, wenn jemand „Gehen“ bucht, ohne zuvor „Gekommen“ zu sein. Auch das passiert – selbst den besten Zeitreisenden.

Und falls Sie nicht basteln, sondern lieber auf eine fertige, professionelle Lösung in einem schicken Gehäuse setzen möchten, brauchen Sie sich keine Sorgen zu machen: Im letzten Abschnitt dieses Artikels wartet ein passendes Angebot auf Sie.

In diesem kurzen Video sehen Sie, wie das Terminal genutzt werden kann. (Ton an)

Ein RFID-Zeiterfassungsterminal mit Raspberry Pi

In diesem Beitrag zeige ich Ihnen, wie Sie mit einem Raspberry Pi Zero 2 ein smartes RFID-Zeiterfassungsterminal bauen, das per Webservice nahtlos mit der Projektmanagementsoftware Projektron BCS kommuniziert.

Was ist ein Raspberry Pi? 

Raspberry Pi sind kleine, kostengünstige Einplatinencomputer, die ursprünglich für Bildungszwecke entwickelt wurden. Trotz der kompakten Größe bieten diese Computer ausreichend Leistung und Flexibilität für zahlreiche Anwendungen, von einfachen DIY-Projekten bis hin zu komplexeren Aufgaben in der Industrie. Besonders hervorzuheben sind die GPIO-Pins, die eine direkte Verbindung zu Sensoren, Aktuatoren und anderen Hardwarekomponenten erlauben. Sie ermöglichen, in die Welt der Programmierung und Elektronik einzutauchen und vielfältige Projekte zu realisieren.

Löten

Hardware

  • Raspberry Pi Zero 2 W (~19 €)
  • Pimoroni Pirate Audio HAT für Raspberry Pi mit integriertem Lautsprecher (~30 €)
  • Elecrow Crowtail NFC – RFID-Reader mit PN532, 13,56 MHz (~11 €)
  • Micro-USB-Netzteil (~4 €)
  • Micro-SD-Speicherkarte (~10 €)
  • 40-Pin-GPIO-Header-Pins, extra lang (~1 €)
  • Waveshare Aluminium-Kühlkörper für Raspberry Pi Zero / Zero 2 W (~3 €)
  • RFID Schlüsselanhänger Mifare Classic (~0,5 € (Menge nach Bedarf))
  • Optional: Micro-USB-zu-Ethernet-Adapter (~5 €), alternativ WLAN
  • Kleinteile: Kabel, Hex-Abstandshalter für Platinen, Kabel, Schrumpfschlauch, Lötzinn, Schrauben, Kabelbinder, Klebstoff sowie eine kleine Plastikplatte

 (erhältlich z.B. bei https://www.berrybase.de/)

Wenn Sie mein Projekt nachbauen möchten, verstehen Sie diese Komponentenliste bitte nur als Orientierung – nicht als Pflichtprogramm. Vielleicht möchten Sie lieber ein größeres 5"-Touchdisplay verwenden? Oder ein E-Ink-Display für besonders stromsparende Anwendungen? Vielleicht reizt Sie auch ein selbst entworfenes Gehäuse aus dem 3D-Drucker?

Alles ist möglich – erlaubt ist, was gefällt und funktioniert. Mit etwas Entdeckergeist, Freude am Basteln und der Bereitschaft, sich ein wenig einzulesen, lassen sich auch ganz individuelle Varianten realisieren.

Benötigte Werkzeuge

  • Einen PC
  • Lötkolben mit feiner Spitze
  • Digitalmultimeter
  • Schraubenzieher
  • Abisolierzange
  • Seitenschneider
  • Laubsäge
  • Bohrmaschine
  • Feuerzeug
  • ESD-Matte und Armband (zum Schutz vor statischen Entladungen)

1. GPIO-Header anlöten

  • Da der Raspberry Pi Zero 2 in meinem Fall noch keine vorinstallierten GPIO-Pins hatte, habe ich diese kurzerhand selbst mit Lötkolben und etwas Lötzinn aufgelötet. Für die rechten zehn Pins habe ich bewusst extra lange Stiftleisten verwendet, die auch auf der Unterseite des Boards herausragen. So konnte ich die benötigten Verbindungen für den RFID-Reader bequem von unten herstellen – platzsparend und ohne Kabelsalat auf der Oberseite. Natürlich wäre auch jede andere Art der elektrischen Verbindung denkbar gewesen – entscheidend ist nur, dass die Pins 1 (3,3 V), 6 (GND), 14 (TXD) und 15 (RXD) korrekt mit dem RFID-Reader verbunden werden.
  • Die Lötstellen habe ich dann noch sorgfältig überprüft, um Kurzschlüsse zu vermeiden.

2. RFID-Modul (Elecrow Crowtail NFC) vorbereiten

  • Das Elecrow Crowtail NFC wird über UART mit dem Raspberry Pi verbunden
    • Nach etwas Recherche in den Datenblättern und auf https://pinout.xyz habe ich mich für den UART-Betriebsmodus des RFID-Readers entschieden. Standardmäßig ist der Reader im I²C-Modus konfiguriert – für den Wechsel auf UART musste daher eine kleine Lötbrücke angepasst werden.
    • Statt die beiden linken Lötpads zu verbinden (Werkseinstellung), habe ich die beiden rechten Pads miteinander verlötet – an der blau markierten Stelle auf der Platine. Um sicherzugehen, dass die ursprüngliche Verbindung wirklich getrennt ist, habe ich die Nicht-Leitfähigkeit anschließend vorsichtshalber mit einem Digitalmultimeter überprüft.
  • Die Anschlüsse des RFID-Moduls habe ich wie folgt mit dem Raspberry verdrahtet:
  • Dazu habe ich passende Kabel abgelängt, die Enden sauber verzinnt und anschließend an die entsprechenden Anschlüsse des RFID-Readers angelötet. Wo nötig, habe ich die Verbindungen mit Schrumpfschlauch isoliert – für etwas mehr Sicherheit und Ordnung im Gehäuse. Zum Anschluss an den Raspberry Pi habe ich kleine Steckerhülsen verwendet, die sich direkt und ohne Werkzeug auf die GPIO-Pins aufstecken lassen. So bleibt die Verdrahtung flexibel und bei Bedarf schnell lösbar.
  • Da die Platine des Elecrow Crowtail NFC 2.0 etwas kleiner ist als der Raspberry Pi, habe ich kurzerhand eine kleine Kunststoffplatte als Adapter zurechtgesägt – nein, kein echtes Carbon, aber optisch durchaus überzeugend. Bei der Umsetzung bin ich nicht mit dem Maßband, sondern eher mit Augenmaß und Stift vorgegangen: Löcher und Konturen habe ich einfach direkt von den Platinen abgepaust und dann ausgesägt. Nicht hochpräzise – aber vollkommen ausreichend für ein solides DIY-Projekt.

3. Abstandshalter montieren

  • Längen und Gewindeart der Hex-Abstandshalter habe ich nach Augenmaß ausgewählt und montiert.
  • Die untere Baugruppe sah dann aus wie im Bild rechts.

4. Kühlkörper anbringen

  • Dann habe ich den Aluminium-Kühlkörper mit den Wärmeleitpads und passenden Hex-Abstandshaltern auf dem Raspberry Pi und der bisherigen Baugruppe montiert.

5. Pirate Audio HAT montieren

  • Den Pirate Audio HAT habe ich dann auf die GPIO-Pins des Raspberry Pi gesteckt und vier Schrauben von oben in die Abstandshalter geschraubt.

6. RFID Antenne montieren

  • Praktischerweise passt die Antenne des Elecrow Crowtail NFC 2.0 RFID-Moduls genau auf den Audio-HAT neben das Display – ein echter Glücksfall in Sachen Gehäuse-Tetris. Ich habe sie dort einfach mit einem Klecks Heißkleber fixiert – hält gut, lässt sich bei Bedarf aber auch wieder lösen.

7. Verbindungen prüfen & Fixierung

  • Abschließend habe ich nochmals alle Verbindungen kontrolliert und das Kabel der Antenne mit einem Kabelbinder gesichert.

Coden

Nach der erfolgreichen Montage war das System bereit für die Software-Einrichtung.

1. SD-Karte mit Raspberry Pi OS Lite vorbereiten

An meinem PC habe ich den Raspberry Pi Imager von der offiziellen Webseite heruntergeladen: https://www.raspberrypi.com/software/

  • Im Raspberry Pi Imager habe ich Raspberry Pi OS Lite 64 Bit ausgewählt. Zum Zeitpunkt der Veröffentlichung dieses Blog-Posts ist dies auf Basis von Debian Version 12 (bookworm).
  • Anschließend habe ich die spätere Konfiguration direkt im Imager angepasst, das war selbsterklärend: Ich habe einen Benutzer, ein Passwort, die Zeitzone sowie SSH und WLAN eingerichtet.
  • Danach habe ich die SD-Karte ausgewählt und den Schreibvorgang gestartet.

2. Raspberry Pi starten & Updates durchführen

  1. Ich habe die SD-Karte in den Raspberry Pi eingelegt.
  2. Dann das Micro-USB-Netzteil angeschlossen und einen Moment gewartet, bis der Pi vollständig gebootet war.
  3. Anschließend habe ich mich per SSH von einem anderen PC aus mit dem Raspberry Pi verbunden.
    (Alternativ hätte ich auch ein Display und eine Tastatur direkt anschließen oder Raspberry Pi Connect installieren können.)
  4. Dann habe ich ein Systemupdate durchgeführt:
$ sudo apt update
$ sudo apt upgrade

3. Audio einrichten

In der Konfigurationsdatei…

$ sudo nano /boot/firmware/config.txt
  

... habe ich diese beiden Zeilen hinzugefügt:

dtoverlay=hifiberry-dac
gpio=25=op,dh
  

4. Schnittstellen aktivieren

Im Raspberry-Konfigurationsmenü…

$ sudo raspi-config
  

… habe ich 

  1. die SPI-Schnittstelle aktiviert: 3 Interface Options > I4 SPI > Yes
  2. die serielle Schnittstelle aktiviert: 3 Interface Options > I6 Serial Port > No > Yes
  3. und dann den Raspberry Pi neu gestartet

5. Notwendige Software installieren

  1. Als Nächstes habe ich alle später benötigten Werkzeuge und Python-Bibliotheken installiert:
$ sudo apt-get install python3-spidev python3-pip python3-pil python3-numpy p7zip-full wget
  

2. Und ein Projektverzeichnis und die virtuelle Umgebung für Python eingerichtet:

$ mkdir zeitmaschine
$ python -m venv zeitmaschine/.venv

3. Die virtuelle Umgebung für Python habe ich dann so aktiviert:

$ . zeitmaschine/.venv/bin/activate
  

(Dass die virtuelle Umgebung aktiviert ist, erkennt man später immer an dem (.venv) $ im Prompt)

 4. Die Python-Paketverwaltung (pip) habe ich dann noch aktualisiert und notwendige Pakete installiert:

(.venv)$ python -m pip install --upgrade --no-cache-dir pip setuptools wheel
(.venv)$ pip install --prefer-binary st7789 pillow pygame rpi-lgpio adafruit-blinka adafruit-circuitpython-pn532 requests

Hinweis

Mein Vorgehen war natürlich längst nicht so strukturiert, wie es diese Anleitung vielleicht vermuten lässt – sie ist erst im Nachhinein entstanden. Sollte ich dabei etwas vergessen oder übersehen haben, bitte ich um Nachsicht. Die meisten Lücken werden ohnehin zuverlässig von Fehlermeldungen aufgedeckt – so war es zumindest bei mir.

Und ja, einige dieser Meldungen haben mich ernsthaft an meinen Entscheidungen zweifeln lassen. Vor allem nachts um halb zwei, wenn ich mal wieder stundenlang durch Raspberry-Foren scrollte oder versuchte, ChatGPT eine Erklärung für eine besonders kryptische Fehlermeldung zu entlocken, die mir partout keinen Sinn ergeben wollte. Aber hey – so lernt man.

6. Code kopieren und entpacken

In dieser 7-Zip-Datei finden Sie meine Skripte, sowie die verwendeten Bilder und Sounds:

7-Zip-Datei herunterladen

(.venv)$ wget -O zeitmaschine/zeitmaschine.7z "https://www.projektron.de/fileadmin/user_upload/1_bilder_website/blog/fachartikel/2025/Basteln_mit_BCS_-_RFID/zeitmaschine.7z"
(.venv)$ 7z x zeitmaschine/zeitmaschine.7z -ozeitmaschine
(.venv)$ nano zeitmaschine/zeitmaschine.py
Skript: zeitmaschine.py anzeigen
#!/usr/bin/env python3
# -*- coding: utf-8 -*-

""" =============================================================================== Skript: zeitmaschine.py Version: 1.0.0 Datum: 14.08.2025 Autor: Max Roth, Projektron GmbH E-Mail: info@projektron.de

Beschreibung: NFC-basierte Zeiterfassung für Raspberry Pi mit Anzeige auf einem ST7789-Display. Anbindung an die Projektron BCS REST-API zur Buchung von Kommen, Gehen und Pausen.

Lizenz: MIT-Lizenz + Kindness: Du darfst diesen Code frei verwenden, verändern und weitergeben. Ich freue mich, wenn du mich erwähnst oder dein Projekt mit mir teilst.
Die Nutzung der BCS REST-API unterliegt dem Projektron Lizenzvertrag.
Teile dieses Codes wurden mit Unterstützung durch ChatGPT (OpenAI) erstellt oder optimiert.
Verwendete Ressourcen: - Soundeffekte von Mixkit (https://mixkit.co) - Bilder mit freundlicher Genehmigung der Projektron GmbH (http://www.projektron.de)

Notiz: Mir sind Schwächen und Stilbrüche dieses Skripts bewusst:
- Alles steckt zur Übersicht einer einzigen großen Datei - Hardcodierter API-Key - Keine Auslagerung von Parametern in eine Konfigurationsdatei - Fehlendes Logging. Es wird direkt mit print() gearbeitet. - Sehr direkte API-Intergration, kein Timeout - Kleinere Redundanzen
Es ist pragmatisch gelöst: "Hauptsache, es bucht!" 😅

© 2025 Max Roth, Projektron GmbH =============================================================================== """
# ============================================================================= # 🧱 Bibliotheken # ============================================================================= # 🧱 Standardbibliotheken import os # Betriebssystemfunktionen wie Pfadoperationen import sys # Zugriff auf Interpreterfunktionen import time # Zeitfunktionen (z.B. sleep) import signal # Behandlung von Signalen wie SIGINT, SIGTERM import atexit # Registrierung von Shutdown-Funktionen from datetime import datetime # Datums- und Zeitfunktionen from zoneinfo import ZoneInfo # Zeitzonenunterstützung
# 🌐 Drittanbieter-Bibliotheken import requests # HTTP-Client für Webanfragen (REST-API) import pygame # Soundausgabe from PIL import Image # Pillow-Bibliothek zur Bildverarbeitung import st7789 as st7789 # Display-Ansteuerung (SPI-TFT ST7789)
# ⚙️ Hardware- / Raspberry-Pi-spezifische Module import RPi.GPIO as GPIO # GPIO-Zugriff für Raspberry Pi import serial # Serielle Kommunikation (UART) import adafruit_pn532.uart as pn532_uart # NFC-Reader PN532 über UART
# ============================================================================= # 🔧 Konfiguration der BCS REST-API # ============================================================================= # 🔗 URL des BCS-Zeiterfassungssystems BCS_API_URL = "https:///rest-api/timerecorder/record"
# 🔑 API-Schlüssel zur Authentifizierung – Besser per Umgebungsvariable setzen! BCS_API_KEY = "...."
# 🏷️ Feldname in der BCS-Datenstruktur an der buchenden Person, der mit der UID aus dem RFID Tag verglichen wird (z.B. externalId oder userShortname) BCS_API_IDFIELDNAME = "userShortname"
# ============================================================================= # 📐 Parametrisierung # ============================================================================= # 📁 Pfad zum Verzeichnis der laufenden Python-Datei – Basis für Datei-Operationen (z.B. Bilder, Sounds) BASE_DIR = os.path.dirname(os.path.abspath(__file__))
# 🖥️ ST7789-Display-Setup disp = st7789.ST7789( height=240, width=240, rotation=90, # Anzeige rotiert um 90° port=0, # SPI-Port 0 cs=1, # Chip-Select-Leitung dc=9, # Data/Command-Pin backlight=13, # Pin für die Hintergrundbeleuchtung spi_speed_hz=80_000_000 # SPI-Taktfrequenz (80 MHz) ) disp.begin()
# 🔊 Audio-Setup mit pygame os.environ["SDL_AUDIODRIVER"] = "alsa" pygame.mixer.init()
# 🎵 Sounds sound_button = pygame.mixer.Sound(os.path.join(BASE_DIR, "mixkit-funny-squeaky-toy-hits-2813.wav")) # Hihihihihi sound_ok = pygame.mixer.Sound(os.path.join(BASE_DIR, "mixkit-winning-notification-2018.wav")) # ✅ sound_nok = pygame.mixer.Sound(os.path.join(BASE_DIR, "mixkit-sad-game-over-trombone-471.wav")) # ❌ sound_sync = pygame.mixer.Sound(os.path.join(BASE_DIR, "mixkit-bell-tick-tock-timer-1046.wav")) # ⏳
# 🖼️ Alle benötigten Bilder laden und skalieren IMAGE_FILES = [ "home.png", "sync.png", "ok.png", # ✅ "nok.png", # ❌ "kommen.png", "pause.png", "gehen.png", "pause_ende.png" ] IMAGES = {} for filename in IMAGE_FILES: path = os.path.join(BASE_DIR, filename) img = Image.open(path).resize((240, 240)) IMAGES[filename] = img
# 🖼️ Hilfs-Funktion: Zeigt das vorab geladene und auf 240x240 skalierte Bild auf dem ST7789-Display an. def show_image(image_key, duration=None): disp.display(IMAGES[image_key]) if duration: time.sleep(duration)
# 🎛️ GPIO-Setup für die Tasten (Interrupt-gesteuert) BUTTONS = { # Taste : (GPIO-Pin, Bild-Datei, bookingtype) "A": (5, "kommen.png", "AS"), "B": (6, "pause.png", "PS"), "X": (16, "gehen.png", "AE"), "Y": (24, "pause_ende.png", "PE") } #Verwende die logischen GPIO-Nummern nach BCM GPIO.setmode(GPIO.BCM)
#Für jeden konfigurierten Button soll der zugehörige GPIO-Pin als Eingang geschaltet werden – mit einem internen Pull-Up-Widerstand for btn, (pin, image, bookingtype) in BUTTONS.items(): GPIO.setup(pin, GPIO.IN, pull_up_down=GPIO.PUD_UP)
# ============================================================================= # 🧠 Menüführung # ============================================================================= current_menu = "home.png" # 🖼️ Aktuell angezeigtes Bild waiting_for_nfc = False # Warten wir gerade auf einen NFC-Scan? current_bookingtype = "" # Aktuell zu buchender Typ (AS, PS, etc.) last_press_time = time.time() # Zeitpunkt der letzten Tasteneingabe FALLBACK_TIME = 10 # Sekunden bis automatische Rückkehr zum Startbild
# 📞 Callback-Funktion für Button-Interrupts: wechselt Anzeige oder kehrt zu Home zurück def button_callback(channel): global current_menu, waiting_for_nfc, current_bookingtype, last_press_time
# Herausfinden, welcher Button gedrückt wurde for name, (pin, image, bookingtype) in BUTTONS.items(): if pin == channel: print(f"Taste {name} gedrückt!") sound_button.play()
# 🖼️ Bild wechseln + NFC-Wartemodus aktualisieren if current_menu != image: current_menu = image waiting_for_nfc = True current_bookingtype = bookingtype else: # 🏠 Wenn derselbe Button nochmal gedrückt wird -> zurück nach Home current_menu = "home.png" waiting_for_nfc = False show_image(current_menu) last_press_time = time.time() break
# 📉 Event Detection für jeden Button einrichten (fallende Flanke am GPIO 🎛️) for btn, (pin, image, bookingtype) in BUTTONS.items(): GPIO.add_event_detect(pin, GPIO.FALLING, callback=button_callback, bouncetime=200) # 🏓 Nicht sicher, ob 200 eine gute Entprellung ist, für mich hat es funktioniert.
# ============================================================================= # 📶 PN532 NFC-Reader Setup # ============================================================================= try: uart = serial.Serial("/dev/ttyS0", baudrate=115200, timeout=1) pn532 = pn532_uart.PN532_UART(uart) ic, ver, rev, support = pn532.firmware_version print(f"Found PN532 with firmware version: {ver}.{rev}") pn532.SAM_configuration() except Exception as e: print(f"Fehler beim Initialisieren des NFC-Readers: {e}") pn532 = None
# ============================================================================= # 🔄 NFC-Tag prüfen und Daten an Server senden # ============================================================================= def check_for_nfc(bookingtype): if pn532: try: uid = pn532.read_passive_target(timeout=0.1) except RuntimeError: return False
if uid: formatted_uid = "-".join(f"{x:02X}" for x in uid) # 🈁 Formatierung der UID print(f"Gefundene Karte mit UID: {formatted_uid}")
timestamp = datetime.now(ZoneInfo("Europe/Berlin")).isoformat(timespec='seconds') # 🕓 Formatierung der Uhrzeit
# 📄 Header vorbereiten headers = { "X-API-KEY": BCS_API_KEY } # 📦 Body vorbereiten data = { "type": bookingtype, # Buchungstyp (AS, AE, etc.) "timestamp": timestamp, # 🕓 Lokale Zeit im ISO-Format "id": formatted_uid, # 🈁 UID von Karte/Chip "idFieldName": BCS_API_IDFIELDNAME # 🏷️ Feldname in der BCS-Datenstruktur am Nutzer der mit der verglichen wird }
# ⏳ Starte die Warteschleife show_image("sync.png") sound_sync.play(-1)
try: # 📡 Und nun... der alles entscheidende POST an die BCS-API. 🔗 Die BCS URL, 📄 Header und 📦 Body werden übergeben: response = requests.post(BCS_API_URL, data=data, headers=headers, verify=True)
print(f"Antwort: {response.status_code}, {response.text}")
# ⏳ Stoppe die Warteschleife und Response-Handling sound_sync.stop()
message = response.text.strip().upper()
if "SUCCESS_" in message or "WARNING_" in message: # ✅ Es hat geklappt! sound_ok.play() show_image("ok.png", 1) elif "ERROR_" in message: # ❌ Es hat nicht geklappt. Schadenfreude aktivieren sound_nok.play() show_image("nok.png", 2) else: # ❌ Wenn der Server mal irgendwas antwortet, was wir gar nicht wissen wollten – z. B. eine Wartungsmeldung. print("Unbekannter Rückgabewert – Behandlung als Fehler.") sound_nok.play() show_image("nok.png", 2)
except Exception as e: # ❌ Verbindung fehlgeschlagen – schnell, tu so als wäre nix passiert! print(f"Fehler beim Senden der Anfrage: {e}") sound_sync.stop() sound_nok.play() show_image("nok.png", 2)
return True return False
# ============================================================================= # 🧹 Shutdown-Routine: Display schwarz schalten und GPIO aufräumen # ============================================================================= def shutdown(): print("System wird heruntergefahren, Display wird schwarz.") black_img = Image.new("RGB", (240, 240), "black") disp.display(black_img) GPIO.cleanup()
# Shutdown-Routine beim normalen Beenden registrieren atexit.register(shutdown)
# Auch auf SIGTERM (z. B. beim Herunterfahren des Systems) reagieren def sigterm_handler(signum, frame): shutdown() sys.exit(0)
signal.signal(signal.SIGTERM, sigterm_handler)
# ============================================================================= # 🧠 Warten auf RFID # ============================================================================= show_image(current_menu)
try: while True: # 📶 NFC prüfen, wenn wir gerade darauf warten if waiting_for_nfc: if check_for_nfc(current_bookingtype): # 🏠 Erfolgreich NFC gescannt -> zurück nach Home show_image("home.png") waiting_for_nfc = False current_menu = "home.png"
# 🏠 Nach 10 Sekunden Inaktivität: zurück nach Home if (time.time() - last_press_time > FALLBACK_TIME) and (current_menu != "home.png"): show_image("home.png") waiting_for_nfc = False current_menu = "home.png" last_press_time = time.time()
# 💤 Kleines Schläfchen zur Entlastung der CPU time.sleep(0.1)
except KeyboardInterrupt: # Bei KeyboardInterrupt wird shutdown() über atexit automatisch aufgerufen. sys.exit(0)

Das Herzstück des Skripts zeitmaschine.py ist der Kontakt zur BCS-API. Alles andere (z. B. die Menüführung) ist nur „Drumherum“. 

Was passiert genau?

1. UID formatieren
Jede Karte oder jeder Chip hat eine eindeutige ID (UID). Diese wird in ein bestimmtes Format gebracht:

213 formatted_uid = "-".join(f"{x:02X}" for x in uid) # 🈁 Formatierung der UID
  

Die Zahlen aus dem Chip werden in Großbuchstaben-Hexadezimalzahlen mit führenden Nullen umgewandelt und mit - verbunden (z. B. 04-1A-B2-FF).

2. Zeitstempel erstellen
Die aktuelle Uhrzeit wird im ISO-Format (z. B. 2025-08-01 T14:32:00) erzeugt.

216 timestamp = datetime.now(ZoneInfo("Europe/Berlin")).isoformat(timespec='seconds') # 🕓 Formatierung der Uhrzeit
  

3. Header vorbereiten
Damit die API weiß, ob das eine autorisierte Anfrage ist, wird ein API-Schlüssel mitgeschickt:

218 # 📄 Header vorbereiten
219 headers = { "X-API-KEY": BCS_API_KEY }
  

4. Daten (Body) vorbereiten
Jetzt wird alles sauber verpackt, was an die API gehen soll:

220 # 📦 Body vorbereiten
221 data = {
222        "type": bookingtype,                # Buchungstyp (AS, AE, etc.)
223        "timestamp": timestamp,             # 🕓 Lokale Zeit im ISO-Format
224        "id": formatted_uid,                # 🈁 UID von Karte/Chip
225        "idFieldName": BCS_API_IDFIELDNAME  # 🏷️ Feldname in der BCS-Datenstruktur am Nutzer der mit der verglichen wird
226 }
  

5. Anfrage senden
Schließlich wird alles per POST an die BCS-API gesendet:

233 # 📡 Und nun... der alles entscheidende POST an die BCS-API. 🔗 Die BCS URL, 📄 Header und 📦 Body werden übergeben:
234 response = requests.post(BCS_API_URL, data=data, headers=headers, verify=True)
  

Hinweis

Für den in diesem Projekt verwendeten REST-API-Endpunkt steht eine technische Dokumentation im OpenAPI-Format (YAML) zur Verfügung. Diese kann bei Bedarf direkt über den Projektron-Support angefragt werden.

Zur Inbetriebnahme müssen in der zeitmaschine.py noch die Parameter BCS_API_URL sowie BCS_API_KEY angepasst werden.

73 # =============================================================
74 # 🔧 Konfiguration der BCS REST-API
75 # =============================================================
76 # 🔗 URL des BCS-Zeiterfassungssystems
77 BCS_API_URL = "https://<Servername>/rest-api/timerecorder/record"
78
79 # 🔑 API-Schlüssel zur Authentifizierung – Besser per Umgebungsvariable setzen! 80 BCS_API_KEY = "...." 81
82 # 🏷️ Feldname in der BCS-Datenstruktur an der buchenden Person, der mit der UID aus dem RFID Tag verglichen wird (z.B. externalId oder userShortname) 83 BCS_API_IDFIELDNAME = "userShortname"

 Im Folgenden erkläre ich Ihnen kurz, wie Sie an diese Werte gelangen:

BCS_API_URL

Dies ist die URL Ihres BCS-Servers, ergänzt um den Pfad zum REST-Endpunkt:

 https://<Servername>/rest-api/timerecorder/record

(Alternativ funktioniert auch http://, sofern Ihr BCS-Server kein SSL-Zertifikat verwendet.)

BCS_API_KEY

Den API-Key können Sie im BCS-System selbst erzeugen und anschließend hier eintragen. Das geht an dieser Stelle (Als Administrator am Synchronisations-User in der Ansicht "Benutzerkonto").

Wichtig: Der BCS-Baustein Webservices > Stechuhr muss aktiviert sein, um einen API-Key erzeugen zu können und Abwesenheiten über das Terminal erfassen zu können.

Stempeln

Nun ist das Terminal einsatzbereit.

(.venv)$ python zeitmaschine/zeitmaschine.py
  

Nun können die ersten Aktionen auf dem Gerät ausprobiert (Taste A drücken) und ein RFID-Tag an den Reader gehalten werden. Folgende Ausgabe ist zu erwarten:

Am Gerät:

Auf Kommandozeile:

Taste A gedrückt!
Gefundene Karte mit UID: 63-D2-E7-0D
Antwort: 404, ERROR_USER_NOT_FOUND: The time recording failed. The user cannot be found. Please record your times manually or contact an administrator.
  

Dass diese Meldung jetzt erscheint, ist völlig normal und war zu erwarten. Der Grund: In Projektron BCS ist bisher noch keine ID für einen zur Zeiterfassung berechtigten Nutzer hinterlegt.

Über die Konstante BCS_API_IDFIELDNAME = "userShortname" kann im Skript festgelegt werden, in welchem Attribut einer Person die ID des RFID-Tags erwartet wird. Ich habe mich hier für "userShortname" – also das Kürzel der Person – entschieden.

82 # 🏷️ Feldname in der BCS-Datenstruktur an der buchenden Person, der mit der UID aus dem RFID Tag verglichen wird (z.B. externalId oder userShortname)
83 BCS_API_IDFIELDNAME = "userShortname"
  

Grundsätzlich kann jedoch jedes andere Attribut (oder Zusatzattribut) einer Person für die Zuordnung genutzt werden. Was also noch fehlt, ist der Eintrag der erkannten UID (in meinem Beispiel "63-D2-E7-0D") als Kürzel bei der entsprechenden Person.

Nun sollte alles funktionieren und die ausgeführten Transaktionen zu korrekten Anwesenheits- und Pause-Buchungen in BCS führen. 

Am Gerät:

Die Frage nach dem "Warum" / Wer nicht basteln möchte

Natürlich gibt es gute Gründe, warum selbstgebaute Hardware im Unternehmensumfeld mit einer gesunden Portion Skepsis betrachtet wird. Auch ich musste meine privat zusammengebastelten Prototypen zunächst der kritisch blickenden Brandschutzbeauftragten und der IT-Abteilung erklären – und durfte sie anschließend, unter genauer Beobachtung, nur einmal im Büro vorführen. (Stichwort: fehlende CE-Zertifizierung)

Hinzu kommt: Die in diesem Projekt verwendeten RFID-Tags vom Typ MIFARE Classic sind technisch längst überholt. MIFARE Classic-Chips lassen sich klonen – und das leider ziemlich einfach. Das zugrunde liegende Verschlüsselungsverfahren Crypto-1 gilt seit vielen Jahren als vollständig kompromittiert – sicherheitstechnisch also ein echtes Relikt aus der Vergangenheit. Für den produktiven Einsatz sind daher moderne Transpondertechnologien wie die aktuellen Generationen von MIFARE DESFire oder vergleichbare Alternativen klar zu bevorzugen. Sie bieten nicht nur deutlich höhere Sicherheitsstandards, sondern ermöglichen auch eine vernünftige verschlüsselte Übertragung personenbeziehbarer Daten. Selbst die tatsächliche UID kann dabei dynamisch ersetzt werden – eine sinnvolle Funktion, um ein Tracking durch Dritte zu verhindern.

Dass Projektron selbst kein Hardwarehersteller werden wird, dürfte wenig überraschen. Mein eigentlicher Arbeitsauftrag bestand darin, geeignete Zeiterfassungsterminals zu recherchieren, die sich reibungslos in unsere bestehende Schnittstellenlandschaft integrieren lassen. Die Entwicklung eines eigenen Terminals war ein privates Freizeitprojekt – entstanden aus Neugier, Bastelfreude und dem Wunsch, ein tieferes technisches Verständnis für die Anforderungen zu gewinnen. Es diente letztlich als Ausgangspunkt für diesen Blogbeitrag – und als Grundlage für eine fundierte Auswahl.

Fündig geworden bin ich schließlich bei einem etablierten Hardwarehersteller aus Thüringen: Datafox ist ein Hersteller von Hardware für Zeiterfassung, Zutrittskontrolle und Betriebsdatenerfassung. Weitere Informationen finden Sie unter https://www.datafox.de/produkte-loesungen/personalzeiterfassung

Datafox: Alternative zum Basteln

Projektron bietet ab sofort Geräte des Herstellers Datafox mit passender Konfiguration an. Unterstützt werden alle Geräte der Datafox EVO-Serie. Zusätzlich wird nur ein kleiner Webserver für die Abwicklung der Kommunikation für die Datafox Http-Schnittstelle benötigt. Für ein individuelles Angebot wenden Sie sich bitte an Ihren Projektron-Kundenbetreuer – oder direkt an mich.

Angebot einfordern

Achim Habbel, Geschäftsführender Gesellschafter | Alfha GmbH & Co. KG

(Tester der ersten Referenzimplementierung der BCS API zur Anwesenheitserfassung)

“Buchungen vom Terminal kommen direkt „live“ in BCS an. Die Mitarbeiterin oder der Mitarbeiter erhält ein direktes Feedback am Terminal. Kommt es zu Buchungsfehlern, z. B. einer versehentlichen erneute Kommen- statt einer Gehen-Buchung, erscheint eine aussagefähige Fehlermeldung am Terminal. Das Datafox-Terminal ist über die Schnittstelle optimal in BCS integriert.”

Zum Anwenderbericht der Alfha GmbH & Co. KG

Über den Autor

Max Roth begann seine berufliche Laufbahn Ende der 1990er Jahre mit einer Ausbildung zum IT-Systemelektroniker – blieb diesem damals noch neuen Berufsbild jedoch nur kurz treu. Nach zwei Hochschulabschlüssen (Dipl.-Wirtschaftsinformatiker (FH) und M.A. HSG in Informations-, Medien- und Technologiemanagement) führte ihn sein Weg zu Projektron. Heute ist er als Berater am Standort Stuttgart tätig.

Weitere interessante Artikel im Projektron-Blog

Vom Basteltisch ins Büro: Entdecken Sie, wie Sie mit Raspberry Pi & REST-API ein eigenes RFID-Zeiterfassungsterminal für Projektron BCS bauen.

Vom Basteltisch ins Büro: Entdecken Sie, wie Sie mit Raspberry Pi & REST-API ein eigenes RFID-Zeiterfassungsterminal für Projektron BCS bauen.