文章目录
一、概述
蓝牙技术是一种无线通信技术,可以在短距离内传输数据。它是由爱立信公司于1994年提出的,使用2.4 GHz的ISM频段,可以在10米左右的距离内进行通信。可以用于连接手机、耳机、音箱、键盘、鼠标、打印机等各种设备。特点是低功耗、低成本、简单易用。目前已经发展到了第五代,支持更高的数据传输速率和更广的覆盖范围。
二、实现原理
蓝牙的实现原理是基于无线电技术的短距离通信协议,使用2.4GHz频段的无线电波进行通信,使用频率跳跃技术(Frequency Hopping Spread Spectrum,FHSS)来避免与其他无线设备的干扰。在通信过程中,蓝牙设备会发送和接收数据包,并且使用不同的蓝牙协议来控制通信流程和数据传输。
三、模块介绍
-
a2dp模块(高级音频分发配置文件):A2DP是Advanced Audio Distribution Profile的缩写,即高级音频分发配置文件。它是一种蓝牙协议,允许无线传输高品质音频流,例如音乐或语音通话,同时支持双向通信,因此可以用于耳机、扬声器、汽车音响等设备。详情请参考@ohos.bluetooth.a2dp API参考。
-
ble模块(低功耗蓝牙):BLE是Bluetooth Low Energy的缩写,意为“低功耗蓝牙”。它是一种能够在低功耗情况下进行通信的蓝牙技术,与传统蓝牙相比,BLE的功耗更低,适用于需要长时间运行的低功耗设备,如智能手表、健康监测设备、智能家居等。详情请参考@ohos.bluetooth.ble API参考。
-
hfp模块(免提模式):HFP模块是指蓝牙耳机或车载蓝牙设备中的Hands-Free Profile,即“免提模式”。HFP允许用户通过蓝牙连接手机或其他蓝牙设备,实现免提通话和语音控制等功能。详情请参考@ohos.bluetooth.hfp API参考。
-
hid模块(人机接口设备):HID是Human Interface Device的缩写,即人机接口设备。在蓝牙中,HID模块是一种允许用户通过蓝牙连接键盘、鼠标、游戏手柄等人机接口设备的模块。用户可以通过HID模块将这些设备连接到蓝牙主机上,实现无线控制和输入。详情请参考@ohos.bluetooth.hid API参考。
-
pan模块(个人区域网络):PAN(Personal Area Network)是个人区域网络的缩写,是一种无线通信技术,用于将设备连接到一个小范围的网络中。例如可以将手机、电脑、打印机等设备连接到一个PAN网络中,实现文件的共享和打印。详情请参考@ohos.bluetooth.pan API参考。
-
gatt模块(通用属性):GATT是指蓝牙技术中的通用属性(Generic Attribute),它是一种用于在蓝牙低功耗设备之间传输数据的协议。GATT协议定义了一套通用的属性和服务框架,用于描述蓝牙设备之间的通信,同时蓝牙设备可以向其他设备提供服务,也可以从其他设备获取服务。详情请参考@ohos.bluetooth.ble API参考。
-
spp模块(串口协议):SPP是Serial Port Profile(串口协议)的缩写,是一种蓝牙协议,用于在蓝牙设备之间建立串行通信连接。通过SPP,蓝牙设备可以像使用串口一样进行数据传输,例如传输文件、文本等。详情请参考@ohos.bluetooth.socket API参考。
四、蓝牙权限
1、需要的权限
// module.json5
"requestPermissions":[
{
"name": "ohos.permission.INTERNET"
},
{
"name": "ohos.permission.USE_BLUETOOTH"
},
{
"name": "ohos.permission.DISCOVER_BLUETOOTH"
},
{
"name": "ohos.permission.ACCESS_BLUETOOTH",
"reason": "$string:grant_location",
"usedScene": {
"abilities": [
"EntryAbility"
],
"when": "inuse"
}
},
{
"name": "ohos.permission.LOCATION",
"reason": "$string:grant_location",
"usedScene": {
"abilities": [
"EntryAbility"
],
"when": "inuse"
}
}
]
2、检查是否授权
import { permissionManager } from '../manager';
import { Permissions } from '@kit.AbilityKit';
import { promptAction } from '@kit.ArkUI';
aboutToAppear(): void {
this.checkPermissions()
}
//检查是否授权
checkPermissions() {
const permissions: Permissions[] = ['ohos.permission.ACCESS_BLUETOOTH']
if (permissionManager.checkPermissions(permissions)) {
// 已授权
} else {
// 未授权,弹窗授权
this.requestPermission(permissions)
}
}
// 申请权限
async requestPermission(permissions: Permissions[]) {
// 申请权限
try {
await permissionManager.requestPermissions(permissions)
// 允许
} catch (error) {
// 禁止 -> 引导用户打开设置页开启权限
// 如果禁止授权,先弹窗询问,用户点击按钮后打开设置页
promptAction.showDialog({
alignment: DialogAlignment.Center,
title: '温馨提示',
message: '建议开启蓝牙权限,以提供更好的体验',
buttons: [
{ text: '取消', color: '#89939c' },
{ text: '去设置', color: '#0a59f7' },
]
})
.then((res) => {
// 用户点击了去设置
if (res.index === 1) {
// 通过代码打开当前应用的设置页
permissionManager.openPermissionSettingsPage()
}
})
}
}
五、蓝牙扫描
1、监听扫描结果
ble.on(‘BLEDeviceFind’)
订阅BLE设备发现上报事件。使用Callback异步回调。
监听我们扫描的结果,一旦开始扫描,发现ble低功耗蓝牙设备。
on(type: 'BLEDeviceFind', callback: Callback<Array<ScanResult>>): void
- ScanResult
扫描结果数据
名称 | 类型 | 可读 | 可写 | 说明 |
---|---|---|---|---|
deviceId | string | 是 | 否 | 表示扫描到的设备地址,例如:“XX:XX:XX:XX:XX:XX”。基于信息安全考虑,此处获取的设备地址为随机MAC地址。配对成功后,该地址不会变更;已配对设备取消配对后重新扫描或蓝牙服务下电时,该随机地址会变更。 |
rssi | number | 是 | 否 | 表示扫描到的设备的rssi值。 |
data | ArrayBuffer | 是 | 否 | 表示扫描到的设备发送的广播包。 |
deviceName | string | 是 | 否 | 表示扫描到的设备名称。 |
connectable | boolean | 是 | 否 | 表示扫描到的设备是否可连接。true表示可连接,false表示不可连接。 |
- 示例
点击按钮后调用openBle这个函数,这个函数里面会开启蓝牙扫描。
import { ble } from '@kit.ConnectivityKit';
import { BusinessError } from '@kit.BasicServicesKit';
@Entry
@Component
struct BlePage {
// 蓝牙扫描结果
onReceiveEvent(data: Array<ble.ScanResult>) {
console.info('bluetooth device find = ' + JSON.stringify(data));
}
// 开启扫描
openBel(): void {
try {
// 监听扫描结果
ble.on("BLEDeviceFind", this.onReceiveEvent)
} catch (err) {
console.error('errCode: ' + (err as BusinessError).code + ', errMessage: ' + (err as BusinessError).message);
}
}
build() {
Column() {
Button('蓝牙扫描').onClick(() => {
this.openBel()
})
}
.height('100%')
.width('100%')
}
}
2、启动扫描
ble.startBLEScan
发起BLE扫描流程。启动扫描。
startBLEScan(filters: Array<ScanFilter>, options?: ScanOptions): void
- ScanOptions
扫描的配置参数
名称 | 类型 | 可读 | 可写 | 说明 |
---|---|---|---|---|
interval | number | 是 | 是 | 表示扫描结果上报延迟时间,默认值为0。 |
dutyMode | ScanDuty | 是 | 是 | 表示扫描模式,默认值为SCAN_MODE_LOW_POWER。 |
matchMode | MatchMode | 是 | 是 | 表示硬件的过滤匹配模式,默认值为MATCH_MODE_AGGRESSIVE。 |
phyType | PhyType | 是 | 是 | 表示扫描中使用的PHY类型。 |
- 示例
import { ble } from '@kit.ConnectivityKit';
import { BusinessError } from '@kit.BasicServicesKit';
@Entry
@Component
struct BlePage {
// 信号强度阈值
private minRssi = -100
// 扫描到的设备列表
@State availableDevices: Array<ble.ScanResult> = []
//蓝牙扫描结果
onReceiveEvent(data: Array<ble.ScanResult>) {
console.info('bluetooth device find = ' + JSON.stringify(data));
data.forEach(element => {
// 信号强的并且有设备名称的保存起来
// rssi无线信号强度的指标,值越大信号越强,设备离得越近,反之表示设备较远,信号较弱。
if (element.rssi > this.minRssi && element.deviceName != '') {
// 符合条件的设备保存起来
this.addData(element)
}
})
}
// 保存符合条件的设备
addData(data: ble.ScanResult): void {
let bleFind = false
// 更新
this.availableDevices.forEach(element => {
if (!bleFind && element.deviceId == data.deviceId) {
console.log('BLE scan update ' + data.deviceId + ' rssi:' + data.rssi);
element.rssi = data.rssi
bleFind = true
}
})
// 新增
if(!bleFind){
console.log('BLE scan add ' + data.deviceId + ' rssi:' + data.rssi);
this.availableDevices.push(data)
}
}
// 开启扫描
openBel(): void {
try {
// 监听扫描结果
ble.on("BLEDeviceFind", this.onReceiveEvent)
let scanOptions: ble.ScanOptions = {
interval: 500,
dutyMode: ble.ScanDuty.SCAN_MODE_LOW_POWER,
matchMode: ble.MatchMode.MATCH_MODE_AGGRESSIVE
}
// 开启扫描
ble.startBLEScan(null, scanOptions)
} catch (err) {
console.error('errCode: ' + (err as BusinessError).code + ', errMessage: ' + (err as BusinessError).message);
}
}
build() {
Column() {
Button('蓝牙扫描').onClick(() => {
this.openBel()
})
}
.height('100%')
.width('100%')
}
}
六、连接设备
1、蓝牙连接
import { ble } from '@kit.ConnectivityKit';
import { BusinessError } from '@kit.BasicServicesKit';
@Entry
@Component
struct BlePage {
// 蓝牙连接
connectBel(): void {
try {
// 创建一个可使用的GattClientDevice实例。
// 对端设备地址, 例如:"XX:XX:XX:XX:XX:XX"。
let device: ble.GattClientDevice = ble.createGattClientDevice('XX:XX:XX:XX:XX:XX');
// client端发起连接远端蓝牙低功耗设备。
device.connect();
} catch (err) {
console.error('errCode: ' + (err as BusinessError).code + ', errMessage: ' + (err as BusinessError).message);
}
}
build() {
Column() {
Button('蓝牙连接').onClick(() => {
this.connectBel()
})
}
.height('100%')
.width('100%')
}
}
2、监听连接状态
import { ble } from '@kit.ConnectivityKit';
import { BusinessError } from '@kit.BasicServicesKit';
@Entry
@Component
struct BlePage {
// 蓝牙连接
connectBel(): void {
try {
// 创建一个可使用的GattClientDevice实例。
// 对端设备地址, 例如:"XX:XX:XX:XX:XX:XX"。
let device: ble.GattClientDevice = ble.createGattClientDevice('XX:XX:XX:XX:XX:XX');
// client端发起连接远端蓝牙低功耗设备。
device.connect();
// 监听蓝牙连接状态
device.on('BLEConnectionStateChange', (state: ble.BLEConnectionChangeState)=>{
let connectState: ble.ProfileConnectionState = state.state;
if(connectState === 2){
console.info('bluetooth connect state changed');
}
});
} catch (err) {
console.error('errCode: ' + (err as BusinessError).code + ', errMessage: ' + (err as BusinessError).message);
}
}
build() {
Column() {
Button('蓝牙连接').onClick(() => {
this.connectBel()
})
}
.height('100%')
.width('100%')
}
}
- ProfileConnectionState
枚举,蓝牙设备的profile连接状态。
名称 | 值 | 说明 |
---|---|---|
STATE_DISCONNECTED | 0 | 表示profile已断连。 |
STATE_CONNECTING | 1 | 表示profile正在连接。 |
STATE_CONNECTED | 2 | 表示profile已连接。 |
STATE_DISCONNECTING | 3 | 表示profile正在断连。 |
七、获取服务
- getServices
client端获取蓝牙低功耗设备的所有服务,即服务发现。使用Promise异步回调。
getServices(): Promise<Array<GattService>>
返回值
名称 | 类型 | 可读 | 可写 | 说明 |
---|---|---|---|---|
serviceUuid | string | 是 | 是 | 特定服务(service)的UUID,例如:00001888-0000-1000-8000-00805f9b34fb。 |
isPrimary | boolean | 是 | 是 | 如果是主服务设置为true,否则设置为false。 |
characteristics | Array | 是 | 是 | 当前服务包含的特征列表。 |
includeServices | Array | 是 | 是 | 当前服务依赖的其它服务。 |
import { ble } from '@kit.ConnectivityKit';
import { BusinessError } from '@kit.BasicServicesKit';
@Entry
@Component
struct BlePage {
// 蓝牙连接
connectBel(): void {
try {
// 创建一个可使用的GattClientDevice实例。
// 对端设备地址, 例如:"XX:XX:XX:XX:XX:XX"。
let device: ble.GattClientDevice = ble.createGattClientDevice('XX:XX:XX:XX:XX:XX');
// client端发起连接远端蓝牙低功耗设备。
device.connect();
// 监听蓝牙连接状态
device.on('BLEConnectionStateChange', (state: ble.BLEConnectionChangeState)=>{
let connectState: ble.ProfileConnectionState = state.state;
if(connectState === 2){
console.info('bluetooth connect state changed');
// client端获取蓝牙低功耗设备的所有服务,即服务发现。
device.getServices().then((result: Array<ble.GattService>) => {
console.info('getServices successfully:' + JSON.stringify(result));
});
}
});
} catch (err) {
console.error('errCode: ' + (err as BusinessError).code + ', errMessage: ' + (err as BusinessError).message);
}
}
build() {
Column() {
Button('蓝牙连接').onClick(() => {
this.connectBel()
})
}
.height('100%')
.width('100%')
}
}
八、接收数据
1、接收一次
- readCharacteristicValue
client端读取蓝牙低功耗设备特定服务的特征值。使用Callback异步回调。
readCharacteristicValue(characteristic: BLECharacteristic, callback: AsyncCallback<BLECharacteristic>): void
参数:
参数名 | 类型 | 必填 | 说明 |
---|---|---|---|
characteristic | BLECharacteristic | 是 | 待读取的特征值。 |
callback | AsyncCallback | 是 | client读取特征值,通过注册回调函数获取。 |
// 接收数据
receiveData(): void {
let descriptors: Array<ble.BLEDescriptor> = [];
let bufferDesc = new ArrayBuffer(8);
let descV = new Uint8Array(bufferDesc);
descV[0] = 11;
let descriptor: ble.BLEDescriptor = {
serviceUuid: '00001810-0000-1000-8000-00805F9B34FB',
characteristicUuid: '00001820-0000-1000-8000-00805F9B34FB',
descriptorUuid: '00002903-0000-1000-8000-00805F9B34FB',
descriptorValue: bufferDesc
};
descriptors[0] = descriptor;
let bufferCCC = new ArrayBuffer(8);
let cccV = new Uint8Array(bufferCCC);
cccV[0] = 1;
let characteristic: ble.BLECharacteristic = {
serviceUuid: '00001810-0000-1000-8000-00805F9B34FB',
characteristicUuid: '00001820-0000-1000-8000-00805F9B34FB',
characteristicValue: bufferCCC,
descriptors: descriptors
};
try {
let device: ble.GattClientDevice = ble.createGattClientDevice('XX:XX:XX:XX:XX:XX');
device.readCharacteristicValue(characteristic, this.readCcc);
} catch (err) {
console.error('errCode: ' + (err as BusinessError).code + ', errMessage: ' + (err as BusinessError).message);
}
}
// 接收数据的回调函数
readCcc(code: BusinessError, BLECharacteristic: ble.BLECharacteristic) {
if (code.code != 0) {
return;
}
console.info('bluetooth characteristic uuid: ' + BLECharacteristic.characteristicUuid);
let value = new Uint8Array(BLECharacteristic.characteristicValue);
console.info('bluetooth characteristic value: ' + value[0] + ',' + value[1] + ',' + value[2] + ',' + value[3]);
}
2、间隔接收
- on(‘BLECharacteristicChange’)
订阅蓝牙低功耗设备的特征值变化事件。
on(type: 'BLECharacteristicChange', callback: Callback<BLECharacteristic>): void
参数:
参数名 | 类型 | 必填 | 说明 |
---|---|---|---|
type | string | 是 | 填写"BLECharacteristicChange"字符串,表示特征值变化事件。 |
callback | Callback | 是 | 表示蓝牙低功耗设备的特征值变化事件的回调函数。 |
- setCharacteristicChangeNotification
向服务端发送设置通知此特征值请求。使用Callback异步回调。
setCharacteristicChangeNotification(characteristic: BLECharacteristic, enable: boolean, callback: AsyncCallback<void>): void
参数:
参数名 | 类型 | 必填 | 说明 |
---|---|---|---|
characteristic | BLECharacteristic | 是 | 蓝牙低功耗特征。 |
enable | boolean | 是 | 启用接收notify设置为true,否则设置为false。 |
callback | AsyncCallback | 是 | 回调函数。当发送成功,err为undefined,否则为错误对象。 |
// 间隔接收
monitorData(): void {
// 创建descriptors
let descriptors: Array<ble.BLEDescriptor> = [];
let arrayBuffer = new ArrayBuffer(8);
let descV = new Uint8Array(arrayBuffer);
descV[0] = 11;
let descriptor: ble.BLEDescriptor = {
serviceUuid: '00001810-0000-1000-8000-00805F9B34FB',
characteristicUuid: '00001820-0000-1000-8000-00805F9B34FB',
descriptorUuid: '00002902-0000-1000-8000-00805F9B34FB',
descriptorValue: arrayBuffer
};
descriptors[0] = descriptor;
let arrayBufferC = new ArrayBuffer(8);
let characteristic: ble.BLECharacteristic = {
serviceUuid: '00001810-0000-1000-8000-00805F9B34FB',
characteristicUuid: '00001820-0000-1000-8000-00805F9B34FB',
characteristicValue: arrayBufferC,
descriptors: descriptors
};
try {
//向服务端发送设置通知此特征值请求。
let device: ble.GattClientDevice = ble.createGattClientDevice('XX:XX:XX:XX:XX:XX');
device.setCharacteristicChangeNotification(characteristic, false);
// 订阅蓝牙低功耗设备的特征值变化事件。
device.on('BLECharacteristicChange', this.CharacteristicChange);
} catch (err) {
console.error('errCode: ' + (err as BusinessError).code + ', errMessage: ' + (err as BusinessError).message);
}
}
// 间隔接收的回调函数
CharacteristicChange(characteristicChangeReq: ble.BLECharacteristic) {
let serviceUuid: string = characteristicChangeReq.serviceUuid;
let characteristicUuid: string = characteristicChangeReq.characteristicUuid;
let value: Uint8Array = new Uint8Array(characteristicChangeReq.characteristicValue);
console.log('serviceUuid: ' + serviceUuid + ', characteristicUuid: ' + characteristicUuid + 'value: ' + value)
}
- BLECharacteristic
返回值
名称 | 类型 | 可读 | 可写 | 说明 |
---|---|---|---|---|
serviceUuid | string | 是 | 是 | 特定服务(service)的UUID,例如:00001888-0000-1000-8000-00805f9b34fb。 |
characteristicUuid | string | 是 | 是 | 特定服务(characteristic)的UUID,例如:00001888-0000-1000-8000-00805f9b34fb。 |
characteristicValue | ArrayBuffer | 是 | 是 | 特征对应的二进制值。 |
descriptors | Array | 是 | 是 | 特定特征的描述符列表。 |
properties | GattProperties | 是 | 是 | 特定特征的属性描述。 |
九、发送数据
- writeCharacteristicValue
client端向低功耗蓝牙设备写入特定的特征值。使用Callback异步回调。
writeCharacteristicValue(characteristic: BLECharacteristic, writeType: GattWriteType, callback: AsyncCallback<void>): void
参数:
参数名 | 类型 | 必填 | 说明 |
---|---|---|---|
characteristic | BLECharacteristic | 是 | 蓝牙设备特征对应的二进制值及其它参数。 |
writeType | GattWriteType | 是 | 蓝牙设备特征的写入类型。 |
callback | AsyncCallback | 是 | 回调函数。当写入成功,err为undefined,否则为错误对象。 |
// 发送数据
sendData(): void {
let descriptors: Array<ble.BLEDescriptor> = [];
let bufferDesc = new ArrayBuffer(8);
let descV = new Uint8Array(bufferDesc);
descV[0] = 11;
let descriptor: ble.BLEDescriptor = {
serviceUuid: '00001810-0000-1000-8000-00805F9B34FB',
characteristicUuid: '00001820-0000-1000-8000-00805F9B34FB',
descriptorUuid: '00002903-0000-1000-8000-00805F9B34FB',
descriptorValue: bufferDesc
};
descriptors[0] = descriptor;
let bufferCCC = new ArrayBuffer(8);
let cccV = new Uint8Array(bufferCCC);
cccV[0] = 1;
let characteristic: ble.BLECharacteristic = {
serviceUuid: '00001810-0000-1000-8000-00805F9B34FB',
characteristicUuid: '00001820-0000-1000-8000-00805F9B34FB',
characteristicValue: bufferCCC,
descriptors: descriptors
};
try {
let device: ble.GattClientDevice = ble.createGattClientDevice('XX:XX:XX:XX:XX:XX');
device.writeCharacteristicValue(characteristic, ble.GattWriteType.WRITE, this.writeCharacteristicValueCallBack);
} catch (err) {
console.error('errCode: ' + (err as BusinessError).code + ', errMessage: ' + (err as BusinessError).message);
}
}
// 发送数据回调
writeCharacteristicValueCallBack(code: BusinessError) {
if (code != null) {
return;
}
console.info('bluetooth writeCharacteristicValue success');
}