本文主要是以「飞书」小程序为准,兼容「微信」小程序,如果没有了解过「飞书」的同学,可以点击此处去官网了解

什么是 NFC

近距离无线通信(英语:Near-field communication,NFC),又简称近距离通信近场通信,是一套通信协议,让两个电子设备(其中一个通常是移动设备,例如智能手机)在相距几厘米之内进行通信。

近场通信技术由非接触式射频识别(RFID)演变而来,由飞利浦半导体(现恩智浦半导体,缩写 NXP)、诺基亚和索尼共同于2004年研制开发,其基础是RFID及互连技术。近场通信是一种短距高频的无线电技术,在13.56MHz频率运行于20厘米距离内。其传输速度有106 Kbit/秒、212 Kbit/秒或者424 Kbit/秒三种。

NFC 其实在刚诞生的时候我就一直在关注,但是不仅仅应用少,而且搭载的设备也少,甚至小米还出现过前一代搭载 NFC 后一代却不搭载的神奇情况。除此之外,使用起来也是特别复杂,想当初,要用 NFC 去实现刷公交卡,你需要去换一个特殊的 SIM 卡才能够支持(当初不理解,现在想来大概率是因为安全问题)。

在现在,随着安卓厂商的不懈努力,现在不论是应用还是设备的安装率都已经逐渐普及开来。从最初 NFC 也就能在支付宝中扫银行卡快速输入卡号,到现在的公交刷卡、X Pay,甚至传输文件,华为甚至给这个东西换了个名字叫做一碰系统(率感无语)。

除此之外,还能推进智能化的发展。比如以后家庭中加入了一个新设备,那么不再需要繁杂的联网过程,直接扫一下机器身上的 NFC 识别码就可以直接将设备加入到家庭网络中。或者说华为路由器上的一碰连接 Wifi 我觉得就是一个极好的应用。当家里来客人的时候,就不再需要一个人一个人的输入密码了。

NFC 技术一览

运行模式

NFC 现在主要有三种运行模式,分别是卡模拟模式(Card Emulation Mode)、主机模拟模式(Host Emulation Mode)、读卡写卡模式(Reader/Writer Mode)、P2P 模式(P2P Mode)

卡模拟模式

  • NFC手机可以模拟成为一张非接触卡,通过 POS 机(非接触读卡器)的 RF 来供电,即使 NFC 手机没电也可以工作。

  • 现在很常见的比如 Apple Pay,BYD NFC 钥匙,都是能够实现在断电情况下的刷卡

主机模拟模式

  • 该模式与卡模拟模式很类似,只不过卡模拟无需供电或者说无需 App 的参与就可以完成,但是主机模拟模式是不行的,他是通过将所有的消息转发给应用,由应用去决定该模拟什么内容,也就说该返回什么内容

  • 现阶段很多支付钱包,比如云闪付、京东闪付等等都是通过该模式实现的。

读卡写卡模式

  • NFC手机可以通过触碰NFC标签(Tag),从中读取非接触标签中的内容,采集数据并发送到对应的应用进行处理。

  • 最常见的应用其实就是华为的一碰系列,除此之外,支付宝支持直接读取信用卡、储蓄卡的卡号。

P2P 模式

  • 两个NFC设备可以近距离内互相连接,直接传递数据,实现点对点数据传输。

  • 例如协助快速建立蓝牙连接、交换手机名片和数据通信等。

  • 最常见的是手机互传、Android Beam。

协议标准类型

因为 NFC 的发展过程的原因,曾经出现过多个协议,甚至每家公司都有不同的协议内容。但现在主要是有一下几个协议标准:

ISO / IEC

主要定义了一下几个协议:

  • ISO/IEC 18092 / ECMA-340— (NFCIP-1)

    Near Field Communication Interface and Protocol-1

  • ISO/IEC 21481 / ECMA-352— (NFCIP-2)

    Near Field Communication Interface and Protocol-2

除此以外,还有一个协议标准很常用,是 ISO-14443 协议,其实这个协议是 RFID 的协议,和上面的唯一区别就是上面的多了一些其他模式的标准,比如点对点模式。

ISO-14443 协议有两个子类,分别是 Type-A 和 Type-B,这两个在 Android 也被称为 NFC-A 和 NFC-B。

但不幸的是,这些协议也不能免费看,要花钱的

NFC Forum

NFC Forum 是一个在 2004 年创建的非盈利行业协会,其成员来自NFC生态系统的各个部分。另外我主要关注了下国内公司,比较知名的有小米、中国移动通讯。

但是你想要从该组织获取任何关于 NFC 相关的技术标准,首先你的公司要成为该组织的成员才行,因为字节根本不在该组织,所以没法从这里获得一手的信息。

不过办法也是有的,该协会的创办者 NXP 公司网站上是有相关的数据资源,后文的参考此资源。

其他

不用管.jpg

沟通协议

以 NFC-A 为例

整个 NFC 卡片其实内部就是一个有限状态机,根据当前不同的状态需要不同的操作。这个图看起来很复杂,其实主要额外包含了两个操作:

  • 密码校验

    • 这个是说 NFC 卡是经过加密的,只有在密码校验通过之后,才能够进行相关的操作。

    • 有一点特殊的是,NFC 的密码长度其实是固定的,即 32 位,4 个字节。

  • 防冲突

    • 之所有有这个设计,是因为在使用过程中,可能会出现同时扫描到多个 NFC 设备的情况,此时就需要通过 READY1/READY2 两个状态来选择正确的 NFC 设备进行操作。

    • 每个卡片都有一个唯一 UID,长度为 7 字节,而每次操作只能选择 4 个字节,所以不得不拆分成两个状态两步去操作。

当没有上面两个操作的时候,可以简单的执行 IDLE -> ACTIVE -> HALT 的状态流程,也就是说连接、操作(也就是读写)、关闭。

存储设计

NFC 在存储上设计了页的概念,一个页表示 4 个字节,以页为最小单位进行操作。所以 NFC 卡片的存储容量其实都是 4 的整数倍。

这里以 NTAG213 180 字节的存储结构为例

这里只需要关注两点:

  • 用户数据存储的空间是从第四页开始

  • 用户可存储空间其实只有 144 字节

只需要记住这两点,在开发 NFC 需求的时候,不要去修改非用户空间的数据,不要存储过长的内容。

设备准备

在有了上面的基础之后,别急你还是不能开始开发 NFC,因为你还缺少至关重要的一个东西,设备

遗憾的是,不是所有的设备都有 NFC 硬件的,也不是说有了 NFC 硬件就能用的

  • 苹果设备只有升级到 iOS 13 以上才能具有开发 NFC 读卡器的能力,不能写入,除此之外,几乎没有其他的 NFC 能力可以使用。机圈也会叫做阉割版 NFC。(暂时没有能力去调研 NFC 的能力一定是需要硬件支持还是说只是软件限制)

  • 安卓设备理论上可以使用几乎所有的 NFC 能力,机圈内叫全功能 NFC,包括读卡、写卡、卡模拟、P2P 等模式。但是不同的手机有着不同的操作系统的限制,所以要选择一个合适的操作系统(原生安卓、类原生安卓是最推荐的)。

所以,请准备好一台安卓手机!

NFC 卡片

在有了设备之后,还要选择正确的 NFC 卡片,因为不是任意一个 NFC 卡片都是可以用的,比如工牌、银行卡等等。这是因为 NFC 卡片是带有加密的,在操作之前必须要通过验证才能操作,所以建议去淘宝买一些可读可写无加密的 NFC 贴纸用于测试。

不过你在淘宝上买的可能是写着 NTAG213 的型号,其实这个是 NXP 出的一款设备,但是支持兼容 ISO NFC-A 协议以及 NFC Forum Type 2 协议,所以大家可以放心使用。

当然了,你也可能看到 NTAG215/NTAG216,这两个都没有任何区别,只是存储空间不同而已。

小程序 API

此时在有了上述的基础知识后,别急,还要了解下小程序的 API 才能更好的开发。

就目前来说,所有与 NFC 相关的操作都被封装到了 NfcAdaptar 类中,通过 tt.getNFCAdaptar() 获取 nfcAfaptar 对象。

具体的 API 参数细节请参考「飞书开放平台

NFC 整体流程

  • 注册 NFC 发现事件回调 nfcAdaptar.onDiscovered

  • 开启 NFC 扫描 nfcAdaptar.startDiscovered

  • NFC 卡片贴近设备

  • 触发回调,通过回调可以获得 NFC 支持的协议

    • 回调参数内的 techs 字段可以用于判断当前卡片支持的协议
  • 根据协议去读写 NFC 卡片内容 nfcAdaptar.getNfcA()

  • 关闭 NFC 扫描,关闭事件监听 nfcAdaptar.offDiscovered / nfcAdaptar.stopDiscovered()

可以发现这个流程非常容易理解,也非常容易操作。那么接下来我们看下重头戏

读写 NFC 卡片

这里以 NFC-A 协议为主

通过 nfcAdaptar.getNfcA() 获取操作 NFC-A 卡片的操作类实例 nfca,流程如下

  • 连接卡片 nfca.connect()

  • 读写卡片 nfca.transceive()

  • 读写完成之后关闭连接 nfca.close()

关键点来了,NFC 卡片的读写不和其他的 IO 设备类似,有专门的 read 和 write 函数。对 NFC 来说,通过给 NFC 卡片发送不同的指令来做到完成不用的操作。

这些指令都在 NTAG213 文档中有写,这里简单列一下常用的数据

命令 代码 功能 参数
Read 0x30 一次读取四个页的数据 <Addr:1B>
Write 0xA2 一次写入一个页的数据 <Addr:1B> <Data:4B>
Fast Read 0x3A 一次读取多个页的数据 <StartAddr:1B> <StopAddr:1B>

比如我要读取第四页的数据,可以写如下代码

1
2
3
4
5
6
7
nfca.transceive({
data: new Uint8Array([0x30, 0x04]).buffer, // 必须要传入 ArrayBuffer
success: (res) => {
// res.data 是 ArrayBuffer,转成数组方便查看
console.log(Array.from(new Uint8Array(res.data))
}
})

根据协议,其实还要传 CRC:2B,也就是校验位,不过这个操作已经由 Android 去做掉了,所以就不需要传了,也不需要去了解校验算法

结语

至此,NFC 开发算是入门了,不过这里要注意不同的 NFC 卡片不同的协议会有不同的读写方式,这里要根据你们各自具体的卡片来看。而且有的还有密码保护,还需要额外再走校验的逻辑。

Refs