实现方式:
本文使用 uni-app + Vue3 的方式进行开发,以手机app的方式运行。
uni-app 提供了低功耗蓝牙 的 api ,和微信小程序提供的 api 是一样的,所以本文的讲解也适用于微信小程序。
本文参考文档:
https://blog.csdn.net/xx_ll_/article/details/108761621?ops_request_misc=&request_id=&biz_id=102&utm_term=uniapp%E8%93%9D%E7%89%99%E6%B5%81%E7%A8%8B&utm_medium=distribute.pc_search_result.none-task-blog-2~all~sobaiduweb~default-0-108761621.142^v38^pc_rank_34&spm=1018.2226.3001.4187
官方文档
https://uniapp.dcloud.net.cn/
配置环境:
- 开发工具:HBuilder X(最新版)
- 框架:uni-app
- 演示效果: iOS或者安卓APP
整体流程:
-
初始化蓝牙(openBluetoothAdapter):查看蓝牙是否可用,若初始化失败,则是蓝牙未打开,提示用户打开蓝牙。若已经打开蓝牙,则准备进行蓝牙搜索。
-
搜索蓝牙(startBluetoothDevicesDiscovery):开始搜索蓝牙备。这一步需要和关闭搜索蓝牙(stopBluetoothDevicesDiscovery)成对使用,长时间占用搜索设备,浪费资源,在查找到需要的设备连接之后需要主动去停止搜索设备。
-
获取已搜索到的蓝牙列表(getBluetoothDevices):查看所有已经发现的蓝牙设备getBluetoothDevices。在这一步可以查看到以前已经获取到的蓝牙设备deviceId。可以在这一步中查看以前已经连接到的设备,主动去尝试连接这个设备。
-
连接蓝牙设备(createBLEConnection):通过设备的deviceId来连接设备。在这里如果APP若是已经连接过此低功耗蓝牙设备,可以直接传入之前设备ID进行尝试连接。这一步的连接操作需要和关闭连接closeBLEConnection成对操作。如果多次调用创建连接,有可能会导致系统持有一个设备的多个连接实例,导致在调用关闭连接的时候不能真正关闭连接。
-
获取设备所有服务(getBLEDeviceServices):在连接设备之后,APP需要主动去获取设备的所有服务(services),设备会返回给APP设备的服务列表(services)包含设备服务的UUID,该服务是否为主服务。
-
获取服务特征值(getBLEDeviceCharacteristic):在获取设备的服务列表之后,根据自己设备的蓝牙协议接口文档,根据指定的服务ID(serviceId)使用获取服务特征值方法传入两个参数设备ID(deviceId)和服务ID(serviceId)向设备获取该服务中的所有的特征值(characteristic),设备会向APP返回该服务中的所有特征值列表,列表包含设备特征值的UUID,该特征值支持的操作类型。
-
向设备写入控制命令(writeBLECharacteristicValue):可以向设备写入(发送)控制命令writeBLECharacteristicValue,此方法是向低功耗蓝牙设备特征值写入二进制数据。需要注意只有该特征值的属性支持write才可以调用此方法。在此方法调用成功后,设备特征值发生改变,就会触发onBLECharacteristicValueChange回调,主动返回特征值数据。
1 初始化蓝牙代码:
<template>
<view class="content">
<image class="logo" src="/static/logo.png"></image>
<view class="text-area">
<button @click="init">初始化一下咯</button>
</view>
</view>
</template>
<script>
export default {
data() {
return {
title: 'Hello'
}
},
onLoad() {
},
methods: {
init() {
uni.openBluetoothAdapter({
success(res) {
console.log('初始化蓝牙成功')
uni.showToast({
title: '初始化成功',
})
console.log(res)
},
fail(err) {
console.log('初始化蓝牙失败')
console.error(err)
}
})
},
}
}
</script>
<style>
.content {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
.logo {
height: 200rpx;
width: 200rpx;
margin-top: 200rpx;
margin-left: auto;
margin-right: auto;
margin-bottom: 50rpx;
}
.text-area {
display: flex;
justify-content: center;
}
.title {
font-size: 36rpx;
color: #8f8f94;
}
</style>
2 搜索蓝牙设备并获取蓝牙设备列表代码:
<template>
<view class="content">
<view class="text-area">
<button @click="init">初始化一下咯</button>
<button @click="search">搜索蓝牙</button>
</view>
<scroll-view scroll-y class="box">
<view class="item" v-for="item in deviceList">
<view>
<text>id:{{item.deviceId}}</text>
</view>
<view>
<text>name:{{item.name}}</text>
<button @click="connect(item)">连接</button>
</view>
</view>
</scroll-view>
</view>
</template>
<script>
export default {
data() {
return {
title: 'Hello',
deviceId: '',
serviceId: '',
characteristicId: '',
deviceList: [],
}
},
onLoad() {
},
methods: {
init() {
uni.openBluetoothAdapter({
success(res) {
console.log('初始化蓝牙成功')
uni.showToast({
title: '初始化成功',
})
console.log(res)
},
fail(err) {
console.log('初始化蓝牙失败')
console.error(err)
}
})
},
search() {
var that = this;
uni.startBluetoothDevicesDiscovery({
success(res) {
uni.showToast({
title: '开始搜索请等待...',
});
setTimeout(() => {
uni.hideLoading()
}, 2000);
setTimeout(() => {
that.getBluetoothDevices();
}, 1000);
console.log('开始搜索')
// 开启监听回调
},
/* fail(err) {
console.log('搜索失败')
console.error(err)
} */
})
},
getBluetoothDevices() {
//获取蓝牙设备列表
uni.getBluetoothDevices({
success: res => {
let data = res.devices
let tempList = [];
data.map(device => {
if (!!device.localName) {
tempList.push(device)
}
});
this.deviceList = tempList;
}
});
},
stop() {
//停止搜索蓝牙
uni.stopBluetoothDevicesDiscovery({
success(res) {
console.log(res)
}
})
},
}
}
</script>
<style>
.box {
width: 100%;
height: 800rpx;
box-sizing: border-box;
margin-bottom: 20rpx;
border: 2px solid dodgerblue;
}
.item {
box-sizing: border-box;
padding: 10rpx;
border-bottom: 1px solid #ccc;
}
button {
margin-bottom: 20rpx;
}
</style>
3 连接设备,并且获取到相关信息传到指定页面:
<template>
<view class="content">
<view class="text-area">
<button @click="init">初始化一下咯</button>
<button @click="search">搜索蓝牙</button>
<!-- <button @click="goto1">控制风扇</button> -->
</view>
<scroll-view scroll-y class="box">
<view class="item" v-for="item in deviceList">
<view>
<text>id:{{item.deviceId}}</text>
</view>
<view>
<text>name:{{item.name}}</text>
<button @click="connect(item)">连接</button>
</view>
</view>
</scroll-view>
</view>
</template>
<script>
export default {
data() {
return {
title: 'Hello',
deviceId: '',
serviceId: '',
characteristicId: '',
deviceList: [],
}
},
onLoad() {
},
methods: {
init() {
uni.openBluetoothAdapter({
success(res) {
console.log('初始化蓝牙成功')
uni.showToast({
title: '初始化成功',
})
console.log(res)
},
fail(err) {
console.log('初始化蓝牙失败')
console.error(err)
}
})
},
search() {
var that = this;
uni.startBluetoothDevicesDiscovery({
success(res) {
uni.showToast({
title: '开始搜索请等待...',
});
setTimeout(() => {
uni.hideLoading()
}, 2000);
setTimeout(() => {
that.getBluetoothDevices();
}, 1000);
console.log('开始搜索')
// 开启监听回调
},
/* fail(err) {
console.log('搜索失败')
console.error(err)
} */
})
},
getBluetoothDevices() {
//获取蓝牙设备列表
uni.getBluetoothDevices({
success: res => {
let data = res.devices
let tempList = [];
data.map(device => {
if (!!device.localName) {
tempList.push(device)
}
});
this.deviceList = tempList;
}
});
},
stop() {
//停止搜索蓝牙
uni.stopBluetoothDevicesDiscovery({
success(res) {
console.log(res)
}
})
},
connect(item) {
let deviceId = item.deviceId
let that = this
console.log(deviceId) //设备id
uni.setStorageSync("deviceId",deviceId);
uni.createBLEConnection({
deviceId,// 这里的 deviceId 需要已经通过 createBLEConnection 与对应设备建立链接
success(res) {
console.log(res)
uni.showToast({
title: '连接成功'
});
that.stop();
setTimeout(() => {
uni.getBLEDeviceServices({
deviceId, // 设备ID
success(res) {
for(let i=0;i < res.services.length;i++)
{
if(res.services[i].isPrimary)
{
this.serviceId=res.services[i].uuid;//获取到isprimary不等于空的哪个ServiceId
}
};
let serviceId=this.serviceId;
console.log(serviceId)
uni.setStorageSync('serviceId',this.serviceId);
setTimeout(() => {
uni.getBLEDeviceCharacteristics({
deviceId, // 设备ID,在前面已经获取到
serviceId, // 服务UUID,在前面已经获取到
success(res) {
console.log('分割线')
for(let i=0;i<res.characteristics.length;i++)
{
console.log('分割线2')
let item=res.characteristics[i];
if(item.properties.write)
{
console.log('分割线222')
this.serviceId=serviceId;
this.characteristics=item.uuid;//获取到characteristicsId
let characteristicId=this.characteristics;
uni.setStorageSync('characteristicId',this.characteristics);
}
};
let characteristicId=this.characteristics;
console.log(characteristicId)
uni.navigateTo({
deviceId, // 设备ID
serviceId, // 服务UUID
characteristicId,
url: '/pages/fancontrol/fancontrol?deviceId=' +
deviceId +
'&serviceId=' +
serviceId +
'&characteristicId=' +
characteristicId,//页面传参,url地址需要改成你自己的
success() {
console.log(
'跳转成功');
},
fail(res) {
console.log(
'跳转失败');
}
})
},
fail(err) {
console.error(err)
}
})
})
},
fail(err) {
console.error(err)
}
})
});
}
})
}
}
}
</script>
<style>
.box {
width: 100%;
height: 800rpx;
box-sizing: border-box;
margin-bottom: 20rpx;
border: 2px solid dodgerblue;
}
.item {
box-sizing: border-box;
padding: 10rpx;
border-bottom: 1px solid #ccc;
}
button {
margin-bottom: 20rpx;
}
</style>