微信小程序蓝牙BLE开发实战(三)
对于我这种小白,遇到问题是常见的哈。这里记录下,避免日后再踩坑
1. iPhone6及6plus无法搜索到设备?
特别注意: wx.onBluetoothDeviceFoundAPI返回的数据结构, 其中advertisData
数据段。来看下res.devices 的结构
。 有些供应商设备是没有返回的。
返回的数据结构图:
下图是不同设备上返回的数据。明显看到左图
有返回advertisData
数据段, 右图
则没有
说明: advertisData
在没有数据返回情况下,Android
设备会返回{}
, IOS
设备直接不返回这个字段。
复现:
当广播数据中没有advertisData
数据段情况下, 代码中获取时,除了真机调试
下正常运行, 其他的预览模式、体验版及线上运行均有问题
。
// 转换后,根据协议文档取需要数据(advertisData不一定有数据)
var hexStr = ab2hex(device.advertisData);
console.log("广播数据中转换后:advertisData---->" + hexStr);
解决方案:
注意:advertisData
数据段,返回是ArrayBuffer
类型。 使用时需转换
if (device.advertisData != null) { //判断对象是否为null,advertisData不一定有
var hexStr = ab2hex(device.advertisData);
console.log("广播数据中转换后:advertisData---->" + hexStr);
}
// ArrayBuffer转16进度字符串示例
function ab2hex(buffer) {
var hexArr = Array.prototype.map.call(
new Uint8Array(buffer),
function(bit) {
return ('00' + bit.toString(16)).slice(-2)
}
)
return hexArr.join('');
}
ps: 当时第一次接触微信小程序BLE, 项目上线
发现此问题。那时项目中对接多个供应商有些没有返回advertisData
数据段, 忘加判断语句。【细节很重要】
记得那个周末休息慢慢排查解决的。
2. IOS无法获取mac
地址,如何连接设备呢?
再一次的了解 wx.onBluetoothDeviceFoundAPI返回的数据结构。主要关注deviceId
数据
注意: deviceId
-
Android
设备中的deviceId
得到是mac
地址
-
IOS
设备中的deviceId
得到是UUID
, 而且是动态
的。
返回数据效果图:
解决方案:
-
通过广播数据段advertisData
中获取需要的mac
地址。【注意
有些供应商设备是没有返回的】
-
通过广播数据段name
、localName
中通过名称匹配设备。【有些供应商直接把mac
地址拼接到name
中】
- 如上图: 关注下
name
返回的格式, 通常供应商以: 前缀+mac
(供应商缩写+设备mac
)
- 格式都可以更改的, 根据需求和硬件硬件部商量
// 例如:advertisData字段返回数据是0000365544332211a7d4...,可根据文档说明取需要数据
let data = '0000365544332211a7d4';
let subMac = data.subString(4, 16); //例取365544332211为mac
3. Android二次连接搜索不到设备?
注意: 操作完成后一定要断开设备连接
及释放蓝牙模块
。否则再次连接会出现搜索不到设备,需关闭小程序再次搜索才可以,IOS不受影响
。
wx.closeBLEConnection(Object object)
wx.closeBluetoothAdapter(Object object)
原因:
wx.onBluetoothDeviceFound
接口返回的是新的蓝牙设备
,之前连接过的在部分安卓机型上,不算是新的
蓝牙设备,所以会出现二次
连接搜索不到设备。
解决方案:
function closeBLEConnection() {
console.log("断开与低功耗蓝牙设备的连接。", deviceId);
if (deviceId) {
wx.closeBLEConnection({
deviceId: deviceId,
success: function(res) {
console.log("closeBLEConnection。success", res);
},
fail: function(res) {
console.log("closeBLEConnection。fail", res);
},
complete: function() {
status = false;
}
})
wx.closeBluetoothAdapter({
success: function(res) {
console.log("closeBluetoothAdapter ==>res:", res);
},
fail: function(error) {
console.log("closeBluetoothAdapter ==>error:", error);
}
})
}
_discoveryStarted = false;
isnotExist = true;
_deviceId = '';
deviceId = '';
}
4. 发送数据过程中易出现写入失败
?
注意: 开启notify
后建议不要
马上发送指令,否则易出现写入失败
。
解决方案:
- 在
wx.writeBLECharacteristicValue
向设备写入数据时,添加setTimeout()
延迟1秒
左右的动作。
function writeData(hex) {
setTimeout(() => {
//类型转换
var enDataBuf = new Uint8Array(hex);
var buffer1 = enDataBuf.buffer
wx.writeBLECharacteristicValue({
deviceId: _deviceId,
serviceId: _serviceId,
characteristicId: _characteristicId,
value: buffer1,
success: (res) => {
wx.hideLoading();
console.log("写数据返回结果", res.errMsg);
},
fail(res) {
console.log("写数据失败..", res);
asddErrorCallback(res.errCode, "");
}
})
}, 1000)
}
5. 什么版本能支持蓝牙
功能?
iOS
微信客户端 6.5.6
版本开始支持
,Android 6.5.7
版本开始支持
,因此项目中需要做好版本检测
蓝牙说明
解决方案:
-
第一步: 在
app.js
的onLaunch()
方法中,获取系统信息接口
//获取系统信息
this.globalData.sysinfo = wx.getSystemInfoSync();
-
第二步: 得到
version
、system
、platform
等需要的相关信息。
getVersion: function() { //获取微信版本号
return this.globalData.sysinfo["version"]
},
getSystem: function() { //获取操作系统版本
return this.globalData.sysinfo["system"]
},
getPlatform: function() { //获取客户端平台
return this.globalData.sysinfo["platform"]
},
-
第三步: 在需要检测版本
页面,通常在首页
对应的.js
中,判断版本是否
支持蓝牙API。
这里放在单独.js
文件中, 在index.js
中引入并使用。
A: 新建checkVersion.js
const app = getApp()
/**
* 版本比较
*/
function versionCompare (ver1, ver2) { //版本比较
// console.log("ver1" + ver1 + 'ver2' + 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
}
}
/**
* 微信版本检测
* Android从微信 6.5.7 开始支持,iOS从微信 6.5.6 开始支持
*/
function checkWechatVersion() {
if (app.getPlatform() == 'android' && versionCompare('6.5.7', app.getVersion())) {
wx.showModal({
title: '提示',
content: '当前微信版本过低,请更新至最新版本体验',
showCancel: false
})
}else if (app.getPlatform() == 'ios' && versionCompare('6.5.6', app.getVersion())) {
wx.showModal({
title: '提示',
content: '当前微信版本过低,请更新至最新版本体验',
showCancel: false
})
}
}
//导出
module.exports = {
checkWechatVersion: checkWechatVersion
}
B: 在index.js
的onLoad()
方法中使用
var checkVersion = require('../../../utils/checkVersion.js');
onLoad: function(options) {
// 检测版本
checkVersion.checkWechatVersion();
}
wx.getSystemInfoSync()
返回的数据效果图
版本比较参考大神们
6. 安卓 6.0 及以上设备需打开定位服务?
解决方案:
这里写简单提示,实际上还可以引导用户打开授权位置,有空再更新下
isOpenLocation: function() {
let that = this;
//判断提示安卓系统6.0及以上需打开定位服务
if (app.getPlatform() == 'android' && app.getSystem() >= 'Android 6.0') {
wx.getSystemInfo({
success(res) {
// console.log("获取系统信息", res);
if (!res.locationAuthorized && res.locationAuthorized != undefined) {
wx.showModal({
title: '提示',
content: '为了更好体验测量,\r\n请您打开微信允许授权定位',
showCancel: false
})
} else {
that.scanCode();
}
}
})
} else {
that.scanCode();
}
},
7. 写入数据大于20字节
,如何发送数据?
注意:
-
并行
调用多次
会存在写失败的可能性。
-
小程序
不会
对写入数据包大小
做限制
,但系统与蓝牙
设备会
限制蓝牙4.0单次
传输的数据大小
,超过最大字节
数后会发生写入错误
,建议每次写入不超过20字节
。
- 若单次写入数据过长,
iOS
上存在系统不会
有任何回调的情况(包括错误回调)
解决方案:
分包发送:
因系统与蓝牙
单次传输只能发送20个字节
, 因此大于20个字节
时需要手动分包发送
。
说明: 在写入数据
时,需要延迟,否则易出现写入失败
【可查看:第4个
案例】
var hex = [0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x50, 0x51, 0x52]; //22个字节
if (hex.length > 20) {
var result = this.split_array(hex, 20);
console.log('分包后的数组', result);
writeData(result[0]); //写入第一条数据
setTimeout(() => {
writeData(result[1]); //写入第二条数据
}, 5)
} else {
console.log('小于20字节写入数据');
writeData(hex);
}
/**
* 将一个数组分割两个
*/
split_array: function(arr, len) {
var a_len = arr.length;
var result = [];
for (var i = 0; i < a_len; i += len) {
result.push(arr.slice(i, i + len));
}
return result;
},
8. 大于255
字节如何处理?
注意:一个
字节:最大整数是255
。
例: 控制时间
指令为: [0xa1, 0x30, 0x00, 自定义时间n]
1. 发送时间30
秒,即[0x0a, 0x30, 0x00, 0x1e]
2. ? 假设: 900
秒,如何发送指令呢?
【难道是这样发送数据:[0x0a, 0x30, 0x00, 0x384]
正常是不可以的。】
解决方案:
拆分为两个
字节,当不足位数前面补0
。关于进制转换请查看
- 假设:
900
转16进制后为 384
, 那么我们把他拆分为两个
字节发送就可以了。 即 0x03
, 0x84
let n = 900;
n = n.toString(16); // 将900转化为16进制数(以字符串显示)
var s = "00000000" + n;
s = s.substr(n.length, s.length); // 截取最后8位字符
let str1 = s.substring(4, 6);
str1 = '0x' + str1; //同等: str1 = str1.toString(16); 无需拼0x
let str2 = s.substring(6, 8);
str2 = str2.toString(16);;
var nS = parseInt(str1, 16) //字符串转16进制
var nE = parseInt(str2, 16)
var hex = [0xa1, 0x30, nS, nE];
writeData(hex); //写入数据
9. 如何把设备返回的随机码
作为加密的key
?
说明:
解决方案:
//Eg: KEY8as6 (其中8as6为随机码)
//第一步:把设备返回的数据进行转换
var resValue = new Uint8Array(res.value, 0);
//第二步: 把Uint8Array转换为字符
var tmp = String.fromCharCode.apply(String, resValue);
//判断是否包含KEY
if(tmp.indexOf('KEY') != -1) {
//获取加密key
getEnKey(resValue);
}
/**
* 获取加密key
*/
function getEnKey(resValue) {
var sliceStr = resValue.subarray(3);
console.log("截取key后的值是:", sliceStr); //得到随机码 8as6
//假设加密的key为:0123456789ABCDEF
var key = [0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46];
var cryptoKey = new Uint8Array(key);
for (var i = 0; i < sliceStr.length; i++) {
cryptoKey[i] = sliceStr[i];
}
enKEY = cryptoKey;
}
//最后加密的key为: 8as6456789ABCDEF
BLE项目实战下载
查看实战案例
或
蓝牙BLE项目实战
关于下篇内容