必须分片,因ble协议规定单次att写入最大20字节,微信小程序强制截断且ios静默丢弃、android报错10008;需基于success回调串行发送,推荐uni-bluetooth插件自动处理分片、排队与重试。

uni-app 微信小程序端向蓝牙设备写入数据时,超过 20 字节大概率会失败,iOS 甚至不触发任何回调(success 或 fail 都没有),这不是 bug,而是系统和 BLE 协议层的硬性限制。必须分包,且不能简单并行调用 writeBLECharacteristicValue。
为什么必须分片?20 字节是硬限制
BLE 4.0+ 协议规定单次 ATT Write Request 最大有效载荷为 20 字节(不含协议头)。微信小程序底层封装了系统 API,沿用了该限制:
- iOS 系统不回调、不报错,写入直接静默丢弃
- Android 多数机型返回
errCode: 10008(“写入特征值失败”),但错误信息模糊 - 即使设备支持 MTU 扩展(如协商到 512),微信小程序目前(2026 年)仍强制截断为 20 字节/包
用 uni-bluetooth 插件自动分片最省心
官方原生 API 没有内置分片逻辑,手写容易出错。推荐使用社区维护的 uni-bluetooth 插件(npm 包名: uni-bluetooth),它已封装好排队 + 分片 + 重试机制:
- 安装:
npm install uni-bluetooth - 写入时第三个参数设为
true即启用自动分片:ble.writeValue('01004124010064...', 'hex', true) - 它会按每 20 字节切片,并在上一片写入成功后(监听
writeCharacteristicValueSuccess事件)再发下一片 - 若某片失败,会停止后续发送并抛出错误,避免设备状态错乱
手动分片需严格控制时序和状态
如果不用插件,必须自己管理写入队列。关键点不是“怎么切”,而是“怎么等”:
- 先用
value.match(/.{1,20}/g)把十六进制字符串切为数组,每项 ≤20 字节(注意:是字节,不是字符;'0100'是 2 字节,'010041'是 3 字节) - 每次调用
uni.writeBLECharacteristicValue前,确保前一次已收到success回调(不能只靠setTimeout延时) - 必须监听
uni.onBLECharacteristicValueChange吗?不必要;但必须监听uni.onBLEConnectionStateChange,连接意外断开时要清空待发队列 - 不要在
fail回调里立即重试——设备可能正忙,建议加退避(如 300ms 后重试 1 次)
容易被忽略的兼容性细节
分片逻辑看似简单,但跨平台时几个点常被漏掉:
- Android 需在
manifest.json补充权限:"android.permission.ACCESS_FINE_LOCATION"(高版本强制要求,否则扫描都失败,更别说写入) - iOS 必须在
mp-weixin的permission中声明scope.bluetooth,且描述文案不能含“定位”“位置”等词,否则审核被拒 - 某些国产 Android 机型(如华为 EMUI、小米 MIUI)会后台杀掉蓝牙相关进程,分片过程中用户切到后台 → 连接中断 → 写入卡死。建议在
onHide中主动断连,在onShow中重建连接
真正难的不是把长数据切成 20 字节,而是让每一片都稳稳落到设备手里。别信“加个 delay 就行”,必须基于真实回调驱动流程。分片只是手段,可靠传输才是目的。











