NRF52832之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 字节数据。

二、广播内容参数

在 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 长度是不同的,分别为 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). */
/** @} */

2.接着,在广播初始化函数中添加如下代码:

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}
};

2.接着,在广播初始化函数中添加如下代码:

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 扫描后显示:
在这里插入图片描述

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

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

1.在广播参数结构体 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;

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

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

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

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

  • 1
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值