系列文章目录

第一章 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. 初始化蓝牙适配器(开启蓝牙检测)
  2. 校验设备蓝牙是否开启,未开启则跳转开启
  3. 开始搜索附近蓝牙设备
  4. 停止搜索(优化性能,避免持续扫描耗电)
  5. 连接指定蓝牙设备
  6. 获取设备服务列表、特征值列表
  7. 开启特征值通知(接收硬件返回数据)
  8. 发送指令、接收硬件数据
  9. 主动断开连接、监听被动断连

四、实现

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.开发常见问题

  1. 搜索不到设备:检查是否为BLE蓝牙、权限是否开启、设备是否被其他手机连接
  2. 连接频繁断开:关闭手机省电模式、不要频繁重复搜索、UUID必须匹配
  3. 发送数据无响应:检查特征值是否开启写入权限、指令格式是否符合硬件协议
  4. 数据乱码:统一编码格式,全部使用十六进制通信,禁止直接传输中文

3.常见报错解决方案

报错码 报错原因 解决方案
10000 蓝牙未开启 弹窗引导用户开启蓝牙
10001 设备连接断开 监听断连事件,自动重连
10002 服务不存在 核对硬件UUID,区分大小写
10008 数据写入失败 检查特征值写入权限

总结

本文完整讲解了UniApp蓝牙开发全流程,从权限配置、设备搜索、连接通信到异常处理,代码可直接复用。蓝牙开发核心难点在于硬件UUID匹配和二进制数据转换,只要掌握固定开发流程,适配各类BLE蓝牙硬件均可快速开发。
优化建议:正式项目中可增加重连机制、加载动画、设备过滤、日志打印,提升用户体验。

更多推荐