在鸿蒙(HarmonyOS)中,使用ETS(Extended TypeScript/ArkTS)实现低功耗蓝牙(BLE)连接匹配需要以下步骤:
一、准备工作
1.1 添加权限
在 module.json5 中配置权限:
c
json { "module": {
"requestPermissions": [
{
"name": "ohos.permission.USE_BLUETOOTH"
},
{
"name": "ohos.permission.DISCOVER_BLUETOOTH"
},
{
"name": "ohos.permission.MANAGE_BLUETOOTH"
},
{
"name": "ohos.permission.APPROXIMATELY_LOCATION"
}
] } }
1.2 导入模块
c
typescript
import ble from '@ohos.bluetooth.ble';
import { BusinessError } from '@ohos.base';
import common from '@ohos.app.ability.common';
二、BLE管理器实现
2.1 创建BLE管理类
c
typescript
export class BleManager {
private context: common.UIAbilityContext;
private isScanning: boolean = false;
private discoveredDevices: Map<string, ble.ScanResult> = new Map();
private gattServer: ble.GattClient | null = null;
constructor(context: common.UIAbilityContext) {
this.context = context;
}
// 检查蓝牙状态
async checkBluetoothState(): Promise<boolean> {
try {
const state = await ble.getBluetoothState();
console.log(`当前蓝牙状态: ${state}`);
return state === ble.BluetoothState.STATE_ON;
} catch (error) {
console.error('检查蓝牙状态失败:', error);
return false;
}
}
// 开启蓝牙
async enableBluetooth(): Promise<boolean> {
try {
await ble.enableBluetooth();
console.log('蓝牙已开启');
return true;
} catch (error) {
console.error('开启蓝牙失败:', error);
return false;
}
}
}
2.2 扫描BLE设备
c
typescript
// 在BleManager类中添加方法
public async startScan(
serviceUuids?: Array<string>,
timeout: number = 10000
): Promise<void> {
if (this.isScanning) {
console.warn('已经在扫描中');
return;
}
// 检查蓝牙状态
const isEnabled = await this.checkBluetoothState();
if (!isEnabled) {
const enabled = await this.enableBluetooth();
if (!enabled) {
throw new Error('蓝牙开启失败');
}
}
this.discoveredDevices.clear();
// 设置扫描参数
const scanOptions: ble.ScanOptions = {
interval: 500,
dutyMode: ble.ScanDuty.SCAN_MODE_LOW_POWER,
matchMode: ble.MatchMode.MATCH_MODE_AGGRESSIVE
};
// 开始扫描
try {
await ble.startBLEScan(scanOptions);
this.isScanning = true;
console.log('开始扫描BLE设备...');
// 注册扫描结果回调
ble.on('BLEDeviceFind', (device: ble.ScanResult) => {
this.handleDeviceFound(device);
});
// 设置超时停止
setTimeout(() => {
this.stopScan();
}, timeout);
} catch (error) {
console.error('扫描失败:', error);
this.isScanning = false;
}
}
// 处理发现的设备
private handleDeviceFound(device: ble.ScanResult): void {
const deviceId = device.deviceId;
if (!this.discoveredDevices.has(deviceId)) {
this.discoveredDevices.set(deviceId, device);
console.log(`发现设备: ${device.deviceId}, 名称: ${device.deviceName || '未知'}, RSSI: ${device.rssi}`);
// 可以在这里触发UI更新
// this.emitDeviceFound(device);
}
}
// 停止扫描
public async stopScan(): Promise<void> {
if (!this.isScanning) {
return;
}
try {
await ble.stopBLEScan();
ble.off('BLEDeviceFind'); // 取消回调监听
this.isScanning = false;
console.log('扫描已停止');
} catch (error) {
console.error('停止扫描失败:', error);
}
}
2.3 连接BLE设备
c
typescript
// 连接设备
public async connectDevice(deviceId: string): Promise<boolean> {
try {
// 先停止扫描
await this.stopScan();
// 创建GATT客户端
this.gattServer = await ble.createGattClientDevice(this.context, deviceId);
// 连接设备
const isConnected = await this.gattServer.connect();
if (isConnected) {
console.log(`设备 ${deviceId} 连接成功`);
// 发现服务
await this.discoverServices();
return true;
} else {
console.error('连接失败');
return false;
}
} catch (error) {
console.error('连接异常:', error);
return false;
}
}
// 发现服务
private async discoverServices(): Promise<void> {
if (!this.gattServer) {
return;
}
try {
const services = await this.gattServer.getServices();
console.log(`发现 ${services.length} 个服务`);
// 遍历所有服务
for (const service of services) {
console.log(`服务 UUID: ${service.serviceUuid}`);
// 获取特征
const characteristics = await this.gattServer.getCharacteristics(service.serviceUuid);
console.log(`服务 ${service.serviceUuid} 有 ${characteristics.length} 个特征`);
// 这里可以处理特定的服务UUID,例如:
// if (service.serviceUuid === '0000180F-0000-1000-8000-00805F9B34FB') {
// // 电池服务
// }
}
} catch (error) {
console.error('发现服务失败:', error);
}
}
2.4 读写特征值
c
typescript
// 读取特征值
public async readCharacteristic(
serviceUuid: string,
characteristicUuid: string
): Promise<Uint8Array | null> {
if (!this.gattServer) {
console.error('未连接设备');
return null;
}
try {
const value = await this.gattServer.readCharacteristicValue(
serviceUuid,
characteristicUuid
);
console.log(`读取特征值成功: ${characteristicUuid}`);
return value;
} catch (error) {
console.error(`读取特征值失败: ${error}`);
return null;
}
}
// 写入特征值
public async writeCharacteristic(
serviceUuid: string,
characteristicUuid: string,
value: Uint8Array
): Promise<boolean> {
if (!this.gattServer) {
console.error('未连接设备');
return false;
}
try {
await this.gattServer.writeCharacteristicValue(
serviceUuid,
characteristicUuid,
value
);
console.log(`写入特征值成功: ${characteristicUuid}`);
return true;
} catch (error) {
console.error(`写入特征值失败: ${error}`);
return false;
}
}
// 开启通知
public async enableNotification(
serviceUuid: string,
characteristicUuid: string
): Promise<boolean> {
if (!this.gattServer) {
return false;
}
try {
// 先设置特征值变化监听
this.gattServer.on('characteristicChange', (characteristic) => {
if (characteristic.characteristicUuid === characteristicUuid) {
console.log(`收到特征值变化: ${characteristicUuid}`);
this.handleCharacteristicChange(characteristic);
}
});
// 启用通知
await this.gattServer.setNotifyCharacteristicChanged(
serviceUuid,
characteristicUuid,
true
);
console.log(`已开启通知: ${characteristicUuid}`);
return true;
} catch (error) {
console.error(`开启通知失败: ${error}`);
return false;
}
}
private handleCharacteristicChange(characteristic: ble.Characteristic): void {
// 处理接收到的数据
const value = characteristic.value;
// 解析数据,根据协议进行匹配
}
2.5 断开连接
c
typescript
// 断开连接
public async disconnect(): Promise<void> {
if (this.gattServer) {
try {
// 停止所有通知
await this.gattServer.setNotifyCharacteristicChanged(
'any-service',
'any-characteristic',
false
);
// 断开连接
await this.gattServer.disconnect();
this.gattServer = null;
console.log('已断开连接');
} catch (error) {
console.error('断开连接失败:', error);
}
}
}
三、UI界面示例
c
typescript
// DeviceList.ets
@Entry
@Component
struct DeviceList {
private bleManager: BleManager = new BleManager(getContext(this) as common.UIAbilityContext);
@State deviceList: Array<ble.ScanResult> = [];
@State isScanning: boolean = false;
build() {
Column() {
// 扫描按钮
Button(this.isScanning ? '停止扫描' : '开始扫描')
.onClick(() => {
if (this.isScanning) {
this.stopScan();
} else {
this.startScan();
}
})
.margin(10)
// 设备列表
List({ space: 10 }) {
ForEach(this.deviceList, (device: ble.ScanResult) => {
ListItem() {
DeviceItem({
device: device,
onConnect: (deviceId: string) => {
this.connectDevice(deviceId);
}
})
}
})
}
.layoutWeight(1)
}
}
async startScan() {
this.isScanning = true;
// 监听设备发现
ble.on('BLEDeviceFind', (device: ble.ScanResult) => {
// 更新设备列表
this.updateDeviceList(device);
});
await this.bleManager.startScan(['0000180F-0000-1000-8000-00805F9B34FB']);
}
async stopScan() {
await this.bleManager.stopScan();
this.isScanning = false;
}
updateDeviceList(device: ble.ScanResult) {
// 避免重复添加
const exists = this.deviceList.some(d => d.deviceId === device.deviceId);
if (!exists) {
this.deviceList = [...this.deviceList, device];
}
}
async connectDevice(deviceId: string) {
const connected = await this.bleManager.connectDevice(deviceId);
if (connected) {
// 连接成功,跳转到控制页面
// router.push(...)
}
}
}
四、匹配逻辑实现
4.1 基于特征值的匹配
c
typescript
// 实现特定的匹配协议
export class BleMatcher {
// 发送匹配请求
static async sendMatchRequest(
bleManager: BleManager,
matchData: Uint8Array
): Promise<boolean> {
// 写入特定的特征值进行匹配
const success = await bleManager.writeCharacteristic(
'YOUR_SERVICE_UUID',
'YOUR_CHARACTERISTIC_UUID',
matchData
);
return success;
}
// 解析匹配响应
static parseMatchResponse(data: Uint8Array): MatchResult {
// 根据协议解析数据
// 返回匹配结果
return {
success: data[0] === 0x01,
matchCode: data.slice(1, 5),
timestamp: new Date()
};
}
}
五、注意事项
- 权限管理:确保用户已授权所有必要的蓝牙权限
- 连接超时:设置合理的连接超时时间
- 错误处理:妥善处理各种异常情况
- 资源释放:在页面销毁时断开连接
- 后台运行:考虑应用退到后台时的处理策略