智能家居小程序BLE配网功能实现

1,565 阅读5分钟

前面记录了softAP的配网流程,现在把BLE蓝牙配网整理一下,其中最关键的配网步骤和控制。

BLE的配网原理

配网流程图:

BLE配网原理.png

BLE配网,即利用设备的蓝牙芯片,将设备进入到蓝牙模式,开启一个蓝牙广播,手机(或其它移动设备)通过搜索蓝牙设备连入设备获取广播的信息,向设备发送指令获取设备相关信息,让设备在无屏幕的情况下,通过蓝牙实现配网控制功能。 上面的流程图简单的画了一下,下面我大概讲一下具体的配网步骤\

配网步骤总共需要4步

  1. 智能家居设备需要长按特定的按键进入配网模式,这个时候设备会启动蓝牙模块并开始广播(不同的设备进入配网的方式不一样) WechatIMG887.jpeg
  2. 小程序进入配网扫描界面,小程序会调用蓝牙搜索连接的api来实现设备的配网 WechatIMG906.jpeg
  3. 通过连接到的设备进行蓝牙的广播通信获取设备的uuid和关联的产品id WechatIMG908.jpeg 4.小程序通过调服务器接口查询智能设备是哪款产品并创建当前设备 WechatIMG909.jpeg

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, '下发指令');