Beware that this post is related to an SDK in maintenance mode
More Info: Consider nRF Connect SDK for new designs
This discussion has been locked.
You can no longer post new replies to this discussion. If you have a question you can start a new discussion

How do you program device to update its own custom characteristic value after a given criteria?

I am still learning how to develop on the nRF52 DK. Nevertheless there are many BLE devices in the market that change their own characteristic / attribute values after connection has been established and some criteria is met. For example, in the following case:

  1. Peripheral and Central Device connect
    1. Peripheral device has 1 custom service with 2 custom characteristics.
      1. Characteristic 1 (char 1) has read, write, and notify.
      2. Characteristic 2 (char 2) has only read and write.
  2. Central Device enables notifications on "char 1".
  3. Central Device writes to "char 2" 4 times in quick succession.
  4. Directly after the 4th write command, the Peripheral Device changes the value of "char 1" to something arbitrary.
  5. Peripheral device sends notification to Central Device that "char 1" has been updated due to notifications for "char 1" being enabled.

In order to try and implement the above functionality, I have been following this guide on getting started with development. However, I am struggling to implement the logic in which char 2 changes after the 4th write command and not before. Any help or guidance would be greatly appreciated. If you require more information, please let me know and I will do my best to provide.

Parents
  • Hi, I would simply set a flag after the 4th receive (do this in the on_write function)

  • I'm almost there! I've added a flag within the on_write() function and the device now does indeed change the characteristic value after the 4th write. However, the value that is written is not the value that I want to change it to.

    Below is my on_write() function:

    static void on_write(ble_cus_t * p_cus, ble_evt_t const * p_ble_evt)
    {
        Number_Of_Updates += 1;
        ble_gatts_evt_write_t const * p_evt_write = &p_ble_evt->evt.gatts_evt.params.write;
        
        // Custom Value Characteristic Written to.
        if (p_evt_write->handle == p_cus->custom_value_handles.value_handle)
        {
            nrf_gpio_pin_toggle(LED_4);
        }
    
        // Check if the Custom value CCCD is written to and that the value is the appropriate length, i.e 2 bytes.
        if ((p_evt_write->handle == p_cus->custom_value_handles.cccd_handle)
            && (p_evt_write->len == 2)
           )
        {
            // CCCD written, call application event handler
            if (p_cus->evt_handler != NULL)
            {
                ble_cus_evt_t evt;
    
                if (ble_srv_is_notification_enabled(p_evt_write->data))
                {
                    evt.evt_type = BLE_CUS_EVT_NOTIFICATION_ENABLED;
                }
                else
                {
                    evt.evt_type = BLE_CUS_EVT_NOTIFICATION_DISABLED;
                }
                // Call the application event handler.
                p_cus->evt_handler(p_cus, &evt);
            }
        }
    
        if (Number_Of_Updates == 4)
        {
            uint8_t value[18] = {0xD3, 0x17, 0x3E, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0xEA};        
            ble_cus_custom_value_update(p_cus, value);
        }
    }

    Near the end of the function you can see my flag that catches the function after 4 updates have been sent. Within this if statement I create the hardcoded value that I want to send. In this case it is D3-17-3E-18-00-00-00-00-00-00-03-01-00-00-00-00-00-EA. However what is actually sent to the device is the following: 7C-1C-71-1D-00-00-00-00-00-00-03-01-00-00-00-00-00-EA.

    What is going on here and how would I go about fixing this?

  • I'm pretty sure the answer to both of those questions is yes.

    I have set a flag in my on_write() function that checks for the 4th write call. Once it finds it, it calls the ble_cus_custom_value_update() function as seen in the code snippet above. Here is my ble_cus_custom_value_update() function:

    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;
        }
    
        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.len     = 18;
        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;
    }

    Is there something I should add to this that I'm missing? After the 4th write, I create the custom value to send the ble_cus_custom_value_update() parameter so I'm pretty sure the values are being stored correctly in hvx_params.p_data.

  • So as far as I can see you only hand over a single custom value to the update function. You need to handle over a pointer to the value array that you have set in your on_write() function.

  • I'm so sorry, I'm really new to this syntax so maybe this is a really stupid question, but how am I only passing a single custom value in the code above? I create the array "value[18] = {...}" and pass it to the function: ble_cus_custom_value_update(p_cus, value). 

    How should I change this function call to make it work? Should it be:
    ble_cus_custom_value_update(p_cus, &value); or
    ble_cus_custom_value_update(p_cus, *value);

    Again I apologize for the dumb question, I'm genuinely still a novice at this.

  • no worries. Those are more C-related questions but yes, the function needs to be declared as ble_cus_custom_value_update(p_cus, *value); 

    the value points to the first address of the array you are handing over. This is necessary to be able to access all elements from the value array inside the update function.

    read more on this here, Im in no position to teach this well enough. www.programiz.com/.../c-pointers-arrays

  • After some more trial and error I eventually got it to work. It was essentially just as you said: I had to adjust my code to not just read the first element due to how arrays and array pointers work in C.

    To anyone that finds this thread, the issue was in the method body of the ble_cus_custom_value_update(): function. See here for the full implementation as well as comments on what was changed to fix it:

    static void on_write(ble_cus_t * p_cus, ble_evt_t const * p_ble_evt)
    {
        Number_Of_Updates += 1;
        ble_gatts_evt_write_t const * p_evt_write = &p_ble_evt->evt.gatts_evt.params.write;
        
        // Custom Value Characteristic Written to.
        if (p_evt_write->handle == p_cus->custom_value_handles.value_handle)
        {
            nrf_gpio_pin_toggle(LED_4);
        }
    
        // Check if the Custom value CCCD is written to and that the value is the appropriate length, i.e 2 bytes.
        if ((p_evt_write->handle == p_cus->custom_value_handles.cccd_handle)
            && (p_evt_write->len == 2)
           )
        {
            // CCCD written, call application event handler
            if (p_cus->evt_handler != NULL)
            {
                ble_cus_evt_t evt;
    
                if (ble_srv_is_notification_enabled(p_evt_write->data))
                {
                    evt.evt_type = BLE_CUS_EVT_NOTIFICATION_ENABLED;
                }
                else
                {
                    evt.evt_type = BLE_CUS_EVT_NOTIFICATION_DISABLED;
                }
                // Call the application event handler.
                p_cus->evt_handler(p_cus, &evt);
            }
        }
    
        if (Number_Of_Updates == 4)
        {
            uint8_t value[18] = {0xAB, 0x17, 0x3E, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0xEA};        
            //THE FOLLOWING FUNCTION CALL WAS ADJUSTED TO PASS A POINTER TO THE ARRAY.
            ble_cus_custom_value_update(p_cus, *value);
        }
    }

    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;
        }
    
        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     = 18;
        gatts_value.offset  = 0;
        
        /**
        * The following line was originally:
        *       gatts_value.p_value = &custom_value;
        * However this made the gatts_value.p_value equal to only the first
        * element of the array. In order for p_value to equal the entire array,
        * simply pass the array with no referencing or dereferencing.
        */
        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;
    }

    Thank you very much for your help!!!

Reply
  • After some more trial and error I eventually got it to work. It was essentially just as you said: I had to adjust my code to not just read the first element due to how arrays and array pointers work in C.

    To anyone that finds this thread, the issue was in the method body of the ble_cus_custom_value_update(): function. See here for the full implementation as well as comments on what was changed to fix it:

    static void on_write(ble_cus_t * p_cus, ble_evt_t const * p_ble_evt)
    {
        Number_Of_Updates += 1;
        ble_gatts_evt_write_t const * p_evt_write = &p_ble_evt->evt.gatts_evt.params.write;
        
        // Custom Value Characteristic Written to.
        if (p_evt_write->handle == p_cus->custom_value_handles.value_handle)
        {
            nrf_gpio_pin_toggle(LED_4);
        }
    
        // Check if the Custom value CCCD is written to and that the value is the appropriate length, i.e 2 bytes.
        if ((p_evt_write->handle == p_cus->custom_value_handles.cccd_handle)
            && (p_evt_write->len == 2)
           )
        {
            // CCCD written, call application event handler
            if (p_cus->evt_handler != NULL)
            {
                ble_cus_evt_t evt;
    
                if (ble_srv_is_notification_enabled(p_evt_write->data))
                {
                    evt.evt_type = BLE_CUS_EVT_NOTIFICATION_ENABLED;
                }
                else
                {
                    evt.evt_type = BLE_CUS_EVT_NOTIFICATION_DISABLED;
                }
                // Call the application event handler.
                p_cus->evt_handler(p_cus, &evt);
            }
        }
    
        if (Number_Of_Updates == 4)
        {
            uint8_t value[18] = {0xAB, 0x17, 0x3E, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0xEA};        
            //THE FOLLOWING FUNCTION CALL WAS ADJUSTED TO PASS A POINTER TO THE ARRAY.
            ble_cus_custom_value_update(p_cus, *value);
        }
    }

    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;
        }
    
        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     = 18;
        gatts_value.offset  = 0;
        
        /**
        * The following line was originally:
        *       gatts_value.p_value = &custom_value;
        * However this made the gatts_value.p_value equal to only the first
        * element of the array. In order for p_value to equal the entire array,
        * simply pass the array with no referencing or dereferencing.
        */
        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;
    }

    Thank you very much for your help!!!

Children
No Data
Related