核心是“连得稳、写得对、打得准”,90%失败源于适配器状态误判、服务特征值硬编码、指令换行符错误;必须先调uni.getbluetoothadapterstate并校验available与powered,services参数不可为空,write需uint8array且cpcl指令结尾必须为\r\n。

App端用uni-app调用原生蓝牙打印机打印小票,核心不是“能不能连”,而是“连得稳、写得对、打得准”。90%的失败都卡在适配器状态误判、服务特征值硬编码、指令换行符错误这三处。
uni.getBluetoothAdapterState 必须放在最前面,不能跳过
很多开发者直接 uni.openBluetoothAdapter 后就立刻 uni.startBluetoothDevicesDiscovery,结果安卓搜不到、iOS弹框没回调就报错。这是因为:
- iOS首次调用会弹定位授权框,
getBluetoothAdapterState可能返回poweredOff: true,实际是等待授权中 - 安卓常因其他App正在扫描,导致
discovering: true,此时再调startBluetoothDevicesDiscovery不触发onBluetoothDeviceFound -
openBluetoothAdapter的 success 回调 ≠ 适配器已 ready,它只表示模块加载成功
正确做法:所有后续操作必须塞进 uni.getBluetoothAdapterState 的 success 回调里,并判断 res.available === true 和 res.powered === true;若 res.discovering === true,先 uni.stopBluetoothDevicesDiscovery() 再启动新搜索。
services 参数必须传,不能靠空数组扫全设备
不填 services 直接扫,会混入大量广播名伪造的“假打印机”(比如叫“Printer_1234”的BLE信标),连上也发不出指令。热敏打印机基本都用固定服务 UUID:
- CPCL 类(芝珂 ZJ-5890、精臣 Q1):常用
'00001101-0000-1000-8000-00805F9B34FB' - ESC/POS 类(佳博、驰腾 CT-320):常用
'0000FFE0-0000-1000-8000-00805F9B34FB'或'000018F0-0000-1000-8000-00805F9B34FB' - 务必把目标 UUID 填进
uni.startBluetoothDevicesDiscovery的services数组,减少干扰
连上后还要查真实服务:调 uni.getBLEDeviceServices,确认返回的服务列表里真有这个 UUID;再用 uni.getBLEDeviceCharacteristics 找 properties.write === true && properties.notify === false 的特征值——纯发送型打印机只认这个。
writeBLECharacteristicValue 必须用 Uint8Array,且 CPCL 指令结尾是 \r\n
字符串直接传给 uni.writeBLECharacteristicValue 肯定失败。底层 BLE socket(尤其 Android 原生层)只接受 Uint8Array,且对换行符极其敏感:
- CPCL 指令如
"TEXT 24 0 30 50 Hello"+"BARCODE 128",必须拼成"TEXT 24 0 30 50 Hello\r\nBARCODE 128\r\n" - 用
\n代替\r\n,打印机大概率只执行第一行,后面被吞掉 - 别用
TextEncoder.encode()直接转——部分热敏机不认 UTF-8 编码的字节流,建议手动new Uint8Array([...])构造,或用unescape(encodeURIComponent(str))兼容老固件 - iOS 对单次 write 长度限制更严(通常 ≤ 20 字节),长指令需分包,每包末尾都带
\r\n
安卓位置权限必须动态申请,不能只靠 manifest 声明
uni.openBluetoothAdapter 成功 ≠ 能搜到设备。Android 6.0+ 和 iOS 都强制要求「扫描蓝牙设备」依赖位置权限,这是系统级限制:
- Android:manifest.json 的
app-plus.distribute.android.permissions必须声明ACCESS_FINE_LOCATION,且运行时必须主动调uni.authorize({scope: 'scope.location'}) - iOS:
NSLocationWhenInUseUsageDescription的 description 必须写明“用于搜索附近蓝牙打印机”,模糊描述(如“提升体验”)会被拒 - 微信小程序:部分华为、小米机型即使开了定位,也得手动进手机「设置 → 应用 → 小程序 → 权限 → 定位」点开,否则
onBluetoothDeviceFound根本不触发
最易忽略的是:不同平台的“连接成功”含义不同——安卓可能连上了但服务没缓存出来,iOS 可能连上了但特征值要等几百毫秒才可读。加 setTimeout 延迟 600–800ms 再查服务和特征值,比盲目重试更可靠。











