Ein Raspberry Pi RFID MQTT Logger erfasst jeden Badge-Scan an einer Tür, einer Werkstattmaschine oder einem Werkzeugschrank und sendet ihn an jedes System, das davon wissen muss: eine Protokolldatenbank, ein Dashboard oder ein Alarmskript. In diesem Tutorial verdrahten Sie einen RC522-Leser, lesen Tags mit Python, entscheiden lokal, ob ein Tag zugelassen ist, und veröffentlichen strukturierte Zutrittsereignisse über MQTT, einschließlich Alarmen für unbekannte Karten.
Was Sie bauen
Auf dem Pi läuft ein Python-Dienst, der:
- Den RC522-Leser auf 13,56-MHz-MIFARE-Karten und Schlüsselanhänger abfragt.
- Die UID jedes Tags hasht, sodass die rohe ID das Gerät nie verlässt.
- Den Hash in einer lokalen Allowlist-Datei nachschlägt und über Zutritt gewährt oder verweigert entscheidet.
- Für jeden Scan ein Zutrittsereignis und für unbekannte Tags einen separaten Alarm veröffentlicht.
- Eine retained Nachricht „letzter Zutritt“ sowie einen Online-/Offline-Status vorhält.
Die Zutrittsentscheidung fällt auf dem Pi, nicht in der Cloud. So funktioniert die Tür auch bei Netzausfall weiter; MQTT dient der Protokollierung und Benachrichtigung, und genau darin ist es stark.
Den RC522 verdrahten
Das RC522-Modul spricht SPI und läuft ausschließlich mit 3,3 V. Verbinden Sie es so:
| RC522-Pin | Physischer Pin am Raspberry Pi | Funktion |
|---|---|---|
| SDA (SS) | 24 | SPI CE0 |
| SCK | 23 | SPI-Takt |
| MOSI | 19 | SPI MOSI |
| MISO | 21 | SPI MISO |
| IRQ | Nicht verbunden | |
| GND | 6 | Masse |
| RST | 22 | GPIO25 |
| 3.3V | 1 | Versorgung |
Schließen Sie das Modul niemals an 5 V an. Aktivieren Sie SPI mit sudo raspi-config nonint do_spi 0 und starten Sie neu; danach sollte ls /dev/spidev* den Eintrag /dev/spidev0.0 zeigen.
Die Bibliotheken installieren
sudo apt update
sudo apt install -y python3-venv python3-dev
mkdir -p ~/rfid-mqtt && cd ~/rfid-mqtt
python3 -m venv .venv
source .venv/bin/activate
pip install "paho-mqtt>=2.0" mfrc522 spidev
Das Paket mfrc522 hängt von RPi.GPIO ab, das auf dem Raspberry Pi 5 nicht funktioniert. Ersetzen Sie es auf einem Pi 5 innerhalb der virtuellen Umgebung durch das kompatible Paket rpi-lgpio:
pip uninstall -y RPi.GPIO
pip install rpi-lgpio
Testen Sie den Leser, bevor Sie MQTT hinzufügen:
python -c "from mfrc522 import SimpleMFRC522; r = SimpleMFRC522(); print('Hold a tag near the reader'); print(r.read_id())"
Halten Sie eine Karte oder einen Anhänger an die Antenne; es sollte eine lange Zahl ausgegeben werden.
Die Ereignisse entwerfen
| Topic | Payload | QoS | Retained |
|---|---|---|---|
connectmq/demo/2fa7c1/door1/access |
Zutrittsereignis als JSON | 1 | Nein |
connectmq/demo/2fa7c1/door1/alert |
Unbekannter Tag als JSON | 1 | Nein |
connectmq/demo/2fa7c1/door1/last |
Letztes Ereignis | 1 | Ja |
connectmq/demo/2fa7c1/door1/status |
online / offline |
1 | Ja |
Ein Zutrittsereignis sieht so aus:
{
"reader": "door1",
"tag": "a3f09b7c41de2280",
"name": "Alice",
"granted": true,
"ts": 1792163300
}
Das Feld tag ist ein mit Schlüssel gebildeter Hash der UID, nicht die UID selbst. Auf einem öffentlichen MQTT-Broker und in Protokolldateien allgemein sollten Sie eine rohe UID nicht verbreiten, denn günstige Karten lassen sich damit klonen.
Die Allowlist-Datei
Legen Sie allowlist.json im Projektordner an. Die Hashes tragen Sie nach den ersten Scans ein:
{
"a3f09b7c41de2280": "Alice",
"51c2e8d4097fab13": "Workshop spare fob"
}
Um eine neue Karte anzulernen, scannen Sie sie einmal, kopieren den tag-Wert aus der Alarmnachricht, tragen ihn mit einem Namen in die Datei ein und senden dem Dienst ein SIGHUP zum Neuladen, oder starten ihn einfach neu.
Das Raspberry Pi RFID MQTT-Skript
Speichern Sie es als rfid_mqtt.py:
#!/usr/bin/env python3
"""RC522 access logging over MQTT."""
import hashlib
import hmac
import json
import os
import signal
import time
import uuid
from pathlib import Path
import paho.mqtt.client as mqtt
import RPi.GPIO as GPIO
from mfrc522 import SimpleMFRC522
BROKER = os.getenv("MQTT_HOST", "broker.connectmq.com")
PORT = int(os.getenv("MQTT_PORT", "1883"))
USERNAME = os.getenv("MQTT_USERNAME")
PASSWORD = os.getenv("MQTT_PASSWORD")
CLIENT_ID = os.getenv("MQTT_CLIENT_ID", f"rfid-{uuid.uuid4().hex[:8]}")
PREFIX = os.getenv("MQTT_PREFIX", "connectmq/demo/2fa7c1/door1")
READER_NAME = os.getenv("READER_NAME", "door1")
SECRET = os.getenv("TAG_SECRET", "change-me-to-a-long-random-string").encode()
ALLOWLIST_FILE = Path(os.getenv("ALLOWLIST", "allowlist.json"))
DEBOUNCE_S = 3
reader = SimpleMFRC522()
allowlist = {}
def load_allowlist(*_):
global allowlist
try:
allowlist = json.loads(ALLOWLIST_FILE.read_text())
print(f"Loaded {len(allowlist)} allowed tags")
except (OSError, json.JSONDecodeError) as err:
print(f"Could not load allowlist: {err}")
def tag_hash(uid: int) -> str:
digest = hmac.new(SECRET, str(uid).encode(), hashlib.sha256).hexdigest()
return digest[:16]
def on_connect(client, userdata, flags, reason_code, properties):
if reason_code.is_failure:
print(f"Connection refused: {reason_code}")
return
print(f"Connected as {CLIENT_ID}")
client.publish(f"{PREFIX}/status", "online", qos=1, retain=True)
client = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2, client_id=CLIENT_ID)
client.on_connect = on_connect
client.will_set(f"{PREFIX}/status", "offline", qos=1, retain=True)
client.reconnect_delay_set(min_delay=1, max_delay=60)
client.max_queued_messages_set(500)
if USERNAME:
client.username_pw_set(USERNAME, PASSWORD)
if PORT == 8883:
client.tls_set()
load_allowlist()
signal.signal(signal.SIGHUP, load_allowlist)
client.connect(BROKER, PORT, keepalive=30)
client.loop_start()
last_tag, last_time = None, 0.0
try:
while True:
uid = reader.read_id_no_block()
if uid is None:
time.sleep(0.1)
continue
tag = tag_hash(uid)
now = time.time()
if tag == last_tag and now - last_time < DEBOUNCE_S:
continue # Karte liegt noch auf dem Leser
last_tag, last_time = tag, now
name = allowlist.get(tag)
event = {
"reader": READER_NAME,
"tag": tag,
"name": name,
"granted": name is not None,
"ts": int(now),
}
payload = json.dumps(event)
client.publish(f"{PREFIX}/access", payload, qos=1)
client.publish(f"{PREFIX}/last", payload, qos=1, retain=True)
if name is None:
client.publish(f"{PREFIX}/alert", payload, qos=1)
print(event)
# Bei gewährtem Zutritt hier Türöffner oder LED mit GPIO.output() ansteuern.
except KeyboardInterrupt:
pass
finally:
client.publish(f"{PREFIX}/status", "offline", qos=1, retain=True).wait_for_publish(2)
client.loop_stop()
client.disconnect()
GPIO.cleanup()
Die Entprellung verhindert, dass eine auf dem Leser liegende Karte jede Zehntelsekunde ein Ereignis erzeugt. Weil paho QoS-1-Nachrichten bei unterbrochener Verbindung im Speicher puffert, werden Scans während eines kurzen Netzausfalls nach der Wiederverbindung zugestellt; max_queued_messages_set begrenzt diese Warteschlange, damit ein langer Ausfall den Speicher nicht erschöpft.
Den Ablauf testen
Beobachten Sie alles in einem anderen Terminal:
mosquitto_sub -h broker.connectmq.com -p 1883 -i audit-2fa7c1 \
-t 'connectmq/demo/2fa7c1/door1/#' -v
Scannen Sie eine unbekannte Karte: Sie sehen ein access-Ereignis mit "granted": false und dieselbe Payload auf alert. Tragen Sie den Hash in allowlist.json ein, senden Sie dem Prozess mit kill -HUP <pid> ein SIGHUP und scannen Sie erneut, um "granted": true mit dem Namen zu sehen.
Protokollierung und Alarmierung
Der Leser veröffentlicht nur; Speicherung und Benachrichtigung übernehmen andere MQTT-Clients, was das Gerät einfach hält. Gängige Optionen sind:
- Ein Python-Abonnent, der jedes
access-Ereignis in SQLite schreibt, wie in MQTT-Daten mit Python in SQLite speichern gezeigt. - Eine E-Mail- oder Chat-Benachrichtigung auf dem
alert-Topic nach dem Muster aus MQTT-E-Mail-Alarme mit Python. - Ein Node-RED-Flow, der die retained
last-Nachricht auf einem Dashboard anzeigt. - Ein Türöffner oder eine Signalleuchte, angesteuert von einem zweiten Pi mit der Steuerung Raspberry Pi Relais über MQTT.
Über die Eingangstür hinaus
Derselbe Leser und dasselbe Ereignisformat passen zu weiteren Aufgaben. In einem Makerspace kann ein Leser neben einem Laserschneider oder einer Drehmaschine protokollieren, wer die Maschine wie lange genutzt hat: Veröffentlichen Sie ein zweites Ereignis, wenn die Karte entfernt wird, und ein Abonnent kann die Nutzungsdauer berechnen. Für die Anwesenheitserfassung in einem Schulungsraum oder kleinen Büro dient das retained last-Topic zugleich als Live-Anzeige „wer gerade angekommen ist“, und das SQLite-Protokoll wird zum Stundenzettel. In einem Werkzeugschrank kann ein Scan eine Schublade entriegeln und festhalten, welcher Anhänger sie geöffnet hat.
In jedem Fall ändern sich nur der Lesername und das Topic-Präfix. Verwenden Sie eine Präfixebene pro Ort, etwa site1/workshop/lathe, dann kann ein einziger Abonnent mit einem +-Wildcard die Ereignisse aller Leser einsammeln. Ein Feld granted und ein stabiler gehashter tag sind alles, was die nachgelagerten Werkzeuge brauchen, sodass der Gerätecode im ganzen Gebäude identisch bleibt.
Rückmeldung für die scannende Person
Menschen erwarten, zu sehen oder zu hören, dass ein Scan funktioniert hat. Ergänzen Sie an freien GPIO-Pins eine grüne und eine rote LED oder einen kleinen Summer und steuern Sie sie direkt nach der Entscheidung mit GPIO.output() an; beachten Sie, dass die Leser-Bibliothek RPi.GPIO bereits in den BOARD-Nummerierungsmodus versetzt, verwenden Sie also physische Pinnummern. Geben Sie die Rückmeldung lokal, statt auf einen MQTT-Roundtrip zu warten, damit sie auch in einem langsamen Netz sofort erfolgt.
Sicherheitsaspekte
RFID-Projekte berühren die physische Sicherheit, seien Sie also realistisch, was ihre Grenzen angeht. Einfache MIFARE-Classic-Karten und viele Anhänger lassen sich klonen. Ein UID-basiertes System eignet sich deshalb für Protokollierung, Anwesenheitserfassung und komfortablen Zutritt, nicht für den Schutz hochwertiger Bereiche. Außerdem gilt:
- Betreiben Sie niemals ein Zutrittssystem auf dem öffentlichen MQTT-Broker. Alles auf broker.connectmq.com ist für jeden lesbar und nur für den ersten Test gedacht.
- Verwenden Sie einen privaten Broker mit TLS und Zugangsdaten pro Gerät, damit niemand gefälschte Ereignisse veröffentlichen oder Ihre Protokolle lesen kann.
- Halten Sie
TAG_SECRETaus dem Code und aus der Versionsverwaltung heraus; speichern Sie es in einer systemd-Umgebungsdatei mitchmod 600. - Treffen Sie die Zutrittsentscheidung lokal, damit ein Netzausfall niemanden aussperrt oder die Tür öffnet.
Weiterführende Hinweise finden Sie in unseren Best Practices zur MQTT-Sicherheit.
Der Weg in die Produktion
Ein verwalteter MQTT-Broker wie ConnectMQ bietet TLS auf Port 8883, generierte Zugangsdaten, die an jede Client ID gebunden sind, und ein Portal, in dem Sie im Live-Topic-Explorer verfolgen, wie Zutrittsereignisse eintreffen. Setzen Sie die Umgebungsvariablen in Ihrer Service-Datei:
MQTT_HOST=broker.connectmq.com
MQTT_PORT=8883
MQTT_USERNAME=YOUR_USERNAME
MQTT_PASSWORD=YOUR_PASSWORD
MQTT_CLIENT_ID=YOUR_CLIENT_ID
MQTT_PREFIX=site1/building-a/door1
TAG_SECRET=a-long-random-secret
Das Raspberry Pi MQTT Tutorial enthält eine systemd-Service-Vorlage, die Sie anpassen können, damit der Leser beim Booten startet.
Starten Sie mit ConnectMQ
Geben Sie Ihren Zutrittsprotokollen einen privaten, verschlüsselten Weg. Erstellen Sie einen kostenlosen Serverless-Broker bei ConnectMQ, ohne Kreditkarte, und verbinden Sie Ihren Leser mit den generierten Zugangsdaten per TLS. Es ist ein Cloud-MQTT-Broker, den Ihr Logger, Ihr Dashboard und Ihre Alarmskripte erreichen, ohne dass am Standort Ports geöffnet werden müssen.
Häufig gestellte Fragen
Welche RFID-Karten funktionieren mit dem RC522?
Der RC522 liest 13,56-MHz-Karten nach ISO 14443A wie MIFARE Classic, MIFARE Ultralight und viele Schlüsselanhänger. 125-kHz-Proximity-Karten liest er nicht.
Warum wird die RFID-UID vor dem Veröffentlichen gehasht?
Mit einer rohen UID lassen sich einfache Karten klonen, und MQTT-Nachrichten landen oft in Protokollen. Ein Hash mit Schlüssel identifiziert die Karte weiterhin eindeutig, ohne die UID preiszugeben.
Funktioniert der RC522 auf einem Raspberry Pi 5?
Ja, mit aktiviertem SPI und dem Paket rpi-lgpio, installiert anstelle von RPi.GPIO, das die Bibliothek mfrc522 erwartet.
Was passiert mit Scans, wenn das Netzwerk ausfällt?
Der Pi entscheidet weiterhin lokal über den Zutritt. QoS-1-Ereignisse werden im Speicher gepuffert und nach der Wiederverbindung zugestellt, solange der Dienst weiterläuft.



