前情:微信小程序中连接电子计重桌秤蓝牙,直接通过蓝牙获取称重重量,自动显示在界面上

前情:在微信小程序中连接蓝牙电子计重桌秤(电子秤品牌:Kunhong坤宏),直接通过蓝牙获取当前称重的重量数据,然后显示在界面上。

###⚠️注意
* 此次,只涉及读取数据,没有写入数据,具体 API 查看小程序官方文档
* 确保手机蓝牙已经打开,并且可以搜索到该电子秤的蓝牙设备,android 可以搜到,ios 搜不到 — 但是没有关系,小程序里 getBluetoothDevices 可以成功就可以了
* 微信小程序中搜索到的蓝牙设备很多,deviceId 在 android 上显示为蓝牙设备主服务的 mac 地址,在 ios 上显示为蓝牙设备主服务的 uuid
* 最终得到的结果是 ArrayBuffer 型数据,需要先转为16进制字符串,再转为10进制数据 — 小程序官方文档上这样提示,实际并不可行

(1)初始化

初始化蓝牙模块 — wx.openBluetoothAdapter

// 定义数据
data: {
    devices: [],    // 搜索到的蓝牙设备 deviceId 数组
    deviceId: \'\',    // 目标蓝牙设备 deviceId
    services: []    //  设备服务列表 serviceId 数组
    serviceId: \'\',
    characteristics: []   // 特征值列表
    characteristicId: \'\'  // 选择某一个特征值 
    value: \'\'   // 16 进制数据值
}
// 蓝牙 API 调用步骤
openBluetoothAdapter() {
    wx.openBluetoothAdapter({   // (1)
        success: res => {
            console.log(\'openBluetoothAdapter初始化蓝牙模块成功:\', res)
             this.startBluetoothDevicesDiscovery()  // (2) 开始搜索
        },
        fail: err => {
            console.log(\'openBluetoothAdapter初始化蓝牙模块失败:\', err)
            if (err.errCode === 10001) {  // 当前蓝牙适配器不可用
                wx.onBluetoothAdapterStateChange( res => {
                    if (res.available) {
                        this.startBluetoothDevicesDiscovery()
                    }
              })
           }
            /* 
            wx.onBluetoothAdapterStateChange({
                success: res => {
                  console.log(\'onBlueToothAdapterStateChange success 监听蓝牙适配器变化: \', res);
                  this.startBluetoothDevicesDiscovery();
                },
                fail: err => {
                     console.log(\'onBlueToothAdapterStateChange fail: \', err)
                 }
            })
            */
        }
    })
}

###(2)搜索蓝牙设备
####搜寻附近的蓝牙外围设备 — wx.startBluetoothDevicesDiscovery
* 入参 services 作用要搜索的蓝牙设备主 service 的 uuid 列表,某些蓝牙设备会广播自己的主 service 的 uuid,如果设置此参数,则只搜索广播包括对应 uuid 的主服务的蓝牙设备,可以通过该参数过滤掉周边不需要处理的其他蓝牙设备
* 入参 allowDuplicatesKey 作用是否允许重复上报同一设备,如果允许重复上报,则 wx.onBlueToothDeviceFound 方法会多次上报同一设备,但是 RSSI 值会有不同,默认为 false
eg: services: [\’FEE7\’] 主服务的 UUID 是 FEE7,传入这个参数,只搜索主服务 UUID 为 FEE7 的设备,该设备是微信硬件平台的蓝牙智能灯

⚠️ 此操作比较耗费系统资源,需要在搜索并连接到设备后调用 wx.stopBluetoothDevicesDiscovery 方法停止搜索

startBluetoothDevicesDiscovery() {
    wx.startBluetoothDevicesDiscovery({
        success: res => {
            console.log(\'startBluetoothDevicesDiscovery开始搜索外围设备成功:\', res)
            this.getBluetoothDevices()  // (3) 获取蓝牙列表
         },
        fail: err => {
            console.log(\'startBluetoothDevicesDiscovery搜索外围设备失败:\', err)
        }
    })
}

###(3)获取蓝牙设备
####获取在蓝牙模块生效期间所有已发现的蓝牙设备,包括已经连接成功的蓝牙设备 — wx.getBluetoothDevices
“`
getBluetoothDevices() {
wx.getBluetoothDevices({
success: res => {
console.log(\’getBluetoothDevices获取蓝牙设备成功:\’, res)
this.setData({
devices: res. devices || [] // uuid 对应的的已连接设备列表
})
this.createBLEConnection(); // (4) 与目标设备建立连接
},
fail: err => {
console.log(\’getBluetoothDevices获取蓝牙设备失败:\’, err)
}
})
}
“`
####这里还可以用 wx.onBluetoothDeviceFound(),但是相较于 wx.getBluetoothDevices(),这个只会监听寻找新设备,因而在一次编译中,不方便同一个蓝牙设备的复用
“`
// 监听寻找新设备
onBluetoothDeviceFound() {
let that = this;
wx.onBluetoothDeviceFound(res => {
console.log(\’onBluetoothDeviceFound success 监听寻找新设备: \’, res);
(res.devices || []).forEach(item => {
if(item.name == \’KunHong\’) {
that.setData({
deviceId: item.deviceId || \’\’
})
that.createBLEConnection(that.data.deviceId);
}
})
})
},
“`

###(4)建立连接
####与目标蓝牙设备建立连接,需要是低功耗蓝牙设备 — wx.createBLEConnection
⚠️ 如果微信小程序此前搜索过某个蓝牙设备,并成功建立连接,可直接传入之前搜索获取的 deviceId 直接尝试连接该设备,不用重新搜索
“`
createBLEConnection() {
// 如果是第一次建立连接,可以通过名称匹配,获取相应设备的 deviceId
let devices = this.data.devices;
devices.forEach(item => {
if(item.name == \’kunHong\’) {
this.setData({
deviceId: item.deviceId
})
}
})
// 建立连接
wx.createBLEConnection({
deviceId: this.data.deviceId,
success: res => {
console.log(\’createBLEConnection与目标蓝牙连接成功:\’, res)
this.getBLEDeviceServices() // (5)获取服务
},
fail: err => {
console.log(\’createBLEConnection与目标蓝牙连接失败:\’, err)
}
})
}
“`

###(5)获取蓝牙设备服务
####获取蓝牙设备所有主服务的 uuid — wx.getBLEDeviceServices
* 入参 deviceId 为 wx.getBluetoothDevices 中获取的目标蓝牙设备的 deviceId

⚠️开发过程中,主服务 serviceId 和 主服务的特征值 characteristics 都是选取的实际操作过程中,得到的类似于该目标蓝牙设备的 id,但是小程序官方文档的 demo,遍历了所有的列表(serviceId 和 characteristics),需要区分一下

getBLEDeviceServices() {
    wx.getBLEDeviceServices({
        deviceId: this.data.deviceId,
        success: res => {
            console.log(\'getBLEDeviceServices获取蓝牙设备服务\', res)
            // getBluetoothDevices 获取的有 deviceId 和 advertisServiceUUIDs,可以在这里获取的服务列表中选择一个一样的作为后续 API 请求的服务id,这个 id 需要满足是否可读
            this.setData({
                 services: res.services,
                 serviceId: res.services[0].uuid    // 假设是第一个
            })
            this.getBLEDeviceCharacteristics()    // (6) 获取特征值

            // 官方 demo
            for(var i = 0; i < res.services.length; i++) {
                // 该服务是否为主服务
                if(res.services[i].isPrimary) {
                    this.getBLEDeviceCharacteristics(res.services[i].uuid)
                }
            }
        },
        fail: err => {
            console.log(\'getBLEDeviceServices获取蓝牙设备服务失败:\', err)
        }
    })
}

###(6)获取特征值
####获取蓝牙设备某个服务中所有特征值 — wx.getBLEDeviceCharacteristics
* 入参 deviceId 为 wx.getBluetoothDevices 中获取的目标蓝牙设备的 deviceId
* 入参 serviceId 为蓝牙服务 uuid ,通过 wx.getBLEDeviceServices 获取
“`
getBLEDeviceCharacteristics(serviceId) {
wx.getBLEDeviceCharacteristics({
deviceId: this.data.deviceId,
serviceId: this.data.serviceId,
success: res => {
console.log(\’getBLEDeviceCharacteristics获取蓝牙服务特征值成功:\’, res)
this.setData({
characteristics: res. characteristics,
characteristics: res. characteristics[0].uuid
})
(res.characteristics || []).forEach(item => {
if(item.properties.read) {
wx.readBLECharacteristicValue({
deviceId: this.data.deviceId,
serviceId: serviceid,
characteristicId: res.characteristicId[i].uuid
})
}
if(item.properties.notify || item.properties.indicate) {
// 开启通知
wx.notifyBLECharacteristicValueChange({
state: true,
deviceId,
serviceId,
characteristicId: item.uuid,
success(res) {
console.log(\’notifyBLECharacteristicValueChange success state: \’, res.errMsg)
that.setData({
notifyFlag: true
})
}
})
}
})
},
fail: err => {
console.log(\’getBLEDeviceCharacteristics获取蓝牙服务特征值失败:\’, err)
}
})
this.onBLECharacteristicValueChange() // (8)监听特征值变化
}
“`

###(7)启用 notify 功能
####启用低功耗蓝牙特征值变化时的 notify 功能,订阅特征值
⚠️必须设备的特征值支持 notify 或者 indicate 才可以成功启用
“`
notifyBLECharacteristicValueChange() {
wx.notifyBLECharacteristicValueChange({
deviceId: this.data.deviceId,
serviceId: this.data.serviceId,
characteristicId: this.data. characteristicId,
state: true // 是否启用 notify (四个字段全部必填)
})
}
“`

###(8)监听特征值变化
####监听低功耗蓝牙设备特征值的变化事件 — wx.onBLECharacteristicValueChange
⚠️必须先启用 notifyBLECharacteristicValueChange 接口才能接收到设备推送的 notification(通知)
“`
// 先监听一下,保证第一时间获取数据
onBLECharacteristicValueChange() {
wx.onBLECharacteristicValueChange( characteristic => {
console.log(\’onBLECharacteristicValueChange从目标蓝牙设备监听到的数据值:\’, characteristic)
this.setData({
value: this.ab2hex(characteristic.value) // (10) 转为 16 进制
})

    //     获取最终结果 监听值是否发生变化,变化时获取最新值 避免一直监听,数据改变量较大
    let result = (this.ab2Str(characteristic.value) || \'\').split(\' \').reverse()[1];
    if(this.data.weight == result) {
       return;
    } else {
        this.setData({
          weight: result,
        })
      }
})

}

<br>
###(9)读取数据
####读取低功耗蓝牙设备的特征值的二进制数据值 --- wx.readBLECharacteristicValue
⚠️必须目标蓝牙设备的特征值支持 read 才可以成功调用,并且单独使用 readBLECharacteristicValue 并不能获取到真正的特征值,只能返回获取特征值的状态,即是否成功获取到值,真正的值需要使用 wx.onBLECharacteristicValueChange() 执行回调才可以在 wx.onBLECharacteristicValueChange() 这个 API 中获得读取到的特征值

readBLECharacteristicValue() {
wx.readBLECharacteristicValue({
deviceId: this.data.deviceId,
serviceId: this.data.serviceId,
characteristicId: this.data.charecteristicId,
success: res => {
console.log(\’readBLECharacteristicValue读取特征值成功:\’, res)
},
fail: err => {
console.log(\’readBLECharacteristicValue读取特征值失败:\’, err)
}
})
}

<br>
###(10)转为 16 进制
####官方文档中介绍了 ArrayBuffer 转为 16 进制的方法

// 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(\’\’);
}

<br>
###(11)值转换
####官方文档介绍的方法似乎有点不适用哎,试下这个

ab2Str(arrayBuffer){
let unit8Arr = new Uint8Array(arrayBuffer);
let encodedString = String.fromCharCode.apply(null, unit8Arr);
return encodedString;
}


版权声明:本文为Pecci原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://www.cnblogs.com/Pecci/p/11861162.html