uni-app使用蓝牙的坑

前言

  在以前的一篇博客中,我记载了给公司写蓝牙锁连接的代码,然后后面我优化了一下代码,想节省一下时间,而这个节省时间让我遇到了问题。

  https://www.cnblogs.com/weiyanei/p/17039919.html

问题点

  服务发现保存蓝牙缓存

  用户扫码获取车锁信息,然后点击开锁按钮,车锁打开。

  这里的开锁按钮就是需要连接蓝牙,然后告诉车锁开锁,那么问题来了,我设计了如下这个表,其中保存了serviceid,characterid,deviceid,这些都是uni-app中连接蓝牙需要的信息。

CREATE TABLE `ims_app_bluetooth_lock_info` (
  `info_id` int NOT NULL AUTO_INCREMENT,
  `qrcode` varchar(30) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL COMMENT '锁表面的二维码',
  `mac` varchar(30) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL COMMENT '锁对应的蓝牙mac',
  `write_serviceid` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL COMMENT '写服务ID',
  `write_characterid` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL COMMENT '写特征值',
  `notify_serviceid` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL COMMENT '监听返回服务ID',
  `notify_characterid` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL COMMENT '监听返回特征值',
  `deviceid` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL,
  PRIMARY KEY (`info_id`)
) ENGINE=InnoDB AUTO_INCREMENT=11 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci COMMENT='蓝牙锁信息';

  我在想,既然我最终的目的是朝蓝牙锁写入指令,然后蓝牙锁告诉我返回数据,那么最重要的就是获取serviceid,characterid,deviceid这三个上了,我能节省时间的也就是在获取这三个上面的。

  在之前那篇博客中,我定义了整个流程的顺序,打开蓝牙适配器--> 监听搜索--> 开始搜索--> (发现目标蓝牙后) 关闭搜索-->  连接蓝牙--> 发送蓝牙指令 --> 断开蓝牙--> 关闭蓝牙适配器

  在发送蓝牙指令后,其实是要接收返回数据,这一步是在notify中实现的,uni.onBLECharacteristicValueChange 使用这个api监听蓝牙返回数据,

uni.notifyBLECharacteristicValueChange({
    state: true, // 启用 notify 功能
    // 这里的 deviceId 需要已经通过 createBLEConnection 与对应设备建立链接
    deviceId: ths.deviceInfo.deviceId,
    // 这里的 serviceId 需要在 getBLEDeviceServices 接口中获取
    // serviceId:"6E400001-E6AC-A7E7-B1B3-E699BAE8D000",
    serviceId: serviceId,
    // 这里的 characteristicId 需要在 getBLEDeviceCharacteristics 接口中获取
    // characteristicId:"6E400003-E6AC-A7E7-B1B3-E699BAE8D000",
    characteristicId: characteristicId,
    success(res) {
        // 必须在这里的回调才能获取
        uni.onBLECharacteristicValueChange(function(res) {
            var receiveValue = ths.ab2hex(res.value) //2进制数据转为16进制字符串
            // console.log("蓝牙返回数据为:"+receiveValue)
            ths.showDebugInfo("蓝牙返回数据为:"+receiveValue)

            if (ths.nowOper == ths.operType.getKey) { //获取秘钥
                ths.key = getKeyInfo(receiveValue);
                if (ths.key) {
                    ths.unlock();
                }
            } else if (ths.nowOper == ths.operType.unlock) { //解锁
                var lockState = unlockInfo(receiveValue);
                if (lockState) { //开锁成功
                    ths.disConnectBle();
                    //开锁成功后需要将定时器关闭
                    // clearTimeout(ths.timeOutValue)
                    ths.stopAll()

                    ths.hasUnLock = true;

                    //新增骑行开始
                    var param = {
                        info_id:ths.lockInfo.info_id,
                        qrcode:ths.lockInfo.qrcode,
                        bike_id:ths.bike.bike_id,
                        area_id:ths.bike.areaInfo.area_id
                    }
                    addBikeUseInfo(param).then(res=>{
                        if(res?.result_code=="0"){
                        }
                    })

                    ths.hideMyLoading(true);

                }
            }
        })

        //获取key  此处一定要延迟,要等Notify成功后才能发送
        setTimeout(function(){
            if(ths.nowOper==ths.operType.getKey){
                ths.getKey();
            }
        },2000);

    },
    fail(res) {
        ths.showDebugInfo(res)
        ths.startNotify = false;
        ths.stopAll()
        ths.hideMyLoading(false);
    },
})

  好,扯了一大堆还没说到问题点上,看下面的代码,

  我在第一次蓝牙发现后,获得了serviceid,characterid,deviceid保存到表里后,就不再所搜附近的蓝牙,直接根据deviceid去连接蓝牙,走的if逻辑,当然连接蓝牙是能连上

uni.openBluetoothAdapter({
    success(res) {
        ths.openAdapterFlag = true;

        if(ths.deviceInfo.deviceId){//一荣俱荣,一损俱损
            //有deviceId,说明已经存在各种连接信息,而且扫码那么就在锁附近了,就直接连接,不用在去发现蓝牙操作
            ths.connectBle()
        }else{
            //监听附近的蓝牙
            ths.findBluetooth(ths.deviceInfo.deviceId);

            //开始搜索蓝牙
            ths.startFindBluetooth();
        }
    },fail(res) {
        ths.showDebugInfo(res)
        ths.hideMyLoading(false,"请打开蓝牙");
    },
});

  

  当我连接上蓝牙后,如果有serviceid和characterid的话,那么说明已经不用调用getService和getCharacter了,

  看到下面的代码了么,表里保存了数据,则不调用getServices()方法,也就是uni.getBLEDeviceServices 和 uni.getBLEDeviceCharacteristics 这两个API的调用。

  直接就进入notify方法了,也就是调用  uni.notifyBLECharacteristicValueChange 这个API。

connectBle() {
    var ths = this; 

    uni.createBLEConnection({
        // 这里的 deviceId 需要已经通过 createBLEConnection 与对应设备建立链接
        deviceId:ths.deviceInfo.deviceId,
        success(res) {
            ths.deviceInfo.connect = true;

            if(ths.deviceInfo.write.serviceId&&ths.deviceInfo.write.characteristicId&&
               ths.deviceInfo.notify.serviceId&&ths.deviceInfo.notify.characteristicId){//已经有信息了,则不再获取
                setTimeout(function(){
                    ths.notify();
                },ths.notifyTimeoutValue)
            }else{
                //此处也需要延迟,刚连接上时,无法获取到特征值
                setTimeout(function(){
                    ths.getServices();
                },ths.serviceTimeoutValue)
            }
        },
        fail(res) {
            ths.showDebugInfo(res)
            ths.stopAll()
            ths.hideMyLoading(false);
        },
        complete(res) { //无论有没有连上蓝牙,都要停止搜索
            ths.stopFindBluetooth(); //停止搜索蓝牙
        }
    })
},

  结果它会报错 fail no connection , 1004

  诈一看这个报错描述,会以为是不是没有连接上蓝牙,但其实压根不是这么回事,就是没有调用 uni.startBluetoothDevicesDiscovery 和  uni.onBluetoothDeviceFound 导致的。

  看后面的1004,这个对应官网错误描述是,没有找到指定服务,这个指定服务其实就是没有找到对应蓝牙缓存。

  当调用 uni.startBluetoothDevicesDiscovery 后,我猜其实是把发现的蓝牙服务保存在手机蓝牙缓存中,然后调用notify时传递的 serviceId,characterid,deviceId其实就是根据这三个key,去蓝牙列表中寻找对应服务,找到了就是有服务,没找到就是没有服务。

  所以这个过程应该是如下图所示,标注为黄色的都是需要从缓存中获取蓝牙信息,也就是说需要uni.startBluetoothDevicesDiscovery缓存的信息,否则会报 1004错误。

流程1

 

  

  而由于我将serviceid保存在表里面,然后不再开启服务发现,自然也没有蓝牙缓存,结果就是如下流程,报1004错误,

  

  但是每次调用 uni.getBLEDeviceServices获取serviceIds  和  uni.getBLEDeviceCharacteristics获取characterids 确实会浪费一些时间,所以整个流程就可以优化成如下所示,

  这是我碰到的第一个坑。

  这个也好还原,只需要把手机蓝牙打开,运行成功后,数据库有serviceid,characterid和deviceid后,第二次执行前把手机蓝牙关闭,再打开,就个清空了缓存。这样照着流程2走的时候就会GG。

 

 延迟问题

    这个问题在上一篇博客中也写到了,估计还是官网API的问题,在连接蓝牙后要延迟一段时间,否则会出现getService, notify都会出问题的情况。

    这玩意没啥好解决的方法,我其实是设定了几个全局的超时时间,每点击一次按钮失败就增加几秒超时,理论上说,用户第一次失败10s算超时,第二次失败15s算超时,这样用来解决问题,也没啥好办法。

    其实这过程中还遇到连接蓝牙后写入数据很长时间,导致超时失败,然后再点击写入数据又很快写入。

    还有蓝牙连接成功,notify偶尔失败的情况。

    不知道是官网这个蓝牙的API不完善还是工厂生产的蓝牙锁有问题。 

 

posted @ 2023-03-09 11:06  伟衙内  阅读(1638)  评论(0编辑  收藏  举报