OpenHarmony蓝牙技术全解析:从设备发现到数据传输的完整流程
目录
- 蓝牙技术概述
- 蓝牙设置
- 传统蓝牙开发
- 查找设备
- 配对连接设备
- 连接和传输数据
- 低功耗蓝牙开发
- 查找设备
- 连接和传输数据
- 开发实践与注意事项
蓝牙技术概述
蓝牙技术发展历程
蓝牙技术起源于1994年,由爱立信公司首创,旨在开发一种短距离无线通信技术,以替代传统有线连接。1998年,蓝牙特别兴趣小组(SIG)成立,负责蓝牙技术的标准化和推广。蓝牙技术经历了多次重大演进:
- 蓝牙1.0/1.1B:基础速率(BR)技术,传输速率约721kbps
- 蓝牙2.0+EDR:增强数据速率(EDR),传输速率提升至2-3Mbps
- 蓝牙3.0+HS:高速传输,结合WiFi技术实现高达24Mbps的传输速率
- 蓝牙4.0:引入低功耗蓝牙(BLE)技术,显著降低功耗
- 蓝牙5.0/5.1/5.2/5.3:不断提升传输距离、速度和连接稳定性,增加音频共享、定位等功能
蓝牙通信原理
蓝牙通信基于**跳频扩频(FHSS)**技术,在2.402-2.480GHz的ISM频段内工作。通信原理包括以下几个关键方面:
1. 跳频技术
蓝牙设备将频段划分为79个1MHz宽的信道(部分国家为23个),通信时按照伪随机序列在这些信道间快速跳变,每秒跳变1600次。跳频技术具有以下优势:
- 抗干扰能力强:避免特定频段的持续干扰
- 安全性高:只有知道跳频序列的设备才能通信
- 频谱利用率高:多设备可同时工作而互不干扰
2. 蓝牙网络拓扑
蓝牙支持多种网络拓扑结构:
- 点对点(Piconet):一个主设备最多连接7个从设备
- 散射网(Scatternet):多个Piconet通过桥接设备互联
- 广播网络:一个设备向多个设备广播数据
3. 蓝牙协议栈架构
蓝牙协议栈分为四层:
-
核心协议层:
- 基带(Baseband):处理物理层连接和数据传输
- 链路管理协议(LMP):负责链路建立、配置和安全
- 逻辑链路控制和适配协议(L2CAP):提供面向连接和无连接的数据服务
-
电缆替代协议层:
- RFCOMM:模拟串口通信,支持传统串口应用
-
电话控制协议层:
- TCS Binary:提供电话控制信令
-
选用协议层:
- PPP、TCP/IP、UDP、OBEX、WAP等
蓝牙技术分类
OpenHarmony系统支持两种蓝牙技术:
-
传统蓝牙(BR/EDR):
- 支持高质量音频传输(A2DP)
- 支持免提通话(HFP)
- 支持人机接口设备(HID)
- 支持串口通信协议(SPP)
- 适合流媒体传输、音频设备等场景
-
低功耗蓝牙(BLE):
- 功耗极低,适合电池供电设备
- 支持通用属性协议(GATT)
- 适用于物联网设备、健康监测设备等场景
- 连接建立速度快,数据传输量小但频繁
蓝牙架构
OpenHarmony蓝牙架构主要包含以下层次:
- 应用层:开发者使用蓝牙API实现应用功能
- 框架层:提供蓝牙功能接口
- 协议层:实现蓝牙协议栈
- 驱动层:与蓝牙硬件交互
蓝牙设置
权限申请
开发蓝牙应用前,需要在module.json5文件中声明必要的权限:
{"module": {"requestPermissions": [{"name": "ohos.permission.ACCESS_BLUETOOTH","reason": "$string:bluetooth_reason","usedScene": {"abilities": ["EntryAbility"],"when": "inuse"}}]}
}
蓝牙状态管理
获取蓝牙状态
import { connection } from '@kit.ConnectivityKit';
import { BusinessError } from '@kit.BasicServicesKit';try {// 获取蓝牙状态let state = connection.getBluetoothState();console.info('bluetooth state: ' + state);
} catch (err) {console.error('errCode: ' + (err as BusinessError).code + ', errMessage: ' + (err as BusinessError).message);
}
订阅蓝牙状态变化
// 定义蓝牙状态变化回调函数
function onReceiveEvent(data: connection.BluetoothState) {console.info('bluetooth state: ' + data);
}try {// 发起订阅connection.on('stateChange', onReceiveEvent);
} catch (err) {console.error('errCode: ' + (err as BusinessError).code + ', errMessage: ' + (err as BusinessError).message);
}
开启/关闭蓝牙
import { connection } from '@kit.ConnectivityKit';
import { BusinessError } from '@kit.BasicServicesKit';
import { common } from '@kit.AbilityKit';
import { promptAction } from '@kit.ArkUI';async function requestBluetoothPermission(context: common.UIAbilityContext): Promise<boolean> {try {// 检查蓝牙权限let result = await context.requestPermissionsFromUser(['ohos.permission.ACCESS_BLUETOOTH']);if (result.authResults[0] === 0) {// 权限已授予return true;} else {// 权限被拒绝return false;}} catch (err) {console.error('requestBluetoothPermission errCode: ' + (err as BusinessError).code + ', errMessage: ' + (err as BusinessError).message);return false;}
}async function enableBluetooth(context: common.UIAbilityContext) {try {// 检查蓝牙权限let hasPermission = await requestBluetoothPermission(context);if (!hasPermission) {console.error('no bluetooth permission');return;}// 检查蓝牙状态let state = connection.getBluetoothState();if (state === connection.BluetoothState.STATE_OFF) {// 蓝牙未开启,请求开启蓝牙connection.enableBluetooth();}} catch (err) {console.error('enableBluetooth errCode: ' + (err as BusinessError).code + ', errMessage: ' + (err as BusinessError).message);}
}async function disableBluetooth() {try {// 检查蓝牙状态let state = connection.getBluetoothState();if (state === connection.BluetoothState.STATE_ON) {// 蓝牙已开启,请求关闭蓝牙connection.disableBluetooth();}} catch (err) {console.error('disableBluetooth errCode: ' + (err as BusinessError).code + ', errMessage: ' + (err as BusinessError).message);}
}
传统蓝牙开发
传统蓝牙(BR/EDR)基于主从架构工作,在通信过程中,一个设备作为主设备(Master),其他设备作为从设备(Slave)。主设备负责控制通信时序和跳频序列,从设备根据主设备的指令进行响应。
传统蓝牙通信流程
传统蓝牙通信遵循以下基本流程:
- 设备发现:通过查询(Inquiry)过程发现周围的蓝牙设备
- 设备配对:建立安全连接,交换密钥信息
- 设备连接:建立物理链路(ACL)和逻辑链路(L2CAP)
- 服务发现:查询对方设备支持的服务和协议
- 数据传输:通过RFCOMM或其他协议进行数据交换
- 连接断开:释放资源,终止通信
传统蓝牙协议栈详解
传统蓝牙协议栈包含以下关键组件:
-
物理层(Baseband):
- 负责无线信号调制解调
- 实现跳频序列生成和同步
- 管理物理信道接入
-
链路管理层(LMP):
- 处理设备认证和加密
- 管理链路参数配置
- 控制设备功率模式
-
L2CAP层:
- 提供面向连接和无连接的数据服务
- 支持协议多路复用和分段重组
- 管理服务质量(QoS)参数
-
RFCOMM层:
- 模拟RS-232串口通信
- 支持流控和错误控制
- 为上层应用提供熟悉的串口接口
查找设备
扫描周边蓝牙设备
传统蓝牙设备发现过程基于查询(Inquiry)和查询扫描(Inquiry Scan)机制:
- 查询设备:主设备发送查询包,从设备在查询扫描状态下响应
- 查询响应:从设备返回包含设备地址、类别等信息的FHS包
- 名称请求:主设备获取从设备的友好名称
OpenHarmony提供了简化的API来封装这些底层过程:
import { connection } from '@kit.ConnectivityKit';
import { BusinessError } from '@kit.BasicServicesKit';export class DiscoveryDeviceManager {// 定义扫描结果上报回调函数onReceiveEvent = (data: Array<string>) => {console.info('bluetooth device: '+ JSON.stringify(data));};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);}}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);}}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);}}
}
配对连接设备
蓝牙配对机制详解
蓝牙配对是建立安全连接的关键步骤,主要目的是生成用于加密通信的链路密钥。配对过程包含以下阶段:
- 能力交换:设备交换输入输出能力、认证要求等信息
- 用户认证:根据设备能力执行认证步骤(如PIN码输入、数字比较等)
- 密钥生成:基于ECDH算法生成共享密钥
- 链路密钥确认:验证生成的密钥是否正确
蓝牙4.2及以上版本引入了**安全连接(SC)**技术,使用椭圆曲线Diffie-Hellman(ECDH)加密替代传统的PIN码认证,提供更强的安全性。
蓝牙连接管理
蓝牙连接分为多个层次:
-
物理链路(ACL):
- 异步无连接链路,用于数据传输
- 支持主设备与多个从设备同时通信
- 提供流量控制和错误检测
-
逻辑链路(L2CAP):
- 在ACL链路上建立逻辑通道
- 支持协议多路复用
- 提供分段重组和流控功能
-
应用层连接:
- 基于特定协议的连接(如RFCOMM、AVDTP等)
- 面向特定应用场景的通信
配对设备
import { connection, a2dp, hfp, hid, baseProfile, constant } from '@kit.ConnectivityKit';
import { BusinessError } from '@kit.BasicServicesKit';export class PairDeviceManager {device: string = '';pairState: connection.BondState = connection.BondState.BOND_STATE_INVALID;a2dpSrc = a2dp.createA2dpSrcProfile();hfpAg = hfp.createHfpAgProfile();hidHost = hid.createHidHostProfile();// 定义配对状态变化回调函数onBondStateEvent = (data: connection.BondStateParam) => {console.info('pair result: '+ JSON.stringify(data));if (data && data.deviceId == this.device) {this.pairState = data.state; // 保存目标设备的配对状态}};// 发起配对,设备地址可以通过查找设备流程获取public startPair(device: string) {this.device = device;try {// 发起订阅配对状态变化事件connection.on('bondStateChange', this.onBondStateEvent);} catch (err) {console.error('bondStateChange errCode: ' + (err as BusinessError).code + ', errMessage: ' + (err as BusinessError).message);}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);}}// 定义A2DP连接状态变化回调函数onA2dpConnectStateChange = (data: baseProfile.StateChangeParam) => {console.info(`A2DP State: ${JSON.stringify(data)}`);};// 定义HFP连接状态变化回调函数onHfpConnectStateChange = (data: baseProfile.StateChangeParam) => {console.info(`HFP State: ${JSON.stringify(data)}`);};// 定义HID连接状态变化回调函数onHidConnectStateChange = (data: baseProfile.StateChangeParam) => {console.info(`HID State: ${JSON.stringify(data)}`);};// 发起连接public async connect(device: string) {try {let uuids = await connection.getRemoteProfileUuids(device);console.info('device: ' + device + ' remoteUuids: '+ JSON.stringify(uuids));let allowedProfiles = 0;// 若存在应用需要的profile,则监听对应的profile连接状态if (uuids.some(uuid => uuid == constant.ProfileUuids.PROFILE_UUID_A2DP_SINK.toLowerCase())) {console.info('device supports a2dp');allowedProfiles++;this.a2dpSrc.on('connectionStateChange', this.onA2dpConnectStateChange);}if (uuids.some(uuid => uuid == constant.ProfileUuids.PROFILE_UUID_HFP_HF.toLowerCase())) {console.info('device supports hfp');allowedProfiles++;this.hfpAg.on('connectionStateChange', this.onHfpConnectStateChange);}if (uuids.some(uuid => uuid == constant.ProfileUuids.PROFILE_UUID_HID.toLowerCase()) ||uuids.some(uuid => uuid == constant.ProfileUuids.PROFILE_UUID_HOGP.toLowerCase())) {console.info('device supports hid');allowedProfiles++;this.hidHost.on('connectionStateChange', this.onHidConnectStateChange);}if (allowedProfiles > 0) { // 若存在可用的profile,则发起连接connection.connectAllowedProfiles(device).then(() => {console.info('connectAllowedProfiles');}, (error: BusinessError) => {console.error('errCode:' + error.code + ',errMessage' + error.message);});}} catch (err) {console.error('errCode:' + err.code + ',errMessage' + err.message);}}
}
连接和传输数据
SPP客户端实现
SPP(Serial Port Profile)是传统蓝牙中最常用的数据传输协议,它基于RFCOMM协议模拟串口通信。SPP通信流程如下:
- 服务发现:客户端查询设备支持的SPP服务
- RFCOMM连接:建立RFCOMM通道
- 数据传输:通过RFCOMM通道进行双向数据传输
- 连接断开:释放RFCOMM通道
以下是一个完整的SPP客户端实现示例:
import { socket } from '@kit.ConnectivityKit';
import { BusinessError } from '@kit.BasicServicesKit';export class SppClient {private clientNumber: number = -1;private readCallback: (data: ArrayBuffer) => void;constructor(readCallback: (data: ArrayBuffer) => void) {this.readCallback = readCallback;}// 发起连接public connect(device: string, uuid: string) {// 配置连接参数let option: socket.SppOptions = {uuid: uuid,secure: false,type: socket.SppType.SPP_RFCOMM};console.info('startConnect ' + device);socket.sppConnect(device, option, (err, num: number) => {if (err) {console.error('startConnect errCode: ' + (err as BusinessError).code + ', errMessage: ' + (err as BusinessError).message);} else {console.info('startConnect clientNumber: ' + num);this.clientNumber = num;// 订阅读取数据事件this.subscribeRead();}});}// 订阅读取数据事件private subscribeRead() {try {socket.on('sppRead', this.clientNumber, this.readCallback);} catch (err) {console.error('readData errCode: ' + (err as BusinessError).code + ', errMessage: ' + (err as BusinessError).message);}}// 发送数据public write(data: ArrayBuffer) {try {socket.sppWrite(this.clientNumber, data);} catch (err) {console.error('errCode: ' + (err as BusinessError).code + ', errMessage: ' + (err as BusinessError).message);}}// 断开连接public disconnect() {try {// 取消接收数据订阅socket.off('sppRead', this.clientNumber, this.readCallback);} 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('errCode: ' + (err as BusinessError).code + ', errMessage: ' + (err as BusinessError).message);}}
}
SPP服务端实现
SPP服务端负责监听来自客户端的连接请求,并建立RFCOMM通道进行数据传输。SPP服务端工作流程如下:
- 创建服务端套接字:注册SPP服务,指定UUID和通道参数
- 监听连接请求:等待客户端连接
- 接受连接:建立RFCOMM通道
- 数据传输:通过RFCOMM通道进行双向数据传输
- 断开连接:释放RFCOMM通道和服务端套接字
以下是一个完整的SPP服务端实现示例:
import { socket } from '@kit.ConnectivityKit';
import { BusinessError } from '@kit.BasicServicesKit';export class SppServer {private serverNumber: number = -1;private clientNumber: number = -1;private readCallback: (data: ArrayBuffer) => void;constructor(readCallback: (data: ArrayBuffer) => void) {this.readCallback = readCallback;}// 创建服务端套接字public createServer(serviceName: string, uuid: string) {// 配置监听参数let option: socket.SppOptions = {uuid: uuid,secure: false,type: socket.SppType.SPP_RFCOMM};// 创建服务端监听socket,将在蓝牙子系统中注册该UUID服务socket.sppListen(serviceName, option, (err, num: number) => {if (err) {console.error('sppListen errCode: ' + (err as BusinessError).code + ', errMessage: ' + (err as BusinessError).message);} else {console.info('sppListen serverNumber: ' + num);this.serverNumber = num;// 开始监听客户端连接this.acceptConnection();}});}// 监听客户端连接private acceptConnection() {socket.sppAccept(this.serverNumber, (err, num: number) => {if (err) {console.error('accept errCode: ' + (err as BusinessError).code + ', errMessage: ' + (err as BusinessError).message);} else {console.info('accept clientNumber: ' + num);this.clientNumber = num;// 订阅读取数据事件this.subscribeRead();}});}// 订阅读取数据事件private subscribeRead() {try {socket.on('sppRead', this.clientNumber, this.readCallback);} catch (err) {console.error('readData errCode: ' + (err as BusinessError).code + ', errMessage: ' + (err as BusinessError).message);}}// 发送数据public write(data: ArrayBuffer) {try {socket.sppWrite(this.clientNumber, data);} catch (err) {console.error('sppWrite errCode: ' + (err as BusinessError).code + ', errMessage: ' + (err as BusinessError).message);}}// 断开连接public disconnect() {try {// 取消接收数据订阅socket.off('sppRead', this.clientNumber, this.readCallback);} catch (err) {console.error('off sppRead errCode: ' + (err as BusinessError).code + ', errMessage: ' + (err as BusinessError).message);}try {// 从server端断开连接socket.sppCloseServerSocket(this.serverNumber);} catch (err) {console.error('errCode: ' + (err as BusinessError).code + ', errMessage: ' + (err as BusinessError).message);}}
}
低功耗蓝牙开发
低功耗蓝牙(BLE)是蓝牙4.0引入的革命性技术,专为低功耗应用场景设计。与传统蓝牙相比,BLE在保持相似通信范围的同时,大幅降低了功耗,使其成为物联网、可穿戴设备等领域的理想选择。
BLE技术特点
BLE具有以下显著特点:
-
超低功耗:
- 待机电流低至微安级别
- 通信时电流消耗仅为传统蓝牙的十分之一
- 支持深度睡眠和快速唤醒机制
-
快速连接:
- 连接建立时间仅需几毫秒
- 适合间歇性数据传输场景
-
简化协议栈:
- 协议栈更简单,降低了实现复杂度
- 减少了处理和内存需求
-
广播机制:
- 支持一对多的无连接广播
- 适合信标(Beacon)等应用场景
BLE通信原理
BLE通信基于**GAP(通用访问配置文件)和GATT(通用属性配置文件)**两个核心协议:
GAP(通用访问配置文件)
GAP定义了设备发现、连接建立和安全等基本功能:
-
设备角色:
- 广播者(Broadcaster):只发送广播数据,不接收连接
- 观察者(Observer):扫描广播数据,不发起连接
- 外围设备(Peripheral):可被连接的设备
- 中央设备(Central):发起连接的设备
-
广播过程:
- 设备在37、38、39三个广播信道上发送广播包
- 广播包包含设备地址、标志位、广播数据等信息
- 广播间隔可配置,通常为20ms到10.24s
-
扫描过程:
- 主动扫描:发送扫描请求,获取扫描响应
- 被动扫描:仅接收广播数据,不发送请求
GATT(通用属性配置文件)
GATT定义了基于属性的数据传输模型:
-
属性协议(ATT):
- 轻量级协议,用于在BLE链路上传输属性
- 属性由句柄、类型和值三部分组成
- 支持查找、读取、写入、通知和指示操作
-
服务、特征和描述符:
- 服务(Service):一组相关特征的集合
- 特征(Characteristic):数据值及其描述信息
- 描述符(Descriptor):特征的元数据信息
-
数据传输方式:
- 读取(Read):中央设备从外围设备读取数据
- 写入(Write):中央设备向外围设备写入数据
- 通知(Notification):外围设备主动发送数据,无需确认
- 指示(Indication):外围设备主动发送数据,需要确认
BLE连接管理
BLE连接管理包含以下关键参数:
-
连接参数:
- 连接间隔(Connection Interval):两次连接事件之间的时间,范围7.5ms-4s
- 从设备延迟(Slave Latency):从设备可跳过的连续连接事件数量
- 监督超时(Supervision Timeout):连接断开前允许的最大通信间隔
-
连接事件:
- 在每个连接事件中,中央设备和外围设备交换数据包
- 连接事件持续时间由数据包数量和大小决定
- 无数据传输时可提前结束连接事件以节省功耗
-
数据长度扩展:
- BLE 4.2支持数据长度扩展,最大数据包长度从27字节增加到251字节
- 减少了数据包开销,提高了传输效率
BLE安全机制
BLE提供多层次的安全保护:
-
设备认证:
- Just Works:无用户交互,适用于低安全需求场景
- Passkey Entry:用户输入6位数字,适用于有显示屏和键盘的设备
- Out of Band:通过其他通信通道交换认证信息
-
数据加密:
- 使用AES-CCM加密算法
- 128位加密密钥
- 每个连接使用不同的会话密钥
-
隐私保护:
- 设备可使用随机可解析地址替代真实地址
- 地址定期更新,防止设备被跟踪
查找设备
BLE扫描实现
BLE扫描过程与传统蓝牙设备发现有显著不同:
- 扫描类型:主动扫描和被动扫描
- 扫描窗口和间隔:控制扫描占空比和功耗
- 过滤器:根据设备地址、服务UUID等过滤扫描结果
OpenHarmony提供了简化的API来封装这些底层过程:
import { ble } from '@kit.ConnectivityKit';
import { BusinessError } from '@kit.BasicServicesKit';export class BleScanner {private scanning: boolean = false;// 定义扫描结果上报回调函数private onReceiveEvent = (data: Array<ble.ScanResult>) => {console.info('bluetooth device: '+ JSON.stringify(data));};// 开始扫描public startScan() {try {// 订阅扫描结果ble.on('BLEDeviceFind', this.onReceiveEvent);// 开始扫描ble.startBLEScan();this.scanning = true;} catch (err) {console.error('errCode: ' + (err as BusinessError).code + ', errMessage: ' + (err as BusinessError).message);}}// 停止扫描public stopScan() {if (this.scanning) {try {// 停止扫描ble.stopBLEScan();// 取消订阅ble.off('BLEDeviceFind', this.onReceiveEvent);this.scanning = false;} catch (err) {console.error('errCode: ' + (err as BusinessError).code + ', errMessage: ' + (err as BusinessError).message);}}}
}
连接和传输数据
GATT客户端实现
GATT客户端是BLE通信中的中央设备角色,负责发起连接、发现服务、读写特征值等操作。GATT客户端工作流程如下:
- 设备扫描:发现周围BLE设备
- 连接建立:与目标设备建立BLE连接
- 服务发现:查询设备支持的服务
- 特征发现:查询服务包含的特征和描述符
- 数据交互:读取、写入特征值或订阅通知
- 连接断开:终止BLE连接
以下是一个完整的GATT客户端实现示例:
import { ble } from '@kit.ConnectivityKit';
import { BusinessError } from '@kit.BasicServicesKit';export class GattClient {private gattClient: ble.GattClientDevice;private connected: boolean = false;constructor(device: string) {this.gattClient = ble.createGattClientDevice(device);this.setupEventHandlers();}// 设置事件处理器private setupEventHandlers() {// 订阅连接状态变化事件this.gattClient.on('BLEConnectionStateChange', (state: ble.BLEConnectionChangeState) => {console.info('bluetooth connect state changed');let connectState: ble.ProfileConnectionState = state.state;this.connected = (connectState === ble.ProfileConnectionState.STATE_CONNECTED);});}// 发起连接public connect() {try {this.gattClient.connect();} catch (err) {console.error('errCode: ' + (err as BusinessError).code + ', errMessage: ' + (err as BusinessError).message);}}// 断开连接public disconnect() {try {this.gattClient.disconnect();} catch (err) {console.error('errCode: ' + (err as BusinessError).code + ', errMessage: ' + (err as BusinessError).message);}}// 服务发现public async getServices(): Promise<Array<ble.GattService>> {try {return await this.gattClient.getServices();} catch (err) {console.error('errCode: ' + (err as BusinessError).code + ', errMessage: ' + (err as BusinessError).message);return [];}}// 读取特征值public readCharacteristicValue(characteristic: ble.BLECharacteristic): Promise<ble.BLECharacteristic> {return new Promise((resolve, reject) => {try {this.gattClient.readCharacteristicValue(characteristic).then((result: ble.BLECharacteristic) => {resolve(result);}).catch((error: BusinessError) => {reject(error);});} catch (err) {reject(err);}});}// 写入特征值public writeCharacteristicValue(characteristic: ble.BLECharacteristic, writeType: ble.GattWriteType = ble.GattWriteType.WRITE): Promise<void> {return new Promise((resolve, reject) => {try {this.gattClient.writeCharacteristicValue(characteristic, writeType, (err) => {if (err) {reject(err);} else {resolve();}});} catch (err) {reject(err);}});}// 订阅特征值变化通知public subscribeCharacteristicChange(callback: (characteristic: ble.BLECharacteristic) => void) {try {this.gattClient.on('BLECharacteristicChange', callback);} catch (err) {console.error('errCode: ' + (err as BusinessError).code + ', errMessage: ' + (err as BusinessError).message);}}// 取消订阅特征值变化通知public unsubscribeCharacteristicChange(callback: (characteristic: ble.BLECharacteristic) => void) {try {this.gattClient.off('BLECharacteristicChange', callback);} catch (err) {console.error('errCode: ' + (err as BusinessError).code + ', errMessage: ' + (err as BusinessError).message);}}// 启用特征值变化通知public setCharacteristicChangeNotification(characteristic: ble.BLECharacteristic, enable: boolean): Promise<void> {return new Promise((resolve, reject) => {try {this.gattClient.setCharacteristicChangeNotification(characteristic, enable, (err) => {if (err) {reject(err);} else {resolve();}});} catch (err) {reject(err);}});}// 启用特征值变化指示public setCharacteristicChangeIndication(characteristic: ble.BLECharacteristic, enable: boolean): Promise<void> {return new Promise((resolve, reject) => {try {this.gattClient.setCharacteristicChangeIndication(characteristic, enable, (err) => {if (err) {reject(err);} else {resolve();}});} catch (err) {reject(err);}});}
}
GATT服务端实现
import { ble } from '@kit.ConnectivityKit';
import { BusinessError } from '@kit.BasicServicesKit';export class GattServer {private gattServer: ble.GattServer;constructor() {this.gattServer = ble.createGattServer();this.setupEventHandlers();}// 设置事件处理器private setupEventHandlers() {// 订阅连接状态变化事件this.gattServer.on('BLEConnectionStateChange', (state: ble.BLEConnectionChangeState) => {console.info('bluetooth connect state changed');let connectState: ble.ProfileConnectionState = state.state;});// 订阅特征值读取请求this.gattServer.on('readCharacteristic', (request: ble.ReadRequest) => {console.info('readCharacteristic request: ' + JSON.stringify(request));});// 订阅特征值写入请求this.gattServer.on('writeCharacteristic', (request: ble.WriteRequest) => {console.info('writeCharacteristic request: ' + JSON.stringify(request));});// 订阅描述符读取请求this.gattServer.on('readDescriptor', (request: ble.ReadRequest) => {console.info('readDescriptor request: ' + JSON.stringify(request));});// 订阅描述符写入请求this.gattServer.on('writeDescriptor', (request: ble.WriteRequest) => {console.info('writeDescriptor request: ' + JSON.stringify(request));});}// 添加服务public addService(service: ble.GattService): Promise<boolean> {return new Promise((resolve, reject) => {try {this.gattServer.addService(service, (err, status) => {if (err) {reject(err);} else {resolve(status === ble.GattServiceStatus.SERVICE_ADDED);}});} catch (err) {reject(err);}});}// 移除服务public removeService(serviceUuid: string): Promise<boolean> {return new Promise((resolve, reject) => {try {this.gattServer.removeService(serviceUuid, (err, status) => {if (err) {reject(err);} else {resolve(status === ble.GattServiceStatus.SERVICE_REMOVED);}});} catch (err) {reject(err);}});}// 清除所有服务public clearServices(): Promise<void> {return new Promise((resolve, reject) => {try {this.gattServer.clearServices((err) => {if (err) {reject(err);} else {resolve();}});} catch (err) {reject(err);}});}// 发送响应public sendResponse(response: ble.ServerResponse): Promise<void> {return new Promise((resolve, reject) => {try {this.gattServer.sendResponse(response, (err) => {if (err) {reject(err);} else {resolve();}});} catch (err) {reject(err);}});}// 通知特征值变化public notifyCharacteristicChanged(notify: ble.NotifyCharacteristicChanged): Promise<void> {return new Promise((resolve, reject) => {try {this.gattServer.notifyCharacteristicChanged(notify, (err) => {if (err) {reject(err);} else {resolve();}});} catch (err) {reject(err);}});}// 启动广播public startAdvertising(setting: ble.AdvertiseSetting, advData: ble.AdvertiseData, scanResponse?: ble.AdvertiseData): Promise<void> {return new Promise((resolve, reject) => {try {this.gattServer.startAdvertising(setting, advData, scanResponse, (err) => {if (err) {reject(err);} else {resolve();}});} catch (err) {reject(err);}});}// 停止广播public stopAdvertising(): Promise<void> {return new Promise((resolve, reject) => {try {this.gattServer.stopAdvertising((err) => {if (err) {reject(err);} else {resolve();}});} catch (err) {reject(err);}});}
}
开发实践与注意事项
蓝牙通信安全考虑
-
数据加密:
- 使用蓝牙内置加密机制
- 对敏感数据进行应用层加密
- 定期更新加密密钥
-
身份认证:
- 实现设备身份验证机制
- 使用安全配对方式
- 避免使用固定PIN码
-
访问控制:
- 实现细粒度的权限控制
- 限制对敏感服务的访问
- 记录和审计访问日志
蓝牙开发常见问题
-
连接不稳定:
- 检查设备距离和干扰源
- 调整连接参数
- 实现自动重连机制
-
数据传输异常:
- 检查数据包大小限制
- 实现数据分包和重组
- 添加校验机制
-
兼容性问题:
- 测试不同品牌设备兼容性
- 遵循蓝牙规范实现
- 提供降级兼容方案
蓝牙技术发展趋势
-
蓝牙5.x系列:
- 更远的传输距离(可达300米)
- 更高的传输速度(2Mbps)
- 更大的广播数据容量
- 方向定位功能
-
蓝牙音频技术:
- LC3编解码器
- 多音频流同时传输
- 助听器支持
-
蓝牙网络技术:
- 蓝牙网状网络(Mesh)
- 大规模设备互联
- 自组织网络
-
蓝牙与物联网融合:
- 低功耗长距离传输
- 边缘计算集成
- 5G与蓝牙协同工作
