How can you add one CCC descriptor for every notification characteristic in a service?

Hello,

I am implementing the Physical Activity Monitor Service which looks like this:


/* Physical Activity Monitor Service Declaration */
BT_GATT_SERVICE_DEFINE(pam_svc,
 BT_GATT_PRIMARY_SERVICE(BT_UUID_PAMS),
 BT_GATT_CHARACTERISTIC(BT_UUID_GATT_PHY_AMF, BT_GATT_CHRC_READ,
 BT_GATT_PERM_READ, read_amf, NULL, NULL),
 BT_GATT_CHARACTERISTIC(BT_UUID_GATT_GEN_AID, BT_GATT_CHRC_NOTIFY,
 BT_GATT_PERM_NONE, NULL, NULL, NULL), 
 BT_GATT_CCC(pamc_ccc_cfg_changed, BT_GATT_PERM_WRITE | BT_GATT_PERM_READ),
 BT_GATT_CHARACTERISTIC(BT_UUID_GATT_GEN_ASD, BT_GATT_CHRC_INDICATE,
 BT_GATT_PERM_NONE, NULL, NULL, NULL), //TODO: Indication callback
 BT_GATT_CCC(pamc_ccc_cfg_changed, BT_GATT_PERM_WRITE | BT_GATT_PERM_READ),
 BT_GATT_CHARACTERISTIC(BT_UUID_GATT_CR_AID, BT_GATT_CHRC_NOTIFY,
 BT_GATT_PERM_NONE, NULL, NULL, NULL), 
 BT_GATT_CCC(pamc_ccc_cfg_changed, BT_GATT_PERM_WRITE | BT_GATT_PERM_READ),
 BT_GATT_CHARACTERISTIC(BT_UUID_GATT_CR_ASD, BT_GATT_CHRC_INDICATE,
 BT_GATT_PERM_NONE, NULL, NULL, NULL), //TODO: Indication callback
 BT_GATT_CCC(pamc_ccc_cfg_changed, BT_GATT_PERM_WRITE | BT_GATT_PERM_READ),
 BT_GATT_CHARACTERISTIC(BT_UUID_GATT_SLP_AID, BT_GATT_CHRC_NOTIFY,
 BT_GATT_PERM_NONE, NULL, NULL, NULL), 
 BT_GATT_CCC(pamc_ccc_cfg_changed, BT_GATT_PERM_WRITE | BT_GATT_PERM_READ),
 BT_GATT_CHARACTERISTIC(BT_UUID_GATT_SC_ASD, BT_GATT_CHRC_INDICATE,
 BT_GATT_PERM_NONE, NULL, NULL, NULL), //TODO: Indication callback
 BT_GATT_CCC(pamc_ccc_cfg_changed, BT_GATT_PERM_WRITE | BT_GATT_PERM_READ),
 BT_GATT_CHARACTERISTIC(BT_UUID_GATT_SLP_ASD, BT_GATT_CHRC_INDICATE,
 BT_GATT_PERM_NONE, NULL, NULL, NULL), //TODO: Indication callback
 BT_GATT_CCC(pamc_ccc_cfg_changed, BT_GATT_PERM_WRITE | BT_GATT_PERM_READ),
 BT_GATT_CHARACTERISTIC(BT_UUID_GATT_PHY_AMCP, BT_GATT_CHRC_WRITE | BT_GATT_CHRC_INDICATE,
 PAMS_GATT_PERM_DEFAULT & GATT_PERM_WRITE_MASK,
 NULL, ctrl_point_write, NULL), //TODO: Test control point write callback
 BT_GATT_CCC(pamc_ccc_cfg_changed, BT_GATT_PERM_WRITE | BT_GATT_PERM_READ),
 BT_GATT_CHARACTERISTIC(BT_UUID_GATT_ACS, BT_GATT_CHRC_INDICATE | BT_GATT_CHRC_READ,
 BT_GATT_PERM_NONE, read_acs, NULL, NULL), //TODO: Indication callback
 BT_GATT_CCC(pamc_ccc_cfg_changed, BT_GATT_PERM_WRITE | BT_GATT_PERM_READ),
 BT_GATT_CHARACTERISTIC(BT_UUID_GATT_PHY_ASDESC, BT_GATT_CHRC_INDICATE,
 BT_GATT_PERM_NONE, NULL, NULL, NULL), //TODO: Indication callback
 BT_GATT_CCC(pamc_ccc_cfg_changed, BT_GATT_PERM_WRITE | BT_GATT_PERM_READ),
}

Every notify or indicate characteristic needs a CCC according to The PAMS spec, but when I try to do this, I need to match every characteristic to its associated callback. Right now, using the HRS example, it simply iterates over every registered callback with a the notify boolean:

SYS_SLIST_FOR_EACH_CONTAINER(&hrs_cbs, listener, _node) {
    if (listener->ctrl_point_write) {
            err = listener->ctrl_point_write(*((uint8_t *)buf));
        /* If we get an error other than ENOTSUP then immediately
        * break the loop and return a generic gatt error, assuming this
        * listener supports this request code, but failed to serve it
        */
        if ((err != 0) && (err != -ENOTSUP)) {
            return BT_GATT_ERR(BT_ATT_ERR_UNLIKELY);
        }
    }
}

I am now trying to match the GATT characteristic with the correct `_node` in order to turn on notifications on a per-characteristic basis as the spec says. 

However, the attribute passed into the ccc function is just the UUID of the CCC function. How can I know which function (below) to pass it to?

static void pams_gen_aid_ntf_changed(bool enabled)
{
    pams_gen_aid_ntf_en = enabled;
    
    LOG_INF("PAMS GEN AID notification status changed: %s\n",
    enabled ? "enabled" : "disabled");
}

static struct bt_pams_cb pams_gen_aid_cb = {
    .ntf_changed = pams_gen_aid_ntf_changed,
};

static void pams_cr_aid_ntf_changed(bool enabled)
{
    pams_cr_aid_ntf_en = enabled;

    LOG_INF("PAMS CR AID notification status changed: %s\n",
    enabled ? "enabled" : "disabled");
}

static struct bt_pams_cb pams_cf_aid_cb = {
    .ntf_changed = pams_cr_aid_ntf_changed,
};

static void pams_slp_aid_ntf_changed(bool enabled)
{
    pams_slp_aid_ntf_en = enabled;

    LOG_INF("PAMS SLEEP AID notification status changed: %s\n",
    enabled ? "enabled" : "disabled");
}

static struct bt_pams_cb pams_slp_aid_cb = {
    .ntf_changed = pams_slp_aid_ntf_changed,
};

static void pams_ctrl_point_ntf_changed(bool enabled)
{
    pams_ctrl_point_ntf_en = enabled;

    LOG_INF("PAMS CTRL Point notification status changed: %s\n",
    enabled ? "enabled" : "disabled");
}

Do I have to make a separate CCC_changed function for every single characteristic?

Is there a way I can return a node of the desired callback and the attribute (uuid for example) of the clicked "notify" in the nrfConnect app? I am a bit stuck as a lot of the macros have less than ideal documentation. 

Thank you.

Parents
  • Hi,

    Right, so your callback function pamc_ccc_cfg_changed takes as the first argument a pointer to a bt_gatt_attr structure on which you can use bt_gatt_attr_get_handle() to get the handle. But that is perhaps not what you are looking for?

    I must admit I get a bit confused when you refer to ctrl_point_write() from HRS, as from what I can tell that one is used for writing to the characteristic, and so it uses the write callback, while what you are looking to do is implement support for indications, which uses the cfg_changed callback.

    Maybe a look at Bluetooth Low Energy Fundamentals, Lesson 4, Exercise 2, which gives a walk-through of adding notification and indication support, provides enough pointers to callbacks and structures that it will clear things up.

    Regards,
    Terje

Reply
  • Hi,

    Right, so your callback function pamc_ccc_cfg_changed takes as the first argument a pointer to a bt_gatt_attr structure on which you can use bt_gatt_attr_get_handle() to get the handle. But that is perhaps not what you are looking for?

    I must admit I get a bit confused when you refer to ctrl_point_write() from HRS, as from what I can tell that one is used for writing to the characteristic, and so it uses the write callback, while what you are looking to do is implement support for indications, which uses the cfg_changed callback.

    Maybe a look at Bluetooth Low Energy Fundamentals, Lesson 4, Exercise 2, which gives a walk-through of adding notification and indication support, provides enough pointers to callbacks and structures that it will clear things up.

    Regards,
    Terje

Children
No Data
Related