IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 移动开发 -> 微信小程序——Ble之notify通信 -> 正文阅读

[移动开发]微信小程序——Ble之notify通信

前言

之前BLE蓝牙通信博客中,重点说明了使用读 -- 写的模式,实现手机端和设备端的BLE信息交互操作。

但在一般的BLE设备中,存在可以使用notify通信的方式。

本篇博客重点说明小程序 BLE使用notify实现数据交互的方式,以及部分测试时碰见的坑!

准备

在开始写具体代码之前,需要准备以下材料:

  • 微信小程序开发者工具
  • 带有BLE的设备,且某个特征值对象支持notify
  • 一款支持BLE的手机

注意事项

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

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

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

开发文档概述

在使用BLE的notify功能之前,还需要熟读微信小程序开发文档。特别注意以下几个重要的API:

  • 1、手机蓝牙端接口

    关于手机蓝牙端,需要注意以下几个接口:

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

    其次,在使用这些接口后,需要保证资源释放:

    wx.closeBluetoothAdapter(Object object); //释放手机蓝牙端资源
    
  • 2、扫描周围设备接口

    扫描周围蓝牙设备等操作,需要使用到以下几种接口:

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

    当然这些资源使用后,也需要在最后阶段进行释放。

    wx.stopBluetoothDevicesDiscovery 停止搜寻附近的蓝牙外围设备
    
  • 3、连接操作接口
    当扫描到指定的设备后,需要执行连接操作,此时需要使用下列接口:

    wx.createBLEConnection 连接低功耗蓝牙设备
    wx.onBLEConnectionStateChange 监听低功耗蓝牙连接的状态事件
    

    当然连接操作也是一种占用资源的操作,需要在最后进行资源的释放

    wx.closeBLEConnection 断开与低功耗蓝牙设备的连接
    wx.offBLEConnectionStateChange(function callback) 关闭连接状态变化的监听事件
    
  • 4、获取服务和特征值对象接口
    连接成功后,在android系统中,可以直接进行数据的交互操作,但在ios系统中,直接进行数据交互时,会产生10004 报错。此时则需要保证使用下列代码。在之前博客中也做了详细说明介绍。

    wx.getConnectedBluetoothDevices 根据 uuid 获取处于已连接状态的设备
    wx.getBLEDeviceServices 获取蓝牙设备所有 service(服务)
    wx.getBLEDeviceCharacteristics  获取蓝牙设备所有 characteristic(特征值)
    
  • 5、通信接口
    之前博客中重大使用的读-写操作,本篇博客使用notify实现交互,需要使用到下列几种接口:

    wx.writeBLECharacteristicValue 向低功耗蓝牙设备特征值中写入二进制数据
    wx.notifyBLECharacteristicValueChange  启用低功耗蓝牙设备特征值变化时的 notify 功能
    wx.onBLECharacteristicValueChange 监听低功耗蓝牙设备的特征值变化
    

    当然,也需要对监听进行资源释放:

    wx.offBLECharacteristicValueChange(function callback) 关闭对特征值数据变化的监听
    

扩展

由于小程序需要兼容androidios,所以需要使用到以下几种自定义的api接口:

	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
    }
}

代码开发

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

    首先需要在app.json中对位置信息进行注册:

    "permission": {
        "scope.userLocation": {
          "desc": "您的位置信息将用于设备定位和蓝牙操作"
        }
      },
    

    其次在代码调用中也需要进行权限校验:

    	//判断系统版本、微信版本、定位服务等权限和信息
    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);
    }
    
  • 2、判断蓝牙状态,提示开启蓝牙等

function bleOperateFun(obj){
  //1、判断手机蓝牙是否开启
  wx.openBluetoothAdapter({
    success: function(res) {
      console.log("初始化蓝牙适配器成功");
 
      //1、开启蓝牙适配器的状态变化监听
      wx.onBluetoothAdapterStateChange(function(res){
        console.log("蓝牙适配器状态变化",res);
        // 如果对按钮dis有操作的,需要判断状态进行按钮状态的释放等。
      });
      //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));
      });
      
      //开启扫描周围设备模式
      startScanAroundDevice(obj);
    },
    fail:function(res){
      console.log("初始化蓝牙适配器失败")
      wx.showModal({
        title: '温馨提示',
        content: '请检查手机蓝牙是否打开',
        showCancel:false,
      });
      obj.setData({
        btnDisabled: false,
      });
       //执行quit机制
      quit(obj);
    }
  })
}
  • 3、扫描周围设备
function startScanAroundDevice(obj) {
  if (isScaning) {
    console.log("正在扫描设备。。。。");
    return;
  }
  wx.startBluetoothDevicesDiscovery({
    success: function(res) {
      console.log("成功开启扫描:" + res);
      isStartScan = true;
      isScaning = true;
    },
    fail: function(res) {
      console.log("扫描操作失败回调");
      obj.setData({
        btnDisabled: false,
      });
      //执行quit机制
      quit(obj);
    }
  });
  //同时进行定时操作  规定时间内未扫描到设备时,进行关闭扫描操作
  if (obj.data.btnText == "扫描") {
    //扫描操作  不会进行连接  所以时间可以短点
    setTimeout(function() {
      stopScanAroundDevice(obj);
    }, apps.bleProperties.scanToConnectTimes);
  } else {
    setTimeout(function() {
      stopScanAroundDevice(obj);
    }, apps.bleProperties.scanTimes);
  }

}

自定义函数中,扫描操作也添加了一个定时任务操作,就是关闭扫描,释放资源。

  • 4、关闭扫描函数
//关闭扫描周围设备操作
function stopScanAroundDevice(obj) {
  wx.stopBluetoothDevicesDiscovery({
    success(res) {
      console.log(new Date() + "定时关闭扫描返回:" + JSON.stringify(res));
      isScaning = false;
    },
    fail: function(res) {
      console.log("关闭扫描失败回调:" + JSON.stringify(res));
      obj.setData({
        btnDisabled: false,
      });
      //执行quit机制
      quit(obj);
    }
  })
}
  • 5、识别设备
    扫描操作执行后,当有设备在手机上识别到时,会触发wx.onBluetoothDeviceFound 监听

但是,wx.onBluetoothDeviceFound 接口中的 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('');
  },
wx.onBluetoothDeviceFound(function(res) {
	// 此时的res是一个集合信息,包含很多设备的广播数据,比如广播包等
	//{"devices":[{"deviceId":"E0:7D:EA:EB:89:A3","name":"ZN-GS04E","RSSI":-80,"advertisData":{},"advertisServiceUUIDs":["0000CCD0-0000-1000-8000-00805F9B34FB"],"localName":"ZN-GS04E","serviceData":{}}]}

	//采取如下方式进行遍历
	res.devices.forEach(device => {
		
	})
})

【注意:】Android系统 deviceId 就是指定设备的 mac 信息;ios系统则是一串随机uuid!

可以将设备mac保存至设备的蓝牙广播字段中。

  • 6、当识别到设备后,此时需要进行连接操作
    连接操作使用下列方式:
//执行连接操作   连接设备
function startConnectTo(obj) {
  
  wx.showLoading({
    title: '连接蓝牙设备中...',
  });

  wx.createBLEConnection({
    deviceId: obj.data.lockMac,
    success: function(res) {
      wx.hideLoading();
      wx.showToast({
        title: '连接成功',
        icon: 'success',
        duration: 1000
      })
      //更改全局连接状态
      isConnected = true;
      console.log("连接设备成功");
      console.log("连接success回调:" + JSON.stringify(res));
      //关闭扫描
      stopScanAroundDevice(obj);
      //开启通信服务
      startCommitServices(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);
    }
  })
}
  • 7、开启服务(兼容ios),开启notify和特征值数据变化监听
function startCommitServices(obj) {
  // sendOpenlockKeys(obj);
  // return;
  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("wx.getBLEDeviceCharacteristics  ---->\n" + JSON.stringify(res))
          
          // notify 功能,开启监听
          // 需要延迟开启
          setTimeout(() => {
            wx.notifyBLECharacteristicValueChange({
              characteristicId: apps.bleProperties.readCharacUUID,
              deviceId: obj.data.lockMac,
              serviceId: apps.bleProperties.bleServiceUUID,
              state: true,
              type:"notification", // 默认为 indication(有ack) ,notification无ack
              success:function(res){
                console.log("wx.notifyBLECharacteristicValueChange  success回执:\n"+JSON.stringify(res));
                
                // // 开启notify监听之后,需要开启特征值数据变更回执
                console.log("######################################  11111111");
                wx.onBLECharacteristicValueChange(function(res) {
                  console.log("######################################  22222222");
                  console.log("----->   wx.onBLECharacteristicValueChange 监听事件:\n" + JSON.stringify(res));
                  isOpenCharacValueChange = true;

                  
                // 写 1800ms
                setTimeout(() => {
                  sendOpenlockKeys(obj);
                }, apps.bleProperties.writeSuccessBegainReadTime);
              },
              fail:function(){
                wx.showModal({
                  title: '温馨提示',
                  content: '开启notify失败!',
                  showCancel: false
                });
                obj.setData({
                  btnDisabled: false,
                });
                quit(obj);
              }
            })
          }, apps.bleProperties.bleNotifyTimeout);

        },
        fail: function() {
          wx.showModal({
            title: '温馨提示',
            content: '获取特征对象失败!',
            showCancel: false
          });
          obj.setData({
            btnDisabled: false,
          });
          quit(obj);
        }
      })
    },
    fail: function() {
      wx.showModal({
        title: '温馨提示',
        content: '获取服务失败!',
        showCancel: false
      });
      obj.setData({
        btnDisabled: false,
      });
      quit(obj);
    }
  })
}

【注意:】开启通信服务后,不要立即去开启notify,不然几率报错!

  • 8、发送数据操作
    发送数据操作,使用如下函数:
function sendOpenlockKeys(obj) {
  console.log("sendOpenlockKeys");
 
  //console.log("加密后的长度--->" + buffer.length);
  //向设备发送开锁命令(传递的应该是数组数据)
  writeCharacVal(obj, buffer);
}

//发送开锁命令
function writeCharacVal(obj, sendValue) {
  console.log("准备向设备发送信息");
  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));
      isFirstWrite = true;
      
    },
    fail(res) {
      console.log("写数据失败回调" + res);
      console.log("发送命令次数:" + isFirstWriteNum);
      
      wx.showModal({
        title: '温馨提示',
        content: '发送开锁命令失败!',
        showCancel: false,
      })
      quit(obj);

    }
  })
}

注意发送的数据类型为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是使用AES加解密库中自带的方式。

将字符串类型的数据转换为十六进制的数组数据。

  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;
    }
  • 9、资源释放
//quit机制
function quit(obj) {
  // 如果开启了notify,则需要在quit中释放资源
  if(isOpenCharacValueChange){
    wx.offBLECharacteristicValueChange({
      success: function(res) {
        console.log("quit  wx.offBLECharacteristicValueChange 成功回调  清理手机蓝牙资源");
        console.log(JSON.stringify(res));
      },
      fail(res) {
        console.log("wx.offBLECharacteristicValueChange  资源失败回调  " + res);
      }
    })
  }
  //如果连接上了  需要断开连接操作
  if (isConnected) {
    wx.closeBLEConnection({
      deviceId: obj.data.lockMac,
      success(res) {
        console.log("quit()  断开连接操作成功回调");
        isConnected = false;
      },
      fail(res) {
        console.log("quit() 断开连接操作失败回调");
      }
    });
  }
  //如果扫描操作执行了的  需要释放手机蓝牙的资源
  if (isStartScan) {
    wx.closeBluetoothAdapter({
      success: function(res) {
        console.log("quit  wx.closeBluetoothAdapter 成功回调  清理手机蓝牙资源");
        console.log(JSON.stringify(res));
      },
      fail(res) {
        console.log("清理手机端  蓝牙  资源失败回调  " + res);
      }
    })
  }

  //不管操作进行到哪步   quit操作后  都需要释放按钮为 可点击状态
  obj.setData({
    btnDisabled: false,
  });
}

关于notify开启成功后,onBLECharacteristicValueChange无法监听的问题

在这里插入图片描述
【解决方式】

可以和设备开发工程师商讨兼容性解决方式。
比如:写一次其他信息,再写一次真实数据信息。

  移动开发 最新文章
Vue3装载axios和element-ui
android adb cmd
【xcode】Xcode常用快捷键与技巧
Android开发中的线程池使用
Java 和 Android 的 Base64
Android 测试文字编码格式
微信小程序支付
安卓权限记录
知乎之自动养号
【Android Jetpack】DataStore
上一篇文章      下一篇文章      查看所有文章
加:2021-07-29 11:46:00  更:2021-07-29 11:46:02 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/2 10:25:09-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码