Tutorial: RC522 RFID Module aansluiten op een Arduino

Leer hier hoe je met de RC522 RFID Module kunt lezen, schrijven én authenticeren! Heb je vragen? Neem contact op: wij helpen je graag op weg.

Deze module moet op een 3.3V voeding worden aangesloten. Ook de datasignalen (SPI en Reset) moeten idealiter van 3.3V niveau zijn. Hoewel de module ook met 5V signalen zonder problemen lijkt te werken is dat geheel op eigen risico. Sluit risico's uit en maak een goed ontwerp door gebruik te maken van een level converter.

De RC522 RFID module (koop hier in de webshop) moet met software worden aangestuurd om de 13.56MHz keyfobs te kunnen lezen en schrijven.

De reset pin (RST) is laag-actief, dat betekend dat de module gereset wordt als deze pin aan de 3.3V wordt aangesloten. Aangezien de module erg robuust is, en een zeer goede interne foute afhandeling heeft, heb je deze pin waarschijnlijk nooit nodig. Het is echter altijd handig om toch de module te kunnen resetten als het via software niet opgelost kan worden.

In totaal zijn er 5 signalen nodig om de RC522 aan te sturen, namelijk: RST, MISO, MOSI, SCK en SS. Als je gebruikt maakt van een 4-kanaals level converter dan denk je dat je 1 kanaal te kort komt. Echter kan het MISO (Master Input Slave Output) direct op de 5V Arduino aangesloten worden. Het MISO datasignaal komt namelijk van de RC522 af, en heeft een signaalniveau van 3.3V. Voor de Arduino is een 3.3V signaal hoog genoeg om als logisch hoog te worden herkend.

  • Pin 3.3V aansluiten op een 3.3V voeding of de 3.3V output van de Arduino
  • Pin GND aansluiten aan de massa van het systeem
  • Pin RST (Reset, Active Low) via een level shifter aansluiten op pin 9 van de Arduino
  • Pin IRQ (Interrupt) wordt in dit voorbeeld niet gebruikt
  • Pin MISO direct op pin 12 van de Arduino
  • Pin MOSI aansluiten via een level shifter op pin 11 van de Arduino
  • Pin SCK (Serial Clock) aansluiten via een level shifter op pin 13 van de Arduino
  • Pin SDA (SPI Slave Select, SS) aansluiten via een level shifter op pin 10 van de Arduino

De datasignalen SS en RST kun je ook op een andere digitale poort zetten als dat beter uitkomt. De overige datasignalen moeten op de genoemde pinnen van de Arduino blijven zitten, aangezien die in de SPI Library zijn vastgelegd.

De opbouw van data in een MIFARE Classic keyfob

Nu je alles hebt aangesloten kun je bijna beginnen aan het programmeren. Ben je nog niet bekend met de datastructuur van een MIFARE keyfob? Lees dan deze sectie even door zodat je weet hoe de interne datastructuur er uit ziet. In dit voorbeeld ga ik uit van een standaard MIFARE Classic 1KB keyfob (of kaart).

  • Sector 0 .. 15
  • Block 0 .. 63
  • Data - 16 bytes per blok
  • Blok #0 - Manufacturer Blok
  • 6 Access bytes in het Trailing Blok van elke sector. Standaard 0xFFFFFFFFFFFF.

De datastructuur bestaat uit 16 sectoren, waarbij elke sector bestaat uit 4 blokken, en elke blok 16 bytes aan data kan vasthouden. Hierbij komen we uit op 16 x 4 x 16 = 1024 bytes, precies 1KB. Maar.. niet alle blokken kunnen gebruikt worden voor het wegschrijven van data.. helaas. Effectief gezien kun je maximaal 752 bytes gebruiken voor opslag.

  • Blok 0 (sector #0, blok 0) is het Manufacturer Blok, en dit blok mag nooit overschreven worden. Zekerheidshalve sla je Blok 0 het liefst in zijn geheel over.
  • Elk laatste blok van een sector is een Trailing Blok en bevat informatie (access bits, een soort wachtwoord) voor het lezen en schrijven naar de overige 3 blokken binnen deze sector. Het gaat dus om de blokken 3, 7, 11, 15, etc.., 63.

Ook is er in de software geen sprake van sectoren, maar alleen van blokken. Wil je bijvoorbeeld blok 0 van sector 2 benaderen, dan zul je in de software blok 8 moeten aanroepen.

RC522 Arduino Software - Lezen & Schrijven

Voordat de software geschreven kan worden moet je eerst onderstaande Arduino Library downloaden.

Download Arduino Library <MFRC522.h>

Met onderstaande code kun je:

  • Lezen: De keyfob (ook wel PICC, Proximity Integrated Circuit Card, genoemd) uitlezen
  • Schrijven: Naar de keyfob schrijven
  • Authenticeren: De keyfob uitlezen en dit vergelijken met een stukje data
Blok 0 in Sector 0 is het zogenaamde Manufacturer Block. Daarin staat informatie zoals het uniek ID van de keyfob. Overschrijf dit blok nooit, aangezien de keyfob daardoor onbruikbaar kan worden. Zekerheidshalve is het advies om Sector 0 in zijn geheel te vermijden.

Hieronder zijn de stappen weergegeven om te lezen, schrijven en authenticeren via de RC522 module:

  1. Nadat de kaart is geïdentificeerd wordt er wat basis informatie weergegeven.
  2. Controleer of deze kaart compitabel is met deze software
  3. Bereid de software voor op het schrijven en lezen van data
  4. Toegang krijgen tot de (beveiligde) sector van de kaart (Sector #1)
  5. Geef de data van Sector #1 (Blok 4 t/m Blok 7) weer op het scherm
  6. Schrijf data naar Blok 4 (Sector #1, Blok #1)
  7. Lees de data van Blok 4 uit, en controleer of deze overeenkomt met de geschreven data (zie vorige stap)
  8. De verbinding met de kaart sluiten, zodat een volgende kaart gelezen kan worden
#include <SPI.h>
#include <MFRC522.h>

#define ResetPin 9
#define SlaveSelectPin 10
MFRC522 mfrc522(SlaveSelectPin, ResetPin); // Create MFRC522 instance.
MFRC522::MIFARE_Key key; // Crytpokey for accessing blocks

void setup() {
  Serial.begin(9600); // Initialize serial communications with the PC
  while (!Serial);    // Do nothing if no serial port is opened (added for Arduinos based on ATMEGA32U4)
  SPI.begin();        // Init SPI bus
  mfrc522.PCD_Init(); // Init MFRC522 Module

  // Set default Crypto key for the trailer block (factory default)
  for (byte i = 0; i < 6; i++) {
    key.keyByte[i] = 0xFF;
  }

  Serial.println(F("WARNING: Data will be written to the PICC, in sector #1"));
}

void loop() {
  // Reset the loop if no new card is presented
  if (!mfrc522.PICC_IsNewCardPresent()) return;

  // Select one of the cards
  if (!mfrc522.PICC_ReadCardSerial()) return;

  /*** [1] Show some basic information of the presented card ***/
  Serial.print(F("Card UID:")); dump_byte_array(mfrc522.uid.uidByte, mfrc522.uid.size); 
  Serial.println();
  
  Serial.print(F("PICC type: ")); MFRC522::PICC_Type piccType = mfrc522.PICC_GetType(mfrc522.uid.sak);
  Serial.println(mfrc522.PICC_GetTypeName(piccType));


  /*** [2] Check if this card is compatible with this software ***/
  if (    piccType != MFRC522::PICC_TYPE_MIFARE_MINI
          &&  piccType != MFRC522::PICC_TYPE_MIFARE_1K
          &&  piccType != MFRC522::PICC_TYPE_MIFARE_4K) {
    Serial.println(F("This sample only works with MIFARE Classic cards."));
    return;
  }

  /*** [3] Preparing data for the PICC ***/
  // To avoid damage to the PICC never write data to Sector #0
  // In this example we will write data to Sector #1
  // Covering Block #4 up to and including Block #7
  byte sector = 1;
  byte blockAddr = 4; // Sector #1, Block #0
  byte dataBlock[]  = {
    0xE5, 0x02, 0x03, 0x04, //  1,  2,   3,  4,
    0x05, 0x06, 0x07, 0x08, //  5,  6,   7,  8,
    0x09, 0x0a, 0xff, 0x0b, //  9, 10, 255, 11,
    0x0c, 0x0d, 0x0e, 0x0f  // 12, 13, 14, 15
  };
  byte trailerBlock = 7; // Last block in Sector #1
  MFRC522::StatusCode status;
  byte buffer[18];
  byte size = sizeof(buffer);

  /*** [4] Accessing Sector #1 ***/
  Serial.println(F("Accessing Sector #1 ..."));
  status = (MFRC522::StatusCode) mfrc522.PCD_Authenticate(MFRC522::PICC_CMD_MF_AUTH_KEY_A, trailerBlock, &key, &(mfrc522.uid));
  if (status != MFRC522::STATUS_OK) {
    Serial.print(F("PCD_Authenticate() failed: "));
    Serial.println(mfrc522.GetStatusCodeName(status));
    return;
  }

  /*** [5] Reading data from Sector #1 ***/
  // Print the data in the sector to the Serial Monitor
  Serial.println(F("Current data in sector:"));
  mfrc522.PICC_DumpMifareClassicSectorToSerial(&(mfrc522.uid), &key, sector);
  Serial.println();

  // Read data from the block we are interested in
  Serial.print(F("Reading data from block ")); Serial.print(blockAddr);
  Serial.println(F(" ..."));
  status = (MFRC522::StatusCode) mfrc522.MIFARE_Read(blockAddr, buffer, &size);
  if (status != MFRC522::STATUS_OK) {
    Serial.print(F("MIFARE_Read() failed: "));
    Serial.println(mfrc522.GetStatusCodeName(status));
  }

  // Show data present in block 4 (Sector #1, Block #0)
  Serial.print(F("Data in block ")); Serial.print(blockAddr); Serial.println(F(":"));
  dump_byte_array(buffer, 16); Serial.println();
  Serial.println();



  /*** [6] Write data to block 4 (Sector #1, Block #0) ***/
  Serial.print(F("Writing data into block ")); Serial.print(blockAddr);
  Serial.println(F(" ..."));
  dump_byte_array(dataBlock, 16); Serial.println();
  status = (MFRC522::StatusCode) mfrc522.MIFARE_Write(blockAddr, dataBlock, 16);
  if (status != MFRC522::STATUS_OK) {
    Serial.print(F("MIFARE_Write() failed: "));
    Serial.println(mfrc522.GetStatusCodeName(status));
  }
  Serial.println();

  /*** [7] Read data from block 4 (Sector #1, Block #0) ***/
  Serial.print(F("Reading data from block ")); Serial.print(blockAddr);
  Serial.println(F(" ..."));
  status = (MFRC522::StatusCode) mfrc522.MIFARE_Read(blockAddr, buffer, &size);
  if (status != MFRC522::STATUS_OK) {
    Serial.print(F("MIFARE_Read() failed: "));
    Serial.println(mfrc522.GetStatusCodeName(status));
  }
  Serial.print(F("Data in block ")); Serial.print(blockAddr); Serial.println(F(":"));
  dump_byte_array(buffer, 16); Serial.println();

  // Check that data in block is what we have written
  // by counting the number of bytes that are equal
  Serial.println(F("Checking result..."));
  byte count = 0;
  for (byte i = 0; i < 16; i++) {
    // Compare buffer (= what we've read) with dataBlock (= what we've written)
    if (buffer[i] == dataBlock[i])
      count++;
  }
  Serial.print(F("Number of bytes that match = ")); Serial.println(count);
  if (count == 16) {
    Serial.println(F("Success :-)"));
  } else {
    Serial.println(F("Failure, no match :-("));
    Serial.println(F("  perhaps the write didn't work properly..."));
  }
  Serial.println();

  // Dump the sector data
  Serial.println(F("Current data in sector:"));
  mfrc522.PICC_DumpMifareClassicSectorToSerial(&(mfrc522.uid), &key, sector);
  Serial.println();

  /*** [8] Close Connection ***/
  mfrc522.PICC_HaltA(); // Halt the PICC before stopping the encrypted session.
  mfrc522.PCD_StopCrypto1(); // Stop encryption on PCD
}

/*** Helper function to dump a byte array as hex values to Serial Monitor. ***/
void dump_byte_array(byte *buffer, byte bufferSize) {
  for (byte i = 0; i < bufferSize; i++) {
    Serial.print(buffer[i] < 0x10 ? " 0" : " ");
    Serial.print(buffer[i], HEX);
  }
}

Verder lezen

Deel deze handleiding