NDEF简介
NDEF
(NFC Data Exchange Format)是一种标准化的数据格式,用于将数据存储在NFC标签或智能手机中。该格式是NFC论坛
定义的,目的是在不同的NFC设备之间交换信息。 NDEF格式可以存储各种类型的数据,例如URL、文本、电话号码和地理位置等。 NFC设备可以读取和写入NDEF格式的数据,这使得NFC成为了在无线场景中实现简单、快捷数据传输的理想选择。 除了标准的NDEF格式外,还有其他类似的格式,例如NFC Forum Type 1-5格式和MIFARE格式,它们也可以用于存储和交换数据。NDEF标准的出现,使得不同类型的NFC设备之间的数据交换更加简单,也为应用开发者提供了更加简单、可靠的NFC数据传输方式。
NFC Forum Type 1-5
格式是由NFC Forum组织定义的一组标准化的标签数据格式。与NDEF格式相似,这些格式可以用于存储各种类型的数据,并可以在不同类型的NFC设备之间交换信息。不同的NFC Forum Type格式具有不同的存储容量和数据传输速度,但它们都采用了类似的数据格式和协议。
MIFARE
格式是一种由NXP Semiconductors公司开发的标签数据格式。与NFC Forum Type和NDEF格式不同,MIFARE格式通常用于存储和传输经过安全加密的数据,例如支付信息和门禁认证信息等。MIFARE标签还具备访问控制和安全管理功能,能够保护存储在标签中的敏感信息不被未经授权的第三方访问和篡改。
arduino平台源码
源码:https://github.com/461911662/NDEF
模块关系梳理
demo学习
1.CleanTag
描述:清除标签到工厂模式。
主要流程:
1.初始化串口、SPI、MFRC522、NFC适配器
2.循坏查找,寻选卡片后,清除标签到工厂模式
关键函数:
NfcAdapter nfc = NfcAdapter(&mfrc522)
// 使用MFRC522构造一个nfc适配器,因为NFC功能是基于MFRC522模块实现的。
// nfc适配器的内部函数如下:
NfcAdapter::NfcAdapter(MFRC522 *interface)
{
// NFC适配器最终使用shield去访问NFC卡片
shield = interface;
}
nfc.tagPresent()
// tagPresent 是对寻卡和选卡的封装
bool NfcAdapter::tagPresent()
{
// If tag has already been authenticated nothing else will work until we stop crypto (shouldn't hurt)
shield->PCD_StopCrypto1();
if(!(shield->PICC_IsNewCardPresent() && shield->PICC_ReadCardSerial()))
{
return false;
}
MFRC522::PICC_Type piccType = shield->PICC_GetType(shield->uid.sak);
return ((piccType == MFRC522::PICC_TYPE_MIFARE_1K) || (piccType == MFRC522::PICC_TYPE_MIFARE_UL));
}
bool success = nfc.clean()
// clean原理是构造一个MifareClassic实例,分别将数据块和控制块格式化成指定格式
byte emptyBlock[16] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
byte authBlock[16] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x07, 0x80, 0x69, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
2.EraseTag
描述:擦除tag就是写一个空的NDEF消息。
主要流程:
1.初始化串口、SPI、MFRC522、NFC适配器
2.循坏查找,寻选卡片后,对卡片进行擦除
关键函数:
bool success = nfc.erase()
// 该函数实现了对nfc卡片信息的擦除功能。
// 主要有以下流程:
// 1.构造NdefMessage实例
// 2.向NdefMessage实例中添加一个空的NdefRecord
// 3.写NdefMessage打开卡片
bool NfcAdapter::erase()
{
NdefMessage message = NdefMessage();
message.addEmptyRecord();
return write(message);
}
// write函数最终会构造MifareClassic实例去写NDEF消息
MifareClassic mifareClassic = MifareClassic(shield);
return mifareClassic.write(ndefMessage);
// 1.MifareClassic实例首先会将NDEF消息进行编码成字节流
// 2.NDEF消息实例会遍历自己的record,然后调用NdefRecord::encode去编码字节流
// 3.然后以TLV+0xFE数据封装
// 4.最后将封装的数据依次写道卡片的数据块中
3.FormatTag
描述:将一个Mifare卡格式化成一个NDEF卡。
主要流程:
1.初始化串口、SPI、MFRC522、NFC适配器
2.循坏查找,寻选卡片后,对卡片进行格式化
关键函数:
bool success = nfc.format()
// 格式化的实现也是构造MifareClassic实例,通过MifareClassic来对卡进行格式化
MifareClassic mifareClassic = MifareClassic(shield)
mifareClassic.formatNDEF()
// 1.将第一个扇区的block1、block2、block3设置成如下数据
byte blockbuffer1[16] = {0x14, 0x01, 0x03, 0xE1, 0x03, 0xE1, 0x03, 0xE1, 0x03, 0xE1, 0x03, 0xE1, 0x03, 0xE1, 0x03, 0xE1};
byte blockbuffer2[16] = {0x03, 0xE1, 0x03, 0xE1, 0x03, 0xE1, 0x03, 0xE1, 0x03, 0xE1, 0x03, 0xE1, 0x03, 0xE1, 0x03, 0xE1};
byte blockbuffer3[16] = {0xA0, 0xA1, 0xA2, 0xA3, 0xA4, 0xA5, 0x78, 0x77, 0x88, 0xC1, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
// 2.其他扇区控制块设置为如下数据
byte blockbuffer4[16] = {0xD3, 0xF7, 0xD3, 0xF7, 0xD3, 0xF7, 0x7F, 0x07, 0x88, 0x40, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
// 3.数据块设置成空 empty NDEF TLV 03 00 FE
byte emptyNdefMesg[16] = {0x03, 0x03, 0xD0, 0x00, 0x00, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
4.ReadTag
描述:展示将 Mifare Classic 标签格式化为 NDEF 标签后的内容。
主要流程:
1.初始化串口、SPI、MFRC522、NFC适配器
2.循坏查找,寻选卡片后,读取卡片内容,然后使用串口打印出来
关键函数:
NfcTag tag = nfc.read()
// 读卡的实现也是构造MifareClassic实例,通过MifareClassic来读取卡标签的
// 读取卡片的过程就是对NDEF数据进行解码的过程
// 1.首先需要找到TLV的开始字节0x3,然后获取message的大小
// 2.然后循环从数据块中读取NDEF数据
// 3.最后构造NfcTag实例,准备对NDEF进行操作
tag.print()
// NfcTag调用自己的方法打印NDEF信息
// 接着会调用NdefMessage::print
// 最终调用NdefRecord::print打印record信息
5.ReadTagExtended
描述:这个例子是对ReadTag的扩展。
主要流程:
1.初始化串口、SPI、MFRC522、NFC适配器
2.循坏查找,寻选卡片后,读取卡片内容
3.判断当前卡片有NDEF消息
4.遍历打印NDEF消息的record内容
关键函数:
tag.hasNdefMessage()
boolean NfcTag::hasNdefMessage()
{
return (_ndefMessage != NULL);
}
NdefMessage message = tag.getNdefMessage()
NdefMessage NfcTag::getNdefMessage()
{
return *_ndefMessage;
}
int recordCount = message.getRecordCount()
uint8_t NdefMessage::getRecordCount()
{
return _recordCount;
}
6.WriteTag
描述:这个例子是对ReadTag的扩展。
主要流程:
1.初始化串口、SPI、MFRC522、NFC适配器
2.循坏查找,寻选卡片
3.构造NdefMessage实例
4.在NdefMessage实例中添加一个URL的record
5.将NdefMessage内容写到M卡片中
关键函数:
message.addUriRecord("https://arduino.cc")
void NdefMessage::addUriRecord(const char *uri)
{
NdefRecord r;
r.setTnf(NdefRecord::TNF_WELL_KNOWN);
uint8_t RTD_URI[] = { NdefRecord::RTD_URI };
r.setType(RTD_URI, sizeof(RTD_URI));
size_t uriLength = strlen(uri);
byte header[] = {0x00};
r.setPayload(header, sizeof(header), (byte *)uri, uriLength);
addRecord(r);
}
bool NdefMessage::addRecord(NdefRecord &record)
{
if (_recordCount < MAX_NDEF_RECORDS)
{
_records[_recordCount] = new NdefRecord(record);
_recordCount++;
return true;
}
...
}
7.WriteTagMultipleRecords
描述:这个例子是对ReadTag的扩展。
主要流程:
1.初始化串口、SPI、MFRC522、NFC适配器
2.循坏查找,寻选卡片
3.构造NdefMessage实例
4.在NdefMessage实例中添加一个URL的record
5.将NdefMessage内容写到M卡片中
关键函数:
void loop() {
if (nfc.tagPresent()) {
Serial.println("Writing multiple records to NFC tag");
NdefMessage message = NdefMessage();
message.addTextRecord("Hello, Arduino!");
message.addUriRecord("https://arduino.cc");
message.addTextRecord("Goodbye, Arduino!");
boolean success = nfc.write(message);
if (success) {
Serial.println("\tSuccess. Try reading this tag with your phone.");
delay(10000);
} else {
Serial.println("\tWrite failed");
}
}
delay(5000);
}
NFC协议
NDEF message包含多个record。一个record由record header
+record payload
组成。如下是1个record数据:
其中第一个字节最重要 包含 MB ME CF SR IL TNF:
2.1 MB(message begin)消息开始的地方 一般用于开头 且置1
2.2 ME(message end)消息结束的地方 一般用于结尾 且置1
2.3 CF(chunk flag)是否分块
2.4 SR(short record)如果设置该值,则上图的 pay length 只需要1个, 表示payload数据长度被限制在255个字节之内。
2.5 IL(id_length标志)表示 header 是否含有 id 和 id length 这两个字段
2.6 TNF(type name format )用于指明 payload 的类型 具体可见图
NFC资料打包