|
| 1 | +--- |
| 2 | +title: RFID |
| 3 | +tags: ["spi"] |
| 4 | +--- |
| 5 | + |
| 6 | +## Funktionsweise |
| 7 | + |
| 8 | +Bei RFID, abgekürzt für Radio Frequency Identification, handelt es sich um eine Technologie, welche es ermöglicht automatisch und kontaktlos |
| 9 | +mit einer Karte entsprechende Informationen auszutauschen. Dies wird zum Beispiel bei weit bekannten NFC Tags verwendet, jedoch auch für |
| 10 | +Anwendungen wie das kontaktlose Zahlen per Kreditkarte. |
| 11 | + |
| 12 | +Ein grosser Vorteil ist hierbei, dass die jeweiligen Karten/Tags, auch Transponder genannt, so klein wie ein Reiskorn sein können und keine |
| 13 | +eigene Stromversorgung benötigen. Im CrowPi ist die Komponente `MFRC522` verbaut, welche ein hochfrequentes elektronisches Wechselfeld bei |
| 14 | +13.56 MHz erzeugt, um damit einerseits die Karte mit Strom zu versorgen und andererseits mit dieser zu kommunizieren. Somit können |
| 15 | +beispielsweise die beiden beigelegten Tags des CrowPi ausgelesen und beschrieben werden. Dies funktioniert dabei mit beliebigen Daten. |
| 16 | + |
| 17 | +Die Technologie dahinter ist sehr komplex und die entsprechenden Informationen dazu sind auf mehrere Standards aufgeteilt, hauptsächlich |
| 18 | +ISO-14443 für den allgemeinen Aufbau und die Kommunikation sowie die entsprechenden Spezifikationen von NXP Semiconductors, dem Hersteller |
| 19 | +des Lese- und Schreibgeräts sowie von den meisten RFID Karten. Die Einhaltung dieser Standards ist hierbei sehr wichtig, da eine RFID Karte |
| 20 | +bei falschen Schreiboperationen auch permanent beschädigt werden kann. Im Fachjargon wird das Lese- und Schreibgerät "Proximity Coupling |
| 21 | +Device" (PCD) genannt, während die verschiedenen Karten "Proximity Integrated Circuit Card" (PICC) heissen. |
| 22 | + |
| 23 | +Es besteht hier jedoch kein Grund zur Sorge, da sich diese Komponente um all die komplexen Aufgaben im Hintergrund kümmert und eine einfache |
| 24 | +sowie zuverlässige Schnittstelle bietet, um mit solchen RFID Karten zu interagieren. So bietet `RfidComponent` die Möglichkeit an, Karten zu |
| 25 | +erkennen und ein beliebiges Java-Objekt darauf zu speichern oder wieder zu lesen. |
| 26 | + |
| 27 | +## Voraussetzungen |
| 28 | + |
| 29 | +### DIP Switches |
| 30 | + |
| 31 | +Für diese Komponente werden keine spezifischen DIP-Switches benötigt, sodass diese in der Standardkonfiguration belassen werden können: |
| 32 | + |
| 33 | +{{< dip-switches >}} |
| 34 | + |
| 35 | +## Verwendung |
| 36 | + |
| 37 | +Nachfolgend wird die Verwendung der Klasse {{< javadoc class="com.pi4j.crowpi.components.RfidComponent" >}} beschrieben. |
| 38 | + |
| 39 | +### Konstruktoren |
| 40 | + |
| 41 | +| Konstruktor | Bemerkung | |
| 42 | +|:--------------------------------------------------------------------------------------------------|:-----------------------------------------------------------------------------------------------------------------------| |
| 43 | +| `RfidComponent(com.pi4j.context.Context pi4j)` | Initialisiert einen RFID Reader/Writer mit der Standard Reset-Pin und SPI Adresse sowie SPI Bandbreite für den CrowPi. | |
| 44 | +| `RfidComponent(com.pi4j.context.Context pi4j, int spiChannel, int spiBaud)` | Initialisiert einen RFID Reader/Writer mit einer benutzerdefinierten SPI Adresse und Bandbreite ohne Reset-Pin. | |
| 45 | +| `RfidComponent(com.pi4j.context.Context pi4j, Integer gpioResetPin, int spiChannel, int spiBaud)` | Initialisiert einen RFID Reader/Writer mit einem benutzerdefinierten Reset-Pin, SPI-Adresse sowie Bandbreite. | |
| 46 | + |
| 47 | +### Methoden |
| 48 | + |
| 49 | +| Methode | Bemerkung | |
| 50 | +|:------------------------------------------------------|:--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| |
| 51 | +| `boolean isNewCardPresent()` | Gibt einen boolschen Wert zurück, ob eine neue Karte erkannt wurde, welche noch nicht gelesen wurde. | |
| 52 | +| `boolean isAnyCardPresent()` | Gibt einen boolschen Wert zurück, ob irgendeine Karte erkannt wurde. Diese Methode erkennt im Gegensatz zu `isNewCardPresent()` auch bereits gelesene Karten. | |
| 53 | +| `RfidCard initializeCard()` | Erzeugt eine neue Instanz von `RfidCard`, welche anschliessend zur Interaktion mit der Karte verwendet werden kann. Erfordert dass `isNewCardPresent` oder `isAnyCardPresent` vorher aufgerufen wurde und `true` zurückgab. | |
| 54 | +| `void uninitializeCard()` | Muss zwingend nach dem Abschliessen aller Aktionen mit einer Karte aufgerufen werden, um die Kommunikation mit der Karte sauber zu beenden und neue Karten erkennen zu können. | |
| 55 | +| `void onCardDetected(EventHandler<RfidCard> handler)` | Definiert einen Event Handler, welcher jedes Mal aufgerufen wird wenn eine neue Karte erkennt wird. Als erster Parameter wird hierbei die erkannte Karte übergeben. | |
| 56 | +| `void waitForNewCard(EventHandler<RfidCard> handler)` | Gleiche Funktionsweise wie `onCardDetected`, blockiert jedoch den aktuellen Thread und wartet bis eine neue Karte erkannt wird, um dann einmalig einen Handler aufzurufen. Diese Methode deaktiviert den aktuellen Event Listener für `onCardDetected`. | |
| 57 | +| `void waitForAnyCard(EventHandler<RfidCard> handler)` | Gleiche Funktionsweise wie `waitForNewCard`, akzeptiert jedoch auch eine Karte welche bereits gelesen oder beschrieben wurde und führt auf dieser die Aktion durch. Diese Methode deaktiviert den aktuellen Event Listener für `onCardDetected`. | |
| 58 | +| `void reset()` | Setzt die Komponente auf den Ausgangszustand zurück, was bei allfälligen Problemen Abhilfe schaffen kann. Normalerweise nicht erforderlich für den normalen Betrieb. | |
| 59 | + |
| 60 | +### Karten-Methoden |
| 61 | + |
| 62 | +Die Klasse {{< javadoc class="com.pi4j.crowpi.components.internal.rfid.RfidCard" >}} welche als Rückgabewert bei `initializeCard()` oder als |
| 63 | +Parameter bei den Event Handlern `onCardDetected()`, `waitForNewCard()` sowie `waitForAnyCard` verwendet wird, bietet verschiedene Methoden |
| 64 | +um mit der erkannten Karte zu interagieren. Wichtig zu beachten ist, dass nach Ausführung eines Event Handlers automatisch |
| 65 | +`uninitializeCard()` aufgerufen wird, sodass sich die erkannte Karte nicht weiter nutzen lässt. |
| 66 | + |
| 67 | +| Methode | Bemerkung | |
| 68 | +|:--------------------------------|:--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| |
| 69 | +| `String getSerial()` | Gibt die Seriennummer der aktuellen Karte als String zurück. | |
| 70 | +| `int getCapacity()` | Gibt die maximale Kapazität der aktuellen Karte in Bytes zurück. | |
| 71 | +| `void writeObject(Object data)` | Schreibt das übergebene Objekt in die Karte. Wird dieser Befehl mehrfach aufgerufen, so wird dieses jeweils überschrieben. Das Objekt muss das Interface `Serializable` implementieren. Wirft eine `RfidException` falls die Operation nicht erfolgreich war. | |
| 72 | +| `Object readObject()` | Liest das auf der Karte gespeicherte Objekt aus und gibt dieses als generischen `Object`-Typ zurück. Wirft eine `RfidException` falls die Operation nicht erfolgreich war. | |
| 73 | +| `T readObject(Class<T> type)` | Gleiche Funktionsweise wie `readObject()`, aber gibt das Objekt gleich mit dem gewünschten Typ zurück. Wurde zum Beispiel mit `writeObject` vorher ein `Animal` gespeichert, so gibt `readObject(Animal.class)` direkt die `Animal`-Instanz zurück. | |
| 74 | + |
| 75 | +## Beispielapplikation |
| 76 | + |
| 77 | +Die nachfolgende Beispielapplikation definiert eine Klasse namens `Person`, um von einer beliebigen Person den Vornamen und Nachnamen, die |
| 78 | +Adresse sowie das Geburtsdatum zusammen mit einer zufällig generierten ID zu speichern. Nun werden zuerst zwei Personen generiert und in den |
| 79 | +Variablen `personA` sowie `personB` gespeichert, welche beide über verschiedene Daten verfügen. |
| 80 | + |
| 81 | +Anschliessend wird mit der `waitForNewCard` Methode die Anwendung so lange blockiert, bis eine neue Karte vom RFID Gerät erkannt wurde. |
| 82 | +Sobald dies der Fall ist, wird die übergebene Funktion ausgeführt, welche die Instanz `personA` von der `Person`-Klasse auf der Karte mit |
| 83 | +`writeObject` speichert. Es kann jedes beliebige Java-Objekt gespeichert werden, solange dieses das `Serializable` mit `implements |
| 84 | +Serializable` implementiert. Wichtig ist hierbei zu beachten, dass diese Methode eine `RfidException` werfen kann, welche abgefangen werden |
| 85 | +muss. Diese tritt auf, wenn die Karte nicht ordnungsgemäss beschrieben werden konnte. |
| 86 | + |
| 87 | +Nachdem die erste Karte beschrieben wurde, wird der gleiche Prozess für die zweite Karte wiederholt. Da mit `waitForNewCard` grundsätzlich |
| 88 | +nur neue Karten erkannt werden und eine Karte nach erfolgter Interaktion in einen Schlafzustand geht, kann hier garantiert werden dass nicht |
| 89 | +sofort die zweite Person auf die gleiche Karte geschrieben wird, sondern sich eine neue Karte annähern oder die bestehende Karte kurzfristig |
| 90 | +entfernt werden muss. |
| 91 | + |
| 92 | +Sobald beide Karten beschrieben wurden, wird ein Event Handler mit `onCardDetected` registriert, welcher asynchron jedes Mal aufgerufen wird |
| 93 | +wenn eine neue Karte erkannt wurde. Da der RFID-Standard ein Verfahren gegen Kollisionen besitzt, können sich sogar mehrere Karten |
| 94 | +gleichzeitig auf dem Lese- und Schreibgerät befinden. Bei jeder ermittelten Karte wird mit `readObject(Person.class)` versucht, eine vorher |
| 95 | +gespeicherte Instanz der `Person`-Klasse auszulesen und in die Variable `person` zu speichern. Wenn dies gelingt, so wird die Person auf der |
| 96 | +Konsole ausgegeben. Auch hier muss eine allfällige `RfidException` aufgefangen werden, welche zum Beispiel auftritt, wenn die Daten nicht |
| 97 | +gelesen werden können oder korrupt sind. |
| 98 | + |
| 99 | +Nach der Registrierung des Event Handlers schläft die Applikation für 30 Sekunden, um genug Zeit zu geben, die zwei verwendeten Karten |
| 100 | +auszuprobieren. Nach Ablauf der Zeit wird der Event Handler wieder sauber entfernt und die Applikation beendet sich. |
| 101 | + |
| 102 | +{{< code file="src/main/java/com/pi4j/crowpi/applications/RfidApp.java" language="java" >}} |
| 103 | + |
| 104 | +## Weitere Möglichkeiten |
| 105 | + |
| 106 | +- Das Beispiel zu einer Art Zutrittskontrolle ausbauen und beispielsweise nur Personen mit einem bestimmten Attribut zulassen. |
| 107 | + |
| 108 | +- Statt dem Speichern von Personen könnten auch andere Daten auf einer Karte abgelegt werden, zum Beispiel eine (nicht sehr sichere) |
| 109 | + Implementation einer Bank wo auf jeder Karte der Inhaber sowie der aktuelle Kontostand gespeichert wird. Hierfür wäre die Methode |
| 110 | + `waitForAnyCard` praktisch, um eine bereits aufgelegte Karte erneut zu beschreiben. |
| 111 | + |
| 112 | + |
| 113 | +## Zusätzliche Links |
| 114 | +- [NXP Semiconductors: ISO/IEC 14443 PICC Selection](https://www.nxp.com/docs/en/application-note/AN10834.pdf) |
| 115 | +- [NXP Semiconductors: Spezifikation zu MFRC522](https://www.nxp.com/docs/en/data-sheet/MFRC522.pdf) |
| 116 | +- [NXP Semiconductors: Spezifikation zu MIFARE Classic 1K](https://www.nxp.com/docs/en/data-sheet/MF1S50YYX_V1.pdf) |
0 commit comments