RFID-MFRC522射频识别S50白卡

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灯闪烁三下)
在这里插入图片描述
不刷卡(显示成功次数)
在这里插入图片描述
串口监视器(打印刷卡信息)
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/ws15168689087/article/details/121765843