更新记录
1.0.0(2026-09-18) 下载此版本
首个正式版本
- 四端支持:Android 5.0+(API 21)/ iOS 12.0+ / HarmonyOS NEXT(API 12)/ 微信小程序(基础库 2.9.2+);uni-app(vue2/vue3)与 uni-app x 双框架
- 中心设备能力:BLE 扫描(服务UUID/名称前缀过滤、功耗等级、去重上报)、多设备并发连接、服务/特征发现、读写(带/无响应自动选择)、通知订阅(自动写 CCCD)、setBLEMTU
- 状态监听:蓝牙开关、扫描状态、连接状态变化,统一 on/off 事件对
- 外围能力:BLE 广播 + GATT Server(安卓/iOS/鸿蒙;微信小程序受平台限制返回 9020003)
- GBK 全量(cp936)编解码内置:encodeGBK/decodeGBK、字符串收发 API(GBK/UTF-8/HEX)、autoSplit 自动分包;四端编解码逐字节一致(全量回归验证)
- Android 体验:按应用 targetSdk 自适应申请权限、蓝牙未开自动拉起系统开启授权弹窗、SCAN_RSP 设备名自动补报
- 工程细节:每设备串行写队列、valueHex 入参规避 Android uni-app ArrayBuffer 限制、遵循 uni 错误码规范(902xxxx)
- 附 uni-app / uni-app x 双演示工程(设备列表页 + 收发信息页,含广播回显测试)
平台兼容性
uni-app(4.51)
| Vue2 | Vue2插件版本 | Vue3 | Vue3插件版本 | Chrome | Safari | app-vue | app-vue插件版本 | app-nvue | app-nvue插件版本 | Android | iOS | 鸿蒙 |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| √ | 1.0.0 | √ | 1.0.0 | - | - | √ | 1.0.0 | √ | 1.0.0 | - | - | - |
| 微信小程序 | 支付宝小程序 | 抖音小程序 | 百度小程序 | 快手小程序 | 京东小程序 | 鸿蒙元服务 | QQ小程序 | 飞书小程序 | 小红书小程序 | 快应用-华为 | 快应用-联盟 |
|---|---|---|---|---|---|---|---|---|---|---|---|
| - | - | - | - | - | - | - | √ | √ | - | √ | √ |
uni-app x(4.61)
| Chrome | Safari | Android | Android插件版本 | iOS | iOS插件版本 | 鸿蒙 | 鸿蒙插件版本 | 微信小程序 | 微信小程序插件版本 |
|---|---|---|---|---|---|---|---|---|---|
| × | × | 5.0 | 1.0.0 | 12 | 1.0.0 | 12+ | 1.0.0 | 2.9.2 | 1.0.0 |
其他
| 多语言 | 暗黑模式 | 宽屏模式 |
|---|---|---|
| × | × | √ |
xm-uts-ble 蓝牙 BLE 插件
UTS 编写的跨端蓝牙 BLE 插件:安卓 / iOS / 鸿蒙Next / 微信小程序 一套 API。
支持平台:App-Android 5.0+ | App-iOS 12.0+ | HarmonyOS NEXT(API 12) | 微信小程序 | uni-app + uni-app x | 免费开源(MIT)
<!-- 效果展示:截图补齐后取消本段注释      -->| 能力 | 说明 | app-android | app-ios | app-harmony | mp-weixin |
|---|---|---|---|---|---|
| 蓝牙扫描 | 支持服务UUID/名称前缀过滤、功耗等级、重复上报 | ✅ | ✅ | ✅ | ✅ |
| 蓝牙状态监听 | 蓝牙开关、扫描状态变化 | ✅ | ✅ | ✅ | ✅ |
| 多设备连接 | deviceId 上下文隔离、连接状态监听 | ✅ | ✅ | ✅ | ✅ |
| 数据读写 | 读/写(带·无响应)/通知订阅/MTU | ✅ | ✅ | ✅ | ✅ |
| GBK 编码 | 内置全量 cp936 表,中文设备直连 | ✅ | ✅ | ✅ | ✅ |
| BLE 广播 | 可连接/不可连接广播、厂商数据 | ✅ | ✅ | ✅ | ❌ 微信平台限制 |
| GATT Server | 被连接、读写事件回调、通知回包 | ✅ | ✅ | ✅ | ❌ 微信平台限制 |
API 命名与微信小程序 / uni 蓝牙 API 对齐(
openBluetoothAdapter、createBLEConnection…),微信蓝牙业务可近乎零成本迁移。
功能特性
- 多蓝牙设备同时连接,每设备独立串行写队列(BLE 写必须串行,队列自动泵送)
- GBK / UTF-8 / HEX 三编码收发:
writeBLECharacteristicString直接发中文、onBLECharacteristicString直接收到解码后的字符串 - 全量 GBK(cp936) 编解码为纯 UTS 实现(23940 双字节条目),四端解码/编码结果逐字节一致(工具链含全量回归测试)
- 蓝牙适配器开关监听、连接断开监听、扫描状态统一走
onXXX/offXXX事件对 - 分包工具
splitBuffer+autoSplit一键大包拆包发送 valueHex十六进制入参:规避 Android uni-app(vue) 下 ArrayBuffer 不能作插件入参的系统限制- 遵循 uni 错误码规范(9020xxx),
BleFail.errCode/errMsg/errSubject
平台要求
- HBuilderX 4.61+(uni-app x 与鸿蒙平台;uni-app 工程最低 4.51+,iOS ArrayBuffer 插件转换 API 要求)
- Android 5.0+(API 21);iOS 12.0+;HarmonyOS NEXT(API 12);微信基础库 2.9.2+
安装
在 HBuilderX 中打开 插件市场 搜索 xm-uts-ble,或从本仓库复制 uni_modules/xm-uts-ble 到项目 uni_modules 目录。
权限配置
Android
插件已内置 AndroidManifest.xml,云打包自动合并(新旧两套权限同时声明,不设 maxSdkVersion,避免 targetSdk<31 的应用在 Android 12+ 设备上旧权限被剥离):
BLUETOOTH/BLUETOOTH_ADMIN/ACCESS_FINE_LOCATION/ACCESS_COARSE_LOCATIONBLUETOOTH_SCAN(neverForLocation)/BLUETOOTH_CONNECT
运行时权限由 openBluetoothAdapter() 自动拉起申请(可传 requestPermissions: false 关闭),按应用 targetSdkVersion 自动二选一:target≥31 申请新权限;target<31 申请定位权限(低 target 申请 BLUETOOTH_CONNECT 会被系统抛 IllegalStateException,已规避)。
iOS
插件已内置 Info.plist(合并到工程):NSBluetoothAlwaysUsageDescription、后台模式 bluetooth-central / bluetooth-peripheral(不需要后台 BLE 可自行删除)。
鸿蒙
在项目的 harmony-configs(或 HBuilderX 鸿蒙工程配置)module.json5 中为 Entry 添加:
"requestPermissions": [
{ "name": "ohos.permission.ACCESS_BLUETOOTH",
"reason": "$string:ble_reason", "usedScene": { "when": "inuse" } },
{ "name": "ohos.permission.APPROXIMATELY_LOCATION",
"reason": "$string:loc_reason", "usedScene": { "when": "inuse" } }
]
微信小程序
- 隐私协议(必须):蓝牙 API 属微信隐私接口,未声明会报
api scope is not declared in the privacy agreement(插件映射为 9020002)。处理:- 正式发布:微信公众平台 → 设置 → 服务内容声明 → 用户隐私保护指引 → 添加“蓝牙”收集使用声明,通过后生效(用户需同意隐私弹窗,可用
wx.requirePrivacyAuthorize/官方隐私弹窗组件) - 本地调试:微信开发者工具 → 详情 → 本地设置 → 勾选 不校验隐私接口(不同版本工具措辞略有差异)
- 正式发布:微信公众平台 → 设置 → 服务内容声明 → 用户隐私保护指引 → 添加“蓝牙”收集使用声明,通过后生效(用户需同意隐私弹窗,可用
- 手机端需开启蓝牙与定位(iOS 微信扫描依赖系统定位服务)
- 小程序不支持蓝牙广播/GATT Server(调用相应 API 返回
9020003)
快速开始
import {
openBluetoothAdapter, startBluetoothDevicesDiscovery, onBluetoothDeviceFound,
createBLEConnection, getBLEDeviceServices, notifyBLECharacteristicValue,
writeBLECharacteristicString, onBLECharacteristicString, setBLEMTU
} from '@/uni_modules/xm-uts-ble'
// 1. 初始化(自动申请权限)
openBluetoothAdapter({
success: () => {
// 2. 设备发现
onBluetoothDeviceFound(res => {
res.devices.forEach(d => console.log(d.name, d.deviceId, d.RSSI))
})
startBluetoothDevicesDiscovery({
services: ['180D'], // 可选:按服务UUID过滤(支持16bit简写)
allowDuplicatesKey: false,
powerLevel: 'high'
})
// 3. 连接(支持同时连接多台设备,按 deviceId 区分)
createBLEConnection({
deviceId: 'AA:BB:CC:DD:EE:FF',
timeout: 10000,
success: () => {
setBLEMTU({ deviceId: 'AA:BB:CC:DD:EE:FF', mtu: 247 })
getBLEDeviceServices({
deviceId: 'AA:BB:CC:DD:EE:FF',
withCharacteristics: true, // 一次取回服务+特征
success: res => console.log(JSON.stringify(res.services))
})
}
})
// 4. GBK 中文收发
onBLECharacteristicString(res => {
console.log('收到:', res.value, res.hex) // value 已按 GBK 解码
}, 'gbk')
writeBLECharacteristicString({
deviceId: 'AA:BB:CC:DD:EE:FF',
serviceId: 'fff0',
characteristicId: 'fff1',
value: '温度查询', // 中文 → GBK 字节
encoding: 'gbk',
autoSplit: true, // 大包自动拆分(默认20字节/包)
chunkSize: 20
})
},
fail: err => console.log(err.errCode, err.errMsg)
})
uni-app(vue) 下发送 hex 数据(Android ArrayBuffer 限制规避)
writeBLECharacteristicValue({
deviceId, serviceId, characteristicId,
value: null,
valueHex: 'a1b2c3d4', // 十六进制字符串
success: () => {}, fail: e => {}
})
蓝牙广播 + GATT Server(App 三端)
import {
startGattServer, startBLEAdvertising, onGattServerWriteRequest,
sendGattServerNotification
} from '@/uni_modules/xm-uts-ble'
// 定义一个被连接的服务(示例: Nordic UART 风格回显)
startGattServer({
services: [{
uuid: '6e400001-b5a3-f393-e0a9-e50e24dcca9e',
characteristics: [
{ uuid: '6e400002-b5a3-f393-e0a9-e50e24dcca9e', properties: ['write'], permissions: ['write'] },
{ uuid: '6e400003-b5a3-f393-e0a9-e50e24dcca9e', properties: ['notify', 'read'], permissions: ['read'] }
]
}]
})
// 收到对端写入 → 回显通知
onGattServerWriteRequest(req => {
sendGattServerNotification({
deviceId: req.deviceId,
serviceId: '6e400001-b5a3-f393-e0a9-e50e24dcca9e',
characteristicId: '6e400003-b5a3-f393-e0a9-e50e24dcca9e',
value: req.value
})
})
startBLEAdvertising({
serviceUUIDs: ['6e400001-b5a3-f393-e0a9-e50e24dcca9e'],
includeDeviceName: true,
manufacturerData: hexToBuffer('8801584D'), // 前2字节为公司ID(小端)
connectable: true
})
API 一览
适配器
| API | 说明 |
|---|---|
openBluetoothAdapter(options?) |
初始化,自动申请权限;蓝牙未开启返回 9020001 |
closeBluetoothAdapter(options?) |
关闭并释放全部资源(扫描/连接/广播/Server) |
getBluetoothAdapterState(options) |
{available, discovering} |
onBluetoothAdapterStateChange(cb) / off... |
蓝牙开关与扫描状态变化 |
扫描
| API | 说明 |
|---|---|
startBluetoothDevicesDiscovery(options) |
services/namePrefix/allowDuplicatesKey/interval/powerLevel |
stopBluetoothDevicesDiscovery(options?) |
停止扫描 |
onBluetoothDeviceFound(cb) / off... |
设备发现事件,res.devices[]:deviceId/name/localName/RSSI/connectable/serviceUUIDs/advertisData/raw |
getBluetoothDevices(options) |
已发现设备缓存 |
getConnectedBluetoothDevices(options) |
已连接设备(按 services 过滤) |
连接与收发
| API | 说明 |
|---|---|
createBLEConnection({deviceId, timeout?}) |
连接,支持多设备并发连接 |
closeBLEConnection({deviceId}) |
断开 |
onBLEConnectionStateChange(cb) / off... |
连接状态监听 {deviceId, connected} |
getBLEDeviceServices({deviceId, withCharacteristics?}) |
服务列表;withCharacteristics:true 时一并拉取特征 |
getBLEDeviceCharacteristics({deviceId, serviceId}) |
特征列表(read/write/writeNoResponse/notify/indicate) |
writeBLECharacteristicValue({deviceId, serviceId, characteristicId, value 或 valueHex, type?}) |
写(带/无响应自动选择,超长返回 9020015) |
writeBLECharacteristicString({value, encoding='gbk', autoSplit?, chunkSize?, splitInterval?}) |
字符串编码发送,可自动分包 |
readBLECharacteristicValue({...}) |
读(结果统一从 onBLECharacteristicValueChange 返回) |
notifyBLECharacteristicValue({state=true}) |
订阅/取消通知(自动写 CCCD) |
onBLECharacteristicValueChange(cb) / off... |
原始字节回调 value: ArrayBuffer |
onBLECharacteristicString(cb, encoding='gbk') / off... |
字符串回调 {value, hex} |
setBLEMTU({deviceId, mtu}) |
Android 请求 MTU;iOS 返回系统协商值;鸿蒙 setBLEMtuSize |
广播 / GATT Server(App 三端;微信小程序返回 9020003)
| API | 说明 |
|---|---|
startBLEAdvertising({serviceUUIDs?, manufacturerData?, includeDeviceName?, connectable?, scanResponseData?}) |
开始广播 |
stopBLEAdvertising(options?) |
停止 |
onBLEAdvertisingStateChange(cb) / off... |
{isAdvertising} |
startGattServer({services}) |
启动 GATT Server 并注册服务;ServerCharacteristicDef{uuid, properties[], permissions[], value?} |
stopGattServer(options?) |
停止并释放 |
onGattServerReadRequest(cb) / off... |
对端读请求;注册回调后需业务调用 sendGattServerResponse 回包;未注册时插件自动以内置值回包 |
onGattServerWriteRequest(cb) / off... |
对端写请求;注册回调后需 sendGattServerResponse 确认(未注册自动 ACK) |
sendGattServerResponse({deviceId, requestId, status?, value?}) |
回包/ACK,requestId 透传自回调 |
sendGattServerNotification({deviceId, serviceId, characteristicId, value 或 valueHex, indicate?}) |
向指定中心设备推送 |
onGattServerConnectionStateChange(cb) / off... |
对端连接/断开、MTU(iOS 不触发,安卓/鸿蒙支持) |
工具(全平台可用,纯 UTS 实现)
encodeGBK(str): ArrayBuffer、decodeGBK(buf): string、encodeUtf8、decodeUtf8、bufferToHex、hexToBuffer、splitBuffer(buf, size)、expandUuid、uuidEquals、bytesToBuffer、bufferToBytes
错误码
| errCode | 含义 |
|---|---|
| 9020001 | 蓝牙不可用/未开启 |
| 9020002 | 权限被拒绝 / 小程序隐私协议未声明蓝牙 |
| 9020003 | 当前平台不支持(如小程序广播) |
| 9020004 | 适配器未初始化 |
| 9020005 | 扫描启动失败 |
| 9020006 | 设备未找到 |
| 9020007 | 连接失败/超时 |
| 9020008 | 设备未连接 |
| 9020009 | 服务未找到 |
| 9020010 | 特征未找到 |
| 9020011 | 读失败 |
| 9020012 | 写失败 |
| 9020013 | 设置通知失败 |
| 9020014 | 设置MTU失败 |
| 9020015 | 数据超过当前MTU |
| 9020016 | 广播启动失败 |
| 9020017 | 设备不支持BLE广播 |
| 9020018 | GATT Server 启动失败 |
| 9020019 | 参数错误 |
| 9020099 | 未知错误 |
平台差异与注意事项
- iOS deviceId 为系统 UUID(非 MAC),且只能连接“本进程扫描到过的”设备(与微信一致)。
- Android uni-app(vue):ArrayBuffer 不能作为插件入参 → 写数据用
valueHex或字符串接口;插件返回值/回调中的 ArrayBuffer 正常可用。 - 广播能力检测:安卓部分机型不支持外围广播(
isMultipleAdvertisementSupported=false 返回 9020017);iOS 广播需真机。 - interval/allowDuplicatesKey 在部分平台为近似实现(按设备去重上报);去重模式下,若首次上报无名称、后续 SCAN_RSP 带出名称,插件会自动补报一次该设备(UI 按 deviceId 覆盖即可显示名称)。
offXXX(callback)按函数引用移除:请保存回调引用;不传参移除该事件全部监听。- 断开设备后请调用
closeBLEConnection;closeBluetoothAdapter会释放全部连接/广播/Server。 - 读特征成功回调仅代表“读指令已下发”,数据一律从
onBLECharacteristicValueChange/String返回(微信同款行为)。 - 鸿蒙端 API 以本机 SDK 为准,若编译报签名差异请按文末“编译核对清单”微调。
iOS 测试与编译核对清单
前置条件(二选一)
- 路线 A(免费):借一台 Mac + 免费 Apple ID。Mac 装 HBuilderX,工程拷入后直接「运行 → 运行到 iOS App」真机调试(Xcode 自动签名,7 天有效)。UTS→Swift 编译错误在 HBuilderX 控制台即时可见,是修错最快的方式。
- 路线 B(付费 $99/年):Apple 开发者账号 + Windows 云打包。developer.apple.com 注册设备 UDID(爱思助手可读取)→ 创建开发证书(.p12,CSR 可用 openssl 生成)与描述文件(.mobileprovision) → HBuilderX 云打包 iOS 填自有证书;或在打包弹窗用 Apple ID 托管自动建证书。产物 ipa 用爱思助手安装。
首次 iOS 编译可能需微调的灰度点(已按文档证据择优实现)
CBATTError.Code(rawValue = x)!(可失败枚举初始化写法)- UTS 函数引用比较
===(offXXX 回调身份匹配,Swift 闭包不支持==) - Swift 数组 ↔ UTS Array 透明桥接(
peripheral.services赋值) Map<string, any>→ Swift[String: Any](广播参数、handleAdv 解析)catch (e : Exception)的e.message(iOS 侧 UTSError 属性)- delegate 方法
@argumentLabel与本机 SDK 签名核对(didDiscover/didUpdateValueFor/respond 等)
iOS 平台行为差异(非 bug)
- 只能连接本会话扫描到过的设备(否则 9020006),与微信一致
- deviceId 为系统 UUID;
setBLEMTU不支持主动设置,返回系统协商值 onGattServerConnectionStateChangeiOS 无对等 API,不触发(安卓/鸿蒙支持)- 广播/被连仅真机可用;蓝牙需用户在系统设置手动开启(iOS 禁止代码开启)
其他平台编译核对清单
- Android:已通过云打包编译验证(v0.1.0 迭代修复记录见 changelog)
- 鸿蒙:
connectionManager.startAdvertising/BLEAdvertisingSetting、registerServerCallback/ServerCallback、GattCharacteristic.writeType(WRITE_VALUE=带响应 / WRITE_COMMAND=无响应) 以本机 SDK d.ts 为准 - 真机验证顺序建议:小程序(纯转发最稳) → 安卓 → iOS → 鸿蒙
仓库结构
ble-plugin/
├─ uni_modules/xm-uts-ble/ # 插件本体(单一真源)
│ ├─ package.json / readme.md / changelog.md
│ └─ utssdk/
│ ├─ interface.uts # 全部类型与API声明
│ ├─ unierror.uts # 错误码实现
│ ├─ common/ # 跨端共享:事件中心/GBK/UTF8/hex/分包/写队列
│ ├─ app-android/ index.uts + AndroidManifest.xml + config.json
│ ├─ app-ios/ index.uts + Info.plist + config.json
│ ├─ app-harmony/ index.uts
│ ├─ mp-weixin/ index.uts
│ └─ web/ index.uts # 占位,返回9020003
├─ apps/
│ ├─ demo-uniappx/ # uni-app x 演示(安卓/iOS/鸿蒙真机)
│ └─ demo-uniapp/ # uni-app vue3 演示(微信小程序+App)
└─ tools/
├─ gen-gbk-table.mjs # 从 iconv-lite cp936 生成内置映射表
└─ test-gbk.mjs # GBK 全量回归(对照 iconv 逐字节校验)
apps/* 的 uni_modules/xm-uts-ble 为目录联接(junction),指向插件本体,修改即时同步。
测试
cd tools && npm i && node gen-gbk-table.mjs && node test-gbk.mjs && node check-syntax.mjs
test-gbk.mjs:GBK 全量回归(当前 23940/23940 解码一致、21791 编码可逆、round-trip 通过)check-syntax.mjs:esbuild 对所有 .uts/demo 脚本做语法变换检查(与 vite:esbuild 同解析器;iOS 文件会先屏蔽@argumentLabel、Swift 标签等 UTS 专有语法再校验)
License
MIT

收藏人数:
下载插件并导入HBuilderX
赞赏(0)
下载 0
赞赏 0
下载 12614869
赞赏 1949
赞赏
京公网安备:11010802035340号