xiaokuaisong-xiaochengxu/uniapp04/pages/blueTooth/blueTooth.vue

857 lines
24 KiB
Vue
Raw Normal View History

2024-10-18 07:53:00 +00:00
<template>
<!-- 蓝牙连接页面 -->
<view class="content">
<button type="default" v-show="!shows" @click="initBle">
初始化蓝牙模块
</button>
<scroll-view scroll-y="true" show-scrollbar="true">
<radio-group>
<view
v-for="(item, index) in bleDevs"
:key="index"
v-show="item.name.length > 0 && !shows"
style="padding: 10rpx 20rpx; border-bottom: 1rpx solid #ececec"
v-if="Math.max(100 + item.RSSI, 0) >= 30"
>
<view style="font-size: 32rpx; color: #333">
<checkbox-group
@change="checkboxChange"
:data-name="item.name"
:data-deviceId="item.deviceId"
>
<label>
<checkbox :value="item.deviceId">
{{ item.name }}
</checkbox>
</label>
</checkbox-group>
</view>
<view style="font-size: 20rpx; padding: 10rpx 0">
deviceId: {{ item.deviceId }} 信号强度: {{ item.RSSI }}dBm ({{
Math.max(100 + item.RSSI, 0)
}}%)
</view>
</view>
<view class="dis">
<view @tap="connectBle" v-if="!shows" class="pl"> 连接 </view>
<view @tap="close" v-if="shows" class="pl"> 断开 </view>
</view>
</radio-group>
</scroll-view>
<view class="barItems" v-if="shows">
<view class="barItem" v-for="(item, index) in testItems" :key="index">
<view class="name">{{ item.name }}</view>
<!-- <sliderBar
class="bar"
:min="item.min"
:max="item.max"
@change="changeBar($event, item)"
></sliderBar> -->
<view class="bar">
<view class="reduce" @click="changNums(1, item)"></view>
<input type="tel" v-model="item.typeNums" @input="changeBar(item)" />
<view class="add" @click="changNums(2, item)"></view>
</view>
</view>
</view>
<view class="timers" v-if="shows">
<view class="time">
{{ titleTime }}
</view>
<view class="btns">
<view @click="begin">启动</view>
<view @click="pause">暂停</view>
<view @click="stop">停止</view>
</view>
</view>
<view v-if="shows">
<view class="input3">
<input type="text" v-model="input1" />
<input type="text" v-model="input2" />
</view>
<button type="default" class="send" @click="send(1)">发送</button>
</view>
<view class="appItems">
<viwe
:class="[item.status ? 'item bakBlue' : 'item']"
v-for="(item, index) in totalList"
:key="index"
>
<view class="txt">{{ item.text }}</view>
<view class="name p_hide">{{ item.name }}</view>
</viwe>
</view>
<view class="items" v-if="shows">
<view class="item" v-for="(item, index) in getData" :key="index">
{{ item.name }}{{ item.txt }}
</view>
</view>
</view>
</template>
<script>
export default {
data() {
return {
config: {
color: "#333",
backgroundColor: [1, "#fff"],
title: "多设备蓝牙连接",
back: false,
},
title: "Hello",
bleDevs: [],
status: -2, //-2未连接 -1已连接 0连接成功
deviceId: "",
serviceId: "",
characteristicId: "",
sendData: "",
getData: [],
deviceIds: [],
totalList: [], // 全部已连接的设备
timeIndex: 0, // 默认是列表的第一个
timeout: null,
shows: false,
testItems: [
{
index: 1,
typeNums: 1,
min: 0,
max: 150,
name: "设定频率",
value: "F",
},
{
index: 2,
typeNums: 250,
min: 50,
max: 250,
name: "设定脉宽",
value: "W",
},
{ index: 3, typeNums: 3, min: 0, max: 3, name: "设定类型", value: "C" },
{
index: 4,
typeNums: 0,
min: 0,
max: 120,
name: "设定电流",
value: "I",
},
{
index: 5,
typeNums: 0,
min: 1,
max: 100,
name: "设定方案",
value: "M",
},
],
titleTime: "00:00:00",
timer: "",
hour: 0,
minutes: 0,
seconds: 0,
input1: "B",
input2: "",
};
},
destroyed() {
clearInterval(this.timer);
},
onLoad() {},
mounted() {
this.onBLEConnectionStateChange();
},
methods: {
// 开始计时
begin() {
if (this.start) {
return;
}
this.sendData = "BS1\r";
this.start = true;
this.timer = setInterval(this.startTimer, 1000);
this.send();
},
startTimer() {
this.seconds += 1;
if (this.seconds >= 60) {
this.seconds = 0;
this.minute = this.minute + 1;
}
if (this.minute >= 60) {
this.minute = 0;
this.hour = this.hour + 1;
}
this.titleTime =
(this.hour < 10 ? "0" + this.hour : this.hour) +
":" +
(this.minutes < 10 ? "0" + this.minutes : this.minutes) +
":" +
(this.seconds < 10 ? "0" + this.seconds : this.seconds);
},
// 暂停倒计时
pause() {
if (this.timer) {
clearInterval(this.timer);
this.start = false;
this.sendData = "BS2\r";
this.send();
// this.timer = null
}
},
stop() {
if (this.timer) {
clearInterval(this.timer);
// this.timer = null
this.sendData = "BS3\r";
this.send();
this.titleTime = "00:00:00";
this.timer = "";
this.hour = 0;
this.minutes = 0;
this.seconds = 0;
this.start = false;
}
},
changNums(index, item) {
// 1为减少2为增加
if (index == 1) {
if (item.typeNums <= item.min) {
uni.showToast({
title: "已经不能再减少了",
icon: "none",
});
return;
}
item.typeNums--;
} else if (index == 2) {
if (item.typeNums >= item.max) {
uni.showToast({
title: "已经不能再增加了",
icon: "none",
});
return;
}
item.typeNums++;
}
this.changeBar(item);
},
changeBar(item) {
// 处理防抖
if (this.timeout) {
clearTimeout(this.timeout);
}
this.timeout = setTimeout(() => {
if (item.typeNums < item.min) {
uni.showToast({
title: "低于最小值,已变更为最小值发送",
icon: "none",
});
item.typeNums = item.min;
} else if (item.typeNums > item.max) {
uni.showToast({
title: "超过最大值,已变更为最大值发送",
icon: "none",
});
item.typeNums = item.max;
}
this.sendData = "B" + item.value + item.typeNums + "\r";
for (let i = 0; i < this.deviceIds.length; i++) {
this.getBLEDeviceServices(1, this.deviceIds[i]);
}
}, 500);
},
checkboxChange(e) {
if (e.target.value[0] && e.target.dataset.name) {
let item = {
deviceId: e.target.value[0],
name: e.target.dataset.name,
};
this.deviceIds.push(item);
} else {
for (let index = 0; index < this.deviceIds.length; index++) {
let item = this.deviceIds[index];
if (item.deviceId == e.target.dataset.deviceid) {
this.deviceIds.splice(index, 1);
}
}
}
},
hextoString(hex) {
var arr = hex.split("");
var out = "";
for (var i = 0; i < arr.length / 2; i++) {
var tmp = "0x" + arr[i * 2] + arr[i * 2 + 1];
var charValue = String.fromCharCode(tmp);
out += charValue;
}
return out;
},
send(index) {
let that = this;
if (index == 1) {
that.sendData = that.input1 + that.input2 + "\r";
}
if (!that.sendData) {
return uni.showToast({
title: "发送数据不可为空",
icon: "none",
});
}
uni.showLoading({
title: "发送中,请稍等",
mask: true,
});
for (let i = 0; i < that.deviceIds.length; i++) {
that.getBLEDeviceServices(1, that.deviceIds[i]);
}
},
// ArrayBuffer转16进度字符串示例
ab2hex(buffer) {
const hexArr = Array.prototype.map.call(
new Uint8Array(buffer),
function (bit) {
return ("00" + bit.toString(16)).slice(-2);
}
);
return hexArr.join("");
},
onBLEConnectionStateChange() {
uni.onBLEConnectionStateChange((res) => {
// 该方法回调中可以用于处理连接意外断开等异常情况
if (res.connected == false) {
uni.hideLoading();
for (let i = 0; i < this.deviceIds.length; i++) {
if (res.deviceId == this.deviceIds[i].deviceId) {
uni.showToast({
title: this.deviceIds[i].name + " 蓝牙设备断开连接",
icon: "none",
});
}
}
}
});
},
//初始化蓝牙
initBle() {
// console.log("初始化蓝牙>>>");
this.bleDevs = [];
this.deviceIds = [];
uni.openBluetoothAdapter({
success: (res) => {
//已打开
uni.getBluetoothAdapterState({
//蓝牙的匹配状态
success: (res1) => {
// console.log(res1, "“本机设备的蓝牙已打开”");
// 开始搜索蓝牙设备
this.startBluetoothDeviceDiscovery();
},
fail(error) {
uni.showToast({ icon: "none", title: "查看手机蓝牙是否打开" });
},
});
},
fail: (err) => {
//未打开
uni.showToast({ icon: "none", title: "查看手机蓝牙是否打开" });
},
});
},
// 开始搜索蓝牙设备
startBluetoothDeviceDiscovery() {
uni.startBluetoothDevicesDiscovery({
success: (res) => {
// console.log("启动成功", res);
// 发现外围设备
this.onBluetoothDeviceFound();
},
fail: (err) => {
// console.log(err, "错误信息");
},
});
},
// 发现外围设备
onBluetoothDeviceFound() {
// console.log("执行到这--发现外围设备")
uni.onBluetoothDeviceFound((res) => {
// 吧搜索到的设备存储起来,方便我们在页面上展示
if (this.bleDevs.indexOf(res.devices[0]) == -1) {
this.bleDevs.push(res.devices[0]);
}
// console.log("蓝牙列表", res);
});
},
// 多选然后连接
connectBle() {
if (this.deviceIds.length == 0) {
uni.showToast({ title: "请选择连接的设备", icon: "none" });
return;
}
this.getData = [];
// for (let i = 0; i < this.deviceIds.length; i++) {
// this.createBLEConnection(this.deviceIds[i]);
// // this.nowLinkLis(this.deviceIds[i]);
// }
this.deviceIds.forEach((item) => {
// this.createBLEConnection(item);
this.nowLinkLis(item);
});
},
//选择设备连接吧deviceId传进来
createBLEConnection(item) {
uni.showLoading({
title: "连接中,请稍等",
mask: true,
});
let that = this;
//连接蓝牙
uni.createBLEConnection({
deviceId: item.deviceId,
success(res) {
that.shows = true;
that.stopBluetoothDevicesDiscovery();
that.getBLEDeviceServices(2, item);
},
fail(res) {
console.log("蓝牙连接失败", res);
uni.showToast({
title: items.name + "蓝牙连接失败",
icon: "none",
});
},
});
},
// 停止搜寻蓝牙设备
stopBluetoothDevicesDiscovery() {
uni.stopBluetoothDevicesDiscovery({
success: (e) => {
this.loading = false;
// console.log("停止搜索蓝牙设备:" + e.errMsg);
},
fail: (e) => {
console.log("停止搜索蓝牙设备失败,错误码:" + e.errCode);
},
});
},
//获取蓝牙的所有服务
getBLEDeviceServices(index, items) {
setTimeout(() => {
uni.getBLEDeviceServices({
// 这里的 deviceId 需要已经通过 createBLEConnection 与对应设备建立链接
deviceId: items.deviceId,
success: (res) => {
// console.log("成功",res)
// console.log("device services:", res);
//这里会获取到好多个services uuid 我们只存储我们需要用到的就行这个uuid一般硬件厂家会给我们提供
console.log("services", res.services);
res.services.forEach((item) => {
if (
item.uuid.indexOf("0000FFE0-0000-1000-8000-00805F9B34FB") != -1
) {
items["serviceId"] = item.uuid;
//进入特征
this.getBLEDeviceCharacteristics(index, items);
}
});
},
});
}, 1000);
},
//获取蓝牙特征
getBLEDeviceCharacteristics(index, items) {
// console.log("进入特征");
setTimeout(() => {
uni.getBLEDeviceCharacteristics({
// 这里的 deviceId 需要已经通过 createBLEConnection 与对应设备建立链接
deviceId: items.deviceId,
// 这里的 serviceId 需要在 getBLEDeviceServices 接口中获取
serviceId: items.serviceId,
success: (res) => {
console.log("characteristics", res);
res.characteristics.forEach((item) => {
if (
// 2 支持监听 1 支持写入
item.uuid.indexOf(
index == 1
? "0000FFE1-0000-1000-8000-00805F9B34FB"
: "0000FFE2-0000-1000-8000-00805F9B34FB"
) != -1
) {
items["characteristicId"] = item.uuid;
if (index == 2) {
this.notifyBLECharacteristicValueChange(items);
}
}
});
if (index == 1) {
this.writeString(this.sendData, items);
}
},
fail: (res) => {
console.log(res);
},
});
}, 0);
},
// 启用 notify 功能
notifyBLECharacteristicValueChange(items) {
let that = this;
uni.notifyBLECharacteristicValueChange({
state: true, // 启用 notify 功能
// 这里的 deviceId 需要已经通过 createBLEConnection 与对应设备建立链接
deviceId: items.deviceId,
// 这里的 serviceId 需要在 getBLEDeviceServices 接口中获取
serviceId: items.serviceId,
// 这里的 characteristicId 需要在 getBLEDeviceCharacteristics 接口中获取
characteristicId: items.characteristicId,
success: (res) => {
console.log("启用 notify 功能成功", res);
uni.hideLoading();
// uni.showToast({
// title: items.name + "连接成功",
// icon: "none",
// });
items["status"] = true;
items["text"] = "";
that.totalList.push(items);
uni.onBLECharacteristicValueChange((res) => {
// console.log("监听成功", res);
// ArrayBuffer;
//res.value是ArrayBuffer类型的官方给了一个方法转16进制我们再进行操作
// console.log(that.ab2hex(res.value));
for (let i = 0; i < that.deviceIds.length; i++) {
if (res.deviceId == that.deviceIds[i].deviceId) {
// uni.showToast({
// title: "接收到蓝牙" + that.deviceIds[i].name + "信息",
// icon: "none",
// });
let item = {
name: that.deviceIds[i].name,
txt: "接收到:" + that.hextoString(that.ab2hex(res.value)),
};
that.getData.unshift(item);
}
}
for (let i = 0; i < that.totalList.length; i++) {
if (res.deviceId == that.totalList[i].deviceId) {
that.totalList[i].text = that.hextoString(
that.ab2hex(res.value)
);
}
}
that.totalList = JSON.stringify(that.totalList);
that.totalList = JSON.parse(that.totalList);
});
},
fail: (res) => {
console.log("启用 notify 功能失败", res);
},
});
},
close() {
let that = this;
uni.showModal({
title: "提示",
content: "将断开全部蓝牙连接",
success: function (res) {
if (res.confirm) {
for (let index = 0; index < that.deviceIds.length; index++) {
let item = that.deviceIds[index];
uni.closeBLEConnection({
deviceId: item.deviceId,
success(res) {
console.log("断开蓝牙成功", res);
that.shows = false;
that.totalList = [];
uni.showToast({
title: "断开蓝牙成功",
});
},
fail(res) {
console.log("断开蓝牙失败", res);
},
});
}
}
},
});
},
// 向蓝牙设备发送字符串数据 writeBLECharacteristicValueString
writeString(str, items) {
let that = this;
// console.log("发送字符串数据", str);
// 发送方式一
let buffer = new ArrayBuffer(str.length);
let dataView = new DataView(buffer);
for (let i in str) {
dataView.setUint8(i, str[i].charCodeAt() | 0);
//打印二进制字节
// console.log("dataView.getUint8(i)>>", dataView.getUint8(i));
}
//延迟发送指令
setTimeout(() => {
uni.writeBLECharacteristicValue({
deviceId: items.deviceId,
serviceId: items.serviceId,
characteristicId: items.characteristicId,
value: buffer,
writeType: "write",
success: function (res) {
uni.hideLoading();
// uni.showToast({
// title: "已成功发送",
// });
let item = {
name: items.name,
txt: "已发送:" + str,
};
that.getData.unshift(item);
},
fail: function (res) {
uni.hideLoading();
uni.showToast({
title: "发送失败,可能蓝牙目前不支持写入",
icon: "none",
});
},
});
}, 0);
},
// 直接启用监听功能
nowLinkLis(items) {
let that = this;
console.log("items", items);
uni.showLoading({
title: "连接中,请稍等",
mask: true,
});
//连接蓝牙
uni.createBLEConnection({
deviceId: items.deviceId,
success(res) {
that.stopBluetoothDevicesDiscovery(); // 停止搜索蓝牙
setTimeout(() => {
uni.notifyBLECharacteristicValueChange({
state: true, // 启用 notify 功能
deviceId: items.deviceId,
serviceId: "0000FFE0-0000-1000-8000-00805F9B34FB",
characteristicId: "0000FFE2-0000-1000-8000-00805F9B34FB",
success: (res) => {
console.log("启用监听了", res);
that.shows = true;
uni.hideLoading();
items["status"] = true;
items["text"] = "";
that.totalList.push(items);
uni.onBLECharacteristicValueChange((res) => {
for (let i = 0; i < that.deviceIds.length; i++) {
if (res.deviceId == that.deviceIds[i].deviceId) {
let item = {
name: that.deviceIds[i].name,
txt:
"接收到:" + that.hextoString(that.ab2hex(res.value)),
};
that.getData.unshift(item);
}
}
for (let i = 0; i < that.totalList.length; i++) {
if (res.deviceId == that.totalList[i].deviceId) {
that.totalList[i].text = that.hextoString(
that.ab2hex(res.value)
);
}
}
that.totalList = JSON.stringify(that.totalList);
that.totalList = JSON.parse(that.totalList);
});
},
fail: (res) => {
console.log("启用 notify 功能失败", res);
uni.hideLoading();
uni.showToast({ title: "连接失败", icon: "none" });
},
});
}, 800);
},
fail(res) {
console.log("蓝牙连接失败", res);
uni.showToast({
title: items.name + "连接失败",
icon: "none",
});
},
});
},
},
};
</script>
<style lang="scss" scoped>
.input3 {
display: flex;
justify-content: space-around;
input {
border: 1rpx solid #ccc;
margin: 20rpx;
text-align: center;
height: 60rpx;
border-radius: 10rpx;
font-size: 50rpx;
}
input:first-child,
input:last-child {
width: 200rpx;
}
}
.bakBlue {
background-color: #007aff !important;
}
.appItems {
padding: 30rpx 0 30rpx 4rpx;
display: flex;
flex-wrap: wrap;
.item {
color: #333;
width: 160rpx;
height: 160rpx;
border-radius: 50%;
border: 1rpx solid #ececec;
margin: 10rpx 15rpx;
position: relative;
.txt {
position: absolute;
font-size: 26rpx;
top: 56rpx;
width: 100%;
color: #fff;
z-index: 10;
text-align: center;
}
.name {
position: absolute;
width: 80%;
left: 10%;
bottom: 30rpx;
font-size: 20rpx;
text-align: center;
}
}
}
.timers {
text-align: center;
margin-top: 30rpx;
.time {
margin-bottom: 40rpx;
width: 100%;
font-size: 80rpx;
font-weight: bold;
}
.btns {
display: flex;
justify-content: space-around;
view {
width: 200rpx;
height: 60rpx;
background-color: #007aff;
color: #fff;
line-height: 60rpx;
border-radius: 10rpx;
}
view:active {
background-color: #2990ff;
}
}
}
.items {
width: 100%;
font-size: 32rpx;
overflow-y: scroll;
height: 300rpx;
background-color: #ccc;
margin: 40rpx 0;
.item {
padding: 4rpx 20rpx 0 20rpx;
}
}
.pl {
margin: 20rpx;
background-color: #007aff;
padding: 10rpx;
}
.classText {
width: 94%;
padding: 10rpx;
margin: 3%;
border: 1rpx solid #ececec;
}
.send {
background-color: #ff3e3e;
color: #fff;
}
.dis {
display: flex;
justify-content: space-between;
color: #fff;
text-align: center;
flex-wrap: wrap;
view {
width: 100%;
border-radius: 8rpx;
font-size: 32rpx;
}
}
.barItems {
width: 100%;
.barItem {
display: flex;
justify-content: space-around;
// border: 1rpx solid #ececec;
height: 100rpx;
padding-top: 20rpx;
align-items: center;
.bar {
width: 300rpx;
display: flex;
justify-content: space-around;
view {
border: 1rpx solid #ececec;
width: 50rpx;
height: 50rpx;
text-align: center;
}
input {
width: 100rpx;
text-align: center;
}
}
}
}
</style>
<style>
page {
background-color: #fff;
}
</style>