1# 查找设备开发指导 2 3## 简介 4广播与扫描,主要提供了蓝牙设备的开启广播、关闭广播、开启扫描、关闭扫描方法,通过广播和扫描发现对端蓝牙设备,实现低功耗的通信。 5 6## 场景介绍 7主要场景有: 8 9- 开启、关闭广播 10- 开启、关闭扫描 11 12## 接口说明 13 14完整的 JS API 说明以及实例代码请参考:[BLE 接口](../../reference/apis-connectivity-kit/js-apis-bluetooth-ble.md)。 15 16具体接口说明如下表。 17 18| 接口名 | 功能描述 | 19| ---------------------------------- | ------------------------------------------------------------------------------ | 20| startBLEScan() | 发起BLE扫描流程。 | 21| stopBLEScan() | 停止BLE扫描流程。 | 22| startAdvertising() | 开始发送BLE广播。 | 23| disableAdvertising() | 临时停止BLE广播。 | 24| enableAdvertising() | 临时启动BLE广播。 | 25| stopAdvertising() | 停止发送BLE广播。 | 26| on(type: 'advertisingStateChange') | 订阅BLE广播状态。 | 27| off(type: 'advertisingStateChange')| 取消订阅BLE广播状态。 | 28| on(type: 'BLEDeviceFind') | 订阅BLE设备发现上报事件。 | 29| off(type: 'BLEDeviceFind') | 取消订阅BLE设备发现上报事件。 | 30 31## 主要场景开发步骤 32 33### 开启、关闭广播 341. import需要的ble模块。 352. 开启设备的蓝牙。 363. 需要申请权限ohos.permission.ACCESS_BLUETOOTH。 374. 需要SystemCapability.Communication.Bluetooth.Core系统能力。 385. 开启广播,对端设备扫描该广播。 396. 关闭广播。 407. 示例代码: 41 42 ```ts 43 import { ble } from '@kit.ConnectivityKit'; 44 import { AsyncCallback, BusinessError } from '@kit.BasicServicesKit'; 45 46 const TAG: string = 'BleAdvertisingManager'; 47 48 export class BleAdvertisingManager { 49 private advHandle: number = 0xFF; // default invalid value 50 51 // 1 订阅广播状态 52 public onAdvertisingStateChange() { 53 try { 54 ble.on('advertisingStateChange', (data: ble.AdvertisingStateChangeInfo) => { 55 console.info(TAG, 'bluetooth advertising state = ' + JSON.stringify(data)); 56 AppStorage.setOrCreate('advertiserState', data.state); 57 }); 58 } catch (err) { 59 console.error(TAG, 'errCode: ' + (err as BusinessError).code + ', errMessage: ' + (err as BusinessError).message); 60 } 61 } 62 63 // 2 首次启动广播 64 public async startAdvertising() { 65 // 2.1 设置广播发送的参数 66 let setting: ble.AdvertiseSetting = { 67 interval: 160, 68 txPower: 0, 69 connectable: true 70 }; 71 // 2.2 构造广播数据 72 let manufactureValueBuffer = new Uint8Array(4); 73 manufactureValueBuffer[0] = 1; 74 manufactureValueBuffer[1] = 2; 75 manufactureValueBuffer[2] = 3; 76 manufactureValueBuffer[3] = 4; 77 let serviceValueBuffer = new Uint8Array(4); 78 serviceValueBuffer[0] = 5; 79 serviceValueBuffer[1] = 6; 80 serviceValueBuffer[2] = 7; 81 serviceValueBuffer[3] = 8; 82 let manufactureDataUnit: ble.ManufactureData = { 83 manufactureId: 4567, 84 manufactureValue: manufactureValueBuffer.buffer 85 }; 86 let serviceDataUnit: ble.ServiceData = { 87 serviceUuid: "00001888-0000-1000-8000-00805f9b34fb", 88 serviceValue: serviceValueBuffer.buffer 89 }; 90 let advData: ble.AdvertiseData = { 91 serviceUuids: ["00001888-0000-1000-8000-00805f9b34fb"], 92 manufactureData: [manufactureDataUnit], 93 serviceData: [serviceDataUnit], 94 includeDeviceName: false // 表示是否携带设备名,可选参数。注意带上设备名时广播包长度不能超出31个字节。 95 }; 96 let advResponse: ble.AdvertiseData = { 97 serviceUuids: ["00001888-0000-1000-8000-00805f9b34fb"], 98 manufactureData: [manufactureDataUnit], 99 serviceData: [serviceDataUnit] 100 }; 101 // 2.3 构造广播启动完整参数AdvertisingParams 102 let advertisingParams: ble.AdvertisingParams = { 103 advertisingSettings: setting, 104 advertisingData: advData, 105 advertisingResponse: advResponse, 106 duration: 0 // 可选参数,若大于0,则广播发送一段时间后,则会临时停止,可重新启动发送 107 } 108 109 // 2.4 首次启动广播,且获取所启动广播的标识ID 110 try { 111 this.onAdvertisingStateChange(); 112 this.advHandle = await ble.startAdvertising(advertisingParams); 113 } catch (err) { 114 console.error(TAG, 'errCode: ' + (err as BusinessError).code + ', errMessage: ' + (err as BusinessError).message); 115 } 116 } 117 118 // 4 临时停止广播,该广播资源仍然存在 119 public async disableAdvertising() { 120 // 4.1 构造临时停止广播参数 121 let advertisingDisableParams: ble.AdvertisingDisableParams = { 122 advertisingId: this.advHandle // 使用首次启动广播时获取到的广播标识ID 123 } 124 // 4.2 临时停止 125 try { 126 await ble.disableAdvertising(advertisingDisableParams); 127 } catch (err) { 128 console.error(TAG, 'errCode: ' + (err as BusinessError).code + ', errMessage: ' + (err as BusinessError).message); 129 } 130 } 131 132 // 5 再次启动广播 133 public async enableAdvertising(enableDuration: number) { 134 // 5.1 构造临时启动广播参数 135 let advertisingEnableParams: ble.AdvertisingEnableParams = { 136 advertisingId: this.advHandle, // 使用首次启动广播时获取到的广播标识ID 137 duration: enableDuration 138 } 139 // 5.2 再次启动 140 try { 141 await ble.enableAdvertising(advertisingEnableParams); 142 } catch (err) { 143 console.error(TAG, 'errCode: ' + (err as BusinessError).code + ', errMessage: ' + (err as BusinessError).message); 144 } 145 } 146 147 // 6 完全关闭广播,释放广播资源 148 public async stopAdvertising() { 149 try { 150 await ble.stopAdvertising(this.advHandle); 151 ble.off('advertisingStateChange', (data: ble.AdvertisingStateChangeInfo) => { 152 console.info(TAG, 'bluetooth advertising state = ' + JSON.stringify(data)); 153 }); 154 } catch (err) { 155 console.error(TAG, 'errCode: ' + (err as BusinessError).code + ', errMessage: ' + (err as BusinessError).message); 156 } 157 } 158 } 159 160 let bleAdvertisingManager = new BleAdvertisingManager(); 161 export default bleAdvertisingManager as BleAdvertisingManager; 162 ``` 163 1647. 错误码请参见[蓝牙服务子系统错误码](../../reference/apis-connectivity-kit/errorcode-bluetoothManager.md)。 165 166### 开启、关闭扫描 1671. import需要的ble模块。 1682. 开启设备的蓝牙。 1693. 需要申请权限ohos.permission.ACCESS_BLUETOOTH。 1704. 需要SystemCapability.Communication.Bluetooth.Core系统能力。 1715. 对端设备开启广播。 1726. 本端设备开启扫描,获取扫描结果。 1737. 关闭扫描。 1748. 示例代码: 175 176 ```ts 177 import { ble } from '@kit.ConnectivityKit'; 178 import { AsyncCallback, BusinessError } from '@kit.BasicServicesKit'; 179 180 const TAG: string = 'BleScanManager'; 181 const BLE_ADV_TYPE_FLAG = 0x01; 182 const BLE_ADV_TYPE_16_BIT_SERVICE_UUIDS_INCOMPLETE = 0x02; 183 const BLE_ADV_TYPE_16_BIT_SERVICE_UUIDS_COMPLETE = 0x03; 184 const BLE_ADV_TYPE_32_BIT_SERVICE_UUIDS_INCOMPLETE = 0x04; 185 const BLE_ADV_TYPE_32_BIT_SERVICE_UUIDS_COMPLETE = 0x05; 186 const BLE_ADV_TYPE_128_BIT_SERVICE_UUIDS_INCOMPLETE = 0x06; 187 const BLE_ADV_TYPE_128_BIT_SERVICE_UUIDS_COMPLETE = 0x07; 188 const BLE_ADV_TYPE_LOCAL_NAME_SHORT = 0x08; 189 const BLE_ADV_TYPE_LOCAL_NAME_COMPLETE = 0x09; 190 const BLE_ADV_TYPE_TX_POWER_LEVEL = 0x0A; 191 const BLE_ADV_TYPE_16_BIT_SERVICE_SOLICITATION_UUIDS = 0x14; 192 const BLE_ADV_TYPE_128_BIT_SERVICE_SOLICITATION_UUIDS = 0x15; 193 const BLE_ADV_TYPE_32_BIT_SERVICE_SOLICITATION_UUIDS = 0x1F; 194 const BLE_ADV_TYPE_16_BIT_SERVICE_DATA = 0x16; 195 const BLE_ADV_TYPE_32_BIT_SERVICE_DATA = 0x20; 196 const BLE_ADV_TYPE_128_BIT_SERVICE_DATA = 0x21; 197 const BLE_ADV_TYPE_MANUFACTURER_SPECIFIC_DATA = 0xFF; 198 199 const BLUETOOTH_UUID_16_BIT_LENGTH = 2; 200 const BLUETOOTH_UUID_32_BIT_LENGTH = 4; 201 const BLUETOOTH_UUID_128_BIT_LENGTH = 16; 202 203 const BLUETOOTH_MANUFACTURE_ID_LENGTH = 2; 204 205 export class BleScanManager { 206 // 1 订阅扫描结果 207 public onScanResult() { 208 ble.on('BLEDeviceFind', (data: Array<ble.ScanResult>) => { 209 if (data.length > 0) { 210 console.info(TAG, 'BLE scan result = ' + data[0].deviceId); 211 this.parseScanResult(data[0].data); 212 } 213 }); 214 } 215 216 private parseScanResult(data: ArrayBuffer) { 217 let advData = new Uint8Array(data); 218 if (advData.byteLength == 0) { 219 console.warn(TAG, 'nothing, adv data length is 0'); 220 return; 221 } 222 console.info(TAG, 'advData: ' + JSON.stringify(advData)); 223 224 let advFlags: number = -1; 225 let txPowerLevel: number = -1; 226 let localName: string = ""; 227 let serviceUuids: string[] = []; 228 let serviceSolicitationUuids: string[] = []; 229 let serviceDatas: Record<string, Uint8Array> = {}; 230 let manufactureSpecificDatas: Record<number, Uint8Array> = {}; 231 232 let curPos = 0; 233 while (curPos < advData.byteLength) { 234 let length = advData[curPos++]; 235 if (length == 0) { 236 break; 237 } 238 let advDataLength = length - 1; 239 let advDataType = advData[curPos++]; 240 switch (advDataType) { 241 case BLE_ADV_TYPE_FLAG: 242 advFlags = advData[curPos]; 243 break; 244 case BLE_ADV_TYPE_LOCAL_NAME_SHORT: 245 case BLE_ADV_TYPE_LOCAL_NAME_COMPLETE: 246 localName = advData.slice(curPos, curPos + advDataLength).toString(); 247 break; 248 case BLE_ADV_TYPE_TX_POWER_LEVEL: 249 txPowerLevel = advData[curPos]; 250 break; 251 case BLE_ADV_TYPE_16_BIT_SERVICE_UUIDS_INCOMPLETE: 252 case BLE_ADV_TYPE_16_BIT_SERVICE_UUIDS_COMPLETE: 253 this.parseServiceUuid(BLUETOOTH_UUID_16_BIT_LENGTH, curPos, advDataLength, advData, serviceUuids); 254 break; 255 case BLE_ADV_TYPE_32_BIT_SERVICE_UUIDS_INCOMPLETE: 256 case BLE_ADV_TYPE_32_BIT_SERVICE_UUIDS_COMPLETE: 257 this.parseServiceUuid(BLUETOOTH_UUID_32_BIT_LENGTH, curPos, advDataLength, advData, serviceUuids); 258 break; 259 case BLE_ADV_TYPE_128_BIT_SERVICE_UUIDS_INCOMPLETE: 260 case BLE_ADV_TYPE_128_BIT_SERVICE_UUIDS_COMPLETE: 261 this.parseServiceUuid(BLUETOOTH_UUID_128_BIT_LENGTH, curPos, advDataLength, advData, serviceUuids); 262 break; 263 case BLE_ADV_TYPE_16_BIT_SERVICE_SOLICITATION_UUIDS: 264 this.parseServiceSolicitationUuid(BLUETOOTH_UUID_16_BIT_LENGTH, curPos, advDataLength, 265 advData, serviceSolicitationUuids); 266 break; 267 case BLE_ADV_TYPE_32_BIT_SERVICE_SOLICITATION_UUIDS: 268 this.parseServiceSolicitationUuid(BLUETOOTH_UUID_32_BIT_LENGTH, curPos, advDataLength, 269 advData, serviceSolicitationUuids); 270 break; 271 case BLE_ADV_TYPE_128_BIT_SERVICE_SOLICITATION_UUIDS: 272 this.parseServiceSolicitationUuid(BLUETOOTH_UUID_128_BIT_LENGTH, curPos, advDataLength, 273 advData, serviceSolicitationUuids); 274 break; 275 case BLE_ADV_TYPE_16_BIT_SERVICE_DATA: 276 this.parseServiceData(BLUETOOTH_UUID_16_BIT_LENGTH, curPos, advDataLength, advData, serviceDatas); 277 break; 278 case BLE_ADV_TYPE_32_BIT_SERVICE_DATA: 279 this.parseServiceData(BLUETOOTH_UUID_32_BIT_LENGTH, curPos, advDataLength, advData, serviceDatas); 280 break; 281 case BLE_ADV_TYPE_128_BIT_SERVICE_DATA: 282 this.parseServiceData(BLUETOOTH_UUID_128_BIT_LENGTH, curPos, advDataLength, advData, serviceDatas); 283 break; 284 case BLE_ADV_TYPE_MANUFACTURER_SPECIFIC_DATA: 285 this.parseManufactureData(curPos, advDataLength, advData, manufactureSpecificDatas); 286 break; 287 default: 288 break; 289 } 290 curPos += advDataLength; 291 } 292 } 293 294 private parseServiceUuid(uuidLength: number, curPos: number, advDataLength: number, 295 advData: Uint8Array, serviceUuids: string[]) { 296 while (advDataLength > 0) { 297 let tmpData: Uint8Array = advData.slice(curPos, curPos + uuidLength); 298 serviceUuids.push(this.getUuidFromUint8Array(uuidLength, tmpData)); 299 advDataLength -= uuidLength; 300 curPos += uuidLength; 301 } 302 } 303 304 private parseServiceSolicitationUuid(uuidLength: number, curPos: number, advDataLength: number, 305 advData: Uint8Array, serviceSolicitationUuids: string[]) { 306 while (advDataLength > 0) { 307 let tmpData: Uint8Array = advData.slice(curPos, curPos + uuidLength); 308 serviceSolicitationUuids.push(this.getUuidFromUint8Array(uuidLength, tmpData)); 309 advDataLength -= uuidLength; 310 curPos += uuidLength; 311 } 312 } 313 314 private getUuidFromUint8Array(uuidLength: number, uuidData: Uint8Array): string { 315 let uuid = ""; 316 let temp: string = ""; 317 for (let i = uuidLength - 1; i > -1; i--) { 318 temp += uuidData[i].toString(16).padStart(2, "0"); 319 } 320 switch (uuidLength) { 321 case BLUETOOTH_UUID_16_BIT_LENGTH: 322 uuid = `0000${temp}-0000-1000-8000-00805F9B34FB`; 323 break; 324 case BLUETOOTH_UUID_32_BIT_LENGTH: 325 uuid = `${temp}-0000-1000-8000-00805F9B34FB`; 326 break; 327 case BLUETOOTH_UUID_128_BIT_LENGTH: 328 uuid = `${temp.substring(0, 8)}-${temp.substring(8, 12)}-${temp.substring(12, 16)}-${temp.substring(16, 20)}-${temp.substring(20, 32)}`; 329 break; 330 default: 331 break; 332 } 333 return uuid; 334 } 335 336 private parseServiceData(uuidLength: number, curPos: number, advDataLength: number, 337 advData: Uint8Array, serviceDatas: Record<string, Uint8Array>) { 338 let tmpUuid: Uint8Array = advData.slice(curPos, curPos + uuidLength); 339 let tmpValue: Uint8Array = advData.slice(curPos + uuidLength, curPos + advDataLength); 340 serviceDatas[tmpUuid.toString()] = tmpValue; 341 } 342 343 private parseManufactureData(curPos: number, advDataLength: number, 344 advData: Uint8Array, manufactureSpecificDatas: Record<number, Uint8Array>) { 345 let manufactureId: number = (advData[curPos + 1] << 8) + advData[curPos]; 346 let tmpValue: Uint8Array = advData.slice(curPos + BLUETOOTH_MANUFACTURE_ID_LENGTH, curPos + advDataLength); 347 manufactureSpecificDatas[manufactureId] = tmpValue; 348 } 349 350 // 2 开启扫描 351 public startScan() { 352 // 2.1 构造扫描过滤器,需要能够匹配预期的广播包内容 353 let manufactureId = 4567; 354 let manufactureData: Uint8Array = new Uint8Array([1, 2, 3, 4]); 355 let manufactureDataMask: Uint8Array = new Uint8Array([0xFF, 0xFF, 0xFF, 0xFF]); 356 let scanFilter: ble.ScanFilter = { // 根据业务实际情况定义过滤器 357 manufactureId: manufactureId, 358 manufactureData: manufactureData.buffer, 359 manufactureDataMask: manufactureDataMask.buffer 360 }; 361 362 // 2.2 构造扫描参数 363 let scanOptions: ble.ScanOptions = { 364 interval: 0, 365 dutyMode: ble.ScanDuty.SCAN_MODE_LOW_POWER, 366 matchMode: ble.MatchMode.MATCH_MODE_AGGRESSIVE 367 } 368 try { 369 this.onScanResult(); // 订阅扫描结果 370 ble.startBLEScan([scanFilter], scanOptions); 371 console.info(TAG, 'startBleScan success'); 372 } catch (err) { 373 console.error(TAG, 'errCode: ' + (err as BusinessError).code + ', errMessage: ' + (err as BusinessError).message); 374 } 375 } 376 377 // 3 关闭扫描 378 public stopScan() { 379 try { 380 ble.off('BLEDeviceFind', (data: Array<ble.ScanResult>) => { // 取消订阅扫描结果 381 console.info(TAG, 'off success'); 382 }); 383 ble.stopBLEScan(); 384 console.info(TAG, 'stopBleScan success'); 385 } catch (err) { 386 console.error(TAG, 'errCode: ' + (err as BusinessError).code + ', errMessage: ' + (err as BusinessError).message); 387 } 388 } 389 } 390 391 let bleScanManager = new BleScanManager(); 392 export default bleScanManager as BleScanManager; 393 ``` 394 3958. 错误码请参见[蓝牙服务子系统错误码](../../reference/apis-connectivity-kit/errorcode-bluetoothManager.md)。