藍芽Beacon廣播資料包格式以及解析

zfb132發表於2019-07-25

如果你只想找到如何用程式碼解析各資料請點選目錄"使用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]

相關文章