微信小程序(uniapp)实现连接蓝牙
一、蓝牙连接的基本流程
在uni-app中实现蓝牙连接,主要包含以下步骤:
- 初始化蓝牙模块:开启蓝牙适配器,为后续操作做准备
- 搜索蓝牙设备:扫描周围可用的蓝牙设备
- 连接目标设备:建立与指定蓝牙设备的连接
- 获取服务与特征值:获取设备提供的服务和特征值信息
- 数据交互:通过特征值进行数据的读写操作
二、具体实现步骤
1. 初始化蓝牙模块
使用uni.openBluetoothAdapter()
方法初始化蓝牙适配器。这是所有蓝牙操作的基础,必须在其他API调用前执行。
javascript
// 初始化蓝牙适配器
function initBluetooth() {
uni.openBluetoothAdapter({
success: (res) => {
console.log('蓝牙适配器初始化成功', res);
// 初始化成功后可以开始搜索设备
searchBluetoothDevices();
},
fail: (err) => {
console.error('蓝牙适配器初始化失败', err);
if (err.errCode === 10001) {
uni.showModal({
title: '提示',
content: '请开启手机蓝牙',
showCancel: false
});
}
}
});
}
2. 搜索蓝牙设备
使用uni.startBluetoothDevicesDiscovery()
方法开始搜索附近的蓝牙设备。由于搜索操作会消耗系统资源,建议在连接成功后及时停止搜索。
javascript
// 搜索蓝牙设备
function searchBluetoothDevices() {
uni.startBluetoothDevicesDiscovery({
allowDuplicatesKey: false, // 不允许重复上报同一设备
success: (res) => {
console.log('开始搜索蓝牙设备', res);
uni.showLoading({ title: '正在搜索设备...' });
// 设置定时器,15秒后超时停止搜索
const timer = setTimeout(() => {
stopBluetoothSearch();
uni.showModal({
title: '提示',
content: '搜索设备超时,请检查设备是否开启',
showCancel: false
});
}, 15000);
// 监听找到新设备的事件
uni.onBluetoothDeviceFound((devices) => {
handleFoundDevices(devices);
clearTimeout(timer); // 找到设备后清除超时定时器
});
},
fail: (err) => {
console.error('开始搜索设备失败', err);
uni.hideLoading();
}
});
}
// 处理找到的设备
function handleFoundDevices(devices) {
const foundDevices = [];
devices.devices.forEach(device => {
if (device.name && device.name.includes('目标设备关键字')) {
foundDevices.push(device);
}
});
if (foundDevices.length > 0) {
stopBluetoothSearch();
showDeviceList(foundDevices);
}
}
// 停止搜索蓝牙设备
function stopBluetoothSearch() {
uni.stopBluetoothDevicesDiscovery({
success: () => {
uni.hideLoading();
uni.offBluetoothDeviceFound(); // 停止监听新设备
}
});
}
3. 显示设备列表并连接
将搜索到的设备显示在页面上,用户可以选择要连接的设备。
javascript
// 在Vue组件的data中定义
data() {
return {
deviceList: [], // 设备列表
currentDevice: null // 当前选中的设备
}
}
// 显示设备列表
function showDeviceList(devices) {
this.deviceList = devices;
// 可以在这里更新页面显示,或者使用uni-app的列表渲染
}
// 连接选中的设备
function connectDevice(deviceId) {
uni.createBLEConnection({
deviceId: deviceId,
success: (res) => {
console.log('连接设备成功', res);
this.currentDevice = deviceId;
// 连接成功后获取设备服务
getDeviceServices(deviceId);
},
fail: (err) => {
console.error('连接设备失败', err);
uni.showToast({
title: '连接失败',
icon: 'none'
});
}
});
}
4. 获取设备服务和特征值
连接成功后,需要获取设备提供的服务和特征值,才能进行数据交互。
javascript
// 获取设备服务
function getDeviceServices(deviceId) {
uni.getBLEDeviceServices({
deviceId: deviceId,
success: (res) => {
console.log('获取服务成功', res.services);
// 通常我们只需要特定的服务,可以根据UUID过滤
const targetService = res.services.find(
service => service.uuid === '目标服务UUID'
);
if (targetService) {
getDeviceCharacteristics(deviceId, targetService.uuid);
}
},
fail: (err) => {
console.error('获取服务失败', err);
}
});
}
// 获取服务特征值
function getDeviceCharacteristics(deviceId, serviceId) {
uni.getBLEDeviceCharacteristics({
deviceId: deviceId,
serviceId: serviceId,
success: (res) => {
console.log('获取特征值成功', res.characteristics);
// 存储特征值信息,用于后续读写操作
this.characteristics = res.characteristics;
// 启用特征值变化的通知
enableCharacteristicNotification(deviceId, serviceId, res.characteristics);
},
fail: (err) => {
console.error('获取特征值失败', err);
}
});
}
// 启用特征值变化的通知
function enableCharacteristicNotification(deviceId, serviceId, characteristics) {
const notifyChar = characteristics.find(
char => char.properties.notify
);
if (notifyChar) {
uni.notifyBLECharacteristicValueChange({
deviceId: deviceId,
serviceId: serviceId,
characteristicId: notifyChar.uuid,
state: true,
success: () => {
console.log('启用通知成功');
// 监听特征值变化
uni.onBLECharacteristicValueChange(res => {
console.log('特征值变化', res);
// 处理接收到的数据
const data = ab2hex(res.value);
console.log('收到数据:', data);
});
},
fail: (err) => {
console.error('启用通知失败', err);
}
});
}
}
5. 数据读写操作
通过特征值进行数据的写入和读取操作。
javascript
// 向设备写入数据
function writeDataToDevice(deviceId, serviceId, characteristicId, data) {
// 将数据转换为ArrayBuffer
const buffer = string2buffer(data);
uni.writeBLECharacteristicValue({
deviceId: deviceId,
serviceId: serviceId,
characteristicId: characteristicId,
value: buffer,
success: () => {
console.log('写入数据成功');
},
fail: (err) => {
console.error('写入数据失败', err);
}
});
}
// 数据转换工具函数
function string2buffer(str) {
let buf = new ArrayBuffer(str.length);
let bufView = new Uint8Array(buf);
for (let i = 0; i < str.length; i++) {
bufView[i] = str.charCodeAt(i);
}
return buf;
}
function ab2hex(buffer) {
const hexArr = Array.prototype.map.call(
new Uint8Array(buffer),
function(bit) {
return ('00' + bit.toString(16)).slice(-2);
}
);
return hexArr.join('');
}
三、完整实现示例
1. 页面结构 (index.vue)
html
<template>
<view class="container">
<button @click="initBluetooth">初始化蓝牙</button>
<button @click="searchBluetoothDevices">搜索设备</button>
<view v-if="deviceList.length > 0">
<view v-for="(device, index) in deviceList" :key="index" @click="connectDevice(device.deviceId)">
{{ device.name || device.localName || '未知设备' }} - {{ device.deviceId }}
</view>
</view>
<button @click="sendData" :disabled="!currentDevice">发送测试数据</button>
</view>
</template>
2. 脚本部分 (index.vue)
javascript
<script>
export default {
data() {
return {
deviceList: [],
currentDevice: null,
currentServiceId: '',
currentCharacteristicId: ''
}
},
methods: {
// 初始化蓝牙适配器
initBluetooth() {
uni.openBluetoothAdapter({
success: (res) => {
console.log('蓝牙适配器初始化成功', res);
uni.showToast({ title: '蓝牙初始化成功', icon: 'success' });
},
fail: (err) => {
console.error('蓝牙适配器初始化失败', err);
uni.showToast({ title: '蓝牙初始化失败', icon: 'none' });
}
});
},
// 搜索蓝牙设备
searchBluetoothDevices() {
uni.startBluetoothDevicesDiscovery({
allowDuplicatesKey: false,
success: (res) => {
console.log('开始搜索蓝牙设备', res);
uni.showLoading({ title: '正在搜索设备...' });
// 监听找到新设备的事件
this.deviceFoundListener = uni.onBluetoothDeviceFound((devices) => {
this.handleFoundDevices(devices);
});
// 15秒后超时停止搜索
setTimeout(() => {
this.stopBluetoothSearch();
if (this.deviceList.length === 0) {
uni.showModal({
title: '提示',
content: '未找到蓝牙设备',
showCancel: false
});
}
}, 15000);
},
fail: (err) => {
console.error('开始搜索设备失败', err);
uni.hideLoading();
}
});
},
// 处理找到的设备
handleFoundDevices(devices) {
const foundDevices = [];
devices.devices.forEach(device => {
if (device.name && device.name.includes('目标设备关键字')) {
foundDevices.push(device);
}
});
if (foundDevices.length > 0) {
// 去重处理
const uniqueDevices = [...new Map(
foundDevices.map(item => [item.deviceId, item])
).values()];
this.deviceList = [...this.deviceList, ...uniqueDevices].filter(
(item, index, self) =>
index === self.findIndex(t => t.deviceId === item.deviceId)
);
}
},
// 停止搜索蓝牙设备
stopBluetoothSearch() {
uni.stopBluetoothDevicesDiscovery({
success: () => {
uni.hideLoading();
if (this.deviceFoundListener) {
uni.offBluetoothDeviceFound(this.deviceFoundListener);
}
}
});
},
// 连接设备
connectDevice(deviceId) {
uni.createBLEConnection({
deviceId: deviceId,
success: (res) => {
console.log('连接设备成功', res);
this.currentDevice = deviceId;
uni.showToast({ title: '连接成功', icon: 'success' });
// 连接成功后获取设备服务
this.getDeviceServices(deviceId);
},
fail: (err) => {
console.error('连接设备失败', err);
uni.showToast({ title: '连接失败', icon: 'none' });
}
});
},
// 获取设备服务
getDeviceServices(deviceId) {
uni.getBLEDeviceServices({
deviceId: deviceId,
success: (res) => {
console.log('获取服务成功', res.services);
const targetService = res.services.find(
service => service.uuid === '目标服务UUID'
);
if (targetService) {
this.currentServiceId = targetService.uuid;
this.getDeviceCharacteristics(deviceId, targetService.uuid);
}
},
fail: (err) => {
console.error('获取服务失败', err);
}
});
},
// 获取服务特征值
getDeviceCharacteristics(deviceId, serviceId) {
uni.getBLEDeviceCharacteristics({
deviceId: deviceId,
serviceId: serviceId,
success: (res) => {
console.log('获取特征值成功', res.characteristics);
const notifyChar = res.characteristics.find(
char => char.properties.notify
);
const writeChar = res.characteristics.find(
char => char.properties.write
);
if (notifyChar) {
this.enableCharacteristicNotification(deviceId, serviceId, notifyChar.uuid);
}
if (writeChar) {
this.currentCharacteristicId = writeChar.uuid;
}
},
fail: (err) => {
console.error('获取特征值失败', err);
}
});
},
// 启用特征值变化的通知
enableCharacteristicNotification(deviceId, serviceId, characteristicId) {
uni.notifyBLECharacteristicValueChange({
deviceId: deviceId,
serviceId: serviceId,
characteristicId: characteristicId,
state: true,
success: () => {
console.log('启用通知成功');
this.characteristicChangeListener = uni.onBLECharacteristicValueChange(res => {
console.log('特征值变化', res);
const data = this.ab2hex(res.value);
console.log('收到数据:', data);
});
},
fail: (err) => {
console.error('启用通知失败', err);
}
});
},
// 发送测试数据
sendData() {
if (!this.currentDevice || !this.currentServiceId || !this.currentCharacteristicId) {
uni.showToast({ title: '请先连接设备', icon: 'none' });
return;
}
const testData = 'Hello Bluetooth!';
const buffer = this.string2buffer(testData);
uni.writeBLECharacteristicValue({
deviceId: this.currentDevice,
serviceId: this.currentServiceId,
characteristicId: this.currentCharacteristicId,
value: buffer,
success: () => {
console.log('写入数据成功');
uni.showToast({ title: '发送成功', icon: 'success' });
},
fail: (err) => {
console.error('写入数据失败', err);
uni.showToast({ title: '发送失败', icon: 'none' });
}
});
},
// 数据转换工具函数
string2buffer(str) {
let buf = new ArrayBuffer(str.length);
let bufView = new Uint8Array(buf);
for (let i = 0; i < str.length; i++) {
bufView[i] = str.charCodeAt(i);
}
return buf;
},
ab2hex(buffer) {
const hexArr = Array.prototype.map.call(
new Uint8Array(buffer),
function(bit) {
return ('00' + bit.toString(16)).slice(-2);
}
);
return hexArr.join('');
}
},
beforeDestroy() {
// 组件销毁前清理蓝牙资源
if (this.currentDevice) {
uni.closeBLEConnection({
deviceId: this.currentDevice
});
}
if (this.deviceFoundListener) {
uni.offBluetoothDeviceFound(this.deviceFoundListener);
}
if (this.characteristicChangeListener) {
uni.offBLECharacteristicValueChange(this.characteristicChangeListener);
}
}
}
</script>
3. 样式部分 (index.vue)
css
<style>
.container {
padding: 20px;
}
button {
margin: 10px 0;
background-color: #07C160;
color: white;
border: none;
padding: 10px 15px;
border-radius: 5px;
}
view {
margin: 10px 0;
padding: 10px;
border: 1px solid #eee;
border-radius: 5px;
}
</style>
四、注意事项
-
权限配置 :在微信小程序中,需要在
app.json
中声明蓝牙权限:json{ "permission": { "scope.bluetooth": { "desc": "需要蓝牙权限以连接设备" } } }
-
iOS与Android差异:
- iOS设备不支持获取MAC地址,如需MAC地址,需要设备厂商将MAC地址放在广播数据中[9]
- Android设备需要定位权限才能搜索蓝牙设备[9]
-
连接稳定性:
- 蓝牙连接可能随时断开,建议监听
onBLEConnectionStateChange
事件进行重连处理[10] - 避免多次调用
createBLEConnection
创建重复连接[10]
- 蓝牙连接可能随时断开,建议监听
-
数据传输限制:
- 单次写入数据建议不超过20字节[4]
- 蓝牙4.0设备对单次传输数据大小有限制,超过可能导致写入错误[4]
-
资源释放:
- 页面销毁或不再需要蓝牙功能时,应调用
closeBLEConnection
和closeBluetoothAdapter
释放资源[10]
- 页面销毁或不再需要蓝牙功能时,应调用
五、总结
通过本文的介绍,我们了解了在uni-app框架下实现微信小程序蓝牙连接的全过程。从初始化蓝牙模块、搜索设备、连接设备到数据交互,每个步骤都有详细的代码示例和说明。
主要实现步骤包括:
- 使用
uni.openBluetoothAdapter()
初始化蓝牙适配器 - 使用
uni.startBluetoothDevicesDiscovery()
搜索设备 - 使用
uni.createBLEConnection()
连接设备 - 获取设备服务和特征值
- 通过特征值进行数据读写操作
在实际开发中,还需要注意权限配置、平台差异、连接稳定性和资源释放等问题。希望本文的内容能帮助开发者快速掌握uni-app蓝牙开发技术,开发出功能完善的蓝牙应用。