前面记录了softAP的配网流程,现在把BLE蓝牙配网整理一下,其中最关键的配网步骤和控制。
BLE的配网原理
配网流程图:
BLE配网,即利用设备的蓝牙芯片,将设备进入到蓝牙模式,开启一个蓝牙广播,手机(或其它移动设备)通过搜索蓝牙设备连入设备获取广播的信息,向设备发送指令获取设备相关信息,让设备在无屏幕的情况下,通过蓝牙实现配网控制功能。 上面的流程图简单的画了一下,下面我大概讲一下具体的配网步骤\
配网步骤总共需要4步
- 智能家居设备需要长按特定的按键进入配网模式,这个时候设备会启动蓝牙模块并开始广播(不同的设备进入配网的方式不一样)
- 小程序进入配网扫描界面,小程序会调用蓝牙搜索连接的api来实现设备的配网
- 通过连接到的设备进行蓝牙的广播通信获取设备的uuid和关联的产品id
4.小程序通过调服务器接口查询智能设备是哪款产品并创建当前设备
BLE实现的代码
我在utils里面创建了bleDisNetwork.js来放通用的蓝牙连接步骤
const app = getApp();
app.globalData.bleDisNetwork = { bleDeviceList: [], MAC: "", UUID: "" }
import utils from "../utils/index";
class BleDisNetwork {
constructor(productId) {
// 当前设备的产品ID
this.productId = productId;
// 蓝牙设备id
this.deviceId = null;
// 蓝牙设备列表
this.bleDeviceList = [];
// 设备服务id
this.serviceId = null;
// 设备的某个特征值
this.characteristicId = null;
}
// 初始化蓝牙模块
openBluetooth() {
let that = this;
// 版本过低兼容
if (!wx.openBluetoothAdapter) {
wx.showModal({
title: '提示',
showCancel: false,
content: '当前微信版本过低,无法使用该功能,请升级到最新微信版本后重试。',
})
return;
}
return new Promise((reslove, reject) => {
wx.openBluetoothAdapter({
success: function (res) {
console.log("初始化蓝牙适配器成功" + JSON.stringify(res));
that.switchChange();
},
fail() {
// 初始化蓝牙适配器失败
reject(0)
wx.showToast({
title: "蓝牙适配失败,请检查手机蓝牙和定位功能是否打开",
icon: "none",
duration: 2000,
});
setTimeout(() => {
wx.navigateBack({
delta: 1,
});
}, 2000);
},
complete() {
// 初始化蓝牙适配器完成
reslove(1)
console.log("初始化蓝牙适配器完成");
},
});
})
}
// 开启蓝牙设备搜索
switchChange() {
let that = this;
console.log("打开蓝牙设备搜索");
// 获取本机蓝牙适配器状态
wx.getBluetoothAdapterState({
success(res) {
console.log("获取本机蓝牙适配器状态", res);
},
});
// 监听蓝牙适配器状态变化事件
wx.onBluetoothAdapterStateChange(function (res) {
console.log("搜索状态:", res.discovering);
console.log("蓝牙状态:", res.available);
});
// 开始搜寻附近的蓝牙外围设备
wx.startBluetoothDevicesDiscovery({
allowDuplicatesKey: false, //是否允许重复上报同一设备
success(res) {
console.log("开始搜寻附近的蓝牙外围设备", res);
// 20s后停止搜索
setTimeout(() => that.stopSearchBluetooth(), 20000);
},
});
// 监听搜索到新设备的事件
wx.onBluetoothDeviceFound(function (devices) {
let device = devices.devices[0];
if (!device) return;
// console.info('shebei', device, device.localName);
if (device.localName && device.localName.indexOf(`XXXXX-${that.productId}`) > -1) {
let result = that.bleDeviceList.some(item => item.deviceId === device.deviceId);
if (!result) {
console.log(`查找到 XXXXX-${that.productId}`, '设备id', device.deviceId);
that.deviceId = device.deviceId;
that.bleDeviceList = [...that.bleDeviceList, device];
}
console.info('连接成功---', that.deviceId)
that.connectBluetooth()
// 连接成功 需要断开蓝牙搜索
that.stopSearchBluetooth()
}
});
// 获取在蓝牙模块生效期间所有搜索到的蓝牙设备。包括已经和本机处于连接状态的设备
wx.getBluetoothDevices({
success(res) {
console.log(
"获取在蓝牙模块生效期间所有搜索到的蓝牙设备。包括已经和本机处于连接状态的设备",
res
);
},
});
}
// 关闭蓝牙设备搜索
stopSearchBluetooth() {
let that = this;
wx.stopBluetoothDevicesDiscovery({
success(res) {
console.log("关闭蓝牙设备搜索", res);
console.log(that.bleDeviceList, "bleDeviceList");
},
});
}
// 设备连接
connectBluetooth() {
let that = this;
wx.createBLEConnection({
deviceId: that.deviceId,
success(res) {
console.log("连接成功设备---" + JSON.stringify(res));
that.getBluetoothServers();
},
fail(err) {
that.closeConnectBluetooth();
console.log("连接失败,结束---" + JSON.stringify(err));
},
});
}
// 断开连接
closeConnectBluetooth() {
let that = this;
// 断开BLE的连接
wx.closeBLEConnection({
deviceId: that.deviceId,
success(res) {
console.log("手动断开连接", res);
},
});
// 断开蓝牙的连接 (初始化所有的状态)
wx.closeBluetoothAdapter({
success(res) {
app.globalData.bleDisNetwork = {};
console.log("断开蓝牙", res);
},
});
}
// 获取设备的服务
getBluetoothServers() {
let that = this;
wx.getBLEDeviceServices({
deviceId: that.deviceId,
success(res) {
console.log("获取设备服务", res.services);
that.serviceId = res.services[4].uuid;
that.getBluetoothCharacteristics();
},
});
}
// 获取设备某个服务特征值列表
getBluetoothCharacteristics() {
let that = this;
wx.getBLEDeviceCharacteristics({
deviceId: that.deviceId,
serviceId: that.serviceId,
success(res) {
console.info("获取设备某个服务特征值列表", res);
that.characteristicId = res.characteristics[0].uuid;
console.log(that, 'this');
setTimeout(() => {
that.getDistributionNetworkParameters()
}, 200)
},
});
}
// 向设备特征值中写入二进制数据
writeBluetoothCharacteristicValue(arr) {
let that = this;
// 下发指令
console.info('获取信息 转化 Buffer', utils.strToBuf(arr))
return new Promise((reslove) => {
wx.writeBLECharacteristicValue({
deviceId: that.deviceId,
serviceId: that.serviceId,
characteristicId: that.characteristicId,
value: utils.strToBuf(arr),
success(res) {
reslove(res);
console.log(res, "写入成功");
that.readBinaryData();
},
fail(res) {
console.log(res, "写入失败 结束");
}
})
})
}
// 读取二进制数据
readBinaryData() {
let that = this;
wx.readBLECharacteristicValue({
deviceId: that.deviceId,
serviceId: that.serviceId,
characteristicId: that.characteristicId,
success(res) {
console.log(res, "读取成功");
},
fail(res) {
console.log(res, "读取失败 结束")
}
})
}
// 监听设备的特征值变化
watchBluetoothCharacteristicValueChange() {
let that = this;
console.log("监听设备的特征值变化")
return new Promise((reslove) => {
wx.onBLECharacteristicValueChange(function (res) {
console.log('返回数据', utils.ab2hex(res.value));
reslove(utils.ab2hex(res.value));
})
})
}
// 获取配网参数
async getDistributionNetworkParameters() {
let that = this;
// arr1是接入蓝牙的指令
let arr1 = [];
await that.writeBluetoothCharacteristicValue(arr1);
let res1 = await that.watchBluetoothCharacteristicValueChange();
console.log(res1, '接入指令');
// arr2是获取MAC地址的指令
let arr2 = [];
await that.writeBluetoothCharacteristicValue(arr2);
let res2 = await that.watchBluetoothCharacteristicValueChange();
let MAC = res2.substring(6, 18);
app.globalData.bleDisNetwork.MAC = MAC;
console.log(MAC, 'MAC');
// arr3是获取UUID地址的指令
let arr3 = [];
await that.writeBluetoothCharacteristicValue(arr3);
let res4 = await that.watchBluetoothCharacteristicValueChange();
let UUID = res4.substring(6, res4.length);
app.globalData.bleDisNetwork.UUID = UUID;
console.log(UUID, 'UUID');
}
}
export default new BleDisNetwork();
hex转ArrayBuffer和ArrayBuffer转16进度字符串示例这两个方法放在了utils/index.js里面
// hex转ArrayBuffer
strToBuf(arr) {
let length = arr.length
let buffer = new ArrayBuffer(length + 2)
let dataview = new DataView(buffer)
for (let i = 0; i < length; i++) {
dataview.setUint8(i, '0x' + arr[i])
}
return buffer
},
// ArrayBuffer转16进度字符串示例
ab2hex(buffer) {
let hexArr = Array.prototype.map.call(
new Uint8Array(buffer),
function (bit) {
return ("00" + bit.toString(16)).slice(-2);
}
);
return hexArr.join("");
},
页面使用
/**
* 生命周期函数--监听页面显示
*/
onShow: function () {
// 初始化蓝牙模块
let productId = this.data.productId;
BleDisNetwork.productId = productId;
BleDisNetwork.openBluetooth();
this.getProgress(1);
},
进度条
// 进度条
async getProgress(i) {
i++;
this.setData({ progressNum: this.data.progressNum + 3 });
let mac = app.globalData.bleDisNetwork.MAC;
let devId = app.globalData.bleDisNetwork.UUID;
let unionId = wx.getStorageSync("unionId");
if (i > 30 && !mac && !devId) {
clearTimeout(this.data.timer);
setTimeout(() => {
wx.navigateBack({
delta: 1,
});
}, 3000);
return wx.showToast({
title: "未扫描到这台设备",
icon: "none",
duration: 2000,
});
}
console.log(i);
let timer = setTimeout(() => {
this.getProgress(i);
}, 2000);
this.setData({ timer });
if (mac.length === 12 && devId.length === 32) {
clearTimeout(this.data.timer);
console.log(mac, devId, unionId, 'kkk');
try {
let params = { mac, devId, unionId }
let res = await global.api.blueDevBindUser(params);
const { code, info, rows } = res;
if (code !== 200) {
setTimeout(() => {
wx.navigateBack({
delta: 1,
});
}, 3000);
return wx.showToast({
title: info,
icon: "none",
duration: 2000,
});
}
this.setData({ progressNum: 100 })
setTimeout(() => {
wx.navigateTo({
url: `/pages/home/equipment/component/chooseRoom/index`,
success: function (res) {
// 通过eventChannel向被打开页面传送数据
res.eventChannel.emit("acceptDataFromOpenerPage", { connectionData: rows });
},
});
}, 1000);
console.log(res, 'res');
}
catch (err) {
console.log(err);
}
}
},
如果需要控制设备,就直接调用BleDisNetwork直接下发对应的控制指令
let arr = [];
await BleDisNetwork.writeBluetoothCharacteristicValue(arr);
let res = await BleDisNetwork.watchBluetoothCharacteristicValueChange();
console.log(res, '下发指令');