HarmonyOS 鸿蒙Next中APP如何读取NFC模块里的数据
HarmonyOS 鸿蒙Next中APP如何读取NFC模块里的数据 我想要读取小熊派nano开发版上NFC模块的数据,但是官方示例并不能读取写入NFC模块的数据,只能读取到NFC的一些基本信息。求大佬解答。



更多关于HarmonyOS 鸿蒙Next中APP如何读取NFC模块里的数据的实战教程也可以访问 https://www.itying.com/category-93-b0.html
NFC这块开发用到的不多,建议直接提交工单咨询鸿蒙技术专家。
更多关于HarmonyOS 鸿蒙Next中APP如何读取NFC模块里的数据的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html
好的,
可以读,但先说最关键的结论:
先说结论
你现在“只能读到 NFC 的基本信息,读不到自己写进去的数据”,通常不是 HarmonyOS 读不到,而是下面 3 种情况之一:
- 开发板那边并不是“标签/卡模拟模式” 手机当读卡器时,对端也必须像“卡”一样响应;如果小熊派上的 NFC 模块本身跑的是
读卡器模式,那两边就对不上,APP 不可能直接把它当标签读。 - 你写进去的数据不是标准可读格式 比如 MCU 只是把数据写进了芯片某些页/块里,但没有按 NDEF 格式组织,那官方示例自然只能拿到
UID / technology / ATQA / SAK这类基础信息,读不到业务数据。 - 你用错了技术类型接口 HarmonyOS 官方
tag能力不只是读基础信息,还支持按技术类型拿对象后真正读写:NDEF / IsoDep / MifareClassic / MifareUltralight / NfcA...这点官方文档是支持的,不是能力缺失。NFC 标签读写开发指南 / [@ohos.nfc.tag](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/js-apis-nfctag) / nfctech
你要先判断一件事
你的“小熊派 NFC 模块”到底是哪种工作方式?
手机读开发板,开发板至少要满足下面之一:
- 标准 NDEF 标签 最容易做,手机侧直接
getNdef()+readNdef()。 - ISO-DEP 卡模拟 手机侧用
getIsoDep()+transmit()发 APDU,开发板按 APDU 协议回数据。 - Mifare Ultralight / Classic 手机侧按页/块读,开发板那边写数据时也要按对应芯片存储结构来。
如果开发板只是普通 NFC 读写芯片的Reader/Writer模式,那手机不能直接把它当标签读。
为什么官方示例只能读“基本信息”
因为很多示例停在这一步:
tag.getTagInfo(want)或readerMode回调里拿到tagInfo- 打印
uid - 打印
technology
这一步只是在判断“这是什么卡”,还没真的去读卡里的数据。
真正读数据要继续往下走:
tagInfo.technology里有tag.NDEF->tag.getNdef(tagInfo)->readNdef()- 有
tag.ISO_DEP->tag.getIsoDep(tagInfo)->transmit(cmd) - 有
tag.MIFARE_ULTRALIGHT->tag.getMifareUltralight(tagInfo)-> 读页 - 有
tag.MIFARE_CLASSIC-> 认证扇区后读块
最推荐你的方案
方案 A:把开发板写成标准 NDEF 标签
如果你的目标只是“手机 APP 读取开发板写入的一段文本/JSON/URL/设备信息”,最稳的是让开发板那边写 NDEF。
这样 HarmonyOS 侧就最简单。
HarmonyOS 侧最小思路
先在 module.json5 里加权限:
"requestPermissions": [
{
"name": "ohos.permission.NFC_TAG",
"reason": "$string:app_name"
}
]
前台读卡时,用官方推荐的 reader mode 注册。开发指南
核心读取逻辑可以这样写:
import { tag } from '@kit.ConnectivityKit';
function hasTech(tagInfo: tag.TagInfo, tech: number): boolean {
for (let i = 0; i < tagInfo.technology.length; i++) {
if (tagInfo.technology[i] === tech) {
return true;
}
}
return false;
}
async function readTagData(tagInfo: tag.TagInfo) {
if (hasTech(tagInfo, tag.NDEF)) {
const ndef = tag.getNdef(tagInfo)
// 不同版本文档里连接方法名可能有差异,以你当前 SDK 自动提示为准
if ((ndef as any).isTagConnected && !(ndef as any).isTagConnected()) {
;(ndef as any).connectTag()
} else if ((ndef as any).isConnected && !(ndef as any).isConnected()) {
;(ndef as any).connect()
}
const msg = await ndef.readNdef()
const records = msg.getNdefRecords()
for (let i = 0; i < records.length; i++) {
const record = records[i]
console.info(`record[${i}] tnf=${record.tnf}, type=${JSON.stringify(record.type)}, payload=${JSON.stringify(record.payload)}`)
}
return
}
if (hasTech(tagInfo, tag.ISO_DEP)) {
const isoDep = tag.getIsoDep(tagInfo)
if ((isoDep as any).isTagConnected && !(isoDep as any).isTagConnected()) {
;(isoDep as any).connectTag()
} else if ((isoDep as any).isConnected && !(isoDep as any).isConnected()) {
;(isoDep as any).connect()
}
// 这里必须换成你开发板固件支持的 APDU
const cmd = [0x00, 0xA4, 0x04, 0x00]
const resp = await isoDep.transmit(cmd)
console.info(`isoDep resp=${JSON.stringify(resp)}`)
return
}
if (hasTech(tagInfo, tag.MIFARE_ULTRALIGHT)) {
const ul = tag.getMifareUltralight(tagInfo)
if ((ul as any).isTagConnected && !(ul as any).isTagConnected()) {
;(ul as any).connectTag()
} else if ((ul as any).isConnected && !(ul as any).isConnected()) {
;(ul as any).connect()
}
// 读第 4 页开始的内容,实际页号按你的芯片布局改
const data = await ul.readMultiplePages(4)
console.info(`ultralight data=${JSON.stringify(data)}`)
return
}
console.info(`未匹配到可读技术类型: ${JSON.stringify(tagInfo.technology)}`)
}
如果你写的是文本,怎么判断是不是 NDEF
如果开发板那边写入的是标准 NDEF Text Record,那么 readNdef() 读出来后,payload 不是直接纯文本,它有一个 NDEF Text 的头。
常见解析思路是:
payload[0]:状态字节- 低 6 位:语言码长度
- 后面才是真正文本字节
也就是说,如果你读到了 payload,但看起来像乱码,很多时候不是没读到,而是你没按 NDEF Text Record 格式解析。
如果你不是 NDEF,而是“自定义写入”
那就别走 NdefTag.readNdef(),而要看你开发板实际是什么协议:
1. 如果是 ISO-DEP
手机侧要发 APDU:
const isoDep = tag.getIsoDep(tagInfo)
const resp = await isoDep.transmit([/* 你的 APDU 指令 */])
这时候“读什么数据”完全取决于你 MCU 固件怎么定义命令。
2. 如果是 Mifare Ultralight
手机侧按页读:
const ul = tag.getMifareUltralight(tagInfo)
const pageData = await ul.readMultiplePages(4)
如果你开发板只是把字符串原样写进某几页,那 APP 侧就自己按页解析。
3. 如果是 Mifare Classic
先认证扇区,再按块读;如果没做 key 认证,会读不到。
小熊派这类板子最常见的坑
坑 1:开发板写了数据,但手机侧读法不匹配
比如:
- 板子写的是 Ultralight 页数据
- 你手机侧却在等 NDEF
那结果就是只能看到基础信息,业务数据读不到。
坑 2:板子不是“标签模式”
如果 NFC 模块没有进入 tag/card emulation,而是在 reader 模式,手机侧就没法按标签去读。
坑 3:数据确实写进去了,但写在错误地址
比如 NDEF 标签需要:
- Capability Container
- TLV
- NDEF Message
如果只是裸写 payload 到用户区,readNdef() 也读不出来。
你现在最该做的排查顺序
按这个顺序查,效率最高:
- 先打印
tagInfo.technology看手机识别出来的是NDEF、ISO_DEP、MIFARE_ULTRALIGHT还是别的。 - 确认开发板 NFC 芯片型号 比如是
NTAG、Mifare Ultralight、PN532 卡模拟、ISO14443-4之类。 - 确认开发板当前工作模式 是
Tag/Card Emulation还是Reader/Writer。 - 确认你写入的数据格式 是标准
NDEF,还是自定义块数据,还是 APDU 应答。 - HarmonyOS 侧改成匹配的读取接口 不要只停留在
getTagInfo()。
最实用的建议
如果你只是想“手机读出小熊派里的一段业务数据”,我建议你直接定这个方案:
- 开发板端:写标准 NDEF Text/URI/自定义 MIME
- HarmonyOS 端:
getNdef()+readNdef()
这是最省事、最兼容、最不容易踩坑的方案。
如果你非要走自定义协议,那就:
- 开发板端做
ISO-DEP 卡模拟 - HarmonyOS 端走
IsoDepTag.transmit()
你这个问题的本质
不是“鸿蒙 APP 能不能读 NFC 模块数据”,而是:
- 对端有没有把自己暴露成标准 NFC 标签
- 你有没有用对应技术类型去读
这两个对上了,就能读。
小熊派 BearPi-HM Nano 板载的是 NT3H1101/NT3H1201,属于 NFC Forum Type 2 Tag(NDEF 格式),可以存文本、URL 等用户数据。
你用鸿蒙手机 App 读它时,官方示例默认只读 Tag 基础信息(ID、技术类型),不会自动解析 NDEF 内容,所以你感觉 “只能读到基本信息,读不到写入的数据。要读到你写入的数据,必须在 App 里主动解析 NDEF 消息”
建议直接提交工单至华为技术支持
NFC 读到“基本信息”和读到“业务数据”是两层能力。TagInfo 只能告诉你这个标签支持哪些技术类型,例如 NDEF、NfcA、NfcV、IsoDep、MifareUltralight 等;真正的数据要按标签技术类型继续读。如果写入的是 NDEF 数据,优先用 NdefTag,连接后调用 readNdef()/getNdefMessage() 再解析 NdefRecord。如果小熊派 Nano 的模块实际是动态 NFC Tag 或 ISO15693 一类标签,可能不是标准 NDEF,这时就需要根据 TagInfo 里支持的 tech 选择 NfcV/NfcA/IsoDep 等对象,再按芯片手册发送读块/APDU 命令。建议先把 TagInfo.supportedProfiles 打印出来,再确认写入端写的是 NDEF 还是芯片私有块数据。
在HarmonyOS NEXT中,APP通过NFC模块读取数据需使用ArkTS调用**@ohos.nfc或@ohos.nfc.tag**等系统API。获取Tag对象后,调用getNdefMessage()等方法解析NDEF数据。需在module.json5中声明ohos.permission.NFC权限,并确保设备支持NFC。
在 HarmonyOS Next 中读取 NFC 模块(如小熊派 Nano 的板载模块)的数据,关键是要区分标签类型并使用对应的会话接口。官方示例通常只展示了获取 TagInfo 基本信息,但没有进入具体的标签数据读取会话。
核心读取步骤:
- 通过
tag.on('tagStateChange')或 NFC 前台调度获取TagInfo对象。 - 根据
tagInfo.technology判断类型(常见如NFC_A、NDEF、MIFARE_CLASSIC)。 - 创建对应的
TagSession(如NdefTag、MifareClassicTag),连接并读取数据。
若模块写入的是 NDEF 格式数据,示例代码如下:
import { tag } from '@kit.ConnectivityKit';
import { BusinessError } from '@kit.BasicServicesKit';
function handleTag(tagInfo: tag.TagInfo): void {
if (tagInfo.technology.includes(tag.NFC_NDEF)) {
let ndefTag = tag.ndef(tagInfo);
ndefTag.connect().then(() => {
return ndefTag.read();
}).then((ndefMsg) => {
for (let record of ndefMsg.ndefRecords) {
let payload = record.payload; // 实际数据
console.info('NDEF payload:', arrayBufferToString(payload));
}
}).catch((err: BusinessError) => {
console.error('NDEF read error:', err);
}).finally(() => {
ndefTag.close();
});
}
}
function arrayBufferToString(buffer: ArrayBuffer): string {
return new TextDecoder().decode(new Uint8Array(buffer));
}
若模块被识别为 MIFARE Classic 卡,需用密钥认证后读块:
if (tagInfo.technology.includes(tag.MIFARE_CLASSIC)) {
let mfTag = tag.mifareClassic(tagInfo);
mfTag.connect().then(() => {
// 默认密钥全FF或A0A1A2A3A4A5,需对照实际写入密钥
return mfTag.authenticateSectorUseKeyA(1, new Uint8Array([0xFF,0xFF,0xFF,0xFF,0xFF,0xFF]));
}).then(() => {
return mfTag.readBlock(4); // 假设数据在扇区1的块4
}).then((blockData) => {
console.info('Block data:', arrayBufferToString(blockData));
}).catch((err: BusinessError) => {
console.error('MIFARE read error:', err);
}).finally(() => {
mfTag.close();
});
}
如果模块只支持原始 NFC-A 命令,则用 TRANSCEIVE 直接发送 APDU:
let nfcA = tag.nfcA(tagInfo);
nfcA.connect();
let rawRsp = nfcA.transceive(new Uint8Array([0x00,0xB0,0x00,0x00,0x10])); // 示例读取二进制数据
注意:务必在 module.json5 中声明 ohos.permission.NFC_TAG 权限且为系统应用或已申请对应权限。标签会话使用后要调用 close()。如果读取不到数据,请确认标签中数据格式与读取方式匹配(NDEF 或透明传输块)。

