RFID-MFRC522射频识别S50白卡
硬件介绍
RFID
无线射频识别即射频识别技术(Radio Frequency Identification,RFID),是自动识别技术的一种,通过无线射频方式进行非接触双向数据通信,利用无线射频方式对记录媒体(电子标签或射频卡)进行读写,从而达到识别目标和数据交换的目的。
MFRC522
MFRC522是高度集成的非接触式读写卡芯片,是一款低电压、低成本、体积小的非接触式读写卡芯片,是智能仪表和便携式手持设备研发的较好选择。
S50卡
S50卡是采用NXP MF1 IC S50制作的非接触智能卡,通常简称S50卡或Mifare 1K,符合ISO14443A标准,4或7字节UID。具有1K数据存储区,数据有密钥保护。
一、主要指标:
· 容量为 8K 位 EEPROM
· 分为 16 个扇区,每个扇区为 4 块,每块 16 个字节,以块为存取单位
· 每个扇区有独立的一组密码及访问控制
· 每张卡有唯一序列号,为 32 位
· 具有防冲突机制,支持多卡操作
· 无电源,自带天线,内含加密控制逻辑和通讯逻辑电路
· 数据保存期为 10 年,可改写 10 万次,读无限次
· 工作温度: -20℃ ~50℃ (湿度为 90%)
· 工作频率: 13.56MHZ
· 通信速率: 106 KBPS
· 读写距离: 10 cm 以内(与读写器有关)
二、存储结构
具体了解S50卡的相关信息请查看相关的中文说明书,链接放下方了。
点击下载——提取码:6666
系统设计(一个刷卡门禁系统)
设计要求
(1)在S50白卡的第15扇区第2块写入系统特征码,前三字节AAH BBH CCH + 其他(任意,各组不同);异形卡不做任何设置。
(2)当刷本组的白卡时,液晶屏显示欢迎词,否则(异形卡或者他组白卡)液晶屏显示拒绝信息,并闪烁LED灯3次以示报警。
(3)在液晶屏上显示当日(开机或重启后)刷卡总次数。
Fritzing图
线路连接
RC522
RC522 |
NodeMcu |
SDA |
D2(GPIO4) |
SCK |
D5(GPIO14) |
MOSI | D7(GPIO13) |
MISO |
D6(GPIO12) |
IRQ | 空 |
GND |
GND |
RST | D1(GPIO5) |
3.3V | 3.3V |
SSD1306
SSD1306 | NodeMcu |
GND | GND |
VCC | 3.3V |
D0 | D5(DPIO14) |
D1 | D7(GPIO13) |
RES | D8(GPIO15) |
DC | D4(GPIO2) |
CS | D10(GPIO1) |
代码设计
1.特征码写入
#include <SPI.h> #include <MFRC522.h> #define RST_PIN 5 // 配置针脚 #define SS_PIN 4 MFRC522 mfrc522(SS_PIN, RST_PIN); // 创建新的RFID实例 MFRC522::MIFARE_Key key; //6字节的密码 void dump_byte_array(byte *buffer, byte bufferSize); //声明dump_byte_array函数 void setup() { Serial.begin(9600); // 设置串口波特率为9600 while (!Serial); // 如果串口没有打开,则死循环下去不进行下面的操作 SPI.begin(); // SPI开始 mfrc522.PCD_Init(); // Init MFRC522 card for (byte i = 0; i < 6; i++) {//设置key为:FF FF FF FF FF FF key.keyByte[i] = 0xFF; } Serial.println(F("扫描卡开始进行读或者写")); Serial.print(F("使用A和B作为键")); dump_byte_array(key.keyByte, MFRC522::MF_KEY_SIZE); Serial.println(); Serial.println(F("注意,会把数据写入到卡第15扇区")); } void loop() { // 寻找新卡 if ( ! mfrc522.PICC_IsNewCardPresent()) return; // 选择一张卡 if ( ! mfrc522.PICC_ReadCardSerial()) return; // 显示卡片的详细信息 Serial.print(F("卡片 UID:")); dump_byte_array(mfrc522.uid.uidByte, mfrc522.uid.size); Serial.println(); Serial.print(F("卡片类型: ")); MFRC522::PICC_Type piccType = mfrc522.PICC_GetType(mfrc522.uid.sak);//获取卡片类型码 Serial.println(mfrc522.PICC_GetTypeName(piccType));//转换类型码为类型名称 // 检查兼容性,只有MIFARE类型的卡才能读写 if ( piccType != MFRC522::PICC_TYPE_MIFARE_MINI && piccType != MFRC522::PICC_TYPE_MIFARE_1K && piccType != MFRC522::PICC_TYPE_MIFARE_4K) { Serial.println(F("仅仅适合Mifare Classic卡的读写")); return; } // 我们只使用第14个扇区 byte sector = 14; byte blockAddr = 58;//第58个块为第14个扇区第2个数据块 byte dataBlock[] = { 0xaa, 0xbb, 0xcc, 0x01, // aa,bb,cc,1 0x00, 0x00, 0x00, 0x00, // 0,0,0,0 0x00, 0x00, 0x00, 0x00, // 0,0,0,0 0x00, 0x00, 0x00, 0x00 // 0,0,0,0 };//写入的数据定义 byte trailerBlock = 59;//第59个块为第14个扇区的控制块 MFRC522::StatusCode status; byte buffer[18]; byte size = sizeof(buffer); // 原来的数据 Serial.println(F("显示原本的数据...")); status = (MFRC522::StatusCode) mfrc522.PCD_Authenticate(MFRC522::PICC_CMD_MF_AUTH_KEY_A, trailerBlock, &key, &(mfrc522.uid));//在uid为mfrc522.uid的卡的trailerBlock块(此块为扇区控制块)验证key是否与A密码相同 if (status != MFRC522::STATUS_OK) { Serial.print(F("身份验证失败?或者是卡链接失败")); Serial.println(mfrc522.GetStatusCodeName(status)); return; } // 显示整个扇区 Serial.println(F("显示所有扇区的数据")); mfrc522.PICC_DumpMifareClassicSectorToSerial(&(mfrc522.uid), &key, sector);//串行输出uid卡,第sector扇区的数据 Serial.println(); // 从块儿读取数据 Serial.print(F("读取块儿的数据在:")); Serial.print(blockAddr); Serial.println(F("块 ...")); status = (MFRC522::StatusCode) mfrc522.MIFARE_Read(blockAddr, buffer, &size);//读取size个第blockAddr块的数据到buffer if (status != MFRC522::STATUS_OK) { Serial.print(F("读卡失败,没有连接上 ")); Serial.println(mfrc522.GetStatusCodeName(status)); } //开始进行写入准备 Serial.println(F("开始进行写入的准备...")); status = (MFRC522::StatusCode) mfrc522.PCD_Authenticate(MFRC522::PICC_CMD_MF_AUTH_KEY_B, trailerBlock, &key, &(mfrc522.uid));//验证密码B if (status != MFRC522::STATUS_OK) { Serial.print(F("写入失败,没有连接上或者没有权限 ")); Serial.println(mfrc522.GetStatusCodeName(status)); return; } // Write data to the block Serial.print(F("在第: ")); 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("写入失败... ")); Serial.println(mfrc522.GetStatusCodeName(status)); } Serial.println(); // 再次读取卡中数据,这次是写入之后的数据 Serial.print(F("读取写入后第")); Serial.print(blockAddr); Serial.println(F(" 块的数据 ...")); status = (MFRC522::StatusCode) mfrc522.MIFARE_Read(blockAddr, buffer, &size); if (status != MFRC522::STATUS_OK) { Serial.print(F("读取失败... ")); Serial.println(mfrc522.GetStatusCodeName(status)); } Serial.print(F("块 ")); Serial.print(blockAddr); Serial.println(F("数据为 :")); dump_byte_array(buffer, 16); Serial.println(); // 验证一下数据,要保证写入前后数据是相等的 // 通过计算块中的字节数量 Serial.println(F("等待验证结果...")); byte count = 0; for (byte i = 0; i < 16; i++) { // 比较一下缓存中的数据(我们读出来的数据) = (我们刚刚写的数据) if (buffer[i] == dataBlock[i]) count++; } Serial.print(F("匹配的字节数量 = ")); Serial.println(count); if (count == 16) { Serial.println(F("验证成功 :")); } else { Serial.println(F("失败,数据不匹配")); Serial.println(F("也许写入的内容不恰当")); } Serial.println(); // 转储扇区数据 Serial.println(F("写入后的数据内容为::")); mfrc522.PICC_DumpMifareClassicSectorToSerial(&(mfrc522.uid), &key, sector); Serial.println(); // 停止 PICC mfrc522.PICC_HaltA(); //停止加密PCD mfrc522.PCD_StopCrypto1(); } /** * 将字节数组串行输出为16进制字符 */ 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); } }
2.实现门禁效果
#include <SPI.h> #include <MFRC522.h> #include <Adafruit_SSD1306.h> #define SCREEN_WIDTH 128 #define SCREEN_HEIGHT 64 #define SS_PIN 4 #define RST_PIN 5 //显示屏和LED灯定义 #define redLed 16 #define OLED_MOSI 13 #define OLED_CLK 14 #define OLED_DC 2 #define OLED_CS 1 #define OLED_RESET 15 Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT,OLED_MOSI, OLED_CLK, OLED_DC, OLED_RESET, OLED_CS); MFRC522 rfid(SS_PIN, RST_PIN); // Instance of the class MFRC522::MIFARE_Key key; // Init array that will store new NUID byte nuidPICC[4]; void dump_byte_array(byte *buffer, byte bufferSize); //声明dump_byte_array函数 byte cnt =0; void setup() { pinMode(redLed,OUTPUT); digitalWrite(redLed,LOW); Serial.begin(9600); display.begin(SSD1306_SWITCHCAPVCC); display.clearDisplay(); display.setTextSize(1); //字体大小 display.setTextColor(WHITE); //字体颜色 display.setCursor(0,0); //调整位置 display.println(cnt); display.display(); SPI.begin(); // Init SPI bus rfid.PCD_Init(); // Init MFRC522 for (byte i = 0; i < 6; i++) { key.keyByte[i] = 0xFF; } Serial.println(F("This code scan the MIFARE Classsic NUID.")); Serial.print(F("Using the following key:")); printHex(key.keyByte, MFRC522::MF_KEY_SIZE); } void loop() { // 我们只使用第14个扇区 byte sector = 14; byte blockAddr = 58;//第58个块为第14个扇区第2个数据块 byte trailerBlock = 59;//第59个块为第14个扇区的控制块 MFRC522::StatusCode status; byte buffer[18]; byte size = sizeof(buffer); // Reset the loop if no new card present on the sensor/reader. This saves the entire process when idle. if ( ! rfid.PICC_IsNewCardPresent()) return; // Verify if the NUID has been readed if ( ! rfid.PICC_ReadCardSerial()) return; rfid.PICC_DumpMifareClassicSectorToSerial(&(rfid.uid), &key, sector);//串行输出uid卡,第sector扇区的数据 Serial.print(F("读取写入后第:")); Serial.print(blockAddr); Serial.println(F("块的数据 ...")); status = (MFRC522::StatusCode) rfid.MIFARE_Read(blockAddr, buffer, &size);//读取size个第blockAddr块的数据到buffer if (status != MFRC522::STATUS_OK) { Serial.print(F("读卡失败,没有连接上 ")); Serial.println(rfid.GetStatusCodeName(status)); } Serial.print(F("该块: ")); Serial.print(blockAddr);Serial.println(F("数据为:")); dump_byte_array(buffer, 16); Serial.println(); // Store NUID into nuidPICC array for (byte i = 0; i < 4; i++) { nuidPICC[i] = rfid.uid.uidByte[i]; } Serial.println(F("The NUID tag is:")); Serial.print(F("In hex: ")); printHex(rfid.uid.uidByte, rfid.uid.size); Serial.println(); Serial.print(F("In dec: ")); printDec(rfid.uid.uidByte, rfid.uid.size); Serial.println(); // Halt PICC rfid.PICC_HaltA(); // Stop encryption on PCD rfid.PCD_StopCrypto1(); } /** * Helper routine to dump a byte array as hex values to Serial. */ void printHex(byte *buffer, byte bufferSize) { for (byte i = 0; i < bufferSize; i++) { Serial.print(buffer[i] < 0x10 ? " 0" : " "); Serial.print(buffer[i], HEX); } } /** * Helper routine to dump a byte array as dec values to Serial. */ void printDec(byte *buffer, byte bufferSize) { String s = ""; for (byte i = 0; i < bufferSize; i++) { Serial.print(buffer[i] < 0x10 ? " 0" : " "); Serial.print(buffer[i], DEC); s += buffer[i]; if(i == bufferSize-1){ Serial.print(" CardID Str: " + s); } } } //验证卡 void dump_byte_array(byte *buffer, byte bufferSize) { byte buffer0[18] = { 0xaa, 0xbb, 0xcc }; int count = 0; for(byte i =0 ;i<bufferSize ;i++) { if(i<3) { if(buffer[i] == buffer0[i]){ count++; } } Serial.print(buffer[i] < 0x10 ? " 0" : " "); Serial.print(buffer[i], HEX); } Serial.println(); if(count==3){ Serial.println("成功"); display.println("Welcome!"); cnt=cnt+1; } else{ Serial.println("验证失败"); display.println("NO Permission!"); digitalWrite(redLed,HIGH); delay(200); digitalWrite(redLed,LOW); delay(200); digitalWrite(redLed,HIGH); delay(200); digitalWrite(redLed,LOW); delay(200); digitalWrite(redLed,HIGH); delay(200); digitalWrite(redLed,LOW); delay(200); } Serial.print("成功次数:"); Serial.println(cnt); display.display(); delay(1000); }
实验结果
1.修改特征码(AA BB CC 01)
2.读卡
不刷卡(显示成功的次数)
可通行(显示“Welcome!” cnt次数加一)
不可通行(显示“No permission!”;LED灯闪烁三下)
不刷卡(显示成功次数)
串口监视器(打印刷卡信息)