美文网首页物联网loT从业者物联网相关技术研究
NRF52832学习笔记(10)——GAP从机端广播自定义数据

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

作者: Leung_ManWah | 来源:发表于2020-02-12 23:54 被阅读0次

    一、背景

    链路层(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 长度是不同的,分别为 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从机端广播自定义数据

        本文链接:https://www.haomeiwen.com/subject/arinxhtx.html