跳到主要内容

发起星闪扫描

场景介绍

发起星闪扫描,可以扫描到正在发送星闪广播的外围设备。

接口说明

接口名描述
startScan(filters: Array<ScanFilters>, options?: ScanOptions): Promise<void>启动星闪扫描。
stopScan(): Promise<void>停止星闪扫描。
on(type: 'deviceFound', callback: Callback<Array<ScanResults>>): void订阅扫描结果。
off(type: 'deviceFound', callback?: Callback<Array<ScanResults>>): void取消订阅扫描结果。

开发步骤

  1. 导入相关模块。

    import { scan } from '@kit.NearLinkKit';
    import { BusinessError } from '@kit.BasicServicesKit';
    import { util } from '@kit.ArkTS';
  2. 定义扫描结果回调,解析扫描结果。

    const SLE_ADV_DATA_TYPE_DISCOVERY_LEVEL = 0x01;
    const SLE_ADV_DATA_TYPE_SERVICE_DATA_16BIT_UUID = 0x03;
    const SLE_ADV_DATA_TYPE_SERVICE_DATA_128BIT_UUID = 0x04;
    const SLE_ADV_DATA_TYPE_COMPLETE_LIST_OF_16BIT_SERVICE_UUIDS = 0x05;
    const SLE_ADV_DATA_TYPE_COMPLETE_LIST_OF_128BIT_SERVICE_UUIDS = 0x06;
    const SLE_ADV_DATA_TYPE_INCOMPLETE_LIST_OF_16BIT_SERVICE_UUIDS = 0x07;
    const SLE_ADV_DATA_TYPE_INCOMPLETE_LIST_OF_128BIT_SERVICE_UUIDS = 0x08;
    const SLE_ADV_DATA_TYPE_SHORTENED_LOCAL_NAME = 0x0A;
    const SLE_ADV_DATA_TYPE_COMPLETE_LOCAL_NAME = 0x0B;
    const SLE_ADV_DATA_TYPE_MANUFACTURER_SPECIFIC_DATA = 0xFF;

    const NEARLINK_UUID_16_BIT_LENGTH = 2;
    const NEARLINK_UUID_128_BIT_LENGTH = 16;

    const NEARLINK_MANUFACTURE_ID_LENGTH = 2;

    // 定义扫描结果回调
    let onReceiveEvent:(data: Array<scan.ScanResults>) => void = (data: Array<scan.ScanResults>) => {
    console.info('scan result addr:'+ data[0].address + 'name:' + data[0].deviceName);
    parseScanResult(data[0].data);
    }

    // 按照数据类型解析扫描结果
    function parseScanResult(data: ArrayBuffer) {
    let advData = new Uint8Array(data);
    if (advData.byteLength == 0) {
    console.warn('nothing, adv data length is 0');
    return;
    }
    console.info('advData: ' + JSON.stringify(advData));

    let discoveryLevel: number = -1;
    let serviceData: Record<string, Uint8Array> = {};
    let standardUuids: string[] = [];
    let specificUuids: string[] = [];
    let localName: string = "";
    let manufactureSpecificData: Record<number, Uint8Array> = {};

    let curPos: number= 0;
    while (curPos < advData.byteLength) {
    let advDataType: number = advData[curPos++];
    let advDataLength: number = advData[curPos++];
    if (advDataLength == 0) {
    break;
    }
    switch (advDataType) {
    case SLE_ADV_DATA_TYPE_DISCOVERY_LEVEL: // 发现等级
    discoveryLevel = advData[curPos];
    break;
    case SLE_ADV_DATA_TYPE_SERVICE_DATA_16BIT_UUID: // 标准服务数据信息
    parseServiceData(NEARLINK_UUID_16_BIT_LENGTH, curPos, advDataLength, advData, serviceData);
    break;
    case SLE_ADV_DATA_TYPE_SERVICE_DATA_128BIT_UUID: // 自定义服务数据信息
    parseServiceData(NEARLINK_UUID_128_BIT_LENGTH, curPos, advDataLength, advData, serviceData);
    break;
    case SLE_ADV_DATA_TYPE_COMPLETE_LIST_OF_16BIT_SERVICE_UUIDS: // 完整标准服务标识列表
    case SLE_ADV_DATA_TYPE_INCOMPLETE_LIST_OF_16BIT_SERVICE_UUIDS: // 部分标准服务标识列表
    parseServiceUuid(NEARLINK_UUID_16_BIT_LENGTH, curPos, advDataLength, advData, standardUuids);
    break;
    case SLE_ADV_DATA_TYPE_COMPLETE_LIST_OF_128BIT_SERVICE_UUIDS: // 完整自定义服务标识列表
    case SLE_ADV_DATA_TYPE_INCOMPLETE_LIST_OF_128BIT_SERVICE_UUIDS: // 部分自定义服务标识列表
    parseServiceUuid(NEARLINK_UUID_128_BIT_LENGTH, curPos, advDataLength, advData, specificUuids);
    break;
    case SLE_ADV_DATA_TYPE_SHORTENED_LOCAL_NAME: // 设备缩写本地名称
    case SLE_ADV_DATA_TYPE_COMPLETE_LOCAL_NAME: // 设备完整本地名称
    let tmpName: Uint8Array = advData.slice(curPos, curPos + advDataLength);
    let decoder = util.TextDecoder.create('utf-8');
    localName = decoder.decodeToString(new Uint8Array(tmpName));
    break;
    case SLE_ADV_DATA_TYPE_MANUFACTURER_SPECIFIC_DATA: // 厂商自定义信息
    parseManufactureData(curPos, advDataLength, advData, manufactureSpecificData);
    break;
    default:
    break;
    }
    curPos += advDataLength;
    }
    }

    // 解析服务数据信息
    function parseServiceData (uuidLength: number, curPos: number, advDataLength: number,
    advData: Uint8Array, serviceData: Record<string, Uint8Array>) {
    let tmpUuid: Uint8Array = advData.slice(curPos, curPos + uuidLength);
    getUuidFromUint8Array(uuidLength, tmpUuid);
    let tmpValue: Uint8Array = advData.slice(curPos + uuidLength, curPos + advDataLength);
    serviceData[tmpUuid.toString()] = tmpValue;
    }

    // 解析服务标识列表
    function parseServiceUuid (uuidLength: number, curPos: number, advDataLength: number,
    advData: Uint8Array, serviceUuids: string[]) {
    while (advDataLength > 0) {
    let tmpData: Uint8Array = advData.slice(curPos, curPos + uuidLength);
    serviceUuids.push(getUuidFromUint8Array(uuidLength, tmpData));
    advDataLength -= uuidLength;
    curPos += uuidLength;
    }
    }

    // 解析厂商自定义信息
    function parseManufactureData(curPos: number, advDataLength: number,
    advData: Uint8Array, manufactureSpecificData: Record<number, Uint8Array>) {
    let manufactureId: number = (advData[curPos + 1] << 8) + advData[curPos];
    let tmpValue: Uint8Array = advData.slice(curPos + NEARLINK_MANUFACTURE_ID_LENGTH, curPos + advDataLength);
    manufactureSpecificData[manufactureId] = tmpValue;
    }

    // 解析UUID
    function getUuidFromUint8Array(uuidLength: number, uuidData: Uint8Array): string {
    let uuid: string = '';
    let temp: string = '';
    for (let i = uuidLength - 1; i > -1; i--) {
    temp += uuidData[i].toString(16).padStart(2, '0');
    }
    switch (uuidLength) {
    case NEARLINK_UUID_16_BIT_LENGTH:
    uuid = `37BEA880-FC70-11EA-B720-00000000${temp}`;
    break;
    case NEARLINK_UUID_128_BIT_LENGTH:
    uuid = `${temp.substring(0, 8)}-${temp.substring(8, 12)}-${temp.substring(12, 16)}-${temp.substring(16,
    20)}-${temp.substring(20, 32)}`;
    break;
    default:
    break;
    }
    return uuid;
    }
  3. 订阅扫描结果。

    try {
    scan.on("deviceFound", onReceiveEvent);
    // 订阅星闪扫描结果。返回的扫描结果中携带的地址为远端设备随机地址。
    } catch (err) {
    console.error('errCode: ' + (err as BusinessError).code + ', errMessage: ' + (err as BusinessError).message);
    }
  4. 配置扫描参数,扫描过滤器配置期望的设备名称、地址等信息。

    1. 扫描过滤器至少携带一个过滤条件,否则扫描过滤器无效。
    2. 过滤器可以配置多组,组之间的条件是或的关系,如步骤5所示。
    3. 一组过滤器内的条件是与的关系,如下示例:address和deviceName同时满足才会上报。
    let scanFilter1: scan.ScanFilters = {
    address:"11:22:33:44:AA:BB", // 期望扫描到的外围设备1的地址
    deviceName:"deviceName1" // 期望扫描到的外围设备1的名称
    };
    let scanFilter2: scan.ScanFilters = {
    address:"22:33:44:AB:CD:EF", // 期望扫描到的外围设备2的地址
    deviceName:"deviceName2" // 期望扫描到的外围设备2的名称
    };
    let scanOptions: scan.ScanOptions = {
    scanMode: scan.ScanMode.SCAN_MODE_LOW_POWER
    }
  5. 开启星闪扫描,参数配置在步骤4中构造。

    try {
    scan.startScan([scanFilter1, scanFilter2], scanOptions).then(() => {
    console.info("start scan success");
    }).catch ((err: BusinessError) => {
    console.error('errCode: ' + err.code + ', errMessage: ' + err.message);
    });
    } catch (err) {
    console.error('errCode: ' + (err as BusinessError).code + ', errMessage: ' + (err as BusinessError).message);
    }
  6. 停止星闪扫描。

    try {
    scan.stopScan().then(() => {
    console.info("stop scan success");
    }).catch ((err: BusinessError) => {
    console.error('errCode: ' + err.code + ', errMessage: ' + err.message);
    });
    } catch (err) {
    console.error('errCode: ' + (err as BusinessError).code + ', errMessage: ' + (err as BusinessError).message);
    }
  7. 取消订阅扫描结果,其中onReceiveEvent是在步骤3中注册的回调函数。

    try {
    scan.off("deviceFound", onReceiveEvent);
    } catch (err) {
    console.error('errCode: ' + (err as BusinessError).code + ', errMessage: ' + (err as BusinessError).message);
    }

示例代码

星闪扫描场景可参考星闪示例代码,entry/src/main/ets/pages/ScanConfigPage.ets中的实现方法。