1# 查找设备 2 3<!--Kit: Connectivity Kit--> 4<!--Subsystem: Communication--> 5<!--Owner: @enjoy_sunshine--> 6<!--Designer: @chengguohong; @tangjia15--> 7<!--Tester: @wangfeng517--> 8<!--Adviser: @zhang_yixin13--> 9 10## 简介 11本指南主要提供了BLE扫描和BLE广播相关操作的开发指导。可以实现发现周边BLE设备和其他设备发现本机设备的场景。 12 13## 开发步骤 14 15### 申请蓝牙权限 16需要申请权限ohos.permission.ACCESS_BLUETOOTH。如何配置和申请权限,请参考[声明权限](../../security/AccessToken/declare-permissions.md)和[向用户申请授权](../../security/AccessToken/request-user-authorization.md)。 17 18### 导入所需API模块 19导入ble和错误码模块。 20```ts 21import { ble } from '@kit.ConnectivityKit'; 22import { BusinessError } from '@kit.BasicServicesKit'; 23``` 24 25### BLE扫描流程 26 27**1. 订阅扫描结果上报事件**<br> 28- 推荐使用API version 15开始支持的扫描方式,该方式支持应用发起和管理多路扫描。该方式支持的上报事件请参考[on('BLEDeviceFind')](../../reference/apis-connectivity-kit/js-apis-bluetooth-ble.md#onbledevicefind15)。 29```ts 30// 定义扫描结果上报回调函数 31function onReceiveEvent(scanReport: ble.ScanReport) { 32 console.info('BLE scan device find result: '+ JSON.stringify(scanReport)); 33} 34 35// 创建ble扫描实例,可以管理该实例下创建的扫描流程 36let bleScanner: ble.BleScanner = ble.createBleScanner(); 37 38try { 39 // 发起订阅 40 bleScanner.on('BLEDeviceFind', onReceiveEvent); 41} catch (err) { 42 console.error('errCode: ' + (err as BusinessError).code + ', errMessage: ' + (err as BusinessError).message); 43} 44``` 45 46- API version 14及以前支持的扫描方式只支持应用发起单路扫描。该方式支持的上报事件请参考[ble.on('BLEDeviceFind')](../../reference/apis-connectivity-kit/js-apis-bluetooth-ble.md#bleonbledevicefind)。 47```ts 48// 定义扫描结果上报回调函数 49function onReceiveEvent(data: Array<ble.ScanResult>) { 50 console.info('BLE scan device find result: '+ JSON.stringify(data)); 51} 52 53try { 54 // 发起订阅 55 ble.on('BLEDeviceFind', onReceiveEvent); 56} catch (err) { 57 console.error('errCode: ' + (err as BusinessError).code + ', errMessage: ' + (err as BusinessError).message); 58} 59``` 60 61- 如何解析扫描到的广播报文,具体可参考本章节[完整示例](#完整示例)。 62 63**2. 发起扫描**<br> 64通过BLE扫描周边其他设备发出的BLE广播,可以发现或者查找到应用需要的目标设备,适用于查找设备场景。 65 66若本机设备扫描到可连接的BLE广播,则可以和该设备进行通用属性协议(Generic Attribute Profile,GATT)的连接和数据传输,此时本机设备角色也被称为GATT客户端。具体操作请参考[连接和传输数据](gatt-development-guide.md)。 67 68- 推荐使用API version 15开始支持的扫描方式,该方式支持应用发起和管理多路扫描。可通过[createBleScanner](../../reference/apis-connectivity-kit/js-apis-bluetooth-ble.md#blecreateblescanner15)创建扫描实例[BleScanner](../../reference/apis-connectivity-kit/js-apis-bluetooth-ble.md#blescanner15),并调用[startScan](../../reference/apis-connectivity-kit/js-apis-bluetooth-ble.md#startscan15)。 69```ts 70// 创建ble扫描实例 71let bleScanner: ble.BleScanner = ble.createBleScanner(); 72 73// 构造扫描BLE广播的过滤条件,目标BLE广播报文需符合该过滤条件 74let manufactureId = 4567; 75let manufactureData: Uint8Array = new Uint8Array([1, 2, 3, 4]); 76let manufactureDataMask: Uint8Array = new Uint8Array([0xFF, 0xFF, 0xFF, 0xFF]); 77let scanFilter: ble.ScanFilter = { // 根据业务实际情况定义过滤器 78 manufactureId: manufactureId, 79 manufactureData: manufactureData.buffer, 80 manufactureDataMask: manufactureDataMask.buffer 81}; 82 83// 构造扫描配置参数 84let scanOptions: ble.ScanOptions = { 85 interval: 0, 86 dutyMode: ble.ScanDuty.SCAN_MODE_LOW_POWER, 87 matchMode: ble.MatchMode.MATCH_MODE_AGGRESSIVE 88} 89 90try { 91 // 发起扫描 92 bleScanner.startScan([scanFilter], scanOptions); 93 console.info('startBleScan success'); 94} catch (err) { 95 console.error('errCode: ' + (err as BusinessError).code + ', errMessage: ' + (err as BusinessError).message); 96} 97``` 98 99- API version 14及以前支持的扫描方式只支持应用发起单路扫描。若要再次发起扫描,必须先停止上一路的扫描流程。详情请见[ble.startBLEScan](../../reference/apis-connectivity-kit/js-apis-bluetooth-ble.md#blestartblescan)。 100```ts 101// 构造扫描BLE广播的过滤条件,目标BLE广播报文需符合该过滤条件 102let manufactureId = 4567; 103let manufactureData: Uint8Array = new Uint8Array([1, 2, 3, 4]); 104let manufactureDataMask: Uint8Array = new Uint8Array([0xFF, 0xFF, 0xFF, 0xFF]); 105let scanFilter: ble.ScanFilter = { // 根据业务实际情况定义过滤器 106 manufactureId: manufactureId, 107 manufactureData: manufactureData.buffer, 108 manufactureDataMask: manufactureDataMask.buffer 109}; 110 111// 构造扫描配置参数 112let scanOptions: ble.ScanOptions = { 113 interval: 0, 114 dutyMode: ble.ScanDuty.SCAN_MODE_LOW_POWER, 115 matchMode: ble.MatchMode.MATCH_MODE_AGGRESSIVE 116} 117try { 118 // 发起扫描 119 ble.startBLEScan([scanFilter], scanOptions); 120 console.info('startBleScan success'); 121} catch (err) { 122 console.error('errCode: ' + (err as BusinessError).code + ', errMessage: ' + (err as BusinessError).message); 123} 124``` 125 126**3. 停止扫描**<br> 127扫描流程会消耗蓝牙硬件资源和影响设备功耗。当应用不再需要该扫描时,需要主动停止。 128 129- 搭配API version 15开始支持的多路扫描方式。详情请见[stopScan](../../reference/apis-connectivity-kit/js-apis-bluetooth-ble.md#stopscan15)。 130```ts 131// 定义扫描结果上报回调函数 132function onReceiveEvent(scanReport: ble.ScanReport) { 133 console.info('BLE scan device find result: '+ JSON.stringify(scanReport)); 134} 135 136// 创建ble扫描实例 137let bleScanner: ble.BleScanner = ble.createBleScanner(); 138 139try { 140 bleScanner.off('BLEDeviceFind', onReceiveEvent); 141 // 停止扫描 142 bleScanner.stopScan(); 143 console.info('stopBleScan success'); 144} catch (err) { 145 console.error('errCode: ' + (err as BusinessError).code + ', errMessage: ' + (err as BusinessError).message); 146} 147``` 148 149- 搭配API version 14及以前支持的单路扫描方式。详情请见[ble.stopBLEScan](../../reference/apis-connectivity-kit/js-apis-bluetooth-ble.md#blestopblescan)。 150```ts 151// 定义扫描结果上报回调函数 152function onReceiveEvent(data: Array<ble.ScanResult>) { 153 console.info('BLE scan device find result: '+ JSON.stringify(data)); 154} 155 156try { 157 // 取消订阅 158 ble.off('BLEDeviceFind', onReceiveEvent); 159 // 停止扫描 160 ble.stopBLEScan(); 161} catch (err) { 162 console.error('errCode: ' + (err as BusinessError).code + ', errMessage: ' + (err as BusinessError).message); 163} 164``` 165 166### BLE广播流程 167本机设备发送BLE广播后,可以实现被其他设备发现的功能。 168 169若本机设备发送的是可连接广播,则可以接受其他设备发起的通用属性协议(Generic Attribute Profile,GATT)连接,此时本机设备角色也被称为GATT服务端。具体操作请参考[连接和传输数据](gatt-development-guide.md)。 170 171推荐使用API version 11及以后开始支持的广播操作方式。 172 173**1. 订阅广播状态上报事件**<br> 174搭配API version 11开始支持的广播操作方式。 175```ts 176function onReceiveEvent(data: ble.AdvertisingStateChangeInfo) { 177 console.info('bluetooth advertising state = ' + JSON.stringify(data)); 178} 179 180try { 181 ble.on('advertisingStateChange', onReceiveEvent); 182} catch (err) { 183 console.error('errCode: ' + (err as BusinessError).code + ', errMessage: ' + (err as BusinessError).message); 184} 185``` 186 187**2. 启动广播**<br> 188- 推荐使用API version 11开始支持的广播操作方式。支持在不释放相关广播资源情况下,多次操作启动或者停止指定标识的广播,且支持设置广播持续发送的时间。<br> 189相关API请参考[ble.startAdvertising](../../reference/apis-connectivity-kit/js-apis-bluetooth-ble.md#blestartadvertising11)和[ble.enableAdvertising](../../reference/apis-connectivity-kit/js-apis-bluetooth-ble.md#bleenableadvertising11)。<br> 190首次启动广播接口[ble.startAdvertising](../../reference/apis-connectivity-kit/js-apis-bluetooth-ble.md#blestartadvertising11)会分配广播相关资源,从API version 15开始,该接口支持应用多次调用,实现启动多路广播的功能,并通过不同的广播标识进行管理。 191```ts 192// 设置广播发送的参数 193let setting: ble.AdvertiseSetting = { 194 interval: 160, 195 txPower: 0, 196 connectable: true // 发送支持连接的广播 197}; 198// 构造广播数据 199let manufactureValueBuffer = new Uint8Array(4); 200manufactureValueBuffer[0] = 1; 201manufactureValueBuffer[1] = 2; 202manufactureValueBuffer[2] = 3; 203manufactureValueBuffer[3] = 4; 204let serviceValueBuffer = new Uint8Array(4); 205serviceValueBuffer[0] = 5; 206serviceValueBuffer[1] = 6; 207serviceValueBuffer[2] = 7; 208serviceValueBuffer[3] = 8; 209let manufactureDataUnit: ble.ManufactureData = { 210 manufactureId: 4567, 211 manufactureValue: manufactureValueBuffer.buffer 212}; 213let serviceDataUnit1: ble.ServiceData = { 214 serviceUuid: "00001999-0000-1000-8000-00805f9b34fb", 215 serviceValue: serviceValueBuffer.buffer 216}; 217let serviceDataUnit2: ble.ServiceData = { 218 serviceUuid: "19991999-0000-1000-8000-00805f9b34fb", 219 serviceValue: serviceValueBuffer.buffer 220}; 221let advData: ble.AdvertiseData = { 222 serviceUuids: ["00001888-0000-1000-8000-00805f9b34fb", "18881888-0000-1000-8000-00805f9b34fb"], 223 manufactureData: [manufactureDataUnit], 224 serviceData: [], 225 includeDeviceName: false // 表示是否携带设备名,可选参数。注意:带上设备名时,容易导致广播报文长度超出31个字节,使得广播启动失败 226}; 227let advResponse: ble.AdvertiseData = { 228 serviceUuids: [], 229 manufactureData: [], 230 serviceData: [serviceDataUnit1, serviceDataUnit2] 231}; 232// 构造广播启动完整参数AdvertisingParams 233let advertisingParams: ble.AdvertisingParams = { 234 advertisingSettings: setting, 235 advertisingData: advData, // 注意: 广播报文长度不能超过31个字节 236 advertisingResponse: advResponse, // 注意: 广播报文长度不能超过31个字节 237 duration: 0 // 可选参数,若大于0,则广播发送一段时间后,则会停止,但分配的广播资源还在,可重新启动发送 238} 239 240let advHandle = 0xFF; // 定义广播标识 241 242// 首次启动广播,蓝牙子系统会分配相关资源,包括应用获取到的广播的标识ID 243try { 244 ble.startAdvertising(advertisingParams, (err, outAdvHandle) => { 245 if (err) { 246 return; 247 } else { 248 advHandle = outAdvHandle; 249 console.info("advHandle: " + advHandle); 250 } 251 }); 252} catch (err) { 253 console.error('errCode: ' + (err as BusinessError).code + ', errMessage: ' + (err as BusinessError).message); 254} 255 256// 构造启动广播参数 257let advertisingEnableParams: ble.AdvertisingEnableParams = { 258 advertisingId: advHandle, // 使用首次启动广播时获取到的广播标识ID 259 duration: 300 260} 261try { 262 // 再次启动 263 ble.enableAdvertising(advertisingEnableParams, (err) => { 264 if (err) { 265 return; 266 } 267 }); 268} catch (err) { 269 console.error('errCode: ' + (err as BusinessError).code + ', errMessage: ' + (err as BusinessError).message); 270} 271``` 272 273- API version 10及以前支持的广播操作方式只支持应用启动单路广播。若要再次启动广播,必须先停止上一路的广播流程。详情请见[ble.startAdvertising](../../reference/apis-connectivity-kit/js-apis-bluetooth-ble.md#blestartadvertising)。 274```ts 275// 设置广播发送的参数 276let setting: ble.AdvertiseSetting = { 277 interval: 160, 278 txPower: 0, 279 connectable: true 280}; 281// 构造广播数据 282let manufactureValueBuffer = new Uint8Array(4); 283manufactureValueBuffer[0] = 1; 284manufactureValueBuffer[1] = 2; 285manufactureValueBuffer[2] = 3; 286manufactureValueBuffer[3] = 4; 287let serviceValueBuffer = new Uint8Array(4); 288serviceValueBuffer[0] = 5; 289serviceValueBuffer[1] = 6; 290serviceValueBuffer[2] = 7; 291serviceValueBuffer[3] = 8; 292let manufactureDataUnit: ble.ManufactureData = { 293 manufactureId: 4567, 294 manufactureValue: manufactureValueBuffer.buffer 295}; 296let serviceDataUnit1: ble.ServiceData = { 297 serviceUuid: "00001999-0000-1000-8000-00805f9b34fb", 298 serviceValue: serviceValueBuffer.buffer 299}; 300let serviceDataUnit2: ble.ServiceData = { 301 serviceUuid: "19991999-0000-1000-8000-00805f9b34fb", 302 serviceValue: serviceValueBuffer.buffer 303}; 304let advData: ble.AdvertiseData = { 305 serviceUuids: ["00001888-0000-1000-8000-00805f9b34fb", "18881888-0000-1000-8000-00805f9b34fb"], 306 manufactureData: [manufactureDataUnit], 307 serviceData: [], 308 includeDeviceName: false // 表示是否携带设备名,可选参数。注意:带上设备名时,容易导致广播报文长度超出31个字节 309}; 310let advResponse: ble.AdvertiseData = { 311 serviceUuids: [], 312 manufactureData: [], 313 serviceData: [serviceDataUnit1, serviceDataUnit2] 314}; 315try { 316 // 启动广播 317 ble.startAdvertising(setting, advData ,advResponse); 318} catch (err) { 319 console.error('errCode: ' + (err as BusinessError).code + ', errMessage: ' + (err as BusinessError).message); 320} 321``` 322 323**3. 停止广播**<br> 324广播流程会消耗蓝牙硬件资源和影响设备功耗。当应用不再需要该广播时,需要主动停止。 325 326- 搭配API version 11开始支持的广播方式。相关API请参考[ble.disableAdvertising](../../reference/apis-connectivity-kit/js-apis-bluetooth-ble.md#bledisableadvertising11)和[ble.stopAdvertising](../../reference/apis-connectivity-kit/js-apis-bluetooth-ble.md#blestopadvertising11)。<br> 327完全停止广播接口[ble.stopAdvertising](../../reference/apis-connectivity-kit/js-apis-bluetooth-ble.md#blestopadvertising11)会释放所有广播资源,因此首次启动广播分配的广播标识将无效。 328```ts 329let advHandle = 1; // 注意:该值是首次启动广播时获取到的广播标识,此处是伪代码ID 330 331// 构造停止广播参数 332let advertisingDisableParams: ble.AdvertisingDisableParams = { 333 advertisingId: advHandle // 使用首次启动广播时获取到的广播标识ID 334} 335try { 336 // 停止 337 ble.disableAdvertising(advertisingDisableParams, (err) => { 338 if (err) { 339 return; 340 } 341 }); 342} catch (err) { 343 console.error('errCode: ' + (err as BusinessError).code + ', errMessage: ' + (err as BusinessError).message); 344} 345 346try { 347 // 完全停止 348 ble.stopAdvertising(advHandle, (err) => { 349 if (err) { 350 return; 351 } 352 }); 353} catch (err) { 354 console.error('errCode: ' + (err as BusinessError).code + ', errMessage: ' + (err as BusinessError).message); 355} 356 357``` 358 359- 搭配API version 11及以前支持的单路广播方式。相关API请参考[ble.stopAdvertising](../../reference/apis-connectivity-kit/js-apis-bluetooth-ble.md#blestopadvertising)。 360```ts 361try { 362 // 停止 363 ble.stopAdvertising(); 364} catch (err) { 365 console.error('errCode: ' + (err as BusinessError).code + ', errMessage: ' + (err as BusinessError).message); 366} 367``` 368 369## 完整示例 370 371### BLE扫描流程 372```ts 373import { ble } from '@kit.ConnectivityKit'; 374import { BusinessError } from '@ohos.base'; 375 376const TAG: string = 'BleAdvertisingManager'; 377 378// 参考蓝牙标准协议规范Core Assigned Numbers 379const BLE_ADV_TYPE_FLAG = 0x01; 380const BLE_ADV_TYPE_16_BIT_SERVICE_UUIDS_INCOMPLETE = 0x02; 381const BLE_ADV_TYPE_16_BIT_SERVICE_UUIDS_COMPLETE = 0x03; 382const BLE_ADV_TYPE_32_BIT_SERVICE_UUIDS_INCOMPLETE = 0x04; 383const BLE_ADV_TYPE_32_BIT_SERVICE_UUIDS_COMPLETE = 0x05; 384const BLE_ADV_TYPE_128_BIT_SERVICE_UUIDS_INCOMPLETE = 0x06; 385const BLE_ADV_TYPE_128_BIT_SERVICE_UUIDS_COMPLETE = 0x07; 386const BLE_ADV_TYPE_LOCAL_NAME_SHORT = 0x08; 387const BLE_ADV_TYPE_LOCAL_NAME_COMPLETE = 0x09; 388const BLE_ADV_TYPE_TX_POWER_LEVEL = 0x0A; 389const BLE_ADV_TYPE_16_BIT_SERVICE_SOLICITATION_UUIDS = 0x14; 390const BLE_ADV_TYPE_128_BIT_SERVICE_SOLICITATION_UUIDS = 0x15; 391const BLE_ADV_TYPE_32_BIT_SERVICE_SOLICITATION_UUIDS = 0x1F; 392const BLE_ADV_TYPE_16_BIT_SERVICE_DATA = 0x16; 393const BLE_ADV_TYPE_32_BIT_SERVICE_DATA = 0x20; 394const BLE_ADV_TYPE_128_BIT_SERVICE_DATA = 0x21; 395const BLE_ADV_TYPE_MANUFACTURER_SPECIFIC_DATA = 0xFF; 396 397const BLUETOOTH_UUID_16_BIT_LENGTH = 2; 398const BLUETOOTH_UUID_32_BIT_LENGTH = 4; 399const BLUETOOTH_UUID_128_BIT_LENGTH = 16; 400 401const BLUETOOTH_MANUFACTURE_ID_LENGTH = 2; 402 403export class BleScanManager { 404 bleScanner: ble.BleScanner = ble.createBleScanner(); 405 406 // 1. 定义扫描结果上报回调函数 407 onReceiveEvent = (scanReport: ble.ScanReport) => { 408 console.info(TAG, 'BLE scan device find result: '+ JSON.stringify(scanReport)); 409 if (scanReport.scanResult.length > 0) { 410 console.info(TAG, 'BLE scan result: ' + scanReport.scanResult[0].deviceId); 411 this.parseScanResult(scanReport.scanResult[0].data); 412 } 413 }; 414 415 public parseScanResult(data: ArrayBuffer) { 416 let advData = new Uint8Array(data); 417 if (advData.byteLength == 0) { 418 console.warn(TAG, 'adv data length is 0'); 419 return; 420 } 421 422 let advertiseFlags: number = -1; 423 let txPowerLevel: number = -1; 424 let localName: string = ''; 425 let serviceUuids: string[] = []; 426 let serviceSolicitationUuids: string[] = []; 427 let manufactureSpecificDatas: Record<number, Uint8Array> = {}; 428 let serviceDatas: Record<string, Uint8Array> = {}; 429 430 let curPos = 0; 431 while (curPos < advData.byteLength) { 432 let length = advData[curPos++]; // 获取当前广播类型的长度(length+data),curPos指向下一个位置 433 if (length == 0) { 434 break; 435 } 436 437 // 获取当前广播类型内容长度(data) 438 let advDataLength = length - 1; 439 440 // 获取当前广播类型,curPos指向下一个位置,从该位置解析实际内容,参考Core Specification Supplement, PartA 441 let advDataType = advData[curPos++]; 442 switch (advDataType) { 443 case BLE_ADV_TYPE_FLAG: 444 advertiseFlags = advData[curPos]; 445 break; 446 case BLE_ADV_TYPE_LOCAL_NAME_SHORT: 447 case BLE_ADV_TYPE_LOCAL_NAME_COMPLETE: 448 localName = advData.slice(curPos, curPos + advDataLength).toString(); 449 break; 450 case BLE_ADV_TYPE_TX_POWER_LEVEL: 451 txPowerLevel = advData[curPos]; 452 break; 453 case BLE_ADV_TYPE_16_BIT_SERVICE_UUIDS_INCOMPLETE: 454 case BLE_ADV_TYPE_16_BIT_SERVICE_UUIDS_COMPLETE: 455 this.parseServiceUuid(BLUETOOTH_UUID_16_BIT_LENGTH, curPos, advDataLength, advData, serviceUuids); 456 break; 457 case BLE_ADV_TYPE_32_BIT_SERVICE_UUIDS_INCOMPLETE: 458 case BLE_ADV_TYPE_32_BIT_SERVICE_UUIDS_COMPLETE: 459 this.parseServiceUuid(BLUETOOTH_UUID_32_BIT_LENGTH, curPos, advDataLength, advData, serviceUuids); 460 break; 461 case BLE_ADV_TYPE_128_BIT_SERVICE_UUIDS_INCOMPLETE: 462 case BLE_ADV_TYPE_128_BIT_SERVICE_UUIDS_COMPLETE: 463 this.parseServiceUuid(BLUETOOTH_UUID_128_BIT_LENGTH, curPos, advDataLength, advData, serviceUuids); 464 break; 465 case BLE_ADV_TYPE_16_BIT_SERVICE_SOLICITATION_UUIDS: 466 this.parseServiceSolicitationUuid(BLUETOOTH_UUID_16_BIT_LENGTH, curPos, advDataLength, 467 advData, serviceSolicitationUuids); 468 break; 469 case BLE_ADV_TYPE_32_BIT_SERVICE_SOLICITATION_UUIDS: 470 this.parseServiceSolicitationUuid(BLUETOOTH_UUID_32_BIT_LENGTH, curPos, advDataLength, 471 advData, serviceSolicitationUuids); 472 break; 473 case BLE_ADV_TYPE_128_BIT_SERVICE_SOLICITATION_UUIDS: 474 this.parseServiceSolicitationUuid(BLUETOOTH_UUID_128_BIT_LENGTH, curPos, advDataLength, 475 advData, serviceSolicitationUuids); 476 break; 477 case BLE_ADV_TYPE_16_BIT_SERVICE_DATA: 478 this.parseServiceData(BLUETOOTH_UUID_16_BIT_LENGTH, curPos, advDataLength, advData, serviceDatas); 479 break; 480 case BLE_ADV_TYPE_32_BIT_SERVICE_DATA: 481 this.parseServiceData(BLUETOOTH_UUID_32_BIT_LENGTH, curPos, advDataLength, advData, serviceDatas); 482 break; 483 case BLE_ADV_TYPE_128_BIT_SERVICE_DATA: 484 this.parseServiceData(BLUETOOTH_UUID_128_BIT_LENGTH, curPos, advDataLength, advData, serviceDatas); 485 break; 486 case BLE_ADV_TYPE_MANUFACTURER_SPECIFIC_DATA: 487 this.parseManufactureData(curPos, advDataLength, advData, manufactureSpecificDatas); 488 break; 489 default: 490 break; 491 } 492 curPos += advDataLength; // curPos指向下一个字段类型 493 } 494 console.info(TAG, 'advertiseFlags: ' + advertiseFlags); 495 console.info(TAG, 'txPowerLevel: ' + txPowerLevel); 496 console.info(TAG, 'localName: ' + localName); 497 console.info(TAG, 'serviceUuids: ' + JSON.stringify(serviceUuids)); 498 console.info(TAG, 'serviceSolicitationUuids: ' + JSON.stringify(serviceSolicitationUuids)); 499 console.info(TAG, 'manufactureSpecificDatas: ' + JSON.stringify(manufactureSpecificDatas)); 500 console.info(TAG, 'serviceDatas: ' + JSON.stringify(serviceDatas)); 501 } 502 503 private parseServiceUuid(uuidLength: number, curPos: number, advDataLength: number, 504 advData: Uint8Array, serviceUuids: string[]) { 505 while (advDataLength > 0) { 506 let tmpData: Uint8Array = advData.slice(curPos, curPos + uuidLength); 507 serviceUuids.push(this.getUuidFromUint8Array(uuidLength, tmpData)); 508 advDataLength -= uuidLength; 509 curPos += uuidLength; 510 } 511 } 512 513 private parseServiceSolicitationUuid(uuidLength: number, curPos: number, advDataLength: number, 514 advData: Uint8Array, serviceSolicitationUuids: string[]) { 515 while (advDataLength > 0) { 516 let tmpData: Uint8Array = advData.slice(curPos, curPos + uuidLength); 517 serviceSolicitationUuids.push(this.getUuidFromUint8Array(uuidLength, tmpData)); 518 advDataLength -= uuidLength; 519 curPos += uuidLength; 520 } 521 } 522 523 private getUuidFromUint8Array(uuidLength: number, uuidData: Uint8Array): string { 524 let uuid = ""; 525 let temp: string = ""; 526 for (let i = uuidLength - 1; i > -1; i--) { 527 temp += uuidData[i].toString(16).padStart(2, "0"); 528 } 529 switch (uuidLength) { 530 case BLUETOOTH_UUID_16_BIT_LENGTH: 531 uuid = `0000${temp}-0000-1000-8000-00805F9B34FB`; 532 break; 533 case BLUETOOTH_UUID_32_BIT_LENGTH: 534 uuid = `${temp}-0000-1000-8000-00805F9B34FB`; 535 break; 536 case BLUETOOTH_UUID_128_BIT_LENGTH: 537 uuid = `${temp.substring(0, 8)}-${temp.substring(8, 12)}-${temp.substring(12, 16)}-${temp.substring(16, 20)}-${temp.substring(20, 32)}`; 538 break; 539 default: 540 break; 541 } 542 return uuid; 543 } 544 545 private parseServiceData(uuidLength: number, curPos: number, advDataLength: number, 546 advData: Uint8Array, serviceDatas: Record<number, Uint8Array>) { 547 let uuid: Uint8Array = advData.slice(curPos, curPos + uuidLength); 548 let data: Uint8Array = advData.slice(curPos + uuidLength, curPos + advDataLength); 549 serviceDatas[this.getUuidFromUint8Array(uuidLength, uuid)] = data; 550 } 551 552 private parseManufactureData(curPos: number, advDataLength: number, 553 advData: Uint8Array, manufactureSpecificDatas: Record<number, Uint8Array>) { 554 let manufactureId: number = (advData[curPos + 1] << 8) + advData[curPos]; 555 let data: Uint8Array = advData.slice(curPos + BLUETOOTH_MANUFACTURE_ID_LENGTH, curPos + advDataLength); 556 manufactureSpecificDatas[manufactureId] = data; 557 } 558 559 // 2. 开启扫描 560 public startScan() { 561 // 2.1 构造扫描BLE广播的过滤条件,目标BLE广播报文需符合该过滤条件 562 let manufactureId = 4567; 563 let manufactureData: Uint8Array = new Uint8Array([1, 2, 3, 4]); 564 let manufactureDataMask: Uint8Array = new Uint8Array([0xFF, 0xFF, 0xFF, 0xFF]); 565 let scanFilter: ble.ScanFilter = { // 根据业务实际情况定义过滤器 566 manufactureId: manufactureId, 567 manufactureData: manufactureData.buffer, 568 manufactureDataMask: manufactureDataMask.buffer 569 }; 570 571 // 2.2 构造扫描配置参数 572 let scanOptions: ble.ScanOptions = { 573 interval: 0, 574 dutyMode: ble.ScanDuty.SCAN_MODE_LOW_POWER, 575 matchMode: ble.MatchMode.MATCH_MODE_AGGRESSIVE 576 } 577 try { 578 // 发起订阅 579 this.bleScanner.on('BLEDeviceFind', this.onReceiveEvent); 580 // 发起扫描 581 this.bleScanner.startScan([scanFilter], scanOptions); 582 console.info('startBleScan success'); 583 } catch (err) { 584 console.error('errCode: ' + (err as BusinessError).code + ', errMessage: ' + (err as BusinessError).message); 585 } 586 } 587 588 // 3. 关闭扫描 589 public stopScan() { 590 try { 591 // 取消订阅 592 this.bleScanner.off('BLEDeviceFind', this.onReceiveEvent); 593 // 停止扫描 594 this.bleScanner.stopScan(); 595 console.info('stopBleScan success'); 596 } catch (err) { 597 console.error('errCode: ' + (err as BusinessError).code + ', errMessage: ' + (err as BusinessError).message); 598 } 599 } 600} 601 602let bleScanManager = new BleScanManager(); 603export default bleScanManager as BleScanManager; 604``` 605 606### BLE广播流程 607```ts 608import { ble } from '@kit.ConnectivityKit'; 609import { BusinessError } from '@kit.BasicServicesKit'; 610 611const TAG: string = 'BleAdvertisingManager'; 612 613export class BleAdvertisingManager { 614 private advHandle: number = 0xFF; // 初始的无效值 615 616 // 1. 定义广播状态上报事件 617 onReceiveEvent = (data: ble.AdvertisingStateChangeInfo) => { 618 console.info(TAG, 'bluetooth advertising state = ' + JSON.stringify(data)); 619 AppStorage.setOrCreate('advertiserState', data.state); 620 }; 621 622 // 2. 首次启动广播 623 public async startAdvertising() { 624 // 2.1 设置广播发送的参数 625 let setting: ble.AdvertiseSetting = { 626 interval: 160, 627 txPower: 0, 628 connectable: true 629 }; 630 // 2.2 构造广播数据 631 let manufactureValueBuffer = new Uint8Array(4); 632 manufactureValueBuffer[0] = 1; 633 manufactureValueBuffer[1] = 2; 634 manufactureValueBuffer[2] = 3; 635 manufactureValueBuffer[3] = 4; 636 let serviceValueBuffer = new Uint8Array(4); 637 serviceValueBuffer[0] = 5; 638 serviceValueBuffer[1] = 6; 639 serviceValueBuffer[2] = 7; 640 serviceValueBuffer[3] = 8; 641 let manufactureDataUnit: ble.ManufactureData = { 642 manufactureId: 4567, 643 manufactureValue: manufactureValueBuffer.buffer 644 }; 645 let serviceDataUnit1: ble.ServiceData = { 646 serviceUuid: "00001999-0000-1000-8000-00805f9b34fb", 647 serviceValue: serviceValueBuffer.buffer 648 }; 649 let serviceDataUnit2: ble.ServiceData = { 650 serviceUuid: "19991999-0000-1000-8000-00805f9b34fb", 651 serviceValue: serviceValueBuffer.buffer 652 }; 653 let advData: ble.AdvertiseData = { 654 serviceUuids: ["00001888-0000-1000-8000-00805f9b34fb", "18881888-0000-1000-8000-00805f9b34fb"], 655 manufactureData: [manufactureDataUnit], 656 serviceData: [], 657 includeDeviceName: false // 表示是否携带设备名,可选参数。注意:带上设备名时,容易导致广播报文长度超出31个字节,使得广播启动失败 658 }; 659 let advResponse: ble.AdvertiseData = { 660 serviceUuids: [], 661 manufactureData: [], 662 serviceData: [serviceDataUnit1, serviceDataUnit2] 663 }; 664 // 2.3 构造广播启动完整参数AdvertisingParams 665 let advertisingParams: ble.AdvertisingParams = { 666 advertisingSettings: setting, 667 advertisingData: advData, // 注意: 广播报文长度不能超过31个字节 668 advertisingResponse: advResponse, // 注意: 广播报文长度不能超过31个字节 669 duration: 0 // 可选参数,若参数大于0,则广播发送一段时间后会停止,但分配的广播资源还在,可重新启动发送 670 } 671 672 // 2.4 首次启动广播,蓝牙子系统会分配相关资源,包括应用获取到的广播标识ID 673 try { 674 ble.on('advertisingStateChange', this.onReceiveEvent); 675 this.advHandle = await ble.startAdvertising(advertisingParams); 676 } catch (err) { 677 console.error(TAG, 'errCode: ' + (err as BusinessError).code + ', errMessage: ' + (err as BusinessError).message); 678 } 679 } 680 681 // 3. 停止指定标识的广播,即首次启动时分配的标识,停止后,该路广播资源仍然存在 682 public async disableAdvertising() { 683 // 3.1 构造停止广播参数 684 let advertisingDisableParams: ble.AdvertisingDisableParams = { 685 advertisingId: this.advHandle // 使用首次启动广播时获取到的广播标识ID 686 } 687 try { 688 // 3.2 停止 689 await ble.disableAdvertising(advertisingDisableParams); 690 } catch (err) { 691 console.error(TAG, 'errCode: ' + (err as BusinessError).code + ', errMessage: ' + (err as BusinessError).message); 692 } 693 } 694 695 // 4. 启动指定标识的广播,即首次启动时分配的标识 696 public async enableAdvertising(enableDuration: number) { 697 // 4.1 构造启动广播参数 698 let advertisingEnableParams: ble.AdvertisingEnableParams = { 699 advertisingId: this.advHandle, // 使用首次启动广播时获取到的广播标识ID 700 duration: enableDuration 701 } 702 try { 703 // 4.2 再次启动 704 await ble.enableAdvertising(advertisingEnableParams); 705 } catch (err) { 706 console.error(TAG, 'errCode: ' + (err as BusinessError).code + ', errMessage: ' + (err as BusinessError).message); 707 } 708 } 709 710 // 5. 完全停止广播,释放广播资源 711 public async stopAdvertising() { 712 try { 713 await ble.stopAdvertising(this.advHandle); 714 ble.off('advertisingStateChange', this.onReceiveEvent); 715 } catch (err) { 716 console.error(TAG, 'errCode: ' + (err as BusinessError).code + ', errMessage: ' + (err as BusinessError).message); 717 } 718 } 719} 720 721let bleAdvertisingManager = new BleAdvertisingManager(); 722export default bleAdvertisingManager as BleAdvertisingManager; 723```