本文将详细介绍如何在uni-app小程序中集成和使用蓝牙功能。我们将从蓝牙的基本概念出发,逐步带领大家了解连接和操作蓝牙设备,同时分享一些实用的代码实例和注意事项,帮助开发者快速上手蓝牙开发。
具体实现思路如下:
1、因为安卓和ios的差异,安卓手机在使用wx小程序蓝牙功能时,需要在页面初始化时先打开GPS定位。
2、初始化蓝牙模块--openBluetoothAdapter。
3、定义设备搜索任务,开始搜寻附近的蓝牙外围设备--startBluetoothDevicesDiscovery,这里定义一个定时器,间隔一秒去搜索,在规定的时间内,未搜索到设备提示超时。清除定时器,调用停止搜索api暂停搜索。
4、搜索到设备时,调用getBluetoothDevices方法将搜索到所有设备存储到变量集合中去,再在页面中将搜索到设备信息以列表的形式展示出来。
5、获取设备服务uuid,连接设备,蓝牙设备不能被多次连接,所以需要在每次连接前关闭BLE连接。
6、获取设备特征值进行读写操作(notify:读,write:写)。
实现代码如下:
html部分
<template>
<view>
<view class="search_button" @click="searchBlueToothDevice">
开始搜索蓝牙设备
</view>
<view class="search_button" @click="stopBlueToothDevice">
停止搜索蓝牙设备
</view>
<view v-if="state?.deviceList && state?.deviceList?.length != 0" v-for="(item, index) in state?.deviceList"
:key="index">
<div class="list-item" @click="connectBlueTooth(item)">
{{ item?.localName }}
{{ item?.deviceId }}
</div>
</view>
<view type="primary" @click="
() =>
sendData(
state?.currentDeviceId,
state?.currentServiceId,
state?.currentCharId
)
" class="search_button">
发送数据
</view>
<view v-for="(item, index) in state?.dataList" :key="index"
v-if="state?.dataList && state?.dataList?.length != 0">
{{ item }}
</view>
</view>
</template>
<style lang="scss" scoped>
.search_button {
width: 50%;
background: greenyellow;
font-size: 32rpx;
text-align: center;
height: 100rpx;
line-height: 100rpx;
margin: 20rpx auto;
}
</style>
js实现部分
<script lang="ts" setup>
import { onMounted, reactive } from 'vue';
import { ab2hex, string2buffer } from "./function";
const state = reactive<any>({
nDelayTimer: -1, //定时器
isFind: false, //是否发现设备
deviceList: [], //设备列表
//特征值
currentDeviceId: undefined,
currentServiceId: undefined,
currentCharId: undefined,
dataList: [],
});
function isNotEmptyObject(obj) {
// 检查是否为对象,并且不是 null
if (typeof obj === 'object' && obj !== null) {
// 使用 Object.keys 判断对象属性个数是否大于 0
return Object.keys(obj).length > 0;
}
return false; // 不是对象或者是 null,返回 false
}
onMounted(() => {
uni.startWifi({
success: (res) => {
uni.getConnectedWifi({
success: (result) => {
console.log(result);
},
fail: (result) => {
console.log(result);
if (result.errCode === 12006) {
uni.showModal({
title: "请打开GPS定位",
content: "Android手机不打开GPS定位,无法搜索到蓝牙设备",
showCancel: false,
})
} else {
uni.showToast({
title: `${result.errCode}`,
icon: "none",
duration: 2000,
})
}
}
})
}
});
});
/**
* @Description: 搜索蓝牙设备
*/
const searchBlueToothDevice = () => {
initBlueTooth();
};
/**
* @Description: 停止搜索蓝牙设备
*/
const stopBlueToothDevice = () => {
uni.hideLoading();
clearInterval(state.nDelayTimer);
};
//初始化蓝牙适配器
const initBlueTooth = () => {
console.log("初始化蓝牙适配器");
uni.openBluetoothAdapter({
success: (res) => {
console.log(res);
findBlueTooth();
},
fail: (res) => {
console.log(res);
uni.showToast({
title: "请先打开蓝牙",
icon: "none",
duration: 2000,
});
},
});
};
//定义搜索设备任务
const findBlueTooth = () => {
uni.startBluetoothDevicesDiscovery({
allowDuplicatesKey: false,
interval: 0,
success: (res) => {
console.log(res);
uni.showLoading({
title: "正在搜索设备"
});
const nTimer = setInterval(() => discoveryBlueTooth(), 1000);
state.nDelayTimer = nTimer;
setTimeout(() => {
uni.hideLoading();
if (state.isFind) {
return;
}
console.log("搜索设备超时");
uni.stopBluetoothDevicesDiscovery({
success: (result) => {
console.log(result);
},
});
clearInterval(state.nDelayTimer);
uni.showModal({
title: "搜索设备超时",
content: "请检查蓝牙设备是否正常工作,Android手机请打开GPS定位",
showCancel: false,
});
}, 15000);
},
fail: (res) => {
console.log(res);
uni.showToast({
title: `蓝牙设备服务发现设备${res.errMsg}`,
icon: "none",
duration: 2000,
})
}
})
}
//搜索设备回调方法
const discoveryBlueTooth = () => {
uni.getBluetoothDevices({
success: (res) => {
console.log("🚀 ~ discoveryBlueTooth ~ res:", res);
if (res?.devices && res?.devices?.length != 0) {
const list = res.devices;
const devices : any[] = [];
list.forEach((item) => {
const name = item.localName || item.name;
if (isNotEmptyObject(name) && name !== "" && name !== "未知设备") {
devices.push(item);
}
});
console.log("🚀 ~ discoveryBlueTooth ~ devices:", devices);
state.deviceList = devices;
state.isFind = true;
}
},
fail: (res) => {
console.log(res);
uni.showToast({
title: "搜索蓝牙设备失败",
icon: "none",
duration: 2000,
});
},
});
}
//连接蓝牙设备
const connectBlueTooth = (device : any) => {
uni.showLoading({ title: "开始连接设备" });
closeBlueTooth(device, () => {
setTimeout(() => {
uni.createBLEConnection({
deviceId: device?.deviceId,
success: (res : any) => {
uni.hideLoading();
uni.showToast({
title: "设备连接成功",
icon: "success",
duration: 2000,
});
console.log(res);
getBlueToothService(device?.deviceId);
},
fail: (res : any) => {
uni.hideLoading();
uni.showToast({
title: `设备连接失败:${res.errMsg}`,
icon: "none",
duration: 2000,
});
console.log(res);
},
});
}, 2000);
});
};
//关闭蓝牙设备(手机端可以同时连接多个蓝牙设备,但是同一个蓝牙设备不能被多次连接,所以需要在每次连接前关闭BLE连接)
const closeBlueTooth = (device : any, callback : any) => {
uni.closeBLEConnection({
deviceId: device?.deviceId,
success: (res : any) => {
console.log(`断开蓝牙设备[${device?.deviceId}]成功`);
console.log(res);
},
fail: (res : any) => {
console.log(res);
},
complete: callback,
});
};
//获取蓝牙设备服务uuid
const getBlueToothService = (deviceId : any) => {
uni.getBLEDeviceServices({
deviceId: deviceId,
success: (res : any) => {
console.log(res);
const services = res.services;
if (services.length <= 0) {
console.log("未找到主服务列表");
return;
}
console.log(`找到服务列表个数:${services.length}`);
const service = services[0];
console.log(`服务UUID:${service.uuid}`);
getBlueToothCharacteristics(deviceId, service.uuid);
},
fail: (res : any) => {
console.log(`获取服务列表失败:${res.errMsg}`);
},
});
}
//获取设备特征值
const getBlueToothCharacteristics = (deviceId : any, uuid : any) => {
uni.getBLEDeviceCharacteristics({
deviceId: deviceId,
serviceId: uuid,
success: (res : any) => {
console.log(res);
// 这里会获取到两个特征值,一个用来写,一个用来读
const chars = res.characteristics;
if (chars.length <= 0) {
console.log("未找到设备特征码");
return;
}
console.log(`找到设备特征码个数:${chars.length}`);
if (chars.length >= 2) {
chars.forEach((item : any) => {
const prop = item.properties;
if (prop.notify === true) {
receiveData(deviceId, uuid, item.uuid);
} else if (prop.write === true) {
sendData(deviceId, uuid, item.uuid);
state.currentDeviceId = deviceId;
state.currentServiceId = uuid;
state.currentCharId = item.uuid;
}
});
}
},
fail: (res) => {
console.log(`获取设备特征码失败:${res.errMsg}`);
}
});
}
//接受蓝牙设备发送过来的数据
const receiveData = (deviceId : any, serviceId : any, charId : any) => {
uni.notifyBLECharacteristicValueChange({
deviceId: deviceId,
serviceId: serviceId,
characteristicId: charId,
state: true,
success: (res : any) => {
console.log(res);
uni.onBLECharacteristicValueChange((data : any) => {
console.log(data);
const value = ab2hex(data.value);
console.log(`收到Notify数据:${value}`);
let list : any[] = state.dataList;
list.push(value);
state.dataList = list;
});
},
fail: (res : any) => {
console.log(`特征值Notice 接收数据失败:${res.errMsg}`);
},
});
};
//向蓝牙设备发送数据
const sendData = (deviceId : any, serviceId : any, charId : any) => {
const str = "111";
const buffer = string2buffer(str);
console.log(buffer);
setTimeout(() => {
uni.writeBLECharacteristicValue({
value: buffer,
deviceId: deviceId,
serviceId: serviceId,
characteristicId: charId,
success: (res : any) => {
console.log(res);
console.log(`发送成功`);
},
fail: (res : any) => {
console.log(`发送失败:${res.errMsg}`);
},
});
}, 1000);
}
</script>
字符串转换方法
//将ArrayBuffer转换成字符串
export const ab2hex = (buffer: any) => {
var hexArr = Array.prototype.map.call(
new Uint8Array(buffer),
function (bit: any) {
return ('00' + bit.toString(16)).slice(-2)
}
);
return hexArr.join('');
}
// 将字符串转换成ArrayBufer
export const string2buffer = (str: string) : ArrayBuffer => {
if (!str) return new ArrayBuffer(0);
let length = str.length;
let index = 0;
let array: any = []
while (index < length) {
array.push(str.substring(index, index + 2));
index = index + 2;
}
const val = array.join(",");
console.log(val);
return new Uint8Array(array.map(function (h) {
return parseInt(h, 16)
})).buffer;
}
希望能对您起到帮助,国庆快乐!!!