NRF52832學(xué)習(xí)筆記(10)——GAP從機(jī)端廣播自定義數(shù)據(jù)

一默蚌、背景

鏈路層(LL)控制設(shè)備的射頻狀態(tài)冻晤,有五個(gè)設(shè)備狀態(tài):待機(jī)、廣播绸吸、掃描鼻弧、初始化和連接。

廣播 為廣播數(shù)據(jù)包锦茁,而 掃描 則是監(jiān)聽廣播攘轩。

GAP通信中角色,中心設(shè)備(Central - 主機(jī))用來掃描和連接 外圍設(shè)備(Peripheral - 從機(jī))码俩。

大部分情況下外圍設(shè)備通過廣播自己來讓中心設(shè)備發(fā)現(xiàn)自己度帮,并建立 GATT 連接,從而進(jìn)行更多的數(shù)據(jù)交換稿存。

也有些情況是不需要連接的笨篷,只要外設(shè)廣播自己的數(shù)據(jù)即可,用這種方式主要目的是讓外圍設(shè)備瓣履,把自己的信息發(fā)送給多個(gè)中心設(shè)備率翅。

在藍(lán)牙 4.x 的協(xié)議中,廣播包的大小為 31 個(gè)字節(jié)拂苹,如果主機(jī)有主動(dòng)掃描安聘,還有一個(gè) 31 字節(jié)大小的掃描響應(yīng)包,也就是說如果是藍(lán)牙 4.x 模式瓢棒,最大可實(shí)現(xiàn) 62 個(gè)字節(jié)大小的廣播內(nèi)容浴韭。

在藍(lán)牙 5.0 中,把廣播信道抽象為兩類脯宿,一種叫主廣播信道(primary advertisement channels)念颈,另一種叫次廣播信道,或者第二廣播信道(secondary advertising packets)连霉。

所謂的主廣播類似于藍(lán)牙 4.x 的廣播榴芳,只工作在 37嗡靡、38、39 三個(gè)信道窟感,最大廣播字節(jié)為 31 字節(jié)讨彼。而次廣播允許藍(lán)牙在除開 37、38柿祈、39 三個(gè)信道之外的其他 37 個(gè)信道上發(fā)送長度介于 0-255 字節(jié)的數(shù)據(jù)哈误。次廣播信道(0-36 channel)廣播 255 字節(jié)數(shù)據(jù)。

本篇是關(guān)于廣播自定義數(shù)據(jù)包躏嚎,配置以及啟動(dòng)或關(guān)閉廣播的流程查看 NRF52832學(xué)習(xí)筆記(9)——GAP從機(jī)端廣播

二蜜自、廣播內(nèi)容參數(shù)

在 ble_advertising.h 文件中,提供了廣播的初始化參數(shù)結(jié)構(gòu)體卢佣,如果你需要自定義廣播內(nèi)容重荠,那么就需要在廣播數(shù)據(jù)包 advdata 或者掃描響應(yīng)包 srdata 中添加內(nèi)容。

/**@brief     Initialization parameters for the Advertising Module.
 * @details This structure is used to pass advertising options, advertising data,
 *          and an event handler to the Advertising Module during initialization.
 */
typedef struct
{
    ble_advdata_t           advdata;       /**< Advertising data: name, appearance, discovery flags, and more. */
    ble_advdata_t           srdata;        /**< Scan response data: Supplement to advertising data. */
    ble_adv_modes_config_t  config;        /**< Select which advertising modes and intervals will be utilized.*/
    ble_adv_evt_handler_t   evt_handler;   /**< Event handler that will be called upon advertising events. */
    ble_adv_error_handler_t error_handler; /**< Error handler that will propogate internal errors to the main applications. */
} ble_advertising_init_t;
  • advdata:廣播數(shù)據(jù)包
  • srdata:掃描響應(yīng)包
  • config:配置廣播參數(shù)(廣播模式虚茶、廣播間隔戈鲁、廣播時(shí)間)
  • evt_handler:將在廣播事件上調(diào)用的事件處理程序
  • error_handler:錯(cuò)誤處理程序,將把內(nèi)部錯(cuò)誤導(dǎo)入主應(yīng)用程序
    我們可以看到廣播數(shù)據(jù)包 advdata 或者掃描響應(yīng)包 srdata 都是結(jié)構(gòu)體 ble_advdata_t 類型媳危,該結(jié)構(gòu)體內(nèi)定義了廣播數(shù)據(jù)包或掃描響應(yīng)包可以定義的內(nèi)容:
/**@brief Advertising data structure. This structure contains all options and data needed for encoding and
 *        setting the advertising data. */
typedef struct
{
    ble_advdata_name_type_t      name_type;                           /**< Type of device name. */
    uint8_t                      short_name_len;                      /**< Length of short device name (if short type is specified). */
    bool                         include_appearance;                  /**< Determines if Appearance shall be included. */
    uint8_t                      flags;                               /**< Advertising data Flags field. */
    int8_t *                     p_tx_power_level;                    /**< TX Power Level field. */
    ble_advdata_uuid_list_t      uuids_more_available;                /**< List of UUIDs in the 'More Available' list. */
    ble_advdata_uuid_list_t      uuids_complete;                      /**< List of UUIDs in the 'Complete' list. */
    ble_advdata_uuid_list_t      uuids_solicited;                     /**< List of solicited UUIDs. */
    ble_advdata_conn_int_t *     p_slave_conn_int;                    /**< Slave Connection Interval Range. */
    ble_advdata_manuf_data_t *   p_manuf_specific_data;               /**< Manufacturer specific data. */
    ble_advdata_service_data_t * p_service_data_array;                /**< Array of Service data structures. */
    uint8_t                      service_data_count;                  /**< Number of Service data structures. */
    bool                         include_ble_device_addr;             /**< Determines if LE Bluetooth Device Address shall be included. */
    ble_advdata_le_role_t        le_role;                             /**< LE Role field. Included when different from @ref BLE_ADVDATA_ROLE_NOT_PRESENT. @warning This field can be used only for NFC. For BLE advertising, set it to NULL. */
    ble_advdata_tk_value_t *     p_tk_value;                          /**< Security Manager TK value field. Included when different from NULL. @warning This field can be used only for NFC. For BLE advertising, set it to NULL.*/
    uint8_t *                    p_sec_mgr_oob_flags;                 /**< Security Manager Out Of Band Flags field. Included when different from NULL. @warning This field can be used only for NFC. For BLE advertising, set it to NULL.*/
    ble_gap_lesc_oob_data_t *    p_lesc_data;                         /**< LE Secure Connections OOB data. Included when different from NULL. @warning This field can be used only for NFC. For BLE advertising, set it to NULL.*/
} ble_advdata_t;
  • name_type:設(shè)備名稱的類型
  • short_name_len:短設(shè)備名稱的長度(如果指定了短類型)
  • include_appearance:確定是否包括展示圖標(biāo)
  • flags:廣播數(shù)據(jù)標(biāo)識(shí)字段
  • p_tx_power_level:TX 電平發(fā)送功率等級(jí)
  • uuids_more_available:部分服務(wù)UUID列表荞彼,只顯示部分UUID列表在廣播中,實(shí)際工程還有更多UUID
  • uuids_complete:全部服務(wù)UUID列表待笑,廣播中顯示UUID列表就是實(shí)際工程中所有UUID
  • uuids_solicited:請(qǐng)求服務(wù)的UUID列表鸣皂,一個(gè)從機(jī)設(shè)備可以發(fā)送服務(wù)請(qǐng)求數(shù)據(jù)類型廣播去邀請(qǐng)主機(jī)進(jìn)行連接,該主機(jī)設(shè)備包含一個(gè)或多個(gè)這個(gè)服務(wù)器請(qǐng)求數(shù)據(jù)廣播所指定的服務(wù)
  • p_slave_conn_int:從機(jī)連接間隔范圍
  • p_manuf_specific_data:制造商特定的數(shù)據(jù)暮蹂,自定義廣播數(shù)據(jù)
  • p_service_data_array:服務(wù)數(shù)據(jù)結(jié)構(gòu)數(shù)組
  • service_data_count:服務(wù)數(shù)據(jù)結(jié)構(gòu)的數(shù)量
  • include_ble_device_addr:確定是否包含LE藍(lán)牙設(shè)備地址
  • le_role:LE角色區(qū)域寞缝。這個(gè)區(qū)域僅僅用于NFC。對(duì)應(yīng)BLE廣播仰泻,設(shè)置為NULL
  • p_tk_value:安全管理TK值的區(qū)域荆陆。這個(gè)區(qū)域僅僅用于NFC。對(duì)應(yīng)BLE廣播集侯,設(shè)置為NULL
  • p_sec_mgr_oob_flags:安全管理器帶外標(biāo)志字段被啼。這個(gè)區(qū)域僅僅用于NFC。對(duì)應(yīng)BLE廣播棠枉,設(shè)置為NULL
  • p_lesc_data:LE OOB數(shù)據(jù)的安全連接浓体。這個(gè)區(qū)域僅僅用于NFC。對(duì)應(yīng)BLE廣播辈讶,設(shè)置為NULL

三命浴、廣播UUID的值

UUID的種類分為兩種:

  • 一種是 SIG 定義的公共服務(wù) UUID,所有的公共服務(wù)共用一個(gè) 128bit 的基礎(chǔ) UUID,不同的服務(wù)采用一個(gè) 16bit UUID 進(jìn)行定義生闲。
  • 另一種就是私有服務(wù)的 UUID媳溺,這是一個(gè)自定義的 128bit UUID。

注意:廣播包里的 UUID 不影響服務(wù)特征值中 UUID 的值碍讯,僅僅是讓廣播把 UUID 的值廣播給掃描設(shè)備悬蔽,方便觀察。

3.1 顯示全部服務(wù)UUID列表

在廣播參數(shù)里列出了三類 UUID 列表的情況:

typedef struct
{
  ···
  ble_advdata_uuid_list_t      uuids_more_available;
  ble_advdata_uuid_list_t      uuids_complete;
  ble_advdata_uuid_list_t      uuids_solicited;
  ···
} ble_advdata_t;
  • uuids_more_available:部分服務(wù)UUID列表捉兴,只顯示部分UUID列表在廣播中屯阀,實(shí)際工程還有更多UUID
  • uuids_complete:全部服務(wù)UUID列表,廣播中顯示UUID列表就是實(shí)際工程中所有UUID
  • uuids_solicited:請(qǐng)求服務(wù)的UUID列表轴术,一個(gè)從機(jī)設(shè)備可以發(fā)送服務(wù)請(qǐng)求數(shù)據(jù)類型廣播去邀請(qǐng)主機(jī)進(jìn)行連接,該主機(jī)設(shè)備包含一個(gè)或多個(gè)這個(gè)服務(wù)器請(qǐng)求數(shù)據(jù)廣播所指定的服務(wù)

UUID專門有個(gè)一個(gè)結(jié)構(gòu)體 ble_advdata_uuid_list_t 進(jìn)行標(biāo)識(shí):

typedef struct
{
  uint16_t uuid_cnt;      // UUID的數(shù)目
  ble_uuid_t * p_uuids;  // 指向UUID列表的指針
} ble_advdata_uuid_list_t

如果需要在廣播中廣播 UUID钦无,需要專門建立一個(gè) UUID 的結(jié)構(gòu)體逗栽,讓指向UUID列表的指針指向這個(gè)結(jié)構(gòu)體。

  1. 首先失暂,在主函數(shù) main.c 中彼宠,聲明如下 m_adv_uuids 結(jié)構(gòu)體:
static ble_uuid_t m_adv_uuids[] =
{
  {BLE_UUID_NUS_SERVICE, BLE_UUID_TYPE_VENDOR_BEGIN},
  {BLE_UUID_BATTERY_SERVICE, BLE_UUID_TYPE_BLE}弟塞,
  {BLE_UUID_TX_POWER_SERVICE, BLE_UUID_TYPE_BLE}
};
  • 這個(gè)結(jié)構(gòu)體內(nèi)包含了 3 個(gè) UUID 的列表:一個(gè)藍(lán)牙串口服務(wù)凭峡,一個(gè)電池服務(wù),一個(gè)發(fā)射功率服務(wù)决记。同時(shí)標(biāo)注服務(wù) UUID 的類型摧冀,其中藍(lán)牙串口服務(wù)為私有服務(wù),電池服務(wù)和發(fā)射功率服務(wù)為 SIG 定義的公共服務(wù)系宫。兩種服務(wù)類型 UUID 長度是不同的索昂,分別為 128bit16bit,如下面定義:
/** @defgroup BLE_UUID_TYPES Types of UUID
 * @{ */
#define BLE_UUID_TYPE_UNKNOWN       0x00 /**< Invalid UUID type. */
#define BLE_UUID_TYPE_BLE           0x01 /**< Bluetooth SIG UUID (16-bit). */
#define BLE_UUID_TYPE_VENDOR_BEGIN  0x02 /**< Vendor UUID types start at this index (128-bit). */
/** @} */
  1. 接著扩借,在廣播初始化函數(shù)中添加如下代碼:
static void advertising_init(void)
{
  ret_code_t err_code;
  ble_advertising_init_t init;
  memset(&init, 0, sizeof(init));
  ···
  ···
  // 定義全部UUID列表(包含一個(gè)128bit的UUID和兩個(gè)服務(wù)16bit的UUID)
  init.srdata.uuids_complete.uuid_cnt = sizeof(m_adv_uuids) / sizeof(m_adv_uuids[0]);
  init.srdata.uuids_complete.p_uuids  = m_adv_uuids;
  ···
  ···
  err_code = ble_advertising_init(&m_advertising, &init);// 初始化廣播椒惨,導(dǎo)入?yún)?shù)
  APP_ERROR_CHECK(err_code);

  ble_advertising_conn_cfg_tag_set(&m_advertising, APP_BLE_CONN_CFG_TAG);// 設(shè)置廣播識(shí)別號(hào)
}

由于要顯示 128bit 的 UUID 長度比較長,因此把 UUID 的參數(shù)放入掃描響應(yīng)包中潮罪。通過手機(jī)APP nrf connect 掃描后顯示康谆,Complete list of 表示是完整 UUID 列表。


3.2 顯示部分服務(wù)UUID列表

通過設(shè)置 uuid_cnt 的數(shù)目控制廣播中顯示的 UUID 數(shù)目嫉到,不管廣播數(shù)據(jù)包還是掃描響應(yīng)包沃暗,都只提供 31 個(gè)字節(jié)的空間,因此需要注意可使用的空間屯碴。

  1. 同上描睦,首先在主函數(shù) main.c 中,聲明如下 m_adv_uuids 結(jié)構(gòu)體:
static ble_uuid_t m_adv_uuids[] =
{
  {BLE_UUID_NUS_SERVICE, BLE_UUID_TYPE_VENDOR_BEGIN}导而,
  {BLE_UUID_BATTERY_SERVICE, BLE_UUID_TYPE_BLE}忱叭,
  {BLE_UUID_TX_POWER_SERVICE, BLE_UUID_TYPE_BLE}
};
  1. 接著隔崎,在廣播初始化函數(shù)中添加如下代碼:
static void advertising_init(void)
{
  ret_code_t err_code;
  ble_advertising_init_t init;
  memset(&init, 0, sizeof(init));
  ···
  ···
  // 定義全部UUID列表(包含一個(gè)128bit的UUID和兩個(gè)服務(wù)16bit的UUID)
  init.srdata.uuids_more_available.uuid_cnt = sizeof(m_adv_uuids) / sizeof(m_adv_uuids[0]) - 1;
  init.srdata.uuids_more_available.p_uuids  = m_adv_uuids;
  ···
  ···
  err_code = ble_advertising_init(&m_advertising, &init);// 初始化廣播,導(dǎo)入?yún)?shù)
  APP_ERROR_CHECK(err_code);

  ble_advertising_conn_cfg_tag_set(&m_advertising, APP_BLE_CONN_CFG_TAG);// 設(shè)置廣播識(shí)別號(hào)
}

通過手機(jī)APP nrf connect 掃描后顯示韵丑,Incomplete list of 表示是部分 UUID 列表爵卒。這里只顯示了一個(gè) 16bit UUID 和 一個(gè) 128bit UUID,而實(shí)際有三個(gè) UUID撵彻。


3.3 顯示請(qǐng)求服務(wù)UUID列表

一個(gè)典型的請(qǐng)求服務(wù)的 UUID 列表例子就是 ANCS 廣播钓株。這個(gè)例子需要一個(gè) GATT 從機(jī)端和一個(gè)有這個(gè) ANCS 的 GATT 主機(jī)端。通過在廣播中廣播 ANCS 請(qǐng)求服務(wù)的 UUID 去告訴掃描端(iOS設(shè)備)它正在“尋找”一個(gè)具有 ANCS 服務(wù)的主機(jī)設(shè)備陌僵。對(duì)于當(dāng)前時(shí)間服務(wù) CTS 的客戶機(jī)也是如此轴合。ble_app_cts_c 使用所請(qǐng)求的服務(wù)是因?yàn)樗枰粋€(gè)具有當(dāng)前時(shí)間服務(wù)的 GATT 服務(wù)器的主機(jī)端,在 ble_app_cts_c 工程中碗短,廣播初始化的代碼如下:

  1. 首先受葛,聲明 m_adv_uuids 結(jié)構(gòu)體:
static ble_uuid_t m_adv_uuids[] =
{
  {BLE_UUID_CURRENT_TIME_SERVICE, BLE_UUID_TYPE_BLE}
};
  1. 接著,在廣播初始化函數(shù)中添加如下代碼:
static void advertising_init(void)
{
  ret_code_t err_code;
  ble_advertising_init_t init;
  memset(&init, 0, sizeof(init));
  ···
  ···
  // 定義全部UUID列表(包含一個(gè)128bit的UUID和兩個(gè)服務(wù)16bit的UUID)
  init.srdata.uuids_solicited.uuid_cnt = sizeof(m_adv_uuids) / sizeof(m_adv_uuids[0]) ;
  init.srdata.uuids_solicited.p_uuids  = m_adv_uuids;
  ···
  ···
  err_code = ble_advertising_init(&m_advertising, &init);// 初始化廣播偎谁,導(dǎo)入?yún)?shù)
  APP_ERROR_CHECK(err_code);

  ble_advertising_conn_cfg_tag_set(&m_advertising, APP_BLE_CONN_CFG_TAG);// 設(shè)置廣播識(shí)別號(hào)
}

通過手機(jī)APP nrf connect 掃描后顯示:


四总滩、廣播從機(jī)的連接間隔參數(shù)

從機(jī)與主機(jī)之間的連接間隔,是由從機(jī)提出與主機(jī)進(jìn)行協(xié)商巡雨,然后再由主機(jī)決定的參數(shù)闰渔。

在廣播參數(shù)結(jié)構(gòu)體 ble_advdata_t 中包含了一個(gè)結(jié)構(gòu)體參數(shù) ble_advdata_conn_int_t

typedef struct
{
  ···
  ble_advdata_conn_int_t *p_slave_conn_int;   /**< Slave Connection Interval Range. */
  ···
} ble_advdata_t;

指定了廣播中可以廣播的兩個(gè)連接參數(shù)的值:

/**@brief Connection interval range structure. */
typedef struct
{
    uint16_t                     min_conn_interval;                   /**< Minimum connection interval, in units of 1.25 ms, range 6 to 3200 (7.5 ms to 4 s). */
    uint16_t                     max_conn_interval;                   /**< Maximum connection interval, in units of 1.25 ms, range 6 to 3200 (7.5 ms to 4 s). The value 0xFFFF indicates no specific maximum. */
} ble_advdata_conn_int_t;

使用這個(gè)結(jié)構(gòu)體,可以在廣播初始化中定義需要廣播的連接間隔的參數(shù):

static void advertising_init(void)
{
  ret_code_t err_code;
  ble_advertising_init_t init;
  memset(&init, 0, sizeof(init));
  ···
  ···
  ble_advdata_conn_int_t conn_range;
  // 從機(jī)連接間隔范圍最小值:10*1.25ms = 12.5ms
  conn_range.min_conn_interval = 10;
  // 從機(jī)連接間隔范圍最大值:20*1.25ms = 25ms
  conn_range.max_conn_interval = 20;
  // 廣播數(shù)據(jù)中包含從機(jī)連接間隔范圍
  init.advdata.p_slave_conn_int = &conn_range;
  ···
  ···
  err_code = ble_advertising_init(&m_advertising, &init);// 初始化廣播铐望,導(dǎo)入?yún)?shù)
  APP_ERROR_CHECK(err_code);

  ble_advertising_conn_cfg_tag_set(&m_advertising, APP_BLE_CONN_CFG_TAG);// 設(shè)置廣播識(shí)別號(hào)
}

通過手機(jī)APP nrf connect 掃描后顯示:


五冈涧、廣播自定義數(shù)據(jù)

  1. 首先,在 ble_advdata.h 文件中蝌以,定義了結(jié)構(gòu)體 ble_advdata_manuf_data_t 表示公司廠家的數(shù)據(jù)與ID代碼:
typedef struct 
{
  uint16_t company_identifier;  // 公司ID代碼
  uint8_arry_t data;            // 制造者自定義的數(shù)據(jù) 
} ble_advdata_manuf_data_t;
  • company_identifier:公司ID號(hào)炕舵,每個(gè)公司都有獨(dú)立申請(qǐng)的值,一般 company_identifier 都是廠家在 SIG 申請(qǐng)定義的唯一 ID 號(hào)跟畅。不同公司的 ID 號(hào)可以具體在 SIG 網(wǎng)站查詢咽筋。例如 Nordic 的制造商 ID 號(hào)為:0x0059
  • data:制造商自定義的數(shù)據(jù),這個(gè)參數(shù)可以自由的設(shè)置徊件,只要廣播包的空間足夠奸攻。假設(shè)自定義數(shù)據(jù)為 0x11,0x22虱痕,0x33睹耐,0x44,0x55部翘。
  1. 接著硝训,在廣播初始化函數(shù)中添加如下代碼:
static void advertising_init(void)
{
  ret_code_t err_code;
  ble_advertising_init_t init;
  memset(&init, 0, sizeof(init));
  ···
  ···
  uint8_t my_adv_manuf_data[5] = {0x11,0x22,0x33,0x44,0x55};
  // 定義一個(gè)制造商自定義數(shù)據(jù)的結(jié)構(gòu)體變量,配置廣播數(shù)據(jù)時(shí)將該變量的地址賦值給廣播數(shù)據(jù)包中
  ble_advdata_manuf_data_t manuf_specific_data;
  // 0x0059是Nordic的制造商ID
  manuf_specific_data.company_identifier = 0x0059;
  // 指向自定義數(shù)據(jù)
  manuf_specific_data.data.p_data = my_adv_manuf_data;
  // 自定義數(shù)據(jù)的大小
  manuf_specific_data.data.size   = sizeof(my_adv_manuf_data);
  // 定義自定義數(shù)據(jù)到廣播包中
  init.advdata.p_manuf_specific_data = &manuf_specific_data;
  ···
  ···
  err_code = ble_advertising_init(&m_advertising, &init);// 初始化廣播,導(dǎo)入?yún)?shù)
  APP_ERROR_CHECK(err_code);

  ble_advertising_conn_cfg_tag_set(&m_advertising, APP_BLE_CONN_CFG_TAG);// 設(shè)置廣播識(shí)別號(hào)
}

通過手機(jī)APP nrf connect 掃描后顯示:


六窖梁、動(dòng)態(tài)更新廣播內(nèi)容

動(dòng)態(tài)更新廣播內(nèi)容實(shí)際就是更新第四節(jié)中 manuf_specific_data.data.p_data 的值赘风,然后停止廣播,再更新廣播內(nèi)容纵刘,再開啟廣播邀窃,這里涉及到三個(gè)函數(shù):

  • advertising_stop():停止廣播
  • advertising_advdata_update():更新廣播內(nèi)容
  • advertising_start():開啟廣播
    例子如下。

七假哎、自定義廣播內(nèi)容及動(dòng)態(tài)更新廣播例子

下載 user_advertising.cuser_advertising.h
鏈接:https://pan.baidu.com/s/1zH3uwEwdla-s331a1XzvkQ 提取碼:8gp5

7.1 user_advertising.c

/*********************************************************************
 * INCLUDES
 */
#include "ble_advertising.h"
#include "app_error.h"


/*********************************************************************
 * GLOBAL VARIABLES
 */
// 廣播數(shù)據(jù)
ble_advertising_init_t g_advertisingInit;
ble_advdata_manuf_data_t g_advertisingData;
uint8 g_advertisingDataEventsAndParamsData[] =
{
    // events and params
    0x00,
    0x00,
    0x00,
    0x00,
    0x00,
    0x00,
    0x00,
    0x00,
    0x00,
    0x00,   
    0x00,
    0x00,
    0x00,
    0x00,
    0x00,
    0x00,
    0x00,
    0x00,
    0x00,
};
// 掃描響應(yīng)數(shù)據(jù)
ble_advdata_manuf_data_t g_scanResponseData;
uint8 g_scanResponseStatusAndParamsData[] =
{
    // status and params
    0x00,
    0x00,
    0x00,
    0x00,
    0x00,
    0x00,
    0x00,
    0x00,
    0x00,
    0x00,
    0x00,
    0x00,
    0x00,
    0x00,
    0x00,
};

/*********************************************************************
 * EXTERN FUNCTIONS
 */
extern void advertising_init(void);
extern void advertising_start(bool eraseBonds);
extern void advertising_stop(void);
extern void advertising_advdata_update(void);

/*********************************************************************
 * PUBLIC FUNCTIONS
 */
/**
 @brief 初始化廣播數(shù)據(jù)包
 @param pInit - 廣播數(shù)據(jù)初始化結(jié)構(gòu)體
 @return 無
*/
void InitAdvertisingData(ble_advertising_init_t *pInit)
{   
    pInit->advdata.flags = BLE_GAP_ADV_FLAGS_LE_ONLY_GENERAL_DISC_MODE;                                         // 藍(lán)牙設(shè)備模式瞬捕,LE普通發(fā)現(xiàn)模式和不支持BR/EDR模式
    
    g_advertisingData.company_identifier = 0x11;                                                                    
    g_advertisingData.company_identifier = g_advertisingData.company_identifier | (0x22 << 8);          
    g_advertisingData.data.p_data = g_advertisingDataEventsAndParamsData;
    g_advertisingData.data.size = 19;
    
    pInit->advdata.p_manuf_specific_data = &g_advertisingData;
}

/**
 @brief 初始化掃描應(yīng)答包
 @param pInit - 廣播數(shù)據(jù)初始化結(jié)構(gòu)體
 @return 無
*/
void InitScanResponseData(ble_advertising_init_t *pInit)
{
    g_scanResponseData.company_identifier = g_scanResponseData.company_identifier | 0x11;                           
    g_scanResponseData.company_identifier = g_scanResponseData.company_identifier | (0x22<<8);

    *g_scanResponseStatusAndParamsData = 0x33;

    g_scanResponseData.data.p_data = g_scanResponseStatusAndParamsData;
    g_scanResponseData.data.size = 15;
    pInit->srdata.p_manuf_specific_data = &g_scanResponseData;
    
    pInit->srdata.name_type = BLE_ADVDATA_FULL_NAME;                                                            // 廣播時(shí)的名稱顯示     
}

/**
 @brief 開啟廣播
 @param 無
 @return 無
*/
void EnableAdvertising(void)
{
    bool eraseBonds;
    advertising_start(eraseBonds);
}

/**
 @brief 關(guān)閉廣播
 @param 無
 @return 無
*/
void DisableAdvertising(void)
{
    advertising_stop();
}

/**
 @brief 更新廣播內(nèi)容
 @param 無
 @return 無
*/
void UpdataAdvData(void)
{
    bool eraseBonds = false;
    advertising_stop();
    
    advertising_advdata_update();;
    
    advertising_start(eraseBonds);
}
  • 這里對(duì)廣播數(shù)據(jù)包中的制造商ID company_identifier 也當(dāng)作自定義數(shù)據(jù)使用
g_advertisingData.company_identifier = 0x11;                                                                    
g_advertisingData.company_identifier = g_advertisingData.company_identifier | (0x22 << 8);
  • 其他自定義數(shù)據(jù)則對(duì) g_advertisingDataEventsAndParamsData 數(shù)組進(jìn)行賦值
g_advertisingData.data.p_data = g_advertisingDataEventsAndParamsData;
g_advertisingData.data.size = 19;
  • 掃描響應(yīng)包也是如此,同時(shí)再掃描響應(yīng)包的內(nèi)容中加入了設(shè)備名稱
pInit->srdata.name_type = BLE_ADVDATA_FULL_NAME; 

7.2 user_advertising.h

#ifndef _USER_ADVERTISING_H_
#define _USER_ADVERTISING_H_

/*********************************************************************
 * INCLUDES
 */
#include "ble_advertising.h"

/*********************************************************************
 * GLOBAL VARIABLES
 */
extern ble_advdata_manuf_data_t g_advertisingData;
extern uint8 g_advertisingDataEventsAndParamsData[];
extern ble_advdata_manuf_data_t g_scanResponseData;
extern uint8 g_scanResponseStatusAndParamsData[];
extern ble_advertising_init_t g_advertisingInit;

/*********************************************************************
 * API FUNCTIONS
 */
void InitAdvertisingData(ble_advertising_init_t *pInit);
void InitScanResponseData(ble_advertising_init_t *pInit);
void EnableAdvertising(void);
void DisableAdvertising(void);
void UpdataAdvData(void);
void SetAdvDataTriggerEventTimeStamp(uint8 eventTypeLenghtLocation);

#endif /* _USER_ADVERTISING_H_ */

7.3 main.c

#include "user_advertising.h"

BLE_ADVERTISING_DEF(m_advertising);  /**< Advertising module instance. */

int main(void)
{
  bool erase_bonds;
  ···
  ···
  advertising_init();              // 廣播初始化
  ···
  advertising_start(erase_bonds);  // 開啟廣播
}

static bool s_isConnectedFlag = false;
/**
 @brief 設(shè)置BLE連接狀態(tài)
 @param status -[in] true - 已連接舵抹;false - 已斷開
 @return 無
*/
void SetBleConnectStatus(bool status)
{
    s_isConnectedFlag = status;
}
/**
 @brief 獲取BLE連接狀態(tài)
 @param 無
 @return true - 已連接肪虎;false - 已斷開
*/
bool GetBleConnectStatus(void)
{
    return s_isConnectedFlag;
}

static bool s_waitForUpdateAdvDataFlag = false;
/**
 @brief 設(shè)置等待更新廣播狀態(tài)
 @param status -[in] true - 等待;false - 空閑
 @return 無
*/
void SetWaitForUpdateAdvDataStatus(bool status)
{
    s_waitForUpdateAdvDataFlag = status;
}
/**
 @brief 獲取等待更新廣播狀態(tài)
 @param 無
 @return true - 等待惧蛹;false - 空閑
*/
bool GetWaitForUpdateAdvDataStatus(void)
{
    return s_waitForUpdateAdvDataFlag;
}

/**@brief Function for starting advertising.
 */
void advertising_start(bool erase_bonds)
{
    if(erase_bonds == true)
    {
        delete_bonds();
        // Advertising is started by PM_EVT_PEERS_DELETED_SUCEEDED event
    }
    else
    {
        ret_code_t err_code = ble_advertising_start(&m_advertising, BLE_ADV_MODE_FAST);

        APP_ERROR_CHECK(err_code);
    }
}
/**@brief Function for stopping advertising.
 */
void advertising_stop(void)
{
    sd_ble_gap_adv_stop(m_advertising.adv_handle);  
}
/**@brief Function for update advertising data or scan response data.
 */
void advertising_advdata_update(void)
{
    ret_code_t err_code;

    err_code = ble_advertising_init(&m_advertising, &g_advertisingInit);                                // 初始化廣播笋轨,導(dǎo)入?yún)?shù)
    APP_ERROR_CHECK(err_code);

    ble_advertising_conn_cfg_tag_set(&m_advertising, APP_BLE_CONN_CFG_TAG);                             // 設(shè)置廣播識(shí)別號(hào)
}

/**@brief Function for initializing the Advertising functionality.
 */
static void advertising_init(void)
{
    ret_code_t err_code;
    memset(&g_advertisingInit, 0, sizeof(g_advertisingInit));
    
    // 初始化廣播數(shù)據(jù)包和掃描響應(yīng)包內(nèi)容
    InitAdvertisingData(&g_advertisingInit);
    InitScanResponseData(&g_advertisingInit);

    g_advertisingInit.config.ble_adv_fast_enabled  = true;                                              // 廣播類型,快速廣播                
    g_advertisingInit.config.ble_adv_fast_interval = APP_ADV_INTERVAL;                                  // 廣播間隔
    g_advertisingInit.config.ble_adv_fast_timeout  = APP_ADV_DURATION;                                  // 廣播超時(shí)時(shí)間赊淑,值0則保持一種廣播模式不變

    g_advertisingInit.evt_handler = on_adv_evt;

    err_code = ble_advertising_init(&m_advertising, &g_advertisingInit);                                // 初始化廣播,導(dǎo)入?yún)?shù)
    APP_ERROR_CHECK(err_code);

    ble_advertising_conn_cfg_tag_set(&m_advertising, APP_BLE_CONN_CFG_TAG);                             // 設(shè)置廣播識(shí)別號(hào)
}

7.4 初始化廣播內(nèi)容

通過調(diào)用 InitAdvertisingDataInitScanResponseData 兩個(gè)函數(shù)初始化廣播數(shù)據(jù)包和掃描響應(yīng)包

7.5 動(dòng)態(tài)更新廣播內(nèi)容

例如在一些中斷函數(shù)中仅讽,通過對(duì) g_advertisingDataEventsAndParamsData 進(jìn)行賦值陶缺,同時(shí)在連接時(shí)不更新廣播,等待斷開連接后更新洁灵。

// 中斷函數(shù)中
g_advertisingDataEventsAndParamsData = 0x55

if(GetBleConnectStatus() == false)
{       
  UpdataAdvData();  // 非連接狀態(tài)饱岸,立即更新廣播
}
else
{
  SetWaitForUpdateAdvDataStatus(true);  // 等待斷開后更新廣播
}

然后再藍(lán)牙事件處理函數(shù) ble_evt_handler 中,判斷是否正在連接中徽千,如果不是苫费,立即更新廣播內(nèi)容

/**@brief Function for handling BLE events.
 *
 * @param[in]   p_ble_evt   Bluetooth stack event.
 * @param[in]   p_context   Unused.
 */
static void ble_evt_handler(ble_evt_t const *p_ble_evt, void *p_context)
{
    ret_code_t err_code = NRF_SUCCESS;

    switch(p_ble_evt->header.evt_id)
    {
        case BLE_GAP_EVT_DISCONNECTED:
            NRF_LOG_INFO("Disconnected.");
            SetBleConnectStatus(false);                                                                 // 已斷開
            if(GetWaitForUpdateAdvDataStatus() == true)                                                 // 等待更新廣播
            {       
                UpdataAdvData();                                                                
                SetWaitForUpdateAdvDataStatus(false);
            }
            break;

        case BLE_GAP_EVT_CONNECTED:
            NRF_LOG_INFO("Connected.");
            err_code = bsp_indication_set(BSP_INDICATE_CONNECTED);
            APP_ERROR_CHECK(err_code);
            m_conn_handle = p_ble_evt->evt.gap_evt.conn_handle;
            err_code = nrf_ble_qwr_conn_handle_assign(&m_qwr, m_conn_handle);
            APP_ERROR_CHECK(err_code);
        
            SetBleConnectStatus(true);                                                                  // 已連接
            break;
  ···
  ···

? 由 Leung 寫于 2020 年 2 月 12 日

? 參考:青風(fēng)電子社區(qū)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市双抽,隨后出現(xiàn)的幾起案子百框,更是在濱河造成了極大的恐慌,老刑警劉巖牍汹,帶你破解...
    沈念sama閱讀 222,378評(píng)論 6 516
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件铐维,死亡現(xiàn)場離奇詭異,居然都是意外死亡慎菲,警方通過查閱死者的電腦和手機(jī)嫁蛇,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,970評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來露该,“玉大人睬棚,你說我怎么就攤上這事。” “怎么了抑党?”我有些...
    開封第一講書人閱讀 168,983評(píng)論 0 362
  • 文/不壞的土叔 我叫張陵包警,是天一觀的道長。 經(jīng)常有香客問我新荤,道長揽趾,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,938評(píng)論 1 299
  • 正文 為了忘掉前任苛骨,我火速辦了婚禮篱瞎,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘痒芝。我一直安慰自己俐筋,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,955評(píng)論 6 398
  • 文/花漫 我一把揭開白布严衬。 她就那樣靜靜地躺著澄者,像睡著了一般。 火紅的嫁衣襯著肌膚如雪请琳。 梳的紋絲不亂的頭發(fā)上粱挡,一...
    開封第一講書人閱讀 52,549評(píng)論 1 312
  • 那天,我揣著相機(jī)與錄音俄精,去河邊找鬼询筏。 笑死,一個(gè)胖子當(dāng)著我的面吹牛竖慧,可吹牛的內(nèi)容都是我干的嫌套。 我是一名探鬼主播,決...
    沈念sama閱讀 41,063評(píng)論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼圾旨,長吁一口氣:“原來是場噩夢啊……” “哼踱讨!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起砍的,我...
    開封第一講書人閱讀 39,991評(píng)論 0 277
  • 序言:老撾萬榮一對(duì)情侶失蹤痹筛,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后廓鞠,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體味混,經(jīng)...
    沈念sama閱讀 46,522評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,604評(píng)論 3 342
  • 正文 我和宋清朗相戀三年诫惭,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了翁锡。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,742評(píng)論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡夕土,死狀恐怖馆衔,靈堂內(nèi)的尸體忽然破棺而出瘟判,到底是詐尸還是另有隱情,我是刑警寧澤角溃,帶...
    沈念sama閱讀 36,413評(píng)論 5 351
  • 正文 年R本政府宣布拷获,位于F島的核電站,受9級(jí)特大地震影響减细,放射性物質(zhì)發(fā)生泄漏匆瓜。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,094評(píng)論 3 335
  • 文/蒙蒙 一未蝌、第九天 我趴在偏房一處隱蔽的房頂上張望驮吱。 院中可真熱鬧,春花似錦萧吠、人聲如沸左冬。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,572評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽拇砰。三九已至,卻和暖如春狰腌,著一層夾襖步出監(jiān)牢的瞬間除破,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,671評(píng)論 1 274
  • 我被黑心中介騙來泰國打工琼腔, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留皂岔,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 49,159評(píng)論 3 378
  • 正文 我出身青樓展姐,卻偏偏與公主長得像,于是被迫代替她去往敵國和親剖毯。 傳聞我的和親對(duì)象是個(gè)殘疾皇子圾笨,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,747評(píng)論 2 361

推薦閱讀更多精彩內(nèi)容