1# BLE Advertising and Scanning 2 3## Introduction 4 5Bluetooth advertising and scanning help discover Bluetooth-enabled devices and implement BLE communication. This topic walks you through on how to start and stop Bluetooth advertising and scanning. 6 7## When to Use 8 9You can use the APIs provided by the **ble** module to: 10 11- Start and stop BLE advertising. 12- Start and stop BLE scanning. 13 14## Available APIs 15 16For details about the APIs and sample code, see [@ohos.bluetooth.ble](../../reference/apis-connectivity-kit/js-apis-bluetooth-ble.md). 17 18The following table describes the related APIs. 19 20| API | Description | 21| ---------------------------------- | ------------------------------------------------------------------------------ | 22| startBLEScan() | Starts BLE scanning. | 23| stopBLEScan() | Stops BLE scanning. | 24| startAdvertising() | Starts BLE advertising. | 25| disableAdvertising() | Disables BLE advertising temporarily. | 26| enableAdvertising() | Enables BLE advertising temporarily. | 27| stopAdvertising() | Stops BLE advertising. | 28| on(type: 'advertisingStateChange') | Subscribes to BLE advertising state changes. | 29| off(type: 'advertisingStateChange')| Unsubscribes from BLE advertising state changes. | 30| on(type: 'BLEDeviceFind') | Subscribes to the BLE device discovery event. | 31| off(type: 'BLEDeviceFind') | Unsubscribes from the BLE device discovery event. | 32 33## How to Develop 34 35### Starting and Stopping BLE Advertising 361. Import the **ble** module. 372. Enable Bluetooth on the device. 383. Apply for the **ohos.permission.ACCESS_BLUETOOTH** permission. 394. Check that the SystemCapability.Communication.Bluetooth.Core capability is available. 405. Start BLE advertising. The peer device scans the advertisement. 416. Stop BLE advertising. 427. Example: 43 44 ```ts 45 import { ble } from '@kit.ConnectivityKit'; 46 import { AsyncCallback, BusinessError } from '@kit.BasicServicesKit'; 47 48 const TAG: string = 'BleAdvertisingManager'; 49 50 export class BleAdvertisingManager { 51 private advHandle: number = 0xFF; // default invalid value 52 53 // 1. Subscribe to BLE advertising state changes. 54 public onAdvertisingStateChange() { 55 try { 56 ble.on('advertisingStateChange', (data: ble.AdvertisingStateChangeInfo) => { 57 console.info(TAG, 'bluetooth advertising state = ' + JSON.stringify(data)); 58 AppStorage.setOrCreate('advertiserState', data.state); 59 }); 60 } catch (err) { 61 console.error(TAG, 'errCode: ' + (err as BusinessError).code + ', errMessage: ' + (err as BusinessError).message); 62 } 63 } 64 65 // 2. Start BLE advertising the first time. 66 public async startAdvertising() { 67 // 2.1 Set BLE advertising parameters. 68 let setting: ble.AdvertiseSetting = { 69 interval: 160, 70 txPower: 0, 71 connectable: true 72 }; 73 // 2.2 Construct the data to be advertised. 74 let manufactureValueBuffer = new Uint8Array(4); 75 manufactureValueBuffer[0] = 1; 76 manufactureValueBuffer[1] = 2; 77 manufactureValueBuffer[2] = 3; 78 manufactureValueBuffer[3] = 4; 79 let serviceValueBuffer = new Uint8Array(4); 80 serviceValueBuffer[0] = 5; 81 serviceValueBuffer[1] = 6; 82 serviceValueBuffer[2] = 7; 83 serviceValueBuffer[3] = 8; 84 let manufactureDataUnit: ble.ManufactureData = { 85 manufactureId: 4567, 86 manufactureValue: manufactureValueBuffer.buffer 87 }; 88 let serviceDataUnit: ble.ServiceData = { 89 serviceUuid: "00001888-0000-1000-8000-00805f9b34fb", 90 serviceValue: serviceValueBuffer.buffer 91 }; 92 let advData: ble.AdvertiseData = { 93 serviceUuids: ["00001888-0000-1000-8000-00805f9b34fb"], 94 manufactureData: [manufactureDataUnit], 95 serviceData: [serviceDataUnit], 96 includeDeviceName: false // Whether the device name is carried. This parameter is optional. Note that the length of an advertising packet including the device name cannot exceed 31 bytes. 97 }; 98 let advResponse: ble.AdvertiseData = { 99 serviceUuids: ["00001888-0000-1000-8000-00805f9b34fb"], 100 manufactureData: [manufactureDataUnit], 101 serviceData: [serviceDataUnit] 102 }; 103 // 2.3 Construct AdvertisingParams for starting the BLE advertising. 104 let advertisingParams: ble.AdvertisingParams = { 105 advertisingSettings: setting, 106 advertisingData: advData, 107 advertisingResponse: advResponse, 108 duration: 0 // This parameter is optional. If the value is greater than 0, the advertising stops temporarily after a period of time. You can restart it as required. 109 } 110 111 // 2.4 Start BLE advertising the first time. The ID of the BLE advertising is returned. 112 try { 113 this.onAdvertisingStateChange(); 114 this.advHandle = await ble.startAdvertising(advertisingParams); 115 } catch (err) { 116 console.error(TAG, 'errCode: ' + (err as BusinessError).code + ', errMessage: ' + (err as BusinessError).message); 117 } 118 } 119 120 // 4. Disable the BLE advertising temporarily. The advertising resources still exist. 121 public async disableAdvertising() { 122 // 4.1 Construct parameters for temporarily disabling the BLE advertising. 123 let advertisingDisableParams: ble.AdvertisingDisableParams = { 124 advertisingId: this.advHandle // Use the ID of the first BLE advertising. 125 } 126 // 4.2 Disable the BLE advertising temporarily. 127 try { 128 await ble.disableAdvertising(advertisingDisableParams); 129 } catch (err) { 130 console.error(TAG, 'errCode: ' + (err as BusinessError).code + ', errMessage: ' + (err as BusinessError).message); 131 } 132 } 133 134 // 5. Enable the BLE advertising again. 135 public async enableAdvertising(enableDuration: number) { 136 // 5.1 Construct the parameters for temporarily enabling the advertising. 137 let advertisingEnableParams: ble.AdvertisingEnableParams = { 138 advertisingId: this.advHandle // Use the ID of the first BLE advertising. 139 duration: enableDuration 140 } 141 // 5.2 Enable BLE advertising again. 142 try { 143 await ble.enableAdvertising(advertisingEnableParams); 144 } catch (err) { 145 console.error(TAG, 'errCode: ' + (err as BusinessError).code + ', errMessage: ' + (err as BusinessError).message); 146 } 147 } 148 149 // 6. Stop BLE advertising and release related resources. 150 public async stopAdvertising() { 151 try { 152 await ble.stopAdvertising(this.advHandle); 153 ble.off('advertisingStateChange', (data: ble.AdvertisingStateChangeInfo) => { 154 console.info(TAG, 'bluetooth advertising state = ' + JSON.stringify(data)); 155 }); 156 } catch (err) { 157 console.error(TAG, 'errCode: ' + (err as BusinessError).code + ', errMessage: ' + (err as BusinessError).message); 158 } 159 } 160 } 161 162 let bleAdvertisingManager = new BleAdvertisingManager(); 163 export default bleAdvertisingManager as BleAdvertisingManager; 164 ``` 165 1667. For details about the error codes, see [Bluetooth Error Codes](../../reference/apis-connectivity-kit/errorcode-bluetoothManager.md). 167 168### Starting and Stop BLE Scanning 1691. Import the **ble** module. 1702. Enable Bluetooth on the device. 1713. Apply for the **ohos.permission.ACCESS_BLUETOOTH** permission. 1724. Check that the SystemCapability.Communication.Bluetooth.Core capability is available. 1735. Start BLE advertising on the peer device. 1746. Start BLE scanning on the local device. 1757. Stop BLE scanning. 1768. Example: 177 178 ```ts 179 import { ble } from '@kit.ConnectivityKit'; 180 import { AsyncCallback, BusinessError } from '@kit.BasicServicesKit'; 181 182 const TAG: string = 'BleScanManager'; 183 const BLE_ADV_TYPE_FLAG = 0x01; 184 const BLE_ADV_TYPE_16_BIT_SERVICE_UUIDS_INCOMPLETE = 0x02; 185 const BLE_ADV_TYPE_16_BIT_SERVICE_UUIDS_COMPLETE = 0x03; 186 const BLE_ADV_TYPE_32_BIT_SERVICE_UUIDS_INCOMPLETE = 0x04; 187 const BLE_ADV_TYPE_32_BIT_SERVICE_UUIDS_COMPLETE = 0x05; 188 const BLE_ADV_TYPE_128_BIT_SERVICE_UUIDS_INCOMPLETE = 0x06; 189 const BLE_ADV_TYPE_128_BIT_SERVICE_UUIDS_COMPLETE = 0x07; 190 const BLE_ADV_TYPE_LOCAL_NAME_SHORT = 0x08; 191 const BLE_ADV_TYPE_LOCAL_NAME_COMPLETE = 0x09; 192 const BLE_ADV_TYPE_TX_POWER_LEVEL = 0x0A; 193 const BLE_ADV_TYPE_16_BIT_SERVICE_SOLICITATION_UUIDS = 0x14; 194 const BLE_ADV_TYPE_128_BIT_SERVICE_SOLICITATION_UUIDS = 0x15; 195 const BLE_ADV_TYPE_32_BIT_SERVICE_SOLICITATION_UUIDS = 0x1F; 196 const BLE_ADV_TYPE_16_BIT_SERVICE_DATA = 0x16; 197 const BLE_ADV_TYPE_32_BIT_SERVICE_DATA = 0x20; 198 const BLE_ADV_TYPE_128_BIT_SERVICE_DATA = 0x21; 199 const BLE_ADV_TYPE_MANUFACTURER_SPECIFIC_DATA = 0xFF; 200 201 const BLUETOOTH_UUID_16_BIT_LENGTH = 2; 202 const BLUETOOTH_UUID_32_BIT_LENGTH = 4; 203 const BLUETOOTH_UUID_128_BIT_LENGTH = 16; 204 205 const BLUETOOTH_MANUFACTURE_ID_LENGTH = 2; 206 207 export class BleScanManager { 208 // 1. Subscribe to the scanning result. 209 public onScanResult() { 210 ble.on('BLEDeviceFind', (data: Array<ble.ScanResult>) => { 211 if (data.length > 0) { 212 console.info(TAG, 'BLE scan result = ' + data[0].deviceId); 213 this.parseScanResult(data[0].data); 214 } 215 }); 216 } 217 218 private parseScanResult(data: ArrayBuffer) { 219 let advData = new Uint8Array(data); 220 if (advData.byteLength == 0) { 221 console.warn(TAG, 'nothing, adv data length is 0'); 222 return; 223 } 224 console.info(TAG, 'advData: ' + JSON.stringify(advData)); 225 226 let advFlags: number = -1; 227 let txPowerLevel: number = -1; 228 let localName: string = ""; 229 let serviceUuids: string[] = []; 230 let serviceSolicitationUuids: string[] = []; 231 let serviceDatas: Record<string, Uint8Array> = {}; 232 let manufactureSpecificDatas: Record<number, Uint8Array> = {}; 233 234 let curPos = 0; 235 while (curPos < advData.byteLength) { 236 let length = advData[curPos++]; 237 if (length == 0) { 238 break; 239 } 240 let advDataLength = length - 1; 241 let advDataType = advData[curPos++]; 242 switch (advDataType) { 243 case BLE_ADV_TYPE_FLAG: 244 advFlags = advData[curPos]; 245 break; 246 case BLE_ADV_TYPE_LOCAL_NAME_SHORT: 247 case BLE_ADV_TYPE_LOCAL_NAME_COMPLETE: 248 localName = advData.slice(curPos, curPos + advDataLength).toString(); 249 break; 250 case BLE_ADV_TYPE_TX_POWER_LEVEL: 251 txPowerLevel = advData[curPos]; 252 break; 253 case BLE_ADV_TYPE_16_BIT_SERVICE_UUIDS_INCOMPLETE: 254 case BLE_ADV_TYPE_16_BIT_SERVICE_UUIDS_COMPLETE: 255 this.parseServiceUuid(BLUETOOTH_UUID_16_BIT_LENGTH, curPos, advDataLength, advData, serviceUuids); 256 break; 257 case BLE_ADV_TYPE_32_BIT_SERVICE_UUIDS_INCOMPLETE: 258 case BLE_ADV_TYPE_32_BIT_SERVICE_UUIDS_COMPLETE: 259 this.parseServiceUuid(BLUETOOTH_UUID_32_BIT_LENGTH, curPos, advDataLength, advData, serviceUuids); 260 break; 261 case BLE_ADV_TYPE_128_BIT_SERVICE_UUIDS_INCOMPLETE: 262 case BLE_ADV_TYPE_128_BIT_SERVICE_UUIDS_COMPLETE: 263 this.parseServiceUuid(BLUETOOTH_UUID_128_BIT_LENGTH, curPos, advDataLength, advData, serviceUuids); 264 break; 265 case BLE_ADV_TYPE_16_BIT_SERVICE_SOLICITATION_UUIDS: 266 this.parseServiceSolicitationUuid(BLUETOOTH_UUID_16_BIT_LENGTH, curPos, advDataLength, 267 advData, serviceSolicitationUuids); 268 break; 269 case BLE_ADV_TYPE_32_BIT_SERVICE_SOLICITATION_UUIDS: 270 this.parseServiceSolicitationUuid(BLUETOOTH_UUID_32_BIT_LENGTH, curPos, advDataLength, 271 advData, serviceSolicitationUuids); 272 break; 273 case BLE_ADV_TYPE_128_BIT_SERVICE_SOLICITATION_UUIDS: 274 this.parseServiceSolicitationUuid(BLUETOOTH_UUID_128_BIT_LENGTH, curPos, advDataLength, 275 advData, serviceSolicitationUuids); 276 break; 277 case BLE_ADV_TYPE_16_BIT_SERVICE_DATA: 278 this.parseServiceData(BLUETOOTH_UUID_16_BIT_LENGTH, curPos, advDataLength, advData, serviceDatas); 279 break; 280 case BLE_ADV_TYPE_32_BIT_SERVICE_DATA: 281 this.parseServiceData(BLUETOOTH_UUID_32_BIT_LENGTH, curPos, advDataLength, advData, serviceDatas); 282 break; 283 case BLE_ADV_TYPE_128_BIT_SERVICE_DATA: 284 this.parseServiceData(BLUETOOTH_UUID_128_BIT_LENGTH, curPos, advDataLength, advData, serviceDatas); 285 break; 286 case BLE_ADV_TYPE_MANUFACTURER_SPECIFIC_DATA: 287 this.parseManufactureData(curPos, advDataLength, advData, manufactureSpecificDatas); 288 break; 289 default: 290 break; 291 } 292 curPos += advDataLength; 293 } 294 } 295 296 private parseServiceUuid(uuidLength: number, curPos: number, advDataLength: number, 297 advData: Uint8Array, serviceUuids: string[]) { 298 while (advDataLength > 0) { 299 let tmpData: Uint8Array = advData.slice(curPos, curPos + uuidLength); 300 serviceUuids.push(this.getUuidFromUint8Array(uuidLength, tmpData)); 301 advDataLength -= uuidLength; 302 curPos += uuidLength; 303 } 304 } 305 306 private parseServiceSolicitationUuid(uuidLength: number, curPos: number, advDataLength: number, 307 advData: Uint8Array, serviceSolicitationUuids: string[]) { 308 while (advDataLength > 0) { 309 let tmpData: Uint8Array = advData.slice(curPos, curPos + uuidLength); 310 serviceSolicitationUuids.push(this.getUuidFromUint8Array(uuidLength, tmpData)); 311 advDataLength -= uuidLength; 312 curPos += uuidLength; 313 } 314 } 315 316 private getUuidFromUint8Array(uuidLength: number, uuidData: Uint8Array): string { 317 let uuid = ""; 318 let temp: string = ""; 319 for (let i = uuidLength - 1; i > -1; i--) { 320 temp += uuidData[i].toString(16).padStart(2, "0"); 321 } 322 switch (uuidLength) { 323 case BLUETOOTH_UUID_16_BIT_LENGTH: 324 uuid = `0000${temp}-0000-1000-8000-00805F9B34FB`; 325 break; 326 case BLUETOOTH_UUID_32_BIT_LENGTH: 327 uuid = `${temp}-0000-1000-8000-00805F9B34FB`; 328 break; 329 case BLUETOOTH_UUID_128_BIT_LENGTH: 330 uuid = `${temp.substring(0, 8)}-${temp.substring(8, 12)}-${temp.substring(12, 16)}-${temp.substring(16, 20)}-${temp.substring(20, 32)}`; 331 break; 332 default: 333 break; 334 } 335 return uuid; 336 } 337 338 private parseServiceData(uuidLength: number, curPos: number, advDataLength: number, 339 advData: Uint8Array, serviceDatas: Record<string, Uint8Array>) { 340 let tmpUuid: Uint8Array = advData.slice(curPos, curPos + uuidLength); 341 let tmpValue: Uint8Array = advData.slice(curPos + uuidLength, curPos + advDataLength); 342 serviceDatas[tmpUuid.toString()] = tmpValue; 343 } 344 345 private parseManufactureData(curPos: number, advDataLength: number, 346 advData: Uint8Array, manufactureSpecificDatas: Record<number, Uint8Array>) { 347 let manufactureId: number = (advData[curPos + 1] << 8) + advData[curPos]; 348 let tmpValue: Uint8Array = advData.slice(curPos + BLUETOOTH_MANUFACTURE_ID_LENGTH, curPos + advDataLength); 349 manufactureSpecificDatas[manufactureId] = tmpValue; 350 } 351 352 // 2. Start scanning. 353 public startScan() { 354 // 2.1 Construct a scan filter that matches the expected advertising packet content. 355 let manufactureId = 4567; 356 let manufactureData: Uint8Array = new Uint8Array([1, 2, 3, 4]); 357 let manufactureDataMask: Uint8Array = new Uint8Array([0xFF, 0xFF, 0xFF, 0xFF]); 358 let scanFilter: ble.ScanFilter = {// Define the filter based on service requirements. 359 manufactureId: manufactureId, 360 manufactureData: manufactureData.buffer, 361 manufactureDataMask: manufactureDataMask.buffer 362 }; 363 364 // 2.2 Construct scanning parameters. 365 let scanOptions: ble.ScanOptions = { 366 interval: 0, 367 dutyMode: ble.ScanDuty.SCAN_MODE_LOW_POWER, 368 matchMode: ble.MatchMode.MATCH_MODE_AGGRESSIVE 369 } 370 try { 371 this.onScanResult(); // Subscribe to the scanning result. 372 ble.startBLEScan([scanFilter], scanOptions); 373 console.info(TAG, 'startBleScan success'); 374 } catch (err) { 375 console.error(TAG, 'errCode: ' + (err as BusinessError).code + ', errMessage: ' + (err as BusinessError).message); 376 } 377 } 378 379 // 3. Stop scanning. 380 public stopScan() { 381 try { 382 ble.off('BLEDeviceFind', (data: Array<ble.ScanResult>) => { // Unsubscribe from the scanning result. 383 console.info(TAG, 'off success'); 384 }); 385 ble.stopBLEScan(); 386 console.info(TAG, 'stopBleScan success'); 387 } catch (err) { 388 console.error(TAG, 'errCode: ' + (err as BusinessError).code + ', errMessage: ' + (err as BusinessError).message); 389 } 390 } 391 } 392 393 let bleScanManager = new BleScanManager(); 394 export default bleScanManager as BleScanManager; 395 ``` 396 3978. For details about the error codes, see [Bluetooth Error Codes](../../reference/apis-connectivity-kit/errorcode-bluetoothManager.md). 398