微信小程序(uniapp)实现连接蓝牙
微信小程序(uniapp)实现连接蓝牙
一、蓝牙连接的基本流程
在uni-app中实现蓝牙连接,主要包含以下步骤:
- 初始化蓝牙模块:开启蓝牙适配器,为后续操作做准备
- 搜索蓝牙设备:扫描周围可用的蓝牙设备
- 连接目标设备:建立与指定蓝牙设备的连接
- 获取服务与特征值:获取设备提供的服务和特征值信息
- 数据交互:通过特征值进行数据的读写操作
二、具体实现步骤
1. 初始化蓝牙模块
使用uni.openBluetoothAdapter()
方法初始化蓝牙适配器。这是所有蓝牙操作的基础,必须在其他API调用前执行。
// 初始化蓝牙适配器
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()
方法开始搜索附近的蓝牙设备。由于搜索操作会消耗系统资源,建议在连接成功后及时停止搜索。
// 搜索蓝牙设备
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. 显示设备列表并连接
将搜索到的设备显示在页面上,用户可以选择要连接的设备。
// 在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. 获取设备服务和特征值
连接成功后,需要获取设备提供的服务和特征值,才能进行数据交互。
// 获取设备服务
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. 数据读写操作
通过特征值进行数据的写入和读取操作。
// 向设备写入数据
function writeDataToDevice(deviceId, serviceId, characteristicId, data) {// 将数据转换为ArrayBufferconst 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)
<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)
<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)
<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
中声明蓝牙权限:{"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蓝牙开发技术,开发出功能完善的蓝牙应用。