HarmonyOS 鸿蒙Next中如何进行蓝牙开发

HarmonyOS 鸿蒙Next中如何进行蓝牙开发 我的软件需要使用到蓝牙协议,应该怎样去做开发

3 回复

正文

首先我们需要清楚在鸿蒙当中怎样去实现蓝牙的功能

1.申请蓝牙权限

这里贴上文档地址:向用户申请授权-申请应用权限-应用权限管控-程序访问控制-安全-系统 - 华为HarmonyOS开发者

通过查看华为官方文档我们可以知道申请蓝牙权限有这些步骤:

首先,我们需要在配置文件当中申请蓝牙权限

如图,我们需要在module.json5配置文件中加入这么一个配置

"requestPermissions": [
    {
        "name" : "ohos.permission.ACCESS_BLUETOOTH",
        "reason": "$string:blooth_reason",
        "usedScene": {
            "abilities": [
                "FormAbility"
            ],
            "when":"inuse"
        }
    }
],

其中reason一项很容易让人搞不懂鼠标移动到reason后,按住control,鼠标左键单击

单击后我们会发现跳转到了string.json,然后在这里仿造格式写上原因就行啦,注意这里name属性对应的就是上面那个配置当中$string:blooth_reason的blooth_reason,这里随意起名字就好,但是要规范,自己能看得懂

在配置文件声明完权限后还需要添加向用户申请权限的代码,代码如下

 // 用户申请权限
 async reqPermissionsFromUser(): Promise<number[]> {
    let context = getContext() as common.UIAbilityContext;
    let atManager = abilityAccessCtrl.createAtManager();
    let grantStatus = await atManager.requestPermissionsFromUser(context, ['ohos.permission.ACCESS_BLUETOOTH']);
    return grantStatus.authResults;
 }

 // 用户申请蓝牙权限
 async requestBlueToothPermission() {
    let grantStatus = await this.reqPermissionsFromUser();
    for (let i = 0; i < grantStatus.length; i++) {
        if (grantStatus[i] === 0) {
            // 用户授权,可以继续访问目标操作
        }
    }
 }

需要 申请权限的时候就调用一下 requestBlueToothPermission ,这里要注意因为 requestBlueToothPermission 是异步函数,所以要用await来进行调用,像这样:

await this.requestBlueToothPermission()

这里给点小建议,可以在页面初始化的时候调用这个函数,这样就可以直接获取授权了

在这里我定义了一个init函数

代码如下

async init() {
    try {
        await this.requestBlueToothPermission()
    } catch (err) {
        promptAction.showToast({
            message: 'errCode: ' + (err as BusinessError).code + ', errMessage: ' + (err as BusinessError).message//弹窗内容
        });
        console.error('errCode: ' + (err as BusinessError).code + ', errMessage: ' + (err as BusinessError).message);
    }
}

我们可以在页面顶端调用该函数,如图所示:

如果一切顺利打开软件到调用init函数所在页面就会跳出授权弹窗

至此授权的过程也就完成了,如果用户选择拒绝授权还需要涉及二次授权,因为博主也没有二次授权的相关经验,还请大家自行参考官方文档进行调试

这里贴上二次授权的文档地址: 二次向用户申请授权-申请应用权限-应用权限管控-程序访问控制-安全-系统 - 华为HarmonyOS开发者

2.蓝牙相关操作开发

这里我定义了一个模型,用来存储扫描到的蓝牙设备数据,方便在连接界面展示

GlobalBlueTooth.ets

export interface BlueToothItem {
    mac: string,
    name: string
}

@ObservedV2
export class GlobalBlueTooth {
    @Trace blueList: BlueToothItem[] = [] //蓝牙设备数据列表

    //重置数据方法
    rest()
    {
        this.blueList = [] //蓝牙设备数据列表
    }
}

1.蓝牙开与关

BlueToothManager.ets

//开启蓝牙
async blueToothOn()
{
    try {
        await this.requestBlueToothPermission()
        await access.enableBluetooth();
    } catch (err) {
        promptAction.showToast({
            message: 'errCode: ' + (err as BusinessError).code + ', errMessage: ' + (err as BusinessError).message//弹窗内容
        });
        console.error('errCode: ' + (err as BusinessError).code + ', errMessage: ' + (err as BusinessError).message);
    }
}

//关闭蓝牙
async blueToothOff()
{
    try {
        await this.requestBlueToothPermission()
        await access.disableBluetooth();
    } catch (err) {
        promptAction.showToast({
            message: 'errCode: ' + (err as BusinessError).code + ', errMessage: ' + (err as BusinessError).message//弹窗内容
        });
        console.error('errCode: ' + (err as BusinessError).code + ', errMessage: ' + (err as BusinessError).message);
    }
}

2.蓝牙设备的搜索

BlueToothManager.ets

//检查扫描到的设备mac是否重复
//重复则不添加到表中
blueToothDeviceChecker(data: Array<string>)
{
    for(let i = 0; i < this.blueToothData.blueList.length; i++)
    {
        if(this.blueToothData.blueList[i].mac == data[0])
        {
            //当检查到重复则跳出循环返回0
            return 0;
        }
    }
    //当循环结束还没有找到重复则返回1
    return 1;
}

//蓝牙搜索方法
// 定义扫描结果上报回调函数
onReceiveEvent = (data: Array<string>) => {
    let deviceName = connection.getRemoteDeviceName(data[0])
    console.info('bluetooth device: '+ JSON.stringify(data));
    if(this.blueToothDeviceChecker(data))
    {
        //当没有扫描到重复时添加数据
        if(deviceName == '')
        {
            console.info('bluetooth device name: 未知设备');
            this.blueToothData.blueList.unshift({
                mac: data[0],
                name: "未知设备"
            }) //在共享的设备数据末位添加一条扫描到的设备数据
        }
        else
        {
            console.info('bluetooth device name: '+ deviceName);
            this.blueToothData.blueList.unshift({
                mac: data[0],
                name: deviceName
            })
        }
    }
};

public startDiscovery() {
    try {
        connection.on('bluetoothDeviceFind', this.onReceiveEvent);
    } catch (err) {
        console.error('errCode: ' + (err as BusinessError).code + ', errMessage: ' + (err as BusinessError).message);
    }
    try {
        // 判断本机设备是否正在进行扫描
        let scan = connection.isBluetoothDiscovering();
        if (!scan) {
            // 若当前不处于扫描过程,则开始扫描设备
            connection.startBluetoothDiscovery();
        }
    } catch (err) {
        console.error('errCode: ' + (err as BusinessError).code + ', errMessage: ' + (err as BusinessError).message);
    }
}

public stopDiscovery() {
    try {
        // 判断本机设备是否正在进行扫描
        let scan = connection.isBluetoothDiscovering();
        if (scan) {
            // 若当前处于扫描过程,则停止扫描设备
            connection.stopBluetoothDiscovery();
        }
        // 若不再需要使用扫描,可以取消订阅扫描上报结果
        connection.off('bluetoothDeviceFind', this.onReceiveEvent);
    } catch (err) {
        console.error('errCode: ' + (err as BusinessError).code + ', errMessage: ' + (err as BusinessError).message);
    }
}

3.获取本机的扫描模式

BlueToothManager.ets

public setScanMode() {
    try {
        // 获取当前本机的扫描模式
        let scanMode: connection.ScanMode = connection.getBluetoothScanMode();
        console.info('scanMode: ' + scanMode);
        if (scanMode != connection.ScanMode.SCAN_MODE_CONNECTABLE_GENERAL_DISCOVERABLE) {
            // 将本机设备的扫描模式设为可被发现和可被连接
            connection.setBluetoothScanMode(connection.ScanMode.SCAN_MODE_CONNECTABLE_GENERAL_DISCOVERABLE, 0);
        }
    } catch (err) {
        console.error('errCode: ' + (err as BusinessError).code + ', errMessage: ' + (err as BusinessError).message);
    }
}

4.获取已经配对的设备信息

public getPairedDevices() {
    try {
        // 获取已配对设备信息
        let devices = connection.getPairedDevices();
        console.info('pairedDevices: ' + JSON.stringify(devices));
        // 若已知道设备地址,可主动查询该设备是否是已配对的
        if (devices.length > 0) {
            let pairState = connection.getPairState(devices[0]);
            console.info('device: '+ devices[0] + ' pairState is ' + pairState);
        }
    } catch (err) {
        console.error('errCode: ' + (err as BusinessError).code + ', errMessage: ' + (err as BusinessError).message);
    }
}

5.与设备连接

这里采用的是spp连接的方式,我定义了一个sppClientManager的类来进行管理

SppClientManager.ets

import { socket } from '@kit.ConnectivityKit'
import { BusinessError } from '@kit.BasicServicesKit';
import { GlobalType } from '../models/globalType';
import { AppStorageV2 } from '@kit.ArkUI';
import { connection } from '@kit.NetworkKit';


class SppClientManager {
    // 定义客户端的socket id
    clientNumber: number = -1;

    //共享连接数据
    typeData: GlobalType = AppStorageV2.connect(GlobalType, 'TYPE_KEY', () => new GlobalType())!


    // 发起连接
    public startConnect(peerDevice: string, deviceName: string): void {
        // 配置连接参数
        let option: socket.SppOptions = {
            uuid: '00001101-0000-1000-8000-00805F9B34FB', // 需要连接的服务端UUID服务,确保服务端支持
            secure: false,
            type: socket.SppType.SPP_RFCOMM
        };
        console.info('startConnect ' + peerDevice);
        socket.sppConnect(peerDevice, option, (err, num: number) => {
            if (err) {
                console.error('startConnect errCode: ' + (err as BusinessError).code + ', errMessage: ' + (err as BusinessError).message);
            } else {
                //连接成功
                this.typeData.connectType = 1; //修改显示的连接状态
                this.typeData.connectedDeviceName = deviceName; //修改显示的连接设备名称
                console.info('startConnect clientNumber: ' + num);
                this.clientNumber = num;
            }
        });
        console.info('startConnect after ' + peerDevice);
    }


    // 发送数据
    public sendData(sendData: number) {
        console.info('sendData ' + this.clientNumber);
        let arrayBuffer = new ArrayBuffer(1);
        let data = new Uint8Array(arrayBuffer);
        data[0] = sendData;
        try {
            socket.sppWrite(this.clientNumber, arrayBuffer);
        } catch (err) {
            console.error('sppWrite errCode: ' + (err as BusinessError).code + ', errMessage: ' + (err as BusinessError).message);
            return -1; //发现异常,也就是有可能断开连接就返回-1
        }
        return 1; //正常发送数据代表还在连接就返回1
    }


    // 定义接收数据的回调函数
    read = (dataBuffer: ArrayBuffer) => {
        let data = new Uint8Array(dataBuffer);
        console.info('client data: ' + JSON.stringify(data));
    };


    // 接收数据
    public readData() {
        try {
            // 发起订阅
            socket.on('sppRead', this.clientNumber, this.read);
        } catch (err) {
            console.error('readData errCode: ' + (err as BusinessError).code + ', errMessage: ' + (err as BusinessError).message);
        }
    }


    // 断开连接
    public stopConnect() {
        console.info('closeSppClient ' + this.clientNumber);
        try {
            // 取消接收数据订阅
            socket.off('sppRead', this.clientNumber, this.read);
        } catch (err) {
            console.error('off sppRead errCode: ' + (err as BusinessError).code + ', errMessage: ' + (err as BusinessError).message);
        }
        try {
            // 从client端断开连接
            socket.sppCloseClientSocket(this.clientNumber);

        } catch (err) {
            console.error('stopConnect errCode: ' + (err as BusinessError).code + ', errMessage: ' + (err as BusinessError).message);
        }
    }
}


export const sppClientManager: SppClientManager = new SppClientManager()

BlueToothManager.ets

public connectDevice(device: string) {
    let pairState = connection.getPairState(device); //查询设备是否配对
    if(pairState == 2)
    {
        //设备已配对
        let deviceName = connection.getRemoteDeviceName(device); //获取设备名称
        sppClientManager.startConnect(device, deviceName) //发起连接
    }
    else
    {
        //设备没有配对
        try {
            // 发起配对
            connection.pairDevice(device).then(() => {
                console.info('pairDevice');
            }, (error: BusinessError) => {
                console.error('pairDevice: errCode:' + error.code + ',errMessage' + error.message);
            });
        } catch (err) {
            console.error('startPair: errCode:' + err.code + ',errMessage' + err.message);
            console.info('mac:' + device);
        }
    }
}

当连接设备的时候调用connectDevice函数,传入连接设备的地址一般为 XX:XX:XX:XX:XX:XX ,该地址可以在步骤二蓝牙设备的搜索当中获取

connectDevice函数会先检查该地址设备是否已配对,如果没有配对则会开始配对设备,如果已配对就会调用SppClientManager类中的startConnect开始连接设备,连接成功后就可以开始收发数据了

6.数据的收发

SppClientManager.ets

// 发送数据
  public sendData(sendData: number) {
    console.info('sendData ' + this.clientNumber);
    let arrayBuffer = new ArrayBuffer(1);
    let data = new Uint8Array(arrayBuffer);
    data[0

更多关于HarmonyOS 鸿蒙Next中如何进行蓝牙开发的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html


在HarmonyOS Next中进行蓝牙开发,需使用ArkTS语言调用蓝牙API。主要步骤包括:1. 申请蓝牙权限(ohos.permission.USE_BLUETOOTH等);2. 初始化蓝牙模块(bluetooth.getLocalState);3. 发现与配对设备(startBluetoothDiscovery);4. 建立GATT连接(connectGatt);5. 通过特征值(characteristic)进行数据读写。开发过程需遵循HarmonyOS蓝牙规范,使用系统提供的Profile和Service。

在HarmonyOS Next中进行蓝牙开发,主要使用@ohos.bluetooth@ohos.bluetoothManager等系统接口。核心流程如下:

1. 权限与配置

  • module.json5中声明必要权限,例如:
"requestPermissions": [
  {
    "name": "ohos.permission.USE_BLUETOOTH",
    "reason": "用于蓝牙通信"
  },
  {
    "name": "ohos.permission.LOCATION",
    "reason": "用于蓝牙设备发现"
  }
]

2. 基础蓝牙操作

  • 启用/禁用蓝牙:通过bluetoothManager.enableBluetooth()/disableBluetooth()控制。
  • 设备可见性:使用bluetoothManager.setBluetoothScanMode()设置可被发现模式。
  • 本地设备信息:通过bluetoothManager.getLocalName()等获取。

3. 经典蓝牙开发

  • 设备发现:调用bluetoothManager.startBluetoothDiscovery()扫描设备,通过订阅bluetoothDeviceFind事件获取设备列表。
  • 配对与连接:使用bluetoothManager.pairDevice()配对,通过socket.construct()创建Socket进行RFCOMM通信。
  • 数据传输:基于Socket的send()/recv()方法实现。

4. 低功耗蓝牙开发

  • 中心设备模式
    • 通过bluetooth.BLE.createGattClientDevice()创建客户端。
    • 连接后使用connect()建立链路,通过discoverServices()发现服务。
    • 通过readCharacteristicValue()/writeCharacteristicValue()读写特征值,订阅characteristicChange事件接收通知。
  • 外围设备模式
    • 使用bluetooth.BLE.createGattServerDevice()创建服务端。
    • 通过addService()添加服务,使用startAdvertising()广播。

5. 关键注意事项

  • 确保在onDestroy()等生命周期中释放资源,停止扫描、断开连接。
  • 涉及UI更新的操作需通过主线程处理。
  • 低功耗蓝牙操作需注意连接参数和MTU设置。

建议参考官方文档中的蓝牙相关指南和API详解,并利用DevEco Studio提供的模拟器或真机进行调试。

回到顶部