导语 蓝牙在日常生活中广泛使用的一项技术,小程序给了我们前端工程师一个控制蓝牙的方法,带上你的设备,来看看怎么控制你的蓝牙设备吧。 1. 背景介绍 蓝牙是爱立信公司创立的
导语蓝牙在日常生活中广泛使用的一项技术,小程序给了我们前端工程师一个控制蓝牙的方法,带上你的设备,来看看怎么控制你的蓝牙设备吧。 1. 背景介绍蓝牙是爱立信公司创立的一种无线技术标准,为短距离的硬件设备提供低成本的通信规范。蓝牙规范由蓝牙技术联盟(Bluetooth Special Interest Group,简称SIG)管理,在计算机,手机,传真机,耳机,汽车,家用电器等等很多场景广泛使用。蓝牙具有以下一些特点: (1) 免费使用:使用的工作频段在2.4GHz的工科医(ISM)频段,无需申请许可证。 (2) 功耗低:BLE4.0包含了一个低功耗标准(Bluetooth Low Energy),可以让蓝牙的功耗显著降低 (3) 安全性高:蓝牙规范提供了一套安全加密机制和授权机制,可以有效防范数据被窃取 (4) 传输率高:目前最新BLE4.0版本,理论传输速率可达3Mbit/s(实际肯定达不到),理论覆盖范围可达100米。
2.小程序蓝牙介绍小程序API提供了一套蓝牙操作接口,所以作为我们前端开发人员可以更加方便的进行蓝牙设备开发,而无需了解安卓和IOS的各种蓝牙底层概念。小程序的蓝牙操作大多都是通过异步调用来处理的,这里面就存在着一些坑,后面会详细介绍。在使用小程序蓝牙API之前有几个概念或者说术语需要预先了解: (1) 蓝牙终端:我们常说的硬件设备,包括手机,电脑等等。 (2) UUID:是由子母和数字组成的40个字符串的序号,根据硬件设备有关联的唯一ID。 (3) 设备地址:每个蓝牙设备都有一个设备地址deviceId,但是安卓和IOS差别很大,安卓下设备地址就是mac地址,但是IOS无法获取mac地址,所以设备地址是针对本机范围有效的UUID,所以这里需要注意,后面会介绍。 (4) 设备服务列表:每个设备都存在一些服务列表,可以跟不同的设备进行通信,服务有一个serviceId来维护,每个服务包含了一组特征值。 (5) 服务特征值:包含一个单独的value值和0 –n个用来描述characteristic 值(value)的descriptors。一个characteristics可以被认为是一种类型的,类似于一个类。 (6) ArrayBuffer:小程序中对蓝牙数据的传递是使用ArrayBuffer的二进制类型来的,所以在我们的使用过程中需要进行转码。
3. API总览小程序对蓝牙设备的操作有18个API
4. 主要流程蓝牙通信的一个正常流程是下面的图示
(1) 开启蓝牙:调用openBluetoothAdapter来开启和初始化蓝牙,这个时候可以根据状态判断用户设备是否支持蓝牙 (2) 检查蓝牙状态:调用getBluetoothAdapterState来检查蓝牙是否开启,如果没有开启可以在这里提醒用户开启蓝牙,并且能在开启后自动启动下面的步骤 这里有一个坑:IOS里面蓝牙状态变化以后不能马上开始搜索,否则会搜索不到设备,必须要等待2秒以上。 ![]() function connect(){ wx.openBluetoothAdapter({ success: function (res) { }, fail(res){ }, complete(res){ wx.onBluetoothAdapterStateChange(function(res) { if(res.available){ setTimeout(function(){ connect(); },2000); } }) //开始搜索 } }) } ![]()
(3) 搜索设备:startBluetoothDevicesDiscovery开始搜索设备,当发现一个设备会触发onBluetoothDeviceFound事件,首先看下标准API
由于IOS无法获取Mac地址所以这里需要区分两个场景 a) 安卓:安卓下可以根据Mac地址来搜索设备,或者跳过此步直接连接到设备。当搜索到一个设备以后,可以在onBluetoothDeviceFound事件回调中判断当前设备的deviceID是否为指定的Mac地址 ![]() let mac = "XXXXXXXXXXXXXXX"; wx.startBluetoothDevicesDiscovery({ services:[], success(res) { wx.onBluetoothDeviceFound(res=>{ let devices = res.devices; for(let i = 0;i<devices.length;i++){ if(devices[i].deviceId = mac){ console.log("find"); wx.stopBluetoothDevicesDiscovery({ success:res=>console.log(res), fail:res=>console.log(res), }) } } }); }, fail(res){ console.log(res); } }) ![]()
b) IOS:IOS下获取设备Mac地址的方法已经被屏蔽,所以不存在mac地址,此时只能通过其他方式来判断,比如在蓝牙设备advertisData字段添加一些特别的信息来判断等等,可以转字符串来判断,也可以直接用二进制来判断。 ![]() let id = "XXXXXXXXXXXXXXX",//设备标识符 deviceId = ""; wx.startBluetoothDevicesDiscovery({ services:[], success(res) { wx.onBluetoothDeviceFound(res=>{ var devices = res.devices; for(let i = 0;i<devices.length;i++){ let advertisData = devices[i].advertisData; var data = arrayBufferToHexString(advertisData);//二进制转字符串 if (!!data && data.indexOf(id) > -1) { console.log("find"); deviceId = devices[i].deviceId; } } }); }, fail(res){ console.log(res); } }); function arrayBufferToHexString(buffer) { let bufferType = Object.prototype.toString.call(buffer) if (buffer != '[object ArrayBuffer]') { return } let dataView = new DataView(buffer) var hexStr = ''; for (var i = 0; i < dataView.byteLength; i++) { var str = dataView.getUint8(i); var hex = (str & 0xff).toString(16); hex = (hex.length === 1) ? '0' + hex : hex; hexStr += hex; } **** return hexStr.toUpperCase(); } ![]()
这里需要注意的是:如果知道mac地址在安卓下可以直接略过搜索过程直接连接,如果不知道mac地址或者是IOS场景下需要开启搜索,由于搜索是比较消耗资源的动作,所以发现目标设备以后一定要及时关闭搜索,以节省系统消耗。 (4) 搜索到设备以后,就是连接设备createBLEConnection: (5) 连接成功以后就开始查询设备的服务列表:getBLEDeviceServices,然后根据目标服务ID或者标识符来找到指定的服务ID ![]() let deviceId = "XXXX"; wx.getBLEDeviceServices({ deviceId: device_id, success: function (res) { let service_id = ""; for(let i = 0;i<res.services.length;i++){ if(services[i].uuid.toUpperCase().indexOf("TEST") != -1){ service_id = services[i].uuid; break; } } return service_id; }, fail(res){ console.log(res); } }) ![]()
这里有个坑的地方:如果是安卓下如果你知道设备的服务ID,你可以省去getBLEDeviceServices的过程,但是IOS下即使你知道了服务ID,也不能省去getBLEDeviceServices的过程,这是小程序里面需要注意的一点。 (6) 获取服务特征值:每个服务都包含了一组特征值用来描述服务的一些属性,比如是否可读,是否可写,是否可以开启notify通知等等,当你跟蓝牙通信时需要这些特征值ID来传递数据。 getBLEDeviceCharacteristics方法返回了res参数包含了以下属性:
characteristics包含了一组特征值列表
通过遍历特征值对象来获取想要的特征值ID ![]() wx.getBLEDeviceCharacteristics({ deviceId: device_id, serviceId: service_id, success: function (res) { let notify_id,write_id,read_id; for (let i = 0; i < res.characteristics.length; i++) { let charc = res.characteristics[i]; if (charc.properties.notify) { notify_id = charc.uuid; } if(charc.properties.write){ write_id = charc.uuid; } if(charc.properties.write){ read_id = charc.uuid; } } }, fail(res){ console.log(res); } }) ![]()
这个例子就通过搜索特征值取到了 notify特征值ID,写ID和读取ID (7) 获取特征值ID以后就可以开启notify通知模式,同时开启监听特征值变化消息
![]() wx.notifyBLECharacteristicValueChange({ state: true, deviceId: device_id, serviceId: service_id, characteristicId:notify_id, complete(res) { wx.onBLECharacteristicValueChange(function (res) { console.log(arrayBufferToHexString(res.value)); }) }, fail(res){ console.log(res); } }) ![]()
(8) 一切都准备好以后,就可以开始给蓝牙发送消息,一旦蓝牙有响应,就可以在onBLECharacteristicValueChange事件中得到消息并打印出来。 这里面有个坑:开启notify以后并不能马上发送消息,蓝牙设备有个准备的过程,需要在setTimeout中延迟1秒以上才能发送,否则会发送失败 ![]() let buf = hexStringToArrayBuffer("test"); wx.writeBLECharacteristicValue({ deviceId: device_id, serviceId: service_id, characteristicId:write_id, value: buf, success: function (res) { console.log(buf); }, fail(res){ console.log(res); } }) function hexStringToArrayBuffer(str) { if (!str) { return new ArrayBuffer(0); } var buffer = new ArrayBuffer(str.length); let dataView = new DataView(buffer) let ind = 0; for (var i = 0, len = str.length; i < len; i += 2) { let code = parseInt(str.substr(i, 2), 16) dataView.setUint8(ind, code) ind++ } return buffer; } ![]()
(9) 所有都通信完毕后可以断开连接: ![]()
wx.closeBLEConnection({
deviceId: device_id,
success(res) {
console.log(res)
},
fail(res) {
console.log(res)
}
})
wx.closeBluetoothAdapter({
success: function (res) {
console.log(res)
}
})
![]()
5. 完整例子这里为了简洁,把fail等异常处理已经省去,主要流程就是设置设备ID和服务ID的过滤值,在开启notify之后写入测试消息,然后监听蓝牙发送过来的消息,整个过程采用简化处理,没有使用事件通信来驱动,仅做参考。 ![]() let blueApi = { cfg:{ device_info:"AAA", server_info:"BBB", onOpenNotify:null }, blue_data:{ device_id:"", service_id:"", write_id:"" }, setCfg(obj){ this.cfg = Object.assign({},this.cfg,obj); }, connect(){ if(!wx.openBluetoothAdapter){ this.showError("当前微信版本过低,无法使用该功能,请升级到最新微信版本后重试。"); return; } var _this = this; wx.openBluetoothAdapter({ success: function (res) { }, complete(res){ wx.onBluetoothAdapterStateChange(function(res) { if(res.available){ setTimeout(function(){ _this.connect(); },2000); } }) _this.getBlueState(); } }) }, //发送消息 sendMsg(msg,toArrayBuf = true) { let _this = this; let buf = toArrayBuf ? this.hexStringToArrayBuffer(msg) : msg; wx.writeBLECharacteristicValue({ deviceId: _this.blue_data.device_id, serviceId: _this.blue_data.service_id, characteristicId:_this.blue_data.write_id, value: buf, success: function (res) { console.log(res); } }) }, //监听消息 onNotifyChange(callback){ var _this = this; wx.onBLECharacteristicValueChange(function (res) { let msg = _this.arrayBufferToHexString(res.value); callback && callback(msg); console.log(msg); }) }, disconnect(){ var _this = this; wx.closeBLEConnection({ deviceId: _this.blue_data.device_id, success(res) { } }) }, /*事件通信模块*/ /*连接设备模块*/ getBlueState() { var _this = this; if(_this.blue_data.device_id != ""){ _this.connectDevice(); return; } wx.getBluetoothAdapterState({ success: function (res) { if (!!res && res.available) {//蓝牙可用 _this.startSearch(); } } }) }, startSearch(){ var _this = this; wx.startBluetoothDevicesDiscovery({ services:[], success(res) { wx.onBluetoothDeviceFound(function(res){ var device = _this.filterDevice(res.devices); if(device){ _this.blue_data.device_id = device.deviceId; _this.stopSearch(); _this.connectDevice(); } }); } }) }, //连接到设备 connectDevice(){ var _this = this; wx.createBLEConnection({ deviceId: _this.blue_data.device_id, success(res) { _this.getDeviceService(); } }) }, //搜索设备服务 getDeviceService(){ var _this = this; wx.getBLEDeviceServices({ deviceId: _this.blue_data.device_id, success: function (res) { var service_id = _this.filterService(res.services); if(service_id != ""){ _this.blue_data.service_id = service_id; _this.getDeviceCharacter(); } } }) }, //获取连接设备的所有特征值 getDeviceCharacter() { let _this = this; wx.getBLEDeviceCharacteristics({ deviceId: _this.blue_data.device_id, serviceId: _this.blue_data.service_id, success: function (res) { let notify_id,write_id,read_id; for (let i = 0; i < res.characteristics.length; i++) { let charc = res.characteristics[i]; if (charc.properties.notify) { notify_id = charc.uuid; } if(charc.properties.write){ write_id = charc.uuid; } if(charc.properties.write){ read_id = charc.uuid; } } if(notify_id != null && write_id != null){ _this.blue_data.notify_id = notify_id; _this.blue_data.write_id = write_id; _this.blue_data.read_id = read_id; _this.openNotify(); } } }) }, openNotify(){ var _this = this; wx.notifyBLECharacteristicValueChange({ state: true, deviceId: _this.blue_data.device_id, serviceId: _this.blue_data.service_id, characteristicId: _this.blue_data.notify_id, complete(res) { setTimeout(function(){ _this.onOpenNotify && _this.onOpenNotify(); },1000); _this.onNotifyChange();//接受消息 } }) }, 邀请
原作者: 模板之家
来自: 网络收集
全部回复(0) 微信:xu08290201 QQ:75283535
返回顶部 |