zurück

RFID und Arduino

08.07.2018

In den folgenden beiden Experimenten werden passive NFC-Transmitter mit dem RFID-Modul RC522 und dem Arduino auslesen und steuern eine programmierte Schaltung.

RFID-Modul RC522
Abb.: RFID-Modul RC522 mit gelieferten NFC-Tags (Schlüsselanhänger und Karte)

Funktionsweise von RFID

RFID

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.

Frequenzbereiche

NFC

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.

Standards

Im Hobbybereich werden häufig die beiden Standards "EM4100" für den Langwellen-Bereich und "Mifare classic" für den Kurzwellen-Bereich verwendet.

Verwendete Bauteile

Aufbau

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.

Anschlüsse des RC522
Abb.: Anschlüsse des RC522

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
Aufbau der Schaltung
Abb.: Aufbau der Schaltung

Ex. I: Auslesen der ID des NFC-Tags

Im ersten Versuch lesen wir die ID eines erkannten NFC-Tags aus und zeigen diese in der seriellen Konsole an. Im Sketch wird die Library MFRC522.h verwendet.

#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()
{
    // PICC = proximity integrated circuit card
    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 ? " 0" : " ");
        Serial.print(mfrc522.uid.uidByte[i], HEX);
    }
    Serial.println();

    mfrc522.PICC_HaltA();
    delay(1000);
    }
}
Ausgabe des Resultats in der seriellen Konsole
Abb.: Ausgabe des Resultats in der seriellen Konsole

Ex. II: Steuerung einer Schaltung durch NFC-Tags

Im zweiten 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);
    }
}

Auslesen der Daten mit einem RDM630/RDM6300 RFID reader

Der folgende Sketch stammt von mschoeffler.de und setzt einen RDM630/RDM6300 RFID reader als Lesegerät voraus:

// (c) Michael Schoeffler 2018, http://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;
}