通过Arduino的RFID(RC522)模块模拟公交卡扣费代码实例

大家好,我是Danny,今天我要记录一个使用Arduino对RFID(RC522)模块进行编程的实例,模仿了公交中的刷卡系统,将余额记录在卡片中,每次刷卡自动扣费500,如果余额不足则会自动显示“余额不足”。

本人第一次学习Arduino,所以有错误欢迎指正.。

Mifare卡的存储原理

RFID的作用,历史什么的,随便找网址一大堆,所以我就不多赘述了。这里主要讲一下我对RFID中的一种—RC522模块所使用的Mifare卡的存储原理的理解。

在Mifare卡中一共有16个扇区(sector),每个扇区有4个区块(Block),每个块有16个字节。按照编号,16个扇区分为0-15号,64个区块可以分为某个扇区的0-3号,或者直接分为0-63号。打个比方,5号扇区的2号区块,即是22号区块。
在这里插入图片描述

在如此多的块中,0号扇区的0号区块用于存放厂商代码,已经固化,不可更改。除此之外,每个扇区的块0、块1、块2为数据块,可用于存贮数据;而每个扇区的块3为控制块,包括了密码A、存取控制、密码B。

在这里插入图片描述
每个扇区的密码和存取控制都是独立的,可以根据实际需要设定各自的密码及存取控制。存取控制为4个字节,共32位,扇区中的每个块(包括数据块和控制块)的存取条件是由密码和存取控制共同决定的,在存取控制中每个块都有相应的三个控制位,定义如下:
在这里插入图片描述

三个控制位以正和反两种形式存在于存取控制字节中,决定了该块的访问权限(如进行减值操作必须验证KEY A,进行加值操作必须验证KEY B,等等)。存取控制(4字节,其中字节9为备用字节)结构如下所示:
注:_b表示取反

数据块(块0、块1、块2)的存取控制如下:
在这里插入图片描述

控制块块 3 的存取控制与数据块(块 0、 1、 2)不同,它的存取控制如下:
在这里插入图片描述
以上即为Mifare卡的重要储存原理。

库,变量和setup函数

#include <SPI.h>
#include <MFRC522.h>
#include <LiquidCrystal.h>
#define RST_PIN     5
#define SS_PIN      53
LiquidCrystal lcd(7, 8, 9, 10, 11, 12);//lcd显示器相关代码
MFRC522 mfrc522(SS_PIN, RST_PIN);  
MFRC522::MIFARE_Key key;          //这部分代码参考网站:https://github.com/miguelbalboa/rfid/blob/master/examples/MifareClassicValueBlock/MifareClassicValueBlock.ino#L66C1-L66C1

long int intvalue;
String strvalue="";
String me="";
String password="5E9941B"; //设置uid为“5E99041B”这个请根据自己的卡片自行定义
void setup() 
{
  Serial.begin(9600);        
  SPI.begin();               
  lcd.begin(16, 2);
  mfrc522.PCD_Init();        
  Serial.println("Scan a MIFARE Classic card");
  
  // 定义读写密钥
  for (byte i = 0; i < 6; i++) {
    key.keyByte[i] = 0xFF;   //keyByte在结构体(struct)"MIFARE_Key"中已经被定义
  }
}

此处说明一下,部分代码源于网站https://github.com/miguelbalboa/rfid/blob/master/examples/MifareClassicValueBlock/MifareClassicValueBlock.ino#L66C1-L66C1,各种头文件均可在Arduino UNO的官网中下载,密钥A、B均为“FFFFFFFFFFFF”。

Mifare卡的Uid识别函数

//识别uid是否对应的函数
void uidtest(byte *buffer){//在loop函数中对应mfrc522.uid.uidByte
  for(byte i=0;i<4;i++){
    me.concat(String(buffer[i], HEX)); //uid既是uidByte数组中的前四位,concat函数的作用为连接字符至字符串String中,HEX代表16进制    
  }
  me.toUpperCase();//改为大写字母
  if(me!=password){//识别失败
    lcd.print("Unable to recognize");
    me="";
    //delay(100);
    return;
  }
  else{
    lcd.print("Danny Leo");//识别成功,在lcd屏幕上显示"Dannt Leo"
    //delay(1000);
  }
  me="";
}

uidtest函数用于识别Mifare卡中的uid(即0扇区0区块的前4个字节)是否与设置的变量“password”一致。

Mifare卡数据读写函数代码

int writeBlock(int blockNumber, byte arrayAddress[]) {
  String strv=String(intvalue);
  String tocard="0000000000000000"+strv;
  tocard=tocard.substring(tocard.length()-16);
  tocard.getBytes(arrayAddress, 17);
  //以上代码的作用为将intvalue里的值(10进制)转换为String,再转换为Byte[16]
  int largestModulo4Number=blockNumber/4*4;//这里先除以4再乘以4的原理是将区块号变成当前扇区的0号,如17号区块,17/4*4的值为16,即4号扇区的0号区块
  int trailerBlock=largestModulo4Number+3;//将区块号变成当前扇区的3号区块
  byte status = mfrc522.PCD_Authenticate(MFRC522::PICC_CMD_MF_AUTH_KEY_B, trailerBlock, &key, &(mfrc522.uid));
  if (status != MFRC522::STATUS_OK) {
    Serial.print("PCD_Authenticate() failed: ");
    Serial.println(mfrc522.GetStatusCodeName(status));
    return 3;//return "3" as error message
  }  
  //核心代码,将arrayAddress[16]写入对应区块 
  status = mfrc522.MIFARE_Write(blockNumber, arrayAddress, 16);
  if (status != MFRC522::STATUS_OK) {
    Serial.print("MIFARE_Write() failed: ");
    Serial.println(mfrc522.GetStatusCodeName(status));
    return 4;
  }
}


int readBlock(int blockNumber, byte arrayAddress[]) {
  int largestModulo4Number=blockNumber/4*4;
  int trailerBlock=largestModulo4Number+3;
  char transform[16];
  //核心代码:验证密钥
  byte status = mfrc522.PCD_Authenticate(MFRC522::PICC_CMD_MF_AUTH_KEY_A, trailerBlock, &key, &(mfrc522.uid));
  if (status != MFRC522::STATUS_OK) {
    Serial.print("PCD_Authenticate() failed (read): ");
    Serial.println(mfrc522.GetStatusCodeName(status));
    return 3;
  }
  byte buffersize = 18; 
  status = mfrc522.MIFARE_Read(blockNumber, arrayAddress, &buffersize);//核心代码:将对应的区块中的数值存入变量arrayAddress[18]中,数值存于前16位
  if (status != MFRC522::STATUS_OK) {
    Serial.print("MIFARE_read() failed: ");
    Serial.println(mfrc522.GetStatusCodeName(status));
    return 4;
  }
  for (int j=0 ; j<16 ; j++){
    transform[j]=arrayAddress[j];
    strvalue.concat(transform[j]);
  }
  intvalue=strvalue.toInt();
  //以上代码的作用是将arrayAddress[18]中的16进制值转换为char[18],然后将前16位连接导入至String变量strvalue中,最后将其变为int变量存入intvalue变量
}

总而言之,writeBlock()的作用是将intvalue中存储的数值存入Mifare卡中,readBlock()的作用是读取Mifare卡中存储的数值至intvalue中。

loop函数

void loop(){  
  if ( ! mfrc522.PICC_IsNewCardPresent()) {
    return;
  }
  if ( ! mfrc522.PICC_ReadCardSerial()) {
    return;
  }

  lcd.clear();
  int block=5;  
  byte blockcontent[17];
  byte readbackblock[18];
  uidtest(mfrc522.uid.uidByte);

  readBlock(block, readbackblock);
  Serial.print(intvalue);
  Serial.println(" - 500");
  intvalue-=500;//卡里的钱-500
  if(intvalue<0){
    lcd.setCursor(0, 1);
    //判定为“余额不足”,在lcd第二行显示出来
    lcd.print("Not Enough Money");
    intvalue+=500;
    return;
  }
  Serial.print("Money Left: ");
  Serial.println(intvalue);
  String lcdd=String(intvalue+500)+"-500="+String(intvalue);
  lcd.setCursor(0, 1);
  lcd.print(lcdd);在这里插入图片描述

  writeBlock(block, blockcontent);
  strvalue="";
  me="";
  mfrc522.PICC_HaltA();
  mfrc522.PCD_StopCrypto1();
}

该代码呈现出的结果为每次刷卡,都会使得卡里的余额-500,如果余额不足则会显示“Not Enough Money”。

硬件部分

本次使用 Arduino Mega 2560 作为控制核心,接线方式为将下面两幅图叠加:
在这里插入图片描述
在这里插入图片描述
实物图如下所示:
在这里插入图片描述

效果呈现

如下视频所示:

Arduino mega 2560 RFID

第一次学习Arduino,有问题或者疑问请务必提出,这也会让我很开心,谢谢!

  • 24
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
### 回答1: sg-6000-vm02 是指一台服务器的名称,下载是指在该服务器上进行文件下载的行为。关于 sg-6000-vm02 下载,以下是一些可能的回答: sg-6000-vm02 是一台虚拟机服务器,我们可以通过远程连接或者基于云技术的管理平台访问和操作它。在 sg-6000-vm02 上进行下载可以是以下几种情况: 1. 文件下载:我们可以通过在 sg-6000-vm02 上运行下载软件或者通过命令行下载文件。可以使用专门的下载管理工具,如 wget 或 curl,也可以通过网页浏览器进行下载操作。 2. 软件下载:在 sg-6000-vm02 上下载软件可以是为了安装新的应用程序或者更新已经存在的软件。我们可以通过运行软件的安装脚本或者下载软件包来进行软件的安装和更新。 3. 数据下载:在 sg-6000-vm02 上下载数据可以是为了备份重要的数据文件或者从其他服务器或平台上获取所需的数据。可以使用特定的数据传输协议,如 FTP、SCP、SFTP 等来完成数据下载的操作。 无论是哪种类型的下载,我们需要确保 sg-6000-vm02 有足够的存储空间来存储下载的文件或数据,同时也需要考虑网络带宽和连接速度的因素,以确保下载的效率和稳定性。另外,在下载过程中可能需要进行安全验证或者进行权限设置,以确保下载是合法和安全的。 ### 回答2: sg-6000-vm02 下载是指从sg-6000-vm02这台虚拟机上进行文件下载的意思。 要进行sg-6000-vm02 下载,首先需要确保这台虚拟机处于开启状态,并且能够连接到互联网。然后,我们可以通过以下步骤进行下载操作: 1. 打开你的本地电脑或者其他可以访问互联网的设备,确保你与sg-6000-vm02虚拟机在同一个网络环境中。 2. 在电脑的浏览器中输入sg-6000-vm02的IP地址或者主机名,并按下回车键,访问该虚拟机的管理界面。 3. 在管理界面中,查找并登录到sg-6000-vm02虚拟机的操作系统。 4. 在操作系统中打开文件资源管理器或者终端,并定位到你想要下载的文件所在的目录。 5. 使用合适的命令或者工具,如wget、curl或者浏览器下载功能,从该目录中下载文件。可以使用绝对路径或者相对路径指定要下载的文件。 6. 等待下载完成,下载进度会显示在下载工具或者浏览器的界面上。 7. 下载完成后,你可以在设备的本地目录中找到该文件。 通过以上步骤,你可以在sg-6000-vm02虚拟机中进行文件下载。请确保你有合法的访问权限,并遵守相关网络和计算机使用规定。 ### 回答3: sg-6000-vm02 是一个虚拟机的名称或编号,下载可能指的是从某个地方获取虚拟机的软件或镜像文件。 要下载 sg-6000-vm02,你需要找到提供该虚拟机的来源或资源。通常情况下,你可以通过以下方式来下载虚拟机: 1. 官方网站:如果 sg-6000-vm02 是某个软件或服务的官方虚拟机,你可以访问其官方网站并在下载页面找到相关的文件。 2. 第三方资源库:有些网站或平台提供了各种虚拟机的镜像文件供用户下载。你可以使用搜索引擎查找这些资源库,并在其中搜索 sg-6000-vm02。 3. 云服务提供商:如果 sg-6000-vm02 是某个云服务提供商提供的虚拟机,你可以登录该云平台的控制面板,在相关的镜像库或市场中搜索并选择下载 sg-6000-vm02。 4. 其他来源:除了上述方式,你还可以从其他论坛、社区或网络上搜索 sg-6000-vm02 的下载资源。但请注意确保来源可靠,以免下载到恶意软件或侵权文件。 在下载 sg-6000-vm02 前,你也需要确认你拥有适当的硬件和软件环境来运行该虚拟机。另外,虚拟机的使用也可能需要一定的技术知识和配置。如果你不确定如何使用或配置 sg-6000-vm02,建议查找相关的文档、指南或向社区寻求帮助。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Danny Leo

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值