微信小程序开发----BLE蓝牙通信(结尾包含ios和android的部分坑和兼容问题)

最近随项目接触了下小程序的BLE开发。体会颇多。

小程序BLE开发前的准备操作:

    1、熟读小程序开发文档,文档链接

    2、微信开发者工具。

    3、对应的BLE蓝牙设备,我们需要对BLE设备进行读写操作。

一、小程序BLE开发API简介

微信小程序目前有蓝牙 API 共 18 个,其中操作蓝牙适配器的共有 4 个,分别是

wx.openBluetoothAdapter 初始化蓝牙适配器
wx.closeBluetoothAdapter 关闭蓝牙模块
wx.getBluetoothAdapterState 获取本机蓝牙适配器状态
wx.onBluetoothAdapterStateChange 监听蓝牙适配器状态变化事件

其中,扫描和获取周围BLE设备的有4个(这4个操作方式与普通蓝牙一样):

wx.startBluetoothDevicesDiscovery 开始搜寻附近的蓝牙外围设备
wx.stopBluetoothDevicesDiscovery 停止搜寻附近的蓝牙外围设备
wx.getBluetoothDevices 获取所有已发现的蓝牙设备
wx.onBluetoothDeviceFound 监听寻找到新设备的事件

连接BLE设备的2个:

wx.createBLEConnection 连接低功耗蓝牙设备
wx.closeBLEConnection 断开与低功耗蓝牙设备的连接

连接成功后,读写BLE对应特征对象的数据:

wx.getConnectedBluetoothDevices 根据 uuid 获取处于已连接状态的设备
wx.getBLEDeviceServices 获取蓝牙设备所有 service(服务)
wx.getBLEDeviceCharacteristics  获取蓝牙设备所有 characteristic(特征值)
wx.readBLECharacteristicValue  读取低功耗蓝牙设备的特征值的二进制数据值
wx.writeBLECharacteristicValue 向低功耗蓝牙设备特征值中写入二进制数据
wx.notifyBLECharacteristicValueChange  启用低功耗蓝牙设备特征值变化时的 notify 功能
wx.onBLECharacteristicValueChange 监听低功耗蓝牙设备的特征值变化
wx.onBLEConnectionStateChange 监听低功耗蓝牙连接的错误事件

二、微信小程序开发前需要注意的事项

android微信支持BLE蓝牙的微信版本为:6.5.7;

android 6.0 以上的手机未打开系统定位服务时,搜索不到蓝牙设备;

ios微信支持BLE蓝牙的微信版本为:6.5.6。

所以做BLE蓝牙开发,为了提高用户的小程序体验感,需要对用户使用的微信版本以及android版本进行判断,并做合理的解决方式。

下面是获取手机当前微信版本和手机系统版本的封装方法,将方法放入apps.js全局js文件中:

onLaunch: function() {
    this.globalData.sysinfo = wx.getSystemInfoSync()
},
getModel: function () { //获取手机型号
    return this.globalData.sysinfo["model"]
},
getVersion: function () { //获取微信版本号
    return this.globalData.sysinfo["version"]
},
getSystem: function () { //获取操作系统版本
    return this.globalData.sysinfo["system"]
},
getPlatform: function () { //获取客户端平台
    return this.globalData.sysinfo["platform"]
},
getSDKVersion: function () { //获取客户端基础库版本
    return this.globalData.sysinfo["SDKVersion"]
}

为了对手机微信版本和系统版本做比较,封装有一个版本比较的方法

versionCompare: function (ver1, ver2) { //版本比较
    var version1pre = parseFloat(ver1)
    var version2pre = parseFloat(ver2)
    var version1next = parseInt(ver1.replace(version1pre + ".", ""))
    var version2next = parseInt(ver2.replace(version2pre + ".", ""))
    if (version1pre > version2pre)
        return true
    else if (version1pre < version2pre) 
        return false
    else {
        if (version1next > version2next)
            return true
        else
            return false
    }
}

所以,在实际的操作中,比如点击了按钮需要调用手机系统蓝牙去扫描操作之前,需要校验以上的各项参数。整体代码:

//判断系统版本、微信版本、定位服务等权限和信息
function checkPhoneInfo(obj){
  //Android 从微信 6.5.7 开始支持,iOS 从微信 6.5.6 开始支持
  //第一项,如果手机是android系统,需要判断版本信息
  if (apps.getPlatform() == "android" && versionCompare("6.5.7", apps.getVersion())) {
    wx.showModal({
      title: '提示',
      content: '当前微信版本过低,请更新至最新版本',
      showCancel: false
    });
    obj.setData({
      btnDisabled: false,
    });
     //执行quit机制
    quit(obj);

    return;
  }
  //第二项,如果手机是ios系统,需要判断版本信息
  if (apps.getPlatform() == "ios" && versionCompare("6.5.6", apps.getVersion())) {
    wx.showModal({
      title: '提示',
      content: '当前微信版本过低,请更新至最新版本',
      showCancel: false
    });
    obj.setData({
      btnDisabled: false,
    });
     //执行quit机制
    quit(obj);

    return;
  }
  //版本  以及  平台校验完毕后   需要判断蓝牙的相关信息
  //微信小程序 android6.0手机需要开启位置服务,否则扫描不到设备
  console.log("当前系统版本:" + apps.getSystem());//Android 8.1.0
  console.log("当前微信版本:" + apps.getVersion());
  if (apps.getPlatform() == "android") {
    console.log("android手机  当前系统版本号:" + apps.getSystem().replace("Android", "").replace(" ", ""));
    //android版本高于6.0.0
    if (!versionCompare("6.0.0", apps.getSystem().replace("Android","").replace(" ",""))) {
      console.log("当前系统版本高于6.0.0");
      //位置服务权限
      wx.getSetting({
        success: function (res) {
          var statu = res.authSetting;
          //位置服务授权校验操作
          if (!statu['scope.userLocation']) {
            wx.showModal({
              title: '温馨提示',
              content: '请授予位置服务权限,以便更好的搜索周围设备',
              success: function (tip) {
                if (tip.confirm) {
                  //点击确认  开始判断位置服务权限信息
                  wx.openSetting({
                    success: function (data) {
                      if (data.authSetting["scope.userLocation"] === true) {
                        wx.showToast({
                          title: '授权成功',
                          icon: 'success',
                          duration: 1000
                        })
                        //授权成功之后,调用自己封装的蓝牙各项操作
                        bleOperateFun(obj);

                      } else {
                        wx.showToast({
                          title: '授权失败',
                          icon: 'none',
                          duration: 1000
                        });
                        obj.setData({
                          btnDisabled: false,
                        });
                         //执行quit机制
                        quit(obj);

                      }
                    }
                  })
                }else{
                  console.log("点击了取消操作");
                  obj.setData({
                    btnDisabled: false,
                  });
                   //执行quit机制
                  quit(obj);

                }
              }
            })
          }else {
            
            //存在权限,调用封装的蓝牙方式继续进行 
            bleOperateFun(obj);
          }
        },
        fail: function (res) {
          wx.showToast({
            title: '调用授权窗口失败',
            icon: 'success',
            duration: 1000
          });
          obj.setData({
            btnDisabled: false,
          });
           //执行quit机制
          quit(obj);

        }
      })
    } else if (!versionCompare(apps.getSystem().replace("Android", "").replace(" ", "")), "4.3.0") {
      //系统版本低于4.3的,使用不了ble蓝牙
      wx.showModal({
        title: '温馨提示',
        content: '您的手机系统版本较低,无法操作BLE蓝牙设备',
        showCancel:false,
      });
      obj.setData({
        btnDisabled: false,
      });
       //执行quit机制
      quit(obj);

    }else {
      console.log("系统版本低于6.0.0但高于4.3.0");
      //除去android系统的手机(由于最开始过滤了能支持蓝牙的最低微信版本   所以此处无需再判断)
      bleOperateFun(obj);
    }
  }
//除android以外的ios或者其他系统
bleOperateFun(obj);
}

三、BLE蓝牙开发,开启当前手机ble蓝牙适配器,注册相关的监听事件

function bleOperateFun(obj){
  //1、判断手机蓝牙是否开启
  wx.openBluetoothAdapter({
    success: function(res) {
      console.log("初始化蓝牙适配器成功");

      //1、开启蓝牙适配器的状态变化监听
      wx.onBluetoothAdapterStateChange(function(res){
        console.log("蓝牙适配器状态变化",res);
        
      });
      //2、开启搜索周围设备的情况监听事件     (扫描   开锁  两次按钮点击   针对同一个设备只执行了一次)
      wx.onBluetoothDeviceFound(function(res){
        console.log("扫描周围设备详情返回:"+JSON.stringify(res));
        //判断是否为 "创想物联" 的设备  若是则需要进行连接操作和关闭扫描操作(节省手机资源)
        res.devices.forEach(device=>{
          console.log(device.name +"/"+device.localName);
          console.log("广播数据:" + apps.ab2hex(device.advertisData));
          
        });
      });
      //  3、连接状态变化监听 --监听低功耗蓝牙连接状态的改变事件。包括开发者主动连接或断开连接,设备丢失,连接异常断开等等
      wx.onBLEConnectionStateChange(function(res){
        console.log("蓝牙设备连接状态监听回调:\n"+JSON.stringify(res));
      });
      //  4、读取特征值数据监听事件  ---- wx.readBLECharacteristicValue(Object object)调用后,若要取得其中的数据,需要再此回调中获取
      wx.onBLECharacteristicValueChange(function(res){
        console.log("wx.onBLECharacteristicValueChange 监听事件:\n"+JSON.stringify(res));
        

      })
      //开启扫描周围设备模式
      startScanAroundDevice(obj);
    },
    fail:function(res){
      console.log("初始化蓝牙适配器失败")
      wx.showModal({
        title: '温馨提示',
        content: '请检查手机蓝牙是否打开',
        showCancel:false,
      });
      obj.setData({
        btnDisabled: false,
      });
       //执行quit机制
      quit(obj);
    }
  })
}

在开启扫描操作后,会触发设置好的 wx.onBluetoothDeviceFound 回调函数,当发现了设备,则会显示设备的mac,uuid,广播数据等。这里有一个重点:

获取广播数据,小程序中读取 BLE 广播数据使用 wx.onBluetoothDeviceFound 接口中的 advertisData,对应上面兼容问题的 devices 格式,如 devices.advertisData,这个数据是 ArrayBuffer类型的数据,如果需要显示至日志中,需要对其进行相关的转换处理操作,使用如下代码:

//广播数据为Array Buffer类型   ArrayBuffer转16进度字符
  ab2hex: function(buffer) {
    var hexArr = Array.prototype.map.call(
      new Uint8Array(buffer),
      function(bit) {
        return ('00' + bit.toString(16)).slice(-2)
      }
    )
    return hexArr.join('');
  },

四、小程序BLE开发,扫描到指定设备后,需要对其进行连接操作

      连接成功后,为了节省手机端的资源,需要对扫描进行关闭处理。(假设设置扫描开始,10秒后会有关闭操作流程;但可能在2秒的时候已经扫描到指定设备,我们知道,扫描操作及其耗电,所以为了避免过多的资源浪费,需要提前进行关闭操作)。

五、读写操作

    1、按照微信提供的api,可以进行读写操作,读操作成功后会触发之前(二)中的设置监听事件  wx.onBLECharacteristicValueChange ,而写操作只有成功和失败没有回调。

    2、写操作的api为

wx.writeBLECharacteristicValue({
    deviceId: obj.data.lockMac,
    serviceId: apps.bleProperties.bleServiceUUID,
    characteristicId: apps.bleProperties.writeCharacUUID,
    value: sendValue,
    success: function(res) {
      console.log("发送开锁命令成功回调:\n"+JSON.stringify(res));
      
    },
    fail(res){
      console.log("写数据失败回调"+res);
        wx.showModal({
          title: '温馨提示',
          content: '发送开锁命令失败!',
          showCancel:false,
        })
        quit(obj);
      
    }
  })

其中的各项参数在api文档中都有充分的介绍,这里不说其他废话,但其中的 value 属性为 ArrayBuffer 类型,所以需要进行数据格式的转换操作

strToArrayBuffer:function(aes,str){
    //首先将str转化为数组
    var strtoHexArray = aes.hex_to_bytes(str);
    //取一半数据的长度作为arraybuffer的长度
    var buffer = new ArrayBuffer(str.length >> 1);
    var bufView = new DataView(buffer);
    //保存数据至arraybuffer中
    for(var i = 0,len = (str.length >> 1);i<len;i++){
      //bufView[i] = strtoHexArray[i].toString(16);
      bufView.setUint8(i, strtoHexArray[i]);
    }
    return buffer;
  }

其中 aes.hex_to_bytes(str) 是我调用的库的方式,是将 十六进制的字符串(如:"6E"),转换成十六进制的数组类型。

function hex_to_bytes(str) {
        var len = str.length;
        if (len & 1) {
            str = '0' + str;
            len++;
        }
        var bytes = new Uint8Array(len >> 1);
        for (var i = 0; i < len; i += 2) {
            bytes[i >> 1] = parseInt(str.substr(i, 2), 16);
        }
        return bytes;
    }

六、最后写操作中有几个潜在的重点

    1、BLE 4.0 中发送一个数据包只能包含 20 字节的数据,大于 20 字节只能分包发送。没做ios的测试(人穷用不起苹果),就拿android来说,每次最大发送20字节的数据,之前就说过了写操作没有可设置的回调,所以不需要操作过于麻烦。只需要在写入20字节成功之后,延迟一段时间(这个时间不好控制,我用的小米8测试设置200ms无压力,不同手机设置值肯定不同,需要找到一个合理的参数做为一个统一的处理),再进行下一个包的发送。

    2、当你扫描操作执行后,假如未扫描到设备,再次点击按钮进行扫描,即使有BLE设备在广播,也不会再扫描到设备。遇到这样的情况,需要释放手机端蓝牙的资源,代码如下(我的代码放于quit机制中):

//如果扫描操作执行了的  需要释放手机蓝牙的资源
  if (isStartScan) {
    wx.closeBluetoothAdapter({
      success: function (res) {
        console.log("quit  wx.closeBluetoothAdapter 成功回调  清理手机蓝牙资源");
        console.log(JSON.stringify(res));
      },
      fail(res) {
        console.log("清理手机端  蓝牙  资源失败回调  " + res);
      }
    })
  }

   3、2019.03.04新增一个问题总结。android手机使用小程序的BLE模块,广播中的deviceId表示设备的mac信息,ios系统则是手机mac和设备mac加密产生的uuid值!连接设备也如此:安卓直接使用mac进行连接操作;但ios使用广播中读取到的UUID进行连接。

    4、2019.03.05新增一个问题。Android手机使用小程序操作BLE设备,连接成功后可以直接进行特征数据的获取;但ios直接调用时,会出现10004的报错  官方文档报错信息连接

如何解决ios手机使用小程序BLE报错问题。

只需要在连接成功和读设备特征数据之间,进行一项操作即可

wx.createBLEConnection({
    deviceId: obj.data.lockMac,
    success: function(res) {
      wx.hideLoading();
      wx.showToast({
        title: '连接成功',
        icon: 'success',
        duration: 1000
      })
      console.log("连接success回调:"+JSON.stringify(res));
      //关闭扫描
      stopScanAroundDevice(obj);
      //连接成功后  进行开锁流程操作(读  ---  写  --  读)
      //判断是android还是ios(流程不同)
      if (apps.getPlatform() == "android"){
        firstReadCharacVal(obj);
        return;
      }
      //ios
      wx.getBLEDeviceServices({
        // 这里的 deviceId 需要已经通过 createBLEConnection 与对应设备建立链接
        deviceId: obj.data.lockMac,
        success(res) {
          console.log(JSON.stringify(res))
          console.log('device services:', res.services)
          console.log('device services:', apps.ab2hex(res))
          console.log('device services:', apps.ab2hex(res.services))

          //获取设备特征对象
          wx.getBLEDeviceCharacteristics({
            deviceId: obj.data.lockMac,
            serviceId: apps.bleProperties.bleServiceUUID,
            success: function(res) {
              console.log(JSON.stringify(res))
              console.log("wx.getBLEDeviceCharacteristics  ---->\n" + apps.ab2hex(res));
              firstReadCharacVal(obj);
            },
            fail:function(){
              wx.showModal({
                title: '温馨提示',
                content: '获取特征对象失败!',
                showCancel: false
              });
              quit(obj);
            }
          })
        },
        fail:function(){
          wx.showModal({
            title: '温馨提示',
            content: '获取服务失败!',
            showCancel:false
          });
          quit(obj);
        }
      })
    },
    fail: function (res) {
      wx.hideLoading();
      wx.showToast({
        title: '连接设备失败',
        icon: 'success',
        duration: 1000
      })
      console.log("连接设备失败")
      console.log("连接fail回调:"+res);
      obj.setData({
        btnDisabled: false,
      });
      //执行 quit 机制
      quit(obj);
    }
  })

也就是说连接成功后,需要多进行一次wx.getBLEDeviceServices 和 wx.getBLEDeviceCharacteristics !!

猜你喜欢

转载自blog.csdn.net/qq_38322527/article/details/87726249