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

How to retrieve multiple values from a server with 1 services with several characteristics ?

Hello everybody !
Designing with the development board nRF52832 and SDK14.2.

My code is based on the code of the "ble_app_multilink_central" on which, I added some of the code "ble_app_uart_c" for the initialization and the event manager on the uart. I have a nrf52832 development board as a central and 2 nrf52832 developpement boards as a peripheral.

In a device, I created my personalized service composed of 4 characteristics.

My first question:
it's possible to send up to how many notifications at the same time to a single Client? Could I, in your opinion, send notifications with a size of 8 bytes? (I thought I saw that the max was 20 bytes so it's good).

Here is a simple diagram representing my system :

How to retrieve the value1, value2, value3 and value4 in the client?

Thank you in advance !

  • Hello,

    The number of notifications you can send (within one connection interval, I assume you mean) depends on your connection parameters. The connection interval length, MTU_SIZE, EVENT_LENGTH and PHY (1 or 2Mpbs).

    Some of the limitations are dependent on how much time you have to use the radio within the connection interval, while some of the limitations are due to buffer sizes.

    I recommend that you check out the example: SDK14.2.0\examples\ble_central_and_peripheral\experimental\ble_app_att_mtu_throughput. A guide on how to use it is found here. Although you can't adjust the packet (notification) size, it gives a good idea for what the other connection parameters does to the speed.

    A notification of 8 bytes is no problem, but you should know that you get more overhead to the data you are sending when you split them up in smaller packages.

     

    If you want to test this with another example, you can use pretty much any example, and just queue up the notifications. In the ble_app_uart example, this can be done with ble_nus_string_send, which you can see, in turn calls sd_ble_gatts_hvx(). You must check the return value of this call. If it returns NRF_SUCCESS; then the package is queued, and you can assume it will be received on the other side of the link (it will be retransmitted if not). If it returns NRF_ERROR_RESOURCES, it means that too many notifications is queued, and you will have to wait for the event BLE_GATTS_EVT_HVN_TX_COMPLETE before you can try to queue notifications again.

     

    Best regards,

    Edvin

     

  • Thank you already for your information and advice!

    In fact I followed the tutorial: https://github.com/bjornspockeli/custom_ble_service_example

    Then as a result of that, I directly used the example :  https://github.com/bjornspockeli/custom_ble_service_example/tree/master/pca10040/s132 using the SDK 15.0 (which I then took now)

    the problem is that i would like to get a value for my characteristic 1 and another for my characteristic 2 on the application nrf connect :

    Creation

    I defined a new uuid for the new characteristics (0x1402)

    I created a characteristics by changing the UUID (line 32)

    static uint32_t custom_value_char_add2(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;
    
        // Add Custom Value characteristic
        memset(&cccd_md, 0, sizeof(cccd_md));
    
        //  Read  operation on cccd should be possible without authentication.
        BLE_GAP_CONN_SEC_MODE_SET_OPEN(&cccd_md.read_perm);
        BLE_GAP_CONN_SEC_MODE_SET_OPEN(&cccd_md.write_perm);
        
        cccd_md.write_perm = p_cus_init->custom_value_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   = 0;
        char_md.char_props.write  = 1;
        char_md.char_props.notify = 1; 
        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         = &cccd_md; 
        char_md.p_sccd_md         = NULL;
    		
        ble_uuid.type = p_cus->uuid_type;
        ble_uuid.uuid = CUSTOM_VALUE2_CHAR_UUID;
    
        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;
    
        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 added it at the end of the ble_cus_init.

    Here for the creation of the 2nd characteristic.

    Then in main.c :

    static void notification_timeout_handler(void * p_context)
    {
        UNUSED_PARAMETER(p_context);
        ret_code_t err_code;
        
        // Increment the value of m_custom_value before nortifing it.
        custom++;
        custom2++;
        
        ble_cus_custom_value_update(&m_cus, custom);
        ble_cus_custom_value_update(&m_cus, custom2);
    
    }

    and in ble_cus.c :

    uint32_t ble_cus_custom_value_update(ble_cus_t * p_cus, uint8_t custom_value)
    {
        NRF_LOG_INFO("In ble_cus_custom_value_update. \r\n"); 
        if (p_cus == NULL)
        {
            return NRF_ERROR_NULL;
        }
        nrf_gpio_pin_toggle(LED_3);
        uint32_t err_code = NRF_SUCCESS;
        ble_gatts_value_t gatts_value;
    
        // Initialize value struct.
        memset(&gatts_value, 0, sizeof(gatts_value));
    
        gatts_value.len     = sizeof(uint8_t);
        gatts_value.offset  = 0;
        gatts_value.p_value = &custom_value;
    
        // Update database.
        err_code = sd_ble_gatts_value_set(p_cus->conn_handle,
                                          p_cus->custom_value_handles.value_handle,
                                          &gatts_value);
        if (err_code != NRF_SUCCESS)
        {
            return err_code;
        }
    
        // Send value if connected and notifying.
        if ((p_cus->conn_handle != BLE_CONN_HANDLE_INVALID)) 
        {
            ble_gatts_hvx_params_t hvx_params;
    
            memset(&hvx_params, 0, sizeof(hvx_params));
    
            hvx_params.handle = p_cus->custom_value_handles.value_handle;
            hvx_params.type   = BLE_GATT_HVX_NOTIFICATION;
            hvx_params.offset = gatts_value.offset;
            hvx_params.p_len  = &gatts_value.len;
            hvx_params.p_data = gatts_value.p_value;
    
            err_code = sd_ble_gatts_hvx(p_cus->conn_handle, &hvx_params);
            NRF_LOG_INFO("sd_ble_gatts_hvx result: %x. \r\n", err_code); 
        }
        else
        {
            err_code = NRF_ERROR_INVALID_STATE;
            NRF_LOG_INFO("sd_ble_gatts_hvx result: NRF_ERROR_INVALID_STATE. \r\n"); 
        }
    
    
        return err_code;
    }

    Here I am blocked, I continue to search but I do not see, I tested several things but without success.

    The "conn_handle" is to connect that to the peripheral not to the characteristicistic, no?

    Thanks in advance Slight smile

  • Yes. The conn_handle is usually used for the connection handle, but you also have a characteristic handle. If you look in one of the examples with several services and characteristics, e.g. the ble_app_hrs example, you can see that it sends the heart rate measurement from main.c in heart_rate_meas_timeout_handler(...) --> ble_hrs_heart_rate_measurement_send(...) --> sd_ble_gatts_hvx(...)

     

    It is the last function that actually sends the notification. you can see that it takes the argument hvx_params, which has a handle set by:

    hvx_params.handle =  p_hrs->hrm_handles.value_handle;

    So you need a different handle for all of your characteristics.

     

    Another example is the ble_app_uart example, which has two characterisitcs within one service, which is closer to what you are doing. You can see there that it sets the handle within:

    uart_event_handle(...) --> ble_nus_data_send(...) --> sd_ble_gatts_hvx(...)

    and it sets the handle with:

    hvx_params.handle = p_nus->tx_handles.value_handle;

    If you look in the file ble_nus.h, you can see that it has two characteristic handles. tx_handles and rx_handles, which both has a .value_handle, so you need one handle for each of your characteristics. 

    Please look at the ble_app_uart example, and see how the tx_char_add() and rx_char_add() are set up, and see if you can port that to your own project.

     

    Best regards,

    Edvin

  • Thank you for all this help I managed to make 4 characteristic that sends a notification.
    The problem is that only the button to allow notifications of characteristic 1 makes me enter the function notification_timeout_handler (void * p_context)

    the function notification_timeout_handler (void * p_context) :

    static void notification_timeout_handler(void * p_context)
    {
        UNUSED_PARAMETER(p_context);
        ret_code_t err_code;
        nrf_gpio_pin_toggle(LED_4); 
        // Increment the value of m_custom_value before nortifing it.
        m_custom_value1=m_custom_value1+1;
        m_custom_value2=m_custom_value2+256;
        m_custom_value3=m_custom_value3+256*256;
        m_custom_value4=m_custom_value4+256*256*256;
    
        ble_cus_custom_value_update(&m_cus, m_custom_value1, m_custom_value2, m_custom_value3, m_custom_value4);
    }

    I'm sure the solution is in the on_cus_evt part with the BLE_CUS_EVT_NOTIFICATION_ENABLED but I do not see where.

    I would like to understand what this button interacts with

  • That button is used to enable notification on a service. If it is pressed (so that the icon gets the cross over, like in the pictures that you have posted) it means that notifications are enabled.

    I am not sure how you have set up your notification_timeout_handler() function. Notifications are usually used like this:

    When notifications are not enabled, any sensor updates is just stored in the device.

    If notifications are enabled, the device will notify the connected device whenever the characteristic changes. So what you should try to do is to enable the notifications and then set up a timer or something to send updated values of the characteristics. I suspect this is what you are trying to do, but I am not sure what your ble_cus_custom_value_update() looks like. I suspect that the version that you sent earlier is an outdated version?

     

    In this function, you should check that notifications are enabled, and send the data for each characteristic with sd_ble_gatts_hvx(conn_handle, &hvx_params);

     

    Best regards,

    Edvin

     

Related