Arduino Uno + RFID-RC522: Reading MIFARE Ultralight Series Cards

Foreword

Because the laboratory has a long history and is all worked by hardware people, components, chips, modules, etc. are piled up in a mess in the laboratory. This obsessive-compulsive disorder patient is very uncomfortable, so I want to make a smart storage system, and I have a friend’s RC522 (the leftovers of the smart door lock) to try it directly. The results are generally from Fudan Card’s teachings, and I bought the NTAG series, and the code is always buggy. So I started to read the library files and data manuals, and finally completed the code for reading and writing NTAG series cards.

Hardware part

Arduino UNO + RC522 module + NTAG213 chip tag

Pictures of the hardware used

The RC522 pins are connected according to the instructions given in the MFRC522 library. The Arduino UNO is used, so you only need to look inside the red box.

Pin connection guidelines given in the sample code

Software part

Use Arduino IDE to write code, official website download address: Software | Arduino

Verify whether the tag on your hand is a MIFARE Ultralight type card

Open Arduino IDE (you may need to create a new file) and download the MFRC522 library

You can then find an example in the “File” option in the upper left corner, as shown below to open “DumpInfo”

Then connect the pin-connected RC522 and Arduino UNO to the computer, select the development board and interface (everyone’s interface is different, you can observe which Arduino port is by plugging and unplugging)

There is no problem with connection and port selection, just burn the sample code directly

If the wiring and ports are correct but the programming fails, the reason may be that the processor is not selected properly. You can refer to this blog: arduino nano programming error_arduino nano cannot be programmed-CSDN blog

Then press the shortcut key Ctrl + Shift + M or open the serial port monitor in the menu bar to view the output information.

If there are no burning errors, the monitor will pop up the following message to prove that the code burning is successful.

Then directly place the label to be scanned on the RC522, and the label information can be printed. The printed information in the figure below indicates that the card is a MIFARE Ultralight type card.

Card reading and writing code

The blogger is just a novice because he has never learned the system. The codes are all made from examples, and there is no analysis that can be used.

The writing part uses “rfid_write_personal_data” in the example, but always encounters the following situation

The code shows me that PCD_Authenticate() failed: Timeout in communication. (connection timeout) corresponds to this line of code.

 status = mfrc522.PCD_Authenticate(MFRC522::PICC_CMD_MF_AUTH_KEY_A, block, & amp;key, & amp;(mfrc522.uid));
  if (status != MFRC522::STATUS_OK) {
    Serial.print(F("PCD_Authenticate() failed: "));
    Serial.println(mfrc522.GetStatusCodeName(status));
    return;
  }
  else Serial.println(F("PCD_Authenticate() success: "));

Read the data manual (the sample code has a github link GitHub – miguelbalboa/rfid: Arduino RFID Library for MFRC522, download the compressed package and there is a pdf file inside)

I found that this part is only used for authentication of MIFARE Classic type cards. MIFARE Ultralight type cards do not need this part, so I commented out this part and tried it again. As a result, there was a problem with writing.

Description on page 15 of the data sheet

The error message shows A MIFARE PICC responded with NAK. I couldn’t find a good solution by searching online or looking through the data manual. It was the same when I tried the Fudan card, so I went back and looked at the source code and found that the problem was that the block was written in the wrong place.

 block = 1;
  // Write block
  status = mfrc522.MIFARE_Write(block, buffer, 16);
  if (status != MFRC522::STATUS_OK) {
    Serial.print(F("MIFARE_Write() failed: "));
    Serial.println(mfrc522.GetStatusCodeName(status));
    return;
  }
  else Serial.println(F("MIFARE_Write() success: "));

According to the design, the first 4 blocks cannot be written if they are not in the data area (I don’t know why the example is written this way. For memory, you can refer to this blog UltraLight card storage structure-CSDN blog). The modified code is as follows, and it is written perfectly.

The reading part also uses “rfid_read_personal_data” in the sample code. Commenting out the authentication part can still run normally.

So there was a big integration, and I only made the code to write one block. I still need to study it later to realize the writing of item data.

#include <SPI.h>
#include <MFRC522.h>

#define RST_PIN 9 // Configurable, see typical pin layout above
#define SS_PIN 10 // Configurable, see typical pin layout above

MFRC522 mfrc522(SS_PIN, RST_PIN); // Create MFRC522 instance

void setup() {
  Serial.begin(9600); // Initialize serial communications with the PC
  SPI.begin(); // Init SPI bus
  mfrc522.PCD_Init(); // Init MFRC522 card
  Serial.println(F("Write personal data on a MIFARE PICC "));
}

void loop() {
  // put your main code here, to run repeatedly:
  // Prepare key - all keys are set to FFFFFFFFFFFFh at chip delivery from the factory.
  MFRC522::MIFARE_Key key;
  for (byte i = 0; i < 6; i + + ) key.keyByte[i] = 0xFF;

  // Reset the loop if no new card present on the sensor/reader. This saves the entire process when idle.
  if ( ! mfrc522.PICC_IsNewCardPresent()) {
    return;
  }

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

  Serial.print(F("Card UID:")); //Dump 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.print(F(" PICC type: ")); // Dump PICC type
  MFRC522::PICC_Type piccType = mfrc522.PICC_GetType(mfrc522.uid.sak);
  Serial.println(mfrc522.PICC_GetTypeName(piccType));

  byte buffer[32];
  byte block;
  MFRC522::StatusCode status;
  byte size = sizeof(buffer);
  byte len;
  byte blockcontent[16] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};

  Serial.setTimeout(20000L); // wait until 20 seconds for input from serial Wait 20 seconds for input from the serial port
  //Ask personal data:
  Serial.println(F("Type Family name, ending with #"));
  len = Serial.readBytesUntil('#', (char *) buffer, 30) ; // read family name from serial
  for (byte i = len; i < 30; i + + ) buffer[i] = ' '; // pad with spaces

  block = 5;

  // Write block
  status = mfrc522.MIFARE_Write(block, buffer, 16);
  if (status != MFRC522::STATUS_OK) {
    Serial.print(F("MIFARE_Write() failed: "));
    Serial.println(mfrc522.GetStatusCodeName(status));
    return;
  }
  else Serial.println(F("MIFARE_Write() success: "));

  // read block
  status = mfrc522.MIFARE_Read(block, buffer, & size);
  if (status != MFRC522::STATUS_OK) {
      Serial.print(F("MIFARE_Read() failed: "));
      Serial.println(mfrc522.GetStatusCodeName(status));
      return;
  }
  else Serial.print(F("MIFARE_Read() success: "));
  for (uint8_t i = 0; i < 16; i + + )
  {
    if (buffer[i] != 32)
    {
      Serial.write(buffer[i]);
    }
  }
  Serial.println(" ");
  delay(1000);
  mfrc522.PICC_HaltA(); // Halt PICC
  mfrc522.PCD_StopCrypto1(); // Stop encryption on PCD
}
Writing and outputting results

Postscript

It’s my first time to write a technical blog, and I’m pretty new to it, so I just wrote it down to record it, and by the way, I’ll leave a legacy to the younger brothers and sisters in the future. If the device really comes out and is used, at least I’ll know what’s going on. If there’s any place to write it, If it’s not good, please give me some advice.