This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

CC2642R: Updating advertising when advertising with 2 advertising sets in both legacy and extended (with code PHY) modes

Part Number: CC2642R

Hi,

I am using SimpleLink CC13xx_CC26xx SDK 6.40.00.13 with the CC2642R.

My application is advertising with a 2s interval in both legacy and long range modes (i.e. code PHY) and I need to update sensor data in the advertising data every 2s.
The advertising parameters are as follows:

GapAdv_params_t advParamsLegacy = {
  .eventProps =   GAP_ADV_PROP_CONNECTABLE | GAP_ADV_PROP_LEGACY | GAP_ADV_PROP_SCANNABLE,
  .primIntMin =   DEFAULT_ADVERTISING_INTERVAL,
  .primIntMax =   DEFAULT_ADVERTISING_INTERVAL,
  .primChanMap =  GAP_ADV_CHAN_ALL,
  .peerAddrType = PEER_ADDRTYPE_PUBLIC_OR_PUBLIC_ID,
  .peerAddr =     { 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa },
  .filterPolicy = GAP_ADV_WL_POLICY_ANY_REQ,
  .txPower =      GAP_ADV_TX_POWER_NO_PREFERENCE,
  .primPhy =      GAP_ADV_PRIM_PHY_1_MBPS,
  .secPhy =       GAP_ADV_SEC_PHY_1_MBPS,
  .sid =          0
};

GapAdv_params_t advParamsLongRange = {
  .eventProps =   GAP_ADV_PROP_CONNECTABLE,
  .primIntMin =   DEFAULT_ADVERTISING_INTERVAL,
  .primIntMax =   DEFAULT_ADVERTISING_INTERVAL,
  .primChanMap =  GAP_ADV_CHAN_ALL,
  .peerAddrType = PEER_ADDRTYPE_PUBLIC_OR_PUBLIC_ID,
  .peerAddr =     { 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa },
  .filterPolicy = GAP_ADV_WL_POLICY_ANY_REQ,
  .txPower =      GAP_ADV_TX_POWER_NO_PREFERENCE,
  .primPhy =      GAP_ADV_PRIM_PHY_CODED_S2,
  .secPhy =       GAP_ADV_SEC_PHY_CODED_S8,
  .sid =          1
};

After creating the two advertising sets I am enabling several events as follows:

status = GapAdv_setEventMask(advHandleLegacy, GAP_ADV_EVT_MASK_START_AFTER_ENABLE | GAP_ADV_EVT_MASK_END_AFTER_DISABLE | GAP_ADV_EVT_MASK_SET_TERMINATED | GAP_ADV_EVT_MASK_START);
status = GapAdv_setEventMask(advHandleLongRange, GAP_ADV_EVT_MASK_START_AFTER_ENABLE | GAP_ADV_EVT_MASK_END_AFTER_DISABLE | GAP_ADV_EVT_MASK_SET_TERMINATED | GAP_ADV_EVT_MASK_START);

And then in the event handler (which is handled in the thread, same as in the simple_peripheral example) when there is a GAP_EVT_ADV_START event I update the advertising data for the handle passed into the event handler:

ret = GapAdv_prepareLoadByHandle(handle, GAP_ADV_FREE_OPTION_DONT_FREE);
CHECK_RETURN_CODE(ret);
Advertising_Update(buffer, fixed_point_press, fixed_point_temp, battery);
ret = GapAdv_loadByHandle(handle, GAP_ADV_DATA_TYPE_ADV, buf_len, buffer);
CHECK_RETURN_CODE(ret);

(Advertising_Update() updates a few bytes in the buffer and 'buffer' points to the advertising data that matches the handle and advertising set).

This does not work properly and the second advertising set stops advertising. Sometimes it works for several advertisements and then the second advertising set stops and sometimes it stops after only one or two advertisements. The first advertisement set (legacy mode) seems to always keep advertising, but the long range (extended advertising with coded PHY) always stops after a while.

But if I remove the calls to GapAdv_prepareLoadByHandle() and GapAdv_loadByHandle() when updating the advertising data then it seems to work OK.

The manuals recommend using GapAdv_prepareLoadByHandle() and GapAdv_loadByHandle(), so what's going wrong here? It seems like GapAdv_prepareLoadByHandle() when called for one advertising set is affecting the other advertising set.

regards,

Charles

  • Hi Charles,

    Thank you for reaching out. We will look into your queries and get back to you as soon as possible. In the meantime, could you confirm if this happening on simple_peripheral or multirole? Are there any modifications present in the example or is this happening on an example with minimal modifications?

    Best Regards,

    Jan

  • Hi Jan,

    Thanks. My code is based on the simple_peripheral example but has several modifications. I will see if I can modify the base simple_peripheral example to update the advertising data regularly and make the problem happen.

    regards,

    Charles

  • Hi Jan,

    I can make the problem happen with a slightly modified version of simle_peripheral.

    I have take the simple_peripheral example and made the following changes:

    1. Changed the target microcontroller to CC2642R

    2. Declared the advertising parameters and data manually in simple_peripheral.c and set syscfg "Number of advertising sets" to 0.

    // Uncomment this to protect updating the advertising data with GapAdv_prepareLoadByHandle()
    //#define USE_PREPARE_LOAD_BY_HANDLE
    
    // Advertising data.
    // In the unmodified simple_peripheral examle this is generated by sysconfig and placed in t_ble_config.c
    
    GapAdv_params_t advParams1 = {
      .eventProps =   GAP_ADV_PROP_CONNECTABLE | GAP_ADV_PROP_LEGACY | GAP_ADV_PROP_SCANNABLE,
      .primIntMin =   3200,
      .primIntMax =   3200,
      .primChanMap =  GAP_ADV_CHAN_ALL,
      .peerAddrType = PEER_ADDRTYPE_PUBLIC_OR_PUBLIC_ID,
      .peerAddr =     { 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa },
      .filterPolicy = GAP_ADV_WL_POLICY_ANY_REQ,
      .txPower =      GAP_ADV_TX_POWER_NO_PREFERENCE,
      .primPhy =      GAP_ADV_PRIM_PHY_1_MBPS,
      .secPhy =       GAP_ADV_SEC_PHY_1_MBPS,
      .sid =          0
    };
    
    uint8_t advData1[] =
    {
      0x03,
      GAP_ADTYPE_LOCAL_NAME_SHORT,
      'S',
      'P',
    
      0x02,   // length of this data
      GAP_ADTYPE_FLAGS,
      GAP_ADTYPE_FLAGS_GENERAL | GAP_ADTYPE_FLAGS_BREDR_NOT_SUPPORTED,
    
      // Service UUID (0x18ff) for Advertising blob
      0x03,   // length of this data
      GAP_ADTYPE_16BIT_MORE,      // Incomplete list of 16-bit UUIDs
      0xff,
      0x18,   // Advertising blob
    
      // Advertising blob
      0x05,   // length of this data
      GAP_ADTYPE_SERVICE_DATA,
      0xff, 0x18, // Service UUID
      0x12,   // 2 bytes of data
      0x34
    
    };
    
    uint8_t scanResData1[] =
    {
      0x12,
      GAP_ADTYPE_LOCAL_NAME_COMPLETE,
      'S',
      'i',
      'm',
      'p',
      'l',
      'e',
      ' ',
      'P',
      'e',
      'r',
      'i',
      'p',
      'h',
      'e',
      'r',
      'a',
      'l',
    
      0x02,
      GAP_ADTYPE_POWER_LEVEL,
      0,
    
      0x05,
      GAP_ADTYPE_SLAVE_CONN_INTERVAL_RANGE,
      LO_UINT16(80),
      HI_UINT16(80),
      LO_UINT16(104),
      HI_UINT16(104),
    
    };
    
    
    GapAdv_params_t advParams2 = {
      .eventProps =   GAP_ADV_PROP_CONNECTABLE,
      .primIntMin =   3200,
      .primIntMax =   3200,
      .primChanMap =  GAP_ADV_CHAN_ALL,
      .peerAddrType = PEER_ADDRTYPE_PUBLIC_OR_PUBLIC_ID,
      .peerAddr =     { 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa },
      .filterPolicy = GAP_ADV_WL_POLICY_ANY_REQ,
      .txPower =      GAP_ADV_TX_POWER_NO_PREFERENCE,
      .primPhy =      GAP_ADV_PRIM_PHY_CODED_S8,
      .secPhy =       GAP_ADV_SEC_PHY_CODED_S8,
      .sid =          0
    };
    
    
    uint8_t advData2[] =
    {
      0x03,
      GAP_ADTYPE_LOCAL_NAME_SHORT,
      'S',
      'P',
    
      0x02,   // length of this data
      GAP_ADTYPE_FLAGS,
      GAP_ADTYPE_FLAGS_GENERAL | GAP_ADTYPE_FLAGS_BREDR_NOT_SUPPORTED,
    
      // Service UUID (0x18ff) for Advertising blob
      0x03,   // length of this data
      GAP_ADTYPE_16BIT_MORE,      // Incomplete list of 16-bit UUIDs
      0xff,
      0x18,   // Advertising blob
    
      // Advertising blob
      0x05,   // length of this data
      GAP_ADTYPE_SERVICE_DATA,
      0xff, 0x18, // Service UUID
      0x12,   // 2 bytes of data
      0x34
    };
    
    uint16_t adv_counter1 = 0;

    3. Set the advertising interval to 2s in both adParams1 and advParams2.

    4. Enable GAP_EVT_ADV_START events.

    5. Modified SimplePeripheral_processAdvEvent() as follows:

    static void SimplePeripheral_processAdvEvent(spGapAdvEventData_t *pEventData)
    {
      uint8_t handle = *(uint8_t *)(pEventData->pBuf);
      bStatus_t ret;
    
      switch (pEventData->event)
      {
        case GAP_EVT_ADV_START_AFTER_ENABLE:
          BLE_LOG_INT_TIME(0, BLE_LOG_MODULE_APP, "APP : ---- GAP_EVT_ADV_START_AFTER_ENABLE", 0);
          Display_printf(dispHandle, SP_ROW_ADVSTATE, 0, "Adv Set %d Enabled",
                         *(uint8_t *)(pEventData->pBuf));
          break;
    
        case GAP_EVT_ADV_END_AFTER_DISABLE:
          Display_printf(dispHandle, SP_ROW_ADVSTATE, 0, "Adv Set %d Disabled",
                         *(uint8_t *)(pEventData->pBuf));
          break;
    
        case GAP_EVT_ADV_START:
    #if USE_PREPARE_LOAD_BY_HANDLE
          // This does not work reliably
          if (handle)
          {
            ret = GapAdv_prepareLoadByHandle(handle, GAP_ADV_FREE_OPTION_DONT_FREE);
            SIMPLEPERIPHERAL_ASSERT(ret == SUCCESS);
            advData2[15] = (uint8_t)(adv_counter1 & 0xFF);
            advData2[16] = (uint8_t)((adv_counter1 >> 8) & 0xFF);
            ret = GapAdv_loadByHandle(handle, GAP_ADV_DATA_TYPE_ADV, sizeof(advData2), advData2);
            SIMPLEPERIPHERAL_ASSERT(ret == SUCCESS);
            Display_printf(dispHandle, SP_ROW_ADV_START2, 0, "%u: Adv %d", Clock_getTicks(), (int)handle);
          }
          else
          {
            adv_counter1++;
            ret = GapAdv_prepareLoadByHandle(handle, GAP_ADV_FREE_OPTION_DONT_FREE);
            SIMPLEPERIPHERAL_ASSERT(ret == SUCCESS);
            advData1[15] = (uint8_t)(adv_counter1 & 0xFF);
            advData1[16] = (uint8_t)((adv_counter1 >> 8) & 0xFF);
            ret = GapAdv_loadByHandle(handle, GAP_ADV_DATA_TYPE_ADV, sizeof(advData1), advData1);
            SIMPLEPERIPHERAL_ASSERT(ret == SUCCESS);
            Display_printf(dispHandle, SP_ROW_ADV_START1, 0, "%u: Adv %d", Clock_getTicks(), (int)handle);
          }
    #else
          // This seems to work fine, but is not what is recommended in the manual.
          if (handle)
          {
            advData2[15] = (uint8_t)(adv_counter1 & 0xFF);
            advData2[16] = (uint8_t)((adv_counter1 >> 8) & 0xFF);
            Display_printf(dispHandle, SP_ROW_ADV_START2, 0, "%u: Adv %d", Clock_getTicks(), (int)handle);
          }
          else
          {
            adv_counter1++;
            advData1[15] = (uint8_t)(adv_counter1 & 0xFF);
            advData1[16] = (uint8_t)((adv_counter1 >> 8) & 0xFF);
            Display_printf(dispHandle, SP_ROW_ADV_START1, 0, "%u: Adv %d", Clock_getTicks(), (int)handle);
          }
    #endif
          break;
    
        case GAP_EVT_ADV_END:
          break;
    
        case GAP_EVT_ADV_SET_TERMINATED:
        {
    #ifndef Display_DISABLE_ALL
          GapAdv_setTerm_t *advSetTerm = (GapAdv_setTerm_t *)(pEventData->pBuf);
    
          Display_printf(dispHandle, SP_ROW_ADVSTATE, 0, "Adv Set %d disabled after conn %d",
                         advSetTerm->handle, advSetTerm->connHandle );
    #endif
        }
        break;
    
        case GAP_EVT_SCAN_REQ_RECEIVED:
          break;
    
        case GAP_EVT_INSUFFICIENT_MEMORY:
          break;
    
        default:
          break;
      }
    
      // All events have associated memory to free except the insufficient memory
      // event
      if (pEventData->event != GAP_EVT_INSUFFICIENT_MEMORY)
      {
        ICall_free(pEventData->pBuf);
      }
    }

    When USE_PREPARE_LOAD_BY_HANDLE is defined the time for advertising set 1 (in the bottom line in the display) often does not update every 2s and usually eventually stops updating altogether.

    When USE_PREPARE_LOAD_BY_HANDLE  is NOT defined it works fine. But is it OK to update the advertising data like that? Will it sometimes send out half-changed data? In my application I have to update a larger number of bytes than in this example.

    Here is my complete version of simpleperipheral.c 3301.simple_peripheral.c

    regards,

    Charles

  • Hi Charles,

    Thank you for your information, Please have a look at the following Simple Link Academy lab, I encourage you to look at the task 2 which fit with your project
    Here is a link to the Simple Link Academy lab: https://dev.ti.com/tirex/content/simplelink_academy_cc13xx_cc26xxsdk_6_40_00_00/modules/ble5stack/ble_scan_adv_basic/ble_scan_adv_basic.html

    You can also have a look to others Simple Link Academy lab on : https://dev.ti.com/tirex/

    Thanks and regards,

  • Hi Guillaume,

    Thanks, but that Academy Lab example doesn't do exactly what I need. I need to regularly (every 2s) update the advertising data for both legacy and long range mode and I have different buffers for each advertising set (the legacy mode advertising uses the scan response to send the full device name and long range mode advertising uses extended advertising so can fit the full device name in the advertising data). That example looks like it will only update the advertising data once after advertising starts.

    So while I can see what the Academy Lab example is doing, I still don't know what my code is doing wrong.

    I think I will try the approach described in "Directly Manipulating a Buffer While Advertising is Enabled¶" in the "GAP Advertiser" section of the SimpleLink CC13XX/CC26XX SDK BLE5-Stack User's Guide 2.02.07.00 software-dl.ti.com/.../gap-cc13xx_cc26xx.html

    regards,

    Charles

  • Hi,

    Has anyone at TI been able to try my example and see why it doesn't work?

    regards,

    Charles

  • Hi Charles,

    I have tested your code, the part where you are directly updating the buffer containing the advertising data has not been tested by TI, it seems to work for the moment but we can't ensure you that this code is working in all the situations.

    I need more information concerning the part where you are using USE_PREPARE_LOAD_BY_HANDLE:

    - What are you using to see the changing of both advertisements ? Your phone, a bluetooth sniffer ?

    - The way you update the buffer is not very common, it could be more secure to use the GapAdv_loadByBuffer() function to do so

    - The GapAdv_prepareLoadByHandle() function enables and disables the adv each time you use it, it takes so many times and it could be a reason why this does not work reliably. Please prefer using the function mention on the previous point.

    regards,

    Guillaume

  • Hi Guillaume,

    I had assumed that GapAdv_loadByBuffer() and GapAdv_loadByHandle() did much the same thing just with a slightly different interface, but it sounds like I am wrong. I will try changing my code to use GapAdv_loadByBuffer().

    I am using the Nordic app to watch the advertisements and the code also prints out the time of the last advertising start event for each handle, so I can see when that time stops updating.

    regards,

    Charles

  • Hi Guillaume,

    I get the same behaviour when using GapAdv_loadByBuffer(). I get the GAP_EVT_ADV_START event a few times for the long range (coded phy) mode advertisement set and then it stops.
    Using a BLE sniffer (Wireshark) I see, for example, the following pattern for advertising messages from my dev board with PHYH=coded PHY, 1 advertising message then none for 18s, then one 4s later, 2s later, 8s, 2s, 2s, 2s, 2s, 16s, 10s, 6s. So it is not able to reliably advertise at the requested rate when the advertising data is being updated with GapAdv_loadByBuffer(). Eventually the sniffer stops seeing any advertising packets.

        case GAP_EVT_ADV_START:
          if (handle)
          {
            ret = GapAdv_prepareLoadByBuffer(advData2, FALSE);
            SIMPLEPERIPHERAL_ASSERT(ret == SUCCESS);
            advData2[15] = (uint8_t)(adv_counter1 & 0xFF);
            advData2[16] = (uint8_t)((adv_counter1 >> 8) & 0xFF);
            ret = GapAdv_loadByBuffer(sizeof(advData2), advData2);
            SIMPLEPERIPHERAL_ASSERT(ret == SUCCESS);
            longrange_adv_messages++;
          }
          else
          {
            adv_counter1++;
            ret = GapAdv_prepareLoadByBuffer(advData1, FALSE);
            SIMPLEPERIPHERAL_ASSERT(ret == SUCCESS);
            advData1[15] = (uint8_t)(adv_counter1 & 0xFF);
            advData1[16] = (uint8_t)((adv_counter1 >> 8) & 0xFF);
            ret = GapAdv_loadByBuffer(sizeof(advData1), advData1);
            SIMPLEPERIPHERAL_ASSERT(ret == SUCCESS);
            legacy_adv_messages++;
          }
          break;

    regards,

    Charles

  • Hi Charles,

    Ok so I'll suggest you to destroy the advertising set, update the advertising data and then recreate the sets.

    Let me know if it works.

    regards,

    Guillaume

  • Hi Guillaume,

    Do you mean every 2s when I need to update the advertising data I should destroy both advertising sets, recreate them and set the data? But that's not going to work very well with keeping a consistent advertisement interval is it? It doesn't seem very energy efficient and would not be practical for a much faster update rate.

    regards,

    Charles

  • Hi Charles,

    It will be no impact on energy but yes you're right for the rest of your content.

    Does your code implementation works well without the calls to GapAdv_prepareLoadByBuffer() and GapAdv_loadByBuffer() as you mention at the beginning ? Does the advertising update appears every 2s properly ?

    regards,

  • Hi Guillaume,

    Yes, I have it working properly with updating the buffer directly in the advertising callback when there is a GAP_EVT_ADV_END. I have spent too much time on this issue and can't really wait for TI to fix the bugs in GapAdv_prepareLoadByBuffer(), GapAdv_loadByBuffer(), GapAdv_loadByBuffer() and GapAdv_loadByHandle(), so I will probably stick with this.

    Have you been able to recreate the issue with GapAdv_prepareLoadByBuffer() and GapAdv_loadByBuffer() or GapAdv_loadByBuffer() and GapAdv_loadByHandle()? I can send you my complete modified SimplePeripheral example if you want - just let me know the best way to send the files.

    regards,

    Charles

  • Hi Charles,

    Yes I have recreate the issue, it comes directly from the GapAdv_prepareLoadByHandle() or GapAdv_prepareLoadByBuffer() functions which enables and disables the adv each time you use it, it takes so many times during the advertisement and that's why it doesn't work reliably. If your solution with updating the buffer directly in the advertising callback when there is a GAP_EVT_ADV_END works well, I suggest you to keep it like this.

    regards,

  • Hi Guillaume,

    OK, that's good to know that I am on the right track.

    Thanks for your help.

    regards,

    Charles