微信小程序实现蓝牙连接通讯

由于最近的项目在做小程序蓝牙通讯这块的内容,所以将实现的过程在这简单的记录下。

1、首先要初始化蓝牙-检查蓝牙适配器是否打开

TypeScript 复制代码
 async initBluetooth(): Promise<void> {
        return new Promise((resolve: any, reject: any) => {
            const _this = this
            if (BluetoothController.adapterOpend) {
                console.log("蓝牙适配器已打开")
                resolve(true)
                return
            }
            wx.openBluetoothAdapter({
                success(res) {
                    BluetoothController.adapterOpend = true
                    console.log("=====*****蓝牙适配器已打开")
                    resolve(true)
                },
                fail(error) { //用来判断蓝牙是否已打开
                    reject(error)
                    BluetoothController.adapterOpend = false
                    console.log("=====*****蓝牙适初始化失败", error)
                }
            })
        })
    }

//使用案例
initBluetooth().then()

2、开始搜寻附近的蓝牙外围设备

TypeScript 复制代码
 /**
     * @param options 
     * options.keywords  蓝牙名称筛选关键字
     * options.deviceid  可选参数,蓝牙设备id,连接用
     */
    async searchAroundBLE(options: any): Promise<void> {
        return new Promise((resolve: any, reject: any) => {
            const _this = this
            if (_this.startDiscovery) {
                console.log("已开启蓝牙扫描,勿重复开启")
                resolve(true)
                return
            }
            _this.startDiscovery = true
            wx.startBluetoothDevicesDiscovery({
                allowDuplicatesKey: true,
                services: options.services,
                success(res) {
                    console.log('搜索成功', res);
                    resolve(true)

                },
                fail(error) {
                    reject(error)
                    _this.startDiscovery = false
                },
            })
        })

    }


//使用案例
 searchAroundBLE({ 'keywords': [''], services: [] }).then()

3、监听搜索到的设备

TypeScript 复制代码
 /**
     * @param options 
     * options.keywords  蓝牙名称筛选关键字
     * options.deviceid  可选参数,蓝牙设备id,连接用
     */
    async onBluetoothDeviceFound(options: any): Promise<void> {
        return new Promise((resolve: any, reject: any) => {
            let _this = this
            let { keywords } = options
            // 超时自动结束
            _this.findTimer = setTimeout(() => {
                clearTimeout(_this.findTimer)
                if (!_this.connectStatus) {
                   reject({ 
                        success: false
                    })
                    console.log('蓝牙扫描超时,自动关闭任务')
                }
            }, 3000); //时间根据自己的需求定
            let arr: any = []
            wx.onBluetoothDeviceFound(res => {
                let devices = res.devices;
                devices.forEach((device) => {
                    if (!device.name && !device.localName) {
                        return
                    }
                    // 获取设备MAC地址,并根据关键字过滤
                    let systemInfo = wx.getSystemInfoSync()
                    let iosDevice = systemInfo.system.toLowerCase().indexOf("ios") > -1;
                    let deviceMac = iosDevice ? uuid2Mac(device.advertisData) : device.deviceId
                    // keywords
                    if (keywords && keywords.length > 0) {
                        let key = keywords[0]
                        if (device.name.indexOf(key) >= 0) {
                            arr.push({
                                ...device,
                                deviceMac
                            })
                        }
                        if (arr.length) {
                            let foundDevices = arr
                            _this.deviceList = foundDevices
                            resolve({
                                data: arr,
                                success: true
                            })
                        }
                    }
                })
            })

        })

    }


/**
 * 统一安卓与IOS端deviceId展示
 * @param advertisData 
 * 在安卓设备中,获取到的 deviceId 为设备 MAC 地址,iOS 上则为设备 uuid,
 * 因此为了展示一致需要将ios的展示进行输入(当然IOS的连接还是得用获取到的uuid)
 */
function uuid2Mac(advertisData: any) {
    if (advertisData) {
        let mac = Array.prototype.map
            .call(new Uint8Array(bf), (x) => ("00" + x.toString(16)).slice(-2))
            .join(":");
        mac = mac.toUpperCase();
        return mac;
    }
}


使用案例: onBluetoothDeviceFound({ 'keywords': [''] }).then()  //keywords根据自己的需求去定,

4、处理蓝牙连接(搜索到设备开始蓝牙链接)

TypeScript 复制代码
/**
     * @param {string} options.deviceId 蓝牙设备id,连接用
     */
    async createBLEConnection(options: any): Promise<void> {
        return new Promise((resolve: any, reject: any) => {
            let { deviceId } = options,_this = this
            _this.deviceId = deviceId
            if (_this.connectStatus) {
                wx.closeBLEConnection({
                    deviceId
                })
            }
            let timeout = 3000 //根据自己需求去定
            console.log('开始连接蓝牙--', deviceId)
            _this.stopBLEDevicesTask()
            wx.createBLEConnection({
                deviceId,
                timeout,
                success(res) {
                    console.log('蓝牙连接成功-----', deviceId)
                    _this.connectStatus = true

                    resolve(true)
                },
                fail() {
                    _this.connectStatus = false
                    reject(false)
                }
            })
        })
    }


使用案例: createBLEConnection({ deviceId: tmp[0].deviceId }).then()

5、连接成功后,要获取蓝牙的所有服务 进而根据项目需求的服务 去获取对应的特征 来进行读写操作

TypeScript 复制代码
 /**
     * @param deviceId  蓝牙设备Id,来自createBLEConnection调用
     */
    async getBLEDeviceServices(deviceId: any): Promise<void> {
        return new Promise((resolve: any, reject: any) => {
            let _this = this
            wx.getBLEDeviceServices({
                deviceId,
                success(res) {
                    /**
                     * 16 位 UUID 从对接文档中获取(注意都是0000开头,接着的4位数字为16进制的uuid,所有服务只有4位uuid不一样)
                注意有多个服务,不同服务的操作不一样,单个服务只能执行单个操作,所以这里需要建立多个连接
                     */
                    for (let i = 0; i < res.services.length; i++) {
                        // 注意uuid的大小写
                        if (
                            res.services[i].isPrimary &&
                            res.services[i].uuid == "0000D0FF-0000-1000-8000-008cbhschs" //设备id,根据自己的项目去走
                        ) {
                            // _this.getBLEDeviceCharacteristics(res.services[i].uuid);
                            resolve({
                                data: res.services[i].uuid,
                                sucess: true
                            })
                            return;
                        }
                    }
                },
                fail: (res) => {
                    reject({
                        sucess: false,
                        data: res.errMsg
                    })
                    console.log("服务获取失败------------->", res.errMsg);
                },
            });
        })
    }


  async getBLEDeviceCharacteristics(serviceId: any): Promise<void> {
        return new Promise((resolve: any, reject: any) => {
            let _this = this
            wx.getBLEDeviceCharacteristics({
                deviceId: _this.deviceId,  //设备id根据自己项目的去赋值
                serviceId,  //服务id
                success: (res) => {
                    // 设备特征列表
                    let characteristics = res.characteristics;
                    for (let i = 0; i < characteristics.length; i++) {
                        let item = characteristics[i];
                        if (item.properties.write) {
                            this.serviceId = serviceId
                            this.characteristicId = item.uuid
                        }
                        if (item.properties.read) {
                            wx.readBLECharacteristicValue({
                                deviceId: _this.deviceId,
                                serviceId,
                                characteristicId: item.uuid
                            });
                        }
                        if (item.properties.write) {
                            // resolve({
                            //     data: 1,
                            //     sucess: true
                            // })
                        }
                        if (item.properties.notify || item.properties.indicate) {
                           /* 启用蓝牙低功耗设备特征值变化时的 notify 功能,订阅特征。注                        意:必须设备的特征支持 notify 或者 indicate 才可以成功调用。
另外,必须先启用 wx.notifyBLECharacteristicValueChange 才能监听到设备 characteristicValueChange 事件*/

                            wx.notifyBLECharacteristicValueChange({
                                deviceId: _this.deviceId,
                                serviceId,
                                characteristicId: item.uuid,
                                state: true,
                            });
                            resolve({
                                data: item.properties,
                                sucess: true
                            })
                        }
                    }
                },
                fail(error: any) {
                    reject({
                        sucess: false,
                        data: error
                    })
                }
            });
        })
    }

使用案例: let { data } = await getBLEDeviceServices(tmp[0].deviceId)  //data就是所有服务内容
 if (data) {
        // 2、获取蓝牙低功耗设备某个服务中所有特征 (characteristic)
        let ble_chart = await getBLEDeviceCharacteristics(data)
        if (ble_chart.sucess) {
              let item = ble_chart.data
              // 该特征是否支持 write 操作
              if (item.write) {
                   // resolve(true)
                   this.handleWriteSucess();  //处理写操作
              }
              // 该特征是否支持 notify ,indicate操作 ,开启监听订阅特征消息
              if (item.notify || item.indicate) {
                   this.watchBleData()//监听蓝牙数据
              }
        }
}

6、读写成功后开始根据蓝牙协议进行操作(蓝牙协议有设备方提供)

TypeScript 复制代码
handleWriteSucess()  //写成功后的操作
watchBleData() {
    wx.onBLECharacteristicValueChange(async (res) => { //监听蓝牙数据变化
         let resHex = ab2hex(res.value).toLocaleUpperCase() //包数据, 根据蓝牙协议去进行处理
         let { data } = writeBLECharacteristicValue('对应指令', '脚电极')
    })
}

/**
 * ArrayBuffer转16进度字符串
 * @param buffer 
 */
// ArrayBuffer转16进度字符串
ab2hex(buffer: any) {
    var hexArr = Array.prototype.map.call(new Uint8Array(buffer), function (bit) {
        return ("00" + bit.toString(16)).slice(-2);
    });
    return hexArr.join("");
}

7、处理蓝牙写指令

TypeScript 复制代码
 /**
   * 4. 发送蓝牙指令。蓝牙指令超出20字符时需要截断多次发送
   * @param {string} cmdStr 蓝牙指令
   * @param {string} cmdName 蓝牙指令名称------可选用于打印调试
   */
    async writeBLECharacteristicValue(cmdStr: any, cmdName: string): Promise<void> {
        // // console.log("发送蓝牙指令------------》", cmdStr, cmdName);
        return new Promise((resolve: any, reject: any) => {
            let _this = this
            let data = cmdStr.split(',')
            let buffer = new ArrayBuffer(data.length);
            let dataViewLast = new DataView(buffer);
            for (let i = 0; i < data.length; i++) {
                dataViewLast.setUint8(i, data[i]);
            }
            let param: any = {
                deviceId: _this.deviceId,
                serviceId: _this.serviceId,
                characteristicId: _this.characteristicId,  //在获取特性那有
                value: dataViewLast.buffer,
            };
            console.log("param", param)
            wx.writeBLECharacteristicValue({
                ...param,
                success: function () {
                    console.log("发送指令成功", cmdName);
                    resolve({
                        data: {
                            cmdName,
                            sucess: true
                        }
                    })
                },
                fail: function (error: any) {
                    reject({
                        data: {
                            data: error,
                            sucess: true
                        }
                    })
                },
            })
        })

    }

注意点:

在判断蓝牙是否打开之前,还要先判断下手机上位置是否打开

let locationEnabled = wx.getSystemInfoSync().locationEnabled

相关推荐
尚梦6 小时前
uni-app 封装刘海状态栏(适用小程序, h5, 头条小程序)
前端·小程序·uni-app
小飞哥liac8 小时前
微信小程序的组件
微信小程序
stormjun10 小时前
Java基于微信小程序的私家车位共享系统(附源码,文档)
java·微信小程序·共享停车位·私家车共享停车位小程序·停车位共享
paopaokaka_luck10 小时前
基于Spring Boot+Vue的助农销售平台(协同过滤算法、限流算法、支付宝沙盒支付、实时聊天、图形化分析)
java·spring boot·小程序·毕业设计·mybatis·1024程序员节
Bessie23412 小时前
微信小程序eval无法使用的替代方案
微信小程序·小程序·uni-app
shenweihong12 小时前
javascript实现md5算法(支持微信小程序),可分多次计算
javascript·算法·微信小程序
蜕变菜鸟12 小时前
小程序跳转另一个小程序
小程序
16 小时前
躺平成长-代码开发,利用kimi开发小程序(09)
小程序
19 小时前
微信小程序运营日记(第四天)
微信小程序·小程序
guanpinkeji19 小时前
旧衣回收小程序:提高回收效率,扩大服务范围
大数据·小程序·团队开发·软件开发·小程序开发·旧衣回收·旧衣回收小程序