This post is older than 2 years and might not be relevant anymore
More Info: Consider searching for newer posts

ble_advdata_encode and 128-bit UUIDS

SDK 15.3.0, code is based on examples\ble_central_and_peripheral\experimental\ble_app_att_mtu_throughput.

After getting the project to work with nRF Connect on the desktop and Android, we are now trying to get it to work with nRF Connect on iOS13+.

Per the Apple Accessory development guide, it is required/expected/recommended to send the UUID in the advertising packet.

For this project/product, it would be a unique 128-bit vendor-specific UUID.

1. This works:

static void advertising_data_set(void)
{
    ret_code_t ret;
    ble_uuid_t primary_uuid =
    {
      .type = BLE_UUID_TYPE_BLE,  // BLE_UUID_TYPE_VENDOR_BEGIN,
      .uuid = MY_SERVICE_UUID,
    };

    ble_gap_adv_params_t const adv_params =
    {
        .properties    =
        {
          .type = BLE_GAP_ADV_TYPE_CONNECTABLE_SCANNABLE_UNDIRECTED,
        },
        .p_peer_addr   = NULL,
        .filter_policy = BLE_GAP_ADV_FP_ANY,
        .interval      = ADV_INTERVAL,
        .duration      = 0,

        /* BLE5 includes the 2Mb/sec PHY, but most mobile devices won't have it.
         * Must be changed to connect in long range. (BLE_GAP_PHY_CODED) */
        .primary_phy   = BLE_GAP_PHY_1MBPS,
        .secondary_phy = BLE_GAP_PHY_1MBPS,
    };

    ble_advdata_t const adv_data =
    {
        .name_type          = BLE_ADVDATA_FULL_NAME,
        .flags              = BLE_GAP_ADV_FLAGS_LE_ONLY_GENERAL_DISC_MODE,
        .include_appearance = false,
        .uuids_more_available =
        {
          .uuid_cnt = 1,
          .p_uuids = &primary_uuid,
        },
    };

    ret = ble_advdata_encode(&adv_data, m_adv_data.adv_data.p_data,
                             &m_adv_data.adv_data.len);
    if(ret != NRF_SUCCESS)
    {
        NRF_LOG_ERROR("advertising_data_set: "
                      "ble_advdata_encode fails: %d (%d)", ret, dope);
        sys_err = SYSERR_BLE_ADVDATA_ENCODE_ADV_SET;
    }

    ret = sd_ble_gap_adv_set_configure(&m_adv_handle,
                                       &m_adv_data, &adv_params);
    if(ret != NRF_SUCCESS)
    {
        NRF_LOG_ERROR("advertising_data_set: "
                      "sd_ble_gap_adv_set_configure fails: %d", ret);
        sys_err = SYSERR_BLE_ADVDATA_CONFIG_ADV_SET;
    }
}

but it isn't what I want.  It advertises the 16-bit segment of the larger UUID (that is, "MY_SERVICE_UUID" is bytes 12 and 13 of "SERVICE_UUID_BASE").  It is my understanding that the use of BLE_UUID_TYPE_BLE is for assigned UUIDs; that is, "Bluetooth SIG UUID (16-bit)", not vendor-unique.

I think I want the BLE_UUID_TYPE_VENDOR_BEGIN type for the ble_uuid_t variable.  According to the SDK documentation, this is "Vendor UUID types start at this index (128-bit)".  This sounds closer to what I want.

The uuid element of the ble_uuid_t type is a uint16_t, "16-bit UUID value or octets 12-13 of 128-bit UUID".  Okay, I'm guessing that if the type is BLE_UUID_TYPE_BLE, then uuid is the 16-bit SIG-assigned UUID.  Not much uncertainty there.  This would then imply that for type BLE_UUID_TYPE_VENDOR_BEGIN, uuid is bytes 12-13 of a custom 128-bit UUID.

1. What does "start at this index" mean?

2. How do I supply the rest of the 128-bit UUID?  Does the SoftDevice obtain this from the service UUID that was already registered with sd_ble_uuid_vs_add()?  Or is uuid actually supposed to be an index into some array where the 128-bit UUID resides?

3. When type is set to BLE_UUID_TYPE_VENDOR_BEGIN, ble_advdata_encode() throws a 2, error is NRF_ERROR_INVALID_PARAM.  I've tracked this down to the call to sd_ble_uuid_encode() in ble_advdata.c.  Hard for me to believe that it's complaining about the value of type itself, when it's set to a legal value.  Or is it the value of uuid when type is set to BLE_UUID_TYPE_VENDOR_BEGIN (that is, uuid really is an index into some array somewhere, and bytes 12 and 13 form an illegal index)?

  • Or is uuid actually supposed to be an index into some array where the 128-bit UUID resides?

    Look at the type output value for sd_ble_uuid_vs_add().

    Note that "Full Name" and 128 Bit UUID probably won't fit into a 31 byte advertizing data.

  • Hello,

    I wouldn't recommend using the ble_app_att_mtu_throughput as a starting point for your application. The reason for this is that this example is a bit complex, and really intended only to test throughput. I suggest you look at how e.g. the ble_app_uart example includes the UUID in it's advertisement. 

    I know that it only takes BLE_UUID_NUS_SERVICE (0x0001) in advertising_init(), but as long as the service is initialized, there is some softdevice magic that includes the full 128 bit UUID. You can verify this by changing a byte in the UUID, and see that the advertisement changes as well.

    Best regards,

    Edvin

  • Thanks, this was the tip I needed.  I discovered I was setting up advertising _before_ setting up the custom service.  sd_ble_uuid_vs_add() wasn't being called to register the vendor-specific UUID until after the the call to ble_advdata_encode().  Once I swapped those, I am getting the 128-bit UUID displayed in nRF Connect.

    Along with a truncated Full Name.  If I read the spec right, advertising data is (as you say) 31 bytes max.  128-bit UUID plus 1 byte length plus 1 byte data type* is 18 bytes.  Flags is 1 byte data plus 1 byte length plus 1 byte data type, or 3 bytes.  Name field plus 1 byte length plus 1 byte data type* is n + 2.  Max n = 31 - 18 - 3 - 2 = 8.  My program is truncating the name to 8 bytes.

    This is interesting. The Apple guidelines say flags and UUID ("Services") are in the minimum recommended set of advertising data items.  The guidelines also say that only three "legacy" advertising PDU types are allowed, with the 31-byte limit on advertising data.  Therefore devices with vendor-specific UUIDs are limited to 8-byte names (or 5-byte names, if the also-recommended TX Power Level is included).  I think the lesson is to just leave out the UUID and let the iPhone get the services through discovery, like everyone else.

  • Yes, well, I used the throughput example as a baseline on the advice from another Nordic engineer in view of our concern about throughput.  There's actually not much left of the Central capability left in our Peripheral-only program.  I notice that in the UART example, advertising setup is done through the Advertising Module rather than the ble_advdata_encode() and sd_ble_gap_adv_set_configure() API.  This is also true of the DFU service (which is included in our custom bootloader) and is presumably Apple-friendly.  Is Nordic recommending the use of the Advertising Module over the previous SD calls?  (I'm sure the Advertising Module just wraps the SD calls in an easier-to-use framework.)

  • Sorry for the late reply David, Edvin became a new father and is on leave. I am responsible for this thread now.

    David Ormand said:
    Is Nordic recommending the use of the Advertising Module over the previous SD calls?  (I'm sure the Advertising Module just wraps the SD calls in an easier-to-use framework.)

    Depends on your experience with Nordic solutions and also depends on your requirement. Advertising Module is a library and is more heavy weight than using the SD calls directly. A new user would find advertising module easier to use as it handles most of the pre and post processing functionality. But if your application does need the pre and post processing of an advertising packet to be more specific, then you can use the SD calls directly.

Related