pdf-icon

RFID

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

案例程序

扫描UID案例

#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();
    }
}

读写卡案例

#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.控制卡进入休眠状态

操作返回值状态码

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()确保读取到卡片。
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