深入NXP低功耗藍(lán)牙SDK開發(fā)系列 - 廣播,掃描與連接

接上一篇整體架構(gòu)介紹后,相信大家對(duì)NXP低功耗協(xié)議的使用還是沒有什么概念(我沒寫錯(cuò)你也沒看錯(cuò))。由于第一篇博文信息量過大,沒有在NXP BLE SDK上做過一定開發(fā)的的同學(xué)看起來肯定是云里霧里。從本篇開始將BLE SDK中逐個(gè)功能進(jìn)行剖析,并且盡可能按照由淺入深的順序發(fā)布。

前置條件

由于網(wǎng)上有大量的文章介紹BLE技術(shù),這兒就不從零講起基本概念了,假定各位同學(xué)對(duì)BLE廣播與掃描功能有基本的了解,至少知道得以下幾個(gè)方面:

  1. 廣播和掃描的意義(為什么雙方要做這件事)
  2. 雙方的角色扮演(簡(jiǎn)單說主機(jī)掃描,從機(jī)廣播)
  3. 基本的廣播參數(shù):時(shí)間間隔,廣播通道,掃描占空比等
  4. 廣播中帶有一些數(shù)據(jù),如果知道他們的格式就更好了

雖然文章主要目的是要在介紹如何在NXP低功耗藍(lán)牙SDK中進(jìn)行廣播和掃描,但如果一上來就講API又過于干澀。在涉及到筆者認(rèn)為有必要展開的協(xié)議內(nèi)容的時(shí)候我還是會(huì)將一下原理的。

BLE 5對(duì)廣播和掃描這部分有一些比較大的變化,目前市面上相關(guān)的應(yīng)用還比較少,因此本篇仍以BLE 4.2規(guī)范中定義的功能作為出發(fā)點(diǎn),后續(xù)如有必要再單獨(dú)介紹BLE 5帶來的廣播擴(kuò)展功能。

什么時(shí)候可以開始廣播 & 掃描?

在BLE協(xié)議規(guī)范中,對(duì)主機(jī)(Host)和控制器(Controller)初始化的流程有一個(gè)清晰定義,用戶要走完這個(gè)流程才能向協(xié)議棧提交請(qǐng)求,同時(shí)BLE芯片也需要對(duì)射頻部分寄存器進(jìn)行初始化來保證RF電路的正常工作。NXP BLE SDK中的系統(tǒng)入口main_task()任務(wù)體實(shí)現(xiàn)里,在正式進(jìn)入事件loop之前將會(huì)調(diào)用Ble_Initialize()來準(zhǔn)備以上兩項(xiàng)工作。注意!是準(zhǔn)備,而非完成。也就是說該函數(shù)返回后,協(xié)議??赡苋匀粵]有準(zhǔn)備好。記得千萬不要直接在這個(gè)函數(shù)后面開啟廣播和掃描!

那何時(shí)才可以呢?初始化過程在調(diào)用Ble_Initialize()時(shí)SDK會(huì)讓安裝一個(gè)默認(rèn)的回調(diào)函數(shù)App_GenericCallback(),回調(diào)觸發(fā)后再由她轉(zhuǎn)而觸發(fā)用戶層的BleApp_GenericCallback()。當(dāng)協(xié)議棧完成所有初始化工作后,用戶將在這個(gè)回調(diào)函數(shù)里收到gInitializationComplete_c事件,這才標(biāo)志著用戶可以正常使用BLE協(xié)議棧提供的服務(wù)了。

廣播(advertising)

在NXP BLE SDK中涉及到廣播主要是4個(gè)API:

// 設(shè)置廣播數(shù)據(jù)(和掃描回復(fù)數(shù)據(jù))
bleResult_t Gap_SetAdvertisingData(gapAdvertisingData_t*   pAdvertisingData, 
                                   gapScanResponseData_t*  pScanResponseData);

// 設(shè)置廣播參數(shù)
bleResult_t Gap_SetAdvertisingParameters(gapAdvertisingParameters_t* 
                                         pAdvertisingParameters);

// 開啟廣播
bleResult_t Gap_StartAdvertising(gapAdvertisingCallback_t advertisingCallback, 
                                 gapConnectionCallback_t  connectionCallback);

// 停止廣播
bleResult_t Gap_StopAdvertising(void);

以上的4個(gè)API都是異步的,就意味著調(diào)用后會(huì)立刻得到返回值,該返回值僅表示函數(shù)調(diào)用(如參數(shù)傳遞是否正確,內(nèi)存是否充足)的結(jié)果,實(shí)際功能的執(zhí)行完成,用戶應(yīng)等到各個(gè)的回調(diào)事件到來作為判斷。

下表列出了相關(guān)的事件:

事件Tag 觸發(fā)函數(shù)&事件 用戶回調(diào)函數(shù)
gAdvertisingDataSetupComplete_c Gap_SetAdvertisingData 通用回調(diào)函數(shù)BleApp_GenericCallback
gAdvertisingParametersSetupComplete_c Gap_SetAdvertisingParameters 通用回調(diào)函數(shù)BleApp_GenericCallback
gAdvertisingStateChanged_c * Gap_StartAdvertising Gap_StopAdvertising 廣播回調(diào)函數(shù)BleApp_AdvertisingCallback

注意:用戶需要自己記錄當(dāng)該事件產(chǎn)生時(shí)廣播是被打開了還是被關(guān)閉了。

通常用戶需要先設(shè)置好廣播數(shù)據(jù)和廣播參數(shù)再開啟廣播,需要按照一定順序調(diào)用這幾個(gè)API(利用回調(diào)作為銜接)。在使用NXP BLE SDK的時(shí),ble conn manager和每一份例程代碼都已經(jīng)幫用戶把廣播流程規(guī)劃好了,用戶只需要在合適的時(shí)候調(diào)用例程代碼中的 BleApp_Advertise()即可。如果例程帶的廣播策略符合用戶的需求,則用戶只需要關(guān)心app_config.c中所填充的廣播數(shù)據(jù)和廣播參數(shù)即可。

廣播數(shù)據(jù)

根據(jù)BLE協(xié)議規(guī)定,廣播包可以發(fā)送最多31個(gè)字節(jié)的數(shù)據(jù),如果設(shè)備支持掃描請(qǐng)求(Scan Request),還可以在掃描回復(fù)(Scan Response)里在回復(fù)31個(gè)字節(jié),這樣最長(zhǎng)也就是62字節(jié)的信息量。這些信息不是隨便填的,必須按照BLE協(xié)議規(guī)范定義的格式,主機(jī)才能正確的解析其中的內(nèi)容。一個(gè)廣播包(或者Scan Response包)由如若干AD Structure結(jié)構(gòu)組成,在代碼層面,NXP協(xié)議棧提供了一個(gè)由gapAdStructure_t組成的結(jié)構(gòu)體數(shù)組,用戶可以將AD數(shù)據(jù)段依次填入。下面是一個(gè)有3個(gè)AD Structure的示例:

static const gapAdStructure_t advScanStruct[3] = {
  {
    .length = NumberOfElements(adData0) + 1,
    .adType = gAdFlags_c,
    .aData = (uint8_t *)adData0
  },
  {
    .length = NumberOfElements(uuid_service_qpps) + 1,
    .adType = gAdComplete128bitServiceList_c,
    .aData = (uint8_t *)uuid_service_qpps
  },
  {
    .adType = gAdShortenedLocalName_c,
    .length = 8,
    .aData = (uint8_t*)"NXP_QPPS"
  }
};

詳細(xì)格式請(qǐng)參照Core Spec中GAP章節(jié)以及CSS(核心規(guī)范補(bǔ)充)。

廣播參數(shù)說明

typedef struct gapAdvertisingParameters_tag {
    uint16_t                         minInterval;
    uint16_t                         maxInterval;
    bleAdvertisingType_t             advertisingType;
    bleAddressType_t                 ownAddressType
    bleAddressType_t                 peerAddressType;
    bleDeviceAddress_t               peerAddress;
    gapAdvertisingChannelMapFlags_t  channelMap;
    gapAdvertisingFilterPolicy_t     filterPolicy;           
} gapAdvertisingParameters_t;
成員 取值范圍 說明
minInterval 0x20 - 0x4000 20ms - 10.24s(步進(jìn)0.625ms),最小建議廣播間隔
maxInterval 0x20 - 0x4000 20ms - 10.24s(步進(jìn)0.625ms),最大建議廣播間隔
advertisingType gAdvConnectableUndirected_c, gAdvDirectedHighDutyCycle_c, gAdvScannable_c, gAdvNonConnectable_c, gAdvDirectedLowDutyCycle_c 四種廣播策略(其中定向廣播還分兩種)
ownAddressType gBleAddrTypePublic_c 或 gBleAddrTypeRandom_c 該參數(shù)決定廣播包的地址類型
peerAddressType gBleAddrTypePublic_c 或 gBleAddrTypeRandom_c 該參數(shù)只在定向廣播時(shí)有效,決定了定向廣播包中填寫的對(duì)方地址類型
peerAddress 48位藍(lán)牙地址 該參數(shù)只在定向廣播時(shí)有效,決定了定向廣播包中填寫的對(duì)方藍(lán)牙地址
channelMap gAdvChanMapFlag37_c, gAdvChanMapFlag38_c,gAdvChanMapFlag39_c 廣播通道的bitmap
filterPolicy gProcessAll_c,gProcessConnAllScanWL_c,gProcessScanAllConnWL_c,gProcessWhiteListOnly_c 廣播白名單策略

掃描(scan)

協(xié)議棧涉及到掃描有3個(gè)API:

// 設(shè)置掃描策略
bleResult_t Gap_SetScanMode(gapScanMode_t           scanMode, 
                            gapAutoConnectParams_t* pAutoConnectParams);

// 開啟掃描
bleResult_t Gap_StartScanning(gapScanningParameters_t*  ScanningParameters,
                              gapScanningCallback_t     scanningCallback,
                              bool_t                    enableFilterDuplicates);

// 停止掃描
bleResult_t Gap_StopScaning(void);

同廣播API,他們也都是異步執(zhí)行的。其中Gap_SetScanMode是一個(gè)可選的API,用戶可以在開啟掃描之前通過這個(gè)函數(shù)來配置掃描的策略,以決定是否將所有掃描到的從設(shè)備都拋給應(yīng)用層(默認(rèn)),或者僅對(duì)‘LimitedDiscovery’或'GeneralDiscovery'的廣播進(jìn)行上報(bào)。同時(shí)還定義是否主機(jī)自動(dòng)連接指定的從機(jī),無需用戶在應(yīng)用層顯示調(diào)用第三部分要介紹的Gap_Connect動(dòng)作,在此模式下不會(huì)上報(bào)被掃描的從機(jī)。枚舉類型gapScanMode_tag的定義如下:

typedef enum gapScanMode_tag {
    gDefaultScan_c,
    gLimitedDiscovery_c,
    gGeneralDiscovery_c,
    gAutoConnect_c
} gapScanMode_t;

Gap_StartScanning 的參數(shù)是自解釋的,主要工作也是在app_config.c中填寫掃描參數(shù)結(jié)構(gòu)體ScanningParameters,下面小節(jié)對(duì)每個(gè)參數(shù)作了說明。

掃描參數(shù)說明

typedef struct gapScanningParameters_tag {
    bleScanType_t               type; 
    uint16_t                    interval; 
    uint16_t                    window; 
    bleAddressType_t            ownAddressType;
    bleScanningFilterPolicy_t   filterPolicy;
} gapScanningParameters_t;
成員 取值范圍 說明
type gScanTypePassive_c 或 gScanTypeActive_c 選擇被動(dòng)或者主動(dòng)掃描
interval 0x04 - 0x4000 2.5ms - 10.24s(步進(jìn)0.625ms),兩次掃描的間隔
window 0x04 - 0x4000 2.5ms - 10.24s(步進(jìn)0.625ms),每次掃描的持續(xù)事件,當(dāng)window = interval時(shí)連續(xù)掃描
ownAddressType gBleAddrTypePublic_c 或 gBleAddrTypeRandom_c 該參數(shù)決定在Scan request時(shí)主機(jī)發(fā)出的包的地址類型
filterPolicy gScanAll_c 或 gScanWithWhiteList_c 是否啟用Scan白名單

連接 (connect)

主設(shè)備在完成掃描流程后,獲取了周圍設(shè)備的列表和基本信息。如果要進(jìn)一步與某一個(gè)設(shè)備進(jìn)行用戶數(shù)據(jù)交互則雙方需要進(jìn)入連接狀態(tài)(非beacon的應(yīng)用)。
建立(和斷開)BLE連接只涉及到2個(gè)API和2個(gè)Event:

// 建立連接,僅主設(shè)備允許調(diào)用
bleResult_t Gap_Connect(gapConnectionRequestParameters_t*   pParameters,
                        gapConnectionCallback_t             connCallback);

// 斷開連接,主從都可以調(diào)用
bleResult_t Gap_Disconnect(deviceId_t deviceId);

這兩個(gè)API各自對(duì)應(yīng)了一個(gè)'連接'和'斷開連接'事件,通知應(yīng)用層示連接已經(jīng)建立或者已經(jīng)斷開:

事件Tag 觸發(fā)函數(shù) 回調(diào)函數(shù)
gConnEvtConnected_c * Gap_Connect 連接回調(diào)函數(shù)BleApp_ConnectionCallback
gConnEvtDisconnected_c * Gap_Disconnect 連接回調(diào)函數(shù)BleApp_ConnectionCallback

注意:這兩個(gè)事件并非只有當(dāng)調(diào)用函數(shù)才會(huì)產(chǎn)生,如果對(duì)端是主設(shè)備與我們建立連接,作為從設(shè)備我們也會(huì)得gConnEvtConnected_c 。同樣,如果對(duì)端主動(dòng)與我們斷開,或者鏈路因某些異常原因斷開,協(xié)議棧也會(huì)拋出gConnEvtDisconnected_c事件。這種會(huì)被協(xié)議棧主動(dòng)觸發(fā)的事件在SDK中還有不少,我們可以稱為異步事件。

在NXP BLE SDK開發(fā)時(shí),Gap_Connect的API被包在App_Connect內(nèi)的,用戶一般是調(diào)用App_Connect,她傳入的連接回調(diào)函數(shù)會(huì)在用戶線程上下文中被調(diào)用。在實(shí)時(shí)操作系統(tǒng)環(huán)境下,事件處理中即使有長(zhǎng)時(shí)間占用CPU的行為(比如打印出事件參數(shù)),也不會(huì)對(duì)協(xié)議棧造成影響。這個(gè)我們?cè)谇懊婕軜?gòu)介紹中有講解這個(gè)機(jī)制,有困惑的同學(xué)可以回去上一篇文章。

連接請(qǐng)求參數(shù)

調(diào)用Gap_Connect時(shí)需要主設(shè)備填充一組連接參數(shù),這組參數(shù)將由協(xié)議棧送給Controller,由Controller最終決定如何與對(duì)端設(shè)備建立連接(Controller將考慮其他連接的時(shí)序要求)

typedef struct gapConnectionRequestParameters_tag {
    uint16_t                    scanInterval; 
    uint16_t                    scanWindow;
    bleInitiatorFilterPolicy_t  filterPolicy;
    bleAddressType_t            ownAddressType;
    bleAddressType_t            peerAddressType;
    bleDeviceAddress_t          peerAddress;
    uint16_t                    connIntervalMin;
    uint16_t                    connIntervalMax;
    uint16_t                    connLatency;
    uint16_t                    supervisionTimeout;
    uint16_t                    connEventLengthMin;
    uint16_t                    connEventLengthMax; 
    bool_t                      usePeerIdentityAddress;
} gapConnectionRequestParameters_t;
成員 取值范圍 說明
scanInterval 0x04 - 0x4000 2.5ms - 10.24s(步進(jìn)0.625ms),兩次掃描的間隔
scanWindow 0x04 - 0x4000 2.5ms - 10.24s(步進(jìn)0.625ms),每次掃描的持續(xù)事件,當(dāng)window = interval時(shí)連續(xù)掃描
filterPolicy gUseDeviceAddress_c, gUseWhiteList_c 是否啟動(dòng)Initiator白名單
ownAddressType gBleAddrTypePublic_c 或 gBleAddrTypeRandom_c 該參數(shù)決定在Connect request中自己的地址類型
peerAddressType gBleAddrTypePublic_c 或 gBleAddrTypeRandom_c 該參數(shù)決定在Connect request時(shí)被連接設(shè)備的地址類型
peerAddress 48位藍(lán)牙地址 該參數(shù)決定在Connect request時(shí)被連接設(shè)備的地址
connIntervalMin 0x06 - 0x0C80 7.5ms - 4s(步進(jìn)1.25ms)建議最小的連接間隔
connIntervalMax 0x06 - 0x0C80 7.5ms - 4s(步進(jìn)1.25ms)建議最大的連接間隔
connLatency 0 - 499 從機(jī)可忽略的連接事件數(shù)量
supervisionTimeout 0x0A - 0x0C80 100ms - 32s(步進(jìn)10ms)最大超時(shí)斷鏈間隔
connEventLengthMin 0 - 0xFFFF 連接事件最小長(zhǎng)度
connEventLengthMax 0 - 0xFFFF 連接事件最大長(zhǎng)度
usePeerIdentityAddress TRUE or FALSE 當(dāng)Controller Privacy開啟時(shí)是否使用

NXP BLE SDK作為主設(shè)備類型的例程在app_config.c都會(huì)給出一個(gè)連接參數(shù)配置gConnReqParams,用戶直接根據(jù)自己需求修改即可,peerAddresspeerAddressType兩個(gè)參數(shù)則來自于掃描得到的數(shù)據(jù)。至于主設(shè)備連接哪一個(gè)掃描到的從設(shè)備,這完全是用戶的定義的行為了,通過廣播數(shù)據(jù)的字段來判斷是一種常用的做法。

同時(shí)廣播與掃描

有的產(chǎn)品擔(dān)任雙角色,既可以作為主設(shè)備掃描并連接其他從設(shè)備,同時(shí)也可以作為從設(shè)備,廣播被其他主設(shè)備發(fā)現(xiàn)并連接。甚至需要同一時(shí)刻做這兩件事(同時(shí)廣播和掃描)。

雖然通常藍(lán)牙芯片只有一個(gè)RF端口,但廣播和掃描這兩項(xiàng)功能一個(gè)是只發(fā)送數(shù)據(jù)(Tx),一個(gè)僅接收數(shù)據(jù)(Rx),因此射頻上不存在問題。從BLE 4.1協(xié)議規(guī)范后,定義了BLE芯片在鏈路層支持多個(gè)狀態(tài)機(jī),這樣就完整的支持了同時(shí)進(jìn)行廣播和掃描,以及建立多個(gè)連接,和混合的拓?fù)浣Y(jié)構(gòu)。

在NXP BLE SDK中實(shí)現(xiàn)這個(gè)功能是非常簡(jiǎn)單的,首先確認(rèn)使用的協(xié)議棧庫(kù)文件是支持主從設(shè)備的,而非僅支持從設(shè)備(peripheral)的庫(kù),然后按照文章前面介紹的廣播與掃描API調(diào)用和Event處理流程操作即可,二者并不沖突。

連接之后

建立完連接只是萬里長(zhǎng)征第一步,連接建立后雙方通常還不能直接進(jìn)行用戶數(shù)據(jù)交互,后面還要進(jìn)行一系列BLE協(xié)議規(guī)范所要求的過程,如配對(duì)加密,客戶端對(duì)服務(wù)器端的服務(wù)進(jìn)行查詢,若知后事如何,請(qǐng)聽下回分解。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容