Beware that this post is related to an SDK in maintenance mode
More Info: Consider nRF Connect SDK for new designs
This post is older than 2 years and might not be relevant anymore
More Info: Consider searching for newer posts

scan name filter in ble_app_uart_c example

Hi,

I use SES IDE, nRF5_SDK_16.0.0, nRF52840 DKs. I am using ble_app_uart_c as a template for my application that needs to connect to multiple peripherals whose device names are "ABC_xxxx" where xxxx is peripheral device's serial number. I am trying to find a way to set a scan name filter so that my central device can connect to any peripheral whose name begins with "ABC_". I set NRF_BLE_SCAN_NAME_CNT= 8 and use SCAN_NAME_FILTER of "ABC_" in scan_init(). I found this function call stack in nrf_ble_scan.c: nrf_ble_scan_on_adv_report() -> adv_name_compare() -> ble_advdata_name_find() which compares advertised name and target name. I removed the original 'if' condition and used my own 'if' condition as seen below:

bool ble_advdata_name_find(uint8_t const * p_encoded_data,
                           uint16_t        data_len,
                           char    const * p_target_name)
{
    uint16_t        parsed_name_len;
    uint8_t const * p_parsed_name;
    uint16_t        data_offset          = 0;

    if (p_target_name == NULL)
    {
        return false;
    }

    parsed_name_len = ble_advdata_search(p_encoded_data,
                                         data_len,
                                         &data_offset,
                                         BLE_GAP_AD_TYPE_COMPLETE_LOCAL_NAME);

    p_parsed_name = &p_encoded_data[data_offset];

   // NAME FILTER COMPARE START
   uint16_t target_name_len= strlen(p_target_name);
   if(memcmp(p_target_name, p_parsed_name, target_name_len)== 0)                    // compare only target_name_len chars of advertised name and target name
/*    // Dont compare entire p_parsed_name with p_target_name
    if (   (data_offset != 0)
        && (parsed_name_len != 0)
        && (strlen(p_target_name) == parsed_name_len)
        && (memcmp(p_target_name, p_parsed_name, parsed_name_len) == 0))              // compare entire p_parsed_name
*/
    // NAME FILTER COMPARE END
    {
        return true;
    }

    return false;
}

I observed that this works well for me. Is this the right way to set the kind of name filter I want or is there a better way to do it?

Thanks

-Kunal

  • Hi,

    This change looks good to me. I don't foresee any problems with doing it this way. You could also do it from your scanner callback in your app if you prefer not to modify the module itself.

    Best regards,

    Vidar

  • Ideally, I do not wish to modify module level drivers. By scanner callback if you mean scan_evt_handler(), then I see these events which can be used:

    /**@brief Enumeration for scanning events.
     *
     * @details These events are propagated to the main application if a handler is provided during
     *          the initialization of the Scanning Module. @ref NRF_BLE_SCAN_EVT_WHITELIST_REQUEST cannot be
     *          ignored if whitelist is used.
     */
    typedef enum
    {
        NRF_BLE_SCAN_EVT_FILTER_MATCH,         /**< A filter is matched or all filters are matched in the multifilter mode. */
        NRF_BLE_SCAN_EVT_WHITELIST_REQUEST,    /**< Request the whitelist from the main application. For whitelist scanning to work, the whitelist must be set when this event occurs. */
        NRF_BLE_SCAN_EVT_WHITELIST_ADV_REPORT, /**< Send notification to the main application when a device from the whitelist is found. */
        NRF_BLE_SCAN_EVT_NOT_FOUND,            /**< The filter was not matched for the scan data. */
        NRF_BLE_SCAN_EVT_SCAN_TIMEOUT,         /**< Scan timeout. */
        NRF_BLE_SCAN_EVT_SCAN_REQ_REPORT,      /**< Scan request report. */
        NRF_BLE_SCAN_EVT_CONNECTING_ERROR,     /**< Error occurred when establishing the connection. In this event, an error is passed from the function call @ref sd_ble_gap_connect. */
        NRF_BLE_SCAN_EVT_CONNECTED             /**< Connected to device. */
    } nrf_ble_scan_evt_t;

    1) Which of this event do I use to get scan data?

    2) Does NRF_BLE_SCAN_EVT_SCAN_REQ_REPORT event makes every advertisement data available to parse?

    3) Or did you mean something different by "scanner callback"? In that case please provide brief steps on how to filter advertised names in app level.

    Thank you

    -Kunal

  • You can use the NRF_BLE_SCAN_EVT_NOT_FOUND event. It's reported whenever the scanner has read an advertisement packet that doesn't match any of the predefined scan filter(s).  I made this small (untested) code snippet to illustrate what mean:

    /**@brief Function for handling Scanning Module events.
     */
    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_NOT_FOUND:
             {
                 uint16_t parsed_name_len;
                 uint8_t const * p_parsed_name;
                 uint16_t data_offset = 0;
    
                 ble_gap_evt_adv_report_t const * adv_report = p_scan_evt->params.p_not_found;
    
                 /* Scan encoded adv. payload for data of type BLE_GAP_AD_TYPE_COMPLETE_LOCAL_NAME */ 
                 parsed_name_len = ble_advdata_search(
                     adv_report->data.p_data, adv_report->data.len, &data_offset, BLE_GAP_AD_TYPE_COMPLETE_LOCAL_NAME);
                 
                 /* Name found if parsed_name_len != 0 */
                 p_parsed_name = &adv_report->data.p_data[data_offset];
                 
    
                 //TODO: Check if p_parsed_name == target name
             }
             break;
             
             ...

  • Hi Vidar,

    Thank you for the code snippet. I used it and called nrf_ble_scan_connect_with_target() if target name matched adv name. It works ok. My code looks like this-

    case NRF_BLE_SCAN_EVT_NOT_FOUND:                       // new case added. Implement ble_advdata.c->ble_advdata_name_find() type function here
             {
                 uint16_t parsed_name_len;
                 uint8_t const * p_parsed_name;
                 uint16_t data_offset = 0;
                 uint16_t target_name_len= strlen(m_target_periph_name);
    
                 ble_gap_evt_adv_report_t const * adv_report = p_scan_evt->params.p_not_found;
    
                 /* Scan encoded adv. payload for data of type BLE_GAP_AD_TYPE_COMPLETE_LOCAL_NAME */ 
                 parsed_name_len = ble_advdata_search(adv_report->data.p_data, adv_report->data.len, &data_offset, BLE_GAP_AD_TYPE_COMPLETE_LOCAL_NAME);
                 
                 /* Name found if parsed_name_len != 0 */
                 p_parsed_name = &adv_report->data.p_data[data_offset];            
    
                 // if p_parsed_name == target name, then Connect with peripheral
                 if(memcmp(m_target_periph_name, p_parsed_name, target_name_len)== 0)                    // compare only target_name_len chars of advertised name and target name
                 {
                    nrf_ble_scan_connect_with_target(&m_scan, adv_report);                              // connect with peripheral
                 }
    
             }
             break;

    Do I have to change any struct or parameters when I call nrf_ble_scan_connect_with_target()? like the code in nrf_ble_scan.c -> nrf_ble_scan_on_adv_report() 

    Thanks 

    -Kunal

  • Hi Kunal,

    It shouldn't be necessary to change any parameters before calling nrf_ble_scan_connect_with_target(). However, this API call is not exposed by default so you would have to make some minor modifications to the module to make it work (add a prototype to the header file and remove the static keyword). I don't think it's problematic to do so, but you could consider replacing this with a direct call to sd_ble_gap_scan_stop & sd_ble_gap_connect() instead.

Related