开发目标:
(1) 对于 Android 手机, 直接通过微信小程序调用手机的 NFC 功能, 对 15693 协议的芯片进行读写操作;
(2)对于苹果手机(及没有 NFC 模块的手机), 通过微信小程序的蓝牙功能连接到蓝牙 / NFC 读写器, 然后通过蓝牙发送指令操作读写器对 15693 协议的芯片进行读写操作
DAY #1
上午开了半天会, 下午开始开发
先开发简单的: 直接通过 Android 手机的 NFC 模块读写芯片开发思路如下:
1. 首先调用 wx.getHCEState(OBJECT), 判断设备是否支持 NFC, 如果不支持就走蓝牙通道;
2. 调用 wx.startHCE(OBJECT) 初始化手机的 NFC 模块;
3. 初始化完成后, 调用 wx.onHCEMessage(CALLBACK) 监听芯片响应的消息;
4. 点击页面上的询卡按钮, 调用 wx.sendHCEMessage(OBJECT)发送询卡指令;
5. 这时 wx.onHCEMessage(CALLBACK) 应该可以收到带有 uid 信息的芯片响应数据;
6. 根据 uid 发送 select 指令, 以及后续多个指令;
7. 全部操作完成后之后, 调用 wx.stopHCE(OBJECT) 停止手机的 NFC 模块;
8. 完成
思路很清晰, 并且开发思路中需要用到的每一个 NFC 的接口都有对应文档, 应该是没有什么问题了, 接着就开始开发 NFC 模块了
第一步微信小程序基本框架搭建, 非常顺利的完成界面, 调试模式等
然后开始写 nfc 模块, 首先是 wx.getHCEState(OBJECT), 比较顺利, 同时测试了一下没有 NFC 模块的手机 / NFC 为开启等多种情况的返回值(因为小程序开发文档没有写啊)
然后是初始化 NFC 模块, 也比较顺利
然后完成 wx.onHCEMessage(CALLBACK)消息监听, 然后就是点击按钮发送消息 wx.sendHCEMessage(OBJECT), 从这时开始就开始进入无助的状态
尝试了多组数据进行发送, 始终不能触发 wx.onHCEMessage, 折腾了半个小时, 未果, 等第二天解决吧
DAY #2
首先仍然怀疑是数据格式不对, 因为第一次开发跟芯片相关的程序, 并且也是第一次接触 15693 协议, 所以一开始非常坚定的相信是自己组装的发送数据不对, 然后就去请教了芯片开发大牛, 以及把 15693 协议的相关部分读了大约 10 遍, 在网上找 15693 协议相关资料, 百度微信小程序 NFC 开发资料
还是不能触发 wx.onHCEMessage
直到在微信小程序开发社区, 输入关键字 wx.onHCEMessage, 然后从寥寥的几篇 post 中, 终于明白: TMD 微信小程序的 NFC 功能仅仅是把手机模拟成一张芯片卡, 而不是把手机当做芯片读写器! WTF!
这时已经下午 3 点
因为我一开始跟公司领导说的是微信小程序可以直接调 NFC 读取芯片, 这下就尴尬了, 对项目的规划影响巨大然后, 抱着一丝恐惧, 也抱着一丝希望, 多方搜索, 多方确认, TMD 微信小程序目前确实只能把手机模拟成一张芯片卡
然后跟公司领导电话里简单汇报了一下, 领导说第二天开会讨论
然后开始写蓝牙通道的解决方案
微信小程序开放的蓝牙接口如下:
共 18 个接口, 实在是太多了这里不得不吐槽一下微信小程序的架构师们, 你们设计的开发文档太粗糙了, 需要开发人员写大量的代码才能完成一些基本功能, 比如我自己想要完成的一些功能:
1. 初始化蓝牙模块;
2. 打开 / 关闭蓝牙发现;
3. 随时只允许单一设备连接, 不允许多设备连接;
4. 已连接上的蓝牙断开后自动重连, 自动重连失败后提示并继续自动重连;
5. 每次连上蓝牙设备后自动记住蓝牙设备 ID, 并写入一个长度为 5 的数组, 并写到 storage 里面, 这样下次进入小程序后就可以自动连接曾经连过的蓝牙设备
下面是我希望自己的程序可以这样调用蓝牙模块的伪代码:
- Page({
- onLoad: function() {
- ble.onMessageReceived = function(res) {
- };
- ble.onDeviceFound = function(device) {
- // 参数是 device 而不要 devices
- };
- ble.autoReconnect = true;
- ble.maxConnections = 1;
- ble.maxRememberedConnections = 5;
- ble.init(function() { //init 方法里面初始化蓝牙模块
- ble.autoConnectToRememberedDevice(function(res) {
- if (ble.connectedDevices.length == 0) {
- // 连接记住的设备失败, 跳转到蓝牙发现页面
- }
- });
- });
- },
- connectToNewDevice: function(newDeviceId) {
- // 由于设置了最大连接数为 1, 这时调这个方法就会主动断掉之前的连接
- // 这样就不需要先调 disconnect 了, 也不需要检查设备 ID / 状态是否不一致
- ble.connectToDevice(newDeviceId,
- function() {
- });
- }
- });
既然微信没有实现这个简便的 ble, 就只有我自己来实现了
当天下午剩下的一两个小时, 已经将这个 ble 写的差不多了, 等到第二天来调试
DAY #3
早上一来跟领导开会, 确认对于 Android 手机必须开发 APP 以避免使用蓝牙读写器, 小程序仍然要开发, 因为 iOS 系统始终需要读写器才能对芯片进行操作
开完会就开始进入蓝牙模块的调试
首先遇到的问题就是获取不到蓝牙设备的名称, 尝试从 advertiseData 解析, 尝试在网上找了很多资料, 未果看了论坛的很多帖子, 应该是小程序 API 还不能很好的兼容其他设备的, 也就是说对于这块功能还很不成熟
于是就只有暂时放弃解决蓝牙设备名称的问题
继续完善 ble 模块, 到下午下班的时候蓝牙连接模块已经比较成熟了, 上文提到的那些功能全部都已实现, 并且封装之后的 BluetoothManager 非常好用, 表现也很稳定, 体验很流畅, 跟 iOS 系统的 wifi 连接模块的体验差不多
DAY #4
早上一来就开始继续写蓝牙的读写功能这里不得不吐槽一下微信小程序的架构师们, 开发人员使用你们设计的接口, 就完成一个读写功能需要调好几个接口, 太复杂了下方是我写的 ble 开放的几个接口, 真希望微信小程序的架构师们也能开发一些这样的快捷接口:
- Page({
- onLoad: function(){
- ble.onMessageReceived = function(res){
- if(询卡指令返回){
- ble.sendMessage("发送 select 指令");
- }
- };
- ble.init(......);
- },
- doInventory: function(){
- ble.sendMessage("发送询卡指令")
- }
- });
等到把这部分代码写完, 正好公司购买的蓝牙读写器到货, 马上拆开进行测试
首先是启动微信小程序, 自动进入发现页面, 很快找到了新的蓝牙设备, 值得庆幸的是竟然读到了新设备的名称 (而不是像其他蓝牙设备一样返回空) 选择该设备进行连接, 非常顺利, 然后继续测试了一下断开自动重连, 更换设备(保持单一连接), 记住连接过的蓝牙设备, 中途修复了两三个小 bug, 又改进了一下体验, 应该说连接部分比较完美了
然后就是要测试第一个指令: 询卡
由于读写器自带一个询卡按钮, 通过按一下按钮就发送询卡指令的方式, 所以询卡变得很简单于是对于代码而言, 按了询卡按钮之后应该直接进入 wx.notifyBLECharacteristicValueChange(OBJECT)写好相应初始化以及消息监听代码之后, 就等着按一下询卡按钮了
一按, 果然收到消息回调了! 调试了这么多天, 终于可以触发 NFC 消息通知了! 虽然很简单, 但是确实是很激动
消息回调虽然收到了, 但是不知道响应消息的格式, 也就无法解析, 不晓得芯片是不是正确响应了问了芯片厂家, 又问了读写器生产厂家, 终于问到了询卡指令的响应格式, 按照格式解析出来了 UID, 这么多天终于读到了 UID! 再对比芯片厂家提供的 demo app 以及读写器厂家提提供的桌面程序, 确认读到的 UID 是正确的
接下来便是发送 select 指令下图为 15693 select 指令格式:
首先第一个字节 flags 就不知道怎么拼, select 字节传 0x25,UID 已得到, CRC 不知道怎么计算
在网上看了很多资料, 包括芯片厂家也说 CRC 一般是不需要传值的, 因为大多数读写器都会在内部进行 CRC 的计算好吧, 我暂且相信, 那么就只剩下一个参数了, 那就是 flags 参数按照 15693 协议规定, 试了多种组合, 尝试发送 select 指令(在第一个询卡成功的消息回调中发送), 然而收不到任何响应消息
下班后折腾了一个多小时, 做了各种数据格式的尝试, 翻阅了多个资料, 将手里的 15693 协议的相关部分阅读了多次, 还是收不到任何响应消息无赖, 不得不下班了等第二天再来研究吧
DAY #5
继续研究 select 指令在跟读写器厂家交流的过程中得知, CRC 是必须自己计算的, 不能不传, 然后就从读写器厂家提供的 DEMO 代码中找到了 CRC 的算法, 然后用 JS 实现了一遍, 然后好不容易在读写器厂家的技术文档中看到一个 CRC 计算示例, 根据示例的传入参数和计算结果再次确认了自己的 JS 算法是正确的
然后, 不管怎么传参数格式, select 指令发送成功后收不到任何消息
在绝望之时, 干脆不用读写器自带的询卡按钮, 自己按照 15693 协议发送询卡指令到这时, 在跟读写器厂家沟通的过程中得知, 他们生产的蓝牙 NFC 读写器都有自己的数据传输格式, 不能完全按照 ISO15693 协议来写, 得按照他们提供的文档来写, 当然, 到这时候读写器厂家提供的文档也读过将近十遍了, 请求格式响应格式已比较熟悉所以几分钟就完成了询卡代码
写完代码之后进入调试, 直接就收到了询卡之后的响应, 然而在接下来的发送 select 指令后还是没有收到任何响应当然, 这也本在预料之中这个时候对读写器厂家的怀疑不断加深, 越来越觉得他们卖的读写器无法读到我们的芯片, 想要退货
但我还是没有死心, 尝试按照读写器厂家提供的文档, 一步一步组装数据在之后的大概第 3 次尝试中, 竟然收到了 select 指令的响应! 并且按照响应格式解析数据表明芯片响应成功!
太激动了!!!
后来才明白, 为什么按读写器自带的询卡按钮之后发送的 select 指令收不到响应, 而自己通过蓝牙发送的询卡指令就收到了响应, 我猜测是因为按钮发起的通讯会话跟蓝牙发起的会话完全就是不同的会话导致的, 所以点击读写器自带的询卡按钮之后再通过蓝牙发送 select 指令, 应该就相当于直接对着一张芯片发送 select 指令, 当然不会收到任何响应了
这时, 调通了第一个指令, 想当然的我觉得后面的指令肯定不是问题了, 肯定就会变得非常简单
然而, 更让人绝望的事情发生了
要说 select 指令的困惑, 差不多花了我 4 个小时, 并且一开始还是非常相信读写器可以通过蓝牙完成 select 指令的, 一直到最后才有点怀疑读写器的问题
接下来的这个指令是芯片厂家的一个定制指令, 不属于 ISO15693 协议规定的范围之内, 读写器厂家提供的文档中自然不会包含整个定制指令所以只能按照芯片厂家给的文档来发送指令
很显然, 这肯定是行不通的读写器肯定只能认识读写器厂家给的文档中定义的指令格式, 然而这个浅显的道理我大概花了 1 个小时才明白, 在尝试了多种尝试之后冷静下来一思考就明白了
然后就是找读写器厂家沟通这个过程也是非常绝望的, 因为在跟芯片厂家沟通的过程中得知, 必须要读写器支持 15693 透传协议才可能发送定制指令然而问了读写器厂商, 他们自己也不确认 这款产品的蓝牙通道是否支持透传, 他们有另一款产品支持透传, 但是不支持蓝牙, 这让我一度觉得肯定要把读写器退货了, 然后再重新买一个支持 15693 协议并支持蓝牙透传的读写器
然后抱着一丝丝希望, 跟读写器厂家咨询了很久, 他们建议我按照他们另一款支持透传协议的读写器的文档来开发
真的自己都不抱什么希望了, 只是再写几行代码总比立即去买一个新的读写器来的更简单一些, 于是又做了一次尝试
这次按照另一款读写器的透传协议, 更改了定制指令的格式
竟然! 竟然直接就收到了芯片响应!
太激动了!
激动了 3 分钟之后, 静下来一看, 响应的数据太短, 肯定不是正确的数据, 然后比对文档中的响应格式, 确认芯片没有能够正确响应
这时还是继续怀疑读写器不支持透传指令, 于是再跟读写器厂家沟通, 把响应数据发给了他们看, 他们确定芯片是收到了指令了, 只是指令格式不对, 芯片没有回发正确的数据
这时已经下班十几分钟了, 感觉就还剩这一个问题了, 如果不能确定该读写器是否支持透传, 那接下来的周末肯定是无法好好休息的
但这时已经对读写器很有信心了, 比较坚定的认为是定制指令格式不对然后又找芯片厂家要了他们以前发送的 demo 代码, 指令的示例, 然后又把芯片厂家的对应文档阅读了几遍, 几乎一个字一个字去理解协议中的每一个字
终于, 终于发现指令中的某个代表响应数据长度的字节计算错误, 当把这个错误数据修复之后就立即收到了正确的响应了
太不容易了!
终于可以安心的下班了!
后记
之后的开发都比较顺利了, 因为要么就是给读写器发送读写器自定义的指令, 要么就是用透传功能发送芯片自定义的指令, 可以说是所有指令都可以发送了
这次芯片项目开发收货到最有价值的经验就是, 一般读写器厂家都会有自己定义的一套指令格式, 或者 SDK, 当通过读写器操作芯片的时候, 必须要首先按照读写器的 API 文档来写, 否则芯片不可能有响应的
来源: https://www.cnblogs.com/leotsai/p/weixin-applet-nfc-bluetooth-dev-experience.html