Has the format of ble_gap_evt_adv_report_t changed recently?

I have modified code for the Dongle to allow reporting multiple advertising devices, and select one device to connect to the USB/serial port. I can display the 64-bit unit ID and can connect and transfer data in both directions. My device inserts the name of the software in the advertising packet per some Nordic examples, and it shows up on the Android nRF Connect app. That app also shows "Raw Data" where I can clearly see the same field. When I attempt to make use of the name field in the Dongle code, sometimes it shows up, sometimes it's shifted a few bytes, and sometimes I get complete gibberish. From what I read, there is a ble_data_t field named data, with elements p_data that should point to the string, and len that gives the number of bytes. How should I use these to extract the text sent from my device?

Parents
  • Hi,

    There has not been a nRF5 SDK nor SoftDevice release for quite a few years, so there has not been any recent changes. If you can elaborate more on what you do (and also what you have changed if it worked before), I can attempt to understand what the problem is.

    Based on what you write where the data is seemingly sometimes corrupted, two things come to mind. One is some sort of parser error as I do not know how you parse the data, but another possibility is if you restart scanning before the data has been parsed. If that is doen with the same scan buffer it could be that the buffer is updated while or before being parsed.

  • I have these declarations in my code, most of which came from Nordic-provided code:

    static struct
       {
       ble_gap_evt_adv_report_t Adv;
       ble_gap_scan_params_t Scan;
       ble_gap_conn_params_t Conn;
       uint8_t ConnCfgTag;
       uint8_t Name [32];
       }
    Seen [50];
    int NumSeen = 0;

    Within the scan evt handler is this code, with my added code highlighted:

       case NRF_BLE_SCAN_EVT_FILTER_MATCH:
          {
          int i;
          nrf_ble_scan_evt_filter_match_t const * p_filter_match = &(p_scan_evt -> params.filter_match);
          if (NumSeen >= 50) NumSeen = 0;
          if (NumSeen < 50)
             {
             Seen [NumSeen].Adv = *(p_filter_match -> p_adv_report);
             for (int j = 0; j < sizeof Seen [NumSeen].Name - 1; ++j)
                Seen [NumSeen].Name [j] = ((uint8_t *) &Seen [NumSeen].Adv.data.p_data) [j];
             Seen [NumSeen].Name [31] = 0;
             Seen [NumSeen].Scan.extended = 0;
             Seen [NumSeen].Scan.report_incomplete_evts = 0;
             Seen [NumSeen].Scan.active = 1;
             Seen [NumSeen].Scan.filter_policy = 0;
             Seen [NumSeen].Scan.scan_phys = 1;
             Seen [NumSeen].Scan.interval = 160;
             Seen [NumSeen].Scan.window = 80;
             Seen [NumSeen].Scan.timeout = 0;
             Seen [NumSeen].Conn.min_conn_interval = 6; // Minimum Connection Interval in 1.25 ms units
             Seen [NumSeen].Conn.max_conn_interval = 24; // Maximum Connection Interval in 1.25 ms units
             Seen [NumSeen].Conn.slave_latency = 0; // Slave Latency in number of connection events
             Seen [NumSeen].Conn.conn_sup_timeout = 400; // Connection Supervision Timeout in 10 ms units
             Seen [NumSeen].ConnCfgTag = MyConfigTag;
             for (i = 0; i < NumSeen; ++i)
                if (!memcmp (&Seen [NumSeen].Adv.peer_addr, &Seen [i].Adv.peer_addr, sizeof Seen [NumSeen].Adv.peer_addr))
                   break;
             if (i == NumSeen)
                {
                ++NumSeen;
                if (!ShowPeripheralsFlag) ShowPeripherals (1);
                }
             }
          }
          break;

    The Name field is my own addition to the struct (named Seen[] in my code) that records all the info found in the scanning for advertisements. I added the copy here (boldface code above) because it appeared that the .Adv.data was getting corrupted later. Are you suggesting that I might be copying too early, before it's all filled in? I'm just trying to capture the name of the device, as it is set via the sd_ble_gap_device_name_set call within the device's application code. Is there a better way to get that field? The Android nRF Connect app gets it flawlessly, so I know it's there to be harvested.

  • Hi,

    The highlighted code looks problematic to me. First, this part:

             for (int j = 0; j < sizeof Seen [NumSeen].Name - 1; ++j)
                Seen [NumSeen].Name [j] = ((uint8_t *) &Seen [NumSeen].Adv.data.p_data) [j];
             Seen [NumSeen].Name [31] = 0;

    Here it looks like you are treating everything from the start of the advertising data struct as the name. Here you are taking the address with "&Seen [NumSeen].Adv.data.p_data" starting with the &, and then dereferencing to get the character. But p_data is already a pointer. So you are essentially making a double pointer which you then dereference once. So the character will be invalid. Here, remove the ampersand. That way, you will copy the character value which I assume is the goal.

    The next problem is that a standard advertising packet is on a TLF format, so even if the advertising packet only contain the name (which I do not know if is the case), the TLV header is first.

    I would recommend using ble_advdata_name_find() from the SDK instead of implementing this yourself, as that handles the task of extracting the from a standard advertising packet and is well tested. You can see an example of how that is used in the scan module (nrf_ble_scan.c).

    Here is a potential implementation replacing your loop (not tested):

    #include "ble_advdata.h"
    
    // ... inside NRF_BLE_SCAN_EVT_FILTER_MATCH, after Seen[NumSeen].Adv = *p_adv_report;
    
    ble_data_t const * p_d = &p_filter_match->p_adv_report->data;
    uint16_t offset = 0;
    uint16_t name_len = ble_advdata_search(p_d->p_data, p_d->len, &offset,
                                           BLE_GAP_AD_TYPE_COMPLETE_LOCAL_NAME);
    if (name_len == 0) {
        offset = 0;
        name_len = ble_advdata_search(p_d->p_data, p_d->len, &offset,
                                      BLE_GAP_AD_TYPE_SHORT_LOCAL_NAME);
    }
    
    if (name_len > 0) {
        uint16_t n = name_len < sizeof(Seen[NumSeen].Name) - 1
                   ? name_len : sizeof(Seen[NumSeen].Name) - 1;
        memcpy(Seen[NumSeen].Name, &p_d->p_data[offset], n);
        Seen[NumSeen].Name[n] = '\0';
    } else {
        Seen[NumSeen].Name[0] = '\0';
    }

Reply
  • Hi,

    The highlighted code looks problematic to me. First, this part:

             for (int j = 0; j < sizeof Seen [NumSeen].Name - 1; ++j)
                Seen [NumSeen].Name [j] = ((uint8_t *) &Seen [NumSeen].Adv.data.p_data) [j];
             Seen [NumSeen].Name [31] = 0;

    Here it looks like you are treating everything from the start of the advertising data struct as the name. Here you are taking the address with "&Seen [NumSeen].Adv.data.p_data" starting with the &, and then dereferencing to get the character. But p_data is already a pointer. So you are essentially making a double pointer which you then dereference once. So the character will be invalid. Here, remove the ampersand. That way, you will copy the character value which I assume is the goal.

    The next problem is that a standard advertising packet is on a TLF format, so even if the advertising packet only contain the name (which I do not know if is the case), the TLV header is first.

    I would recommend using ble_advdata_name_find() from the SDK instead of implementing this yourself, as that handles the task of extracting the from a standard advertising packet and is well tested. You can see an example of how that is used in the scan module (nrf_ble_scan.c).

    Here is a potential implementation replacing your loop (not tested):

    #include "ble_advdata.h"
    
    // ... inside NRF_BLE_SCAN_EVT_FILTER_MATCH, after Seen[NumSeen].Adv = *p_adv_report;
    
    ble_data_t const * p_d = &p_filter_match->p_adv_report->data;
    uint16_t offset = 0;
    uint16_t name_len = ble_advdata_search(p_d->p_data, p_d->len, &offset,
                                           BLE_GAP_AD_TYPE_COMPLETE_LOCAL_NAME);
    if (name_len == 0) {
        offset = 0;
        name_len = ble_advdata_search(p_d->p_data, p_d->len, &offset,
                                      BLE_GAP_AD_TYPE_SHORT_LOCAL_NAME);
    }
    
    if (name_len > 0) {
        uint16_t n = name_len < sizeof(Seen[NumSeen].Name) - 1
                   ? name_len : sizeof(Seen[NumSeen].Name) - 1;
        memcpy(Seen[NumSeen].Name, &p_d->p_data[offset], n);
        Seen[NumSeen].Name[n] = '\0';
    } else {
        Seen[NumSeen].Name[0] = '\0';
    }

Children
Related