pdf-icon

Arduino 上手教程

Dial RFID 射频识别(NFC)

M5Dial RFID使用MFRC522库作为驱动, 参考下方API & 案例即可实现简单的显示, 获取更多API内容可以参考MFRC522源码。

案例程序

扫描UID案例

cpp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
#include "M5Dial.h"
void setup() {
auto cfg = M5.config();
M5Dial.begin(cfg, false, true);
}
void loop() {
if (M5Dial.Rfid.PICC_IsNewCardPresent() &&
M5Dial.Rfid.PICC_ReadCardSerial()) {
M5Dial.Display.clear();
Serial.print(F("PICC type: "));
uint8_t piccType = M5Dial.Rfid.PICC_GetType(M5Dial.Rfid.uid.sak);
Serial.println(M5Dial.Rfid.PICC_GetTypeName(piccType));
// Check is the PICC of Classic MIFARE type
if (piccType != MFRC522::PICC_TYPE_MIFARE_MINI &&
piccType != MFRC522::PICC_TYPE_MIFARE_1K &&
piccType != MFRC522::PICC_TYPE_MIFARE_4K) {
Serial.println(F("Your tag is not of type MIFARE Classic."));
return;
}
for (byte i = 0; i < M5Dial.Rfid.uid.size;
i++) { // Output the stored UID data.
Serial.printf("%02X ", M5Dial.Rfid.uid.uidByte[i]);
}
Serial.println();
}
}

读写卡案例

cpp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158
#include "M5Dial.h"
MFRC522::MIFARE_Key key;
void setup() {
auto cfg = M5.config();
M5Dial.begin(cfg, false, true);
// Prepare the key (used both as key A and as key B)
// using FFFFFFFFFFFFh which is the default at chip delivery from the factory
for (byte i = 0; i < 6; i++) {
key.keyByte[i] = 0xFF;
}
}
/**
* Helper routine to dump a byte array as hex values to Serial.
*/
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);
}
}
void loop() {
M5Dial.update();
if (M5Dial.Rfid.PICC_IsNewCardPresent() && M5Dial.Rfid.PICC_ReadCardSerial()) {
Serial.print(F("PICC type: "));
uint8_t piccType = M5Dial.Rfid.PICC_GetType(M5Dial.Rfid.uid.sak);
Serial.println(M5Dial.Rfid.PICC_GetTypeName(piccType));
// Check is the PICC of Classic MIFARE type
if (piccType != MFRC522::PICC_TYPE_MIFARE_MINI && piccType != MFRC522::PICC_TYPE_MIFARE_1K &&
piccType != MFRC522::PICC_TYPE_MIFARE_4K) {
Serial.println(F("Your tag is not of type MIFARE Classic."));
return;
}
String uid = "";
for (byte i = 0; i < M5Dial.Rfid.uid.size; i++) { // Output the stored UID data. 将存储的UID数据输出
Serial.printf("%02X ", M5Dial.Rfid.uid.uidByte[i]);
uid += String(M5Dial.Rfid.uid.uidByte[i], HEX);
}
Serial.println();
// M5Dial.Rfid.PICC_DumpToSerial(&(M5Dial.Rfid.uid));
// In this sample we use the second sector,
// that is: sector #1, covering block #4 up to and including block #7
byte sector = 1;
byte blockAddr = 4;
byte dataBlock[] = {
0x01, 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;
MFRC522::StatusCode status;
byte buffer[18];
byte size = sizeof(buffer);
// Authenticate using key A
Serial.println(F("Authenticating using key A..."));
status = (MFRC522::StatusCode)M5Dial.Rfid.PCD_Authenticate(MFRC522::PICC_CMD_MF_AUTH_KEY_A, trailerBlock, &key,
&(M5Dial.Rfid.uid));
if (status != MFRC522::STATUS_OK) {
Serial.print(F("PCD_Authenticate() failed: "));
Serial.println(M5Dial.Rfid.GetStatusCodeName(status));
return;
}
// Show the whole sector as it currently is
Serial.println(F("Current data in sector:"));
M5Dial.Rfid.PICC_DumpMifareClassicSectorToSerial(&(M5Dial.Rfid.uid), &key, sector);
Serial.println();
// Read data from the block
Serial.print(F("Reading data from block "));
Serial.print(blockAddr);
Serial.println(F(" ..."));
status = (MFRC522::StatusCode)M5Dial.Rfid.MIFARE_Read(blockAddr, buffer, &size);
if (status != MFRC522::STATUS_OK) {
Serial.print(F("MIFARE_Read() failed: "));
Serial.println(M5Dial.Rfid.GetStatusCodeName(status));
}
Serial.print(F("Data in block "));
Serial.print(blockAddr);
Serial.println(F(":"));
dump_byte_array(buffer, 16);
Serial.println();
Serial.println();
// Authenticate using key B
Serial.println(F("Authenticating again using key B..."));
status = (MFRC522::StatusCode)M5Dial.Rfid.PCD_Authenticate(MFRC522::PICC_CMD_MF_AUTH_KEY_B, trailerBlock, &key,
&(M5Dial.Rfid.uid));
if (status != MFRC522::STATUS_OK) {
Serial.print(F("PCD_Authenticate() failed: "));
Serial.println(M5Dial.Rfid.GetStatusCodeName(status));
return;
}
// Write data to the block
Serial.print(F("Writing data into block "));
Serial.print(blockAddr);
Serial.println(F(" ..."));
dump_byte_array(dataBlock, 16);
Serial.println();
status = (MFRC522::StatusCode)M5Dial.Rfid.MIFARE_Write(blockAddr, dataBlock, 16);
if (status != MFRC522::STATUS_OK) {
Serial.print(F("MIFARE_Write() failed: "));
Serial.println(M5Dial.Rfid.GetStatusCodeName(status));
}
Serial.println();
// Read data from the block (again, should now be what we have written)
Serial.print(F("Reading data from block "));
Serial.print(blockAddr);
Serial.println(F(" ..."));
status = (MFRC522::StatusCode)M5Dial.Rfid.MIFARE_Read(blockAddr, buffer, &size);
if (status != MFRC522::STATUS_OK) {
Serial.print(F("MIFARE_Read() failed: "));
Serial.println(M5Dial.Rfid.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:"));
M5Dial.Rfid.PICC_DumpMifareClassicSectorToSerial(&(M5Dial.Rfid.uid), &key, sector);
Serial.println();
// Halt PICC
M5Dial.Rfid.PICC_HaltA();
// Stop encryption on PCD
M5Dial.Rfid.PCD_StopCrypto1();
}
}
  • 读写案例输出, 可以看到扇区1的4号block被成功写入数据。
« PICC type: MIFARE 1KB
C2 34 D4 44 
Authenticating using key A...
Current data in sector:
   1      7   00 00 00 00  00 00 FF 07  80 69 FF FF  FF FF FF FF  [ 0 0 1 ] 
          6   00 00 00 00  00 00 00 00  00 00 00 00  00 00 00 00  [ 0 0 0 ] 
          5   00 00 00 00  00 00 00 00  00 00 00 00  00 00 00 00  [ 0 0 0 ] 
          4   00 00 00 00  00 00 00 00  00 00 00 00  00 00 00 00  [ 0 0 0 ] 

Reading data from block 4 ...
Data in block 4:
 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00

Authenticating again using key B...
Writing data into block 4 ...
 01 02 03 04 05 06 07 08 09 0A FF 0B 0C 0D 0E 0F

Reading data from block 4 ...
Data in block 4:
 01 02 03 04 05 06 07 08 09 0A FF 0B 0C 0D 0E 0F
Checking result...
Number of bytes that match = 16
Success :-)

Current data in sector:
   1      7   00 00 00 00  00 00 FF 07  80 69 FF FF  FF FF FF FF  [ 0 0 1 ] 
          6   00 00 00 00  00 00 00 00  00 00 00 00  00 00 00 00  [ 0 0 0 ] 
          5   00 00 00 00  00 00 00 00  00 00 00 00  00 00 00 00  [ 0 0 0 ] 
          4   01 02 03 04  05 06 07 08  09 0A FF 0B  0C 0D 0E 0F  [ 0 0 0 ] 

INIT API

以下为常用API的使用说明, 一般读写MIFARE卡使用流程为:

  • 1.初始化RFID
  • 2.检测新卡存在, 获取卡uid
  • 3.通过uid选中卡片, 进入active状态
  • 4.通过A,B KEY解锁对应要操作的块
  • 5.读写数据
  • 6.控制卡进入休眠状态

操作返回值状态码

cpp
1 2 3 4 5 6 7 8 9 10 11
enum StatusCode {
STATUS_OK = 1, // Success
STATUS_ERROR = 2, // Error in communication
STATUS_COLLISION = 3, // Collission detected
STATUS_TIMEOUT = 4, // Timeout in communication.
STATUS_NO_ROOM = 5, // A buffer is not big enough.
STATUS_INTERNAL_ERROR = 6, // Internal error in the code. Should not happen ;-)
STATUS_INVALID = 7, // Invalid argument.
STATUS_CRC_WRONG = 8, // The CRC_A does not match
STATUS_MIFARE_NACK = 9 // A MIFARE PICC responded with NAK.
};

begin

函数原型:

void begin();

功能说明:

  • 初始化RFID

可通过调用M5Dial.begin时设置参数enableRFID为true一同初始化。

M5Dial.begin(m5::M5Unified::config_t cfg, bool enableEncoder,bool enableRFID)

传入参数:

  • null

返回值:

  • null

PICC_IsNewCardPresent

函数原型:

bool PICC_IsNewCardPresent();

功能说明:

  • 扫描是否存在未检测过且处于IDLE状态的卡片, 处于HALT状态的卡将被忽略。

传入参数:

  • null

返回值:

  • bool:
    • true:扫描到了新卡
    • false:未扫描到新卡

PICC_ReadCardSerial

函数原型:

bool PICC_ReadCardSerial();

功能说明:

  • 操作读取卡UID, 读取成功后的UID可以在类成员Uid uid;中读取。读取操作前需执行PICC_IsNewCardPresent(), PICC_RequestA()PICC_WakeupA()确保读取到卡片。
cpp
1 2 3 4
for (byte i = 0; i < M5Dial.Rfid.uid.size;
i++) { // Output the stored UID data.
Serial.printf("%02X ", M5Dial.Rfid.uid.uidByte[i]);
}

传入参数:

  • null

返回值:

  • bool:
    • true:读取成功
    • false:读取失败

PICC_RequestA

函数原型:

uint8_t PICC_RequestA(uint8_t *bufferATQA, uint8_t *bufferSize);

功能说明:

  • 扫描检测读取范围内的Type A标准的卡片

传入参数:

  • uint8_t *bufferATQA:
    • 存储请求响应ATQA (Answer to request) 的buffer
  • uint8_t *bufferSize:
    • buffer长度(>2byte).

返回值:

  • uint8_t:
    • StatusCode

PICC_WakeupA

函数原型:

uint8_t PICC_WakeupA(uint8_t *bufferATQA, uint8_t *bufferSize);

功能说明:

  • 唤醒范围内的Type A标准的卡片

传入参数:

  • uint8_t *bufferATQA:
    • 存储请求响应ATQA (Answer to request) 的buffer
  • uint8_t *bufferSize:
    • buffer长度(>2byte).

返回值:

  • uint8_t:
    • StatusCode

PICC_Select

函数原型:

uint8_t PICC_Select(Uid *uid, uint8_t validBits = 0);

功能说明:

  • 通过uid选中一张卡片进入active状态

传入参数:

  • Uid *uid:
    • 通过扫描获取到的卡片uid结构体指针
  • uint8_t validBits:
    • 最后一个uint8_t中的有效位, 0表示8个有效位。

返回值:

  • uint8_t:
    • StatusCode

PICC_HaltA

函数原型:

uint8_t PICC_HaltA();

功能说明:

  • 选中当前卡片进入休眠状态

传入参数:

  • null

返回值:

  • uint8_t:
    • StatusCode

MIFARE API

PCD_Authenticate

函数原型:

uint8_t PCD_Authenticate(uint8_t command, uint8_t blockAddr, MIFARE_Key *key, Uid *uid);

功能说明:

  • 进行MIFARE身份验证, 在调用此函数之前,需要对卡片进行select操作使其处于active状态。经过身份验证的 PICC 通信结束后需调用 PCD_StopCrypto1(), 否则无法开始新的通信。

传入参数:

  • uint8_t command:
    • PICC_CMD_MF_AUTH_KEY_A
    • PICC_CMD_MF_AUTH_KEY_B
  • uint8_t blockAddr:
    • Block地址
  • MIFARE_Key *key:
    • 默认状态下,A,B密钥均为FFFFFFFFFFFF
  • Uid *uid

返回值:

  • uint8_t:
    • StatusCode

PCD_StopCrypto1

函数原型:

void PCD_StopCrypto1();

功能说明:

  • 退出 PCD 的认证状态。经过身份验证的 PICC 通信结束后需调用 PCD_StopCrypto1(), 否则无法开始新的通信。

传入参数:

  • null

返回值:

  • null

MIFARE_Read

函数原型:

uint8_t MIFARE_Read(uint8_t blockAddr, uint8_t *buffer, uint8_t *bufferSize);

功能说明:

  • 向指定block addr写入数据

传入参数:

  • uint8_t blockAddr:
    • 实际卡片扇区中的block地址
  • uint8_t *buffer:
    • 接收数据的buffer指针
  • uint8_t *bufferSize:
    • 接收数据的buffer长度(>=18byte)

返回值:

  • uint8_t:
    • StatusCode

MIFARE_Write

函数原型:

uint8_t MIFARE_Write(uint8_t blockAddr, uint8_t *buffer, uint8_t bufferSize);

功能说明:

  • 向指定block addr写入数据

传入参数:

  • uint8_t blockAddr:
    • 实际卡片扇区中的block地址
  • uint8_t *buffer:
    • 数据指针
  • uint8_t *bufferSize:
    • 数据长度(16byte)

返回值:

  • uint8_t:
    • StatusCode
On This Page