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)。