更新记录

1.0.13(2024-08-19)

  • 修复createBLEConnection和closeBLEConnection的并行执行丢失回调的bug
  • 修复某些情况下影响多设备连接的bug
  • 修复Android端在存在BLE链接时,在系统设置关闭蓝牙会回调通知两次的BUG
  • 更新readme文档,标注某些接口的对齐特性

1.0.12(2024-08-16)

  • Android端修复连接设备会概率性的同时fail回调超时和success回调成功的问题
  • 某些资源删除了删除过时的注释
  • 更新readme文档,排版更加简洁

1.0.11(2024-07-27)

  • Android端notifyBLECharacteristicValueChange接口优化
  • BLE.js添加异步api的Promise风格调用的兼容性代码
  • 屏蔽iOS端某些调试日志的打印
  • readme更新,添加Promise风格相关的文档
查看更多

平台兼容性

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

开源对接例程,持续更新

GITEE开源仓库,UniappX与本蓝牙插件的对接例程

  • 由于DCloud插件市场的示例项目功能无法管理版本和更新日志,因此使用gitee进行版本管理
  • 工程内的 utils 文件夹包含了一些对BLE开发很有帮助的封装库
    • utils.uts 【常用BLE开发工具】 内含比如ab2hex的替代品bytes2hex,可将蓝牙数据转换为hex字符串
    • ble.uts 【跨平台调用插件】 极大简化 UniappX 工程跨端引用本插件
    • 更多用法请阅览源代码 ......

插件与接口的特点

UniAPP工程种类兼容性多

UniAPP JS UniAPP X

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

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

选型开发维护全程无忧

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

创新接口解决关键痛点

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

接口的已知问题

本插件某些接口只支持注册一个回调而Uniapp和WX支持注册多个

  • 原因:某些接口提供的注册方法虽然可以注册多个,但是解注册方法只能是全部进行解注册,解注册和注册的过程极其容易留下暗坑,比如内存泄露,回调混乱,生命周期不符合需求
  • 解决方案:开发者自行维护这些API的注册以及解注册,在需要的时候注册,在不需要的时候解注册。真正传给本插件库的只有一个注册的回调函数,通过此回调函数进行分发处理
  • 可参考 不同的BLE第三方库导致的回调混乱
  • 可参考 本插件的例程实现的回调维护机制
  • 受影响的API:onBluetoothDeviceFound、onBluetoothAdapterStateChange、onBLECharacteristicValueChange。注意,一般仅在多设备下链接下有影响。

云打包报错

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

IOS端扫描到的设备信息暂时无法提供serviceData字段(已解决)

  • 原因:闪退issues 1
  • 解决方案:HBuilderX升级到4.23及以上可解决此问题,目前插件已加入此修复逻辑,在 HBuilderX版本 >= 4.23 时自动开放此字段

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

  • 原因:闪退issues 2
  • 临时方案:编译运行后,手动杀死app,从桌面图标重新进入可正常运行不闪退
  • tips:此问题仅在本插件开发时被插件开发者频繁遇到,如果插件使用者并未遇到此问题,可忽略本条问题事项。

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. 禁止用 errMsg 判断接口的执行结果

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

    • 注:在实际的迭代过程中,wx或者uniapp或者本插件的errMsg的格式都有可能会变,且本插件无法百分百知晓在出现错误时,各大厂商对于errMsg的格式的实现。
  3. batchWriteBLECharacteristicValue 接口在传输的过程中,严禁再次并行调用 writeBLECharacteristicValue 和 batchWriteBLECharacteristicValue

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

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

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

    • 注:在对一个指定的服务特征并行执行了多次写操作之后,在还未全部结束的情况下尝试执行 batchWriteBLECharacteristicValue 接口会直接报错busy,请勿混用写接口。
  5. 所有的操作传入的参数,没有特殊说明的情况下按照以下规范进行:

    • 字符串类型请传入大写字符串,比如deviceId, serviceId, characteristicId
  6. 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二次封装库(UniAppJS引擎项目推荐此方法)

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

      • 适用于UniappJS引擎的 IOS & Android项目
      • 适用于UniappX引擎JS逻辑层的IOS项目
      • 支持代码提示,且参数自动修正为UTS要求类型,避免闪退
      • 支持Promise语法调用,异步api的success,fail,complete三个回调都不传则返回一个Promise对象
    • (2) 导入UTS插件原生支持库(不推荐,原因看下方事项)

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

      • 仅适用于Android端的 UniappX引擎 的项目
      • 不支持Promise语法调用
    • (3) 导入uts二次封装库(UniAppX引擎项目推荐此方法)

      import * as UTSBLE from '@/utils/ble.uts';

      • 适用于IOS与Android双端的 UniappX引擎 的项目
      • 不支持UniappX swift逻辑层的IOS项目(官方未上线swift逻辑层,但是我先备注说明)
      • 支持Promise语法调用,异步api的success,fail,complete三个回调都不传则返回一个Promise对象

      注:ble.uts源码可在 开源示例项目 找到。 在下载 ble.uts 完成后,将其放入到你的工程中(注意,不要放入到任何的uni_modules文件夹内),如果工程根目录已有utils文件夹,则可以直接放入进去然后参考上述文章的导入语法 如果 ble.uts 的存放目录有任何的变动,导入时也请修正为正确的路径!

    • 导入插件的额外注意事项和参考备注:

      • UniAppJS引擎的 Android & IOS工程 支持两种形式导入。只是第2种导入没有代码提醒,且可能导致重复注册回调,且类型或者值错误有可能会导致闪退,不建议使用。建议使用第1种
      • UniAppX引擎的 Android工程 只支持第2、3种调用方式。最终都是编译为kotlin原生代码,没有JS运行环境,如果没有跨端需求可以用第2种调用方式
      • iOS端的 UniAppX引擎JS逻辑层 的项目,第1和2和3种方式都支持。但是建议用第1(无跨端需求)或者第3种(有跨端需求),可以解决很多的已知问题
      • UniAppX引擎的项目,如果需要兼容 Android & IOS 可使用第3种调用方式导入 ble.uts 进行调用,其内部已自动条件编译
  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或number[] 蓝牙设备特征值对应的二进制值
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,然后再调用本接口发送,后期开发者会尽快优化此问题

示例代码

  • 以下为UniappJS示例,X工程暂无ArrayBuffer和DataView支持,请使用number[]进行替代
    // 向蓝牙设备发送一个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 或者上述的开源例程中的 ble.uts 进行接口调用,那就必须要传入 number[] 类型!!!否则可能导致闪退或其他的异常
    value: buffer,
    success(res) {
    console.log('batchWriteBLECharacteristicValue success', res.errMsg)
    }
    })

隐私、权限声明

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

蓝牙相关权限,位置权限

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

插件不采集任何数据

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

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