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

How to send notification for a large custom attribute

I am using nRF52 S132 and latest SDK V11. I defined a custom service with a custom characteristic which is 509 bytes (based on your tutorial). Read operation is fine from a client. But when using sd_ble_gatts_hvx() to send notification, only the first 20 bytes are received. The return code of this function is 0x3401, which means BLE_ERROR_GATTS_SYS_ATTR_MISSING. But it does not make sense to me. So I reduce the size from 509 to 10. The notification is sent and received. But the error code is still 0x3401. How to send notification properly?

Parents
  • Another approach which seems to work, is to queue up multiple notifications using a characteristic with short length. By updating the characteristic value between each call to sd_ble_gatts_hvx() it results in multiple notifications, each with the portion you want to send.

            uint16_t len = BLOCK_SIZE;
            ble_gatts_hvx_params_t hvx_params;
            hvx_params.handle = m_p_resp->handles.value_handle;
            hvx_params.type   = BLE_GATT_HVX_NOTIFICATION;
            hvx_params.offset = 0;
            hvx_params.p_data = NULL;
            hvx_params.p_len  = &len;
            // Notify full value as three individual piece notifications.
            for (int i = 0 ; i < (3 * BLOCK_SIZE) ; i += len)
            {
                static uint8_t fill = 0;
                memset(m_response, fill++, sizeof(m_response));
    
                uint32_t err_code = sd_ble_gatts_hvx(
                    p_ble_event->evt.gatts_evt.conn_handle,
                    &hvx_params
                    );
                APP_ERROR_CHECK(err_code);
            }
    

    Another way to do this would be to break the longer characteristic into multiple short characteristics and notify on each one.

Reply
  • Another approach which seems to work, is to queue up multiple notifications using a characteristic with short length. By updating the characteristic value between each call to sd_ble_gatts_hvx() it results in multiple notifications, each with the portion you want to send.

            uint16_t len = BLOCK_SIZE;
            ble_gatts_hvx_params_t hvx_params;
            hvx_params.handle = m_p_resp->handles.value_handle;
            hvx_params.type   = BLE_GATT_HVX_NOTIFICATION;
            hvx_params.offset = 0;
            hvx_params.p_data = NULL;
            hvx_params.p_len  = &len;
            // Notify full value as three individual piece notifications.
            for (int i = 0 ; i < (3 * BLOCK_SIZE) ; i += len)
            {
                static uint8_t fill = 0;
                memset(m_response, fill++, sizeof(m_response));
    
                uint32_t err_code = sd_ble_gatts_hvx(
                    p_ble_event->evt.gatts_evt.conn_handle,
                    &hvx_params
                    );
                APP_ERROR_CHECK(err_code);
            }
    

    Another way to do this would be to break the longer characteristic into multiple short characteristics and notify on each one.

Children
  • I can not follow your example: m_response is not declared and not link with the hvx_params; p_data is NULL?

  • Hi Christian,

    My snippet of code (above) doesn't show every detail--it is merely an illustration. In my example, BLOCK_SIZE was 16 and m_response is a static uint8_t array (of size no larger than the maximum MTU size). So this is one way to transfer 3 * 16 = 48 bytes through a smaller characteristic (up to 23 in size) by a series of notifies.

    The main things to notice are: 1) no matter how large a characteristic you have, a notify will only transfer the first portion. Typically, that notify would be followed by a long read by the peer to get the rest. However, in our situation we want to "push" the full value of the longer characteristic without the peer having to turn around and do a long read. So the technique is to take the longer value and copy it into the shorter characteristic variable in chunks and then notify each chunk. This requires the peer to also know the scheme.

  • To continue:

    The peer has to know that the notifying characteristic is being handled specially--to reassemble what appears to be repeated notifications of the first 23 bytes into the longer full value.

    Also, to handle the general case of longer values--you need to handle the situation where the call to sd_ble_gatts_hvx() returns indicating no more TX buffers remain. When that happens, you have to wait to send the remainder until you get a BLE_EVT_TX_COMPLETE indicating more TX buffers have become available.

  • Hi Tony Very informativ your addition comments. I was very irritated by the completely different meaning of the parameter offset and p_data for notification (only for updating the internal value, before sending always the first bytes) and reading (ask for a special part of a long value). Thanks Chris

  • Yes, it is definitely misleading how the offset doesn't work as one might expect in this situation. Would have been much simpler to implement if the offset would allow sending different parts of the value without having to copy each piece down into the lower portion (and then restore the full value in case there is a read by a peer afterwards).

    I wound up writing a function: "notify_long_chx()" which handles the general case. It gets called to send a long value as a series of shorter notifications and also handles running out of TX buffers--indicating so in the return value--so that it can be called again to finish on a subsequent BLE_EVT_TX_COMPLETE. Sorry I can't share the code since it was written for a client.

    Good luck - Tony

Related