This post is older than 2 years and might not be relevant anymore
More Info: Consider searching for newer posts
This discussion has been locked.
You can no longer post new replies to this discussion. If you have a question you can start a new discussion

Creating a custom service, ble_add_char_params_t/characteristic_add vs sd_ble_gatts_characteristic_add

Hello,

I am following a tutorial about writing a custom ble service on the NordicPlayground Github:

https://github.com/NordicPlayground/nRF5x-custom-ble-service-tutorial

On Step 3 - Initializing the Service and advertising our 128-bit UUID, it mentions to add the 128bit UUID to the advertisement packet, from this line (1), to line (2). If I do this, part of the device name gets cut off when using nRF Connect. However, if I leave it the same (1), or if I keep BLE_UUID_TYPE_BLE, and change the BLE_UUID_DEVICE_INFORMATION_SERVICE to CUSTOM_SERVICE_UUID, I can see the full device name, as seen in the pictures. Is there a reason for this? or can I keep it default (1) or change it to (3).

(1) static ble_uuid_t m_adv_uuids[] = {{BLE_UUID_DEVICE_INFORMATION_SERVICE, BLE_UUID_TYPE_BLE}}; /**< Universally unique service identifiers. */

(2) static ble_uuid_t m_adv_uuids[] = {{CUSTOM_SERVICE_UUID, BLE_UUID_TYPE_VENDOR_BEGIN }}; /**< Universally unique service identifiers. */

(3) static ble_uuid_t m_adv_uuids[] = {CUSTOM_SERVICE_UUID, BLE_UUID_TYPE_BLE}}; /**< Universally unique service identifiers. */

On Step 4 - Adding a Custom Value Characteristic to the Custom Service, I've followed another tutorial to add custom services and I don't know what the difference is. In the NordicPlayground tutorial, the final code of adding characteristics to the custom service is shown below. In this multiple data types are used to set the characteristics, as well as it uses sd_ble_gatts_characteristic_add.

/* This code belongs in ble_cus.c*/

static uint32_t custom_value_char_add(ble_cus_t * p_cus, const ble_cus_init_t * p_cus_init)
{
    uint32_t            err_code;
    ble_gatts_char_md_t char_md;
    ble_gatts_attr_md_t cccd_md;
    ble_gatts_attr_t    attr_char_value;
    ble_uuid_t          ble_uuid;
    ble_gatts_attr_md_t attr_md;

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

    char_md.char_props.read   = 1;
    char_md.char_props.write  = 1;
    char_md.char_props.notify = 0; 
    char_md.p_char_user_desc  = NULL;
    char_md.p_char_pf         = NULL;
    char_md.p_user_desc_md    = NULL;
    char_md.p_cccd_md         = NULL; 
    char_md.p_sccd_md         = NULL;
		
    memset(&attr_md, 0, sizeof(attr_md));

    attr_md.read_perm  = p_cus_init->custom_value_char_attr_md.read_perm;
    attr_md.write_perm = p_cus_init->custom_value_char_attr_md.write_perm;
    attr_md.vloc       = BLE_GATTS_VLOC_STACK;
    attr_md.rd_auth    = 0;
    attr_md.wr_auth    = 0;
    attr_md.vlen       = 0;

    ble_uuid.type = p_cus->uuid_type;
    ble_uuid.uuid = CUSTOM_VALUE_CHAR_UUID;

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

    attr_char_value.p_uuid    = &ble_uuid;
    attr_char_value.p_attr_md = &attr_md;
    attr_char_value.init_len  = sizeof(uint8_t);
    attr_char_value.init_offs = 0;
    attr_char_value.max_len   = sizeof(uint8_t);

    err_code = sd_ble_gatts_characteristic_add(p_cus->service_handle, &char_md,
                                               &attr_char_value,
                                               &p_cus->custom_value_handles);
    if (err_code != NRF_SUCCESS)
    {
        return err_code;
    }

    return NRF_SUCCESS;
}

I have seen in another tutorial that it has been written like this (in this case it is to initialise the buttons). This example ble_add_char_params_t is used, and instead of sd_ble_gatts_characteristic_add, characteristic_add is used.

static uint32_t custom_value_char_add(ble_cus_t * p_cus, const ble_cus_init_t * p_cus_init)
{
  uint32_t                  err_code;
  ble_uuid_t                ble_uuid;
  ble_add_char_params_t     add_char_params;

  /* Adding the service  characteristics */

    // Add the buttons characteristics
    uint8_t buttons_char_init_value [4] = {0};

    memset(&add_char_params, 0, sizeof(add_char_params));
    add_char_params.uuid                = BUTTONS_STATES_CHAR_UUID;
    add_char_params.uuid_type           = p_cus->uuid_type;

    add_char_params.init_len            = 4;
    add_char_params.max_len             = 4;
    add_char_params.p_init_value        = buttons_char_init_value;

    add_char_params.char_props.read     = 1; // (in bytes)
    add_char_params.char_props.notify   = 1;

    add_char_params.read_access         = SEC_OPEN;
    add_char_params.cccd_write_access   = SEC_OPEN;
    
    err_code = characteristic_add(p_cus->service_handle, 
                                  &add_char_params, 
                                  &p_cus->buttons_states_char_handles);

  if (err_code != NRF_SUCCESS)
    {
      return err_code;
    }

    return  NRF_SUCCESS;
}

It seems like the second code uses ble_add_char_params_t and characteristic_add data types. Where in the first example it is using multiple data types to set the characteristics, and then using the function sd_ble_gatts_characteristic_add. Can I get further clarification on what method of creating the custom characteristics to use? The tutorial from Nordic is quite a few years old, so I'm not sure if new API's has been introduced, in addition the tutorial is using nRF sdk 15.

Kind regards,

JSC

  • Not sure if I have any short answer for you here. I recommend to also look at the existing examples in the nRF5 SDK in parallell, for instance the ble_app_blinky and ble_app_uart. Both show proprietary data transfer over BLE, while many of the others BLE examples are based on BLE profiles.

    Typically BLE profiles are using 16-bit UUID's, specified by BT sig, and you need to follow the BLE profile in terms of implementation and use. Only BLE profiles defined by BT sig can use 16-bit UUID's.

    While proprietarty BLE applications are using 128-bit UUID's. To create a 128-bit UUID you need to use 3 softdevice commands:

    1.  During softdevice init you need to call sd_ble_cfg_set(BLE_COMMON_CFG_VS_UUID...) to set the number of base UUID's you need in your product (e.g. how many services and characteristics you need in your application that are proprietary BLE use).

    2. For each 128-bit UUID you want to use you may need to call sd_ble_uuid_vs_add() to create a base UUID that contain 128-bit UUID you later want to use. Note that bytes 12 and 13 of the provided UUID will not be used internally, since those are always replaced by the command in step 3.

    3. To create a service or characteristic you can call sd_ble_gatts_service_add() or sd_ble_gatts_characteristic_add(). The final 128-bit UUID will then be the provide 16-bit UUID in sd_ble_gatts_service_add()/sd_ble_gatts_characteristic_add() + the one provided by sd_ble_uuid_vs_add() that you provided in step 2. However to make sure you combine the correct 16-bit UUID + 128-bit base UUID, it is important that you buffer the address type received in step 2, and use this address type value when calling sd_ble_gatts_service_add()/sd_ble_gatts_characteristic_add().

    Kenneth

Related