內容轉載自 "我的博客" 如果你只想找到如何用代碼解析各數據請點擊目錄"使用Java解析數據" @ "TOC" 1. 獲取原始藍牙廣播包 首先需要開啟 開發者選項 :不同Android手機打開此功能的方法基本一致,首先打開設置,然後找到系統版本號(例如MIUI系統的全部參數選項的MIUI版本),快速 ...
目錄
內容轉載自我的博客
如果你只想找到如何用代碼解析各數據請點擊目錄"使用Java解析數據"
@(文章目錄)
1. 獲取原始藍牙廣播包
首先需要開啟開發者選項:不同Android手機打開此功能的方法基本一致,首先打開設置,然後找到系統版本號(例如MIUI系統的全部參數選項的MIUI版本),快速連續點擊5次以上即可自動打開開發者選項;然後選擇"打開藍牙數據包日誌"功能,接著打開藍牙功能即可開始記錄數據包,日誌文件存放位置在不同的手機上略有不同;最後把日誌複製到電腦上等待處理
2. 安裝WireShark軟體
對於ubuntu系統來說,只需要輸入以下命令即可成功安裝:
sudo apt-get install wireshark
對於windows或其他系統來說,打開官網按照提示下載安裝即可
3. 分析Beacon廣播包數據
把日誌文件導入WireShark軟體,會自動識別為藍牙廣播包。首先需要瞭解藍牙數據包的主要格式:一個廣播包是由若幹個廣播單元AD Structure構成的。每個廣播單元的組成是:第一個位元組是長度值 length,表示接下來的 length個位元組是數據部分;數據部分的第一個位元組表示數據的類型AD Type,AD type非常關鍵,決定了AD Data的數據代表的是什麼以及怎麼解析,這是官網上面不同的值代表的數據類型 https://www.bluetooth.com/specifications/assigned-numbers/generic-access-profile ;剩下的length-1個位元組是真正的數據AD data。這裡需要特別註意的是,由於發送數據是從低位到高位依次發送,所以接收到的數據要反過來按位元組拼接。例如接收到的MAC為 8b 03 00 b0 01 c2,那麼實際的MAC為 c2:01:b0:00:03:8b
通過分析可以發現,藍牙設備會連續收到兩個來自(同一個)Beacon的廣播數據包,每個原始數據包都是59bytes,前一個主要包含MAC和設備名稱等信息,後一個主要包含UUID,txPower等信息。不妨認為前一個數據包為packetA,後一個為packetB。以下是一組實際數據
3.1 第一個數據包格式
可以看到,軟體會自動把每個部分的數據進行解釋,如果你的英語水平可以的話,以下內容就不需要看了。數據包內容:
04 3e 38 0d 01 1b 00 01 8b 03 00 b0 01 c2 01 00 ff 7f af 00 00 00 00 00 00 00 00 00 1e
02 0a 00 08 16 f0 ff 64 27 11 4c b9 11 09 4d 69 6e 69 42 65 61 63 6f 6e 5f 30 30 39 30 37
下麵是每個位元組對應的含義:
第一個位元組是HCI Packet Type,04表示這是HCI Event;剩下的58bytes則是HCI Event的具體內容
第二個位元組是EventCode,3e是此事件的代碼;第三個位元組是Parameter Length,0x38(十進位56)表示後面數據長度56bytes
第四個位元組是SubEvent,0d表示這是LE Extended Advertising Report;第五個位元組是Num Reports,數值為01
1b 00這兩個位元組代表Event Type,由於發送數據都是按位元組發送以及從低位向高位發送,因此真實值是 001b
01 表示這是隨機設備地址
8b 03 00 b0 01 c2 是此設備的MAC,根據從低向高的發送規則,所以真實MAC是 c2:01:b0:00:03:8b
01 代表首要廣播通道的帶寬
00 代表次要廣播通道的帶寬,此處表示不使用次要通道
ff 表示廣播SID
7f 代表Tx Power的大小,此處是127dbm
af 代表RSSI的大小,此處是-81dbm
00 00 代表周期廣播間隔
00 代表直接地址類型,次數是公共設備地址
00 00 00 00 00 00 代表直接BD_ADDR
1e 代表接下的的數據的位元組數(長度),以下數據就是最重要的廣播數據了
-------------------------------------------
02 0a 00 代表的是Tx Power Level的信息,02 表示數據位元組數,0a 表示數據類型,00 表示功率水平(單位是dBm)
08 16 f0 ff 代表的是Service Data信息,08 表示數據位元組數,16 表示數據類型,ff f0 表示16bit UUID ,
64 27 11 4c b9 表示Service Data的具體信息
11 表示後面的數據的位元組數
09 表示數據類型
4d 69 6e 69 42 65 61 63 6f 6e 5f 30 30 39 30 37 表示設備的名稱,每一個位元組對應一個ASCII
如果你想查看WireShark軟體的解析名稱,可在附錄里瀏覽
3.2 第二個數據包格式
此數據包才是最重要的,59bytes的數據包內容如下:
04 3e 38 0d 01 13 00 01 8b 03 00 b0 01 c2 01 00 ff 7f af 00 00 00 00 00 00 00 00 00 1e
02 01 06 1a ff 4c 00 02 15 fd a5 06 93 a4 e2 4f b1 af cf c6 eb 07 64 78 25 27 11 4c b9 c5
下麵是每個位元組對應的含義:
第一行數據同上,不再分析,重點分析第二行(也就是廣播數據部分)
--------------------------------------
02 表示接下來的數據有兩個位元組
01 表示數據類型,此處的類型是Flags
06 表示Flags的具體模式
1a 表示接下來的數據有26個位元組
ff 表示數據類型,此處是廠家特定字(Manufacturer specific)
4c 00 表示公司的ID,此處的004c代表蘋果公司
02 代表beacon標識位
15 表示接下來有22個位元組的數據
fd a5 06 93 a4 e2 4f b1 af cf c6 eb 07 64 78 25 表示beacon UUID
27 11 是major的值,2711轉化為10進位是10001
4c b9 是minor的值,4cb9轉化為10進位是19641
c5 是txPower的補碼,計算可知原碼是-59
如果你想查看WireShark軟體的解析名稱,可在附錄里瀏覽
3.3 Android程式開發中的藍牙廣播包
上面所講解的都是Android 系統中的藍牙廣播包的格式,是最底層的數據包格式,如果我們是在開發OS 的話才可能會接觸解析這些數據。對於只是進行普通應用程式開發的我們來說,只需要處理已經被Android 系統一次解析之後的數據包,這個數據包才是我們開發應用程式時遇到的數據記錄。
對於Android 開發中,系統會把packetB中的第二行數據(30bytes,長度不定)和packetA(30bytes)中的第二行數據連接在一起,最後總的數據長度為62bytes,不夠的話用0填充,如下所示:
02 01 06 1a ff 4c 00 02 15 fd a5 06 93 a4 e2 4f b1 af cf c6 eb 07 64 78 25 27 11 4c b9 c5 02 0a 00 08 16 f0 ff 64 27 11 4c b9 11 09 4d 69 6e 69 42 65 61 63 6f 6e 5f 30 30 39 30 37 00 00
4. 使用Java解析各數據
以下是主要解析代碼,相容Android P最新版本和Android 較低版本:
//Android Lollipop 版本以上的掃描回調函數
private ScanCallback mScanCallback = new ScanCallback() {
@Override
public void onScanResult(int callbackType, ScanResult result) {
super.onScanResult(callbackType, result);
final BluetoothDevice device = result.getDevice();
final int rssi = result.getRssi();
final byte[] scanRecord = result.getScanRecord().getBytes();
// 判斷Activity是否已經退出
if (mainActivity == null) {
return;
}
mainActivity.runOnUiThread(new Runnable() {
@Override
public void run() {
String name = device.getName();
if (name == null) {
//Log.d(TAG, "onLeScan: 此條數據被過濾---"+mac);
return;
}
Log.d(TAG, "onScanResult: 開始處理廣播數據");
handleScanResult(device, rssi, scanRecord);
}
});
}
@Override
public void onBatchScanResults(List<ScanResult> results) {
super.onBatchScanResults(results);
}
@Override
public void onScanFailed(int errorCode) {
super.onScanFailed(errorCode);
Log.e(TAG, "啟動掃描失敗");
}
};
// Android KITKAT 版本以下的掃描回調函數
private BluetoothAdapter.LeScanCallback mLeScanCallback = new BluetoothAdapter.LeScanCallback() {
@Override
public void onLeScan(final BluetoothDevice device, final int rssi, final byte[] scanRecord) {
// 判斷Activity是否已經退出
if (mainActivity == null) {
return;
}
mainActivity.runOnUiThread(new Runnable() {
@Override
public void run() {
String name = device.getName();
String mac = device.getAddress();
// 官方提供的過濾UUID方法存在問題,此處自己先根據MAC過濾
if (name == null || mac == null || !Arrays.asList(beaconMAC).contains(mac)) {
//Log.d(TAG, "onLeScan: 此條數據被過濾---"+mac);
return;
}
Log.d(TAG, "onLeScan: 開始處理廣播數據");
handleScanResult(device, rssi, scanRecord);
}
});
}
};
// 處理廣播數據
private void handleScanResult(BluetoothDevice device, int rssi, byte[] scanRecord) {
int startIndex = 2;
boolean patternFound = false;
Log.d(TAG, "handleScanResult: "+bytesToHex(scanRecord));
// 尋找是否存在beacon以及有效數據的起始索引
while (startIndex <= 5) {
if (((int) scanRecord[startIndex + 2] & 0xff) == 0x02
&& ((int) scanRecord[startIndex + 3] & 0xff) == 0x15) {
patternFound = true;
break;
}
startIndex++;
}
// 如果找到
if (patternFound) {
String ibeaconName = device.getName();
String mac = device.getAddress();
Log.d(TAG, "onLeScan: 搜索到一個Beacon設備" + mac);
Log.d(TAG, "onLeScan: 它的名字是" + ibeaconName);
String data = parseBLEData(scanRecord, startIndex);
Log.d(TAG, "onLeScan: RSSI=" + rssi);
String result = "Beacon設備名稱:" + ibeaconName + " MAC:" + mac + "\n";
double distance = rssi2distance(rssi);
String dis = String.format("%.2f", distance);
result = result + data + " RSSI:" + rssi + " ( " + dis + " )";
Log.d(TAG, result);
} else {
String name = device.getName();
if (name == null) {
//Log.d(TAG, "onLeScan: 搜索到一個普通藍牙設備,它的MAC是"+device.getAddress());
} else {
//Log.d(TAG, "onLeScan: 搜索到一個普通藍牙設備,它的名字是"+name);
}
}
}
// 根據RSSI計算距離
public double rssi2distance(int rssi) {
int iRssi = Math.abs(rssi);
// 發射端和接收端相隔1米時的信號強度
int A = 59;
// 環境雜訊衰減因數
double n = 2.0;
double power = (iRssi - A) / (10 * n);
return Math.pow(10, power);
}
// 位元組數據轉為Hex: 1 byte = 8bit = 兩個16進位數字
public String bytesToHex(byte[] src) {
StringBuilder stringBuilder = new StringBuilder("");
for (int i = 0; i < src.length; i++) {
int v = src[i] & 0xFF;
String hv = Integer.toHexString(v);
if (hv.length() < 2) {
stringBuilder.append(0);
}
stringBuilder.append(hv);
}
return stringBuilder.toString();
}
// 格式化UUID
public String parseUUID(String data) {
String uuid = "";
if (data.length() == 32) {
uuid = data.substring(0, 8) + "-"
+ data.substring(8, 12) + "-"
+ data.substring(12, 16) + "-"
+ data.substring(16, 20) + "-"
+ data.substring(20);
} else {
showTips(getString(R.string.toast_uuid_not_found));
}
return uuid;
}
// 解析BLE數據
public String parseBLEData(byte[] scanRecord, int startIndex) {
// uuid的長度是16bytes
byte[] uuidBytes = new byte[16];
System.arraycopy(scanRecord, startIndex + 4, uuidBytes, 0, 16);
String hexString = bytesToHex(uuidBytes);
// beacon的UUID值
String uuid = parseUUID(hexString);
// beacon的Major值
int major = (scanRecord[startIndex + 20] & 0xff) * 0x100
+ (scanRecord[startIndex + 21] & 0xff);
// ibeacon的Minor值
int minor = (scanRecord[startIndex + 22] & 0xff) * 0x100
+ (scanRecord[startIndex + 23] & 0xff);
int txPower = (scanRecord[startIndex + 24]);
Log.d(TAG, "onLeScan: 它的UUID是" + uuid + ",txPower是" + txPower);
Log.d(TAG, "onLeScan: major=" + major + ",minor=" + minor);
return "UUID:" + uuid + "\nmajor:" + major + " minor:" + minor + "\ntxPower:" + txPower;
}
以上就是主要代碼
5. 附錄
5.1 第一個數據包的內容以及解析
數據包內容:
04 3e 38 0d 01 1b 00 01 8b 03 00 b0 01 c2 01 00 ff 7f af 00 00 00 00 00 00 00 00 00 1e
02 0a 00 08 16 f0 ff 64 27 11 4c b9 11 09 4d 69 6e 69 42 65 61 63 6f 6e 5f 30 30 39 30 37
下麵是每個位元組對應的含義(WireShark軟體的解析):
Bluetooth HCI H4
[Direction: Rcvd (0x01)]
HCI Packet Type: HCI Event (0x04)
Bluetooth HCI Event - LE Meta
Event Code: LE Meta (0x3e)
Parameter Total Length: 56
Sub Event: LE Extended Advertising Report (0x0d)
Num Reports: 1
Event Type: 0x001b, Connectable, Scannable, Scan Response, Legacy, Data Status: Complete
.... .... .... ...1 = Connectable: True
.... .... .... ..1. = Scannable: True
.... .... .... .0.. = Directed: False
.... .... .... 1... = Scan Response: True
.... .... ...1 .... = Legacy: True
.... .... .00. .... = Data Status: Complete (0x0)
0000 0000 0... .... = Reserved: 0x000
Peer Address Type: Random Device Address (0x01)
BD_ADDR: c2:01:b0:00:03:8b (c2:01:b0:00:03:8b)
Primary PHY: LE 1M (0x01)
Secondary PHY: No packets on the secondary advertising channel (0x00)
Advertising SID: 0xff (not available)
TX Power: 127dBm (not available)
RSSI: -81dBm
Periodic Advertising Interval: 0x0000 (no periodic advertising)
Direct Address Type: Public Device Address (0x00)
Direct BD_ADDR: 00:00:00_00:00:00 (00:00:00:00:00:00)
Data Length: 30
Advertising Data
Tx Power Level
Length: 2
Type: Tx Power Level (0x0a)
Power Level (dBm): 0
Service Data - 16 bit UUID
Length: 8
Type: Service Data - 16 bit UUID (0x16)
UUID 16: Unknown (0xfff0)
Service Data: 6427114cb9
Device Name: MiniBeacon_00907
Length: 17
Type: Device Name (0x09)
Device Name: MiniBeacon_00907
5.2 第二個數據包的內容以及解析
數據包內容:
04 3e 38 0d 01 13 00 01 8b 03 00 b0 01 c2 01 00 ff 7f af 00 00 00 00 00 00 00 00 00 1e
02 01 06 1a ff 4c 00 02 15 fd a5 06 93 a4 e2 4f b1 af cf c6 eb 07 64 78 25 27 11 4c b9 c5
下麵是每個位元組對應的含義(WireShark軟體的解析):
Bluetooth HCI H4
[Direction: Rcvd (0x01)]
HCI Packet Type: HCI Event (0x04)
Bluetooth HCI Event - LE Meta
Event Code: LE Meta (0x3e)
Parameter Total Length: 56
Sub Event: LE Extended Advertising Report (0x0d)
Num Reports: 1
Event Type: 0x0013, Connectable, Scannable, Legacy, Data Status: Complete
.... .... .... ...1 = Connectable: True
.... .... .... ..1. = Scannable: True
.... .... .... .0.. = Directed: False
.... .... .... 0... = Scan Response: False
.... .... ...1 .... = Legacy: True
.... .... .00. .... = Data Status: Complete (0x0)
0000 0000 0... .... = Reserved: 0x000
Peer Address Type: Random Device Address (0x01)
BD_ADDR: c2:01:b0:00:03:8b (c2:01:b0:00:03:8b)
Primary PHY: LE 1M (0x01)
Secondary PHY: No packets on the secondary advertising channel (0x00)
Advertising SID: 0xff (not available)
TX Power: 127dBm (not available)
RSSI: -81dBm
Periodic Advertising Interval: 0x0000 (no periodic advertising)
Direct Address Type: Public Device Address (0x00)
Direct BD_ADDR: 00:00:00_00:00:00 (00:00:00:00:00:00)
Data Length: 30
Advertising Data
Flags
Length: 2
Type: Flags (0x01)
000. .... = Reserved: 0x0
...0 .... = Simultaneous LE and BR/EDR to Same Device Capable (Host): false (0x0)
.... 0... = Simultaneous LE and BR/EDR to Same Device Capable (Controller): false (0x0)
.... .1.. = BR/EDR Not Supported: true (0x1)
.... ..1. = LE General Discoverable Mode: true (0x1)
.... ...0 = LE Limited Discoverable Mode: false (0x0)
Manufacturer Specific
Length: 26
Type: Manufacturer Specific (0xff)
Company ID: Apple, Inc. (0x004c)
Data: 0215fda50693a4e24fb1afcfc6eb0764782527114cb9c5
[Expert Info (Note/Undecoded): Undecoded]
[Undecoded]
[Severity level: Note]
[Group: Undecoded]