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

Setting Custom Gatt characteristic data.

Hi,

I want to set a custom value to my Gatt characteristic value. I can do this by writing to the characteristic in my app or the NRF_connect application. 

What I want to do is based on an event or the data I receive to change the GATT characteristic data for my app to receive. I tried using this code which, did not change the data.

//err_code = ble_cus_custom_value_update(p_cus, p_ble_evt,m_custom_value);
//APP_ERROR_CHECK(err_code);
//I pass in a fixed value for testing into m_custom_value

uint32_t ble_cus_custom_value_update(ble_cus_t * p_cus, ble_evt_t const * p_ble_evt, uint8_t custom_value)
{
    NRF_LOG_INFO("In ble_cus_custom_value_update. \r\n"); 
    if (p_cus == NULL)
    {
        return NRF_ERROR_NULL;
    }

    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);
    }
    else
    {
        err_code = NRF_ERROR_INVALID_STATE;
    }


    return err_code;
}

What I need to know is how to update this data value so I can transfer useful data between the two devices (mobile application and nordic board).

Note I am trying to do this within my GATT connection itself.

Note using SDK15 , PCA10056 NRF52840-dk Segger,

Thanks

Parents
  • Hi,

    What I want to do is based on an event or the data I receive to change the GATT characteristic data for my app to receive. I tried using this code which, did not change the data.

     So you are calling ble_cus_custom_value_update() to update the value? but the value is not updated? Did you get any errors codes returned from ble_cus_custom_value_update() ?

  • Thanks for your response. Sorry I forgot to add on calling the function(from within my gatt connection code). It returns an error: 3400. 0r 0x00003400. Just to add some context I am receiving a command from a GATT connection via a mobile application which if passed(Correct command) i then output some text via uart and call the ble_cus_custom_value_update();

    I am not however, sure why this crash occurs. Thanks

  • Did you add the service / characteristic like this ?

    https://github.com/bjornspockeli/custom_ble_service_example/blob/SDK_v15.0.0/ble_cus.c#L198

    Thomas said:
    It returns an error: 3400. 0r 0x00003400

     This is BLE_ERROR_GATTS_INVALID_ATTR_TYPE .  

    If you call sd_ble_gatts_hvx() and the return error code is:

    * @retval ::BLE_ERROR_GATTS_INVALID_ATTR_TYPE Invalid attribute type(s) supplied, only characteristic values may be notified and indicated.

    Then I think either hvx_params.handle is wrong (maybe it's not the correct handle to the characteristic that can notify values), or the handle you are using is pointing to a characteristic that don't support notifications (char_props.notify).

  • I believe I added the service and characteristic properly. I did follow that tutorial but here is my init code non the less. 

    The data for p_cus and the event is passed in from the on_write statement inside of the gatt connection itself. 

    uint32_t ble_cus_init(ble_cus_t * p_cus, const ble_cus_init_t * p_cus_init)
    {
        //Init statemachine for reading the commands
    
        CMD_BLE_STATE_VALUE = CMD_BLE_STATE_START;//Move to start state
        CMD_STARTED = true;
    
        if (p_cus == NULL || p_cus_init == NULL)
        {
            return NRF_ERROR_NULL;
        }
    
        uint32_t   err_code;
        ble_uuid_t ble_uuid;
    
        // Initialize service structure
        //p_cus->evt_handler               = p_cus_init->evt_handler;
        p_cus->conn_handle               = BLE_CONN_HANDLE_INVALID;
        //p_cus->conn_handle               = BLE_CONN_HANDLE_INVALID;
    
        // Add Custom Service UUID
        ble_uuid128_t base_uuid = {CUSTOM_SERVICE_UUID_BASE};
        err_code =  sd_ble_uuid_vs_add(&base_uuid, &p_cus->uuid_type);
        VERIFY_SUCCESS(err_code);
        
        ble_uuid.type = p_cus->uuid_type;
        ble_uuid.uuid = CUSTOM_SERVICE_UUID;
    
        // Add the Custom Service
        err_code = sd_ble_gatts_service_add(BLE_GATTS_SRVC_TYPE_PRIMARY, &ble_uuid, &p_cus->service_handle);
        if (err_code != NRF_SUCCESS)
        {
            return err_code;
        }
        // Add Custom Value characteristic
        return custom_value_char_add(p_cus, p_cus_init);
    }

    You are correct though it is sd_ble_gatt_hvx that is the issue. So how can I write to it? Do I change it from having notifications enabled to something that is not?

  • 1) Could you post the code for the place where you call ble_cus_custom_value_update()?

    2) Can you post your ble_cus.c code ?

  • I call the ble_cus_custom_value_update() within my ble_cus.c code.

    I do it within a statemachine that handles processing of incoming data. This incoming data is handled via two statemachines. Process_Command and process_selected_value. Within this I have a help command that if run prints out help info is: . Then runs the command( this is for testing). Below is my code:

    #include "sdk_common.h"
    #include "ble_srv_common.h"
    #include "ble_cus.h"
    #include <string.h>
    #include "nrf_gpio.h"
    #include "boards.h"
    #include "nrf_log.h"
    
    bool CMD_STARTED = false;
    bool Print_log_enabled = false;//If ==1 enable printing log if == 0 dissabled
    bool Processing_Commands = false;
    static uint8_t m_custom_value = 77;
    //Statemachine for GATT commands
    enum
    {
        CMD_BLE_STATE_START,
        CMD_BLE_STATE_STOP,
        CMD_BLE_STATE_SELECT_COMMAND,
        CMD_BLE_STATE_UART_LOGGING,
        CMD_BLE_STATE_SD_LOGGING,
        CMD_BLE_STATE_READ_A_DATUM,
        CMD_BLE_STATE_TIME_STAMP_START,//Might need several due to data limitations
        CMD_BLE_STATE_TIME_STAMP_RECEIVE_YEAR,
        CMD_BLE_STATE_TIME_STAMP_RECEIVE_MONTH,
        CMD_BLE_STATE_TIME_STAMP_RECEIVE_DAY,
        CMD_BLE_STATE_TIME_STAMP_RECEIVE_HOUR,
        CMD_BLE_STATE_TIME_STAMP_RECEIVE_MINUTE,
        CMD_BLE_STATE_TIME_STAMP_RECEIVE_SECOND,
        CMD_BLE_STATE_GET_LOG,//Get the SD card log
        CMD_BLE_STATE_HELP,
        CMD_BLE_STATE_ERROR,
    } CMD_BLE_STATE_VALUE;
    
    
    uint32_t ble_cus_init(ble_cus_t * p_cus, const ble_cus_init_t * p_cus_init)
    {
        //Init statemachine for reading the commands
    
        CMD_BLE_STATE_VALUE = CMD_BLE_STATE_START;//Move to start state
        CMD_STARTED = true;
    
        if (p_cus == NULL || p_cus_init == NULL)
        {
            return NRF_ERROR_NULL;
        }
    
        uint32_t   err_code;
        ble_uuid_t ble_uuid;
    
        // Initialize service structure
        //p_cus->evt_handler               = p_cus_init->evt_handler;
        p_cus->conn_handle               = BLE_CONN_HANDLE_INVALID;
        //p_cus->conn_handle               = BLE_CONN_HANDLE_INVALID;
    
        // Add Custom Service UUID
        ble_uuid128_t base_uuid = {CUSTOM_SERVICE_UUID_BASE};
        err_code =  sd_ble_uuid_vs_add(&base_uuid, &p_cus->uuid_type);
        VERIFY_SUCCESS(err_code);
        
        ble_uuid.type = p_cus->uuid_type;
        ble_uuid.uuid = CUSTOM_SERVICE_UUID;
    
        // Add the Custom Service
        err_code = sd_ble_gatts_service_add(BLE_GATTS_SRVC_TYPE_PRIMARY, &ble_uuid, &p_cus->service_handle);
        if (err_code != NRF_SUCCESS)
        {
            return err_code;
        }
        // Add Custom Value characteristic
        return custom_value_char_add(p_cus, p_cus_init);
    }
    uint32_t custom_value_char_add(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;
    
        memset(&char_md, 0, sizeof(char_md));
    
        char_md.char_props.read   = 1;
        char_md.char_props.write  = 1;
        char_md.char_props.notify = 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         = NULL; 
        char_md.p_sccd_md         = NULL;
    		
        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;
    
        ble_uuid.type = p_cus->uuid_type;
        ble_uuid.uuid = CUSTOM_VALUE_CHAR_UUID;
    
        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(uint32_t);//was Uint8_t
        attr_char_value.init_offs = 0;
        attr_char_value.max_len   = sizeof(uint32_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;
    }
    
    void ble_cus_on_ble_evt( ble_evt_t const * p_ble_evt, void * p_context)
    {
       ble_cus_t * p_cus = (ble_cus_t *) p_context;
    
       if (p_cus == NULL || p_ble_evt == NULL)
       {
           return;
       }
       
       switch (p_ble_evt->header.evt_id)
       {
           case BLE_GAP_EVT_CONNECTED:
               on_connect(p_cus, p_ble_evt);
               break;
    
           case BLE_GAP_EVT_DISCONNECTED:
               on_disconnect(p_cus, p_ble_evt);
               break;
           case BLE_GATTS_EVT_WRITE:
               on_write(p_cus, p_ble_evt);
               break;
           default:
               // No implementation needed.
               break;
       }
    }
    void on_connect(ble_cus_t * p_cus, ble_evt_t const * p_ble_evt)
    {
        p_cus->conn_handle = p_ble_evt->evt.gap_evt.conn_handle;
    
    }
    void on_disconnect(ble_cus_t * p_cus, ble_evt_t const * p_ble_evt)
    {
        UNUSED_PARAMETER(p_ble_evt);
        p_cus->conn_handle = BLE_CONN_HANDLE_INVALID;
    }
    void on_write(ble_cus_t * p_cus, ble_evt_t const * p_ble_evt)
    {
        ble_gatts_evt_write_t * p_evt_write = &p_ble_evt->evt.gatts_evt.params.write;
        
        uint32_t found_data = 0;
        
        // Check if the handle passed with the event matches the Custom Value Characteristic handle.
        if (p_evt_write->handle == p_cus->custom_value_handles.value_handle)
        {
            memcpy(&found_data, p_ble_evt->evt.gatts_evt.params.write.data, p_ble_evt->evt.gatts_evt.params.write.len);
    
            Process_Command(p_cus, p_ble_evt,found_data);
        }
    }
    uint32_t ble_cus_custom_value_update(ble_cus_t * p_cus, ble_evt_t const * p_ble_evt, uint8_t custom_value)
    {
        NRF_LOG_INFO("In ble_cus_custom_value_update. \r\n"); 
        if (p_cus == NULL)
        {
            return NRF_ERROR_NULL;
        }
    
        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);
        }
        else
        {
            err_code = NRF_ERROR_INVALID_STATE;
        }
    
    
        return err_code;
    }
    void Process_Command(ble_cus_t * p_cus, ble_evt_t const * p_ble_evt, uint32_t command)
    {
        if((command == CMD_BLE_START) && (CMD_STARTED == 0))
        {
            CMD_BLE_STATE_VALUE = CMD_BLE_STATE_START;//Move to start state
            CMD_STARTED = true; 
        }
        NRF_LOG_INFO("Command is: %d",command);
        if(Processing_Commands == false){
            switch (CMD_BLE_STATE_VALUE)
            {
              case CMD_BLE_STATE_START://Start command issued
                   //See next command
                   NRF_LOG_INFO("Start state");
                   CMD_BLE_STATE_VALUE = CMD_BLE_STATE_SELECT_COMMAND;  
                   break;//Removed break as to fall into other states
               case CMD_BLE_STATE_SELECT_COMMAND://Start command issued
                   //See next command
                   NRF_LOG_INFO("Command state");
                   if(command == CMD_BLE_STOP)
                   {
                      CMD_BLE_STATE_VALUE = CMD_BLE_STATE_STOP;//Move to stop state
                      NRF_LOG_INFO("Go to stop state");
                   }
                   else if(command == CMD_BLE_HELP)//HELP
                   {
                      CMD_BLE_STATE_VALUE = CMD_BLE_STATE_HELP;//Move to help state
                      NRF_LOG_INFO("Go to help state");
                   }
                   else if(command == CMD_BLE_SD_LOG)//SD Logging
                   {
                      CMD_BLE_STATE_VALUE = CMD_BLE_STATE_SD_LOGGING;//Move to SD logging state
                      NRF_LOG_INFO("Go to SD log state");
                   }
                   else if(command == CMD_BLE_UART_LOG)//UART LOGGING
                   {
                      CMD_BLE_STATE_VALUE = CMD_BLE_STATE_UART_LOGGING;//Move to uart log state
                      NRF_LOG_INFO("Go to UART state");
                   }
                   else if(command == CMD_BLE_READ_DATAM)//Read a datam
                   {
                      CMD_BLE_STATE_VALUE = CMD_BLE_STATE_READ_A_DATUM;//Move to help state
                      NRF_LOG_INFO("Go to Read datum state");
                   }
                   else if(command == CMD_BLE_TIME_STAMP_START)//Receive the timestamp
                   {
                      CMD_BLE_STATE_VALUE = CMD_BLE_STATE_TIME_STAMP_START;//Go to reading the timestamp date
                      Processing_Commands = true;//Set to process the rest of the commands
                      NRF_LOG_INFO("Go to Timestamp state");
                   }
                   else if(command == CMD_BLE_GET_LOG)//Get the log
                   {
                      CMD_BLE_STATE_VALUE = CMD_BLE_STATE_GET_LOG;//Get the log
                      NRF_LOG_INFO("Go to log state");
                   }
                   else
                   {
                      CMD_BLE_STATE_VALUE = CMD_BLE_STATE_ERROR;//Move to error state unknown command
                      NRF_LOG_INFO("Go to error state");
                   }
                   if(Processing_Commands == false){
                      Process_Selected_state(p_cus, p_ble_evt, command);//Run the command
                   }
                   break;//Removed break as to fall into other states
               default:
                   // No implementation needed.
                   break;
             }
        }//End of case statements
        if(Processing_Commands == true){
            Process_Selected_state(p_cus, p_ble_evt, command);//Run the command
        }
    }
    void Process_Selected_state(ble_cus_t * p_cus, ble_evt_t const * p_ble_evt, uint32_t command)
    {
    switch (CMD_BLE_STATE_VALUE)
    {
        ret_code_t err_code;
        case CMD_BLE_STATE_HELP:
            NRF_LOG_INFO("Help state");
            NRF_LOG_INFO("Help info is:");
            err_code = ble_cus_custom_value_update(p_cus, p_ble_evt,m_custom_value);
            APP_ERROR_CHECK(err_code);
            CMD_BLE_STATE_VALUE = CMD_BLE_STATE_SELECT_COMMAND;  
            break;
        case CMD_BLE_STATE_SD_LOGGING:
            NRF_LOG_INFO("SD card LOG state");
            CMD_BLE_STATE_VALUE = CMD_BLE_STATE_SELECT_COMMAND;  
            break;
        case CMD_BLE_STATE_UART_LOGGING:
            NRF_LOG_INFO("Uart Logging state");
            CMD_BLE_STATE_VALUE = CMD_BLE_STATE_SELECT_COMMAND;
            if(Print_log_enabled == 1)//Toggle the printing
            {
                Print_log_enabled = 0;
                NRF_LOG_INFO("Logging disabled");
            }
            else
            {
                Print_log_enabled = 1;
                NRF_LOG_INFO("Logging enabled");
            }
            break;
       case CMD_BLE_STATE_READ_A_DATUM:
            NRF_LOG_INFO("Reading datam state");
            CMD_BLE_STATE_VALUE = CMD_BLE_STATE_SELECT_COMMAND;
            SD_CARD_Read_Line();//Run the read command for the SD card reading
            break;
        case CMD_BLE_STATE_TIME_STAMP_START:
            NRF_LOG_INFO("Receiving Time stamp data");
            //CMD_BLE_STATE_VALUE = CMD_BLE_STATE_SELECT_COMMAND;
            CMD_BLE_STATE_VALUE = CMD_BLE_STATE_TIME_STAMP_RECEIVE_YEAR;
            //Need to read the time stamp data here
            break;
        case CMD_BLE_STATE_TIME_STAMP_RECEIVE_YEAR:
            NRF_LOG_INFO("Receving year");
            Time_Year = command;//Set the year
            NRF_LOG_INFO("Year data is: %d",Time_Year);
            CMD_BLE_STATE_VALUE = CMD_BLE_STATE_TIME_STAMP_RECEIVE_MONTH;
            break;
        case CMD_BLE_STATE_TIME_STAMP_RECEIVE_MONTH:
            Time_Month = command;//Set the month
            NRF_LOG_INFO("Receving month");
            CMD_BLE_STATE_VALUE = CMD_BLE_STATE_TIME_STAMP_RECEIVE_DAY;
            break;
        case CMD_BLE_STATE_TIME_STAMP_RECEIVE_DAY:
            Time_Day = command;//Set the day
            NRF_LOG_INFO("Receving day");
            CMD_BLE_STATE_VALUE = CMD_BLE_STATE_TIME_STAMP_RECEIVE_HOUR;
            break;
        case CMD_BLE_STATE_TIME_STAMP_RECEIVE_HOUR:
            Time_Hour = command;//Set the hour
            NRF_LOG_INFO("Receving hour");
            CMD_BLE_STATE_VALUE = CMD_BLE_STATE_TIME_STAMP_RECEIVE_MINUTE;
            break;
        case CMD_BLE_STATE_TIME_STAMP_RECEIVE_MINUTE:
            Time_Minute = command;//Set the minute
            NRF_LOG_INFO("Receving minute");
            CMD_BLE_STATE_VALUE = CMD_BLE_STATE_TIME_STAMP_RECEIVE_SECOND;
            break;
        case CMD_BLE_STATE_TIME_STAMP_RECEIVE_SECOND:
            Time_Second = command;//Set the second
            NRF_LOG_INFO("Receving second");
            CMD_BLE_STATE_VALUE = CMD_BLE_STATE_SELECT_COMMAND;//Finished
            Processing_Commands = false;//Finished processing the
            Convert_time(Time_Year,Time_Month,Time_Day,Time_Hour,Time_Minute,Time_Second);
            NRF_LOG_INFO("Time in unix is: %d ",Unix_Time);
            break;
        case CMD_BLE_STATE_GET_LOG:
            NRF_LOG_INFO("Sending SD card log");
            //Need to send the SD card log here]
            break;
        case CMD_BLE_STATE_STOP://Stop command issued
            NRF_LOG_INFO("Stop state");
            CMD_STARTED = false;//Ready for next command
            CMD_BLE_STATE_VALUE = CMD_BLE_STATE_START;  
            break;
        case CMD_BLE_STATE_ERROR://Stop command issued
            NRF_LOG_INFO("Error state");
            NRF_LOG_INFO("Unknown command:");
            CMD_STARTED = false;//Ready for next command
            CMD_BLE_STATE_VALUE = CMD_BLE_STATE_START;  
            break;
        default:
        // No implementation needed.
        break;
        }
    }
    

  • As indicated in my second reply, you can get the BLE_ERROR_GATTS_INVALID_ATTR_TYPE if the handle you are using is pointing to a characteristic that don't support notifications (char_props.notify).

    This seems to be the case here, since you have char_md.char_props.notify = 0; in your code(custom_value_char_add() function).

Reply Children
No Data
Related