如何识别EddystoneURL和uid?

How to identify Eddystone URL and uid?

我希望在不使用 Proximity Beacon API 或 Nearby Messages API 的情况下检测 Eddystone Ul 和 uid。我希望使用原生 android 库,如 BluetoothAdapter 或 BluetoothGatt 或 BluetoothGap 来解析 eddystone 帧。这可行吗?如果是这样的话,如果不可行,那么还有什么选择呢?

所有 Eddystone 数据都包含在蓝牙 4.0 ("Low Energy," "BLE") 广告数据包中,因此不需要 BluetoothGattBluetoothGap。请改用 BluetoothLeScanner。在 onScanResult 回调中,您可以通过以下方式访问广告数据:

// assuming `result` is the ScanResult passed to the `onScanResult` callback
byte[] rawData = result
    .getScanRecord()
    .getServiceData(ParcelUuid.fromString("0000FEAA-0000-1000-8000-00805F9B34FB"));

然后,您需要根据 Eddystone 规范解析字节:

UID: https://github.com/google/eddystone/tree/master/eddystone-uid
URL:https://github.com/google/eddystone/tree/master/eddystone-url

Eddystone 存储库中还包含一个示例项目,因此您可以从那里开始,也许可以重用一些代码:

https://github.com/google/eddystone/tree/master/tools/eddystone-validator

您可以使用本机 Android onLeScan 回调检测 Eddystone-UID 信标。我在此处的答案中发布了示例代码,展示了如何执行此操作:

public void onLeScan(BluetoothDevice device, int rssi, byte[] scanRecord) {
    for (int startByte = 0; startByte < scanRecord.length; startByte++) {
        if (scanRecord.length-startByte > 19) { // need at least 19 bytes for Eddystone-UID
            // Check that this has the right pattern needed for this to be Eddystone-UID
            if (scanRecord[startByte+0] == 0xaa && scanRecord[startByte+1] == 0xfe &&
                    scanRecord[startByte+2] == 0x00) {
                // This is an Eddystone-UID beacon.
                byte[] namespaceIdentifierBytes = Arrays.copyOfRange(scanRecord, startByte+4, startByte+13);
                byte[] instanceIdentifierBytes = Arrays.copyOfRange(scanRecord, startByte+14, startByte+19);
                // TODO: do something with the above identifiers here
            }
        }

    }
}

这样做的关键是理解和解析每种信标类型的字节布局。这就是开源 Android Beacon Library 通过为每个信标定义布局表达式来帮助解析来解析多种信标类型(如 Eddystone)的方式。即使您想推出自己的代码,也可以查看其源代码并使用它来了解其工作原理。我已经在上面链接的答案中发布了关于这种解析如何发生的描述。

以下是获取有关 Eddystone AFAIK 信息的最简单方法。

// onLeScan() method of BluetoothAdapter.LeScanCallback interface.
public void onLeScan(BluetoothDevice device, int rssi, byte[] scanRecord)
{
    // Parse the payload of the advertisement packet
    // as a list of AD structures.
    List<ADStructure> structures =
        ADPayloadParser.getInstance().parse(scanRecord);

    // For each AD structure contained in the advertisement packet.
    for (ADStructure structure : structures)
    {
        // If the AD structure represents Eddystone UID.
        if (structure instanceof EddystoneUID)
        {
            // Eddystone UID
            EddystoneUID es = (EddystoneUID)structure;

            Log.d(TAG, "Tx Power = "     + es.getTxPower());
            Log.d(TAG, "Namespace ID = " + es.getNamespaceIdAsString());
            Log.d(TAG, "Instance ID = "  + es.getInstanceIdAsString());
            Log.d(TAG, "Beacon ID = "    + es.getBeaconIdAsString());

            // As byte arrays if you want.
            byte[] namespaceId = es.getNamespaceId();
            byte[] instanceId  = es.getInstanceId();
            byte[] beaconId    = es.getBeaconId();
        }
        // If the AD structure represents Eddystone URL.
        else if (structure instanceof EddystoneURL)
        {
            // Eddystone URL
            EddystoneURL es = (EddystoneURL)structure;

            Log.d(TAG, "Tx Power = " + es.getTxPower());
            Log.d(TAG, "URL = "      + es.getURL());
        }
        // If the AD structure represents Eddystone TLM.
        else if (structure instanceof EddystoneTLM)
        {
            // Eddystone TLM
            EddystoneTLM es = (EddystoneTLM)structure;

            Log.d(TAG, "TLM Version = "         + es.getTLMVersion());
            Log.d(TAG, "Battery Voltage = "     + es.getBatteryVoltage());
            Log.d(TAG, "Beacon Temperature = "  + es.getBeaconTemperature());
            Log.d(TAG, "Advertisement Count = " + es.getAdvertisementCount());
            Log.d(TAG, "Elapsed Time = "        + es.getElapsedTime());
        }
    }
}

您不必了解 Eddystone specification if you use nv-bluetooth.

的详细信息

Gradle

dependencies {
    compile 'com.neovisionaries:nv-bluetooth:1.7'
}

Java文档

JavaDoc

备忘录

Eddystone TLM is expressed in a signed fixed-point notation. The code examples in How to detect Eddystone-Compatible Beacons (Android Beacon Library) don't show how to extract the data as a floating point number as of this writing. On the other hand, <a href="http://takahikokawasaki.github.io/nv-bluetooth/com/neovisionaries/bluetooth/ble/advertising/EddystoneTLM.html" rel="nofollow">EddystoneTLM</a>class中的Beacon Temperature在nv-bluetooth中有如下所示的方法,因此您不必解码定点符号。

public float getBeaconTemperature();

同样,<a href="http://takahikokawasaki.github.io/nv-bluetooth/com/neovisionaries/bluetooth/ble/advertising/EddystoneURL.html" rel="nofollow">EddystoneURL</a> class 有一种方法可以将 URL 作为 URL.

public URL getURL();

因此,当您使用 Android 信标库时,您不必执行如下所示的步骤。

String url = UrlBeaconUrlCompressor.uncompress(beacon.getId1().toByteArray());

nv-bluetooth将Eddystone相关的数据结构实现为继承树,如下图。这种合适的继承树在其他库中很难找到。

ADStructure
  |
  +-- ServiceData
        |
        +-- Eddystone
              |
              +-- EddystoneUID
              |
              +-- EddystoneURL
              |
              +-- EddystoneTLM

适当的继承树的好处之一是方法被放置在正确的位置。像这样:

ADStructure
  |  // AD Structure Length - 1
  |  int getLength();
  |
  |  // AD Type
  |  int getType();
  |
  |  // AD Data
  |  byte[] getData();
  |
  +-- ServiceData
  |     |  // Service UUID
  |     |  UUID getServiceUUID();
  |     |
  |     +-- Eddystone
  |           |  // Eddystone Frame Type
  |           |  FrameType getFrameType();
  |           |
  |           +-- EddystoneUID
  |           |     // Tx Power
  |           |     int getTxPower();
  |           |
  |           |     // Namespace ID (byte[])
  |           |     byte[] getNamespaceId();
  |           |
  |           |     // Instance ID (byte[])
  |           |     byte[] getInstanceId();
  |           |
  |           |     // Beacon ID (byte[])
  |           |     byte[] getBeaconId();
  |           |
  |           |     // Namespace ID (String)
  |           |     String getNamespaceIdAsString();
  |           |
  |           |     // Instance ID (String)
  |           |     String getInstanceIdAsString();
  |           |
  |           |     // Beacon ID (String)
  |           |     String getBeaconIdAsString();
  |           |
  |           +-- EddystoneURL
  |           |     // Tx Power
  |           |     int getTxPower();
  |           |
  |           |     // URL
  |           |     URL getURL();
  |           |
  |           +-- EddystoneTLM
  |                 // TLM Version
  |                 int getTLMVersion();
  |
  |                 // Battery Voltage
  |                 int getBatteryVoltage();
  |
  |                 // Beacon Temperature
  |                 float getBeaconTemperature();
  |
  |                 // Advertisement Count
  |                 long getAdvertisementCount();
  |
  |                 // Elapsed Time
  |                 long getElapsedTime();
  |
  +-- ADManufacturerSpecific
  |      |  // Company ID
  |      |  int getCompanyId();
  |      |
  |      +-- IBeacon
  |      |     // Major Number
  |      |     int getMajor();
  |      |
  |      |     (abbrev)
  |      |
  |      +-- Ucode
  |      |     // Ucode
  |      |     String getUcode();
  |      |
  |      |     (abbrev)