
一、引言
理解和分析這個資料包結構(這里面也涉及廣播間隔時間的設定,設備廣播資料間隔設定長了,會影響設備被發現的效率;設定短時,又回應功耗),
我們所說的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());
byte[] scanData=https://www.cnblogs.com/Bytezero/p/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. |
ScanRecord介面:
| 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
轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/527914.html
標籤:Android
