本实验将会实现一个通过串口发送命令读取信息或者写入特定信息到指定位置,我目前做的是实现对某药品名称、性状、用法用量等具体信息的存取,因此,此处我实现的是对药品名称进行读写,我在代码中定义了一个有关药品信息的结构体,读者可以通过在其中添加成员来实现添加信息项的目的,在此记录下来供以后参考,由于本人菜鸟一个且本实验还未成熟,大佬发现有错误烦请指正。
本实验基于MFRC522库进行开发,需要先加载此库文件。
恩智浦生产的一种工作在13.56MHz电磁场下的RFID读卡器,可以读取MIFARE 4K、MIFARE 1K、MIFARE Mini等卡片。
一种使用ISO 14443A接口的RFID卡片,例如:MIFARE、NTAG203。
MIFARE 经常使用的又有MIFARE 4K,MIFARE 1K和MIFARE Mini几种,由于我使用的时MIFARE 1K,因此我下面以MIFARE 1K为例进行介绍。
MIFARE 1K共有16个扇区,每个扇区4个块,每个块可以存储16个字节,因此似乎我们一共有16 x 4 x 16 Byte的存储空间,即1KB。然而事实并非如此,每个扇区中都固定最后一个块(称之为Sector Trailer)我们不能存储数据,而被用来存储整个扇区访问控制有关的数据。具体每个字段是什么意思我也不是也太清楚,可以查阅一下恩智浦公司相关产品的数据手册(但是我知道对每个块进行读写时需要使用Key B进行验证,验证通过后才能完成读写)Key B出厂时默认设置为FF FF FF FF FF FF。每个扇区中最后一个块的内字段定义如下:
Bytes 0-5 | Key A |
---|---|
Bytes 6-8 | Access Bits |
Bytes 9 | User data |
Bytes 10-15 | Key B (or user data) |
而对于扇区0来说,它的第0块存储的为该卡片的序列号等信息,因此我们也是只能读取不能写入用户新数据。因此MIFARE 1K实际可用容量为[(15 x 3)+2 ] x 16 = 752 B,其余MIFARE 4K 和 MIFARE Mini类似。因此,对于一个MIFARE 1K 我们实际可用的空间分布为:
Block0 | UID、Type、etc… |
---|---|
Block1 | UserData |
Block2 | UserData |
Block3 | Sector trailer |
Block4 | UserData |
Block5 | UserData |
Block6 | UserData |
Block7 | Sector trailer |
Block8 | UserData |
Block9 | UserData |
Block10 | UserData |
Block11 | Sector trailer |
Block12 | UserData |
Block13 | UserData |
Block14 | UserData |
Block15 | Sector trailer |
MFRC522 | Arduino UNO |
---|---|
RST | 9 |
SDA(SS) | 10 |
MOSI | 11 / ICSP-4 |
MISO | 12 / ICSP-1 |
SCK | 13 / ICSP-3 |
主程序运行过程:
/*
* Write and Read personal data of a MIFARE RFID card using a RFID-RC522 reader
* Uses MFRC522 - Library to use ARDUINO RFID MODULE KIT 13.56 MHZ WITH TAGS SPI W AND R BY COOQROBOT.
* -----------------------------------------------------------------------------------------
* MFRC522 Arduino Arduino Arduino Arduino Arduino
* Reader/PCD Uno/101 Mega Nano v3 Leonardo/Micro Pro Micro
* Signal Pin Pin Pin Pin Pin Pin
* -----------------------------------------------------------------------------------------
* RST/Reset RST 9 5 D9 RESET/ICSP-5 RST
* SPI SS SDA(SS) 10 53 D10 10 10
* SPI MOSI MOSI 11 / ICSP-4 51 D11 ICSP-4 16
* SPI MISO MISO 12 / ICSP-1 50 D12 ICSP-1 14
* SPI SCK SCK 13 / ICSP-3 52 D13 ICSP-3 15
*
* Hardware required:
* Arduino
* PCD (Proximity Coupling Device): NXP MFRC522 Contactless Reader IC
* PICC (Proximity Integrated Circuit Card): A card or tag using the ISO 14443A interface, eg Mifare or NTAG203.
* The reader can be found on eBay for around 5 dollars. Search for "mf-rc522" on ebay.com.
*/
#include <SPI.h>
#include <MFRC522.h>
typedef struct{
byte DrugName[48]; //药品名称
byte Dosage[12]; //用法用量
byte Remaining[4]; //剩余量
}DrugInfo;
#define RST_PIN 9 // Configurable, see typical pin layout above
#define SS_PIN 10 // Configurable, see typical pin layout above
MFRC522 mfrc522(SS_PIN, RST_PIN); // Create MFRC522 instance
MFRC522::MIFARE_Key key;
MFRC522::StatusCode status;
DrugInfo drugInfo; //实例化一个药品信息的结构体变量
byte block;
byte len;
const byte drugNameBlockAddr[3] = {1,2,4}; //药品名称的存储地址
void setup() {
Serial.begin(9600); // Initialize serial communications with the PC
SPI.begin(); // Init SPI bus
mfrc522.PCD_Init(); // Init MFRC522 card
for (byte i = 0; i < 6; i++) key.keyByte[i] = 0xFF; // Prepare key - all keys are set to FFFFFFFFFFFFh at chip delivery from the factory.
Serial.println(F("Write and Read personal data on a MIFARE PICC "));
}
void loop(){
char cmd = ' ';
byte count = 0;
while(Serial.read() >= 0); //清空在PICC卡读写期间的串口数据
Serial.println("please input a command(0:read; 1:write):");
while(Serial.available() <= 0); //等待指令输入
delay(10); //等待数据全部进入缓冲区
if(Serial.available() != 1)
{
Serial.println("error: Invalid command!");
while(Serial.read() >= 0);
return;
}
cmd = (char)Serial.read();
if(cmd != '0' && cmd != '1')
{
Serial.println("error: Invalid command!");
return;
}
/*###############################################################################################################################*/
if(cmd == '0')
{
//read a PICC card
Serial.println("Waiting for the appearance of the PICC card in one minute(read)...");
// Look for new cards
while ( ! mfrc522.PICC_IsNewCardPresent())
{
delay(1000);
if(count++>60)
{
Serial.println("Waiting timeout...");
count = 0;
return;
}
}
// Select one of the cards
if ( ! mfrc522.PICC_ReadCardSerial()) {
return;
}
Serial.println(F("**Card Detected:**"));
mfrc522.PICC_DumpDetailsToSerial(&(mfrc522.uid)); //dump some details about the card
//mfrc522.PICC_DumpToSerial(&(mfrc522.uid)); //uncomment this to see all blocks in hex
Serial.print(F("Drug Name: "));
len = 18;
for(uint8_t i = 0; i < sizeof(drugNameBlockAddr);i++)
{
status = mfrc522.PCD_Authenticate(MFRC522::PICC_CMD_MF_AUTH_KEY_A, drugNameBlockAddr[i], &key, &(mfrc522.uid)); //line 834 of MFRC522.cpp file
if (status != MFRC522::STATUS_OK) {
Serial.print(F("Authentication failed: "));
Serial.println(mfrc522.GetStatusCodeName(status));
return;
}
status = mfrc522.MIFARE_Read(drugNameBlockAddr[i], &drugInfo.DrugName[i*16], &len);
if (status != MFRC522::STATUS_OK) {
Serial.print(F("Reading failed: "));
Serial.println(mfrc522.GetStatusCodeName(status));
return;
}
}
//PRINT FIRST NAME
for (uint8_t i = 0; i < sizeof(drugInfo.DrugName); i++)
{
Serial.write(drugInfo.DrugName[i]);
}
Serial.print(" ");
Serial.println(F("\n**End Reading**\n"));
mfrc522.PICC_HaltA();
mfrc522.PCD_StopCrypto1();
}
/*###############################################################################################################################*/
else if(cmd == '1')
{
//write a PICC card
Serial.println("Waiting for the appearance of the PICC card in one minute(write)...");
// Look for new cards
while( ! mfrc522.PICC_IsNewCardPresent())
{
delay(1000);
count++;
if(count > 60)
{
count = 0;
Serial.println("Waiting timeout...");
return;
}
}
// Select one of the cards
if ( ! mfrc522.PICC_ReadCardSerial()) {
return;
}
Serial.print(F("Card UID:")); //Dump UID
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.print(F(" \nPICC type: ")); // Dump PICC type
MFRC522::PICC_Type piccType = mfrc522.PICC_GetType(mfrc522.uid.sak);
Serial.println(mfrc522.PICC_GetTypeName(piccType));
for(byte i = 0; i < sizeof(drugInfo.DrugName); i++) drugInfo.DrugName[i] = ' ';
while(Serial.read() >= 0);
Serial.println("please input drug name(Less than 48 characters):");
while(Serial.available()<=0)//等待输入
{
delay(1000);
count++;
if(count > 300)
{
Serial.println("No drug name entered");
count = 0;
return;
}
}
delay(10);//等待数据全部进入缓冲区
int length = Serial.available();
for(byte i = 0; i < length; i++)
{
drugInfo.DrugName[i] = Serial.read();
}
for(byte i = 0; i < sizeof(drugNameBlockAddr); i++)
{
status = mfrc522.PCD_Authenticate(MFRC522::PICC_CMD_MF_AUTH_KEY_A, drugNameBlockAddr[i], &key, &(mfrc522.uid));
if (status != MFRC522::STATUS_OK) {
Serial.print(F("PCD_Authenticate() failed: "));
Serial.println(mfrc522.GetStatusCodeName(status));
return;
}
else Serial.println(F("PCD_Authenticate() success: "));
// Write block
status = mfrc522.MIFARE_Write(drugNameBlockAddr[i], &drugInfo.DrugName[i*16], 16);
if (status != MFRC522::STATUS_OK) {
Serial.print(F("MIFARE_Write() failed: "));
Serial.println(mfrc522.GetStatusCodeName(status));
return;
}
else Serial.println(F("MIFARE_Write() success: "));
}
/*###############################################################################################################################*/
Serial.println(" ");
mfrc522.PICC_HaltA(); // Halt PICC
mfrc522.PCD_StopCrypto1(); // Stop encryption on PCD
}
}