w***@163.com
w***@163.com
  • 发布:2024-01-29 14:25
  • 更新:2025-05-29 14:25
  • 阅读:1090

【报Bug】安卓APP低功耗蓝牙写入数据经常失败,报10007错误

分类:uni-app

产品分类: uniapp/App

PC开发环境操作系统: Windows

PC开发环境操作系统版本号: 10

HBuilderX类型: 正式

HBuilderX版本号: 3.99

手机系统: Android

手机系统版本号: Android 13

手机厂商: OPPO

手机机型: oppo reno 7

页面类型: vue

vue版本: vue2

打包方式: 云端

项目创建方式: HBuilderX

示例代码:
setTimeout(()=>{  
                  console.log(that.characteristics)  
                  console.log(that.connectedDeviceId)  
                  console.log(that.services[getApp().globalData.services_code].uuid)  
                  console.log(that.characteristics[getApp().globalData.Tcharacter_code].uuid)  
                  uni.writeBLECharacteristicValue({            //向低功耗蓝牙设备特征值中写入二进制数据  
                    deviceId: that.connectedDeviceId,  
                    serviceId: that.services[getApp().globalData.services_code].uuid,  
                    characteristicId: that.characteristics[getApp().globalData.Tcharacter_code].uuid,  
                    value: buffer,  
                    success: function (res) {  
                      setTimeout(()=>{  
                          console.log(res)  
                          console.log('助手发送成功home信息')  
                      },10)  
                    },  
                    fail: function (res) {  
                        console.log(res)  
                        console.log(that.connectedDeviceId)  
                        console.log(that.services[getApp().globalData.services_code].uuid)  
                        console.log(that.characteristics[getApp().globalData.Tcharacter_code].uuid)  
                            console.log('助手发送失败home信息')  
                    }  
                  })  
              },1000) 

操作步骤:

连接蓝牙后,写入数据时有时报错10007有时正常

预期结果:

{"errMsg":"writeBLECharacteristicValue:ok"}

实际结果:

{"errMsg":"writeBLECharacteristicValue:fail property not support","errCode":10007}

bug描述:

使用uniapp开发低功耗蓝牙时,小程序端一切正常,但安卓APP端和安卓APP基座经常会写入数据失败提示10007错误,

{    
  "errMsg": "writeBLECharacteristicValue:fail property not support",    
  "errCode": 10007    
}

但此时读到的特征值是有write权限的,失败一段时间后会突然又可以正常收发数据。基座端如果不断开手机连接会一直可以成功发送数据,APP端有时可以有时候不行。

2024-01-29 14:25 负责人:无 分享
已邀请:
小许吃得饱

小许吃得饱

你好,这个问题解决了吗

5***@qq.com

5***@qq.com

并行调用多次会存在写失败的可能性。
会不会是因为你放在异步中,然后在重复调用。

2***@qq.com

2***@qq.com

请问解决了吗?我也有出现这个问题

  • 不如摸鱼去

    写入失败时,在失败的回调里面,尝试用失败的这次的数据再写一次。

    2024-11-26 17:42

  • 1***@qq.com

    回复 不如摸鱼去: 写数据太慢了 写完全部数据得10分钟

    2025-01-16 17:38

  • 不如摸鱼去

    回复 1***@qq.com: 用经典蓝牙

    2025-01-18 12:02

3***@qq.com

3***@qq.com

搜了半天,也没找到解决办法,加延迟,失败重写都不行啊

5***@qq.com

5***@qq.com

给大家发一段我实际Android App项目中的代码,实测【汉印-A300Q】可以连续并稳定批量打印标签和票据,经过多次测试,有2个关键点需要添加延迟,一个是调用uni.createBLEConnection需要延迟800ms(多次测试比较的经验值)和调用uni.writeBLECharacteristicValue分包写入20字节后需要延迟40ms(多次测试比较的经验值),否则,就会出现各种不稳定。
最后,调用printAny方法可以批量打印,示例:

// peripheralId 是蓝牙设备Id,  
// data: 是字节数组,示例:[33,32,48,32,50,48,48,32,50,48,48,32,51,55,48,32,49,13,10,80,65,71,69,45,87,73,68,84,72,32,53,56,48,13,10,67,69,78,84,69,82,13,10,83,69,84,66,79,76,68,32,50,13,10,84,69,88,84,32,50,32,54,32,48,32,49,48,32,188,206,188,177,203,205,206,239,193,247,13,10,84,69,88,84,32,50,32,53,32,48,32,57,48,32,182,171,190,219,32,214,193,32,186,163,182,171,213,242,13,10,83,69,84,66,79,76,68,32,48,13,10,84,69,88,84,32,50,32,51,32,50,32,49,55,48,32,202,213,187,245,200,203,58,32,205,245,209,222,209,222,13,10,84,69,88,84,32,50,32,51,32,50,32,50,50,48,32,183,162,182,175,187,250,44,50,188,254,13,10,66,65,82,67,79,68,69,45,84,69,88,84,32,79,70,70,13,10,66,65,82,67,79,68,69,32,49,50,56,32,51,32,51,32,54,48,32,48,32,50,55,48,32,48,53,50,56,49,49,52,56,53,51,49,48,50,13,10,84,69,88,84,32,50,32,48,32,50,32,51,52,48,32,48,53,50,56,49,49,52,56,53,51,49,48,50,32,181,218,49,188,254,32,47,32,185,178,50,188,254,13,10,70,79,82,77,13,10,80,82,73,78,84,13,10,33,32,48,32,50,48,48,32,50,48,48,32,51,55,48,32,49,13,10,80,65,71,69,45,87,73,68,84,72,32,53,56,48,13,10,67,69,78,84,69,82,13,10,83,69,84,66,79,76,68,32,50,13,10,84,69,88,84,32,50,32,54,32,48,32,49,48,32,188,206,188,177,203,205,206,239,193,247,13,10,84,69,88,84,32,50,32,53,32,48,32,57,48,32,182,171,190,219,32,214,193,32,186,163,182,171,213,242,13,10,83,69,84,66,79,76,68,32,48,13,10,84,69,88,84,32,50,32,51,32,50,32,49,55,48,32,202,213,187,245,200,203,58,32,205,245,209,222,209,222,13,10,84,69,88,84,32,50,32,51,32,50,32,50,50,48,32,183,162,182,175,187,250,44,50,188,254,13,10,66,65,82,67,79,68,69,45,84,69,88,84,32,79,70,70,13,10,66,65,82,67,79,68,69,32,49,50,56,32,51,32,51,32,54,48,32,48,32,50,55,48,32,48,53,50,56,49,49,52,56,53,51,49,48,50,13,10,84,69,88,84,32,50,32,48,32,50,32,51,52,48,32,48,53,50,56,49,49,52,56,53,51,49,48,50,32,181,218,50,188,254,32,47,32,185,178,50,188,254,13,10,70,79,82,77,13,10,80,82,73,78,84,13,10]  

printAny([{ type: '票据', peripheralId: [设备Id], data: [number[]] })  
                    .then(({ succeed, error }) => {  
                        if (!succeed) {  
                            uni.showToast({  
                                title: error,  
                                icon: 'none',  
                                duration: 2000  
                            })  
                            resolve(false)  
                        }  
                        else{  
                            resolve(true)  
                        }  
                    })
// 打印相关接口  
import _ from 'lodash'  

/**  
 * 打印机服务参数  
 * @typedef {Object} PrinterParameter  
 * @property {string} id 打印机Id(地址)  
 * @property {string} name 打印机名称  
 */  

/**  
 * @typedef PrintResult  
 * @property {boolean} succeed 是否执行成功  
 * @property {string} error 执行失败时的错误  
 */  

/**  
 * @typedef PrintArgs  
 * @property {string} type 打印内容类型  
 * @property {string} peripheralId 设备Id  
 * @property {number[]} data 打印指令字节  
 */  

/**  
 * 获取标签打印机参数  
 * @returns {PrinterParameter} 打印机参数,如果未设置返回undefined  
 */  
const getLabelPrinter = () => {  
    const value = uni.getStorageSync('labelPrinter')  
    if (value) {  
        return value  
    } else {  
        return undefined  
    }  
}  

/**  
 * 设置标签打印机参数  
 * @param {PrinterParameter} parameter 打印机参数  
 */  
const setLabelPrinter = parameter => {  
    if (!parameter) {  
        throw new Error('错误的打印机参数')  
    }  
    uni.setStorageSync('labelPrinter', parameter)  
}  

/**  
 * 获取票据打印机参数  
 * @returns {PrinterParameter} 打印机参数,如果未设置返回undefined  
 */  
const getReceiptPrinter = () => {  
    const value = uni.getStorageSync('receiptPrinter')  
    if (value) {  
        return value  
    } else {  
        return undefined  
    }  
}  

/**  
 * 设置票据打印机参数  
 * @param {PrinterParameter} parameter 打印机参数  
 */  
const setReceiptPrinter = parameter => {  
    if (!parameter) {  
        throw new Error('错误的打印机参数')  
    }  
    uni.setStorageSync('receiptPrinter', parameter)  
}  

/**  
 * 连接打印机并可向多个打印机写入打印数据,该方法每次调用时先初始化蓝牙模块  
 * @param {PrintArgs[]} items 打印参数数组  
 * @returns {Promise<PrintResult>}  
 */  
const printAny = async items => {  
    return new Promise(resolve => {  
        uni.openBluetoothAdapter({  
            success: () => {  
                console.log('已打开蓝牙模块资源')  
                const actions = new Array()  
                items.forEach(args => {  
                    actions.push(printSingle(args))  
                })  
                Promise.all(actions)  
                    .then(results => {  
                        for (const result of results) {  
                            const { succeed, error } = result  
                            if (!succeed) {  
                                resolve({  
                                    succeed: false,  
                                    error: error  
                                })  
                                return  
                            }  
                        }  
                        resolve({  
                            succeed: true  
                        })  
                    })  
                    .finally(() => {  
                        // 执行完所有打印后,释放蓝牙模块资源  
                        console.log('已释放蓝牙模块资源')  
                        uni.closeBluetoothAdapter()  
                    })  
            },  
            fail: () => {  
                resolve({  
                    succeed: false,  
                    error: '请打开手机蓝牙'  
                })  
            }  
        })  
    })  
}  

/**  
 * 向打印机写入打印数据,该方法每次调用时先连接设备,向打印机写入数据完成后,立即断开设备连接  
 * 说明:调用此方法前,先确保已成功初始化蓝牙模块,调用方法后不释放蓝牙模块资源  
 * @param {PrintArgs} args 打印参数  
 * @returns {Promise<PrintResult>}  
 */  
const printSingle = async args => {  
    return new Promise(resolve => {  
        const { type, peripheralId, data } = args  
        /**  
         * 向打印机写入指令的顺序  
         * 1. 连接打印机  
         * 2. 获取打印机所有服务的特征值中具有写入功能的一项  
         * 3. 向打印机写入打印指令  
         */  
        uni.createBLEConnection({  
            deviceId: peripheralId,  
            timeout: 3000,  
            success: () => {  
                _.delay(() => {  
                    getWritableCharacteristic(peripheralId).then(async result => {  
                        if (result) {  
                            // 将number[]分批向打印机发送(每批20个字节)  
                            let batchSize = 20  
                            let times = Math.floor(data.length / batchSize)  
                            if (data.length % batchSize != 0) {  
                                times++  
                            }  
                            for (let i = 0; i < times; i++) {  
                                const pieceData = data.slice(i * batchSize, i * batchSize + batchSize)  
                                const writeRes = await writeBLECharacteristicValue(peripheralId, result.serviceId, result.characteristicId, pieceData)  
                                if (!writeRes) {  
                                    uni.closeBLEConnection({  
                                        deviceId: peripheralId  
                                    })  
                                    resolve({  
                                        succeed: false,  
                                        error: `${type}打印失败,请稍候重试`  
                                    })  
                                    return  
                                }  
                                await sleep(40)  
                            }  
                            uni.closeBLEConnection({  
                                deviceId: peripheralId  
                            })  
                            resolve({  
                                succeed: true  
                            })  
                        } else {  
                            uni.closeBLEConnection({  
                                deviceId: peripheralId  
                            })  
                            resolve({  
                                succeed: false,  
                                error: `${type}打印机未准备就绪,请稍候重试`  
                            })  
                        }  
                    })  
                }, 800)  
            },  
            fail: error => {  
                resolve({  
                    succeed: false,  
                    error: `连接${type}打印机失败,请重试`  
                })  
            }  
        })  
    })  
}  

/**  
 * 休眠操作  
 */  
const sleep = async ms => {  
    return new Promise(resolve => {  
        _.delay(resolve, ms)  
    })  
}  

/**  
 * 单次向打印机写入数据  
 * @param {string} peripheralId 打印设备Id  
 * @param {string} serviceId 打印设备服务Id  
 * @param {string} characteristicId 打印设备服务特征Id  
 * @param {number[]} data 写入数据  
 */  
const writeBLECharacteristicValue = async (peripheralId, serviceId, characteristicId, data) => {  
    return new Promise(resolve => {  
        if (data.length == 0) {  
            resolve(true)  
            return  
        }  
        uni.writeBLECharacteristicValue({  
            deviceId: peripheralId,  
            serviceId,  
            characteristicId,  
            value: new Uint8Array(data).buffer,  
            writeType: 'write',  
            success: () => {  
                // console.log('向打印机写入成功')  
                resolve(true)  
            },  
            fail: error => {  
                // console.log('向打印机写入失败', error)  
                resolve(false)  
            }  
        })  
    })  
}  

/**  
 * 使用指定打印机打印标签,该方法每次调用时先连接设备,向打印机写入数据完成后,立即断开连接  
 * @param {string} peripheralId 蓝牙设备Id  
 * @param {number[]} data 打印内容命令  
 * @returns {Promise<PrintResult>}  
 */  
const printLabel = async (peripheralId, data) =>  
    await printAny([  
        {  
            type: '标签',  
            peripheralId,  
            data  
        }  
    ])  

/**  
 * 使用指定打印机打印标签,该方法每次调用时先连接设备,向打印机写入数据完成后,立即断开连接  
 * @param {string} peripheralId 蓝牙设备Id  
 * @param {number[]} data 打印内容命令  
 * @returns {Promise<PrintResult>}  
 */  
const printReceipt = async (peripheralId, data) =>  
    await printAny([  
        {  
            type: '票据',  
            peripheralId,  
            data  
        }  
    ])  

/**  
 * 打印机服务特征值  
 * @typedef {Object} CharacteristicResult  
 * @property {string} serviceId 服务Id  
 * @property {string} characteristicId 特征值Id  
 */  

/**  
 * 获取指定打印机的可写入的服务特征值,调用此函数时需要已连接设备  
 * @param {Object} peripheralId 打印机设备Id  
 * @returns {Promise<CharacteristicResult>} 服务特征值  
 */  
const getWritableCharacteristic = peripheralId => {  
    return new Promise(resolve => {  
        // 获取打印机服务列表  
        const serviceCharacteristics = new Array()  
        uni.getBLEDeviceServices({  
            deviceId: peripheralId,  
            success: ({ services }) => {  
                // console.log('services', services)  
                const apis = new Array()  
                services.forEach(p => {  
                    // 获取每个服务的特征值列表  
                    apis.push(getServiceCharacteristics(peripheralId, p.uuid))  
                })  
                Promise.all(apis).then(result => {  
                    const serviceCharacteristics = result.find(p => !_.isNil(p) && p.serviceId.length == 36)  
                    // console.log('valid characteristics', serviceCharacteristics)  
                    if (serviceCharacteristics) {  
                        resolve(serviceCharacteristics)  
                    } else {  
                        resolve(undefined)  
                    }  
                })  
            },  
            fail: err => {  
                console.log(`getBLEDeviceServices error:${JSON.stringify(err)}`)  
                resolve(undefined)  
            }  
        })  
    })  
}  

/**  
 * 获取指定打印机某一服务的可写特征值  
 * @param {Object} peripheralId 打印机Id  
 * @param {Object} serviceId 打印机服务Id  
 * @returns {Promise<CharacteristicResult>} 服务特征值  
 */  
const getServiceCharacteristics = (peripheralId, serviceId) => {  
    return new Promise(resolve => {  
        uni.getBLEDeviceCharacteristics({  
            deviceId: peripheralId,  
            serviceId,  
            success: ({ characteristics }) => {  
                const item = characteristics.find(p => p.properties.write)  
                if (item) {  
                    resolve({  
                        serviceId,  
                        characteristicId: item.uuid  
                    })  
                } else {  
                    resolve(undefined)  
                }  
            },  
            fail: () => {  
                resolve(undefined)  
            }  
        })  
    })  
}  

export { getLabelPrinter, setLabelPrinter, getReceiptPrinter, setReceiptPrinter, printLabel, printReceipt, printAny }
9***@qq.com

9***@qq.com

老兄 你的HBuilder X升级到最新v4.66了吗?我发现升级之后 安卓的搜索正常 苹果版的首次搜索可以 后面刷新搜索就不正常了,无法查找 有没有遇到类似问题?咋解决呢

  • 5***@qq.com

    升级到最新版v4.66了,但是我们没做ios版

    2025-05-29 15:45

要回复问题请先登录注册