基于Nordic nRF52840 Longrange移植细节和应用探讨

苗子RF/无线 2024-01-07 11:42:20 3861阅读 举报

Long Range 是BLE 5.0中新增加的特性,主要是通过扩频的方式,提高了天线接收的灵敏度,同时将最大的发送功率,从4.0/4.1/4.2中的10mW增大到5.0的100mW。相对于BLE来说,确实增加了通信的距离。

Long Range 并没有那么神奇,主要代码变动在于PHY层的编码方式,应用层是一样的。将普通的BLE方式修改为Long Range模式也非常简单,如下:

从机设备(BLE Peripheral):

  1. sdk_config配置

设置广播包数据长度,255Bytes

/**@defgroup BLE_GAP_ADV_SET_DATA_SIZES Advertising data sizes.
 * @{ */
#define BLE_GAP_ADV_SET_DATA_SIZE_MAX                    (255)   /**< Maximum data length for an advertising set.

2. 广播初始化配置

因为longrange模式需要工作在可连接,扫描不可回应的状态,所以广播回应包需要设置为NULL,禁止respond。

另外,广播参数主要是编码方式改为PHY CODED方式

adv_params.primary_phy = BLE_GAP_PHY_CODED;
adv_params.secondary_phy = BLE_GAP_PHY_CODED;
adv_params.properties.type = BLE_GAP_ADV_TYPE_EXTENDED_CONNECTABLE_NONSCANNABLE_UNDIRECTED;

详情看如下初始化代码:

/**@brief Advertising functionality initialization.
 *
 * @details Encodes the required advertising data and passes it to the stack.
 *          Also builds a structure to be passed to the stack when starting advertising.
 */
static void advertising_init(void)
{
    uint32_t             err_code;
    ble_advdata_t        advdata;
    ble_gap_adv_params_t adv_params;
    static uint8_t       advdata_buff[BLE_GAP_ADV_SET_DATA_SIZE_MAX];
    static uint8_t       srdata_buff[BLE_GAP_ADV_SET_DATA_SIZE_MAX];
    uint16_t             advdata_buff_len = BLE_GAP_ADV_SET_DATA_SIZE_MAX;
    uint16_t             srdata_buff_len = BLE_GAP_ADV_SET_DATA_SIZE_MAX;
    uint8_t              flags = BLE_GAP_ADV_FLAGS_LE_ONLY_GENERAL_DISC_MODE;
		
    // Build and set advertising data.
    memset(&advdata, 0, sizeof(advdata));
    //设置广播包
    advdata.name_type               = BLE_ADVDATA_FULL_NAME;
    advdata.include_appearance      = true;
    advdata.flags                   = flags;
    advdata.uuids_complete.uuid_cnt = sizeof(adv_uuids) / sizeof(adv_uuids[0]);
    advdata.uuids_complete.p_uuids  = adv_uuids;
    //编码
    err_code = ble_advdata_encode(&advdata, advdata_buff, &advdata_buff_len);
    APP_ERROR_CHECK(err_code);
		
    memset(&advdata_enc, 0, sizeof(advdata_enc));
    //广播包赋值
    advdata_enc.adv_data.p_data = advdata_buff;
    advdata_enc.adv_data.len    = advdata_buff_len;
		
    //设置回应包
    // Build and set advertising data.
    memset(&advdata, 0, sizeof(advdata));
    //设置厂商信息
    ble_advdata_manuf_data_t manuf_data;
    manuf_data.company_identifier = COMPANY_IDENTIFIER;
    manuf_data.data.size          = ADV_ADDL_MANUF_DATA_LEN;
    manuf_data.data.p_data        = m_add_adv_manuf_data;
    advdata.p_manuf_specific_data = &manuf_data;
    //编码
    err_code = ble_advdata_encode(&advdata, srdata_buff, &srdata_buff_len);
    APP_ERROR_CHECK(err_code);
    //回应包赋值
    advdata_enc.scan_rsp_data.p_data = NULL;
    advdata_enc.scan_rsp_data.len    = 0;

    // Initialise advertising parameters (used when starting advertising).
    memset(&adv_params, 0, sizeof(adv_params));

    adv_params.primary_phy     = BLE_GAP_PHY_CODED;
    adv_params.secondary_phy   = BLE_GAP_PHY_CODED; 
    adv_params.properties.type = BLE_GAP_ADV_TYPE_EXTENDED_CONNECTABLE_NONSCANNABLE_UNDIRECTED;
    adv_params.filter_policy   = BLE_GAP_ADV_FP_ANY;
    adv_params.interval        = APP_ADV_INTERVAL;
    adv_params.duration        = APP_ADV_DURATION;
		
    err_code = sd_ble_gap_adv_set_configure(&m_adv_handle, &advdata_enc, &adv_params);
    APP_ERROR_CHECK(err_code);
}

3. 设置广播功率并启动广播

建议设置为最高的8DB,如果电量有限,也可以设置低一些发射功率。代码如下:

void advertising_start(void)
{
    uint32_t err_code;
	
    //设置广播功率,8dB,连接状态也将会是继承这个发射功率
    err_code = sd_ble_gap_tx_power_set(BLE_GAP_TX_POWER_ROLE_ADV, m_adv_handle, 8);
    APP_ERROR_CHECK(err_code);
	
    err_code = sd_ble_gap_adv_start(m_adv_handle, APP_BLE_CONN_CFG_TAG);
    if (err_code != NRF_SUCCESS && err_code != NRF_ERROR_INVALID_STATE)
    {
        APP_ERROR_HANDLER(err_code);
    }
}


主机设备(BLE Central):

  1. sdk_config配置

配置扫描广播包和从机数目和从机一致,255Bytes

// <o> NRF_BLE_SCAN_BUFFER - Data length for an advertising set. 
#ifndef NRF_BLE_SCAN_BUFFER
#define NRF_BLE_SCAN_BUFFER 255
#endif

2. 通过配置好扫描参数和扫描功率启动蓝牙扫描

扫描PHY层参数需要匹配广播的参数

//蓝牙扫描参数配置
static const ble_gap_scan_params_t m_scan_params =
{
    .active   = 1,
    .extended = 1,
    .interval = NRF_BLE_SCAN_SCAN_INTERVAL,
    .window   = NRF_BLE_SCAN_SCAN_WINDOW,
    .timeout  = NRF_BLE_SCAN_SCAN_DURATION,
    .filter_policy = BLE_GAP_SCAN_FP_ACCEPT_ALL,
    .scan_phys =  BLE_GAP_PHY_CODED,
};

//蓝牙扫描数据,提供给系统一个缓冲
static ble_data_t m_scan_buffer;
static uint8_t    scan_buffer_data[NRF_BLE_SCAN_BUFFER]; 

/**@brief Function to start scanning. */
uint32_t ble_scan_start(void)
{
    ret_code_t ret;
	
    m_scan_buffer.p_data = scan_buffer_data;
    m_scan_buffer.len = NRF_BLE_SCAN_BUFFER;
    /* It is ok to ignore the function return value here, because this function can return NRF_SUCCESS or
     NRF_ERROR_INVALID_STATE, when app is not in the scanning state.*/
    UNUSED_RETURN_VALUE(sd_ble_gap_scan_stop());
	
    //设置发送功率,8dB,连接也将会是继承这个发射功率
    ret = sd_ble_gap_tx_power_set(BLE_GAP_TX_POWER_ROLE_SCAN_INIT, BLE_CONN_HANDLE_INVALID, 8);
    APP_ERROR_CHECK(ret);

    ret = sd_ble_gap_scan_start(&m_scan_params, &m_scan_buffer);
    APP_ERROR_CHECK(ret);
	
    return ret;
}


实际测量和应用

1. 经笔者实际体验和使用,发现longrange模式下时,常规的BLE主机是无法扫描到该设备的,只有两个设备同时处于longrange模式才能通信,这会限制从设备和APP上应用的场景。

2. 经测试,RSSI受天线和遮挡物的影响很大,基本上没法和距离形成比例关系,不建议通过RSSI来估算距离。

3. 远距离发起连接容易超时导致连接失败,因为BLE连接需要比较严格的通信时序,一旦握手时期未收到指定的RF数据包,则会导致连接失败。这种表现比如在近处连接好后,走到远处可以持续通信。但是在远处可以扫描到设备,却无法正常连接。

因此笔者比较推荐longrange的应用可以通过更新可变数据的广播包来实现接收数据。如果需要双向通信,可以通过编程让设备运行于主从模式下,既扫描,又广播以达到稳定的异步通信过程。


关于BLE可变数据广播包,笔者推荐利用广播超时模式,在超时事件里更新广播数据即可,广播包数据更新放在厂商信息区域,不影响其他蓝牙广播数据段。实现代码如下:

static void advertising_updata(uint8_t adv_manuf_data[ADV_ADDL_MANUF_DATA_LEN])
{
    uint32_t             err_code;
    ble_advdata_t        advdata;
    ble_gap_adv_params_t adv_params;
    static uint8_t       advdata_buff[BLE_GAP_ADV_SET_DATA_SIZE_MAX];
    uint16_t             advdata_buff_len = BLE_GAP_ADV_SET_DATA_SIZE_MAX;
    uint8_t              flags = BLE_GAP_ADV_FLAGS_LE_ONLY_GENERAL_DISC_MODE;
		
    // Build and set advertising data.
    memset(&advdata, 0, sizeof(advdata));
    //设置广播包
    advdata.name_type               = BLE_ADVDATA_FULL_NAME;
    advdata.include_appearance      = true;
    advdata.flags                   = flags;
    advdata.uuids_complete.uuid_cnt = sizeof(adv_uuids) / sizeof(adv_uuids[0]);
    advdata.uuids_complete.p_uuids  = adv_uuids;
    //设置厂商信息
    ble_advdata_manuf_data_t manuf_data;
    manuf_data.company_identifier = 0x1234;
    manuf_data.data.size          = ADV_ADDL_MANUF_DATA_LEN;
    manuf_data.data.p_data        = adv_manuf_data;
    advdata.p_manuf_specific_data = &manuf_data;
   //编码
    err_code = ble_advdata_encode(&advdata, advdata_buff, &advdata_buff_len);
    APP_ERROR_CHECK(err_code);
		
    //memset(&advdata_enc, 0, sizeof(advdata_enc));
    //广播包赋值
    advdata_enc.adv_data.p_data = advdata_buff;
    advdata_enc.adv_data.len    = advdata_buff_len;
		
    //回应包赋值
    advdata_enc.scan_rsp_data.p_data = NULL;
    advdata_enc.scan_rsp_data.len    = 0;

    // Initialise advertising parameters (used when starting advertising).
    memset(&adv_params, 0, sizeof(adv_params));

    adv_params.primary_phy     = BLE_GAP_PHY_CODED;
    adv_params.secondary_phy   = BLE_GAP_PHY_CODED; 
    adv_params.properties.type = BLE_GAP_ADV_TYPE_EXTENDED_CONNECTABLE_NONSCANNABLE_UNDIRECTED;;
    adv_params.filter_policy   = BLE_GAP_ADV_FP_ANY;
    adv_params.interval        = APP_ADV_INTERVAL;
    adv_params.duration        = APP_ADV_DURATION;
		
    err_code = sd_ble_gap_adv_set_configure(&m_adv_handle, &advdata_enc, NULL);
    APP_ERROR_CHECK(err_code);
}

注意:这个广播更新函数必须在广播超时或者停止后才能调用,否则容易出错导致产生err_code

版权声明:
作者:苗子
链接:https://www.dianziwang.net/p/3c82f8b68c4ea.html
来源:RF/无线
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以点击 “举报”


登录 后发表评论
0条评论
还没有人评论过~