Android的BLE廣播數據包解析---Android系列, 藍牙技術(含BLE)

来源:https://www.cnblogs.com/Bytezero/archive/2022/11/04/16857725.html
-Advertisement-
Play Games

一、引言 理解和分析這個數據包結構(這裡面也涉及廣播間隔時間的設置,設備廣播數據間隔設置長了,會影響設備被髮現的效率;設置短時,又響應功耗)。 我們所說的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介面

TypePublic methodsins
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介面

TypePublic methodsins
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&gt; 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


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 在進入正題之前我們先來瞭解一下sed的模式空間,如下 什麼是sed的模式空間? 答(僅供參考):就是從文件讀取一行文本然後存入的緩衝區,然後命令操作模式空間的內容,在初始狀態下模式空間是沒有內容的,在每次迴圈讀取數據的過程中,模式空間都會被清空並寫入新的內容。 解釋以下命令結果: seq 10 | ...
  • 前言 今天我們一起來看一下如何使用LabVIEW實現語義分割。 一、什麼是語義分割 圖像語義分割(semantic segmentation),從字面意思上理解就是讓電腦根據圖像的語義來進行分割,例如讓電腦在輸入下麵左圖的情況下,能夠輸出右圖。語義在語音識別中指的是語音的意思,在圖像領域,語義指 ...
  • iShowU Studio for Mac擁有一個整潔但有用的屏幕錄製界面,使用iShowU Studio Mac版,你可以輕鬆添加文本,註釋,過渡,指針和高亮,然後分享您的結果! 詳情:iShowU Studio for Mac(高清錄屏工具) 最新功能 1.改進捕獲和速度 更簡單的捕獲設置重新設 ...
  • 1.Linux環境完全卸載mysql相關文件: 完全卸載mysql相關文件: yum remove mysql mysql-server mysql-libs compat-mysql rm -rf /var/lib/mysql rm /etc/my.cnf rpm -qa|grep mysql / ...
  • 1.組名 瀟灑不是擺爛 2.組員 黃笑然 陳佳瑤 蘇國培 劉玉婷 安游珺 3.項目名 基於模板匹配的票證識別平臺 4.項目介紹 我們的項目是基於模板匹配的票證識別平臺。隨著人工智慧技術的發展,OCR技術有了重大突破,票證管理也逐漸朝著數字化的方向發展。本票證識別平臺採用OCR識別技術,結合預置模板, ...
  • 簡述 Apache Doris 是一個現代化的 MPP 分析型資料庫產品,僅需 亞秒級 響應時間即可獲得查詢結果,能有效地支持實時數據分析。 本文主要介紹如何使用 CloudCanal 快速構建一條穩定高效運行的 PostgreSQL 到 Doris 數據同步鏈路。 技術點 基於 StreamLoa ...
  • 摘要:GaussDB(for Influx)是一款基於計算存儲分離架構,完全相容 InfluxDB 生態的雲原生時序資料庫。 本文分享自華為雲社區《雲資料庫 GaussDB(for Influx) 解密第十一期:讓智能電網中時序數據處理更高效》,作者:華為雲資料庫 GaussDB(for Influ ...
  • CitusData於日前推出了Citus11.0,並宣佈將所有企業版的特性都進行開源。此前,Citus在版本更新時通常會同步發佈2個版本:開源版本和包含一些額外功能的企業版本。“但是,Citus11.0將只有一個版本,因為Citus擴展中的所有內容現在都是完全開源的!” 公告指出,這意味著你現在可... ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...