一、背景
链路层(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列表的指针指向这个结构体。
- 首先,在主函数 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 长度是不同的,分别为
128bit
和16bit
,如下面定义:
/** @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). */
/** @} */
- 接着,在广播初始化函数中添加如下代码:
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 个字节的空间,因此需要注意可使用的空间。
- 同上,首先在主函数 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}
};
- 接着,在广播初始化函数中添加如下代码:
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 工程中,广播初始化的代码如下:
- 首先,声明
m_adv_uuids
结构体:
static ble_uuid_t m_adv_uuids[] =
{
{BLE_UUID_CURRENT_TIME_SERVICE, BLE_UUID_TYPE_BLE}
};
- 接着,在广播初始化函数中添加如下代码:
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 扫描后显示:
五、广播自定义数据
- 首先,在 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。
- 接着,在广播初始化函数中添加如下代码:
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.c
和 user_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 初始化广播内容
通过调用 InitAdvertisingData
和 InitScanResponseData
两个函数初始化广播数据包和扫描响应包
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 日
• 参考:青风电子社区