Nordic 的自定义广播和动态广播实现(1)

4 篇文章 4 订阅
3 篇文章 0 订阅

蓝牙自定义广播的实现方式

1.基于SDK应用函数的编写

static void advertising_init(void)
{
    uint32_t               err_code;
    ble_advertising_init_t init;

    memset(&init, 0, sizeof(init));

    init.advdata.name_type          = BLE_ADVDATA_FULL_NAME;
    init.advdata.include_appearance = false;
    init.advdata.flags              = BLE_GAP_ADV_FLAGS_LE_ONLY_LIMITED_DISC_MODE;

    init.srdata.uuids_complete.uuid_cnt = sizeof(m_adv_uuids) / sizeof(m_adv_uuids[0]);
    init.srdata.uuids_complete.p_uuids  = m_adv_uuids;

    init.config.ble_adv_fast_enabled  = true;
    init.config.ble_adv_fast_interval = APP_ADV_INTERVAL;
    init.config.ble_adv_fast_timeout  = APP_ADV_DURATION;
    init.evt_handler = on_adv_evt;

    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);
}
static void advertising_start(void)
{
    uint32_t err_code = ble_advertising_start(&m_advertising, BLE_ADV_MODE_FAST);
    APP_ERROR_CHECK(err_code);
}

这是标准SDK的广播初始化程序,可以很清晰的看到初始化函数,只调用了两个ble函数:
1 ble_advertising_init(&m_advertising, &init);
2 ble_advertising_conn_cfg_tag_set(&m_advertising,
APP_BLE_CONN_CFG_TAG);
这里要看一下传的参数 &m_advertising ,&init。
&m_advertising在这里定义的
BLE_ADVERTISING_DEF(m_advertising); 主函数定义的观察者,不理解可以先不用管。
&int是一个结构体类型变量 ble_advertising_init_t init;可以看到初始化全篇都是在配置init,最后把init丢进ble_advertising_init里面。
ble_advertising_conn_cfg_tag_set()里面其实是配置了conn_cfg_tag这个标记,
p_advertising->conn_cfg_tag = ble_cfg_tag;
那看一下 init里面到底有哪些参数:

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;

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;

typedef struct
{
    bool     ble_adv_on_disconnect_disabled;     /**< Enable or disable automatic return to advertising upon disconnecting.*/
    bool     ble_adv_whitelist_enabled;          /**< Enable or disable use of the whitelist. */
    bool     ble_adv_directed_high_duty_enabled; /**< Enable or disable high duty direct advertising mode. Can not be used together with extended advertising. */
    bool     ble_adv_directed_enabled;           /**< Enable or disable direct advertising mode. */
    bool     ble_adv_fast_enabled;               /**< Enable or disable fast advertising mode. */
    bool     ble_adv_slow_enabled;               /**< Enable or disable slow advertising mode. */
    uint32_t ble_adv_directed_interval;          /**< Advertising interval for directed advertising. */
    uint32_t ble_adv_directed_timeout;           /**< Time-out (number of tries) for direct advertising. */
    uint32_t ble_adv_fast_interval;              /**< Advertising interval for fast advertising. */
    uint32_t ble_adv_fast_timeout;               /**< Time-out (in units of 10ms) for fast advertising. */
    uint32_t ble_adv_slow_interval;              /**< Advertising interval for slow advertising. */
    uint32_t ble_adv_slow_timeout;               /**< Time-out (in units of 10ms) for slow advertising. */
    bool     ble_adv_extended_enabled;           /**< Enable or disable extended advertising. */
    uint32_t ble_adv_secondary_phy;              /**< PHY for the secondary (extended) advertising @ref BLE_GAP_PHYS (BLE_GAP_PHY_1MBPS, BLE_GAP_PHY_2MBPS or BLE_GAP_PHY_CODED). */
    uint32_t ble_adv_primary_phy;                /**< PHY for the primary advertising. @ref BLE_GAP_PHYS (BLE_GAP_PHY_1MBPS, BLE_GAP_PHY_2MBPS or BLE_GAP_PHY_CODED). */
} ble_adv_modes_config_t;

主要是ble_advdata_t; 和 ble_adv_modes_config_t结构体。

其中 advdata 主要包括广播的相关内容,包括,名称,蓝牙工作模式,发射功率,UUID,制造商特定数据,服务数据,等,config主要是广播的模式配置,包含快速广播,慢速广播,高速定向广播,以及对应的广播间隔和超时时间等;需要哪个就配置对应的内容,最后调用 ble_advertising_init();把配置好的结构体,装载进去。最后调用advertising_start 发射出去。初级小白可以先在上面的基础上修改参数试试。
比如:修改名字长度
init.advdata.name_type = BLE_ADVDATA_SHORT_NAME;
init.advdata.short_name_len = 3; //要跟上长度

1.1增加制造商信息

一般用这个方便自定义广播信息

typedef struct
{
    uint16_t                     company_identifier;                  /**< Company identifier code. */
    uint8_array_t                data;                                /**< Additional manufacturer specific data. */
} ble_advdata_manuf_data_t;

typedef struct
{
    uint16_t  size;                 /**< Number of array entries. */
    uint8_t * p_data;               /**< Pointer to array entries. */
} uint8_array_t;

可以在初始化增加这些

//主函数的全局数组,一个广播包,一个扫描响应包
uint8_t test_array_adv[] = {0x55,0x55,0x55,0x55};
uint8_t test_array_scan[] = {0xAA,0xAA,0xAA,0xAA,};

//主函数自定义的全局结构体,包括Init结构体,定义为全局是方便后面改动
ble_advertising_init_t init;
ble_advdata_manuf_data_t adv_data;
ble_advdata_manuf_data_t sr_data;


  adv_data.company_identifier = 0x8888;
  adv_data.data.p_data = test_array_adv;
  adv_data.data.size =  sizeof(test_array_adv)/sizeof(test_array_adv[0]);
  init.advdata.p_manuf_specific_data = &adv_data;

    sr_data.company_identifier = 0x8888;
    sr_data.data.p_data = test_array_scan;
    sr_data.data.size = sizeof(test_array_scan) /sizeof(test_array_scan[0]);
    init.srdata.p_manuf_specific_data = &sr_data;

增加上面这些,用nRE Connect 查看广播包:
在这里插入图片描述
会出现两个TYPE为FF,的一串数据,一个是广播包,一个是扫描响应包,数据内容就是你 test_array_adv[] 和 test_array_scan[] 当然数组会有长度限制,所有广播内容一起不超过31个字节(扫描响应包可选,是额外的扩充)。

1.2广播的结构

补充一下广播基本结构:
AD struct = AD len + AD type +AD data
其中AD len 是AD type AD data的长度和。AD type 蓝牙官方定义广播类型。AD data是 AD type后对应的数据

在这里插入图片描述
大概是这样的一个表,Bluetooth官网有定义的。

1.3广播名称和MAC地址修改

名称在gap_params_init函数里面,

    err_code = sd_ble_gap_device_name_set(&sec_mode,
                                          (const uint8_t *) DEVICE_NAME,
                                          strlen(DEVICE_NAME));
                                          
#define DEVICE_NAME                     "Nordic_UART"   

修改DEVICE_NAME 这个宏即可。

mac地址的话设置方法如下:


typedef struct
{
  uint8_t addr_id_peer : 1;       /**< Only valid for peer addresses.
                                       This bit is set by the SoftDevice to indicate whether the address has been resolved from
                                       a Resolvable Private Address (when the peer is using privacy).
                                       If set to 1, @ref addr and @ref addr_type refer to the identity address of the resolved address.

                                       This bit is ignored when a variable of type @ref ble_gap_addr_t is used as input to API functions. */
  uint8_t addr_type    : 7;       /**< See @ref BLE_GAP_ADDR_TYPES. */
  uint8_t addr[BLE_GAP_ADDR_LEN]; /**< 48-bit address, LSB format.
                                       @ref addr is not used if @ref addr_type is @ref BLE_GAP_ADDR_TYPE_ANONYMOUS. */
} ble_gap_addr_t;



ble_gap_addr_t  mac_address;    //地址相关的结构体变量

  mac_address.addr[5] = 0x20;
  mac_address.addr[4] = 0x21;
  mac_address.addr[3] = 0x06;
  mac_address.addr[2] = 0x01;
  mac_address.addr[1] = 0X66;
  mac_address.addr[0] = 0x66;
  mac_address.addr_type = BLE_GAP_ADDR_TYPE_PUBLIC;  //默认是静态随机地址,改为了公共地址

  NRF_LOG_INFO("mac address is");
  NRF_LOG_HEXDUMP_INFO(mac_address.addr, 6);

  err_code = sd_ble_gap_addr_set(&mac_address);

可以把上面代码直接放在广播初始化里面。

2基于SDK底层函数的编写

应用函数其实是基于底层sd函数的再封装;我们把ble_advertising_init()打开看一下

uint32_t ble_advertising_init(ble_advertising_t            * const p_advertising,
                              ble_advertising_init_t const * const p_init)
{
    uint32_t ret;
    if ((p_init == NULL) || (p_advertising == NULL))
    {
        return NRF_ERROR_NULL;
    }
    if (!config_is_valid(&p_init->config))
    {
        return NRF_ERROR_INVALID_PARAM;
    }

    p_advertising->adv_mode_current               = BLE_ADV_MODE_IDLE;
    p_advertising->adv_modes_config               = p_init->config;
    p_advertising->conn_cfg_tag                   = BLE_CONN_CFG_TAG_DEFAULT;
    p_advertising->evt_handler                    = p_init->evt_handler;
    p_advertising->error_handler                  = p_init->error_handler;
    p_advertising->current_slave_link_conn_handle = BLE_CONN_HANDLE_INVALID;
    p_advertising->p_adv_data                     = &p_advertising->adv_data;

    memset(&p_advertising->peer_address, 0, sizeof(p_advertising->peer_address));

    // Copy advertising data.
    if (!p_advertising->initialized)
    {
        p_advertising->adv_handle = BLE_GAP_ADV_SET_HANDLE_NOT_SET;
    }
    p_advertising->adv_data.adv_data.p_data = p_advertising->enc_advdata;

    if (p_advertising->adv_modes_config.ble_adv_extended_enabled == true)
    {
#ifdef BLE_GAP_ADV_SET_DATA_SIZE_EXTENDED_CONNECTABLE_MAX_SUPPORTED
        p_advertising->adv_data.adv_data.len = BLE_GAP_ADV_SET_DATA_SIZE_EXTENDED_CONNECTABLE_MAX_SUPPORTED;
#else
    p_advertising->adv_data.adv_data.len = BLE_GAP_ADV_SET_DATA_SIZE_MAX;
#endif // BLE_GAP_ADV_SET_DATA_SIZE_EXTENDED_CONNECTABLE_MAX_SUPPORTED
    }
    else
    {
        p_advertising->adv_data.adv_data.len = BLE_GAP_ADV_SET_DATA_SIZE_MAX;
    }

    ret = ble_advdata_encode(&p_init->advdata, p_advertising->enc_advdata, &p_advertising->adv_data.adv_data.len);
    VERIFY_SUCCESS(ret);

    p_advertising->adv_data.scan_rsp_data.p_data = p_advertising->enc_scan_rsp_data;
    if (p_advertising->adv_modes_config.ble_adv_extended_enabled == true)
    {
#ifdef BLE_GAP_ADV_SET_DATA_SIZE_EXTENDED_CONNECTABLE_MAX_SUPPORTED
        p_advertising->adv_data.scan_rsp_data.len = BLE_GAP_ADV_SET_DATA_SIZE_EXTENDED_CONNECTABLE_MAX_SUPPORTED;
#else
        p_advertising->adv_data.scan_rsp_data.len = BLE_GAP_ADV_SET_DATA_SIZE_MAX;
#endif // BLE_GAP_ADV_SET_DATA_SIZE_EXTENDED_CONNECTABLE_MAX_SUPPORTED
    }
    else
    {
        p_advertising->adv_data.scan_rsp_data.len = BLE_GAP_ADV_SET_DATA_SIZE_MAX;
    }
    ret = ble_advdata_encode(&p_init->srdata,
                              p_advertising->adv_data.scan_rsp_data.p_data,
                             &p_advertising->adv_data.scan_rsp_data.len);
    VERIFY_SUCCESS(ret);

    // Configure a initial advertising configuration. The advertising data and and advertising
    // parameters will be changed later when we call @ref ble_advertising_start, but must be set
    // to legal values here to define an advertising handle.
    p_advertising->adv_params.primary_phy     = BLE_GAP_PHY_1MBPS;
    p_advertising->adv_params.duration        = p_advertising->adv_modes_config.ble_adv_fast_timeout;
    p_advertising->adv_params.properties.type = BLE_GAP_ADV_TYPE_CONNECTABLE_SCANNABLE_UNDIRECTED;
    p_advertising->adv_params.p_peer_addr     = NULL;
    p_advertising->adv_params.filter_policy   = BLE_GAP_ADV_FP_ANY;
    p_advertising->adv_params.interval        = p_advertising->adv_modes_config.ble_adv_fast_interval;

    ret = sd_ble_gap_adv_set_configure(&p_advertising->adv_handle, NULL, &p_advertising->adv_params);
    VERIFY_SUCCESS(ret);

    p_advertising->initialized = true;
    return ret;
}

可以看到大部分工作实在配置 p_advertising这个结构体变量,此外还有两个底层的函数:ble_advdata_encode 和sd_ble_gap_adv_set_configure
原型是
ble_advdata_encode(ble_advdata_t const * const p_advdata,
uint8_t * const p_encoded_data,
uint16_t * const p_len)

和 sd_ble_gap_adv_set_configure(uint8_t *p_adv_handle, ble_gap_adv_data_t const *p_adv_data, ble_gap_adv_params_t const *p_adv_params));

第一个函数的作用,相当于编码的作用,是把advdata 里面的结构体的配置,转换为一个encoded_data数组,这个数组有p_len指向的长度。

第二个函数是设置advertising data 和params然后直接发射出去,ble_gap_adv_data_t 结构体下是最原始的广播内容,里面存放的数组是按 AD_len, AD_type, AD_data,的结构存放,可以自己动手编辑好这种格式的数组,然后调用sd_ble_gap_adv_set_configure发射出去。

编写一个使用 sd_ble_gap_adv_set_configure 的广播实例。先观察sd_ble_gap_adv_set_configure的参数 。

static uint8_t m_adv_handle = BLE_GAP_ADV_SET_HANDLE_NOT_SET; //可以作为全局变量

uint8_t test_array_adv[31] = {0x020x010x06,0x02,0x0A0x00};
uint8_t test_array_scan[31] = {0x00};

//data
 ble_gap_adv_data_t set_data;
 
  set_data.adv_data.p_data = test_array_adv;
  set_data.adv_data.len = 31;

  set_data.scan_rsp_data.p_data = test_array_scan;
  set_data.scan_rsp_data.len = 31;

//config params
  ble_gap_adv_params_t const adv_params = {
      .properties =
          {
              .type = BLE_GAP_ADV_TYPE_CONNECTABLE_SCANNABLE_UNDIRECTED,
          },
      .p_peer_addr = NULL,
      .filter_policy = BLE_GAP_ADV_FP_ANY,
      .interval = 300,
      .duration = 0,

      .primary_phy = BLE_GAP_PHY_1MBPS, // Must be changed to connect in long
                                        // range. (BLE_GAP_PHY_CODED)
      .secondary_phy = BLE_GAP_PHY_1MBPS,
  };

   APP_ERROR_CHECK(
      sd_ble_gap_adv_set_configure(&m_adv_handle, &set_data, &adv_params));

  APP_ERROR_CHECK(
  sd_ble_gap_adv_start(m_adv_handle, APP_BLE_CONN_CFG_TAG));

这里把原始广播数据指向了自定义的数组,这个数组大小是31.数组内按照上面广播结构的格式去放数据,否则会报错。
解析一下 test_array_adv[31] = {0x02,0x01,0x06,0x02,0x0A,0x00};
02表示AD_len 长度为2 ,
01表示 AD_type ,-----flags.
06-表示AD_data,对应了普通发现模式,不支持BR.
等价于执行了以下配置:
init.advdata.flags = BLE_GAP_ADV_FLAGS_LE_ONLY_GENERAL_DISC_MODE;
而 0x02,0x0A,0x00 表示发射功率0dB
APP上可以看到
在这里插入图片描述

接下来,在以上基础上我们加上ble_advdata_encode(),感受一下它的用法,依然先观察形参,

static __attribute__((unused)) void adv_init(void) {


  int8_t tx_power_leval = TX_POWER_LEVAL;


  ble_gap_adv_data_t set_data;
   ble_advdata_t adv_data1;
  ble_advdata_t adv_data2;

  memset(&adv_data1, 0, sizeof(adv_data1));
  memset(&adv_data2, 0, sizeof(adv_data2));           //省略会没有广播,奇怪!


  adv_data.company_identifier = 0x8888;
  adv_data.data.p_data = test_array;
  adv_data.data.size = 8;

   adv_data1.flags = 0x06;                            //广播包和扫描响应包中只能初始化一个

  adv_data1.p_tx_power_level = &tx_power_leval;
  adv_data1.p_manuf_specific_data = &adv_data;
  adv_data1.name_type = BLE_ADVDATA_NO_NAME;

   adv_data2.name_type = BLE_ADVDATA_FULL_NAME;
   adv_data2.p_manuf_specific_data = &adv_data;

  ble_gap_adv_params_t const adv_params = {
      .properties =
          {
              .type = BLE_GAP_ADV_TYPE_CONNECTABLE_SCANNABLE_UNDIRECTED,
          },
      .p_peer_addr = NULL,
      .filter_policy = BLE_GAP_ADV_FP_ANY,
      .interval = 300,
      .duration = 0,

      .primary_phy = BLE_GAP_PHY_1MBPS, // Must be changed to connect in long
                                        // range. (BLE_GAP_PHY_CODED)
      .secondary_phy = BLE_GAP_PHY_1MBPS,
  };

  set_data.adv_data.p_data = test_array_adv;
  set_data.adv_data.len = 31;

  set_data.scan_rsp_data.p_data = test_array_scan;
  set_data.scan_rsp_data.len = 31;

  APP_ERROR_CHECK(ble_advdata_encode(&adv_data1, set_data.adv_data.p_data,
                                     &set_data.adv_data.len));
   APP_ERROR_CHECK(ble_advdata_encode(&adv_data2, set_data.scan_rsp_data.p_data,
                                     &set_data.scan_rsp_data.len));

  APP_ERROR_CHECK(
      sd_ble_gap_adv_set_configure(&m_adv_handle, &set_data, &adv_params));

  APP_ERROR_CHECK(sd_ble_gap_adv_start(m_adv_handle, APP_BLE_CONN_CFG_TAG));
}

定义了ble_advdata_t 类型的adv_data1,adv_data2作为广播包和扫描响应包,接着配置这两个结构体,与初始化类似。
接下来就是ble_advdata_encode函数的作用,看他干了什么,可以打开进入函数内部,能更加清晰看到处理过程,简单说明一下
ble_advdata_encode(&adv_data1, set_data.adv_data.p_data,
&set_data.adv_data.len)
把刚才配置好的结构体 adv_data1,编码进adv_data这个数组,len是这个数组的长度。同样的
ble_advdata_encode(&adv_data2, set_data.scan_rsp_data.p_data,
&set_data.scan_rsp_data.len)
把adv_data2,编码进了scan_rsp_data。
因为set_data的adv_data和scan_rsp_data都已经编码进了数据 ,最后
sd_ble_gap_adv_set_configure(&m_adv_handle, &set_data, &adv_params));
把set_data设置进去,调用sd_ble_gap_adv_start()打开广播发射

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值