最近很闲,但是行业很卷!因为公司有硬件设备对接,但是介于原生app。闲来无事,便研究了下这个小程序通过蓝牙与硬件设备进行通讯。废话少说上干货!
本次讲解的目录大致分为三模块。根据我写的代码做讲解。
- 初始化并搜索蓝牙
- 获取并启用service服务
- 数据读取初始化并监听设备返回的数据
html部分
js逻辑部分-分开讲解
页面进来是需要初始化的date中的数据,所以需要在onload中执行
onload中的初始化代码-就是设置列表高度、蓝牙列表的初始化
其中初始化最后面有一段代码注释也写得很明确了,第一种的过滤出你设置好的蓝牙,第二种反之。
在onHide事件就是--重置清空
接下来就是事件逻辑了!!!
我们这里罗列下uniapp官网提供的接下来需要用到的api
// 关闭蓝牙
uni.closeBluetoothAdapter({})
// 打开蓝牙
uni.openBluetoothAdapter({})
// 搜索附近的蓝牙
uni.startBluetoothDevicesDiscovery({})
// 停止搜索
uni.stopBluetoothDevicesDiscovery({})
// 连接低功耗BLE蓝牙
uni.createBLEConnection({})
// 获取蓝牙设备所有服务(service)
uni.getBLEDeviceServices({})
// 获取蓝牙特征值
uni.getBLEDeviceCharacteristics({})
// 启用低功耗蓝牙设备特征值变化时的 notify 功能,订阅特征值
uni.notifyBLECharacteristicValueChange({})
// 监听低功耗蓝牙设备的特征值变化事件
uni.onBLECharacteristicValueChange({})
// 读取低功耗蓝牙设备的特征值的二进制数据值
uni.readBLECharacteristicValue({})
我是把各个事件归总到三个事件里面去执行了。没有分事件来讲解,见谅
首先,是搜索蓝牙:搜索蓝牙里面需用到的就是,关闭蓝牙-打开蓝牙-搜索蓝牙-停止搜索。
搜索成功后点击蓝牙列表进行连接
紧接着获取蓝牙设备所有服务(service)及相关的格式转换
启动所有服务(service)后,进行蓝牙读取的初始化并监听
附上代码,希望对大家有所帮助。全文干货满满,实战,文章附上实战视频。
<template>
<view class="device">
<scroll-view scroll-y :style="'width:500rpx;height:' + list_height + 'rpx'">
<block v-for="(item, index) in devicesList" :key="index">
<view class="list-item" :id="item.deviceId" @tap="Connect">
<view style="display: flex; flex-direction: column; width: 80%">
<text style="font-size: medium; word-break: break-all"
>设备名称: {{ item.name }}</text
>
<text style="font-size: x-small; color: gray; word-break: break-all"
>设备ID: {{ item.deviceId }}</text
>
<text style="font-size: x-small; color: gray; word-break: break-all"
>信号强度RSSI: {{ item.RSSI }}</text
>
</view>
<image
style="width: 36px; height: 36px"
mode="aspectFit"
src="/static/images/bluetooth.png"
></image>
</view>
</block>
</scroll-view>
<button
type="primary"
class="button"
:loading="searching"
@tap="Search"
style="margin-bottom: 10px"
>
{{ searching ? '搜索中...' : '1.搜索蓝牙设备' }}
</button>
<button
type="primary"
class="button"
@click="getBLEDeviceServices"
style="margin-bottom: 10px"
>
2.获取servies服务
</button>
<button
type="primary"
class="button"
:loading="initLoading"
@click="readBluetoothAdapter"
style="margin-bottom: 10px"
>
3.初始化数据
</button>
</view>
</template>
<script>
const app = getApp()
export default {
data () {
return {
searching: false,
deviceName: 'EV-IC-1961B', // 指定蓝牙名称
deviceId: null, // 设备id
services: null, // services服务
connectedDeviceId: null, // 记录设备id
notifyCharacteristicsId: null, // 蓝牙特征值
balanceData: null, // 转换的格式
notifyServicweId: null, // 特征id
hexstr: null, // 转换格式
devicesList: [], // 列表
list_height: '',
initLoading: false
}
},
onLoad () {
var that = this
var list_height =
(app.globalData.SystemInfo.windowHeight - 200) *
(750 / app.globalData.SystemInfo.windowWidth) -
60
that.$set(that, 'list_height', list_height)
uni.onBluetoothAdapterStateChange(function (res) {
that.$set(that, 'searching', res.discovering)
if (!res.available) {
that.$set(that, 'searching', false)
}
})
that.$set(that, 'list_height', list_height)
uni.onBluetoothAdapterStateChange(function (res) {
that.$set(that, 'searching', res.discovering)
if (!res.available) {
that.$set(that, 'searching', false)
}
})
uni.onBluetoothDeviceFound(function (devices) {
//剔除重复设备,兼容不同设备API的不同返回值
var isnotexist = true
if (devices.deviceId) {
if (devices.advertisData) {
devices.advertisData = app.globalData.buf2hex(devices.advertisData)
} else {
devices.advertisData = ''
}
for (var i = 0; i < that.devicesList.length; i++) {
if (devices.deviceId == that.devicesList[i].deviceId) {
isnotexist = false
}
}
if (isnotexist) {
that.devicesList.push(devices)
}
} else if (devices.devices) {
if (devices.devices[0].advertisData) {
devices.devices[0].advertisData = app.globalData.buf2hex(
devices.devices[0].advertisData
)
} else {
devices.devices[0].advertisData = ''
}
for (var i = 0; i < that.devicesList.length; i++) {
if (devices.devices[0].deviceId == that.devicesList[i].deviceId) {
isnotexist = false
}
}
if (isnotexist) {
that.devicesList.push(devices.devices[0])
}
} else if (devices[0]) {
if (devices[0].advertisData) {
devices[0].advertisData = app.globalData.buf2hex(
devices[0].advertisData
)
} else {
devices[0].advertisData = ''
}
// console.log(devices[0])
for (var i = 0; i < devices_list.length; i++) {
if (devices[0].deviceId == that.devicesList[i].deviceId) {
isnotexist = false
}
}
if (isnotexist) {
that.devicesList.push(devices[0])
}
}
let devList = []
// 专用名称 意思是只有这名字才显示出来
// that.devicesList.forEach(item=>{
// if(item.name == that.deviceName){
// that.deviceId = item.deviceId; // 直接获取指定设备的id
// devList.push(item)
// }
// })
// 有设备名 意思是有设备名就显示
that.devicesList.forEach(item => {
if (item.name) {
devList.push(item)
}
})
// that.$set(that, 'devicesList', that.devicesList)
that.$set(that, 'devicesList', devList) // 过滤
})
},
onHide: function () {
var that = this
that.$set(that, 'devicesList', [])
if (this.searching) {
uni.stopBluetoothDevicesDiscovery({
success: function (res) {
that.$set(that, 'searching', false)
}
})
}
},
methods: {
// 搜索设备
Search: function () {
var that = this
if (!that.searching) {
// 关闭蓝牙
uni.closeBluetoothAdapter({
complete: function (res) {
// 打开蓝牙
uni.openBluetoothAdapter({
success: function (res) {
// 搜索蓝牙
uni.startBluetoothDevicesDiscovery({
allowDuplicatesKey: false,
success: function (res) {
that.$set(that, 'searching', true)
that.$set(that, 'devicesList', [])
setTimeout(() => {
// 停止搜索
uni.stopBluetoothDevicesDiscovery({
success: function (res) {
uni.showToast({
title: '搜索完成',
icon: 'success',
duration: 500
})
that.$set(that, 'searching', false)
}
})
}, 2000)
}
})
},
fail: function (res) {
uni.showModal({
title: '提示',
content: '请检查手机蓝牙是否打开',
showCancel: false,
success: function (res) {
that.$set(that, 'searching', false)
}
})
}
})
}
})
} else {
uni.stopBluetoothDevicesDiscovery({
success: function (res) {
that.$set(that, 'searching', false)
}
})
}
},
// 5连接蓝牙Connect connectTO
Connect (e) {
var that = this
uni.showLoading({
title: '连接蓝牙设备中...'
})
that.deviceId = e.currentTarget.id // 这里是做非指定设备的兼容赋值
console.log('设备id', that.deviceId)
app.globalData.deviceId = that.deviceId
uni.createBLEConnection({
deviceId: that.deviceId, // 蓝牙设备id
success: function (res) {
that.connectedDeviceId = that.deviceId
uni.hideLoading()
uni.showToast({
title: '连接成功',
icon: 'success',
duration: 1000
})
},
fail: function (res) {
uni.hideLoading()
uni.showModal({
title: '提示',
content: '连接失败',
showCancel: false
})
}
})
},
// 6获取蓝牙设备的service服务
getBLEDeviceServices () {
var that = this
uni.getBLEDeviceServices({
deviceId: that.deviceId || app.globalData.deviceId,
success: function (res) {
uni.showToast({
title: '启动成功',
icon: 'success',
duration: 1000
})
that.services = res.services // 拿到数组对象
/* 获取连接设备的所有特征值 */
that.getBLEDeviceCharacteristics()
},
fail: error => {
console.log(error, '错误反馈')
}
})
},
// 7获取蓝牙设备特征值
getBLEDeviceCharacteristics () {
var that = this
// console.log(that.services,'that.services'); // 拿到了数据
uni.getBLEDeviceCharacteristics({
// deviceId: that.connectedDeviceId,
deviceId: that.deviceId || app.globalData.deviceId,
serviceId: that.services[0].uuid,
success: function (res) {
console.log(res, 'res')
for (var i = 0; i < res.characteristics.length; i++) {
if (
(res.characteristics[i].properties.notify ||
res.characteristics[i].properties.indicate) &&
res.characteristics[i].properties.read &&
res.characteristics[i].properties.write
) {
console.log(res.characteristics[i].uuid, '蓝牙特征值 ==========') // 已获得
/* 获取蓝牙特征值 */
that.notifyCharacteristicsId = res.characteristics[i].uuid
// 启用低功耗蓝牙设备特征值变化时的 notify 功能
that.notifyBLECharacteristicValueChange()
}
}
},
fail: function (res) {}
})
},
// 启动notify 蓝牙监听功能 后使用 wx.onBLECharacteristicValueChange用来监听蓝牙设备传递数据
// 接收到的数据和发送的数据必须是二级制数据, 页面展示的时候需要进行转换
notifyBLECharacteristicValueChange () {
// 启用低功耗蓝牙设备特征值变化时的 notify 功能
var that = this
console.log('6.启用低功耗蓝牙设备特征值变化时的 notify 功能')
uni.notifyBLECharacteristicValueChange({
state: true,
deviceId: that.deviceId || app.globalData.deviceId,
serviceId: that.services[0].uuid, // 蓝牙特征值对应服务的 uuid
characteristicId: that.notifyCharacteristicsId,
complete (res) {
console.log('7启用低功耗蓝牙设备监听成功')
/*用来监听手机蓝牙设备的数据变化*/
uni.onBLECharacteristicValueChange(function (res) {
/**/
that.balanceData += that.buf2string(res.value)
that.hexstr += that.receiveData(res.value)
})
},
fail (res) {
console.log(res, '启用低功耗蓝牙设备监听失败')
}
})
},
/*转换成需要的格式*/
buf2string (buffer) {
var arr = Array.prototype.map.call(new Uint8Array(buffer), x => x)
return arr
.map((char, i) => {
return String.fromCharCode(char)
})
.join('')
},
receiveData (buf) {
return this.hexCharCodeToStr(this.ab2hex(buf))
},
/*转成二进制*/
ab2hex (buffer) {
var hexArr = Array.prototype.map.call(
new Uint8Array(buffer),
function (bit) {
return ('00' + bit.toString(16)).slice(-2)
}
)
return hexArr.join('')
},
/*转成可展会的文字*/
hexCharCodeToStr (hexCharCodeStr) {
var trimedStr = hexCharCodeStr.trim()
var rawStr =
trimedStr.substr(0, 2).toLowerCase() === '0x'
? trimedStr.substr(2)
: trimedStr
var len = rawStr.length
var curCharCode
var resultStr = []
for (var i = 0; i < len; i = i + 2) {
curCharCode = parseInt(rawStr.substr(i, 2), 16)
resultStr.push(String.fromCharCode(curCharCode))
}
return resultStr.join('')
},
// 读取蓝牙传输的数据
readBluetoothAdapter () {
var that = this
that.initLoading = true
// 必须在这里的回调才能获取
uni.onBLECharacteristicValueChange(function (characteristic) {
console.log(that.buf2string(characteristic.value), '16进制解析')
})
uni.readBLECharacteristicValue({
// 这里的 deviceId 需要已经通过 createBLEConnection 与对应设备建立链接
deviceId: that.deviceId || app.globalData.deviceId,
// 这里的 serviceId 需要在上面的 getBLEDeviceServices 接口中获取
serviceId: that.services[0].uuid,
// 这里的 characteristicId 需要在上面的 getBLEDeviceCharacteristics 接口中获取
characteristicId: that.notifyCharacteristicsId,
success: function (res) {
console.log('readBLECharacteristicValue:', res) // ok
}
})
setTimeout(() => {
that.initLoading = false
uni.showToast({
title: '初始化成功',
icon: 'success'
})
}, 2000)
}
// 在向蓝牙设备传递数据和接收数据的过程中,并未使用到read的API 不知道有没有潜在的问题,目前线上运行为发现任何的问题
}
}
</script>
<style>
.device {
padding: 0 15px;
}
</style>
视频链接:小程序蓝牙设备数据对接实战