writeMifareClassicMemory()- 针对MIFARE Classic卡片
MIFARE Classic按照扇区进行读写,这种标签需要秘钥进行读写
每个扇区(Sector)包含:
-
块0-2:数据块(16字节/块)
-
块3:控制块(包含密钥A、访问控制位、密钥B)
-
总共16个扇区(MIFARE 1K)
总共64个扇区
writeMifareUltralightMemory()- 针对MIFARE Ultralight卡片,这种标签不需要秘钥
-
页0-3:系统页(包含UID、锁定位)
-
页4-39:用户数据页(4字节/页)
-
总共40页,每个页4字节
卡片页数是否可读写是按照页数2的数据进行判断的如页数2的数据是 9B 48 00 00(16进制,4字节) 从左到右,1代表连续两个页不可写,0代表连续的两个页可写,所以要读写这一类型的卡片,需要先读数据,确定哪些页是可以读写的。

代码如下
#include <SPI.h>
#include <MFRC522.h>
// ESP32-S3引脚定义
// 请根据你的实际接线修改以下引脚
#define RST_PIN 9 // 复位引脚,根据你的连接修改
#define SS_PIN 10 // 片选引脚,根据你的连接修改
MFRC522 mfrc522(SS_PIN, RST_PIN);
// 默认密钥(通常用于MIFARE Classic卡片)
MFRC522::MIFARE_Key key;
// 要写入的数据
String writeData = "HELLO";
int mode = 1; // 1=读取模式, 2=写入模式
void setup() {
Serial.begin(115200);
Serial.println("ESP32-S3 RC522 RFID读写程序");
Serial.println("输入命令:");
Serial.println(" 1 - 读取RFID标签");
Serial.println(" 2 - 写入'HELLO'到RFID标签(20-25页)");
Serial.println("当前模式: 读取模式");
// 初始化SPI
SPI.begin(); // 使用默认SPI引脚
// 初始化RC522
mfrc522.PCD_Init();
// 设置天线增益(可选)
mfrc522.PCD_SetAntennaGain(mfrc522.RxGain_38dB);
// 准备默认密钥
for (byte i = 0; i < 6; i++) {
key.keyByte[i] = 0xFF; // 默认密钥通常是FF FF FF FF FF FF
}
// 显示RC522版本信息
byte version = mfrc522.PCD_ReadRegister(MFRC522::VersionReg);
Serial.print("MFRC522版本: 0x");
Serial.print(version, HEX);
if (version == 0x91) {
Serial.println(" = v1.0");
} else if (version == 0x92) {
Serial.println(" = v2.0");
} else if (version == 0x88) {
Serial.println(" = CLONE 0x88");
} else if (version == 0x90) {
Serial.println(" = CLONE 0x90");
} else {
Serial.println(" (未知)");
}
Serial.println("RC522初始化完成,等待卡片...");
Serial.println("注意:如果无法读取卡片,请检查SPI引脚连接是否正确");
}
void loop() {
// 检查串口输入
if (Serial.available()) {
char input = Serial.read();
if (input == '1') {
mode = 1;
Serial.println("模式切换为: 读取模式");
} else if (input == '2') {
mode = 2;
Serial.println("模式切换为: 写入模式");
Serial.println("准备写入数据: 'HELLO' (20-25页)");
Serial.println("请放置要写入的RFID标签...");
} else if (input == '\n' || input == '\r') {
// 忽略换行符
} else {
Serial.print("未知命令: '");
Serial.print(input);
Serial.println("'");
Serial.println("可用命令: 1=读取, 2=写入");
}
}
// 检查是否有新卡片
if (!mfrc522.PICC_IsNewCardPresent()) {
delay(50);
return;
}
// 选择一张卡片
if (!mfrc522.PICC_ReadCardSerial()) {
delay(50);
return;
}
Serial.println("\n=== 检测到RFID标签 ===");
// 显示UID
Serial.print("UID值(HEX): ");
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_Type piccType = mfrc522.PICC_GetType(mfrc522.uid.sak);
Serial.print("卡片类型: ");
Serial.println(mfrc522.PICC_GetTypeName(piccType));
// 根据模式执行读取或写入操作
if (mode == 1) {
// 读取模式
Serial.println("执行读取操作...");
// 根据卡片类型读取内存
switch (piccType) {
case MFRC522::PICC_TYPE_MIFARE_MINI:
case MFRC522::PICC_TYPE_MIFARE_1K:
case MFRC522::PICC_TYPE_MIFARE_4K:
Serial.println("读取MIFARE Classic卡片内存:");
readMifareClassicMemory();
break;
case MFRC522::PICC_TYPE_MIFARE_UL:
Serial.println("读取MIFARE Ultralight卡片内存:");
readMifareUltralightMemory();
break;
case MFRC522::PICC_TYPE_ISO_14443_4:
case MFRC522::PICC_TYPE_ISO_18092:
case MFRC522::PICC_TYPE_MIFARE_DESFIRE:
Serial.println("此卡片类型需要特殊协议,无法直接读取所有内存");
break;
default:
Serial.println("未知或不受支持的卡片类型");
break;
}
} else if (mode == 2) {
// 写入模式
Serial.println("执行写入操作...");
Serial.print("写入数据: '");
Serial.print(writeData);
Serial.println("' (20-25页)");
// 根据卡片类型写入内存
switch (piccType) {
case MFRC522::PICC_TYPE_MIFARE_MINI:
case MFRC522::PICC_TYPE_MIFARE_1K:
case MFRC522::PICC_TYPE_MIFARE_4K:
Serial.println("写入到MIFARE Classic卡片...");
writeMifareClassicMemory();
break;
case MFRC522::PICC_TYPE_MIFARE_UL:
Serial.println("写入到MIFARE Ultralight卡片...");
writeMifareUltralightMemory();
break;
case MFRC522::PICC_TYPE_ISO_14443_4:
case MFRC522::PICC_TYPE_ISO_18092:
case MFRC522::PICC_TYPE_MIFARE_DESFIRE:
Serial.println("此卡片类型不支持直接写入");
break;
default:
Serial.println("未知或不受支持的卡片类型");
break;
}
// 写入完成后自动切换回读取模式
mode = 1;
Serial.println("写入完成,已自动切换回读取模式");
}
// 使卡片进入休眠状态
mfrc522.PICC_HaltA();
mfrc522.PCD_StopCrypto1();
Serial.println("==================================");
delay(1000);
}
// 读取MIFARE Classic类型卡片的内存
void readMifareClassicMemory() {
MFRC522::StatusCode status;
byte buffer[18];
byte size = sizeof(buffer);
// 计算扇区数量
byte no_of_sectors = 16; // MIFARE 1K默认有16个扇区
if (mfrc522.uid.sak == 0x18) { // MIFARE 4K
no_of_sectors = 40;
}
Serial.println("扇区\t块\t数据(十六进制)\t\t\t\t数据(ASCII)");
Serial.println("----------------------------------------------------------------");
for (byte sector = 0; sector < no_of_sectors; sector++) {
for (byte block = 0; block < 4; block++) {
// 计算绝对块地址
byte absoluteBlock = (sector * 4) + block;
// 每个扇区的最后一个块是控制块,不进行认证读取
if (block == 3) {
Serial.print("扇区 ");
Serial.print(sector, DEC);
Serial.print(" 块 ");
Serial.print(block, DEC);
Serial.println(": [控制块,跳过]");
continue;
}
// 认证当前扇区
status = mfrc522.PCD_Authenticate(MFRC522::PICC_CMD_MF_AUTH_KEY_A,
absoluteBlock, &key, &(mfrc522.uid));
if (status != MFRC522::STATUS_OK) {
Serial.print("扇区 ");
Serial.print(sector, DEC);
Serial.print(" 认证失败: ");
Serial.println(mfrc522.GetStatusCodeName(status));
break; // 当前扇区认证失败,跳过该扇区
}
// 读取数据块
status = mfrc522.MIFARE_Read(absoluteBlock, buffer, &size);
if (status != MFRC522::STATUS_OK) {
Serial.print("扇区 ");
Serial.print(sector, DEC);
Serial.print(" 块 ");
Serial.print(block, DEC);
Serial.print(" 读取失败: ");
Serial.println(mfrc522.GetStatusCodeName(status));
continue;
}
// 显示读取的数据
Serial.print(sector, DEC);
Serial.print("\t");
Serial.print(block, DEC);
Serial.print("\t");
// 显示十六进制数据
for (byte i = 0; i < 16; i++) {
if (buffer[i] < 0x10) {
Serial.print("0");
}
Serial.print(buffer[i], HEX);
Serial.print(" ");
}
Serial.print("\t");
// 显示ASCII数据
for (byte i = 0; i < 16; i++) {
if (buffer[i] < 32 || buffer[i] > 126) { // 不可打印字符
Serial.print(".");
} else {
Serial.print((char)buffer[i]);
}
}
Serial.println();
}
}
}
// 读取MIFARE Ultralight类型卡片的内存
void readMifareUltralightMemory() {
MFRC522::StatusCode status;
byte buffer[18];
byte size = sizeof(buffer);
Serial.println("页\t数据(十六进制)\t\t\t数据(ASCII)");
Serial.println("------------------------------------------------");
// Ultralight卡片通常有16页,每页4字节
// 但有些卡片可能有更多页,这里读取0-39页
for (byte page = 0; page < 40; page++) {
// 跳过前4页(包含UID和锁定位)
if (page < 4) {
Serial.print(page, DEC);
Serial.print("\t[系统页,跳过]");
Serial.println();
continue;
}
// 检查页数是否有效
if (page >= 40) { // 如果卡片没有这么多页,可能会读取失败
break;
}
// 读取页
status = mfrc522.MIFARE_Read(page, buffer, &size);
if (status != MFRC522::STATUS_OK) {
Serial.print("页 ");
Serial.print(page, DEC);
Serial.print(" 读取失败: ");
Serial.println(mfrc522.GetStatusCodeName(status));
continue;
}
// 显示页号和数据
Serial.print(page, DEC);
Serial.print("\t");
// 显示十六进制数据
for (byte i = 0; i < 4; i++) {
if (buffer[i] < 0x10) {
Serial.print("0");
}
Serial.print(buffer[i], HEX);
Serial.print(" ");
}
// 填充对齐
Serial.print("\t\t");
// 显示ASCII数据
for (byte i = 0; i < 4; i++) {
if (buffer[i] < 32 || buffer[i] > 126) { // 不可打印字符
Serial.print(".");
} else {
Serial.print((char)buffer[i]);
}
}
Serial.println();
}
}
// 写入数据到MIFARE Classic卡片
void writeMifareClassicMemory() {
MFRC522::StatusCode status;
// 选择扇区0的块1进行写入(块0是UID,块3是控制块)
byte blockAddr = 1; // 扇区0的块1
// 认证当前扇区
status = mfrc522.PCD_Authenticate(MFRC522::PICC_CMD_MF_AUTH_KEY_A,
blockAddr, &key, &(mfrc522.uid));
if (status != MFRC522::STATUS_OK) {
Serial.print("认证失败: ");
Serial.println(mfrc522.GetStatusCodeName(status));
return;
}
// 准备要写入的数据
byte dataBuffer[16];
// 清空缓冲区
for (byte i = 0; i < 16; i++) {
dataBuffer[i] = 0;
}
// 复制"HELLO"到缓冲区
writeData.getBytes(dataBuffer, 16);
// 写入数据
status = mfrc522.MIFARE_Write(blockAddr, dataBuffer, 16);
if (status != MFRC522::STATUS_OK) {
Serial.print("写入失败: ");
Serial.println(mfrc522.GetStatusCodeName(status));
return;
}
Serial.print("写入成功! 数据已写入到块 ");
Serial.println(blockAddr, DEC);
// 验证写入的数据
byte readBuffer[18];
byte size = sizeof(readBuffer);
status = mfrc522.MIFARE_Read(blockAddr, readBuffer, &size);
if (status != MFRC522::STATUS_OK) {
Serial.print("验证读取失败: ");
Serial.println(mfrc522.GetStatusCodeName(status));
return;
}
Serial.print("验证读取的数据: ");
for (byte i = 0; i < 5; i++) { // 只显示前5个字节
if (readBuffer[i] >= 32 && readBuffer[i] <= 126) {
Serial.print((char)readBuffer[i]);
} else {
Serial.print(".");
}
}
Serial.println();
}
// 写入数据到MIFARE Ultralight卡片
void writeMifareUltralightMemory() {
MFRC522::StatusCode status;
// 修改:写入20-25页,共6页
byte startPage = 20; // 从第20页开始写入
byte endPage = 25; // 写入到第25页
byte pagesCount = endPage - startPage + 1; // 总共6页
Serial.print("写入范围: 第");
Serial.print(startPage, DEC);
Serial.print("页到第");
Serial.print(endPage, DEC);
Serial.print("页 (共");
Serial.print(pagesCount, DEC);
Serial.println("页)");
// 检查卡片是否有足够的页
if (endPage >= 40) { // 假设卡片最多有40页
Serial.println("错误: 卡片页数不足!");
return;
}
// 准备要写入的数据
// 每页4字节,总共6页 = 24字节
byte dataBuffer[24]; // 6页 * 4字节/页 = 24字节
// 清空缓冲区
for (byte i = 0; i < 24; i++) {
dataBuffer[i] = 0;
}
// 复制"HELLO"到缓冲区的前5个字节
writeData.getBytes(dataBuffer, 5);
// 在数据后添加结束符'\0' (可选)
dataBuffer[4] = 'O';
dataBuffer[5] = '\0';
// 在数据中添加一些额外信息
dataBuffer[6] = 'R';
dataBuffer[7] = 'F';
dataBuffer[8] = 'I';
dataBuffer[9] = 'D';
// 写入时间戳或版本信息
dataBuffer[10] = 0x01; // 版本1
dataBuffer[11] = 0x00; // 保留
// 显示要写入的数据
Serial.print("要写入的数据(HEX): ");
for (byte i = 0; i < 24; i++) {
if (dataBuffer[i] < 0x10) {
Serial.print("0");
}
Serial.print(dataBuffer[i], HEX);
Serial.print(" ");
}
Serial.println();
Serial.print("要写入的数据(ASCII): ");
for (byte i = 0; i < 12; i++) { // 只显示前12个字节
if (dataBuffer[i] >= 32 && dataBuffer[i] <= 126) {
Serial.print((char)dataBuffer[i]);
} else if (dataBuffer[i] == 0) {
Serial.print("\\0");
} else {
Serial.print(".");
}
}
Serial.println();
// 写入数据到20-25页
Serial.println("开始写入...");
bool writeSuccess = true;
for (byte page = 0; page < pagesCount; page++) {
byte currentPage = startPage + page;
byte dataOffset = page * 4; // 每页4字节
// 写入当前页,20页开始写
status = mfrc522.MIFARE_Ultralight_Write(currentPage, &dataBuffer[dataOffset], 4);
if (status != MFRC522::STATUS_OK) {
Serial.print("写入第 ");
Serial.print(currentPage, DEC);
Serial.print(" 页失败: ");
Serial.println(mfrc522.GetStatusCodeName(status));
writeSuccess = false;
break;
}
Serial.print("成功写入第 ");
Serial.print(currentPage, DEC);
Serial.println(" 页");
// 添加延迟,避免写入过快
delay(10);
}
if (!writeSuccess) {
Serial.println("写入过程中发生错误!");
return;
}
Serial.print("写入成功! 数据已写入到第 ");
Serial.print(startPage, DEC);
Serial.print(" 页到第 ");
Serial.print(endPage, DEC);
Serial.println(" 页");
// 验证写入的数据
byte readBuffer[18];
byte size = sizeof(readBuffer);
Serial.println("\n验证写入的数据:");
Serial.println("页\t数据(十六进制)\t\t数据(ASCII)");
Serial.println("----------------------------------------");
for (byte page = 0; page < pagesCount; page++) {
byte currentPage = startPage + page;
status = mfrc522.MIFARE_Read(currentPage, readBuffer, &size);
if (status != MFRC522::STATUS_OK) {
Serial.print("第 ");
Serial.print(currentPage, DEC);
Serial.print(" 页验证读取失败: ");
Serial.println(mfrc522.GetStatusCodeName(status));
continue;
}
// 显示页号和数据
Serial.print(currentPage, DEC);
Serial.print("\t");
// 显示十六进制
for (byte i = 0; i < 4; i++) {
if (readBuffer[i] < 0x10) {
Serial.print("0");
}
Serial.print(readBuffer[i], HEX);
Serial.print(" ");
}
Serial.print("\t");
// 显示ASCII
for (byte i = 0; i < 4; i++) {
if (readBuffer[i] >= 32 && readBuffer[i] <= 126) {
Serial.print((char)readBuffer[i]);
} else if (readBuffer[i] == 0) {
Serial.print("\\0");
} else {
Serial.print(".");
}
}
Serial.println();
}
// 验证原始数据
Serial.println("\n原始数据验证:");
bool verificationPassed = true;
for (byte page = 0; page < pagesCount; page++) {
byte currentPage = startPage + page;
byte dataOffset = page * 4;
status = mfrc522.MIFARE_Read(currentPage, readBuffer, &size);
if (status != MFRC522::STATUS_OK) {
Serial.print("第 ");
Serial.print(currentPage, DEC);
Serial.println(" 页读取失败,验证未通过");
verificationPassed = false;
continue;
}
// 比较数据
for (byte i = 0; i < 4; i++) {
if (readBuffer[i] != dataBuffer[dataOffset + i]) {
Serial.print("第 ");
Serial.print(currentPage, DEC);
Serial.print(" 页第 ");
Serial.print(i, DEC);
Serial.print(" 字节不匹配: 期望=0x");
Serial.print(dataBuffer[dataOffset + i], HEX);
Serial.print(", 实际=0x");
Serial.println(readBuffer[i], HEX);
verificationPassed = false;
}
}
}
if (verificationPassed) {
Serial.println("数据验证通过!");
} else {
Serial.println("数据验证失败!");
}
}