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