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 NodeMcu
SSD1306 |
NodeMcu |
GND |
GND |
VCC |
3.3V |
D0 |
D5(DPIO14) |
D1 |
D7(GPIO13) |
RES |
D8(GPIO15) |
DC |
D4(GPIO2) |
CS |
D10(GPIO1) |
代码设计
1.特征码写入
#include
#include
#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);
}
}
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
2.实现门禁效果
#include
#include
#include
#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 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
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
159
160
161
162
163
164
165
166
167
168
169
实验结果
1.修改特征码(AA BB CC 01)
2.读卡
不刷卡(显示成功的次数)
可通行(显示“Welcome!” cnt次数加一)
不可通行(显示“No permission!”;LED灯闪烁三下)
不刷卡(显示成功次数)
串口监视器(打印刷卡信息)
————————————————
版权声明:本文为CSDN博主「失散多年的哥哥」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/ws15168689087/article/details/121765843