一、背景
1.1 藍牙協議棧
**鏈路層(LL)**控制設備的射頻狀態,有五個設備狀態:待機、廣播、掃描、初始化和連接。
廣播 爲廣播數據包,而 掃描 則是監聽廣播。
GAP通信中角色,中心設備(Central - 主機) 用來掃描和連接 外圍設備(Peripheral - 從機)。
大部分情況下外圍設備通過廣播自己來讓中心設備發現自己,並建立 GATT 連接,從而進行更多的數據交換。
也有些情況是不需要連接的,只要外設廣播自己的數據即可,用這種方式主要目的是讓外圍設備,把自己的信息發送給多箇中心設備。
1.2 掃描概念
掃描是一個在一定範圍內用來尋址其他低功耗藍牙設備廣播的過程。掃描者設備在掃描過程中會使用廣播信道。與廣播過程不同的是,掃描過程沒有嚴格的時間定義和信道規則。掃描過程應該按照由 Host 層所設定掃描定時參數還運行。
1.2.1 被動掃描
被動掃描:在被動掃描中,掃描設備應該僅僅去監聽廣播包,而不向廣播設備發送任何數據。
一旦掃描參數設置完成,主機就可以在協議棧中發送命令啓動掃描。掃描過程中,如果控制器接收到符合過濾策略或其他規則的廣播數據包,則發送一個廣播報告事件給主機。報告事件除了有廣播者的設備地址外,還包括廣播數據包中的數據,以及接收廣播數據包時的信號接收強度。我們可以利用該信號強度以及位於廣播包中的發射功率,共同確定信號的路徑損失,從而給出大致的範圍,這個應用就是防丟器和藍牙定位。
1.2.2 主動掃描
主動掃描:不僅可以捕獲到從端設備的廣播數據包,還可以捕獲掃描響應包,並區分它們。
控制器收到掃描數據後將向主機發送一個廣播報告事件(adv_report),該事件包括了鏈路層數據包的廣播類型。因此,主機能夠判斷從端設備是否可以連接或掃描,並且區分出廣播數據包和掃描響應包。
二、配置掃描參數
2.1 掃描參數變量
在 main.c 中
static ble_gap_scan_params_t m_scan_param = /**< Scan parameters requested for scanning and connection. */
{
.active = 0x01, // 主動掃描
.interval = NRF_BLE_SCAN_SCAN_INTERVAL, // 掃描間隔
.window = NRF_BLE_SCAN_SCAN_WINDOW, // 掃描窗口
.filter_policy = BLE_GAP_SCAN_FP_ACCEPT_ALL,
.timeout = NRF_BLE_SCAN_SCAN_DURATION, // 掃描超時
.scan_phys = BLE_GAP_PHY_1MBPS,
.extended = true,
};
- active:是否主動掃描,配置爲1則是主動掃描,0則是被動掃描
- interval:掃描間隔,控制器間隔多長時間掃描一次,也就是兩個連續的掃描窗口開始時間的時間間隔。在 NRF 上設置爲 0x0004 and 0x4000 in 0.625ms units(2.5ms 到 10.24s)
- window:掃描窗口,每次掃描所持續的時間,在持續時間內,掃描設備一直在廣播信道上運行。在 NRF 上設置爲 0x0004 and 0x4000 in 0.625ms units(2.5ms 到 10.24s)
- filter_policy:掃描篩選策略,也就死說接受任何廣播數據或者僅僅接受白名單設備的廣播數據包。實際上就死決定是否使用白名單過濾廣播數據包。這裏注意一點,如果定向廣播數據包中的目的地址並非是自己的,那麼該數據必須拋棄,即使廣播數據包的發送者在自己的白名單中。
- timeout:掃描超時,超過指定的時間後,沒有掃描到設備將停止掃描。在 NRF 上設置爲 0x0001 and 0xFFFF in seconds,設置爲 0 則認爲沒有 timeout
- scan_phys:掃描的物理層速度
**注意:掃描窗口和掃描間隔兩個參數非常重要。掃描窗口的設置要小於或等於掃描間隔,並且都要是 0.625ms 的整倍數。這兩個參數決定了主機控制器的掃描佔空比。**比如,如果設置掃描間隔爲 100 ms,掃描窗口爲 10ms ,那麼主機控制器的掃描佔空比就死 10%。特別注意可以捕獲的定向數據包的最低佔空比爲 0.4%,即每一秒中掃描時間爲 3.75ms,這些時間設置在任何藍牙 4.x 處理器中都是一致的,不僅僅限於 NRF 處理器。
如果把時間間隔設置爲相同的大小,那麼控制器會進行連續掃描,每個間隔會改變掃描頻率,也就死切換掃描信道。
2.2 掃描過濾相關宏
/**@defgroup BLE_GAP_SCAN_FILTER_POLICIES GAP Scanner filter policies
* @{ */
#define BLE_GAP_SCAN_FP_ACCEPT_ALL 0x00 /**< Accept all advertising packets except directed advertising packets
not addressed to this device. */
#define BLE_GAP_SCAN_FP_WHITELIST 0x01 /**< Accept advertising packets from devices in the whitelist except directed
packets not addressed to this device. */
#define BLE_GAP_SCAN_FP_ALL_NOT_RESOLVED_DIRECTED 0x02 /**< Accept all advertising packets specified in @ref BLE_GAP_SCAN_FP_ACCEPT_ALL.
In addition, accept directed advertising packets, where the advertiser's
address is a resolvable private address that cannot be resolved. */
#define BLE_GAP_SCAN_FP_WHITELIST_NOT_RESOLVED_DIRECTED 0x03 /**< Accept all advertising packets specified in @ref BLE_GAP_SCAN_FP_WHITELIST.
In addition, accept directed advertising packets, where the advertiser's
address is a resolvable private address that cannot be resolved. */
/**@} */
- BLE_GAP_SCAN_FP_ACCEPT_ALL:接收所有的廣播包,除去廣播地址不是指向該設備的定向廣播。
- BLE_GAP_SCAN_FP_WHITELIST:接收在白名單裏所有的廣播,除去廣播地址不是指向該設備的定向廣播。
- BLE_GAP_SCAN_FP_ALL_NOT_RESOLVED_DIRECTED:接收所有的廣播包,包含定向廣播包。這裏如果廣播MAC地址是私密地址,這裏是無法被解析的。
- BLE_GAP_SCAN_FP_WHITELIST_NOT_RESOLVED_DIRECTED:接收白名單裏所有的廣播包,包含定向廣播包。這裏如果廣播MAC地址是私密地址,這裏是無法被解析的。
2.3 定義掃描模塊
NRF_BLE_SCAN_DEF(m_scan); /**< Scanning module instance. */
2.4 初始化掃描參數
/**@brief Function for initialization the scanning and setting the filters.
*/
static void scan_init(void)
{
ret_code_t err_code;
nrf_ble_scan_init_t init_scan;
memset(&init_scan, 0, sizeof(init_scan));
init_scan.p_scan_param = &m_scan_param;
err_code = nrf_ble_scan_init(&m_scan, &init_scan, scan_evt_handler);
APP_ERROR_CHECK(err_code);
if(strlen(m_target_periph_name) != 0)
{
err_code = nrf_ble_scan_filter_set(&m_scan,
SCAN_NAME_FILTER,
m_target_periph_name);
APP_ERROR_CHECK(err_code);
}
err_code = nrf_ble_scan_filter_set(&m_scan,
SCAN_UUID_FILTER,
&m_adv_uuids[HART_RATE_SERVICE_UUID_IDX]);
APP_ERROR_CHECK(err_code);
err_code = nrf_ble_scan_filter_set(&m_scan,
SCAN_UUID_FILTER,
&m_adv_uuids[RSCS_SERVICE_UUID_IDX]);
APP_ERROR_CHECK(err_code);
err_code = nrf_ble_scan_filters_enable(&m_scan,
NRF_BLE_SCAN_ALL_FILTER,
false);
APP_ERROR_CHECK(err_code);
}
在 main 函數中,執行 scan_init() 進行初始化掃描參數
/**@brief Function for initializing the application main entry.
*/
int main(void)
{
bool erase_bonds;
/*-------------------------- 外設驅初始化 ---------------------------*/
// Initialize.
log_init(); // 日誌驅動初始化
/*-------------------------- 藍牙協議棧初始化 ---------------------------*/
power_management_init(); // 能量初始化
ble_stack_init(); // 協議棧初始化
scan_init(); // 掃描初始化
gap_params_init();
gatt_init();
conn_params_init(); // 連接參數初始化
db_discovery_init();
peer_manager_init();
services_init(); // 服務初始化
advertising_init(); // 廣播初始化
/*-------------------------- 開啓應用 ---------------------------*/
// Start execution.
NRF_LOG_INFO("Relay example started.");
if(erase_bonds == true)
{
NRF_LOG_INFO("delete_bonds");
// Scanning and advertising is done upon PM_EVT_PEERS_DELETE_SUCCEEDED event.
delete_bonds();
}
else
{
NRF_LOG_INFO("adv_scan_start");
adv_scan_start();
}
// Enter main loop.
for(;;)
{
idle_state_handle();
}
}
三、開啓掃描
包含頭文件
#include "nrf_ble_scan.h"
開啓掃描函數
/**@brief Function for initializing the scanning.
*/
void scan_start(void)
{
ret_code_t err_code;
err_code = nrf_ble_scan_start(&m_scan);
APP_ERROR_CHECK(err_code);
}
四、關閉掃描
包含頭文件
#include "nrf_ble_scan.h"
關閉掃描函數
nrf_ble_scan_stop();
五、廣播報告
控制器收到廣播數據包後向主機發送一個廣播報告事件。
5.1 掃描事件處理
在 main.c 中
/**@brief Function for handling BLE events from the central application.
*
* @details This function parses scanning reports and initiates a connection to peripherals when a
* target UUID is found. It updates the status of LEDs used to report the central application
* activity.
*
* @param[in] p_ble_evt Bluetooth stack event.
*/
static void on_ble_central_evt(ble_evt_t const *p_ble_evt)
{
ret_code_t err_code;
ble_gap_evt_t const *p_gap_evt = &p_ble_evt->evt.gap_evt;
switch(p_ble_evt->header.evt_id)
{
···
case BLE_GAP_EVT_ADV_REPORT:
GetAdvReport(p_gap_evt->params.adv_report);
break;
···
5.2 獲取廣播報告
/**
@brief 獲取廣播數據
@param advReport - 廣播報告結構體
@return 無
*/
void GetAdvReport(ble_gap_evt_adv_report_t advReport)
{
// 在這裏加入對廣播報告的處理
}
5.2.1 ble_gap_evt_adv_report_t
/**@brief Event structure for @ref BLE_GAP_EVT_ADV_REPORT.
*
* @note If @ref ble_gap_adv_report_type_t::status is set to @ref BLE_GAP_ADV_DATA_STATUS_INCOMPLETE_MORE_DATA,
* not all fields in the advertising report may be available.
*
* @note When ble_gap_adv_report_type_t::status is not set to @ref BLE_GAP_ADV_DATA_STATUS_INCOMPLETE_MORE_DATA,
* scanning will be paused. To continue scanning, call @ref sd_ble_gap_scan_start.
*/
typedef struct
{
ble_gap_adv_report_type_t type; /**< Advertising report type. See @ref ble_gap_adv_report_type_t. */
ble_gap_addr_t peer_addr; /**< Bluetooth address of the peer device. If the peer_addr is resolved:
@ref ble_gap_addr_t::addr_id_peer is set to 1 and the address is the
peer's identity address. */
ble_gap_addr_t direct_addr; /**< Contains the target address of the advertising event if
@ref ble_gap_adv_report_type_t::directed is set to 1. If the
SoftDevice was able to resolve the address,
@ref ble_gap_addr_t::addr_id_peer is set to 1 and the direct_addr
contains the local identity address. If the target address of the
advertising event is @ref BLE_GAP_ADDR_TYPE_RANDOM_PRIVATE_RESOLVABLE,
and the SoftDevice was unable to resolve it, the application may try
to resolve this address to find out if the advertising event was
directed to us. */
uint8_t primary_phy; /**< Indicates the PHY on which the primary advertising packet was received.
See @ref BLE_GAP_PHYS. */
uint8_t secondary_phy; /**< Indicates the PHY on which the secondary advertising packet was received.
See @ref BLE_GAP_PHYS. This field is set to @ref BLE_GAP_PHY_NOT_SET if no packets
were received on a secondary advertising channel. */
int8_t tx_power; /**< TX Power reported by the advertiser in the last packet header received.
This field is set to @ref BLE_GAP_POWER_LEVEL_INVALID if the
last received packet did not contain the Tx Power field.
@note TX Power is only included in extended advertising packets. */
int8_t rssi; /**< Received Signal Strength Indication in dBm of the last packet received. */
uint8_t ch_index; /**< Channel Index on which the last advertising packet is received (0-39). */
uint8_t set_id; /**< Set ID of the received advertising data. Set ID is not present
if set to @ref BLE_GAP_ADV_REPORT_SET_ID_NOT_AVAILABLE. */
uint16_t data_id:12; /**< The advertising data ID of the received advertising data. Data ID
is not present if @ref ble_gap_evt_adv_report_t::set_id is set to
@ref BLE_GAP_ADV_REPORT_SET_ID_NOT_AVAILABLE. */
ble_data_t data; /**< Received advertising or scan response data. If
@ref ble_gap_adv_report_type_t::status is not set to
@ref BLE_GAP_ADV_DATA_STATUS_INCOMPLETE_MORE_DATA, the data buffer provided
in @ref sd_ble_gap_scan_start is now released. */
ble_gap_aux_pointer_t aux_pointer; /**< The offset and PHY of the next advertising packet in this extended advertising
event. @note This field is only set if @ref ble_gap_adv_report_type_t::status
is set to @ref BLE_GAP_ADV_DATA_STATUS_INCOMPLETE_MORE_DATA. */
} ble_gap_evt_adv_report_t;
- type:廣播報告類型
- peer_addr:掃描設備的MAC地址。如果peer_addr被解析,那麼參數ble_gap_addr_t::addr_id_peer被設置爲1,同時該地址是對等方的身份地址
- direct_addr:掃描定向廣播的MAC地址。當參數ble_gap_adv_report_type_t::directed被設置爲1,包含目的地址的廣播事件。如果協議棧能夠解析地址,參數ble_gap_addr_t::addr_id_peer被設置爲1,direct_addr包含本地的認證地址。如果廣播事件的目的地址是BLE_GAP_ADDR_TYPE_RANDOM_PRIVATE_RESOLVABLE類型,協議棧是服務解析的,應用程序可以嘗試解析這個地址,以查明廣播是否指向我們
- primary_phy:主廣播包的物理層速度
- secondry_phy:第二擴展廣播包的物理層速度。看參數BLE_GAP_PHYS。如果在二級廣播頻道上沒有收到信息包,則此字段爲0
- tx_power:TX功率報告由廣播客戶端在最後收到的包頭。如果最後收到的數據包不包含TX字段,則這個字段被設置爲BLE_GAP_POWER_LEVEL_INVALID
- rssi:接收的信號強度
- ch_index:接收到最後一個廣播包的頻道(0-39)
- set_id:設置接收廣播數據的ID
- data_id:接收的廣播數據的廣播數據ID
- data:接收的廣播數據包或者掃描響應包。如果參數ble_gap_adv_report_type_t::status沒有被設置爲參數BLE_GAP_ADV_DATA_STATUS_INCOMPLETE_MORE_DATA,參數sd_ble_gap_scan_start中提供的數據緩存立即釋放
- aux_pointer:在此擴展廣播事件中,下一個廣播包的偏移量。注意:只有當ble_gap_adv_report_type_t::status被設置爲BLE_GAP_ADV_DATA_STATUS_INCOMPLETE_MORE_DATA時,該字段纔會被設置
5.2.2 ble_gap_adv_report_type_t
/**@brief Advertising report type. */
typedef struct
{
uint16_t connectable : 1; /**< Connectable advertising event type. */
uint16_t scannable : 1; /**< Scannable advertising event type. */
uint16_t directed : 1; /**< Directed advertising event type. */
uint16_t scan_response : 1; /**< Received a scan response. */
uint16_t extended_pdu : 1; /**< Received an extended advertising set. */
uint16_t status : 2; /**< Data status. See @ref BLE_GAP_ADV_DATA_STATUS. */
uint16_t reserved : 9; /**< Reserved for future use. */
} ble_gap_adv_report_type_t;
- connectable:可連接廣播事件類型
- scannable:可掃描廣播事件類型
- directed:定向廣播事件類型
- scan_response:收到掃描響應
- extended_pdu:收到加長廣播
- status:數據狀態,參考參數BLE_GAP_ADV_DATA_STATUS
/**@defgroup BLE_GAP_ADV_DATA_STATUS GAP Advertising data status
* @{ */
// 廣播包的所有數據已收到
#define BLE_GAP_ADV_DATA_STATUS_COMPLETE 0x00 /**< All data in the advertising event have been received. */
// 需要接收更多的數據
#define BLE_GAP_ADV_DATA_STATUS_INCOMPLETE_MORE_DATA 0x01 /**< More data to be received.
@note This value will only be used if
@ref ble_gap_scan_params_t::report_incomplete_evts and
@ref ble_gap_adv_report_type_t::extended_pdu are set to true. */
// 不完整的數據,緩衝區大小不足以接收更多
#define BLE_GAP_ADV_DATA_STATUS_INCOMPLETE_TRUNCATED 0x02 /**< Incomplete data. Buffer size insufficient to receive more.
@note This value will only be used if
@ref ble_gap_adv_report_type_t::extended_pdu is set to true. */
// 未能接收剩餘的數據
#define BLE_GAP_ADV_DATA_STATUS_INCOMPLETE_MISSED 0x03 /**< Failed to receive the remaining data.
@note This value will only be used if
@ref ble_gap_adv_report_type_t::extended_pdu is set to true. */
/**@} */
- reserved:保留部分
5.2.3 ble_data_t
/**@brief Data structure. */
typedef struct
{
uint8_t *p_data; /**< Pointer to the data buffer provided to/from the application. */
uint16_t len; /**< Length of the data buffer, in bytes. */
} ble_data_t;
- p_data:廣播或掃描響應的數據
- len:廣播或掃描響應的數據長度
• 由 Leung 寫於 2020 年 3 月 14 日
• 參考:青風電子社區