【问题标题】:BLE obtain uuid encoded in advertising packetBLE 获取广告包中编码的 uuid
【发布时间】:2014-02-25 13:53:29
【问题描述】:

我正在尝试获取 ble 设备的 UUID。我一直在关注 android 开发人员指南,到目前为止我只能获得设备名称和 rssi。我试图获取设备的 Uuid 扫描方法,如下所示:

    public void onLeScan(final BluetoothDevice device, int rssi,byte[] scanRecord) {

        ParcelUuid[] myUUid =device.getUuids();
        for(ParcelUuid a :myUUid){
            Log.d("UUID",a.getUuid().toString());
        }
        String s = new String(scanRecord);
        int len = scanRecord.length;
        String scanRecords =new String(scanRecord) ;



        deviceMap.put(device.getName().toString(), rssi);
        Message msg = MainActivity.myHandler.obtainMessage();
        Bundle bundle = new Bundle();
        bundle.putCharSequence("dev_name", device.getName().toString());
        bundle.putCharSequence("rssi", Integer.toString(rssi));
        msg.setData(bundle);
        MainActivity.myHandler.sendMessage(msg);
   }

这会返回 - btif_gattc_upstreams_evt:事件 4096

【问题讨论】:

  • 哪个 UUID?你的目标是什么?
  • 我刚刚扫描的蓝牙设备uuid
  • 但是哪个 UUID?一个设备通常有很多。或者,您想满足什么类型的设备和应用需求?
  • 我对服务 UUID 不感兴趣,但对 ble 设备 uuid 感兴趣。据我所知,它是蓝牙的唯一标识符。 iOS 应用可以在广告包中获取。
  • 不,一般情况下,BLE 设备中没有用于此目的的 UUID,尽管有设备地址。你的意思是 iBeacon UUID 吗?如果是这样,在 Android 上,您必须从广告数据包中解析出来 - 就 BLE 标准和堆栈而言,这些字节只是制造商特定的数据字段。

标签: android bluetooth


【解决方案1】:

如果您想获取 UUID / 任何其他数据,例如BLE Scan后的scanRec[] bytes的Manufacturer Data,首先需要了解那些Advertisement Data包的数据格式。

来自 Bluetooth.org:

理论太多,想看一些sn-p的代码吗?下面的这个函数将直接打印解析的原始数据字节。现在,你需要知道每种类型的代码才能知道什么数据包指的是什么信息。例如Type:0x09,指BLE Device Name,Type:0x07,指UUID。

public void printScanRecord (byte[] scanRecord) {

    // Simply print all raw bytes   
    try {
        String decodedRecord = new String(scanRecord,"UTF-8");
        Log.d("DEBUG","decoded String : " + ByteArrayToString(scanRecord));
    } catch (UnsupportedEncodingException e) {
        e.printStackTrace();
    }

    // Parse data bytes into individual records
    List<AdRecord> records = AdRecord.parseScanRecord(scanRecord);


    // Print individual records 
    if (records.size() == 0) {
        Log.i("DEBUG", "Scan Record Empty");
    } else {
        Log.i("DEBUG", "Scan Record: " + TextUtils.join(",", records));
    }

}


public static String ByteArrayToString(byte[] ba)
{
  StringBuilder hex = new StringBuilder(ba.length * 2);
  for (byte b : ba)
    hex.append(b + " ");

  return hex.toString();
}


public static class AdRecord {

    public AdRecord(int length, int type, byte[] data) {
        String decodedRecord = "";
        try {
            decodedRecord = new String(data,"UTF-8");

        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }

        Log.d("DEBUG", "Length: " + length + " Type : " + type + " Data : " + ByteArrayToString(data));         
    }

    // ...

    public static List<AdRecord> parseScanRecord(byte[] scanRecord) {
        List<AdRecord> records = new ArrayList<AdRecord>();

        int index = 0;
        while (index < scanRecord.length) {
            int length = scanRecord[index++];
            //Done once we run out of records
            if (length == 0) break;

            int type = scanRecord[index];
            //Done if our record isn't a valid type
            if (type == 0) break;

            byte[] data = Arrays.copyOfRange(scanRecord, index+1, index+length);

            records.add(new AdRecord(length, type, data));
            //Advance
            index += length;
        }

        return records;
    }

    // ...
}

解析后,这些数据字节会更有意义,您可以找出下一级解码。

【讨论】:

  • 对我来说它返回类似64898bd, aa0ceb2, 51e6e03, ccbf180, f6b03b9, 5c923fe.. 如何将其转换为 UUID ?
【解决方案2】:

如 cmets 中所述,BLE 设备实际上并没有特定的 UUID(但包含的服务有很多)。但是,某些方案(例如 iBeacon)在广告数据包中的制造商特定数据记录中编码唯一标识符。

这是一种效率很低但概念上简单的方法,可以将整个 scanRecord 转换为十六进制字符串表示形式以进行调试打印:

String msg = "payload = ";
for (byte b : scanRecord)
  msg += String.format("%02x ", b);

请注意,这将包括实际的广告包和一些无意义的尾随字节,在解析广告包本身包含的结构(长度字段)后应忽略这些字节。

【讨论】:

  • 有没有一种清晰的方法可以将此字符串转换为类似 4CAD1A61-A9D3-428A-2913-DD84502313FC 的东西
  • 当然,删除空格并添加基于计数器的规则以在需要的地方放入-,可能切换到for循环的索引样式此时您正在询问基本Java编程,而不是 BLE。
  • Acctualy as paring this information 我只能得到设备名称,但不能得到 UUID。
  • 您似乎没有在处理 scanRecord 本身,而只是调用了 device.getName() 方法。
  • 事实上,BLE 设备可能会宣传(这不是强制性的)一个 primary service UUID,要么是长的(128 位,自定义服务)要么是短的( 16 位,标准服务)。这是为了让 Central 设备可以轻松地整理出外围设备。
【解决方案3】:

我在开发 ble 应用程序时遇到了同样的问题,但在阅读了以下链接上的文档后:https://developer.android.com/reference/android/bluetooth/le/ScanResult.html

就 UUID(取决于您正在开发的 API)而言,重要的类是:

广告数据 AdvertiseData.Builder 扫描记录 扫描结果

在阅读了这些类的文档后,这是我编写的用于获取任何被扫描设备的 UUID 的代码:

//对于 API

private BluetoothAdapter.LeScanCallback scanCallBackLe =
        new BluetoothAdapter.LeScanCallback() {
            @Override
            public void onLeScan(final BluetoothDevice device, int rssi, final byte[] scanRecord) {
                final int RSSI = rssi;
                if (RSSI >= signalThreshold){
                    scanHandler.post(new Runnable() {
                        @Override
                        public void run() {
                            AdvertiseData data = new AdvertiseData.Builder()
                                    .addServiceUuid(ParcelUuid
                                            .fromString(UUID
                                                    .nameUUIDFromBytes(scanRecord).toString())).build();
                            scannerActivity.addDevice(device, RSSI, getUUID(data));
                        }
                    });
                }
            }
        };

//For APIs less than 21, Returns Device UUID
public String getUUID(AdvertiseData data){
    List<ParcelUuid> UUIDs = data.getServiceUuids();
    //ToastMakers.message(scannerActivity.getApplicationContext(), UUIDs.toString());
    String UUIDx = UUIDs.get(0).getUuid().toString();
    Log.e("UUID", " as list ->" + UUIDx);
    return UUIDx;
}

对于 API > 21:

private ScanCallback mScanCallback = new ScanCallback() {
    @Override
    public void onScanResult(int callbackType, final ScanResult result) {
        Log.i("callbackType", String.valueOf(callbackType));
        Log.i("result", result.toString());
        final int RSSI = result.getRssi();
        if (RSSI>=signalThreshold) {
            scanHandler.post(
                    new Runnable() {
                        @Override
                        public void run() {
                            BluetoothDevice device = result.getDevice();
                            scannerActivity.addDevice(device, result.getRssi(), getUUID(result));
                        }
                    });
        }
    } ...}

//For APIs greater than 21, Returns Device UUID
public String getUUID(ScanResult result){
    String UUIDx = UUID
            .nameUUIDFromBytes(result.getScanRecord().getBytes()).toString();
    ToastMakers.message(scannerActivity.getApplicationContext(), UUIDx);
    Log.e("UUID", " as String ->>" + UUIDx);
    return UUIDx;
}

我能够使用此代码获得任何设备的 128 位 UUID。 :)

【讨论】:

  • 更正 API > 21 getUUID 函数的“String UUIDx”应该是:String UUIDx = result.getScanRecord().getServiceUuids().toString();
  • 这两种方法都需要 API > 21
  • 如何过滤扫描结果,只添加特定的uuid?
【解决方案4】:

我找到了一个解析广告包的java库

https://github.com/TakahikoKawasaki/nv-bluetooth

【讨论】:

    【解决方案5】:

    您可以使用标准的 android BluetoothGattCharacteristic api,例如 getFloatValue(int formatType, int offset), getIntValue(int formatType, int offset), getStringValue(int offset)..参考非常好的安卓开发者网站教程here

     if (UUID_HEART_RATE_MEASUREMENT.equals(characteristic.getUuid())) {
            int flag = characteristic.getProperties();
            int format = -1;
            if ((flag & 0x01) != 0) {
                format = BluetoothGattCharacteristic.FORMAT_UINT16;
                Log.d(TAG, "Heart rate format UINT16.");
            } else {
                format = BluetoothGattCharacteristic.FORMAT_UINT8;
                Log.d(TAG, "Heart rate format UINT8.");
            }
            final int heartRate = characteristic.getIntValue(format, 1);
            Log.d(TAG, "Received heart rate: " + heartRate);
            intent.putExtra(EXTRA_DATA, String.valueOf(heartRate));
        }
    

    【讨论】:

    • 行:int flag = characteristic.getProperties();在该示例代码中,预计将返回支持的特征字段的掩码。 它没有。 它返回特征属性,例如 PROPERTY_NOTIFY。要获得 HRM 支持的特征字段的位掩码,需要读取和使用特征的第一个值。因此替换行 int flag = characteristic.getProperties();类似 byte[] charValue = characteristic.getValue();字节标志 = charValue[0];会好的。
    猜你喜欢
    • 2017-02-17
    • 1970-01-01
    • 1970-01-01
    • 2014-05-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多