一、引言 理解和分析這個數據包結構(這裡面也涉及廣播間隔時間的設置,設備廣播數據間隔設置長了,會影響設備被髮現的效率;設置短時,又響應功耗)。 我們所說的BLE設備,其實是有區分有兩種角色 Central 和 Peripheral,也就是中心設備和外圍設備。中心設備可以主動連接外圍設備,外圍設備發送 ...
一、引言
理解和分析這個數據包結構(這裡面也涉及廣播間隔時間的設置,設備廣播數據間隔設置長了,會影響設備被髮現的效率;設置短時,又響應功耗)。
我們所說的BLE設備,其實是有區分有兩種角色 Central 和 Peripheral,也就是中心設備和外圍設備。中心設備可以主動連接外圍設備,外圍設備發送廣播或者被中心設備連接。外圍通過廣播被中心設備發現,廣播中帶有外圍設備自身的相關信息。在日常APP開發中,手機端的BLE一般都是充當中心設備的。
廣播包有兩種:廣播包(Advertising Data)和響應包(Scan Response),其中廣播包是每個設備必須廣播的,而響應包是可選的。
二、廣播的類型
廣播的類型一般分為四種,有:
2.1 可連接的非定向廣播(Connectable Undirected Event Type)
這種是用途最廣的廣播類型,包括廣播數據和掃描響應數據,它表示當前設備可以接受其他任何設備的連接請求。進行通用廣播的設備能夠被掃描設備掃描到,或者在接收到連接請求時作為從設備進入一個連接。通用廣播可以在沒有連接的情況下發出,換句話說,沒有主從設備之分。
2.2 可連接的定向廣播(Connectable Directed Event Type)
定向廣播類型是為了儘可能快的建立連接。這種報文包含兩個地址:廣播者的地址和發起者的地址。發起者收到發給自己的定向廣播報文之後,可以立即發送連接請求作為回應。定向廣播類型有特殊的時序要求。完整的廣播事件必須每3.75ms重覆一次。這一要求使得掃描設備只需掃描3.75ms便可以收到定向廣播設備的消息。當然,如此快的發送會讓報文充斥著廣播通道,進而導致該區域內的其他設備無法進行廣播。因此,定向廣播不可以持續1.28s以上的時間。如果主機沒有主動要求停止,或者連接沒有建立,控制器都會自動停止廣播。一旦到了1.28s,主機便只能使用間隔長得多的可連接非定向廣播讓其他設備來連接。
當使用定向廣播時,設備不能被主動掃描。此外,定向廣播報文的凈荷中也不能帶有其他附加數據。該凈荷只能包含兩個必須的地址。
2.3 不可連接的非定向廣播(Non-connectable Undirected Event Type)
僅僅發送廣播數據,而不想被掃描或者連接。這也是唯一可用於只有發射機而沒有接收機設備的廣播類型。不可連接廣播設備不會進入連接態,因此,它只能根據主機的要求在廣播態和就緒態之間切換。
2.4 可掃描的非定向廣播(Scannable Undirected Event Type)
又稱可發現廣播,這種廣播不能用於發起連接,但允許其他設備掃描該廣播設備。這意味著該設備可以被髮現,既可以發送廣播數據,也可以響應掃描發送掃描回應數據,但不能建立連接。這是一種適用於廣播數據的廣播形式,動態數據可以包含於廣播數據之中,而靜態數據可以包含於掃描響應數據之中。
註意:所謂的定向和非定向針對的是廣播的對象,如果是針對特定的對象進行廣播(在廣播包PDU中會包含目標對象的MAC)就是定向廣播,反之就是非定向。可連接和不可連接是指是否接受連接請求,如果是不可連接的廣播類型,它將不回應連接請求。可掃描廣播類型是指回應掃描請求。
三、廣播數據格式
每個包都是 31 位元組,數據包中分為有效數據(significant)和無效數據(non-significant)兩部分。
- 有效數據部分:包含若幹個廣播數據單元,稱為 AD Structure。如圖中所示,AD Structure 的組成是:第一個位元組是長度值 Len,表示接下來的 Len 個位元組是數據部分。數據部分的第一個位元組表示數據的類型 AD Type,剩下的 Len - 1 個位元組是真正的數據 AD data。其中 AD type 非常關鍵,決定了 AD Data 的數據代表的是什麼和怎麼解析
- 無效數據部分:因為廣播包的長度必須是 31 個 byte,如果有效數據部分不到 31 自己,剩下的就用 0 補全。這部分的數據是無效的,解釋的時候,忽略即可
AD Type 包括如下類型:
Flags: TYPE = 0x01。這個數據用來標識設備 LE 物理連接的功能。DATA 是 0 到多個位元組的 Flag 值,每個 bit 上用 0 或者 1 來表示是否為 True。如果有任何一個 bit 不為 0,並且廣播包是可連接的,就必須包含此數據。各 bit 的定義如下:
- bit 0: LE 有限發現模式
- bit 1: LE 普通發現模式
- bit 2: 不支持 BR/EDR
- bit 3: 對 Same Device Capable(Controller) 同時支持 BLE 和 BR/EDR
- bit 4: 對 Same Device Capable(Host) 同時支持 BLE 和 BR/EDR
- bit 5..7: 預留
Service UUID: 廣播數據中一般都會把設備支持的 GATT Service 廣播出來,用來告訴外面本設備所支持的 Service。有三種類型的 UUID:16 bit, 32bit, 128 bit。廣播中,每種類型類型有有兩個類別:完整和非完整的。這樣就共有 6 種 AD Type。
- 非完整的 16 bit UUID 列表: TYPE = 0x02;
- 完整的 16 bit UUID 列表: TYPE = 0x03;
- 非完整的 32 bit UUID 列表: TYPE = 0x04;
- 完整的 32 bit UUID 列表: TYPE = 0x05;
- 非完整的 128 bit UUID 列表: TYPE = 0x06;
- 完整的 128 bit UUID 列表: TYPE = 0x07;
Local Name: 設備名字,DATA 是名字的字元串。Local Name 可以是設備的全名,也可以是設備名字的縮寫,其中縮寫必須是全名的前面的若幹字元。
- 設備全名: TYPE = 0x08
- 設備簡稱: TYPE = 0x09
TX Power Level: TYPE = 0x0A,表示設備發送廣播包的信號強度。DATA 部分是一個位元組,表示 -127 到 + 127 dBm。
帶外安全管理(Security Manager Out of Band):TYPE = 0x11。DATA 也是 Flag,每個 bit 表示一個功能:
- bit 0: OOB Flag,0 表示沒有 OOB 數據,1 表示有
- bit 1: 支持 LE
- bit 2: 對 Same Device Capable(Host) 同時支持 BLE 和 BR/EDR
- bit 3: 地址類型,0 表示公開地址,1 表示隨機地址
外設(Slave)連接間隔範圍:TYPE = 0x12。數據中定義了 Slave 最大和最小連接間隔,數據包含 4 個位元組:
- 前 2 位元組:定義最小連接間隔,取值範圍:0x0006 ~ 0x0C80,而 0xFFFF 表示未定義;
- 後 2 位元組:定義最大連接間隔,同上,不過需要保證最大連接間隔大於或者等於最小連接間隔。
服務搜尋:外圍設備可以要請中心設備提供相應的 Service。其數據定義和前面的 Service UUID 類似:
- 16 bit UUID 列表: TYPE = 0x14
- 32 bit UUID 列表: TYPE = 0x??
- 128 bit UUID 列表: TYPE = 0x15
Service Data: Service 對應的數據。
- 16 bit UUID Service: TYPE = 0x16, 前 2 位元組是 UUID,後面是 Service 的數據;
- 32 bit UUID Service: TYPE = 0x??, 前 4 位元組是 UUID,後面是 Service 的數據;
- 128 bit UUID Service: TYPE = 0x??, 前 16 位元組是 UUID,後面是 Service 的數據;
公開目標地址:TYPE = 0x17,表示希望這個廣播包被指定的目標設備處理,此設備綁定了公開地址,DATA 是目標地址列表,每個地址 6 位元組。
隨機目標地址:TYPE = 0x18,定義和前一個類似,表示希望這個廣播包被指定的目標設備處理,此設備綁定了隨機地址,DATA 是目標地址列表,每個地址 6 位元組。
Appearance:TYPE = 0x19,DATA 是表示了設備的外觀。
**廠商自定義數據: **TYPE = 0xFF,廠商自定義的數據中,前兩個位元組表示廠商 ID,剩下的是廠商自己按照需求添加,裡面的數據內容自己定義。
主要的也就是這些了,更多的可參考SIG官網。
四、廣播數據解析
4.1 舊介面(API 21 之前)
在API21之前,Android藍牙掃描可以使用 BluetoothAdapter的startLeScan來發起掃描。基本用法如下:
BluetoothAdapter:
//不帶過濾搜索
startLeScan(BluetoothAdapter.LeScanCallback callback)
//帶UUID過濾搜索
startLeScan(UUID[] serviceUuids, BluetoothAdapter.LeScanCallback callback)
BluetoothAdapter.LeScanCallback mLeScanCallback = new BluetoothAdapter.LeScanCallback() {
@Override
public void onLeScan(final BluetoothDevice device, int rssi, byte[] scanRecord) {
// 解析廣播數據
parseScanRecodeData(scanRecord);
}
};
當掃描到設備以後,就會回調 onLeScan(...),這裡的參數 scanRecord 就是廣播數據,同時包含廣播數據和掃描相應數據(如果有的話),所以長度一般就是 62 位元組。搜索結果如下:
整個廣播數據就在這個scanRecord中,所以需要對scanRecord進行位元組解析
public static ParsedAd parseScanRecodeData(byte[] adv_data) {
ParsedAd parsedAd = new ParsedAd();
ByteBuffer buffer = ByteBuffer.wrap(adv_data).order(ByteOrder.LITTLE_ENDIAN);
while (buffer.remaining() > 2) {
byte length = buffer.get();
if (length == 0)
break;
byte type = buffer.get();
length -= 1;
switch (type) {
case 0x01: // Flags
parsedAd.flags = buffer.get();
length--;
break;
case 0x02: // Partial list of 16-bit UUIDs
case 0x03: // Complete list of 16-bit UUIDs
case 0x14: // List of 16-bit Service Solicitation UUIDs
while (length >= 2) {
parsedAd.uuids.add(UUID.fromString(String.format(
"%08x-0000-1000-8000-00805f9b34fb", buffer.getShort())));
length -= 2;
}
break;
case 0x04: // Partial list of 32 bit service UUIDs
case 0x05: // Complete list of 32 bit service UUIDs
while (length >= 4) {
parsedAd.uuids.add(UUID.fromString(String.format(
"%08x-0000-1000-8000-00805f9b34fb", buffer.getInt())));
length -= 4;
}
break;
case 0x06: // Partial list of 128-bit UUIDs
case 0x07: // Complete list of 128-bit UUIDs
case 0x15: // List of 128-bit Service Solicitation UUIDs
while (length >= 16) {
long lsb = buffer.getLong();
long msb = buffer.getLong();
parsedAd.uuids.add(new UUID(msb, lsb));
length -= 16;
}
break;
case 0x08: // Short local device name
case 0x09: // Complete local device name
byte sb[] = new byte[length];
buffer.get(sb, 0, length);
length = 0;
parsedAd.localName = new String(sb).trim();
break;
case (byte) 0xFF: // Manufacturer Specific Data
parsedAd.manufacturer = buffer.getShort();
length -= 2;
break;
default: // skip
break;
}
if (length > 0) {
buffer.position(buffer.position() + length);
}
}
return parsedAd;
}
4.2 新介面
在Android 5.0 (API 21)及之後使用新介面掃描BLE設備,也提供了一些廣播數據包的解析介面。新和掃描方法使用的是BluetoothLeScanner,封裝成了一個新的類,BluetoothLeScanner還有另一個掃描方法,這個方法就是多了一個掃描過濾 和 掃描設置參數:
BluetoothLeScanner bluetoothLeScanner=bluetoothAdapter.getBluetoothLeScanner();
bluetoothLeScanner.startScan(mLeScanCallback);
//掃描過濾 和 掃描設置參數:
bluetoothLeScanner.startScan(List<ScanFilter> filters, ScanSettings settings, mLeScanCallback)
ScanCallback mLeScanCallback = new ScanCallback() {
@Override
public void onScanResult(int callbackType, ScanResult result) {
Log.e(TAG, "ScanResult:" + scanResult.toString());
![](http://www.xmamiga.com/wp-content/uploads/2018/10/Android-BLE-Scaner.png)byte[] scanData=result.getScanRecord().getBytes();
//把byte數組轉成16進位字元串,方便查看
//Log.e("TAG","onScanResult :"+utils.Bytes2HexString(scanData));//與舊介面一致
Log.e(TAG, "newdevice:" + device.getAddress() + " " + device.getName());
Log.e(TAG, "scanRecord:" + scanResult.getScanRecord().toString());
Log.e(TAG, "scanRecord getManufacturerSpecificData:" + scanResult.getScanRecord().getManufacturerSpecificData().toString());
}
};
搜索結果如下:
手機端運行Log:
涉及介面有:
ScanResult介面:
Type | Public methods | ins |
---|---|---|
int | getAdvertisingSid() | Returns the advertising set id. |
BluetoothDevice | getDevice() | Returns the remote Bluetooth device identified by the Bluetooth device address. |
int | getPeriodicAdvertisingInterval() | Returns the periodic advertising interval in units of 1.25ms. |
int | getRssi() | Returns the received signal strength in dBm. |
ScanRecord | getScanRecord() | Returns the scan record, which is a combination of advertisement and scan response. |
Type | Public methods | ins |
---|---|---|
int | getAdvertiseFlags() | Returns the advertising flags indicating the discoverable mode and capability of the device. |
byte[] | getBytes() | Returns raw bytes of scan record. |
String | getDeviceName() | Returns the local name of the BLE device. |
SparseArray<byte[]> | getManufacturerSpecificData() | Returns a sparse array of manufacturer identifier and its corresponding manufacturer specific data. |
byte[] | getManufacturerSpecificData(int manufacturerId) | Returns the manufacturer specific data associated with the manufacturer id. |
byte[] | getServiceData(ParcelUuid serviceDataUuid) | Returns the service data byte array associated with the serviceUuid. |
List≶ParcelUuid>; | getServiceUuids() | Returns a list of service UUIDs within the advertisement that are used to identify the bluetooth GATT services. |
五、總結
BLE廣播數據包並不複雜,數來數去也有那幾個位元組,用心地去理解,就可以解析出自已想要的數據。
本文來自博客園,作者:Bytezero!,轉載請註明原文鏈接:https://www.cnblogs.com/Bytezero/p/16857725.html