RFID RC522 - Eine Einführung

Hier werden einzelne Projekte mit MicroPython vorgestellt
Antworten
Heinrichs
Beiträge: 151
Registriert: Do 21. Okt 2010, 18:31

RFID RC522 - Eine Einführung

Beitrag von Heinrichs » Sa 30. Apr 2022, 18:54

Die Abkürzung RFID steht für Radio Frequency Identification, zu deutsch: „Identifizierung mit Hilfe elektromagnetischer Wellen“. Damit bezeichnet man eine Technologie für Sender-Empfänger-Systeme, mit der man automatisch und berührungslos Objekte und Lebewesen mit Hilfe von Radiowellen identifizieren und lokalisieren kann. In der Abb. 1 ist das im Folgenden behandelte RFID-Modul mitsamt zweier sogenannter Transponder zu sehen, einer RFID-Karte und einem RFID-Chip. Karte und Chip unterscheiden sich nicht in der Funktionalität. Der Einfachheit halber werden wir hier immer nur von “RFID-Karten” sprechen.

rfid_kit.jpg
Abb. 1: RFID-Modul, RFID-Karte und -Chip
rfid_kit.jpg (76.55 KiB) 271 mal betrachtet

Für den Einsatz von solchen RFID-Modulen und zugehörigen Tags gibt es zahlreiche Anwendungsbereiche; dazu gehören Zugangskontrollen, Identifikation von Personen oder Objekten, Navigation, Bestandsverfolgung oder auch Zahlungssystemen.


1. Funktionsweise

Mit Hilfe des RFID-Moduls können Daten auf die RFID-Karte geschrieben oder auch von ihr gelesen werden. Für eine Zugangskontrolle kann z. B. ein auf der RFID-Karte gespeicherter Code mit dem am Türschloss-Controller eingegebenen Code verglichen werden und damit ein Zugang gewährt werden (oder auch nicht). Dabei erfolgt die Datenübertragung kontaktlos: Die Karte muss nur in die Nähe des RFID-Moduls in einem Abstand von wenigen Zentimetern gehalten werden; dann können die Daten per Funk ausgetauscht werden.

RFID_Funk.jpg
Abb. 2: Energieversorgung und Kommunikation durch elektromagnetische Wellen
RFID_Funk.jpg (32.96 KiB) 269 mal betrachtet

Die RFID-Karte besitzt selbst keine elektrische Quelle. Woher bezieht sie nun ihre Energie? Das RFID-Modul strahlt mit seiner spulenartiger Antenne (deutlich in der Abb. 1 zu erkennen) elektromagnetische Wellen aus. Die RFID-Karte empfängt mit ihrer Antenne (vgl. Abb. 2) die vom Modul ausgehenden elektromagnetischen Wellen; ein Teil des daraus resultierenden Wechselstroms wird gleichgerichtet und dient dann zur elektrischen Versorgung der Karte.

Zur Übertragung von Daten werden die elektromagnetischen Wellen moduliert. Sendet das RFID-Modul, wird die sogenannte Miller-Codierung benutzt; hierbei handelt es sich um eine Phasen-Kodierung, die der Manchester-Codierung (vgl. http://www.forum.g-heinrichs.de/viewtopic.php?f=12&t=76) ähnelt. Sendet die Karte, wird (je nach Übertragungsgeschwindigkeit) die Manchester-Kodierung bzw. BPSK (vgl. z. B. https://de.wikipedia.org/wiki/Phasenumtastung) benutzt. Genauere Informationen zur prinzipiellen Funktionsweise von RFID findet man hier.

Auf unserer RFID-Karte lassen sich 1 KByte Daten speichern. Allerdings beträgt der Payload (der tatsächlich nutzbare Speicherplatz) nur etwa 75% davon. (Später mehr dazu...)


2. Inbetriebnahme

Wir beschreiben nun Schritt für Schritt, wie wir das RFID-Modul mit dem TTGO in Betrieb nehmen.

2.1 Anschließen

Zunächst verbinden wir das RFID-Modul mit dem TTGO gemäß folgender Tabelle:

Code: Alles auswählen

RFID		3.3V	GND	MISO 	MOSI	SCK	SDA
TTGO		3V	G	12	15	17	25
Die Bezeichnungen der Anschlüsse machen es schon deutlich: Die Datenübertragung zwischen TTGO und RFID-Modul erfolgen mit dem SPI-Protokoll (vgl. ...); der Anschluss SDA wird allerdings meist mit CS (Chip Select) gekennzeichnet.

2.2 Kommunikation mit dem RFID-Modul

Mit Hilfe des SPI-Protokolls können Einstellungen am RFID-Modul vorgenommen werden, es können Befehle gegeben und damit u. a. auch Daten gesendet bzw. empfangen werden. Wie dies im Detail geschieht, ist in dem Datenblatt MFRR522.pdf (Link) ausführlich dokumentiert. Das Datenblatt umfasst knapp hundert Seiten und der Umgang auf dieser Byte-Ebene ist hochgradig komplex (Hinweis auf SPI-Ansteuerung von OLED-Display bzw. Von SD-Karte). Glücklicherweise gibt es Micropython-Module, welche uns eine Klasse MFRC522 zur Verfügung stellt. Diese Klasse enthält eine Reihe von Eigenschaften und Methoden, mit deren Hilfe sich das RFID-Modul deutlich einfacher bedienen lässt.

Das hier benutzte Modul mfrc522.py habe ich von der Webseite https://github.com/Tasm-Devil/micropython-mfrc522-esp32 herunter geladen; allerdings habe ich einige kleinere Anpassungen für den TTGO vorgenommen und einen fehlenden Rückgabewert bei der write-Methode ergänzt. Dieses modifizierte Modul wird in der Anlage rfid1.zip zur Verfügung gestellt. Um das Modul einzusetzen, muss es in den Speicher des ESP32 hochgeladen werden.

2.3 Unser erstes Programm

Mit unserem ersten Programm wollen wir den UID (Unique IDentifier) einer RFID-Karte auslesen und auf dem Terminal ausgeben. Der UID besteht aus 4 (bei einigen neueren Karten aus 7) Bytes. Bitte beachten Sie: Seit der Einführung der 7-Byte-UIDs kann man nicht mehr davon ausgehen, dass der UID einer RFID-Karte wirklich einzigartig (unique) ist; deswegen nennt man diese IDs inzwischen manchmal auch NUIDs (Non-Unique Identifier).

Hier zunächst das Programm:

Code: Alles auswählen

# Initialisierungen...

from time import sleep_ms
from machine import Pin, SoftSPI
from mfrc522 import MFRC522

sck = Pin(17, Pin.OUT) # 18 -> 17
mosi = Pin(15, Pin.OUT) # 23 -> 15
miso = Pin(12, Pin.OUT) # 19 -> 12
spi = SoftSPI(baudrate=100000, polarity=0, phase=0, sck=sck, mosi=mosi, miso=miso)

sda = Pin(25, Pin.OUT) # 5 -> 25

# Funktionen...

def do_read_uid():
    try:
        while True:
            rdr = MFRC522(spi, sda)
            uid = ''
            (stat, tag_type) = rdr.request(rdr.REQIDL)
            if stat == rdr.OK:
                (stat, raw_uid) = rdr.anticoll()
                if stat == rdr.OK:
                    uid = ("0x%02x%02x%02x%02x" % (raw_uid[0], raw_uid[1], raw_uid[2], raw_uid[3]))
                    print(uid)
                    sleep_ms(100)
    except KeyboardInterrupt:
        print('Tschüss')

# Hauptprogramm...

print('Karte!')
print('Exit mit Strg-C')
do_read_uid()
Erläuterungen zum Programm:

Nachdem die benötigten Klassen importiert worden sind, wird ein spi-Objekt instanziiert; dabei greifen wir nicht auf die Hardware-SPI zurück, sondern auf eine Software-SPI. Auf diese Weise wird es uns später möglich sein, das Display mit der Hardware-SPI anzusteuern.

Im Hauptprogramm wird man zunächst aufgefordert, eine Karte in die Nähe des RFID-Moduls zu halten. Sodann wird mitgeteilt, dass das fortwährende Auslesen der UID mit Strg-C abgebrochen werden kann.

Die Funktion do_read_uid() besteht aus einer Endlos-Schleife, in welcher die UID fortwährend von der Kartte gelesen und am Terminal ausgegeben wird. Diese Schleife befindet sich innerhalb einer try ... except - Konstruktion; damit kann durch die Eingabe von Strg-C ein Interrupt ausgelöst werden, welcher zum Abbruch der Schleife führt.

Was geschieht nun innerhalb der Schleife? Zunächst wird eine Instanz rdr der Klasse MFRC522 erzeugt. Neben dem spi-Objekt muss hier als weiterer Parameter noch der Pin für sda (Chip Select) angegeben werden. Nun wird mit der rdr-Methode request() eine Abfrage bei der Karte durchgeführt; der Parameter rdr.REQIDL beschreibt, wonach gefragt wird. Als Rückgabewert erhält man das Tupel (stat, tag_type). Uns interessiert hier nur der Status-Wert: Ist stat = 0, dann liegt kein Fehler vor, d. h. es wurde eine Karte detektiert. Nun wird im nächsten Schritt kontrolliert, ob stat den Wert rdr.OK (= 0) hat. Ist dies der Fall, dann versucht die Methode rdr.anticoll(), die UID zu ermitteln. Die Bezeichnung für diese Methode erscheint auf den ersten Blick merkwürdig zu sein. In der Tat wird mit dieser Methode zunächst kontrolliert, ob sich nicht zwei oder sogar noch mehr Karten im Empfangsbereich des RFID-Moduls befinden. In diesem Fall kommt es zu einer so genannten Collision. Nur wenn keine Collision vorliegt, wird als Wert für stat der Wert rdr.OK übergeben und raw_uid erhält die gewünschten Informationen über die UID. Genauer gesagt besteht raw_uid aus einer Liste mit 5 Elementen; die ersten 4 Elemente geben die einzelnen Bytes der UID an. Diese werden nun zu einer entsprechenden Zeichenkette umgewandelt, welche mit einer print-Funktion ausgegeben wird.

Austesten des Programms:

Starten Sie nun das Programm und halten Sie dann eine Karte in die Nähe des RFID-Moduls. Entfernen Sie die Karte und halten Sie eine andere Karte vor das RFID-Modul. Beobachten Sie dabei die Ausgaben auf dem Bildschirm.

Halten Sie nun das Programm an und stellen Sie jetzt zwei Karten vor das RFID-Modul. Wenn Sie nun das Programm starten, wird keine UID angezeigt. Jetzt findet eine Collision statt. Entfernen wir eine der beiden Karte, wird wieder eine UID ausgegeben.

Testen Sie auch einmal aus, bis zu welchem Abstand die UID ausgelesen werden kann.


3. Eine kleine Anwendung (Zugangskontrolle)

Kommen wir nun zu einer einfachen Anwendung: eine Zugangskontrolle. Hierbei soll eine grüne LED für 3 Sekunden aufleuchten, wenn eine RFID-Karte mit einer bekannten UID an das RFID-Modul gehalten wird; andernfalls soll eine rote LED für 3 Sekunden aufleuchten. Eine UID soll dabei als “bekannt” gelten, wenn sie in einer entsprechenden Liste “bekannte” auftaucht.

aufbau_access.jpg
Abb. 3: Aufbau für Zugangskontrolle
aufbau_access.jpg (152.78 KiB) 250 mal betrachtet

Zunächst schließen wir an den TTGO die beiden LEDs an, die grüne an Pin 27, die rote an Pin 26. Wir gehen von unserem bisherigen Programm aus und erweitern es um einige Zeilen für die Zugangskontrolle. Das Programm sieht dann z. B. so aus:

Code: Alles auswählen

# Initialisierungen...

from time import sleep_ms
from machine import Pin, SoftSPI
from mfrc522 import MFRC522

sck = Pin(17, Pin.OUT)
mosi = Pin(15, Pin.OUT)
miso = Pin(12, Pin.OUT)
spi = SoftSPI(baudrate=100000, polarity=0, phase=0, sck=sck, mosi=mosi, miso=miso) 

sda = Pin(25, Pin.OUT)

gruene_LED = Pin(27, Pin.OUT)
rote_LED = Pin(26, Pin.OUT)

bekannte = ['0x8604fa29', '0x7698a629', '0x05e24f28'] # Liste mit den “bekannten UIDs”

# Funktionen...

def access(uid, bekannte):
    if uid in bekannte: # falls UID bekannt...
        print('Zugang!')
        gruene_LED.value(1) # gruene LED einschalten
    else: # sonst ...
        print('Kein Zugang')
        rote_LED.value(1) # rote LED einschalten
    print('Karte entfernen!')    
    sleep_ms(3000)
    print('Warte auf neue Karte')
    print()
    gruene_LED.value(0) # LEDs ausschalten
    rote_LED.value(0)

def do_read_uid():
    try:
        while True:
            rdr = MFRC522(spi, sda)
            uid = ''
            (stat, tag_type) = rdr.request(rdr.REQIDL)
            if stat == rdr.OK:
                (stat, raw_uid) = rdr.anticoll()
                if stat == rdr.OK:
                    uid = ("0x%02x%02x%02x%02x" % (raw_uid[0], raw_uid[1], raw_uid[2], raw_uid[3]))
                    print('uid: ', uid)
                    access(uid, bekannte) # Zugangskontrolle durchführen
    except KeyboardInterrupt:
        print('Tschüss')

# Hauptprogramm...

print('Warte auf Karte!')
print('Exit mit Strg-C')
print()
do_read_uid()
Das Programm befindet sich ebenfalls in dem Anhang rfid1.zip. Die neuen Programmteile sind ausführlich kommentiert und sollen hier deswegen nicht weiter erläutert werden.

Unser Programm stellt nur eine sehr rudimentäre Zugangskontrolle dar. So kann jeder, der die Karte findet, sich einen Zugang verschaffen. In einem weiteren Beitrag soll dargelegt werden, wie man die Zugangskontrolle in dieser Hinsicht verbessern kann.

.
Dateianhänge
rfid1.zip
Das Modul mfcr522.py und die beiden hier behandelten Programme
(3.1 KiB) 43-mal heruntergeladen

Antworten