UniApp蓝牙开发全攻略|连接通信实战
·
系列文章目录
第一章 UniApp蓝牙开发全攻略|连接通信实战(本文)
第二章 UniApp蓝牙开发进阶实战|解决断连重连、多设备管理与性能优化
第三章 UniApp蓝牙开发高阶实战|广播包解析、固件升级与工业级场景适配
目录
前言:为什么要用UniApp开发蓝牙?
目前智能家居、智能手环、蓝牙测温仪、车载设备等硬件产品,大多采用BLE低功耗蓝牙通信。UniApp一次开发多端适配,无需单独编写安卓、iOS原生代码,大幅降低开发成本。
一、环境要求
- HBuilderX 最新稳定版
- 测试设备:安卓手机/苹果手机(模拟器不支持蓝牙调试,必须真机)
- 蓝牙硬件设备(BLE低功耗蓝牙设备)
二、权限配置
不同平台必须手动配置权限,否则蓝牙无法正常使用:
1.微信小程序权限
manifest.json → 小程序配置 → 勾选蓝牙权限、位置权限
代码如下:
"mp-weixin": {
"permission": {
"scope.userLocation": {
"desc": "用于蓝牙设备搜索"
}
}
}
2.APP安卓权限
manifest.json → APP模块 → 权限配置,添加:蓝牙、定位、模糊定位权限
3.APPIOS权限
manifest.json → APP模块 → 权限配置,添加:蓝牙、定位、模糊定位权限
三、蓝牙开发完整流程图解
- 初始化蓝牙适配器(开启蓝牙检测)
- 校验设备蓝牙是否开启,未开启则跳转开启
- 开始搜索附近蓝牙设备
- 停止搜索(优化性能,避免持续扫描耗电)
- 连接指定蓝牙设备
- 获取设备服务列表、特征值列表
- 开启特征值通知(接收硬件返回数据)
- 发送指令、接收硬件数据
- 主动断开连接、监听被动断连
四、实现
1.代码
代码如下:
<template>
<view class="blue-container">
<!-- 操作按钮区 -->
<view class="btn-box">
<button @click="initBlue" size="mini">初始化蓝牙</button>
<button @click="searchBlue" size="mini" type="primary">搜索设备</button>
<button @click="stopSearch" size="mini">停止搜索</button>
</view>
<!-- 设备列表 -->
<scroll-view scroll-y class="device-list">
<view class="device-item" v-for="(item,index) in deviceList" :key="index" @click="connectDevice(item)">
<text>设备名称:{{item.name || '未知设备'}}</text>
<text>设备地址:{{item.deviceId}}</text>
</view>
</scroll-view>
<!-- 通信操作 -->
<view class="send-box">
<input v-model="sendData" placeholder="输入十六进制指令" />
<button @click="sendMsg" size="mini" type="success">发送指令</button>
</view>
<!-- 接收数据展示 -->
<view class="recv-box">
<text class="title">接收数据:</text>
<text>{{recvData}}</text>
</view>
<button @click="closeBlue" style="margin:20rpx" type="warn">断开蓝牙连接</button>
</view>
</template>
<script>
export default {
data() {
return {
deviceList: [], // 搜索到的设备列表
connectedDeviceId: '', // 当前连接的设备ID
serviceId: '', // 服务UUID
characteristicId: '', // 特征值UUID
sendData: '', // 发送的数据
recvData: '', // 接收的数据
isSearching: false // 是否正在搜索
}
},
onUnload() {
// 页面卸载自动断开蓝牙
this.closeBlue();
},
methods: {
// 1、初始化蓝牙适配器
initBlue() {
uni.openBluetoothAdapter({
success: (res) => {
uni.showToast({title: '蓝牙初始化成功'});
// 监听蓝牙状态变化
this.listenBlueState();
},
fail: (err) => {
uni.showModal({
title: '提示',
content: '蓝牙未开启,请前往设置开启蓝牙',
success: (res) => {
if(res.confirm) {
uni.openSetting();
}
}
})
}
})
},
// 2、监听蓝牙开关状态
listenBlueState() {
uni.onBluetoothAdapterStateChange((res) => {
if(!res.available) {
uni.showToast({title: '蓝牙已关闭',icon:'none'});
this.deviceList = [];
}
})
},
// 3、搜索蓝牙设备
searchBlue() {
if(this.isSearching) return;
this.deviceList = [];
this.isSearching = true;
// 开始搜索
uni.startBluetoothDevicesDiscovery({
allowDuplicatesKey: false, // 不重复上报同一设备
success: () => {
uni.showToast({title: '开始搜索设备'});
// 监听设备发现
uni.onBluetoothDeviceFound((res) => {
let device = res.devices[0];
// 过滤无效设备(可根据需求修改过滤规则)
if(device.name && device.name != '') {
let hasExist = this.deviceList.some(item=>item.deviceId === device.deviceId);
if(!hasExist) {
this.deviceList.push(device);
}
}
})
}
})
// 5秒后自动停止搜索,优化性能
setTimeout(()=>{
this.stopSearch();
},5000)
},
// 4、停止搜索
stopSearch() {
uni.stopBluetoothDevicesDiscovery();
this.isSearching = false;
uni.hideToast();
},
// 5、连接设备
connectDevice(item) {
uni.showLoading({title:'正在连接...'});
// 先停止搜索
this.stopSearch();
// 建立连接
uni.createBLEConnection({
deviceId: item.deviceId,
success: (res) => {
this.connectedDeviceId = item.deviceId;
this.getBLEDeviceServices(); // 获取服务
uni.showToast({title:'连接成功'});
},
fail: () => {
uni.showToast({title:'连接失败',icon:'error'});
},
complete: () => {
uni.hideLoading();
}
})
// 监听设备断开
uni.onBLEConnectionStateChange((res) => {
if(!res.connected) {
uni.showToast({title:'设备已断开',icon:'none'});
this.connectedDeviceId = '';
}
})
},
// 6、获取设备服务
getBLEDeviceServices() {
uni.getBLEDeviceServices({
deviceId: this.connectedDeviceId,
success: (res) => {
// 此处替换为自己硬件的服务UUID
let service = res.services.find(item=>item.uuid.includes('FFF0'));
if(service) {
this.serviceId = service.uuid;
this.getBLEDeviceCharacteristics(); // 获取特征值
}
}
})
},
// 7、获取特征值
getBLEDeviceCharacteristics() {
uni.getBLEDeviceCharacteristics({
deviceId: this.connectedDeviceId,
serviceId: this.serviceId,
success: (res) => {
// 筛选可读写、可通知的特征值,根据硬件修改UUID
let char = res.characteristics.find(item=>item.uuid === 'FFF1');
if(char) {
this.characteristicId = char.uuid;
this.notifyBLE(); // 开启数据通知
}
}
})
},
// 8、开启特征值通知(接收硬件数据)
notifyBLE() {
uni.notifyBLECharacteristicValueChange({
deviceId: this.connectedDeviceId,
serviceId: this.serviceId,
characteristicId: this.characteristicId,
state: true,
success: () => {
uni.showToast({title:'监听数据成功'});
// 监听硬件返回数据
uni.onBLECharacteristicValueChange((res) => {
// ArrayBuffer 转十六进制字符串
let data = this.ab2hex(res.value);
this.recvData = data;
})
}
})
},
// 9、发送蓝牙指令
sendMsg() {
if(!this.sendData) {
uni.showToast({title:'请输入指令',icon:'none'});
return;
}
// 十六进制转ArrayBuffer
let buffer = this.hex2ab(this.sendData);
uni.writeBLECharacteristicValue({
deviceId: this.connectedDeviceId,
serviceId: this.serviceId,
characteristicId: this.characteristicId,
value: buffer,
success: () => {
uni.showToast({title:'发送成功'});
}
})
},
// 10、断开蓝牙连接
closeBlue() {
if(this.connectedDeviceId) {
uni.closeBLEConnection({
deviceId: this.connectedDeviceId
})
}
uni.closeBluetoothAdapter();
this.connectedDeviceId = '';
uni.showToast({title:'已断开连接'});
},
// 工具方法:ArrayBuffer转十六进制
ab2hex(buffer) {
let hexArr = Array.prototype.map.call(
new Uint8Array(buffer),
function(bit) {
return ('00' + bit.toString(16)).slice(-2)
}
)
return hexArr.join('');
},
// 工具方法:十六进制转ArrayBuffer
hex2ab(hex) {
let buffer = new ArrayBuffer(hex.length / 2);
let dataView = new DataView(buffer);
for (let i = 0; i < hex.length; i += 2) {
dataView.setUint8(i / 2, parseInt(hex.substr(i, 2), 16));
}
return buffer;
}
}
}
</script>
<style scoped>
.blue-container {
padding: 20rpx;
}
.btn-box {
display: flex;
gap: 20rpx;
margin-bottom: 30rpx;
}
.device-list {
height: 400rpx;
border: 1rpx solid #eee;
border-radius: 10rpx;
padding: 10rpx;
margin-bottom: 30rpx;
}
.device-item {
padding: 15rpx;
border-bottom: 1rpx solid #f5f5f5;
}
.send-box {
display: flex;
gap: 20rpx;
align-items: center;
margin-bottom: 30rpx;
}
.send-box input {
flex: 1;
border: 1rpx solid #eee;
padding: 15rpx;
border-radius: 8rpx;
}
.recv-box {
padding: 20rpx;
background: #f8f8f8;
border-radius: 10rpx;
}
.title {
font-weight: bold;
}
</style>
2.说明
- UUID修改:代码中 FFF0、FFF1 为通用测试UUID,需替换为硬件厂商提供的服务、特征值UUID
- 数据格式:默认采用十六进制通信,绝大多数蓝牙硬件通用
- 自动停止搜索:设置5秒自动停止扫描,避免手机耗电、卡顿
五、API详解
| API名称 | 作用 | 必填参数 |
|---|---|---|
| openBluetoothAdapter | 初始化蓝牙适配器 | 无 |
| startBluetoothDevicesDiscovery | 搜索蓝牙设备 | allowDuplicatesKey |
| createBLEConnection | 连接指定设备 | deviceId |
| notifyBLECharacteristicValueChange | 连接指定设备 | deviceId |
| createBLEConnection | 开启数据监听 | 设备ID、服务ID、特征值ID |
| writeBLECharacteristicValue | 发送蓝牙数据 | value(二进制数据) |
六、数据解析
1.数据格式转换
蓝牙硬件传输数据均为二进制 ArrayBuffer,前端无法直接识别,必须进行格式转换:
- 接收数据:ArrayBuffer → 十六进制字符串(代码中ab2hex方法)
- 发送数据:十六进制字符串 → ArrayBuffer(代码中hex2ab方法)
2.自定义数据
若硬件返回自定义协议数据(例如:温度、湿度、设备状态),可通过截取十六进制字符串解析,示例:
代码如下:
// 示例:截取第3-4位,转换为十进制温度
let temp = parseInt(recvData.slice(4,8),16) / 10;
console.log('当前温度:',temp+'℃');
七、避坑指南
1.兼容性问题
- iOS适配:iOS13以上必须开启蓝牙权限,单纯开启定位无效
- 安卓适配:安卓12以上需要单独申请蓝牙扫描、连接权限
- 小程序限制:小程序后台需要配置蓝牙设备白名单
2.开发常见问题
- 搜索不到设备:检查是否为BLE蓝牙、权限是否开启、设备是否被其他手机连接
- 连接频繁断开:关闭手机省电模式、不要频繁重复搜索、UUID必须匹配
- 发送数据无响应:检查特征值是否开启写入权限、指令格式是否符合硬件协议
- 数据乱码:统一编码格式,全部使用十六进制通信,禁止直接传输中文
3.常见报错解决方案
| 报错码 | 报错原因 | 解决方案 |
|---|---|---|
| 10000 | 蓝牙未开启 | 弹窗引导用户开启蓝牙 |
| 10001 | 设备连接断开 | 监听断连事件,自动重连 |
| 10002 | 服务不存在 | 核对硬件UUID,区分大小写 |
| 10008 | 数据写入失败 | 检查特征值写入权限 |
总结
本文完整讲解了UniApp蓝牙开发全流程,从权限配置、设备搜索、连接通信到异常处理,代码可直接复用。蓝牙开发核心难点在于硬件UUID匹配和二进制数据转换,只要掌握固定开发流程,适配各类BLE蓝牙硬件均可快速开发。
优化建议:正式项目中可增加重连机制、加载动画、设备过滤、日志打印,提升用户体验。
更多推荐



所有评论(0)