Bluetooth LEscanner を使用して BLE をスキャンする Android アプリケーションを作成しました。ビーコンが iBeacon に属しているか、Eddystone に属しているかをアプリで識別する必要があります。これまでのところ、AD フレームを解析して ibeacon の UUID,MajorId,MinorId を特定することに成功しています。
3 に答える
すべてのフィールドのバイト オフセットがわかっていれば、広告のバイトを読み取るのは比較的簡単です。以下の 2 つのコード スニペットは、これらを解析する方法を示しています。1 つ目は、 Android ビーコン ライブラリを使用して独自のonLeScan
コールバックでこれを行う方法を示し、2 つ目は、独自のものを最初からロールバックする方法を示しています。
レイアウトがどのように機能するかを説明するには、以下のコードを見てください。構成可能なレイアウトのすべての解析を処理する Android ビーコン ライブラリのBeaconParser
クラスを使用します。(2 番目のコード スニペットに示されているように独自のものを作成したい場合でも、レイアウト式を確認して、それらがどのように機能するかを理解することをお勧めします。以下の式は、iBeacon と非常によく似た AltBeacon の詳細を示しています。AltBeacon が表示されます。 AltBeacon と Eddystone はどちらもオープン ソースの標準です。)
最初のレイアウト式は、AltBeacon (これも iBeacon に非常に似ています) に 3 つの識別子 (「i」式) があることを示しています。最初のもの (iBeacon では UUID として知られています) は、バイト オフセット 4 から 19 までの 16 バイトです。2 番目のもの (iBeacon ではメジャーと呼ばれる) は、バイト オフセット 20-21 から始まる 2 バイトです。3 番目のもの (iBeacon ではマイナーと呼ばれる) は、バイト オフセット 22 ~ 23 から始まる 2 バイトです。
2 番目のレイアウト式は、Eddystone-UID が 0xfeaa の 16 ビット サービス UUID とそれに続く 0x00 の一致するバイト コードを持つサービス アドバタイズメントであることを示しています。これには 2 つの識別子があり、最初の識別子はバイト オフセット 4 ~ 13 の「名前空間識別子」として知られています。2 番目の識別子は、バイト オフセット 14 ~ 19 の「インスタンス識別子」として知られています。
public void onLeScan(BluetoothDevice device, int rssi, byte[] scanRecord) {
ArrayList<BeaconParser> beaconParsers = new ArrayList<BeaconParser>();
final String ALTBEACON_LAYOUT = "m:2-3=beac,i:4-19,i:20-21,i:22-23,p:24-24,d:25-25";
final String EDDYSTONE_UID_LAYOUT = "s:0-1=feaa,m:2-2=00,p:3-3:-41,i:4-13,i:14-19";
beaconParsers.add(new BeaconParser().setBeaconLayout(EDDYSTONE_UID_LAYOUT));
beaconParsers.add(new BeaconParser().setBeaconLayout(ALTBEACON_LAYOUT));
Beacon beacon = null;
for (BeaconParser parser : beaconParsers) {
beacon = parser.fromScanData(scanRecord,
rssi, device);
if (beacon != null) {
if (beacon.getServiceUuid() == 0xfeaa) {
// This is Eddystone, which uses a service Uuid of 0xfeaa
Identifier eddystoneNamespaceId = beacon.getId1();
Identifier eddystoneInstanceId = beacon.getId2();
}
else {
// This is another type of beacon like AltBeacon or iBeacon
Identifier uuid = beacon.getId1();
Identifier major = beacon.getId2();
Identifier minor = beacon.getId3();
}
}
}
オープン ソースの Android ビーコン ライブラリは、スキャン応答内のバイト オフセットをわずかに変更できる可変長 PDU に関するすべての詳細を処理します。そのBeaconParserがどのように機能するかのソース コードは、こちらで確認できます。
ゼロから完全に独自のものを作成したい場合、最も簡単な方法は、見つけたいパターンを探してバイトを単純にループし、オフセットに基づいて対象のバイトを解析することです。(Android ビーコン ライブラリは、個々の PDU を実際に解析する、より堅牢で洗練された方法を使用します。) ただし、ループ手法は引き続き機能します。
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] == (byte)0xaa && scanRecord[startByte+1] == (byte)0xfe &&
scanRecord[startByte+2] == (byte)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
}
}
if (scanRecord.length-startByte > 24) { // need at least 24 bytes for AltBeacon
// Check that this has the right pattern needed for this to be AltBeacon
// iBeacon has a slightly different layout. Do a Google search to find it.
if (scanRecord[startByte+2] == (byte)0xbe && scanRecord[startByte+3] == (byte)0xac) {
// This is an AltBeacon
byte[] uuidBytes = Arrays.copyOfRange(scanRecord, startByte+4, startByte+19);
byte[] majorBytes = Arrays.copyOfRange(scanRecord, startByte+20, startByte+21);
byte[] minorBytes = Arrays.copyOfRange(scanRecord, startByte+22, startByte+23);
// TODO: do something with the above identifiers here
}
}
}
}
繰り返しますが、上記のコードは、オープン ソースの AltBeacons を解析する方法を示しています (知的財産上の理由から)。iBeacon を解析するには、Google で BeaconLayout を検索し、上記のコードを少し調整する必要があります。
ビーコンは、 iBeacon とEddystoneの両方をアドバタイズできます。実際、私が関わっているプロジェクトでは、このようなビーコンを使用しています。
nv-bluetoothライブラリを使用すると、パケットから iBeacon と Eddystone を簡単に抽出できます。このような:
// onLeScan() method of BluetoothAdapter.LeScanCallback interface.
public void onLeScan(BluetoothDevice device, int rssi, byte[] scanRecord)
{
// Parse the payload of the advertisement packet.
List<ADStructure> structures =
ADPayloadParser.getInstance().parse(scanRecord);
// For each AD structure contained in the advertising packet.
for (ADStructure structure : structures)
{
// If the ADStructure instance can be cast to IBeacon.
if (structure instanceof IBeacon)
{
// An iBeacon was found.
IBeacon iBeacon = (IBeacon)structure;
......
}
// If the ADStructure instance can be cast to Eddystone.
else if (structure instanceof Eddystone)
{
if (structure instanceof EddystoneUID)
{
// Eddystone UID
EddystoneUID es = (EddystoneUID)structure;
......
}
else if (structure instanceof EddystoneURL)
{
// Eddystone URL
EddystoneURL es = (EddystoneURL)structure;
......
}
else if (structure instanceof EddystoneTLM)
{
// Eddystone TLM
EddystoneTLM es = (EddystoneTLM)structure;
......
}
}
......
}
......
}
android.bluetooth.le.ScanRecord
は Android で最悪の API の 1 つであるため、 を使用するよりも手動でパケットを解析することをお勧めしますScanRecord
。
ScanRecord
https://developer.android.com/reference/android/bluetooth/le/ScanRecord.htmlに関するドキュメントを読んでください。メソッドは、getManufacturerSpecificData()
あなたが探しているものかもしれません。