Peripheral to Central - Communication

Hello,

     I'm using Da14531 as peripheral device and smart phone acts as the central. I want to transmit the data to peripheral device on the particular service and characteristic UUID. I'm using BLE_peripheral example available in the SDK.  

How the   user_catch_rest_hndl()     function is identifying the  services whether it is CUSTS1_VAL_WRITE_IND or CUSTS1_VAL_NTF_CFM?

To write the data to central device which custom message(CUSTS1_VALUE_REQ_IND/CUSTS1_VAL_SET_REQ/CUSTS1_VAL_WRITE_IND) I need to choose from custs1_task.h?

Where I can learn the difference between these messages?

 

Parents
  • Hi MA,

    Thank you for posting your question online and for your interest in our BLE products.
    The user_catch_rest_hndl function is identifying the messages we are getting from GAP manager. The implementation of this function is inside the ROM and it is not accessible. 

    #define app_process_catch_rest_cb       user_catch_rest_hndl

        .catch_rest_func            = app_process_catch_rest_cb,

    To write custom data on a specific characteristic it highly depends on the Characteristics Permissions. 
    My comments are made regarding the ble_app_peripheral example you are using as well:
    You can find the Custom 1 Database with the declaration of the Services and Characteristics inside the custs1_def.c and custs1_def.h files. 
    There you can find the Permissions we have given to each Service and Characteristic.
    Lets take as example the following characteristic from Service 1:
        // LED State Characteristic Declaration
        [SVC1_IDX_LED_STATE_CHAR]          = {(uint8_t*)&att_decl_char, ATT_UUID_16_LEN, PERM(RD, ENABLE),
                                                0, 0, NULL},
    
        // LED State Characteristic Value
        [SVC1_IDX_LED_STATE_VAL]           = {SVC1_LED_STATE_UUID_128, ATT_UUID_128_LEN, PERM(WR, ENABLE) | PERM(WRITE_COMMAND, ENABLE),
                                                PERM(RI, ENABLE) | DEF_SVC1_LED_STATE_CHAR_LEN, 0, NULL},
    
        // LED State Characteristic User Description
        [SVC1_IDX_LED_STATE_USER_DESC]     = {(uint8_t*)&att_desc_user_desc, ATT_UUID_16_LEN, PERM(RD, ENABLE),
                                                sizeof(DEF_SVC1_LED_STATE_USER_DESC) - 1, sizeof(DEF_SVC1_LED_STATE_USER_DESC) - 1,
                                                (uint8_t *) DEF_SVC1_LED_STATE_USER_DESC},

    When connected with a central and write on this characteristic, the user_catch_rest_hndl function is being triggered with a case of CUSTS1_VAL_WRITE_IND :
    void user_catch_rest_hndl(ke_msg_id_t const msgid,
                              void const *param,
                              ke_task_id_t const dest_id,
                              ke_task_id_t const src_id)
    {
        switch(msgid)
        {
            case CUSTS1_VAL_WRITE_IND:
            {
                struct custs1_val_write_ind const *msg_param = (struct custs1_val_write_ind const *)(param);
    
                switch (msg_param->handle)
                {
                    ...
    
                    case SVC1_IDX_LED_STATE_VAL:
                        user_svc1_led_wr_ind_handler(msgid, msg_param, dest_id, src_id);
                        break;
    
                    ...

    This will trigger the user_svc1_led_wr_ind_handler function which is inside the user_custs1_impl.c file:
    void user_svc1_led_wr_ind_handler(ke_msg_id_t const msgid,
                                         struct custs1_val_write_ind const *param,
                                         ke_task_id_t const dest_id,
                                         ke_task_id_t const src_id)
    {
        uint8_t val = 0;
        memcpy(&val, &param->value[0], param->length);
    
        if (val == CUSTS1_LED_ON)
        {
            GPIO_SetActive(GPIO_LED_PORT, GPIO_LED_PIN);
        }
        else if (val == CUSTS1_LED_OFF)
        {
            GPIO_SetInactive(GPIO_LED_PORT, GPIO_LED_PIN);
        }
    }

    With this characteristic we enable the LED if we sent 01 and we disable it when we sent 00.
    You can find the messages available for custs1 in the custs1_task.h file:
    /// Messages for CUSTS1
    enum
    {
        /// Add a CUSTOMS instance into the database
        CUSTS1_CREATE_DB_REQ = KE_FIRST_MSG(TASK_ID_CUSTS1),
        /// Inform APP of database creation status
        CUSTS1_CREATE_DB_CFM,
        /// Start the Custom Service Task - at connection
        CUSTS1_ENABLE_REQ,
        /// Set/update characteristic value
        CUSTS1_VAL_SET_REQ,
        /// Peer device request to get a non-database value (RI enabled)
        CUSTS1_VALUE_REQ_IND,
        /// Response to non-database value request
        CUSTS1_VALUE_REQ_RSP,
        /// Inform profile task that the peer has subscribed/unsubscribed to notification
        CUSTS1_VAL_NTF_REQ,
        /// Response after receiving a CUSTS1_VAL_NTF_REQ message and a notification is triggered
        CUSTS1_VAL_NTF_CFM,
        /// Inform profile task that the peer has subscribed/unsubscribed to indication
        CUSTS1_VAL_IND_REQ,
        /// Response after receiving a CUSTS1_VAL_IND_REQ message and an indication is triggered
        CUSTS1_VAL_IND_CFM,
        /// Indicate that the characteristic value has been written
        CUSTS1_VAL_WRITE_IND,
        /// Inform the application that the profile service role task has been disabled after a disconnection
        CUSTS1_DISABLE_IND,
        /// Profile error report
        CUSTS1_ERROR_IND,
        /// Inform the application that there is an attribute info request that shall be processed
        CUSTS1_ATT_INFO_REQ,
        /// Inform back that the attribute info request has been processed
        CUSTS1_ATT_INFO_RSP,
    };

    The CUSTS1_VALUE_REQ_IND message is being used when we want to read a characteristic with permission RI. This is implemented in Service 3, Characteristic 4 on the ble_app_peripheral_example:
        // Read 4 Characteristic Declaration
        [SVC3_IDX_READ_4_CHAR]             = {(uint8_t*)&att_decl_char, ATT_UUID_16_LEN, PERM(RD, ENABLE), 0, 0, NULL},
    
        // Read 4 Characteristic Value
        [SVC3_IDX_READ_4_VAL]              = {SVC3_READ_VAL_4_UUID_128, ATT_UUID_128_LEN, PERM(RD, ENABLE),
                                                PERM(RI, ENABLE) | DEF_SVC3_READ_VAL_4_CHAR_LEN, 0, NULL},
    
        // Read 4 Characteristic User Description
        [SVC3_IDX_READ_4_USER_DESC]        = {(uint8_t*)&att_desc_user_desc, ATT_UUID_16_LEN, PERM(RD, ENABLE),
                                                sizeof(DEF_SVC3_READ_VAL_4_USER_DESC) - 1, sizeof(DEF_SVC3_READ_VAL_4_USER_DESC) - 1,
                                                (uint8_t *) DEF_SVC3_READ_VAL_4_USER_DESC},

    ...
            case CUSTS1_VALUE_REQ_IND:
            {
                struct custs1_value_req_ind const *msg_param = (struct custs1_value_req_ind const *) param;
    
                switch (msg_param->att_idx)
                {
                    case SVC3_IDX_READ_4_VAL:
                    {
                        user_svc3_read_non_db_val_handler(msgid, msg_param, dest_id, src_id);
                    } break;
    ...

    void user_svc3_read_non_db_val_handler(ke_msg_id_t const msgid,
                                               struct custs1_value_req_ind const *param,
                                               ke_task_id_t const dest_id,
                                               ke_task_id_t const src_id)
    {
        // Increase value by one
        non_db_val_counter++;
    
        struct custs1_value_req_rsp *rsp = KE_MSG_ALLOC_DYN(CUSTS1_VALUE_REQ_RSP,
                                                            prf_get_task_from_id(TASK_ID_CUSTS1),
                                                            TASK_APP,
                                                            custs1_value_req_rsp,
                                                            DEF_SVC3_READ_VAL_4_CHAR_LEN);
    
        // Provide the connection index.
        rsp->conidx  = app_env[param->conidx].conidx;
        // Provide the attribute index.
        rsp->att_idx = param->att_idx;
        // Force current length to zero.
        rsp->length  = sizeof(non_db_val_counter);
        // Provide the ATT error code.
        rsp->status  = ATT_ERR_NO_ERROR;
        // Copy value
        memcpy(&rsp->value, &non_db_val_counter, rsp->length);
        // Send message
        ke_msg_send(rsp);
    }

    Each time we read this characteristic, the value of the characteristic increments by 1.
    The CUSTS1_VAL_SET_REQ message is being used when we want to update the value of a characteristic inside our code. 
    For example you could have a timer that triggers every 1 second. Then use the CUSTS1_VAL_SET_REQ message in order to change your characteristic value.
    The CUSTS1_VAL_WRITE_IND message is being triggered when the central has attempted to write on one of our characteristics. You should create different cases for each characteristic on the user_catch_rest_hndl function in order to implement different functionalities for your characteristics.

    Kind Regards,
    OV_Renesas



  • Hello,

           I want to send the data to central device (smartphone) from the peripheral device( DA14531 evaluation board) on the particular characterisitcs.

    When connected with a central and write on this characteristic, the user_catch_rest_hndl function is being triggered with a case of CUSTS1_VAL_WRITE_IND :

            As you mentioned earlier when the central device wants  to write data to the peripheral device, then CUSTS1_VAL_WRITE_IND case will be true. 

            If the peripheral device wants to send the data, which case will be true?

            

    void user_svc4_write(ke_msg_id_t const msgid,
                                               struct custs1_val_write_ind const *param,
                                               ke_task_id_t const dest_id,
                                               ke_task_id_t const src_id)
    																					 {
    	  if (param->value[0])
        {
            uint8_t conidx = KE_IDX_GET(src_id);
    
            struct custs1_val_ind_req* req = KE_MSG_ALLOC_DYN(CUSTS1_VAL_IND_REQ,
                                                              prf_get_task_from_id(TASK_ID_CUSTS1),
                                                              TASK_APP,
                                                              custs1_val_ind_req,
                                                              2);
    
    				
    				req->conidx = app_env[conidx].conidx;
            req->handle = SVC4_WRITE_VAL;
            req->length = 2;
            req->value[0] = 1;
            req->value[1] = 2;
    
    
            ke_msg_send(req);
        }																					 																						 
     }																

            I have wrote the above mentioned function to transmit the data to the central device.

           Is this correct way of doing it?

          The peripheral example available in the SDK will act as GATT server or GATT client?

          For example if the DA14531 evaluation board acts as GATT server, How the smartphone will know that it has to acts as the server?

           

        

  • Hi MA,

    Thank you for the reply.
    The code snippet you have shared is utilizing the CUSTS1_VAL_IND_REQ which is the message being used when we want to read a characteristic value.
    You should use:

        /// Set/update characteristic value
        CUSTS1_VAL_SET_REQ,

    In case you have implemented the Notify permission on your characteristic you should use CUSTS1_VAL_NTF_CFM as your case on user_catch_rest_hndl and the CUSTS1_VAL_NTF_REQ message in order to update the characteristic.
    Thesame applies for Indications as well:
        /// Inform profile task that the peer has subscribed/unsubscribed to indication
        CUSTS1_VAL_IND_REQ,
        /// Response after receiving a CUSTS1_VAL_IND_REQ message and an indication is triggered
        CUSTS1_VAL_IND_CFM,

    The ble_app_peripheral example will act as GATT Client. 
    If you configure the DA14531 as central it can act as GATT Server and you will have to configure your smartphone as peripheral in order to be able to connect. In order to connect, the central must send a connect request.

    Kind Regards,
    OV_Renesas

  • Hello,

        Below I have mentioned the charactersitics permission , I have given for custom service and characteristics.

    																					
    	  [SVC4_IDX_SVC]                     = {(uint8_t*)&att_decl_svc, ATT_UUID_128_LEN, PERM(RD, ENABLE),
                                                sizeof(custs1_svc4), sizeof(custs1_svc4), (uint8_t*)&custs1_svc4},
    		
    				// Write 1 Characteristic Declaration
        [SVC4_WRITE_CHAR]                = {(uint8_t*)&att_decl_char, ATT_UUID_16_LEN, PERM(RD, ENABLE), 0, 0, NULL},
    
        // Write 1 Characteristic Value
        [SVC4_WRITE_VAL]                 = {custs1_svc4, ATT_UUID_128_LEN, PERM(WR, ENABLE) | PERM(WRITE_REQ, ENABLE),
                                                PERM(RI, ENABLE) | DEF_SVC4_WRITE_VAL_CHAR_LEN, 0, NULL},
    
        // Write 1 Characteristic User Description
        [SVC4_WRITE_USER_DESC]           = {(uint8_t*)&att_desc_user_desc, ATT_UUID_16_LEN, PERM(RD, ENABLE),
                                                sizeof(DEF_SVC4_WRITE_VAL_USER_DESC) - 1, sizeof(DEF_SVC4_WRITE_VAL_USER_DESC) - 1,
                                                (uint8_t *) DEF_SVC4_WRITE_VAL_USER_DESC},

    In the user_catch_rest_hndl() function , I have added the case following case. 

    case CUSTS1_VAL_SET_REQ:
    			{
    				 struct custs1_val_set_req const *msg_param = (struct custs1_val_set_req const *)(param);
    								
    				 switch (msg_param->handle)
                {
                   case SVC4_WRITE_VAL:
    							 {user_svc4_write(msgid, msg_param, dest_id, src_id);
    							 }break;
    						 }	
    			} break;

     But this case is not getting implemented while debugging. I have wrote the following function to send the data to the server.

    void user_svc4_write(ke_msg_id_t const msgid,
                                               struct custs1_val_set_req const *param,
                                               ke_task_id_t const dest_id,
                                               ke_task_id_t const src_id)
    																					 {
    	  if (param->value[0])
        {
            uint8_t conidx = KE_IDX_GET(src_id);
    			
    			struct custs1_val_set_req* req= KE_MSG_ALLOC_DYN( CUSTS1_VAL_SET_REQ,
                                                              prf_get_task_from_id(TASK_ID_CUSTS1),
                                                              TASK_APP,
                                                              custs1_val_set_req,
                                                              2);
    	
    
    				
    				req->conidx = app_env[conidx].conidx;
            req->handle = SVC4_WRITE_VAL;
            req->length = 2;
            req->value[0] = 1;
            req->value[1] = 2;
    
    
            ke_msg_send(req);
        }																					 																						 
     }															

    Why the characteristics are not matching?

    Why the data is not transmitted to the central device?

    Please help me to solve the issue!!

  • Hi MA,

    Thank you for the reply.
    According to the custom Characteristic you shared:
    You will have to set a case for CUSTS1_VALUE_REQ_IND on your user_catch_hndl and call the user_svc4_write function.
    On your user_svc4_write function you should use the CUSTS1_VALUE_REQ_RSP type to give the value you want to your characteristic.
    You will need those because you have given the permission RI.
    I do not understand why you got the if statement there.
    Also, if you had implemented some sort of timer that would call the user_svc4_write function then you should be able to update your characteristic, you cannot catch a CUSTS1_VAL_SET_REQ the way it is implemented.
    The if statement and 

    uint8_t conidx = KE_IDX_GET(src_id);

    I do not understand their usage.

    Kind Regards,
    OV_Renesas

Reply
  • Hi MA,

    Thank you for the reply.
    According to the custom Characteristic you shared:
    You will have to set a case for CUSTS1_VALUE_REQ_IND on your user_catch_hndl and call the user_svc4_write function.
    On your user_svc4_write function you should use the CUSTS1_VALUE_REQ_RSP type to give the value you want to your characteristic.
    You will need those because you have given the permission RI.
    I do not understand why you got the if statement there.
    Also, if you had implemented some sort of timer that would call the user_svc4_write function then you should be able to update your characteristic, you cannot catch a CUSTS1_VAL_SET_REQ the way it is implemented.
    The if statement and 

    uint8_t conidx = KE_IDX_GET(src_id);

    I do not understand their usage.

    Kind Regards,
    OV_Renesas

Children
No Data