nordic 的蓝牙扫描配置和条件过滤

1 篇文章 0 订阅
1 篇文章 0 订阅

主机扫描

​ 蓝牙扫描可以用作发现周围的从机设备,为建立连接作准备;也可以用作发现空中的蓝牙广播信息。扫描是蓝牙的一个重要功能,也是主机所必备的。

扫描参数配置

ble_gap_scan_params_t 结构体下面是一些常用的扫描参数

.extended 是否接受延长广播

.active 主动扫描,可以获得额外的扫描响应包

.filter_policy 扫描过滤,可以选择过滤的方式

.scan_phys 扫描的速率

.interval 扫描间隔

.window 扫描窗口

.timeout 超时时间

一次扫描的过程包括:

  • 1 打开射频开启扫描,并持续一段时间,这段时间是扫描窗口的参数设置。

  • 2 关闭射频,等待下一次的扫描。这个时间由扫描间隔决定。

  • 3 重复1,2,直到达到超时时间。

超时时间设为0,扫描事件将会一直进行;设置扫描窗口(开窗),应该不大于扫描间隔。如果扫描窗口等于扫描间隔,那么每一次扫描将会一直打开射频,没有空闲状态。当然,这样功耗也会很大;要根据实际需要,合理调整扫描窗口的占空比。

扫描过滤

​ 扫描过滤,主要是可以在混乱的空中广播中筛选指定的广播。有两种方式:一种是白名单,一种是条件过滤。

白名单

​ 白名单就是添加一组蓝牙MAC地址,只有指定MAC地址的设备才可以扫描,连接,从而达到过滤的目的。

过滤一个白名单设备

具体实现:

修改参数设置 .filter_policy = BLE_GAP_SCAN_FP_WHITELIST,

在scan_evt_handler 扫描事件里,增加 NRF_BLE_SCAN_EVT_WHITELIST_REQUEST,添加过滤的地址,然后调用sd_ble_gap_whitelist_set().

			 case NRF_BLE_SCAN_EVT_WHITELIST_REQUEST:{
			ble_gap_addr_t whitelist_address;
            ble_gap_addr_t const *p_whitelist_address;

            memset(&whitelist_address,0,sizeof(whitelist_address));    
           whitelist_address.addr_id_peer = 1;
           whitelist_address.addr_type = BLE_GAP_ADDR_TYPE_PUBLIC;

            whitelist_address.addr[5] = 0x20;
            whitelist_address.addr[4] = 0x21;
            whitelist_address.addr[3] = 0x06;
            whitelist_address.addr[2] = 0x17;
            whitelist_address.addr[1] = 0x66;
            whitelist_address.addr[0] = 0x66;


            p_whitelist_address = &whitelist_address;
            err_code = sd_ble_gap_whitelist_set(&p_whitelist_address,1);

            if(err_code == NRF_SUCCESS)
            {
                NRF_LOG_INFO("SUCCESSFUL SETTING !");
            }
            APP_ERROR_CHECK(err_code);
            }

然后再注册的扫描观察者的回调 nrf_ble_scan_on_ble_evt 中,收到广播报告BLE_GAP_EVT_ADV_REPORT后,nrf_ble_scan_on_adv_report函数中,判断是白名单,且设置了自动连接后,直接调用 sd_ble_gap_connect(),发起连接。
在这里插入图片描述

同时过滤多个白名单设备,并记录广播包和扫描响应包数目

在主函数中设置过滤的白名单:

uint16_t target_one_resp_bag_num = 0;
uint16_t target_one_adv_bag_num  = 0;

uint16_t target_two_resp_bag_num = 0;
uint16_t target_two_adv_bag_num  = 0;

uint16_t target_three_resp_bag_num = 0;
uint16_t target_three_adv_bag_num  = 0;

uint8_t slaver_one_mac[6]   = { 0x01, 0x00, 0x12, 0x08, 0x21, 0x20 };
uint8_t slaver_two_mac[6]   = { 0x02, 0x00, 0x12, 0x08, 0x21, 0x20 };
uint8_t slaver_three_mac[6] = { 0x03, 0x00, 0x12, 0x08, 0x21, 0x20 };

在扫描事件回调函数中添加设置
case NRF_BLE_SCAN_EVT_WHITELIST_REQUEST: 设置白名单的条件
case NRF_BLE_SCAN_EVT_WHITELIST_ADV_REPORT: 是白名单的广播报告,在这里判断一下广播包还是扫描响应包,再判断一下是哪个从机,对应变量加一;

	static void scan_evt_handler(scan_evt_t const *p_scan_evt)
{
    ret_code_t err_code;

    switch (p_scan_evt->scan_evt_id) {
        case NRF_BLE_SCAN_EVT_CONNECTING_ERROR: {
            err_code = p_scan_evt->params.connecting_err.err_code;
            APP_ERROR_CHECK(err_code);
        } break;

        case NRF_BLE_SCAN_EVT_CONNECTED: {
            ble_gap_evt_connected_t const *p_connected = p_scan_evt->params.connected.p_connected;
            // Scan is automatically stopped by the connection.
            NRF_LOG_INFO("Connecting to target %02x%02x%02x%02x%02x%02x",
                         p_connected->peer_addr.addr[0],
                         p_connected->peer_addr.addr[1],
                         p_connected->peer_addr.addr[2],
                         p_connected->peer_addr.addr[3],
                         p_connected->peer_addr.addr[4],
                         p_connected->peer_addr.addr[5]);

            sd_ble_gap_rssi_start(p_scan_evt->params.connected.conn_handle, 8, 3);
            sd_ble_gap_rssi_get(p_scan_evt->params.connected.conn_handle, &m_rssi, &m_channel);

            NRF_LOG_INFO("rssi is  %d", m_rssi);
            NRF_LOG_INFO("channel id  %d", m_channel);
        } break;

        case NRF_BLE_SCAN_EVT_SCAN_TIMEOUT: {
            NRF_LOG_INFO("Scan timed out.");
            scan_start();
        } break;
#if WHITELIST_ENABLE
        case NRF_BLE_SCAN_EVT_WHITELIST_REQUEST: {

            NRF_LOG_INFO("whitelist set !");

            ble_gap_addr_t        whitelist_address[3];
            ble_gap_addr_t const *p_whitelist_address[3];

            /* 白名单1 */
            memset(&whitelist_address[0], 0, sizeof(whitelist_address[0]));
            whitelist_address[0].addr_id_peer = 1;
            whitelist_address[0].addr_type    = BLE_GAP_ADDR_TYPE_PUBLIC;

            memcpy(whitelist_address[0].addr,slaver_one_mac,6);

            p_whitelist_address[0] = &whitelist_address[0];

            /* 白名单2 */
            memset(&whitelist_address[1], 0, sizeof(whitelist_address[1]));
            whitelist_address[1].addr_id_peer = 1;
            whitelist_address[1].addr_type    = BLE_GAP_ADDR_TYPE_PUBLIC;

             memcpy(whitelist_address[1].addr,slaver_two_mac,6);

            p_whitelist_address[1] = &whitelist_address[1];

            /* 白名单3 */
            memset(&whitelist_address[2], 0, sizeof(whitelist_address[2]));
            whitelist_address[2].addr_id_peer = 1;
            whitelist_address[2].addr_type    = BLE_GAP_ADDR_TYPE_PUBLIC;
             memcpy(whitelist_address[2].addr,slaver_three_mac,6);

            p_whitelist_address[2] = &whitelist_address[2];

            err_code = sd_ble_gap_whitelist_set(p_whitelist_address, 3);

            if (err_code == NRF_SUCCESS) {
                NRF_LOG_INFO("SUCCESSFUL SETTING !");
            }
            APP_ERROR_CHECK(err_code);
        } break;
#endif
        case NRF_BLE_SCAN_EVT_WHITELIST_ADV_REPORT: {
            // NRF_LOG_INFO("white list adv report");

            if (p_scan_evt->params.p_whitelist_adv_report->type.scan_response) {
                if (!memcmp(p_scan_evt->params.p_whitelist_adv_report->peer_addr.addr,
                            slaver_one_mac,
                            6)) {

                    //NRF_LOG_INFO("slaver 1 response bag");
                    target_one_resp_bag_num += 1;
                }

                else if (!memcmp(p_scan_evt->params.p_whitelist_adv_report->peer_addr.addr,
                                 slaver_two_mac,
                                 6)) {

                    //NRF_LOG_INFO("slaver 2 response bag");
                    target_two_resp_bag_num += 1;
                } else if (!memcmp(p_scan_evt->params.p_whitelist_adv_report->peer_addr.addr,
                                   slaver_three_mac,
                                   6)) {

                    //NRF_LOG_INFO("slaver 3 response bag");
                    target_three_resp_bag_num += 1;
                }
            }

            else {
                if (!memcmp(p_scan_evt->params.p_whitelist_adv_report->peer_addr.addr,
                            slaver_one_mac,
                            6)) {

                    //NRF_LOG_INFO("slaver 1 adv bag");
                    target_one_adv_bag_num += 1;
                }

                else if (!memcmp(p_scan_evt->params.p_whitelist_adv_report->peer_addr.addr,
                                 slaver_two_mac,
                                 6)) {

                    //NRF_LOG_INFO("slaver 2 adv bag");
                    target_two_adv_bag_num += 1;
                } else if (!memcmp(p_scan_evt->params.p_whitelist_adv_report->peer_addr.addr,
                                   slaver_three_mac,
                                   6)) {

                    //NRF_LOG_INFO("slaver 3 adv bag");
                    target_three_adv_bag_num += 1;
                }
            }

        } break;

        default:
            break;
    }
}
	

在开一个定时器,每30s循环打印:

static void count_timeout(void *p_context)
{

    static uint8_t i = 1;

    NRF_LOG_INFO("run time is %d s", 30 * i);

    mac_addr_printf(slaver_one_mac);
    NRF_LOG_INFO("adv package is %d", target_one_adv_bag_num);
    NRF_LOG_INFO("scan resp package is %d\n", target_one_resp_bag_num);

    mac_addr_printf(slaver_two_mac);
    NRF_LOG_INFO("adv package is %d", target_two_adv_bag_num);
    NRF_LOG_INFO("scan resp package is %d\n", target_two_adv_bag_num);

    mac_addr_printf(slaver_three_mac);
    NRF_LOG_INFO("adv package is %d", target_three_adv_bag_num);
    NRF_LOG_INFO("scan resp package is %d\n", target_three_resp_bag_num);

    i++;
}
条件过滤

​ 条件过滤,顾名思义可以提供设置的条件,来筛选出需要连接的设备,有以下几种过滤方式:

typedef enum

{

​ SCAN_NAME_FILTER, /**< Filter for names. */

​ SCAN_SHORT_NAME_FILTER, /**< Filter for short names. */

​ SCAN_ADDR_FILTER, /**< Filter for addresses. */

​ SCAN_UUID_FILTER, /**< Filter for UUIDs. */

​ SCAN_APPEARANCE_FILTER, /**< Filter for appearances. */

} nrf_ble_scan_filter_type_t;

​ 可以看到有名字,UUID,地址,外观等多种过滤方式,可以选择一种或几种来作为过滤条件。

具体实现:

在扫描初始化函数里添加

err_code = nrf_ble_scan_filter_set(&m_scan, SCAN_UUID_FILTER, &m_nus_uuid);
APP_ERROR_CHECK(err_code);

err_code = nrf_ble_scan_filters_enable(&m_scan, NRF_BLE_SCAN_UUID_FILTER, false);
 APP_ERROR_CHECK(err_code);

如果需要多个条件过滤:

	err_code = nrf_ble_scan_filter_set(&m_scan, SCAN_NAME_FILTER, m_target_periph_name);
    APP_ERROR_CHECK(err_code);

    err_code = nrf_ble_scan_filter_set(&m_scan, SCAN_UUID_FILTER, &target_uuid);
    APP_ERROR_CHECK(err_code);
    
    err_code = nrf_ble_scan_filters_enable(&m_scan,
                       NRF_BLE_SCAN_NAME_FILTER | NRF_BLE_SCAN_UUID_FILTER,
                       false);
    

如果仔细观察上面两个库函数,可以自己简化

     memcpy(m_scan.scan_filters.name_filter.target_name[m_scan.scan_filters.name_filter.name_cnt++],
           m_peripheral_name,
           strlen(m_peripheral_name));

替换 nrf_ble_scan_filter_set(&m_scan, SCAN_NAME_FILTER, m_target_periph_name);

err_code = nrf_ble_scan_filters_disable(&m_scan);
     ASSERT(err_code == NRF_SUCCESS);
     m_scan.scan_filters.name_filter.name_filter_enabled = true;

替换 nrf_ble_scan_filters_enable(&m_scan, SCAN_NAME_FILTER, false);

其他的可以过滤方式同样可以这样替换。

设置完成后,与白名单类似,扫描观察者的回调 nrf_ble_scan_on_ble_evt 中,收到广播报告BLE_GAP_EVT_ADV_REPORT,判断过滤条件,建立连接。

获取动态rssi

在scan_evt_handler 的 NRF_BLE_SCAN_EVT_CONNECTED 中,添加:

 sd_ble_gap_rssi_start(p_scan_evt->params.connected.conn_handle, 8, 3);
 sd_ble_gap_rssi_get(p_scan_evt->params.connected.conn_handle, &m_rssi, &m_channel);
 
 NRF_LOG_INFO("rssi is  %d", m_rssi);
 NRF_LOG_INFO("channel id  %d", m_channel);

在扫描观察者的回调函数 nrf_ble_scan_on_ble_evt 中,添加

case BLE_GAP_EVT_RSSI_CHANGED:

            sd_ble_gap_rssi_start(p_ble_evt->evt.gatts_evt.conn_handle, 10, 3);
            sd_ble_gap_rssi_get(p_ble_evt->evt.gatts_evt.conn_handle, &m_rssi, &m_channel);

            NRF_LOG_INFO("rssi is  %d", m_rssi);
            NRF_LOG_INFO("channel id  %d", m_channel);
            break;

这样每次rssi发生变化,都会触发事件,然后打印rssi .

广播报告事件

​ 当设置好扫描参数并开启扫描后,主机收到广播包之后,将会触发扫描报告事件BLE_GAP_EVT_ADV_REPORT

,广播的信息将会赋值给扫描报告结构体 ble_gap_evt_adv_report_t ,包含广播类型,地址,物理层传输速率,发射功率,rssi,广播数据等;可以在这里打印出广播信息

case BLE_GAP_EVT_ADV_REPORT:

            NRF_LOG_INFO("adv report");
            NRF_LOG_INFO("Connecting to target %02x%02x%02x%02x%02x%02x",
                         p_adv_report->peer_addr.addr[5],
                         p_adv_report->peer_addr.addr[4],
                         p_adv_report->peer_addr.addr[3],
                         p_adv_report->peer_addr.addr[2],
                         p_adv_report->peer_addr.addr[1],
                         p_adv_report->peer_addr.addr[0]);
            NRF_LOG_INFO("adv tx power is %d", p_adv_report->tx_power);

            if (p_adv_report->type.scan_response) {

                if (p_adv_report->data.len > 0) {

                    NRF_LOG_INFO("scan response received !");
                    NRF_LOG_HEXDUMP_INFO(p_adv_report->data.p_data, p_adv_report->data.len);
                } else {

                    NRF_LOG_INFO("empty bag");
                }
            }

            else //广播数据
            {
                NRF_LOG_INFO("adv bag !");
                NRF_LOG_HEXDUMP_INFO(p_adv_report->data.p_data, p_adv_report->data.len);
            }

            nrf_ble_scan_on_adv_report(p_scan_data, p_adv_report);

            break;

​ 如果你不需要连接,你就可以直接把数据广播出来,然后在 BLE_GAP_EVT_ADV_REPORT 事件中,直接按照私有协议解析数据。

​ 如果你需要连接,我们来看一下 nrf_ble_scan_on_adv_report 函数,它主要是对扫描到广播进行处理,首先会判断你开了哪些过滤条件,比如名字,扫描初始化的时候,name_filter_enabled 被打开,

  • init_scan.connect_if_match = true;

nrf_ble_scan_on_adv_report函数中,会判断,并且使能过滤匹配的标志 is_filter_matched,

    if (name_filter_enabled) {
        filter_cnt++;
        if (adv_name_compare(p_adv_report, p_scan_ctx)) {
            filter_match_cnt++;

            // Information about the filters matched.
            scan_evt.params.filter_match.filter_match.name_filter_match = true;
            is_filter_matched                                           = true;
        }
    }

函数最后,会根据上面的条件判断,进行连接操作

if (all_filter_mode && (filter_match_cnt == filter_cnt)) {
        scan_evt.scan_evt_id = NRF_BLE_SCAN_EVT_FILTER_MATCH;
        nrf_ble_scan_connect_with_target(p_scan_ctx, p_adv_report);
    }
else if ((!all_filter_mode) && is_filter_matched) {
        scan_evt.scan_evt_id = NRF_BLE_SCAN_EVT_FILTER_MATCH;
        nrf_ble_scan_connect_with_target(p_scan_ctx, p_adv_report);

判断 connect_if_match

  • 0 退出
  • 1 建立连接
static void nrf_ble_scan_connect_with_target(nrf_ble_scan_t const *const           p_scan_ctx,
                                             ble_gap_evt_adv_report_t const *const p_adv_report)
{
    //NRF_LOG_INFO("start connect !");
    ret_code_t err_code;
    scan_evt_t scan_evt;

    // For readability.
    ble_gap_addr_t const *       p_addr        = &p_adv_report->peer_addr;
    ble_gap_scan_params_t const *p_scan_params = &p_scan_ctx->scan_params;
    ble_gap_conn_params_t const *p_conn_params = &p_scan_ctx->conn_params;
    uint8_t                      con_cfg_tag   = p_scan_ctx->conn_cfg_tag;

    // Return if the automatic connection is disabled.
    if (!p_scan_ctx->connect_if_match) {
        return;
    }

    // Stop scanning.
    nrf_ble_scan_stop();

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

    // Establish connection.
    err_code = sd_ble_gap_connect(p_addr, p_scan_params, p_conn_params, con_cfg_tag);

    NRF_LOG_DEBUG("Connecting");

    scan_evt.scan_evt_id                    = NRF_BLE_SCAN_EVT_CONNECTING_ERROR;
    scan_evt.params.connecting_err.err_code = err_code;

    NRF_LOG_DEBUG("Connection status: %d", err_code);

    // If an error occurred, send an event to the event handler.
    if ((err_code != NRF_SUCCESS) && (p_scan_ctx->evt_handler != NULL)) {
        p_scan_ctx->evt_handler(&scan_evt);
    }
}
  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值