1. 权限配置
在 module.json5 文件中添加蓝牙相关权限:
{
"module": {
"requestPermissions": [
{
"name": "ohos.permission.ACCESS_BLUETOOTH",
"reason": "需要使用蓝牙功能"
}
]
}
}
2. 导入蓝牙模块
import bluetooth from '@ohos.bluetooth';
3. 蓝牙状态检查与开启
//用户申请蓝牙权限
async requestBlueToothPermission() {
let grantStatus = await this.reqPermissionsFromUser();
for (let i = 0; i < grantStatus.length; i++) {
if (grantStatus[i] === 0) {
//查看当前蓝牙开启状态
this.isStartBlueTooth = BleState.getBluetoothState();
if (!this.isStartBlueTooth) {
showConfirmAlertDialog('蓝牙未开启,请滑出系统控制中心,打开蓝牙进行连接', () => {})
}
} else {
showConfirmAlertDialog('蓝牙授权失败,请打开设置进行授权', () => {
WantUtil.toAppSetting()
})
console.info(CommonConstants.log_prefix + '蓝牙授权失败!')
}
}
}
//用户申请权限
async reqPermissionsFromUser(): Promise<number[]> {
// 申请权限
let context = getContext() as common.UIAbilityContext;
// 获取AbilityAccessCtrl对象
let atManager = abilityAccessCtrl.createAtManager();
// 请求权限,传入上下文和需要申请的权限列表
let grantStatus = await atManager.requestPermissionsFromUser(context, ['ohos.permission.ACCESS_BLUETOOTH']);
// 返回授权结果列表
return grantStatus.authResults;
}
4. 设备扫描
try {
const scanOptions: ble.ScanOptions = {
interval: 1000,
dutyMode: ble.ScanDuty.SCAN_MODE_LOW_POWER,
matchMode: ble.MatchMode.MATCH_MODE_AGGRESSIVE,
};
// 订阅 BLEDeviceFind 事件
ble.on('BLEDeviceFind', (data: Array<ble.ScanResult>) => {
//解析蓝牙广播数据
});
// 开始扫描
ble.startBLEScan(filters, scanOptions);
this.isScanning = true;
} catch (err) {
throw new Error('Failed to start BLE scan');
}
5. 设备连接、数据接收
const clientDevice = ble.createGattClientDevice(deviceId)
//连接状态
clientDevice.on('BLEConnectionStateChange', (state: ble.BLEConnectionChangeState) => {
const connectState = state.state;
switch (connectState) {
case constant.ProfileConnectionState.STATE_DISCONNECTED: // 设备已断开连接
break;
case constant.ProfileConnectionState.STATE_CONNECTING: // 设备正在连接
break;
case constant.ProfileConnectionState.STATE_CONNECTED: // 设备已连接
//发现服务
this.getServices();
break;
case constant.ProfileConnectionState.STATE_DISCONNECTING: // 设备正在断开连接
break;
default:
break;
}
})
//连接
try {
clientDevice.connect()
} catch (err) {
}
//获取服务
private async getServices() {
if (this.gattClientDevice) {
const result: Array<ble.GattService> = await this.gattClientDevice.getServices();
console.info('getServices successfully:' + JSON.stringify(result));
let gatt = this.gattClientDevice;
LogUtils.debug('发现蓝牙服务>>>' + JSON.stringify(result))
for (const item of result) {
console.info('getServices item= ' + JSON.stringify(item));
//服务UUID
if (item.serviceUuid === ‘读写特征UUID'’) {
this.gattServiceInfo = item;
const writeCharacteristic = item.characteristics.find(c => c.characteristicUuid == '读写特征UUID');
const notyCharacteristic = item.characteristics.find(c => c.characteristicUuid == '通知特征UUID');
// setNotification
if (writeCharacteristic) {
try {
const success = await this.setNotificationChannel(gatt, item, writeCharacteristic, true);
if (success) {
console.log(`Notification set successfully for characteristic(通道1) ${writeCharacteristic.characteristicUuid}`);
} else {
console.log(`Failed to set notification for characteristic(通道1) ${writeCharacteristic.characteristicUuid}`);
}
}catch (err){
}
}
// setNotification
if (notyCharacteristic) {
try {
const success = await this.setNotificationChannel(gatt, item, notyCharacteristic, true);
if (success) {
console.log(`Notification set successfully for characteristic(通道1) ${notyCharacteristic.characteristicUuid}`);
} else {
console.log(`Failed to set notification for characteristic(通道1) ${notyCharacteristic.characteristicUuid}`);
}
}catch (err){
}
}
}
}
//接收设备数据
this.onBleCharacteristicChange()
}
}
//向下位机发送设置通知此indicate征值请求
private async setNotificationChannel(
gatt: ble.GattClientDevice | null ,
gattService: ble.GattService | undefined,
characteristic: ble.BLECharacteristic,
enable: boolean
): Promise<boolean>{
if (!gattService) {
return false; // 返回失败
}
if (gatt == null){
return false; // 返回失败
}
try {
if (gatt) {
// 向下位机发送设置通知此indicate征值请求
await gatt?.setCharacteristicChangeNotification(characteristic, enable)
return true; // 返回成功
}
} catch (err) {
return false; // 返回失败
}
return false; // 如果没有gatt,返回失败
}
//订阅蓝牙低功耗设备的特征值变化事件 (接收消息)
private onBleCharacteristicChange() {
LogUtils.debug('开始监听特征值变化')
try {
if (this.gattClientDevice) {
//监听
this.gattClientDevice.on('BLECharacteristicChange', (characteristicChangeReq: ble.BLECharacteristic) => {
let serviceUuid: string = characteristicChangeReq.serviceUuid;
let characteristicUuid: string = characteristicChangeReq.characteristicUuid;
const characteristicValue = characteristicChangeReq.characteristicValue
//服务UUID
if(serviceUuid == '读写特征UUID'){
//回调数据
} else {
}
})
}
} catch (err) {
}
}
6. 数据发送
export class TBBleCommandManager {
private gatt: ble.GattClientDevice | null
private gattServiceInfo: ble.GattService | null
private data: ArrayBuffer;//特征数据
/**
* 构造函数
* @param gatt GattClientDevice实例
* @param gattServiceInfo GattService实例
* @param data 特征数据
*/
constructor(
gatt: ble.GattClientDevice | null,
gattServiceInfo: ble.GattService | null,
data: ArrayBuffer,
) {
this.gatt = gatt
this.gattServiceInfo = gattServiceInfo
this.data = data
}
//写入特征值
writeBrushCharacteristicValue(){
//查找读写特征
const characteristic = this.gattServiceInfo?.characteristics.find(c => c.characteristicUuid === BrushConst.WRITE_CHARACTERISTIC_UUID);
if (!characteristic) {
return
}
try {
if (this.gatt) {
// 设置特征值
characteristic.characteristicValue = this.data
// 写入特征值到设备中
this.gatt.writeCharacteristicValue(characteristic, ble.GattWriteType.WRITE)
} else {
}
}
catch (err) {
const errorCode = (err as BusinessError).code;
const errorMessage = (err as BusinessError).message;
}
}
}
数据粘包的问题,我们可以采用队列的方式处理
import { BluetoothCommand } from "./BluetoothCommand";
import { BusinessError } from "@kit.BasicServicesKit";
import { TBBleCommandManager } from "../../toothbrush/model/TBBleCommandManager";
export class BluetoothCommandQueue {
// 队列存储指令,根据优先级排序
private queue: BluetoothCommand[] = [];
// 标记是否正在处理指令
private isProcessing: boolean = false;
// 队列最大长度限制,可根据实际情况调整
private maxSize: number = 999;
// 添加指令到队列
enqueue(command: BluetoothCommand): boolean {
if (this.queue.length >= this.maxSize) {
return false;
}
// 根据优先级插入队列
let inserted = false;
for (let i = 0; i < this.queue.length; i++) {
if (this.queue[i].priority < command.priority) {
this.queue.splice(i, 0, command);
inserted = true;
break;
}
}
if (!inserted) {
this.queue.push(command);
}
this.processQueue();
return true;
}
// 从队列取出指令
dequeue(): BluetoothCommand | undefined {
return this.queue.shift();
}
// 处理队列中的指令
private async processQueue(): Promise<void> {
if (this.isProcessing || this.queue.length === 0) {
return;
}
this.isProcessing = true;
while (this.queue.length > 0) {
const command:BluetoothCommand = this.dequeue() as BluetoothCommand;
if (command) {
try {
// 这里调用实际的蓝牙发送接口
await this.sendBluetoothCommand(command);
command.callback?.(null);
} catch (error) {
command.callback?.(error as BusinessError);
}
}
}
// 处理完成后,标记为非处理状态
this.isProcessing = false;
}
// 发送蓝牙指令的方法
private sendBluetoothCommand(command: BluetoothCommand): Promise<void> {
return new Promise((resolve, reject) => {
// 发送蓝牙指令
new TBBleCommandManager(command.gatt, command.gattServiceInfo, command.command).writeBrushCharacteristicValue()
setTimeout(() => {
console.info('Bluetooth command sent successfully');
// 成功回调
resolve();
}, 100);
});
}
// 清空队列
clear(): void {
this.queue = [];
this.isProcessing = false;
}
// 获取队列大小
size(): number {
return this.queue.length;
}
}
import { BusinessError } from "@kit.BasicServicesKit";
import { ble } from "@kit.ConnectivityKit";
export class BluetoothCommand {
command: ArrayBuffer;
priority: number;
timestamp: number;
callback?: (result: BusinessError | null) => void;
gatt: ble.GattClientDevice | null;
gattServiceInfo: ble.GattService | null;
/**
* 构造函数
* @param command 命令
* @param priority 优先级
* @param gatt GattClientDevice实例
* @param gattServiceInfo GattService实例
* @param callback 回调函数
*/
constructor(command: ArrayBuffer, priority: number = 0, gatt: ble.GattClientDevice , gattServiceInfo: ble.GattService , callback?: (result: BusinessError | null) => void) {
this.command = command;
this.gatt = gatt;
this.gattServiceInfo = gattServiceInfo;
this.priority = priority;
this.timestamp = Date.now();
this.callback = callback;
}
}
队列的调用方法如下
private commandQueue: BluetoothCommandQueue = new BluetoothCommandQueue();//指令发送队列
// 写入BLE特征值
// 创建蓝牙指令对象
const command = new BluetoothCommand( buff, 0, this.gattClientDevice!, this.gattServiceInfo!,
(error: BusinessError | null) => {
if (error) {
// `发送失败: ${error.message}`;
} else {
// '发送成功';
}
}
);
this.commandQueue.enqueue(command)
注意事项
- 权限申请:必须在配置文件中声明蓝牙权限,并在运行时检查权限状态
- 异步操作:蓝牙操作多为异步,需正确处理回调
- 状态监听:建议监听蓝牙状态变化,及时响应开关变化
- 资源释放:使用完毕后及时断开连接,释放蓝牙资源
- 异常处理:做好异常捕获和错误处理机制
- 兼容性:注意不同设备型号和系统版本的兼容性问题
- 安全性:配对和数据传输时注意安全加密
- 功耗控制:合理控制扫描频率,避免过度耗电











网友评论