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

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

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

相关推荐
郭wes代码2 小时前
Cmd命令大全(万字详细版)
python·算法·小程序
.生产的驴7 小时前
SpringBoot 对接第三方登录 手机号登录 手机号验证 微信小程序登录 结合Redis SaToken
java·spring boot·redis·后端·缓存·微信小程序·maven
汤姆yu12 小时前
基于微信小程序的乡村旅游系统
微信小程序·旅游·乡村旅游
计算机徐师兄12 小时前
基于TP5框架的家具购物小程序的设计与实现【附源码、文档】
小程序·php·家具购物小程序·家具购物微信小程序·家具购物
曲辒净13 小时前
微信小程序实现二维码海报保存分享功能
微信小程序·小程序
朽木成才14 小时前
小程序快速实现大模型聊天机器人
小程序·机器人
peachSoda714 小时前
随手记:小程序使用uni.createVideoContext视频无法触发播放
小程序
何极光14 小时前
uniapp小程序样式穿透
前端·小程序·uni-app
小墨&晓末15 小时前
【PythonGui实战】自动摇号小程序
python·算法·小程序·系统安全
oil欧哟1 天前
🤔认真投入一个月做的小程序,能做成什么样子?有人用吗?
前端·vue.js·微信小程序