如何通过 scanRecord 识别 Eddystone
How to identify a Eddystone via scanRecord
我正在开发一个扫描 BLE 设备的 Android 应用程序。每次找到设备时,我都会收到:
byte[] scanRecord, BluetoothDevice device, int rssi
来自
BluetoothAdapter.startLeScan()
然后我将字节数组转换为 ScanRecord 对象:
ScanRecord.parseFromBytes()
我现在从我的 Eddystone 中获得了以下信息(来自 toString()
方法)。
`com.reelyactive.blesdk.support.ble.ScanRecord [mAdvertiseFlags=6, mServiceUuids=[0000feaa-0000-1000-8000-00805f9b34fb], mManufacturerSpecificData={}, mServiceData={0000feaa-0000-1000-8000-00805f9b34fb=[16, -36, 2, 107, 110, 116, 107, 46, 105, 111, 47, 101, 100, 100, 121, 115, 116, 111, 110, 101], 0000d00d-0000-1000-8000-00805f9b34fb=[67, 77, 103, 52, 50, 57, 100]}, mTxPowerLevel=-12, mDeviceName=IIS_EDDY_003] IIS_EDDY_003`
谁能告诉我,如何根据这些信息将设备识别为 Eddystone?服务uuid可能吗?我总是不知道设备的名称或地址。
android.bluetooth.le.ScanRecord
是 Android 中最差的 API 之一。
如果你已经有一个scanRecord
(字节数组),我推荐nv-bluetooth来提取Eddystone数据。以下代码片段显示了 nv-bluetooth 的用法。
// Parse the payload of the advertising packet.
List<ADStructure> structures =
ADPayloadParser.getInstance().parse(scanRecord);
// For each AD structure contained in the payload.
for (ADStructure structure : structures)
{
if (structure instanceof EddystoneUID)
{
// Eddystone UID
EddystoneUID es = (EddystoneUID)structure;
// (1) Calibrated Tx power at 0 m.
int power = es.getTxPower();
// (2) 10-byte Namespace ID
byte[] namespaceId = es.getNamespaceId();
String namespaceIdAsString = es.getNamespaceIdAsString();
// (3) 6-byte Instance ID
byte[] instanceId = es.getInstanceId();
String instanceIdAsString = es.getInstanceIdAsString();
// (4) 16-byte Beacon ID
byte[] beaconId = es.getBeaconId();
String beaconIdAsString = es.getBeaconIdAsString();
}
else if (structure instanceof EddystoneURL)
{
// Eddystone URL
EddystoneURL es = (EddystoneURL)structure;
// (1) Calibrated Tx power at 0 m.
int power = es.getTxPower();
// (2) URL
URL url = es.getURL();
}
else if (structure instanceof EddystoneTLM)
{
// Eddystone TLM
EddystoneTLM es = (EddystoneTLM)structure;
// (1) TLM Version
int version = es.getTLMVersion();
// (2) Battery Voltage
int voltage = es.getBatteryVoltage();
// (3) Beacon Temperature
float temperature = es.getBeaconTemperature();
// (4) Advertisement count since power-on or reboot.
long count = es.getAdvertisementCount();
// (5) Elapsed time in milliseconds since power-on or reboot.
long elapsed = es.getElapsedTime();
}
else if (structure instanceof IBeacon)
{
// iBeacon
IBeacon iBeacon = (IBeacon)structure;
// (1) Proximity UUID
UUID uuid = iBeacon.getUUID();
// (2) Major number
int major = iBeacon.getMajor();
// (3) Minor number
int minor = iBeacon.getMinor();
// (4) Tx Power
int power = iBeacon.getPower();
}
}
以上代码暗示扫描记录应该被解析为AD结构的列表。但是,android.bluetooth.le.ScanRecord
的 parseFromBytes
没有以正确的方式解析扫描记录。
ScanRecord
有以下方法(和其他一些方法):
getAdvertiseFlags()
getDeviceName()
getManufacturerSpecificData()
getServiceData()
getTxPowerLevel()
这些方法对应一些AD结构。此 API 设计与下面显示的 AnimalRecord
class 的结构相同。
public class AnimalRecord
{
public Cat getCat() { ... }
public Dog getDog() { ... }
public Eagle getEagle() { ... }
...
}
标志、本地名称、制造商特定数据、服务数据和 Tx 功率级别也应解析为如下所示的 AD 结构。
// Parse the payload of the advertising packet.
List<ADStructure> structures =
ADPayloadParser.getInstance().parse(scanRecord);
// For each AD structure contained in the payload.
for (ADStructure structure : structures)
{
if (structure instanceof Flags)
{
// Flags
Flags flags = (Flags)structure;
}
else if (structure instanceof LocalName)
{
// Local Name
LocalName name = (LocalName)structure;
}
else if (structure instanceof ADManufacturerSpecific)
{
// Manufacturer Specific Data
// Note that iBeacon is a kind of Manufacturer Specific Data
ADManufacturerSpecific ms = (ADManufacturerSpecific)structure;
}
else if (structure instanceof ServiceData)
{
// Service Data
// Note that Eddystone is a kind of Service Data.
ServiceData sd = (ServiceData)structure;
}
else if (structure instanceof TxPowerLevel)
{
// TxPowerLevel
TxPowerLevel level = (TxPowerLevel)structure;
}
}
正如上面代码中的注释,Eddystone 是一种服务数据。因此,Eddystone UID、Eddystone URL 和 Eddystone TLM 应该具有如下继承树。
ADStructure
|
+-- ServiceData
|
+-- Eddystone
|
+-- EddystoneUID
+-- EddystoneURL
+-- EddystoneTLM
希望非常了解BLE规范并且有好的设计能力的人从头开始重写Android的BLEAPI
对于那些想知道它实际如何工作的人:
mServiceData={
0000feaa-0000-1000-8000-00805f9b34fb=[
16, -36, 2, 107, 110, 116, 107, 46, 105, 111, 47, 101, 100, 100, 121, 115, 116, 111, 110, 101
],
0000d00d-0000-1000-8000-00805f9b34fb=[
67, 77, 103, 52, 50, 57, 100
]
}
第一个业务数据包通过“0000feaa-0000-1000-8000-00805f9b34fb”的前32位可识别为EddyStone数据。转换时 0000feAA 是可以在 the Bluetooth Data Service Specification.
中找到的 16 位 EddyStone 服务 UUID
16-bit UUID for Members => 0xFEAA => Google
服务始终发出“??????????-0000-1000-8000-00805f9b34fb”,并将此 UUID 的前 32 位替换为服务的别名。在这种情况下,'feaa' 表示 EddyStone 服务数据(created/specified by Google)。
因此,由于识别了键,我们现在知道该值是一个 EddyStone DataView。根据 EddyStone 规范,这些值需要 mapped/interpreted:
https://github.com/google/eddystone/blob/master/protocol-specification.md
要提取帧类型(EddyStone UID、URL、TLM 或 EID),请使用数组的第一个值:
FrameType = 16; => 0x10 => EddyStone URL
要了解剩余的值,我们需要查看 EddyStone URL 规范:
https://github.com/google/eddystone/tree/master/eddystone-url
要提取 TX 功率,请使用数组的第二个值:
TX Power = -36; => -36
要提取 URL 架构,您需要获取所有剩余值并将它们转换为字符代码:
107 => k
110 => n
116 => t
107 => k
46 => .
105 => i
111 => o
47 => /
101 => e
100 => d
100 => d
121 => y
115 => s
116 => t
111 => o
110 => n
101 => e
So the URL is: 'kntk.io/eddystone'
总结一下:
信标通告可由 128 位 UUID“0000feaa-0000-1000-8000-00805f9b34fb”识别的 EddyStone 数据服务包,并使用“EddyStone URL”帧类型(帧的第一个值)和正在宣传以下 url:“kntk.io/eddystone”
我希望通过将问题中的这些数据分解为实际的实际值,将帮助最终来到这里的人们了解蓝牙广告的实际工作原理。
您可以使用众多库中的一个来为您完成所有这些事情,但了解基础知识可能会有用...
注意: 我怀疑第二个包是本地的 Kontakt.io 服务帧类型被信标广告在内部使用Kontakt.io 工具。
我正在开发一个扫描 BLE 设备的 Android 应用程序。每次找到设备时,我都会收到:
byte[] scanRecord, BluetoothDevice device, int rssi
来自
BluetoothAdapter.startLeScan()
然后我将字节数组转换为 ScanRecord 对象:
ScanRecord.parseFromBytes()
我现在从我的 Eddystone 中获得了以下信息(来自 toString()
方法)。
`com.reelyactive.blesdk.support.ble.ScanRecord [mAdvertiseFlags=6, mServiceUuids=[0000feaa-0000-1000-8000-00805f9b34fb], mManufacturerSpecificData={}, mServiceData={0000feaa-0000-1000-8000-00805f9b34fb=[16, -36, 2, 107, 110, 116, 107, 46, 105, 111, 47, 101, 100, 100, 121, 115, 116, 111, 110, 101], 0000d00d-0000-1000-8000-00805f9b34fb=[67, 77, 103, 52, 50, 57, 100]}, mTxPowerLevel=-12, mDeviceName=IIS_EDDY_003] IIS_EDDY_003`
谁能告诉我,如何根据这些信息将设备识别为 Eddystone?服务uuid可能吗?我总是不知道设备的名称或地址。
android.bluetooth.le.ScanRecord
是 Android 中最差的 API 之一。
如果你已经有一个scanRecord
(字节数组),我推荐nv-bluetooth来提取Eddystone数据。以下代码片段显示了 nv-bluetooth 的用法。
// Parse the payload of the advertising packet.
List<ADStructure> structures =
ADPayloadParser.getInstance().parse(scanRecord);
// For each AD structure contained in the payload.
for (ADStructure structure : structures)
{
if (structure instanceof EddystoneUID)
{
// Eddystone UID
EddystoneUID es = (EddystoneUID)structure;
// (1) Calibrated Tx power at 0 m.
int power = es.getTxPower();
// (2) 10-byte Namespace ID
byte[] namespaceId = es.getNamespaceId();
String namespaceIdAsString = es.getNamespaceIdAsString();
// (3) 6-byte Instance ID
byte[] instanceId = es.getInstanceId();
String instanceIdAsString = es.getInstanceIdAsString();
// (4) 16-byte Beacon ID
byte[] beaconId = es.getBeaconId();
String beaconIdAsString = es.getBeaconIdAsString();
}
else if (structure instanceof EddystoneURL)
{
// Eddystone URL
EddystoneURL es = (EddystoneURL)structure;
// (1) Calibrated Tx power at 0 m.
int power = es.getTxPower();
// (2) URL
URL url = es.getURL();
}
else if (structure instanceof EddystoneTLM)
{
// Eddystone TLM
EddystoneTLM es = (EddystoneTLM)structure;
// (1) TLM Version
int version = es.getTLMVersion();
// (2) Battery Voltage
int voltage = es.getBatteryVoltage();
// (3) Beacon Temperature
float temperature = es.getBeaconTemperature();
// (4) Advertisement count since power-on or reboot.
long count = es.getAdvertisementCount();
// (5) Elapsed time in milliseconds since power-on or reboot.
long elapsed = es.getElapsedTime();
}
else if (structure instanceof IBeacon)
{
// iBeacon
IBeacon iBeacon = (IBeacon)structure;
// (1) Proximity UUID
UUID uuid = iBeacon.getUUID();
// (2) Major number
int major = iBeacon.getMajor();
// (3) Minor number
int minor = iBeacon.getMinor();
// (4) Tx Power
int power = iBeacon.getPower();
}
}
以上代码暗示扫描记录应该被解析为AD结构的列表。但是,android.bluetooth.le.ScanRecord
的 parseFromBytes
没有以正确的方式解析扫描记录。
ScanRecord
有以下方法(和其他一些方法):
getAdvertiseFlags()
getDeviceName()
getManufacturerSpecificData()
getServiceData()
getTxPowerLevel()
这些方法对应一些AD结构。此 API 设计与下面显示的 AnimalRecord
class 的结构相同。
public class AnimalRecord
{
public Cat getCat() { ... }
public Dog getDog() { ... }
public Eagle getEagle() { ... }
...
}
标志、本地名称、制造商特定数据、服务数据和 Tx 功率级别也应解析为如下所示的 AD 结构。
// Parse the payload of the advertising packet.
List<ADStructure> structures =
ADPayloadParser.getInstance().parse(scanRecord);
// For each AD structure contained in the payload.
for (ADStructure structure : structures)
{
if (structure instanceof Flags)
{
// Flags
Flags flags = (Flags)structure;
}
else if (structure instanceof LocalName)
{
// Local Name
LocalName name = (LocalName)structure;
}
else if (structure instanceof ADManufacturerSpecific)
{
// Manufacturer Specific Data
// Note that iBeacon is a kind of Manufacturer Specific Data
ADManufacturerSpecific ms = (ADManufacturerSpecific)structure;
}
else if (structure instanceof ServiceData)
{
// Service Data
// Note that Eddystone is a kind of Service Data.
ServiceData sd = (ServiceData)structure;
}
else if (structure instanceof TxPowerLevel)
{
// TxPowerLevel
TxPowerLevel level = (TxPowerLevel)structure;
}
}
正如上面代码中的注释,Eddystone 是一种服务数据。因此,Eddystone UID、Eddystone URL 和 Eddystone TLM 应该具有如下继承树。
ADStructure
|
+-- ServiceData
|
+-- Eddystone
|
+-- EddystoneUID
+-- EddystoneURL
+-- EddystoneTLM
希望非常了解BLE规范并且有好的设计能力的人从头开始重写Android的BLEAPI
对于那些想知道它实际如何工作的人:
mServiceData={
0000feaa-0000-1000-8000-00805f9b34fb=[
16, -36, 2, 107, 110, 116, 107, 46, 105, 111, 47, 101, 100, 100, 121, 115, 116, 111, 110, 101
],
0000d00d-0000-1000-8000-00805f9b34fb=[
67, 77, 103, 52, 50, 57, 100
]
}
第一个业务数据包通过“0000feaa-0000-1000-8000-00805f9b34fb”的前32位可识别为EddyStone数据。转换时 0000feAA 是可以在 the Bluetooth Data Service Specification.
中找到的 16 位 EddyStone 服务 UUID16-bit UUID for Members => 0xFEAA => Google
服务始终发出“??????????-0000-1000-8000-00805f9b34fb”,并将此 UUID 的前 32 位替换为服务的别名。在这种情况下,'feaa' 表示 EddyStone 服务数据(created/specified by Google)。
因此,由于识别了键,我们现在知道该值是一个 EddyStone DataView。根据 EddyStone 规范,这些值需要 mapped/interpreted:
https://github.com/google/eddystone/blob/master/protocol-specification.md
要提取帧类型(EddyStone UID、URL、TLM 或 EID),请使用数组的第一个值:
FrameType = 16; => 0x10 => EddyStone URL
要了解剩余的值,我们需要查看 EddyStone URL 规范:
https://github.com/google/eddystone/tree/master/eddystone-url
要提取 TX 功率,请使用数组的第二个值:
TX Power = -36; => -36
要提取 URL 架构,您需要获取所有剩余值并将它们转换为字符代码:
107 => k
110 => n
116 => t
107 => k
46 => .
105 => i
111 => o
47 => /
101 => e
100 => d
100 => d
121 => y
115 => s
116 => t
111 => o
110 => n
101 => e
So the URL is: 'kntk.io/eddystone'
总结一下:
信标通告可由 128 位 UUID“0000feaa-0000-1000-8000-00805f9b34fb”识别的 EddyStone 数据服务包,并使用“EddyStone URL”帧类型(帧的第一个值)和正在宣传以下 url:“kntk.io/eddystone”
我希望通过将问题中的这些数据分解为实际的实际值,将帮助最终来到这里的人们了解蓝牙广告的实际工作原理。
您可以使用众多库中的一个来为您完成所有这些事情,但了解基础知识可能会有用...
注意: 我怀疑第二个包是本地的 Kontakt.io 服务帧类型被信标广告在内部使用Kontakt.io 工具。