HarmonyOS APP开发蓝牙基础:BLE扫描与连接
蓝牙基础:BLE扫描与连接
说到蓝牙,很多人第一反应是"配对耳机"。但在物联网时代,蓝牙的角色远不止于此——智能手环、体温计、门锁、甚至共享单车,都在用一种叫BLE(低功耗蓝牙)的技术。今天咱们就来聊聊鸿蒙系统中的BLE开发,看看如何扫描和连接这些"隐形"的智能设备。
一、背景与动机
1.1 BLE与传统蓝牙的区别
先来搞清楚一个概念:BLE(Bluetooth Low Energy)和传统蓝牙(Classic Bluetooth)是两种不同的技术:
| 对比项 | 传统蓝牙 | BLE蓝牙 |
|---|---|---|
| 功耗 | 较高(通话级) | 极低(纽扣电池可用数年) |
| 传输速率 | 1-3 Mbps | 1 Mbps(实际更低) |
| 连接时间 | 数秒 | 毫秒级 |
| 应用场景 | 音频传输、文件传输 | 传感器数据、控制指令 |
| 拓扑结构 | 点对点 | 星型拓扑(一对多) |
BLE的典型应用:
- 健康设备:心率带、血氧仪、体温计
- 智能家居:智能锁、灯控、温湿度传感器
- 可穿戴设备:手环、手表、运动传感器
- 位置服务:iBeacon、室内定位
1.2 BLE通信模型
BLE的核心概念是GATT(Generic Attribute Profile),它定义了数据的组织方式:

角色说明:
- 外围设备(Peripheral):提供数据的设备(如传感器),作为GATT服务器
- 中心设备(Central):消费数据的设备(如手机),作为GATT客户端
二、核心原理
2.1 BLE扫描流程
扫描BLE设备是连接的第一步,流程如下:

2.2 扫描参数详解
BLE扫描的效率很大程度上取决于扫描参数的配置:
interface ScanParams {
// 扫描间隔(毫秒)
// 决定扫描的频率,越小扫描越频繁,功耗越高
interval: number;
// 扫描窗口(毫秒)
// 每次扫描的持续时间,必须小于等于interval
window: number;
// 扫描模式
// LOW_POWER: 低功耗模式,适合后台
// BALANCED: 平衡模式
// AGGRESSIVE: 激进模式,适合前台快速发现
scanMode: ScanMode;
// 过滤器
// 用于只扫描特定设备,减少无效扫描
filters?: ScanFilter[];
}
扫描模式对比:
| 模式 | 间隔 | 窗口 | 功耗 | 发现速度 |
|---|---|---|---|---|
| LOW_POWER | 5000ms | 500ms | 低 | 慢 |
| BALANCED | 1000ms | 200ms | 中 | 中 |
| AGGRESSIVE | 100ms | 100ms | 高 | 快 |
2.3 BLE连接流程
找到目标设备后,就可以发起连接:

三、代码实战
3.1 BLE管理核心类封装
import bluetooth from '@ohos.bluetooth';
import { BusinessError } from '@ohos.base';
/**
* BLE设备信息
*/
export interface BleDeviceInfo {
// 设备ID
deviceId: string;
// 设备名称
deviceName: string;
// 信号强度(RSSI)
rssi: number;
// 广播数据
advertisData: ArrayBuffer;
// 服务UUID列表
serviceUuids: Array<string>;
// 是否可连接
isConnectable: boolean;
}
/**
* BLE管理器
* 封装BLE扫描、连接、通信等核心功能
*/
export class BleManager {
private static instance: BleManager;
private gattClient: bluetooth.GattClient | null = null;
private connectedDeviceId: string = '';
private discoveredDevices: Map<string, BleDeviceInfo> = new Map();
// 回调函数
private onDeviceFound: ((device: BleDeviceInfo) => void) | null = null;
private onConnectionStateChanged: ((state: bluetooth.ProfileConnectionState) => void) | null = null;
private onCharacteristicChanged: ((data: ArrayBuffer) => void) | null = null;
private constructor() {
this.initEventListeners();
}
/**
* 获取单例实例
*/
public static getInstance(): BleManager {
if (!BleManager.instance) {
BleManager.instance = new BleManager();
}
return BleManager.instance;
}
/**
* 初始化事件监听
*/
private initEventListeners(): void {
// 监听BLE扫描结果
bluetooth.on('BLEDeviceFind', (result: Array<bluetooth.ScanResult>) => {
result.forEach((scanResult) => {
const deviceInfo: BleDeviceInfo = {
deviceId: scanResult.deviceId,
deviceName: scanResult.deviceName || 'Unknown',
rssi: scanResult.rssi,
advertisData: scanResult.advertisData,
serviceUuids: this.parseServiceUuids(scanResult.advertisData),
isConnectable: true
};
this.discoveredDevices.set(deviceInfo.deviceId, deviceInfo);
if (this.onDeviceFound) {
this.onDeviceFound(deviceInfo);
}
});
});
// 监听蓝牙状态变化
bluetooth.on('stateChange', (state: bluetooth.BluetoothState) => {
const stateText = this.getStateText(state);
console.info(`[BLE] 蓝牙状态变化: ${stateText}`);
});
}
/**
* 获取状态文本
*/
private getStateText(state: bluetooth.BluetoothState): string {
switch (state) {
case bluetooth.BluetoothState.STATE_OFF:
return '已关闭';
case bluetooth.BluetoothState.STATE_ON:
return '已开启';
case bluetooth.BluetoothState.STATE_TURNING_OFF:
return '正在关闭';
case bluetooth.BluetoothState.STATE_TURNING_ON:
return '正在开启';
default:
return '未知';
}
}
/**
* 解析服务UUID
*/
private parseServiceUuids(advertisData: ArrayBuffer): Array<string> {
// 简化实现,实际需要解析广播数据
return [];
}
/**
* 检查蓝牙是否开启
*/
public async isBluetoothEnabled(): Promise<boolean> {
try {
const state = bluetooth.getState();
return state === bluetooth.BluetoothState.STATE_ON;
} catch (error) {
console.error('[BLE] 检查蓝牙状态失败:', error);
return false;
}
}
/**
* 开启蓝牙
*/
public async enableBluetooth(): Promise<boolean> {
try {
await bluetooth.enableBluetooth();
console.info('[BLE] 蓝牙已开启');
return true;
} catch (error) {
const err = error as BusinessError;
console.error(`[BLE] 开启蓝牙失败: ${err.message}`);
return false;
}
}
/**
* 关闭蓝牙
*/
public async disableBluetooth(): Promise<boolean> {
try {
await bluetooth.disableBluetooth();
console.info('[BLE] 蓝牙已关闭');
return true;
} catch (error) {
const err = error as BusinessError;
console.error(`[BLE] 关闭蓝牙失败: ${err.message}`);
return false;
}
}
/**
* 开始BLE扫描
* @param filters 扫描过滤器
* @param scanMode 扫描模式
*/
public async startScan(
filters?: Array<bluetooth.ScanFilter>,
scanMode: bluetooth.ScanMode = bluetooth.ScanMode.SCAN_MODE_LOW_POWER
): Promise<boolean> {
try {
// 检查蓝牙状态
const isEnabled = await this.isBluetoothEnabled();
if (!isEnabled) {
console.error('[BLE] 蓝牙未开启');
return false;
}
// 清空已发现的设备
this.discoveredDevices.clear();
// 配置扫描参数
const scanParams: bluetooth.ScanParams = {
scanMode: scanMode,
interval: 5000,
window: 500
};
// 开始扫描
await bluetooth.startBleScan(filters, scanParams);
console.info('[BLE] 开始扫描');
return true;
} catch (error) {
const err = error as BusinessError;
console.error(`[BLE] 启动扫描失败: ${err.message}`);
return false;
}
}
/**
* 停止BLE扫描
*/
public async stopScan(): Promise<boolean> {
try {
await bluetooth.stopBleScan();
console.info('[BLE] 停止扫描');
return true;
} catch (error) {
const err = error as BusinessError;
console.error(`[BLE] 停止扫描失败: ${err.message}`);
return false;
}
}
/**
* 获取已发现的设备列表
*/
public getDiscoveredDevices(): Array<BleDeviceInfo> {
return Array.from(this.discoveredDevices.values());
}
/**
* 连接到BLE设备
* @param deviceId 设备ID
*/
public async connect(deviceId: string): Promise<boolean> {
try {
// 创建GATT客户端
this.gattClient = bluetooth.createGattClient(deviceId);
// 设置连接状态回调
this.gattClient.on('connectionStateChange', (state: bluetooth.ProfileConnectionState) => {
console.info(`[BLE] 连接状态: ${state}`);
if (state === bluetooth.ProfileConnectionState.STATE_CONNECTED) {
this.connectedDeviceId = deviceId;
} else if (state === bluetooth.ProfileConnectionState.STATE_DISCONNECTED) {
this.connectedDeviceId = '';
}
if (this.onConnectionStateChanged) {
this.onConnectionStateChanged(state);
}
});
// 发起连接
await this.gattClient.connect();
console.info(`[BLE] 正在连接到 ${deviceId}`);
return true;
} catch (error) {
const err = error as BusinessError;
console.error(`[BLE] 连接失败: ${err.message}`);
return false;
}
}
/**
* 断开BLE连接
*/
public async disconnect(): Promise<boolean> {
try {
if (this.gattClient) {
await this.gattClient.disconnect();
this.gattClient.close();
this.gattClient = null;
this.connectedDeviceId = '';
console.info('[BLE] 已断开连接');
}
return true;
} catch (error) {
const err = error as BusinessError;
console.error(`[BLE] 断开连接失败: ${err.message}`);
return false;
}
}
/**
* 发现GATT服务
*/
public async discoverServices(): Promise<Array<bluetooth.GattService> | null> {
if (!this.gattClient) {
console.error('[BLE] 未连接设备');
return null;
}
try {
const services = await this.gattClient.discoverServices();
console.info(`[BLE] 发现 ${services.length} 个服务`);
return services;
} catch (error) {
console.error('[BLE] 发现服务失败:', error);
return null;
}
}
/**
* 读取特征值
*/
public async readCharacteristic(
serviceUuid: string,
characteristicUuid: string
): Promise<ArrayBuffer | null> {
if (!this.gattClient) {
console.error('[BLE] 未连接设备');
return null;
}
try {
const value = await this.gattClient.readCharacteristicValue({
serviceUuid: serviceUuid,
characteristicUuid: characteristicUuid
});
console.info(`[BLE] 读取特征值成功`);
return value;
} catch (error) {
console.error('[BLE] 读取特征值失败:', error);
return null;
}
}
/**
* 写入特征值
*/
public async writeCharacteristic(
serviceUuid: string,
characteristicUuid: string,
value: ArrayBuffer,
writeType: bluetooth.GattWriteType = bluetooth.GattWriteType.WRITE_TYPE_DEFAULT
): Promise<boolean> {
if (!this.gattClient) {
console.error('[BLE] 未连接设备');
return false;
}
try {
await this.gattClient.writeCharacteristicValue({
serviceUuid: serviceUuid,
characteristicUuid: characteristicUuid,
characteristicValue: value,
writeType: writeType
});
console.info('[BLE] 写入特征值成功');
return true;
} catch (error) {
console.error('[BLE] 写入特征值失败:', error);
return false;
}
}
/**
* 启用特征值通知
*/
public async enableNotification(
serviceUuid: string,
characteristicUuid: string,
enable: boolean = true
): Promise<boolean> {
if (!this.gattClient) {
console.error('[BLE] 未连接设备');
return false;
}
try {
await this.gattClient.setCharacteristicChangeNotification({
serviceUuid: serviceUuid,
characteristicUuid: characteristicUuid,
enable: enable
});
// 设置通知回调
if (enable) {
this.gattClient.on('characteristicValueChange', (data: ArrayBuffer) => {
if (this.onCharacteristicChanged) {
this.onCharacteristicChanged(data);
}
});
}
console.info(`[BLE] 通知已${enable ? '启用' : '禁用'}`);
return true;
} catch (error) {
console.error('[BLE] 设置通知失败:', error);
return false;
}
}
/**
* 设置回调函数
*/
public setOnDeviceFound(callback: (device: BleDeviceInfo) => void): void {
this.onDeviceFound = callback;
}
public setOnConnectionStateChanged(callback: (state: bluetooth.ProfileConnectionState) => void): void {
this.onConnectionStateChanged = callback;
}
public setOnCharacteristicChanged(callback: (data: ArrayBuffer) => void): void {
this.onCharacteristicChanged = callback;
}
/**
* 移除所有监听
*/
public removeAllListeners(): void {
bluetooth.off('BLEDeviceFind');
bluetooth.off('stateChange');
if (this.gattClient) {
this.gattClient.off('connectionStateChange');
this.gattClient.off('characteristicValueChange');
}
console.info('[BLE] 已移除所有监听');
}
/**
* 获取当前连接的设备ID
*/
public getConnectedDeviceId(): string {
return this.connectedDeviceId;
}
/**
* 判断是否已连接
*/
public isConnected(): boolean {
return this.connectedDeviceId !== '';
}
}
3.2 BLE设备扫描与连接UI
import { BleManager, BleDeviceInfo } from './BleManager';
import bluetooth from '@ohos.bluetooth';
@Entry
@Component
struct BleScanPage {
private bleManager: BleManager = BleManager.getInstance();
@State isBluetoothEnabled: boolean = false;
@State isScanning: boolean = false;
@State discoveredDevices: Array<BleDeviceInfo> = [];
@State isConnected: boolean = false;
@State connectedDeviceName: string = '';
@State services: Array<bluetooth.GattService> = [];
aboutToAppear(): void {
this.checkBluetoothState();
this.registerCallbacks();
}
aboutToDisappear(): void {
this.bleManager.removeAllListeners();
}
/**
* 检查蓝牙状态
*/
async checkBluetoothState(): Promise<void> {
this.isBluetoothEnabled = await this.bleManager.isBluetoothEnabled();
}
/**
* 注册回调
*/
registerCallbacks(): void {
// 设备发现回调
this.bleManager.setOnDeviceFound((device: BleDeviceInfo) => {
// 更新设备列表
const index = this.discoveredDevices.findIndex(d => d.deviceId === device.deviceId);
if (index >= 0) {
this.discoveredDevices[index] = device;
} else {
this.discoveredDevices.push(device);
}
});
// 连接状态回调
this.bleManager.setOnConnectionStateChanged((state: bluetooth.ProfileConnectionState) => {
if (state === bluetooth.ProfileConnectionState.STATE_CONNECTED) {
this.isConnected = true;
this.discoverServices();
} else if (state === bluetooth.ProfileConnectionState.STATE_DISCONNECTED) {
this.isConnected = false;
this.connectedDeviceName = '';
this.services = [];
}
});
}
/**
* 发现服务
*/
async discoverServices(): Promise<void> {
const services = await this.bleManager.discoverServices();
if (services) {
this.services = services;
}
}
/**
* 开启蓝牙
*/
async enableBluetooth(): Promise<void> {
const success = await this.bleManager.enableBluetooth();
if (success) {
this.isBluetoothEnabled = true;
}
}
/**
* 开始扫描
*/
async startScan(): Promise<void> {
this.isScanning = true;
this.discoveredDevices = [];
const success = await this.bleManager.startScan();
if (!success) {
this.isScanning = false;
}
// 设置扫描超时
setTimeout(() => {
if (this.isScanning) {
this.stopScan();
}
}, 30000);
}
/**
* 停止扫描
*/
async stopScan(): Promise<void> {
await this.bleManager.stopScan();
this.isScanning = false;
}
/**
* 连接设备
*/
async connectDevice(device: BleDeviceInfo): Promise<void> {
await this.stopScan();
const success = await this.bleManager.connect(device.deviceId);
if (success) {
this.connectedDeviceName = device.deviceName;
}
}
/**
* 断开连接
*/
async disconnect(): Promise<void> {
await this.bleManager.disconnect();
}
/**
* 获取信号强度图标
*/
getRssiIcon(rssi: number): string {
if (rssi >= -50) return '📶';
if (rssi >= -70) return '📶';
return '📶';
}
build() {
Column() {
// 标题栏
Row() {
Text('BLE设备')
.fontSize(24)
.fontWeight(FontWeight.Bold)
.fontColor('#FFFFFF')
Blank()
Toggle({ type: ToggleType.Switch, isOn: this.isBluetoothEnabled })
.onChange((isOn: boolean) => {
if (isOn) {
this.enableBluetooth();
} else {
this.bleManager.disableBluetooth();
this.isBluetoothEnabled = false;
}
})
}
.width('100%')
.padding(20)
.backgroundColor('#1A1A2E')
// 连接状态
if (this.isConnected) {
Column() {
Row() {
Text('已连接')
.fontSize(16)
.fontColor('#4CAF50')
Text(this.connectedDeviceName)
.fontSize(14)
.fontColor('#FFFFFF')
.margin({ left: 10 })
}
.width('100%')
if (this.services.length > 0) {
Text(`发现 ${this.services.length} 个服务`)
.fontSize(12)
.fontColor('#AAAAAA')
.margin({ top: 10 })
}
Button('断开连接')
.width('100%')
.height(40)
.backgroundColor('#FF5722')
.margin({ top: 15 })
.onClick(() => {
this.disconnect();
})
}
.width('90%')
.padding(20)
.backgroundColor('#16213E')
.borderRadius(12)
.margin({ top: 20 })
}
// 扫描按钮
if (this.isBluetoothEnabled && !this.isConnected) {
Button(this.isScanning ? '扫描中...' : '扫描设备')
.width('90%')
.height(50)
.backgroundColor(this.isScanning ? '#666666' : '#4A90E2')
.enabled(!this.isScanning)
.onClick(() => {
this.startScan();
})
.margin({ top: 20 })
if (this.isScanning) {
Progress({ value: 0, total: 100, type: ProgressType.Linear })
.width('90%')
.color('#4A90E2')
.margin({ top: 10 })
}
}
// 设备列表
if (this.discoveredDevices.length > 0) {
Column() {
Text(`发现 ${this.discoveredDevices.length} 台设备`)
.fontSize(16)
.fontColor('#FFFFFF')
.width('100%')
.margin({ bottom: 15 })
List() {
ForEach(this.discoveredDevices, (device: BleDeviceInfo) => {
ListItem() {
Row() {
// 信号图标
Text(this.getRssiIcon(device.rssi))
.fontSize(28)
// 设备信息
Column() {
Text(device.deviceName)
.fontSize(16)
.fontColor('#FFFFFF')
Row() {
Text(`${device.rssi} dBm`)
.fontSize(12)
.fontColor('#AAAAAA')
Text(device.deviceId.substring(0, 8) + '...')
.fontSize(12)
.fontColor('#666666')
.margin({ left: 10 })
}
.margin({ top: 5 })
}
.alignItems(HorizontalAlign.Start)
.margin({ left: 15 })
.layoutWeight(1)
// 连接按钮
Button('连接')
.width(70)
.height(35)
.backgroundColor('#4A90E2')
.onClick(() => {
this.connectDevice(device);
})
}
.width('100%')
.padding(15)
.backgroundColor('#1A1A2E')
.borderRadius(8)
.margin({ bottom: 10 })
}
}, (device: BleDeviceInfo) => device.deviceId)
}
.width('100%')
.layoutWeight(1)
}
.width('90%')
.padding(20)
.backgroundColor('#16213E')
.borderRadius(12)
.margin({ top: 20 })
.layoutWeight(1)
}
// 提示信息
if (!this.isBluetoothEnabled) {
Column() {
Text('蓝牙未开启')
.fontSize(18)
.fontColor('#AAAAAA')
Text('请开启蓝牙以扫描BLE设备')
.fontSize(14)
.fontColor('#666666')
.margin({ top: 10 })
}
.margin({ top: 100 })
}
}
.width('100%')
.height('100%')
.backgroundColor('#0F0F1A')
}
}
3.3 BLE数据通信示例
连接成功后,通过GATT进行数据交互:
import { BleManager } from './BleManager';
/**
* BLE数据通信示例
* 以心率传感器为例
*/
export class HeartRateMonitor {
private bleManager: BleManager = BleManager.getInstance();
// 心率服务的UUID(标准UUID)
private readonly HEART_RATE_SERVICE_UUID = '0000180D-0000-1000-8000-00805F9B34FB';
// 心率测量特征值的UUID
private readonly HEART_RATE_MEASUREMENT_UUID = '00002A37-0000-1000-8000-00805F9B34FB';
// 心率数据回调
private onHeartRateReceived: ((heartRate: number) => void) | null = null;
/**
* 开始监听心率数据
*/
public async startMonitoring(): Promise<boolean> {
if (!this.bleManager.isConnected()) {
console.error('[心率] 设备未连接');
return false;
}
try {
// 启用心率通知
const success = await this.bleManager.enableNotification(
this.HEART_RATE_SERVICE_UUID,
this.HEART_RATE_MEASUREMENT_UUID,
true
);
if (success) {
// 设置数据回调
this.bleManager.setOnCharacteristicChanged((data: ArrayBuffer) => {
const heartRate = this.parseHeartRate(data);
if (this.onHeartRateReceived) {
this.onHeartRateReceived(heartRate);
}
});
}
return success;
} catch (error) {
console.error('[心率] 启动监听失败:', error);
return false;
}
}
/**
* 停止监听心率数据
*/
public async stopMonitoring(): Promise<boolean> {
return await this.bleManager.enableNotification(
this.HEART_RATE_SERVICE_UUID,
this.HEART_RATE_MEASUREMENT_UUID,
false
);
}
/**
* 解析心率数据
* 根据蓝牙心率服务规范解析
*/
private parseHeartRate(data: ArrayBuffer): number {
const view = new DataView(data);
// 第一个字节是标志位
const flags = view.getUint8(0);
// 检查心率值格式(8位或16位)
const is16Bit = (flags & 0x01) === 1;
if (is16Bit) {
// 16位心率值
return view.getUint16(1, true);
} else {
// 8位心率值
return view.getUint8(1);
}
}
/**
* 设置心率回调
*/
public setOnHeartRateReceived(callback: (heartRate: number) => void): void {
this.onHeartRateReceived = callback;
}
}
/**
* 智能灯控制示例
*/
export class SmartLightController {
private bleManager: BleManager = BleManager.getInstance();
// 灯控服务UUID(自定义)
private readonly LIGHT_SERVICE_UUID = '0000FFF0-0000-1000-8000-00805F9B34FB';
// 开关控制特征值UUID
private readonly SWITCH_UUID = '0000FFF1-0000-1000-8000-00805F9B34FB';
// 亮度控制特征值UUID
private readonly BRIGHTNESS_UUID = '0000FFF2-0000-1000-8000-00805F9B34FB';
// 颜色控制特征值UUID
private readonly COLOR_UUID = '0000FFF3-0000-1000-8000-00805F9B34FB';
/**
* 开灯
*/
public async turnOn(): Promise<boolean> {
const data = new ArrayBuffer(1);
new Uint8Array(data)[0] = 0x01; // 1表示开
return await this.bleManager.writeCharacteristic(
this.LIGHT_SERVICE_UUID,
this.SWITCH_UUID,
data
);
}
/**
* 关灯
*/
public async turnOff(): Promise<boolean> {
const data = new ArrayBuffer(1);
new Uint8Array(data)[0] = 0x00; // 0表示关
return await this.bleManager.writeCharacteristic(
this.LIGHT_SERVICE_UUID,
this.SWITCH_UUID,
data
);
}
/**
* 设置亮度
* @param brightness 亮度值(0-100)
*/
public async setBrightness(brightness: number): Promise<boolean> {
// 限制范围
const value = Math.max(0, Math.min(100, brightness));
const data = new ArrayBuffer(1);
new Uint8Array(data)[0] = value;
return await this.bleManager.writeCharacteristic(
this.LIGHT_SERVICE_UUID,
this.BRIGHTNESS_UUID,
data
);
}
/**
* 设置颜色
* @param r 红色分量(0-255)
* @param g 绿色分量(0-255)
* @param b 蓝色分量(0-255)
*/
public async setColor(r: number, g: number, b: number): Promise<boolean> {
const data = new ArrayBuffer(3);
const view = new Uint8Array(data);
view[0] = Math.max(0, Math.min(255, r));
view[1] = Math.max(0, Math.min(255, g));
view[2] = Math.max(0, Math.min(255, b));
return await this.bleManager.writeCharacteristic(
this.LIGHT_SERVICE_UUID,
this.COLOR_UUID,
data
);
}
}
四、踩坑与注意事项
4.1 权限配置
// module.json5
{
"module": {
"requestPermissions": [
{
"name": "ohos.permission.USE_BLUETOOTH",
"reason": "使用蓝牙功能"
},
{
"name": "ohos.permission.DISCOVER_BLUETOOTH",
"reason": "发现蓝牙设备"
},
{
"name": "ohos.permission.MANAGE_BLUETOOTH",
"reason": "管理蓝牙设置"
},
{
"name": "ohos.permission.LOCATION",
"reason": "BLE扫描需要位置权限"
},
{
"name": "ohos.permission.APPROXIMATELY_LOCATION",
"reason": "粗略位置权限"
}
]
}
}
4.2 扫描过滤器优化
坑点:不过滤会扫描到所有BLE设备,浪费资源。
/**
* 使用过滤器优化扫描
*/
async function scanWithFilter(targetServiceUuid: string): Promise<boolean> {
const filter: bluetooth.ScanFilter = {
serviceUuid: targetServiceUuid // 只扫描包含指定服务的设备
};
return await bleManager.startScan([filter]);
}
// 示例:只扫描心率设备
const HEART_RATE_SERVICE = '0000180D-0000-1000-8000-00805F9B34FB';
await scanWithFilter(HEART_RATE_SERVICE);
4.3 连接超时处理
坑点:BLE连接可能超时,需要处理。
/**
* 带超时的BLE连接
*/
async function connectWithTimeout(
deviceId: string,
timeout: number = 15000
): Promise<boolean> {
return new Promise(async (resolve) => {
let resolved = false;
// 设置超时
const timer = setTimeout(() => {
if (!resolved) {
resolved = true;
resolve(false);
console.warn('[BLE] 连接超时');
}
}, timeout);
// 设置连接回调
bleManager.setOnConnectionStateChanged((state) => {
if (!resolved) {
if (state === bluetooth.ProfileConnectionState.STATE_CONNECTED) {
resolved = true;
clearTimeout(timer);
resolve(true);
} else if (state === bluetooth.ProfileConnectionState.STATE_DISCONNECTED) {
resolved = true;
clearTimeout(timer);
resolve(false);
}
}
});
// 发起连接
await bleManager.connect(deviceId);
});
}
4.4 MTU大小协商
坑点:BLE默认MTU较小(20字节),大数据传输需要协商更大的MTU。
/**
* 协商MTU大小
*/
async function negotiateMtu(mtu: number = 512): Promise<boolean> {
if (!gattClient) return false;
try {
const actualMtu = await gattClient.requestMtuSize(mtu);
console.info(`[BLE] MTU协商成功: ${actualMtu}`);
return true;
} catch (error) {
console.error('[BLE] MTU协商失败:', error);
return false;
}
}
4.5 数据分包处理
坑点:超过MTU的数据需要分包发送。
/**
* 分包发送大数据
*/
async function sendDataInChunks(
serviceUuid: string,
characteristicUuid: string,
data: ArrayBuffer,
mtu: number = 20
): Promise<boolean> {
const totalLength = data.byteLength;
const chunkCount = Math.ceil(totalLength / mtu);
for (let i = 0; i < chunkCount; i++) {
const start = i * mtu;
const end = Math.min(start + mtu, totalLength);
const chunk = data.slice(start, end);
const success = await bleManager.writeCharacteristic(
serviceUuid,
characteristicUuid,
chunk,
bluetooth.GattWriteType.WRITE_TYPE_NO_RESPONSE
);
if (!success) {
console.error(`[BLE] 发送第 ${i + 1} 包失败`);
return false;
}
// 等待一小段时间,避免发送过快
await delay(10);
}
console.info(`[BLE] 数据发送完成,共 ${chunkCount} 包`);
return true;
}
function delay(ms: number): Promise<void> {
return new Promise(resolve => setTimeout(resolve, ms));
}
五、HarmonyOS 6适配
5.1 API变更
| API | HarmonyOS 5 | HarmonyOS 6 | 说明 |
|---|---|---|---|
| startBleScan() | 返回void | 返回Promise<void> | 改为异步 |
| createGattClient() | 同步创建 | 异步创建 | 统一异步模式 |
| 新增 | - | getBleScanResults() | 获取扫描结果 |
适配代码:
/**
* HarmonyOS 6 BLE适配
*/
async function startScanAdaptive(filters?: Array<bluetooth.ScanFilter>): Promise<boolean> {
const apiVersion = getApiVersion();
try {
if (apiVersion >= 6) {
// HarmonyOS 6
await bluetooth.startBleScan(filters);
const results = await bluetooth.getBleScanResults();
return results.length >= 0;
} else {
// HarmonyOS 5
await bluetooth.startBleScan(filters);
return true;
}
} catch (error) {
console.error('[BLE] 扫描失败:', error);
return false;
}
}
5.2 新增功能
HarmonyOS 6增强了BLE能力:
// 1. 扫描结果缓存
const cachedResults = await bluetooth.getBleScanResults();
console.info(`缓存结果: ${cachedResults.length} 个设备`);
// 2. 扫描设置增强
const scanSettings: bluetooth.ScanSettings = {
scanMode: bluetooth.ScanMode.SCAN_MODE_BALANCED,
legacyMode: false, // 使用扩展广播
phy: 1, // 1M PHY
callbackType: bluetooth.CallbackType.CALLBACK_TYPE_ALL_MATCHES
};
await bluetooth.startBleScan(filters, scanSettings);
// 3. 连接参数优化
const connectionParams: bluetooth.ConnectionParams = {
minInterval: 6, // 最小间隔(7.5ms)
maxInterval: 12, // 最大间隔(15ms)
latency: 0, // 延迟
timeout: 500 // 超时(5s)
};
await gattClient.connect(connectionParams);
5.3 隐私增强
HarmonyOS 6对蓝牙隐私有更严格的保护:
// 1. 设备地址随机化
const privacyConfig: bluetooth.PrivacyConfig = {
privacyMode: bluetooth.PrivacyMode.DEVICE_PRIVACY_MODE,
irk: new ArrayBuffer(16) // 身份解析密钥
};
await bluetooth.setPrivacy(privacyConfig);
// 2. 广播数据过滤
const advertisFilter: bluetooth.AdvertisFilter = {
includeDeviceName: false, // 不包含设备名
includeTxPowerLevel: false
};
六、总结
BLE蓝牙是物联网应用的重要通信方式,掌握它对于开发智能硬件应用至关重要。本文全面讲解了鸿蒙系统中BLE开发的核心要点:
核心要点回顾:
- 权限管理:BLE扫描需要位置权限,不可遗漏
- 扫描优化:使用过滤器和合适的扫描模式
- 连接管理:处理连接超时、断开重连等场景
- GATT操作:理解服务、特征值的概念和操作方法
- 数据传输:注意MTU限制和数据分包
最佳实践建议:
- 封装统一的BLE管理类,简化使用
- 使用扫描过滤器减少无效扫描
- 实现完善的连接重试机制
- 处理好MTU协商和数据分包
下一步学习:
- 蓝牙GATT服务与特征值操作(下一篇文章)
- 蓝牙音频传输
- 多设备同时连接
- BLE安全机制
BLE开发看似复杂,但掌握了GATT模型和核心API后,实现起来其实有章可循。希望本文能帮助你建立起完整的知识体系,在物联网开发中游刃有余!
- 点赞
- 收藏
- 关注作者
评论(0)