HarmonyOS 鸿蒙Next中APP如何读取NFC模块里的数据

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


更多关于HarmonyOS 鸿蒙Next中APP如何读取NFC模块里的数据的实战教程也可以访问 https://www.itying.com/category-93-b0.html

8 回复

NFC这块开发用到的不多,建议直接提交工单咨询鸿蒙技术专家。

更多关于HarmonyOS 鸿蒙Next中APP如何读取NFC模块里的数据的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html


好的,

可以读,但先说最关键的结论:

先说结论

你现在“只能读到 NFC 的基本信息,读不到自己写进去的数据”,通常不是 HarmonyOS 读不到,而是下面 3 种情况之一:

  1. 开发板那边并不是“标签/卡模拟模式” 手机当读卡器时,对端也必须像“卡”一样响应;如果小熊派上的 NFC 模块本身跑的是读卡器模式,那两边就对不上,APP 不可能直接把它当标签读。
  2. 你写进去的数据不是标准可读格式 比如 MCU 只是把数据写进了芯片某些页/块里,但没有按 NDEF 格式组织,那官方示例自然只能拿到 UID / technology / ATQA / SAK 这类基础信息,读不到业务数据。
  3. 你用错了技术类型接口 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 模块”到底是哪种工作方式?

手机读开发板,开发板至少要满足下面之一:

  1. 标准 NDEF 标签 最容易做,手机侧直接 getNdef() + readNdef()
  2. ISO-DEP 卡模拟 手机侧用 getIsoDep() + transmit() 发 APDU,开发板按 APDU 协议回数据。
  3. 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() 也读不出来。

你现在最该做的排查顺序

按这个顺序查,效率最高:

  1. 先打印 tagInfo.technology 看手机识别出来的是 NDEFISO_DEPMIFARE_ULTRALIGHT 还是别的。
  2. 确认开发板 NFC 芯片型号 比如是 NTAGMifare UltralightPN532 卡模拟ISO14443-4 之类。
  3. 确认开发板当前工作模式Tag/Card Emulation 还是 Reader/Writer
  4. 确认你写入的数据格式 是标准 NDEF,还是自定义块数据,还是 APDU 应答。
  5. 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 基本信息,但没有进入具体的标签数据读取会话。

核心读取步骤:

  1. 通过 tag.on('tagStateChange') 或 NFC 前台调度获取 TagInfo 对象。
  2. 根据 tagInfo.technology 判断类型(常见如 NFC_ANDEFMIFARE_CLASSIC)。
  3. 创建对应的 TagSession(如 NdefTagMifareClassicTag),连接并读取数据。

若模块写入的是 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 或透明传输块)。

回到顶部