更新记录

1.0.5(2024-06-08)

  • 解决了一定要调用一次 openBluetoothAdapter 才能注册回调的问题
  • 添加了 batchWriteBLECharacteristicValue 接口的相关文档
  • readme.md文档优化

1.0.4(2024-06-07)

  • BLE.js 添加了 jsdoc 进行接口注释,实现了代码提示
  • 删掉遗漏的 getPermissionRequired 接口相关的定义
  • 文档更新,已确认本插件可完美兼容iOS与Android双端的UniAppX引擎工程

1.0.3(2024-06-05)

  • 移除服务发现的回调里自动请求设置MTU的逻辑,可能会有问题
  • 在调用 setBLEMTU 时,检测系统版本是否符合要求,不符合就抛出异常上报给插件使用者
查看更多

平台兼容性

Vue2 Vue3
App 快应用 微信小程序 支付宝小程序 百度小程序 字节小程序 QQ小程序
HBuilderX 4.15,Android:4.4,iOS:9 × × × × × ×
钉钉小程序 快手小程序 飞书小程序 京东小程序
× × × ×
H5-Safari Android Browser 微信浏览器(Android) QQ浏览器(Android) Chrome IE Edge Firefox PC-Safari
× × × × × × × × ×

插件与接口的特点

UniAPP工程种类兼容性多

UniAPP JS UniAPP X

接口对齐度与接口兼容性高

  • 95%的WX与UniApp的BLE主机模式的接口兼容性,尽全力对齐表现,减少代码迁移的时间损耗和兼容性问题排查的时间损耗
  • IOS与Android双端支持,基于UniAPP JS引擎版本的工程甚至可以通过本插件自带的 BLE.js 无缝对齐,部署为小程序工程
  • IOS与Android双端平台通用实现基本完成对齐,没有跨端兼容性的后顾之忧

选型开发维护全程无忧

  • 完善的文档,细致的注意事项,可帮助开发者减少开发难度,提高开发效率,快速上线APP
  • 稳定维护,本插件已有对接的APP在提供服务,从立项到维护不怕插件断更导致APP功能异常甚至缺失而无法解决只能另寻他法

创新接口解决关键痛点

  • 跟随最新的WX的BLE接口的实现,且创新式添加API,UniApp没有的本插件有,WX没有的本插件也有
  • 全新设计批量传输接口,解决传输速率和传输包之间的间隔长的痛点
    • 完美解决传输OTA大包时外设固件UART超时中断过短(1.5s以下),而WX与UNIAPP的官方API分包发送间隔过长导致大概率失败的问题
    • 有效提高传输速率,减少js层分包发送带来性能损耗

接口的已知问题

暂未支持promise语法调用

  • 原因:UniAppX对Promise特性的兼容还不是很好。UniAppX下使用 BLE.js 进行接口调用时,理论上可以实现promise封装,但是插件开发者还未实现
  • 解决方案:插件使用者自己封装一层Promise调用层,或者等插件开发者抹平此需求

云打包报错

  • 原因:HBuilderX 版本过低
  • 解决方案:请使用 HBuilderX 4.15 正式版本,目前本插件的开发和调试是 [HBuilderX 4.15正式版本] 和 [HBuilderX 4.17 Alpha版本]

IOS端扫描到的设备信息暂时无法提供serviceData字段

IOS端首次编译运行或者点击重新运行,使用蓝牙后一段时间内崩溃

  • 原因:闪退issues 2
  • 临时方案:编译运行后,手动杀死app,从桌面图标重新进入可正常运行不闪退

IOS端UniAppJS引擎的APP调用接口许久以后内存爆炸导致闪退

  • 原因:闪退issues 3
  • 临时方案:使用 BLE.js 封装库在js引擎的app下调用接口,切勿直接调用UTS接口
    • 临时解决方案仅能解决带 options 入参的接口的内存泄漏,各种 onXXX 回调注册接口执行多了还是会泄漏
    • onXXX 接口调用超过几千上万次才‘可能’导致发生崩溃,如果你的逻辑里面没有非常频繁的以ms级别调用此类接口则无需担心,在用户使用APP的周期内都是比较稳定的
  • tips:仅有js引擎且是在IOS下的工程受到影响,包括 UniAppx IOS js逻辑层、UniApp JS IOS

异步接口并行多次调用可能存在只回调一次的可能性

  • setBLEMTU 接口需要等待原生层回调通知结果,期间如果开发者并行调用多次此接口,则可能存在只回调一次 success 而后 onBLEMTUChange 多次回调的可能性。

是否考虑优化:考虑,但不优先考虑,请开发者规范调用异步接口,在success与fail与complete中进行下一步操作

当前插件不支持的但待实现的功能

  1. SPP之类的经典蓝牙相关的操作接口,包括搜索,连接,创建通信通道
  2. Peripheral模式,也就是手机作为一个外设(BLE从机)广播的模式
  3. BEACON模式,用于短距离定位,常用于外设之间相互通报位置,属于定位权限

tips: 待实现优先级按照列表排名

某些接口注册回调会重复的问题

  • IOS由于swift的闭包函数无法被比较的特性,在直接调用插件实现的UTS层实现的原生接口函数无法识别是否是已经注册过的函数,某些可以注册多个监听回调的接口多次调用会导致重复注册

  • Android端由于UTS插件的编译器的BUG,导致UniAPP JS引擎的js回调每次传递到UTS层都会创建一个新的lambda去实现UTS层定义的callback

  • 关注重复注册的问题:重复注册 issues

  • 受影响的API:

    • onBLEConnectionStateChange
    • onBLEMTUChange

同样,因为无法识别唯一性,上述的接口也无法调用其对应的 offXXX 接口,传入同一个回调函数的变量,去移除指定的回调函数的注册,而只能传入为空的变量,去移除所有的注册记录

插件开发者已经通过一些窍门去解决重复注册的问题,查看下方的兼容性框图,了解你的工程上是否会遇到此问题 🔥

平台 引擎 调用方式 是否解决重复注册的问题 注释
Android UniAPP JS BLE.js 绕过编译器隐式的桥接
iOS UniAPP JS BLE.js 绕过编译器隐式的桥接
Android UniAPP JS UTS export的接口
iOS UniAPP JS UTS export的接口
Android UniAPP X UTS export的接口 没有编译器隐式桥接的问题
iOS UniAPP X(js逻辑层) BLE.js 绕过编译器隐式的桥接
iOS UniAPP X(swift逻辑层) UTS export的接口 swift逻辑层官方未上线

因为iOS端的uniapp x swift逻辑层暂未上线,因此你可以忽略此工程的问题,只需要关注以下事项:

  • 如果你是UniApp JS引擎的工程,按照下方教程安装插件后导入 BLE.js 直接使用,没有此问题
  • 如果你是UniApp X 引擎的工程
    • 在Android端使用 import * as UTSBLE from '@/uni_modules/xl-uts-bluetooth'; 导入后,直接按照教程调用接口
    • 在IOS端使用 import * as UTSBLE from '@/uni_modules/xl-uts-bluetooth/jssdk/BLE.js'; 导入后,直接按照教程调用接口
    • 注意:如果需要兼容iOS与Android双端,应当使用条件编译,详细用法请参考插件的示例工程

接口的兼容性处理

  1. 蓝牙需要获取相关的权限(不获取就没法搜索或者连接到设备):

    • Android:
      • 在android6.0及以上,android12以下(不包括12),需要在运行时申请 (定位权限)(权限用途:用于搜索BLE设备,不授权则无法搜索到任何蓝牙设备)
        • android.permission.ACCESS_FINE_LOCATION
        • android.permission.ACCESS_COARSE_LOCATION
        • android.permission.ACCESS_BACKGROUND_LOCATION
      • 在android12及以上会出现的权限,需要在运行时申请 (附近的设备权限)(权限用途:用于搜索,连接,管理蓝牙设备,不授权则无法操作任何蓝牙设备)
        • android.permission.BLUETOOTH_SCAN
        • android.permission.BLUETOOTH_ADVERTISE
        • android.permission.BLUETOOTH_CONNECT

    为了尽可能正常使用本插件,以及减少代码复杂性,尽可能贴近wx和uniapp的官方api的用法。本插件在使用 openBluetoothAdapter 接口时会自动申请需要的权限, 这可能会导致app上架失败(有些市场要求上架的app在申请权限之前要app自定义一个弹窗声明权限用途,请求用户赋予) 如果您的APP不考虑上架市场,则可以直接使用所有的API进行开发,但是如果您需要上架国内的应用市场,请务必按照市场的要求检查系统版本号并且弹窗声明权限信息 在弹窗声明和申请相关的权限时,可参考上述的“权限用途”描述

  2. 插件开发者在搜索引擎以及官方论坛以及实际的代码中测试发现 getBluetoothAdapterState 接口在蓝牙未授权或是未开启的状态下,有些情况下走的是fail回调上报10001异常,而大部分情况下,都是走success回调上报available为false,为了抹平,我选择实现后者

  3. 在进行插件开发时,开发者搜索了大量的用例,发现网络上的开发者们的用法真是千奇百怪,有些用户甚至使用errMsg判断是否调用接口成功, 比如下方链接指向的用法:第82行代码,理论上来讲这是不规范的, 正确用法应当是判断errCode,如果你是从老的wx小程序代码中迁移BLE调用逻辑到本插件,请务必检查代码中是否包此类使用errMsg进行判断的逻辑,有的话请更改为使用errCode,

    • 注:在实际的迭代过程中,wx或者uniapp或者本插件的errMsg的格式都有可能会变,且本插件无法百分百知晓在出现错误时,各大厂商对于errMsg的格式的实现。
  4. 在连接到设备后立刻调用 getBLEDeviceServices 可能会走fail上报 10004 错误,是因为创建连接后本插件底层会自动开启搜索服务与特征,在还未完成底层的搜索任务时,调用此接口会检查搜索结果列表,还未完成或者不存在服务时直接上报 10004 为何我不实现服务搜索完毕再回调 getBLEDeviceServices 的success或者fail的逻辑?因为发现服务特征比较,通常在 1 - 8s左右(服务越多时间越长),而BLE的APi设计思想都是尽可能低耗时回调告知结果,因此我选择实现上报10004的逻辑

    • 参考:加延时解决服务搜索上报10004的问题
    • 注:硬编码延时去获取服务,并不优雅,根据上文描述,如果你的API是二次封装的库,用于提供给不同的开发者适配不同的设备,那么不同的设备的固件也许会提供更多或者更少的服务, 此时你是写死的延时,在服务少的情况下,过多的延时拖累了连接或是其他逻辑的执行速度,在服务多的情况下,过少的延时会导致百分百失败甚至是概率性失败,在我的设计思路中。优雅的方案是在获取服务失败后,重新执行 getBLEDeviceServices 接口, 在一个最大的超时内, 重复的调用 getBLEDeviceServices 接口,如果提前成功则直接跳出,如果直到超时则认为确实设备缺失服务。
  5. 参考第5项的描述,getBLEDeviceCharacteristics 接口同样的实现机制,在特征扫描的过程中调用此接口会上报 10005 错误,因此您也需要针对此情况做出超时内自动重试的处理逻辑。

  6. batchWriteBLECharacteristicValue 接口在传输的过程中,是严禁再次并行调用 writeBLECharacteristicValue 和 batchWriteBLECharacteristicValue 的,此接口一旦开启传输,会占用指定的服务下的指定特征用于自动在原生层分包发送数据块 一般此接口用于发送完整的固件、或者较大的数据封包,而这些数据在传输的过程中,往往都需要保证连续性和完整性,因此也不应该在发送的过程中穿插其他的数据

    • 正确调用:在接口的success执行后,再去调用任何的写接口
    • 注:如果看到以下的错误代码以及错误信息,则说明你的代码逻辑没处理好串行写的逻辑,此时你应当检查是否错误的并行调用写接口,或者是在不同的事件(比如setTimer、setInterval)中误调接口,或是async调用时没有await

      {
        "errSubject": "xl-uts-bluetooth",
        "errCode": 10008,
        "errMsg": "writeBLECharacteristicValue:fail characteristic handler is busy"
      }
  7. writeBLECharacteristicValue 接口并行调用且传入 writeType 还不相同的情况下,会导致顺序错乱 这是已知问题,且短时间内无法修复,可能也不会考虑修复。在正常的蓝牙开发过程中,蓝牙的写操作是一个异步的操作,且是一个可能会由于各种原因失败的异步操作, 在你设计程序之初,就应当考虑将异步操作进行串行化,同步化(async + await),根据上次的write的执行结果决定本次需要重试还是继续发送还是结束发送和通知客户。而不是对一个设计之初就是异步的api进行并行调用,并行调用的情况下,由于writeType在各个平台 的实现略有不同,因此各个平台展现出来的特性也是也是不一样的,所以失败+数据错乱的概率会大大增加。

    • 注:在对一个指定的服务特征并行执行了多次写操作之后,在还未全部结束的情况下尝试执行 batchWriteBLECharacteristicValue 接口会直接报错busy,请勿混用写接口。
  8. closeBluetoothAdapter 接口干的第一件事就是把所有注册的回调给清空,然后再关闭所有已经连接的设备,最后再清空所有缓存的运行时数据,所以如果执行了此接口,你将无法从其他的回调得到相关的信息,比如说你想在关闭适配器的同时还能得到设备断开的回调通知, 这是办不到的,如果你需要设备断开的通知,请在关闭适配器之前,先手动执行 closeBLEConnection 接口关闭各个打开的设备。

  9. 所有的操作传入的参数,没有特殊说明的情况下按照以下规范进行:

    • 字符串类型请传入大写字符串,比如deviceId, serviceId, characteristicId
  10. MTU相关的接口在双端的差异性

    • 下方论据以蓝牙模块 XY-MBA32A 为例子
    • Android: 经测试,在不同的机型,Android版本,系统版本,三个大区别上,BLE的表现特征是不一致的,碎片化比较严重

      • 设备:红米Note8Pro,系统版本为miui12.5.6.0稳定版,Android版本为11

        • 特性:createBLEConnection 创建BLE链接后,不会触发 onBLEMTUChange , 且如果 setBLEMTU 不为517,会返回错误 setBLEMTU:fail android ble status 4
      • 设备:红米k60pro上,系统版本为hyperos1.0.8.0.UMKCNXM,Android版本为14

        • 特性:createBLEConnection 创建BLE链接后会紧接着触发 onBLEMTUChange 通知MTU更改(外设的固件请求更改的),因此 getBLEMTU 获取的MTU就是更改后的MTU
      • 设备:三星S23ULTRA上,系统版本为OneUI6.1,Android版本为14

        • 特性:createBLEConnection 创建BLE链接后并没有任何的MTU更改的回调,因此 getBLEMTU 只能获取到默认的23大小
        • 如果希望 getBLEMTU 可以获取到更大的MTU,则需要在外设支持的情况下调用 setBLEMTU 进行MTU的设置,经测试,对 XY-MBA32A 模块调用 setBLEMTU(任意值) 会触发 onBLEMTUChange 更改为517
      • getBLEMTU 接口获取到的MTU需要减去3,看这个文档了解详情:MTU在Android端的注意事项

      • 结论:

        • 连接设备前:先注册 onBLEMTUChange, 因为可能连接成功后直接就协商好MTU了,如果你没提前监听的话,可能会导致措施MTU更改的消息通知
        • 连接设备后:尝试一次 setBLEMTU,无论能不能设置为指定的MTU值,至少在外设支持更改的情况下,Android底层是会尝试设置到双方都支持的最大值的, 设置成功后就能触发 onBLEMTUChange
    • IOS: 系统底层有协商MTU的逻辑的,只是开发者没办法接触到,也就是说,如果开发者需要更改MTU,需要由外设端发起更改,也就是说可以把锅丢给硬件固件部门的同事了
      • getBLEMTU 的底层是调用了 maximumWriteValueLength ,在IOS的官方API中,是把 writewriteNoResponse 两种传输类型区分对待的,两者能传输的长度是不一样的,注意,此长度不需要减去3,也就是此长度已经减去包含 Op-Code 和 Attribute Handle 的长度
      • setBLEMTU 由于系统原因,原生不支持此API,自然也就不支持 onBLEMTUChange
    • 特别注意:插件开发者在使用 XY-MBA32A 透传模块和 JDY-31-LE 透传模块进行测试时,发现这两款产品都有MTU相关的逻辑问题
      • XY-MBA32A 模块上,发现尽管MTU可以设置为更大的值(517),但是调用API时如果发送比较大的数组,会回调成功,但是模块的TX没有数据出现,经测试只能发送255以内的包大小
      • JDY-31-LE 模块上,对其串口RX写大于等于MTU大小的数据包时,BLE总是回调20个字节一次的包大小,也就是说其串口端并没有根据新的MTU进行分包发送
      • MTU在一些非规范实现的BLE透传模块上,有很大的问题,并不可信,因此切记先测试模块的MTU是否支持设置,以及设置完成后,传输 MTU - 3 大小的字节的包是否能正常收到

未实现的接口列表

  1. getConnectedBluetoothDevices

    功能描述:根据主服务 UUID 获取已连接的蓝牙设备。

    文档链接:微信文档

  2. makeBluetoothPair

    功能描述:蓝牙配对接口,仅安卓支持。

    文档链接:微信文档

  3. isBluetoothDevicePaired

    功能描述:查询蓝牙设备是否配对,仅安卓支持。

    文档链接:微信文档

  4. getBLEDeviceRSSI

    功能描述:获取蓝牙低功耗设备的信号强度 (Received Signal Strength Indication, RSSI)。

    文档链接:微信文档

tips:有些接口用上的频率比较低,或者是实现的复杂度比较高,本插件的开发者会优先实现其他的接口,上述的接口后期会慢慢更新支持

接口的使用前提

  1. 安装此插件到你的工程中(如果你是在DCLOUD插件市场中,请点击右侧按钮 ‘试用’)

  2. 导入插件

    • (1) 导入js二次封装库,支持代码提示,且参数自动修正为UTS要求类型,避免闪退

      import * as UTSBLE from '@/uni_modules/xl-uts-bluetooth/jssdk/BLE.js';

      • 适用于UniappJS引擎 的项目
      • 适用于UniappX引擎JS逻辑层 的项目
    • (2) 导入UTS插件原生支持库,适用于Android端的 uniapp x引擎 的项目

      import * as UTSBLE from '@/uni_modules/xl-uts-bluetooth';

    • 导入插件的注意事项:

      • UniAppJS引擎支持两种形式导入,只是第二种导入没有代码提醒,且可能导致重复注册回调,且类型或者值错误有可能会导致闪退,不建议使用
      • Android端的UniAppX引擎只支持第二种调用方式,最终都是编译为kotlin原生代码,没有JS运行环境
      • iOS端的 UniAppX引擎JS逻辑层 的项目,两种都支持,但是建议用第一种,可以解决很多的已知问题
        • 已测试可直接引入BLE.js,但为了兼容双端,需要使用条件编译,可参考本插件的实例项目
  3. 按照下方接口文档,调用接口

已实现的接口列表

如果遇到特性没有完全对齐的情况,可以酌情向插件开发者反馈,插件开发者会在接口下方标注,然后尽量抹除差异进行对齐

调用时请根据您的import方式进行调用,如果您的import是别名as方式,请使用别名进行调用,比如 UTSBLE.openBluetoothAdapter(参数)

以下无序接口列表不带文档,是对齐到Uni的官方API,或者WX的官方API的,因此不再重复书写文档,请参照指定链接的文档进行开发


batchWriteBLECharacteristicValue(OBJECT)

自动在底层分包然后向低功耗蓝牙设备特征值中写入二进制数据。注意:必须设备的特征值支持 write 才可以成功调用。

传输时间的测试条件和结果比较:

  • 蓝牙模块:XY-MBA32A
  • 蓝牙模块的波特率:115200
  • 蓝牙模块的MTU:23
  • 蓝牙模块的传输间隙:固件默认(对照测试时,没有通过任何蓝牙API进行更改)
  • 周边的蓝牙设备数量:20+
  • 测试手机:红米note9,OPPO reno5, 红米k60pro, 三星S23Ultra...
  • batchWriteBLECharacteristicValue 接口:0.4到0.8s
  • writeBLECharacteristicValue 接口:2s+

OBJECT 参数说明

属性 类型 默认值 必填 说明
deviceId string 蓝牙设备 id
serviceId string 蓝牙特征值对应服务的 uuid
characteristicId string 蓝牙特征值的 uuid
value ArrayBuffer 蓝牙设备特征值对应的二进制值
size number 每次发送的分包大小,建议传入20
writeType string 蓝牙特征值的写模式设置,有两种模式,iOS 优先 write,安卓优先 writeNoResponse 。
success function 接口调用成功的回调函数
fail function 接口调用失败的回调函数
complete function 接口调用结束的回调函数(调用成功、失败都会执行)

writeType

属性 说明
write 强制回复写,不支持时报错
writeNoResponse 强制无回复写,不支持时报错

错误

错误码 错误信息 说明
0 ok 正常
-1 already connect 已经连接(WX特性)
10000 not init 未初始化蓝牙适配器
10001 not available 当前蓝牙适配器不可用
10002 no device 没有找到指定设备
10003 connection fail 连接失败
10004 no service 没有找到指定服务
10005 no characteristic 没有找到指定特征值
10006 no connection 当前连接已断开
10007 property not support 当前特征值不支持此操作
10008 system error 其余所有系统上报的异常
10009 system not support Android 系统特有,系统版本低于 4.3 不支持 BLE
10010 already connect 已连接(UNI特性)
10011 need pin 配对设备需要配对码
10012 operate time out 连接超时
10013 invalid_data 连接 deviceId 为空或者是格式不正确

注意

  1. MTU虽然大,并且 size 参数也给了接近MTU的值,速度并不比只发送20个字节快多少?

    • 即使MTU增大,如果通信周期(connection interval)较长或者连接间隔没有相应优化,两次传输之间的时间增加可能会抵消掉因MTU增大而获得的潜在速度提升。
    • 建议 size 参数赋值为20,稳定性极佳,速率又比 writeBLECharacteristicValue 接口高
  2. 暂时不建议使用此接口传输超过 10KB 的包(发送一次可能要花费1s+以上,包越大越久)

    • 因为现在还未实现取消发送和发送进度通知的能力,如果发送太大的数据包,会导致此接口无法在短时间内success或者fail,用户无法得知进度而体验较差
    • 建议先分包为4KB,然后再调用本接口发送,后期开发者会尽快优化此问题

示例代码

// 向蓝牙设备发送一个0x00的16进制数据
const buffer = new ArrayBuffer(1)
const dataView = new DataView(buffer)
dataView.setUint8(0, 0)
batchWriteBLECharacteristicValue({
  // 这里的 deviceId 需要在 getBluetoothDevices 或 onBluetoothDeviceFound 接口中获取
  deviceId,
  // 这里的 serviceId 需要在 getBLEDeviceServices 接口中获取
  serviceId,
  // 这里的 characteristicId 需要在 getBLEDeviceCharacteristics 接口中获取
  characteristicId,
  // UTS插件目前只支持number[]类型,但是很多官方的实例代码都是 ArrayBuffer 类型,因此插件开发者在 BLE.js 做了自动转换的过程
  // 但是开发者如果可以办到,请在传入的时候改为使用 number[] 类型
  // 如果不使用 BLE.js 进行接口调用,那就必须要传入 number[] 类型!!!否则可能导致闪退或其他的异常
  value: buffer,
  success(res) {
    console.log('batchWriteBLECharacteristicValue success', res.errMsg)
  }
})

隐私、权限声明

1. 本插件需要申请的系统权限列表:

蓝牙相关权限,位置权限

2. 本插件采集的数据、发送的服务器地址、以及数据用途说明:

插件不采集任何数据

3. 本插件是否包含广告,如包含需详细说明广告表达方式、展示频率:

暂无用户评论。

使用中有什么不明白的地方,就向插件作者提问吧~ 我要提问