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

Use of ble_cus_custom_value_update

Hi,

I followed the tutorial in the Github and create a custom service. (Github:https://github.com/NordicPlayground/nRF52-Bluetooth-Course)

1. I use the ble_cus_custom_value_update in the main function like this. I get the data from sensor. First I transmit the float data to uint_8 type and sent it to the function. But I found that there is no delay in the transmission. Why does it happen?

 

         m_custom_value = 0x00;
         ble_cus_custom_value_update(&m_cus, m_custom_value);
         nrf_delay_ms(10);
         uint8_t  AccX[4];//store 4 bytes of a float data
         bds_float_encode(&acceleration_g[0],&AccX);
         m_custom_value=AccX[0];
         ble_cus_custom_value_update(&m_cus, m_custom_value);
         nrf_delay_ms(10);
         m_custom_value=AccX[1];
         ble_cus_custom_value_update(&m_cus, m_custom_value);
         nrf_delay_ms(10);
         m_custom_value=AccX[2];
         ble_cus_custom_value_update(&m_cus, m_custom_value);
         nrf_delay_ms(10);
         m_custom_value=AccX[3];
         ble_cus_custom_value_update(&m_cus, m_custom_value);
         nrf_delay_ms(100);

2. The above code can work despite the latency. But when I add more data transmission it will have some problems. I want to send six groups of data and every group has four bytes. One group can work successfully. But when adding more groups, some bytes will disappear. They cannot be recieved. What is the reason?

Parents
  • Hello,

    In a BLE connection there is something called a connection interval. This is the interval that determines how often the devices should talk to one another. Everything you write to your characteristic in between the connection intervals will be sent rapidly when the interval expires. Based on the log from your phone, it looks like your connection interval is about 500ms. So if you delay 10ms, you will still have time to queue 50 notifications. 

    The reason you don't see 50 notifications in each connection interval is that the softdevice doesn't have the memory to do so.

    I assume that the ble_cus_custom_value_update() calls sd_ble_gatts_hvx() in the end. You should check the return value of this. If it returns 0, it means that the notification is successfully queued, and will be sent. If it returns something else, it was not queued, and will not be sent. Depending on the return value this may have different reasons. Please check the description of sd_ble_gatts_hvx() in ble_gatts.h:

    /**@brief Notify or Indicate an attribute value.
     *
     * @details This function checks for the relevant Client Characteristic Configuration descriptor value to verify that the relevant operation
     *          (notification or indication) has been enabled by the client. It is also able to update the attribute value before issuing the PDU, so that
     *          the application can atomically perform a value update and a server initiated transaction with a single API call.
     *
     * @note    The local attribute value may be updated even if an outgoing packet is not sent to the peer due to an error during execution.
     *          The Attribute Table has been updated if one of the following error codes is returned: @ref NRF_ERROR_INVALID_STATE, @ref NRF_ERROR_BUSY,
     *          @ref NRF_ERROR_FORBIDDEN, @ref BLE_ERROR_GATTS_SYS_ATTR_MISSING and @ref NRF_ERROR_RESOURCES.
     *          The caller can check whether the value has been updated by looking at the contents of *(@ref ble_gatts_hvx_params_t::p_len).
     *
     * @note    Only one indication procedure can be ongoing per connection at a time.
     *          If the application tries to indicate an attribute value while another indication procedure is ongoing,
     *          the function call will return @ref NRF_ERROR_BUSY.
     *          A @ref BLE_GATTS_EVT_HVC event will be issued as soon as the confirmation arrives from the peer.
     *
     * @note    The number of Handle Value Notifications that can be queued is configured by @ref ble_gatts_conn_cfg_t::hvn_tx_queue_size
     *          When the queue is full, the function call will return @ref NRF_ERROR_RESOURCES.
     *          A @ref BLE_GATTS_EVT_HVN_TX_COMPLETE event will be issued as soon as the transmission of the notification is complete.
     *
     * @note    The application can keep track of the available queue element count for notifications by following the procedure below:
     *          - Store initial queue element count in a variable.
     *          - Decrement the variable, which stores the currently available queue element count, by one when a call to this function returns @ref NRF_SUCCESS.
     *          - Increment the variable, which stores the current available queue element count, by the count variable in @ref BLE_GATTS_EVT_HVN_TX_COMPLETE event.
     *
     * @events
     * @event{@ref BLE_GATTS_EVT_HVN_TX_COMPLETE, Notification transmission complete.}
     * @event{@ref BLE_GATTS_EVT_HVC, Confirmation received from the peer.}
     * @endevents
     *
     * @mscs
     * @mmsc{@ref BLE_GATTS_HVX_SYS_ATTRS_MISSING_MSC}
     * @mmsc{@ref BLE_GATTS_HVN_MSC}
     * @mmsc{@ref BLE_GATTS_HVI_MSC}
     * @mmsc{@ref BLE_GATTS_HVX_DISABLED_MSC}
     * @endmscs
     *
     * @param[in] conn_handle      Connection handle.
     * @param[in,out] p_hvx_params Pointer to an HVx parameters structure. If @ref ble_gatts_hvx_params_t::p_data
     *                             contains a non-NULL pointer the attribute value will be updated with the contents
     *                             pointed by it before sending the notification or indication. If the attribute value
     *                             is updated, @ref ble_gatts_hvx_params_t::p_len is updated by the SoftDevice to
     *                             contain the number of actual bytes written, else it will be set to 0.
     *
     * @retval ::NRF_SUCCESS Successfully queued a notification or indication for transmission, and optionally updated the attribute value.
     * @retval ::BLE_ERROR_INVALID_CONN_HANDLE Invalid Connection Handle.
     * @retval ::NRF_ERROR_INVALID_STATE One or more of the following is true:
     *                                   - Invalid Connection State
     *                                   - Notifications and/or indications not enabled in the CCCD
     *                                   - An ATT_MTU exchange is ongoing
     * @retval ::NRF_ERROR_INVALID_ADDR Invalid pointer supplied.
     * @retval ::NRF_ERROR_INVALID_PARAM Invalid parameter(s) supplied.
     * @retval ::BLE_ERROR_INVALID_ATTR_HANDLE Invalid attribute handle(s) supplied. Only attributes added directly by the application are available to notify and indicate.
     * @retval ::BLE_ERROR_GATTS_INVALID_ATTR_TYPE Invalid attribute type(s) supplied, only characteristic values may be notified and indicated.
     * @retval ::NRF_ERROR_NOT_FOUND Attribute not found.
     * @retval ::NRF_ERROR_FORBIDDEN The connection's current security level is lower than the one required by the write permissions of the CCCD associated with this characteristic.
     * @retval ::NRF_ERROR_DATA_SIZE Invalid data size(s) supplied.
     * @retval ::NRF_ERROR_BUSY For @ref BLE_GATT_HVX_INDICATION Procedure already in progress. Wait for a @ref BLE_GATTS_EVT_HVC event and retry.
     * @retval ::BLE_ERROR_GATTS_SYS_ATTR_MISSING System attributes missing, use @ref sd_ble_gatts_sys_attr_set to set them to a known value.
     * @retval ::NRF_ERROR_RESOURCES Too many notifications queued.
     *                               Wait for a @ref BLE_GATTS_EVT_HVN_TX_COMPLETE event and retry.
     * @retval ::NRF_ERROR_TIMEOUT There has been a GATT procedure timeout. No new GATT procedure can be performed without reestablishing the connection.
     */
    SVCALL(SD_BLE_GATTS_HVX, uint32_t, sd_ble_gatts_hvx(uint16_t conn_handle, ble_gatts_hvx_params_t const *p_hvx_params));

    If it returns NRF_ERROR_RESOURCES, your queue is full. If it returns something else, you need to check what that value means. The common error codes are listed in nrf_error.h.

    As for your second question. This is probably related to the above. Check what sd_ble_gatts_hvx returns. It is a good idea to forward this return value in the call ble_cus_custom_value_update(), so that it returns this value. Then you can check whether your packet is queued or not.

    Another hint is that you should not send only one byte at the time. It is not very efficient. Try to write a string of bytes. This will drastically reduce the amount of headers you will need, and you can send a lot more payload.

    BR,
    Edvin

  • Hi Edvin,

    I found that it returns NRF_ERROR_RESOURCES. I think it should be the main problem and how to solve it? I put the code in a while loop because I want the programme always running. Is it the reason?

    About sending a string, should I use the UART? I didn't find a function to send a string in the turtorial.

    Thanks,

    Jiahe

  • NRF_ERROR_RESOURCES means that the TX buffer is full, wait for a BLE_GATTS_EVT_HVN_TX_COMPLETE event from the SoftDevice before you queue any more data transfers.

    See also the ble_app_uart/ UART/Serial Port Emulation over BLE example. 

Reply Children
  • Hi,

    I didn't use the UART example. I use the template and add a custom service. This is the main function of my code.Can you take a look? I add a check in line 78 and the LED will always toggle. What's the reason?

     

    int main(void)
    {
        bool erase_bonds;
        int i, j;
    
        // Initialize.
        log_init();
        timers_init();
        buttons_leds_init(&erase_bonds);
        power_management_init();
        ble_stack_init();
        gap_params_init();
        gatt_init();
        services_init();
        advertising_init();
        conn_params_init();
        peer_manager_init();
    
        // Start execution.
        NRF_LOG_INFO("Transmission example started.");
        advertising_start(erase_bonds);
    
        twi_init();
        dev_ctx.write_reg = platform_write;
        dev_ctx.read_reg  = platform_read;
        dev_ctx.handle    = (void*)&m_twi;
        lsm6dso_device_id_get(&dev_ctx, &whoamI);
        if(whoamI != LSM6DSO_ID)
            while(1);
        lsm6dso_reset_set(&dev_ctx, PROPERTY_ENABLE);
       // do{
            lsm6dso_reset_get(&dev_ctx, &rst);
       // } while (rst);
        lsm6dso_i3c_disable_set(&dev_ctx, LSM6DSO_I3C_DISABLE);
        lsm6dso_block_data_update_set(&dev_ctx, PROPERTY_ENABLE);
    
        lsm6dso_xl_data_rate_set(&dev_ctx, LSM6DSO_XL_ODR_208Hz);
        lsm6dso_gy_data_rate_set(&dev_ctx, LSM6DSO_GY_ODR_208Hz);
        
        lsm6dso_xl_full_scale_set(&dev_ctx, LSM6DSO_2g);
        lsm6dso_gy_full_scale_set(&dev_ctx, LSM6DSO_250dps);
    
        lsm6dso_xl_hp_path_on_out_set(&dev_ctx, LSM6DSO_LP_ODR_DIV_100);
        lsm6dso_xl_filter_lp2_set(&dev_ctx, PROPERTY_ENABLE);
    
        while (true){
            uint8_t reg;
            lsm6dso_xl_flag_data_ready_get(&dev_ctx, &reg);
            if(reg){
                memset(data_raw_acceleration.u8bit, 0x00, 3 * sizeof(int16_t));
                lsm6dso_acceleration_raw_get(&dev_ctx, data_raw_acceleration.u8bit);
                acceleration_mg[0] = lsm6dso_from_fs2_to_mg(data_raw_acceleration.i16bit[0]);
                acceleration_mg[1] = lsm6dso_from_fs2_to_mg(data_raw_acceleration.i16bit[1]);
                acceleration_mg[2] = lsm6dso_from_fs2_to_mg(data_raw_acceleration.i16bit[2]);
                acceleration_g[0] = acceleration_mg[0]/1000;//AccX
                acceleration_g[1] = acceleration_mg[1]/1000;//AccY
                acceleration_g[2] = acceleration_mg[2]/1000;//AccZ
            }
    
            lsm6dso_gy_flag_data_ready_get(&dev_ctx, &reg);
            if(reg){
                memset(data_raw_angular_rate.u8bit, 0x00, 3 * sizeof(int16_t));
                lsm6dso_angular_rate_raw_get(&dev_ctx, data_raw_angular_rate.u8bit);
                angular_rate_mdps[0] = lsm6dso_from_fs250_to_mdps(data_raw_angular_rate.i16bit[0]);
                angular_rate_mdps[1] = lsm6dso_from_fs250_to_mdps(data_raw_angular_rate.i16bit[1]);
                angular_rate_mdps[2] = lsm6dso_from_fs250_to_mdps(data_raw_angular_rate.i16bit[2]);
                angular_rate_dps[0] = angular_rate_mdps[0]/1000;//GyrX
                angular_rate_dps[1] = angular_rate_mdps[1]/1000;//GyrY
                angular_rate_dps[2] = angular_rate_mdps[2]/1000;//GyrZ
            }
    
            uint8_t  AccX[4];//store 4 bytes of a float data
             bds_float_encode(&acceleration_g[0],&AccX);
    
             uint32_t x;
             m_custom_value = 0xFF;
             x = ble_cus_custom_value_update(&m_cus, m_custom_value);
             if(x = NRF_ERROR_RESOURCES)
             {
                nrf_gpio_pin_toggle(LED_4);
             }
             for(i = 0;i < 4; i++)
             {
                m_custom_value=AccX[i];
                ble_cus_custom_value_update(&m_cus, m_custom_value);
                nrf_delay_ms(10);
             }
           }
    }

  • What does ble_cus_custom_value_update do?

    xiao0528 said:
    I didn't use the UART example. I use the template and add a custom service.

     Study the uart example with regards to how it is sending notifications and handling the BLE_GATTS_EVT_HVN_TX_COMPLETE event

Related