How to know if the Notification/Indication of a Characteristic is enabled?

I would like to know if the Notification/Indication of a Characteristic is enabled. I suppose this data is stored in a CCCD on the stack. I have not found any SVC instruction to read this info from the BLE stack.

Where can I access this info?

Thanks for your help.

  • There are basically 3 ways you can handle this:

    1. Don't care, just try to send a notification/indication and check the error code you get back from sd_ble_gatts_hvx(). If it is NRF_ERROR_INVALID_STATE, notifications/indications are not enabled.
    2. Make a handler for the BLE_GATTS_EVT_WRITE. When the CCCD is written, you'll receive such event, with the handle set to the CCCD handle and the value either 0x0001 (Notification) or 0x0002 (Indication). Beware that you will not receive such event when reconnecting to a previously bonded Central, since CCCD values are retained over the lifetime of a bond.
    3. Read the value of the CCCD using sd_ble_gatts_value_get(), and check whether it is 0x0001 (Notification) or 0x0002 (Indication).

    Of these, I'd recommend using method 1 or method 2. If you only need to send a notification once in a while, it might be ok to just ignore the error with method 1, but you should most definitely not busy-loop on this error. If you need to transfer lots of data, you should be certain that the CCCD has been enabled before starting the transfer.

    Edit: Clarify recommendations, and when they don't apply.

  • Hi Ole,

    Thanks for your quick reply. I am looking at this issue from the peripheral side. I still have few questions about the 3rd way taht you commented above. As an example, I will use part of the code from the battery service. This part is when we are add the Battery Level Characteristic:

    
    // Add Battery Level characteristic
        if (p_bas->is_notification_supported)
        {
            memset(&cccd_md, 0, sizeof(cccd_md));
        
            // According to BAS_SPEC_V10, the read operation on cccd should be possible without
            // authentication.
            BLE_GAP_CONN_SEC_MODE_SET_OPEN(&cccd_md.read_perm);
            cccd_md.write_perm = p_bas_init->battery_level_char_attr_md.cccd_write_perm;
            cccd_md.vloc = BLE_GATTS_VLOC_STACK;
        }
        
        memset(&char_md, 0, sizeof(char_md));
        
        char_md.char_props.read   = 1;
        char_md.char_props.notify = (p_bas->is_notification_supported) ? 1 : 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         = (p_bas->is_notification_supported) ? &cccd_md : NULL;
        char_md.p_sccd_md         = NULL;
        
        BLE_UUID_BLE_ASSIGN(ble_uuid, BLE_UUID_BATTERY_LEVEL_CHAR);
        
        memset(&attr_md, 0, sizeof(attr_md));
    
        attr_md.read_perm  = p_bas_init->battery_level_char_attr_md.read_perm;
        attr_md.write_perm = p_bas_init->battery_level_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;
        
        initial_battery_level = p_bas_init->initial_batt_level;
        
        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);
        attr_char_value.p_value      = &initial_battery_level;
        
        err_code = sd_ble_gatts_characteristic_add(p_bas->service_handle, &char_md,
                                                   &attr_char_value,
                                                   &p_bas->battery_level_handles);
    

    From my understanding in the we are flagging the client that notification is supported (char_md.char_props.notify = (p_bas->is_notification_supported) ? 1 : 0; ). The characteristic can either support notification or indication. Is that right?

    Also, I would like to know if both this information(notification supported flag) and the notification enabled flag are stored in the CCCD attribute. If this is the case, what is the bit order of the CCCD? Where is this info explained?

    Finally, is the CCCD handle normally next to the attribute value. For example if the Battery Level attribute value handle is 0x000A we should be expecting the CCCD have the 0x000B handle. Is this right?

  • A GATT Server has an attribute table, and a characteristic is one type of attribute, others being service declarations, descriptors or actual characteristic values. An attribute have a set of properties, which defines what kind of operations can be done with this attribute. Notifications/indication are two of those operations.

    The existence of one of those properties also promises the existence of a CCCD, which is used by the GATT Client to control whether it currently receives Notifications/Indications or not.

    I believe that the spec doesn't make a promise the CCCD will be next to the value attribute, but in practice it often is.

    For further details, I'd recommend you to read chapter 2.2 of nAN-36, if you haven't already done so: https://www.nordicsemi.com/eng/content/download/34055/573345/file/nAN-36.zip For even more details, you should refer to the Core Specification itself, Volume 3, Part G and the underlying Part F.

    A characteristic can actually support both at once.

Related