NRF52832学习笔记(10)——GAP从机端广播自定义数据

一、背景

链路层(LL)控制设备的射频状态,有五个设备状态:待机、广播、扫描、初始化和连接。

广播 为广播数据包,而 扫描 则是监听广播。

GAP通信中角色,中心设备(Central - 主机)用来扫描和连接 外围设备(Peripheral - 从机)。

大部分情况下外围设备通过广播自己来让中心设备发现自己,并建立 GATT 连接,从而进行更多的数据交换。

也有些情况是不需要连接的,只要外设广播自己的数据即可,用这种方式主要目的是让外围设备,把自己的信息发送给多个中心设备。

在蓝牙 4.x 的协议中,广播包的大小为 31 个字节,如果主机有主动扫描,还有一个 31 字节大小的扫描响应包,也就是说如果是蓝牙 4.x 模式,最大可实现 62 个字节大小的广播内容。

在蓝牙 5.0 中,把广播信道抽象为两类,一种叫主广播信道(primary advertisement channels),另一种叫次广播信道,或者第二广播信道(secondary advertising packets)。

所谓的主广播类似于蓝牙 4.x 的广播,只工作在 37、38、39 三个信道,最大广播字节为 31 字节。而次广播允许蓝牙在除开 37、38、39 三个信道之外的其他 37 个信道上发送长度介于 0-255 字节的数据。次广播信道(0-36 channel)广播 255 字节数据。

本篇是关于广播自定义数据包,配置以及启动或关闭广播的流程查看 NRF52832学习笔记(9)——GAP从机端广播

二、广播内容参数

在 ble_advertising.h 文件中,提供了广播的初始化参数结构体,如果你需要自定义广播内容,那么就需要在广播数据包 advdata 或者扫描响应包 srdata 中添加内容。

/**@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:广播数据包
  • srdata:扫描响应包
  • config:配置广播参数(广播模式、广播间隔、广播时间)
  • evt_handler:将在广播事件上调用的事件处理程序
  • error_handler:错误处理程序,将把内部错误导入主应用程序
    我们可以看到广播数据包 advdata 或者扫描响应包 srdata 都是结构体 ble_advdata_t 类型,该结构体内定义了广播数据包或扫描响应包可以定义的内容:
/**@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:设备名称的类型
  • short_name_len:短设备名称的长度(如果指定了短类型)
  • include_appearance:确定是否包括展示图标
  • flags:广播数据标识字段
  • p_tx_power_level:TX 电平发送功率等级
  • uuids_more_available:部分服务UUID列表,只显示部分UUID列表在广播中,实际工程还有更多UUID
  • uuids_complete:全部服务UUID列表,广播中显示UUID列表就是实际工程中所有UUID
  • uuids_solicited:请求服务的UUID列表,一个从机设备可以发送服务请求数据类型广播去邀请主机进行连接,该主机设备包含一个或多个这个服务器请求数据广播所指定的服务
  • p_slave_conn_int:从机连接间隔范围
  • p_manuf_specific_data:制造商特定的数据,自定义广播数据
  • p_service_data_array:服务数据结构数组
  • service_data_count:服务数据结构的数量
  • include_ble_device_addr:确定是否包含LE蓝牙设备地址
  • le_role:LE角色区域。这个区域仅仅用于NFC。对应BLE广播,设置为NULL
  • p_tk_value:安全管理TK值的区域。这个区域仅仅用于NFC。对应BLE广播,设置为NULL
  • p_sec_mgr_oob_flags:安全管理器带外标志字段。这个区域仅仅用于NFC。对应BLE广播,设置为NULL
  • p_lesc_data:LE OOB数据的安全连接。这个区域仅仅用于NFC。对应BLE广播,设置为NULL

三、广播UUID的值

UUID的种类分为两种:

  • 一种是 SIG 定义的公共服务 UUID,所有的公共服务共用一个 128bit 的基础 UUID,不同的服务采用一个 16bit UUID 进行定义。
  • 另一种就是私有服务的 UUID,这是一个自定义的 128bit UUID。

注意:广播包里的 UUID 不影响服务特征值中 UUID 的值,仅仅是让广播把 UUID 的值广播给扫描设备,方便观察。

3.1 显示全部服务UUID列表

在广播参数里列出了三类 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:部分服务UUID列表,只显示部分UUID列表在广播中,实际工程还有更多UUID
  • uuids_complete:全部服务UUID列表,广播中显示UUID列表就是实际工程中所有UUID
  • uuids_solicited:请求服务的UUID列表,一个从机设备可以发送服务请求数据类型广播去邀请主机进行连接,该主机设备包含一个或多个这个服务器请求数据广播所指定的服务

UUID专门有个一个结构体 ble_advdata_uuid_list_t 进行标识:

typedef struct
{
  uint16_t uuid_cnt;      // UUID的数目
  ble_uuid_t * p_uuids;  // 指向UUID列表的指针
} ble_advdata_uuid_list_t

如果需要在广播中广播 UUID,需要专门建立一个 UUID 的结构体,让指向UUID列表的指针指向这个结构体。

  1. 首先,在主函数 main.c 中,声明如下 m_adv_uuids 结构体:
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}
};
  • 这个结构体内包含了 3 个 UUID 的列表:一个蓝牙串口服务,一个电池服务,一个发射功率服务。同时标注服务 UUID 的类型,其中蓝牙串口服务为私有服务,电池服务和发射功率服务为 SIG 定义的公共服务。两种服务类型 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. 接着,在广播初始化函数中添加如下代码:
static void advertising_init(void)
{
  ret_code_t err_code;
  ble_advertising_init_t init;
  memset(&init, 0, sizeof(init));
  ···
  ···
  // 定义全部UUID列表(包含一个128bit的UUID和两个服务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);// 初始化广播,导入参数
  APP_ERROR_CHECK(err_code);

  ble_advertising_conn_cfg_tag_set(&m_advertising, APP_BLE_CONN_CFG_TAG);// 设置广播识别号
}

由于要显示 128bit 的 UUID 长度比较长,因此把 UUID 的参数放入扫描响应包中。通过手机APP nrf connect 扫描后显示,Complete list of 表示是完整 UUID 列表。


3.2 显示部分服务UUID列表

通过设置 uuid_cnt 的数目控制广播中显示的 UUID 数目,不管广播数据包还是扫描响应包,都只提供 31 个字节的空间,因此需要注意可使用的空间。

  1. 同上,首先在主函数 main.c 中,声明如下 m_adv_uuids 结构体:
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. 接着,在广播初始化函数中添加如下代码:
static void advertising_init(void)
{
  ret_code_t err_code;
  ble_advertising_init_t init;
  memset(&init, 0, sizeof(init));
  ···
  ···
  // 定义全部UUID列表(包含一个128bit的UUID和两个服务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);// 初始化广播,导入参数
  APP_ERROR_CHECK(err_code);

  ble_advertising_conn_cfg_tag_set(&m_advertising, APP_BLE_CONN_CFG_TAG);// 设置广播识别号
}

通过手机APP nrf connect 扫描后显示,Incomplete list of 表示是部分 UUID 列表。这里只显示了一个 16bit UUID 和 一个 128bit UUID,而实际有三个 UUID。


3.3 显示请求服务UUID列表

一个典型的请求服务的 UUID 列表例子就是 ANCS 广播。这个例子需要一个 GATT 从机端和一个有这个 ANCS 的 GATT 主机端。通过在广播中广播 ANCS 请求服务的 UUID 去告诉扫描端(iOS设备)它正在“寻找”一个具有 ANCS 服务的主机设备。对于当前时间服务 CTS 的客户机也是如此。ble_app_cts_c 使用所请求的服务是因为它需要一个具有当前时间服务的 GATT 服务器的主机端,在 ble_app_cts_c 工程中,广播初始化的代码如下:

  1. 首先,声明 m_adv_uuids 结构体:
static ble_uuid_t m_adv_uuids[] =
{
  {BLE_UUID_CURRENT_TIME_SERVICE, BLE_UUID_TYPE_BLE}
};
  1. 接着,在广播初始化函数中添加如下代码:
static void advertising_init(void)
{
  ret_code_t err_code;
  ble_advertising_init_t init;
  memset(&init, 0, sizeof(init));
  ···
  ···
  // 定义全部UUID列表(包含一个128bit的UUID和两个服务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);// 初始化广播,导入参数
  APP_ERROR_CHECK(err_code);

  ble_advertising_conn_cfg_tag_set(&m_advertising, APP_BLE_CONN_CFG_TAG);// 设置广播识别号
}

通过手机APP nrf connect 扫描后显示:


四、广播从机的连接间隔参数

从机与主机之间的连接间隔,是由从机提出与主机进行协商,然后再由主机决定的参数。

在广播参数结构体 ble_advdata_t 中包含了一个结构体参数 ble_advdata_conn_int_t

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

指定了广播中可以广播的两个连接参数的值:

/**@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;

使用这个结构体,可以在广播初始化中定义需要广播的连接间隔的参数:

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;
  // 从机连接间隔范围最小值:10*1.25ms = 12.5ms
  conn_range.min_conn_interval = 10;
  // 从机连接间隔范围最大值:20*1.25ms = 25ms
  conn_range.max_conn_interval = 20;
  // 广播数据中包含从机连接间隔范围
  init.advdata.p_slave_conn_int = &conn_range;
  ···
  ···
  err_code = ble_advertising_init(&m_advertising, &init);// 初始化广播,导入参数
  APP_ERROR_CHECK(err_code);

  ble_advertising_conn_cfg_tag_set(&m_advertising, APP_BLE_CONN_CFG_TAG);// 设置广播识别号
}

通过手机APP nrf connect 扫描后显示:


五、广播自定义数据

  1. 首先,在 ble_advdata.h 文件中,定义了结构体 ble_advdata_manuf_data_t 表示公司厂家的数据与ID代码:
typedef struct 
{
  uint16_t company_identifier;  // 公司ID代码
  uint8_arry_t data;            // 制造者自定义的数据 
} ble_advdata_manuf_data_t;
  • company_identifier:公司ID号,每个公司都有独立申请的值,一般 company_identifier 都是厂家在 SIG 申请定义的唯一 ID 号。不同公司的 ID 号可以具体在 SIG 网站查询。例如 Nordic 的制造商 ID 号为:0x0059
  • data:制造商自定义的数据,这个参数可以自由的设置,只要广播包的空间足够。假设自定义数据为 0x11,0x22,0x33,0x44,0x55。
  1. 接着,在广播初始化函数中添加如下代码:
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};
  // 定义一个制造商自定义数据的结构体变量,配置广播数据时将该变量的地址赋值给广播数据包中
  ble_advdata_manuf_data_t manuf_specific_data;
  // 0x0059是Nordic的制造商ID
  manuf_specific_data.company_identifier = 0x0059;
  // 指向自定义数据
  manuf_specific_data.data.p_data = my_adv_manuf_data;
  // 自定义数据的大小
  manuf_specific_data.data.size   = sizeof(my_adv_manuf_data);
  // 定义自定义数据到广播包中
  init.advdata.p_manuf_specific_data = &manuf_specific_data;
  ···
  ···
  err_code = ble_advertising_init(&m_advertising, &init);// 初始化广播,导入参数
  APP_ERROR_CHECK(err_code);

  ble_advertising_conn_cfg_tag_set(&m_advertising, APP_BLE_CONN_CFG_TAG);// 设置广播识别号
}

通过手机APP nrf connect 扫描后显示:


六、动态更新广播内容

动态更新广播内容实际就是更新第四节中 manuf_specific_data.data.p_data 的值,然后停止广播,再更新广播内容,再开启广播,这里涉及到三个函数:

  • advertising_stop():停止广播
  • advertising_advdata_update():更新广播内容
  • advertising_start():开启广播
    例子如下。

七、自定义广播内容及动态更新广播例子

下载 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
 */
// 广播数据
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,
};
// 扫描响应数据
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 初始化广播数据包
 @param pInit - 广播数据初始化结构体
 @return 无
*/
void InitAdvertisingData(ble_advertising_init_t *pInit)
{   
    pInit->advdata.flags = BLE_GAP_ADV_FLAGS_LE_ONLY_GENERAL_DISC_MODE;                                         // 蓝牙设备模式,LE普通发现模式和不支持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 初始化扫描应答包
 @param pInit - 广播数据初始化结构体
 @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;                                                            // 广播时的名称显示     
}

/**
 @brief 开启广播
 @param 无
 @return 无
*/
void EnableAdvertising(void)
{
    bool eraseBonds;
    advertising_start(eraseBonds);
}

/**
 @brief 关闭广播
 @param 无
 @return 无
*/
void DisableAdvertising(void)
{
    advertising_stop();
}

/**
 @brief 更新广播内容
 @param 无
 @return 无
*/
void UpdataAdvData(void)
{
    bool eraseBonds = false;
    advertising_stop();
    
    advertising_advdata_update();;
    
    advertising_start(eraseBonds);
}
  • 这里对广播数据包中的制造商ID company_identifier 也当作自定义数据使用
g_advertisingData.company_identifier = 0x11;                                                                    
g_advertisingData.company_identifier = g_advertisingData.company_identifier | (0x22 << 8);
  • 其他自定义数据则对 g_advertisingDataEventsAndParamsData 数组进行赋值
g_advertisingData.data.p_data = g_advertisingDataEventsAndParamsData;
g_advertisingData.data.size = 19;
  • 扫描响应包也是如此,同时再扫描响应包的内容中加入了设备名称
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 设置BLE连接状态
 @param status -[in] true - 已连接;false - 已断开
 @return 无
*/
void SetBleConnectStatus(bool status)
{
    s_isConnectedFlag = status;
}
/**
 @brief 获取BLE连接状态
 @param 无
 @return true - 已连接;false - 已断开
*/
bool GetBleConnectStatus(void)
{
    return s_isConnectedFlag;
}

static bool s_waitForUpdateAdvDataFlag = false;
/**
 @brief 设置等待更新广播状态
 @param status -[in] true - 等待;false - 空闲
 @return 无
*/
void SetWaitForUpdateAdvDataStatus(bool status)
{
    s_waitForUpdateAdvDataFlag = status;
}
/**
 @brief 获取等待更新广播状态
 @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);                                // 初始化广播,导入参数
    APP_ERROR_CHECK(err_code);

    ble_advertising_conn_cfg_tag_set(&m_advertising, APP_BLE_CONN_CFG_TAG);                             // 设置广播识别号
}

/**@brief Function for initializing the Advertising functionality.
 */
static void advertising_init(void)
{
    ret_code_t err_code;
    memset(&g_advertisingInit, 0, sizeof(g_advertisingInit));
    
    // 初始化广播数据包和扫描响应包内容
    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;                                  // 广播超时时间,值0则保持一种广播模式不变

    g_advertisingInit.evt_handler = on_adv_evt;

    err_code = ble_advertising_init(&m_advertising, &g_advertisingInit);                                // 初始化广播,导入参数
    APP_ERROR_CHECK(err_code);

    ble_advertising_conn_cfg_tag_set(&m_advertising, APP_BLE_CONN_CFG_TAG);                             // 设置广播识别号
}

7.4 初始化广播内容

通过调用 InitAdvertisingDataInitScanResponseData 两个函数初始化广播数据包和扫描响应包

7.5 动态更新广播内容

例如在一些中断函数中,通过对 g_advertisingDataEventsAndParamsData 进行赋值,同时在连接时不更新广播,等待断开连接后更新。

// 中断函数中
g_advertisingDataEventsAndParamsData = 0x55

if(GetBleConnectStatus() == false)
{       
  UpdataAdvData();  // 非连接状态,立即更新广播
}
else
{
  SetWaitForUpdateAdvDataStatus(true);  // 等待断开后更新广播
}

然后再蓝牙事件处理函数 ble_evt_handler 中,判断是否正在连接中,如果不是,立即更新广播内容

/**@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 日

• 参考:青风电子社区

你可能感兴趣的:(NRF52832学习笔记(10)——GAP从机端广播自定义数据)