In den folgenden Experimenten werden passive NFC-Transmitter mit den RFID-Modulen RC522 und PN532 und einem Arduino ausgelesen und steuern eine programmierte Schaltung.
RFID (radio-frequency identification) funktioniert mittels Radiowellen. Es gibt ein breites Spektrum an Chips und Lesegeräten, die sich hauptsächlich in Speicherkapazität, Herstellverfahren, Kosten, Frequenzbereich und in ihrer Reichweite unterscheiden.
NFC (= "near field communication" oder "Nahfeldkopplung") basiert auf der RFID Technologie und zeichnet sich
durch ein spezielles Kopplungsverfahren aus, dass in einem Standard genormt ist (ISO 14443, 18092, 21481).
Dabei ist neben dem Kopplungsverfahren auch der Frequenzbereich festgelegt. (135 kHz; 13,56 MHz nach ISO 18000-2, -3; 22536).
Bei NFC beträgt die normierte Frequenz 107,7 MHz.
Im Hobbybereich werden häufig die beiden Standards "EM4100" für den Langwellen-Bereich und "Mifare classic" für den Kurzwellen-Bereich verwendet.
Als PICC (engl. "proximity integrated circuit card") wird eine kontaktlose Chipkarte
(hier mit RFID-Technologie) bezeichnet, die in unmittelbarer Nähe eines Kartenlesegerätes
beispielsweise für Zahlungszwecke benutzt werden kann.
Token, (Chip)coins oder (Chip-)Marken sind mehr oder weniger münzenförmige Transponder, die
oft auch als Schlüsselanhänger angeboten werden.
Der folgende Schaltungsaufbau bezieht sich auf beide der folgenden Experimente, d.h. für das erste Experiment können die LEDs mit ihren Vorwiderständen weggelassen werden.
Die Pin-Belegung auf dem Arduino Uno ist für das SPI-Protokoll festgelegt und muss evtl. angepasst werden, wenn ein anderer Mikrocontroller verwendet wird.
RC522 | Arduino |
---|---|
GND | GND |
3,3V | 3,3V |
SDA (SS) | Pin 10 |
SCK | Pin 13 |
MOSI | Pin 11 |
MISO | Pin 12 |
RST | Pin 9 |
In diesem und den folgenden Experimenten wird die Library
miguelbalboa/rfid
verwendet.
Um die Funktionalität des RFID-Lesegerätes und des oder der
Transponder zu testen, bietet sich an das Beispiel
DumpInfo
dieser Library zu verwenden, welches mitgeliefert wird.
Im diesem Versuch lesen wir die ID eines erkannten NFC-Tags aus und zeigen diese in der seriellen Konsole an.
#include <SPI.h>
#include <MFRC522.h>
#define PIN_RESET 9 // SPI Reset Pin
#define PIN_SS 10 // SPI Slave Select Pin
MFRC522 mfrc522(PIN_SS, PIN_RESET);
void setup()
{
Serial.begin(9600);
SPI.begin();
mfrc522.PCD_Init();
}
void loop()
{
if (mfrc522.PICC_IsNewCardPresent() && mfrc522.PICC_ReadCardSerial()) {
Serial.print("Detected UID:");
for (byte i = 0; i < mfrc522.uid.size; i++) {
Serial.print(mfrc522.uid.uidByte[i] < 0x10 ? " 0x0" : " 0x");
Serial.print(mfrc522.uid.uidByte[i], HEX);
}
Serial.println();
mfrc522.PICC_HaltA();
delay(1000);
}
}
Mit Hilfe der oben verwendeten Library kann man auch die auf dem Transponder eingestellte UID verändern. In diesem Beispiel geht das nur mit einem MIFARE-kompatiblen Transponder, auf dem die UID überschreibbar ist.
#include <SPI.h>
#include <MFRC522.h>
#define RST_PIN 9
#define SS_PIN 10
// New customizable UID for the transponder:
const byte newUid[4] = {0xDE, 0xAD, 0xBE, 0xEF};
MFRC522 mfrc522(SS_PIN, RST_PIN);
MFRC522::MIFARE_Key key;
void setup()
{
Serial.begin(9600);
SPI.begin();
mfrc522.PCD_Init();
// Prepare key with factory settings (FFFFFFFFFFFFh)
for (byte i=0; i<6; i++) {
key.keyByte[i] = 0xFF;
}
}
void loop()
{
if (!mfrc522.PICC_IsNewCardPresent() || !mfrc522.PICC_ReadCardSerial()) {
delay(500);
return;
}
// Reading and printing current UID
Serial.print("Currrent UID: ");
for (byte i = 0; i < mfrc522.uid.size; i++) {
Serial.print(mfrc522.uid.uidByte[i] < 0x10 ? " 0x0" : " 0x");
Serial.print(mfrc522.uid.uidByte[i], HEX);
}
Serial.println();
// Writing new UID to transponder
if (mfrc522.MIFARE_SetUid(newUid, (byte)4, true)) {
Serial.println("Wrote new UID");
}
delay(1000);
}
In diesem Versuch lassen wir in Abhängigkeit von zwei verschiedenen NFC-Tag jeweils eine anders farbige LED aufleuchten. Somit "erkennt" unsere Logik die richtigen NFC-Tags.
#include <SPI.h>
#include <MFRC522.h>
#define PIN_RESET 9 // SPI Reset Pin
#define PIN_SS 10 // SPI Slave Select Pin
#define PIN_LED_BLUE 6
#define PIN_LED_RED 7
MFRC522 mfrc522(PIN_SS, PIN_RESET);
bool isBlue, isRed;
// insert the IDs of your personal NFC tags
byte uidBlue[] = {0x16, 0xE0, 0xC1, 0x49};
byte uidRed[] = {0x3A, 0x56, 0xDA, 0x29};
void setup()
{
SPI.begin();
mfrc522.PCD_Init();
pinMode(PIN_LED_BLUE, OUTPUT);
pinMode(PIN_LED_RED, OUTPUT);
}
void loop()
{
// PICC = proximity integrated circuit card
if (mfrc522.PICC_IsNewCardPresent() && mfrc522.PICC_ReadCardSerial()) {
isBlue = true;
isRed = true;
for (byte i=0; i<4; i++) {
if (mfrc522.uid.uidByte[i] != uidBlue[i]) {
isBlue = false;
}
if (mfrc522.uid.uidByte[i] != uidRed[i]) {
isRed = false;
}
}
if (isBlue) {
digitalWrite(PIN_LED_BLUE, HIGH);
}
if (isRed) {
digitalWrite(PIN_LED_RED, HIGH);
}
mfrc522.PICC_HaltA();
delay(1000);
digitalWrite(PIN_LED_BLUE, LOW);
digitalWrite(PIN_LED_RED, LOW);
}
}
Dieses Experiment liest und schreibt Daten eines Transponders. Somit
können je nach Speichergröße des Transponders 512Byte, 1kByte oder
mehr Daten verarbeitet werden.
Hier wird am Besten das Beispiel
ReadAndWrite
verwendet.
Der folgende Sketch stammt von mschoeffler.de und setzt einen RDM630/RDM6300 RFID reader als Lesegerät voraus:
// (c) Michael Schoeffler 2018, https://www.mschoeffler.de
#include <SoftwareSerial.h>
const int BUFFER_SIZE = 14; // RFID DATA FRAME FORMAT: 1byte head (value: 2), 10byte data (2byte version + 8byte tag), 2byte checksum, 1byte tail (value: 3)
const int DATA_SIZE = 10; // 10byte data (2byte version + 8byte tag)
const int DATA_VERSION_SIZE = 2; // 2byte version (actual meaning of these two bytes may vary)
const int DATA_TAG_SIZE = 8; // 8byte tag
const int CHECKSUM_SIZE = 2; // 2byte checksum
SoftwareSerial ssrfid = SoftwareSerial(6,8);
uint8_t buffer[BUFFER_SIZE]; // used to store an incoming data frame
int buffer_index = 0;
void setup() {
Serial.begin(9600);
ssrfid.begin(9600);
ssrfid.listen();
Serial.println("INIT DONE");
}
void loop() {
if (ssrfid.available() > 0){
bool call_extract_tag = false;
int ssvalue = ssrfid.read(); // read
if (ssvalue == -1) { // no data was read
return;
}
if (ssvalue == 2) { // RDM630/RDM6300 found a tag => tag incoming
buffer_index = 0;
} else if (ssvalue == 3) { // tag has been fully transmitted
call_extract_tag = true; // extract tag at the end of the function call
}
if (buffer_index >= BUFFER_SIZE) { // checking for a buffer overflow (It's very unlikely that an buffer overflow comes up!)
Serial.println("Error: Buffer overflow detected!");
return;
}
buffer[buffer_index++] = ssvalue; // everything is alright => copy current value to buffer
if (call_extract_tag == true) {
if (buffer_index == BUFFER_SIZE) {
unsigned tag = extract_tag();
} else { // something is wrong... start again looking for preamble (value: 2)
buffer_index = 0;
return;
}
}
}
}
unsigned extract_tag() {
uint8_t msg_head = buffer[0];
uint8_t *msg_data = buffer + 1; // 10 byte => data contains 2byte version + 8byte tag
uint8_t *msg_data_version = msg_data;
uint8_t *msg_data_tag = msg_data + 2;
uint8_t *msg_checksum = buffer + 11; // 2 byte
uint8_t msg_tail = buffer[13];
// print message that was sent from RDM630/RDM6300
Serial.println("--------");
Serial.print("Message-Head: ");
Serial.println(msg_head);
Serial.println("Message-Data (HEX): ");
for (int i = 0; i < DATA_VERSION_SIZE; ++i) {
Serial.print(char(msg_data_version[i]));
}
Serial.println(" (version)");
for (int i = 0; i < DATA_TAG_SIZE; ++i) {
Serial.print(char(msg_data_tag[i]));
}
Serial.println(" (tag)");
Serial.print("Message-Checksum (HEX): ");
for (int i = 0; i < CHECKSUM_SIZE; ++i) {
Serial.print(char(msg_checksum[i]));
}
Serial.println("");
Serial.print("Message-Tail: ");
Serial.println(msg_tail);
Serial.println("--");
long tag = hexstr_to_value(msg_data_tag, DATA_TAG_SIZE);
Serial.print("Extracted Tag: ");
Serial.println(tag);
long checksum = 0;
for (int i = 0; i < DATA_SIZE; i+= CHECKSUM_SIZE) {
long val = hexstr_to_value(msg_data + i, CHECKSUM_SIZE);
checksum ^= val;
}
Serial.print("Extracted Checksum (HEX): ");
Serial.print(checksum, HEX);
if (checksum == hexstr_to_value(msg_checksum, CHECKSUM_SIZE)) { // compare calculated checksum to retrieved checksum
Serial.print(" (OK)"); // calculated checksum corresponds to transmitted checksum!
} else {
Serial.print(" (NOT OK)"); // checksums do not match
}
Serial.println("");
Serial.println("--------");
return tag;
}
long hexstr_to_value(char *str, unsigned int length) { // converts a hexadecimal value (encoded as ASCII string) to a numeric value
char* copy = malloc((sizeof(char) * length) + 1);
memcpy(copy, str, sizeof(char) * length);
copy[length] = '\0';
// the variable "copy" is a copy of the parameter "str". "copy" has an additional '\0' element to make sure that "str" is null-terminated.
long value = strtol(copy, NULL, 16); // strtol converts a null-terminated string to a long value
free(copy); // clean up
return value;
}
PN532 ist ein NFC-Controller von NXP, der auf dem 80C51-Mikrocontroller basiert und kontaktlose Kommunikation bei 13,56 MHz ermöglicht. Darüber hinaus ermöglicht die Unterstützung für MIFARE Classic 1K/MIFARE Classic 4K-Karten höhere Übertragungsgeschwindigkeiten von bis zu 424 kbit/s in beiden Richtungen.
Das PN532 verfügt mit SPI und I²C über zwei verschiedene Kommunikations-Schnittstellen, die mit switches
(I0
und I1
)auf dem Board eingestellt werden können:
I0 | I1 | |
---|---|---|
HSV | 0 | 0 |
I²C | 1 | 0 |
SPI | 0 | 1 |
Die Einstellung "HSV" steht für "High-Speed Variation" und ermöglicht es, die maximale
Übertragungsgeschwindigkeit auf bis zu 848 kBit/s zu erhöhen. Diese Einstellung ist nur für
NFCIP-1-Modus (Near Field Communication over the Internet Protocol) verfügbar und erfordert eine
entsprechende Anpassung der Antennenabstimmung und der Stromversorgung.
Hinweis: Im Folgenden wird nur die I²C-Schnittstelle verwendet.
PN532 | Arduino Uno |
---|---|
GND | GND |
VCC | 5V |
SDA | A4 |
SCL | A5 |
Für den folgenden Sketch wird die Library adafruit/Adafruit-PN532 verwendet, wobei der Beispiel-Sketch "readMifare" angepasst und vereinfacht wurde:
#include <Wire.h>
#include <Adafruit_PN532.h>
#define PN532_IRQ (2)
#define PN532_RESET (3) // Not connected by default on the NFC Shield
Adafruit_PN532 nfc(PN532_IRQ, PN532_RESET);
void setup()
{
Serial.begin(9600);
nfc.begin();
uint32_t versiondata = nfc.getFirmwareVersion();
if (! versiondata) {
Serial.print("Did not find PN53x board");
while (1);
}
Serial.print("Found chip PN5");
Serial.println((versiondata >> 24) & 0xFF, HEX);
Serial.print("Firmware ver. ");
Serial.print((versiondata >> 16) & 0xFF, DEC);
Serial.print('.');
Serial.println((versiondata >> 8) & 0xFF, DEC);
Serial.println("Waiting for an ISO14443A Card...");
}
void loop()
{
uint8_t success;
uint8_t uid[] = { 0, 0, 0, 0, 0, 0, 0 }; // Buffer to store the returned UID
uint8_t uidLength; // Length of the UID (4 or 7 bytes depending on ISO14443A card type)
success = nfc.readPassiveTargetID(PN532_MIFARE_ISO14443A, uid, &uidLength);
if (success) {
Serial.println("Found an ISO14443A card");
Serial.print(" UID Length: ");
Serial.print(uidLength, DEC);
Serial.println(" bytes");
Serial.print(" UID Value: ");
nfc.PrintHex(uid, uidLength);
Serial.println("");
if (uidLength == 4) {
// We probably have a Mifare Classic card ...
Serial.println("Seems to be a Mifare Classic card (4 byte UID)");
// Now we need to try to authenticate it for read/write access
// Try with the factory default KeyA: 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF
Serial.println("Trying to authenticate block 4 with default KEYA value");
uint8_t keya[6] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
// Start with block 4 (the first block of sector 1) since sector 0
// contains the manufacturer data and it's probably better just
// to leave it alone unless you know what you're doing
success = nfc.mifareclassic_AuthenticateBlock(uid, uidLength, 4, 0, keya);
if (success) {
Serial.println("Sector 1 (Blocks 4..7) has been authenticated");
uint8_t data[16];
success = nfc.mifareclassic_ReadDataBlock(4, data);
if (success) {
Serial.println("Reading Block 4:");
nfc.PrintHexChar(data, 16);
Serial.println("");
delay(1000);
} else {
Serial.println("Unable to read the requested block.");
}
} else {
Serial.println("Authentication failed.");
}
}
if (uidLength == 7) {
Serial.println("Seems to be a Mifare Ultralight tag (7 byte UID)");
Serial.println("Reading page 4");
uint8_t data[32];
success = nfc.mifareultralight_ReadPage (4, data);
if (success)
{
nfc.PrintHexChar(data, 4);
Serial.println("");
delay(1000);
} else {
Serial.println("Unable to read the requested page.");
}
}
}
}
Wenn das Board angeschlossen wurde und ein NFID-Tag in die Nähe des Boards gehalten wird, so sollte eine änhliche Ausgabe in der seriellen Konsole der Arduino-IDE erscheinen: