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

Create custom event to trigger updating data in GATT table

Hi,

I have implemented the nRF5x-Custom-BLE-Service-Tutorial (https://github.com/NordicPlayground/nRF5x-custom-ble-service-tutorial) using nRF5 SDK 17.1.0 on my nRF52 DK using Segger Embedded Studio. The used Softdevice is S132.

At the end of the tutorial, a timer is implemented to call a function (ble_cus_custom_value_update()) at a regular interval to update the data in the GATT table and send a notification to the peer.

I would like to change that procedure, so that ble_cus_custom_value_update() is called when a specific event occurs, and not by the timer. In particular, I want to fill a buffer with data and as soon as the buffer is full, I want to raise an event and pass the content of the buffer to the ble_cus_custom_value_update() that will insert the data into the GATT table.

Unfortunately, I couldn't find any tutorial or question that demonstrates how to implement that functionality. Could somebody point me towards the right resources or suggest how I can approach this issue?

Thanks a lot in advance!

  • Hi Hung,

    sorry for the late response. I was out of office for some time and just found time today to continue working on that.

    I moved the loop into my main function as you suggested. I can now send some packages (usually around 4), but then the app stops with the following error message:

    <error> app: ERROR 19 [NRF_ERROR_RESOURCES] at /home/engineer/nRF-Dev/nRF5_SDK_17.1.0_ddde560/examples/ble_peripheral/surag_sense/main.c:906
    PC at: 0x00032F6F
    <error> app: End of error report

    Here is what I did:

    1. I added a new case to the ble_evt_handler in main.c:

    case BLE_GATTS_EVT_HVN_TX_COMPLETE:
                NRF_LOG_INFO("transfer complete");
                ble_ready = true;
                break;

    ble_ready is initially false and set to true as soon as the event is received. However, the "transfer complete" doesn't appear in the debugging window, so I wonder if this event is received properly?

    2. my loop in the main function looks like that now.

        // max length of array
        uint8_t max_len = 243;
        // declare write pointer
        uint8_t write_pointer = 0;
    
        // Enter main loop.
        for (;;)
        {
            idle_state_handle();
    
            if (i2s_transfer) 
            {
              NRF_LOG_INFO("Starting data generation");
              while (write_pointer <= max_len)
              {
                  m_array[write_pointer] = rand() % 100;
                  write_pointer++;
              }
    
              if (write_pointer >= max_len)
              {
                  err_code = ble_aas_value_update(&m_aas, &m_array);
                  APP_ERROR_CHECK(err_code);
    
                  if (err_code == NRF_ERROR_RESOURCES)
                  {
                      while (ble_ready != true)
                      {
                          nrf_delay_ms(10);
                      }
                  }
                  
                  write_pointer = 0;
                  
              }
            }
        }   

    i2s_transfer is another flag that is set to true as soon as notifications are enabled by the peer. Subsequently, the m_array is filled with data. As soon as the array is filled, I'm sending the data. If the returned err_code is NRF_ERROR_RESOURCES, I'm waiting until the ble_ready is set to true (which should happen when the BLE_GATTS_EVT_HVN_TX_COMPLETE is received).

    I'm new to BLE and event-based programming, so I'm sure that there are better approaches to handle that. Do you have any idea why I'm only able to send so little packages and the receive that error? How can I change my code to handle such errors so that it works?

    EDIT:
    I checked again and it seems I'm receiving the BLE_GATTS_EVT_HVN_TX_COMPLETE (the "transfer complete") is printed. But if I put a breakpoint inside the if-clause that checks err_code == NRF_ERROR_RESOURCES, it's never reached. So it seems the error is raised by something else. So far I wasn't able to track that down.

    EDIT2:

    I realized now that the NRF_ERROR_RESOURCES occurs when calling APP_ERROR_CHECK(err_code) (line 23 in the code above). When I comment out that line (what I don't want to do), the code works. So how can I resolve this?

  • Hi Moritz, 
    You are getting there. 

    The APP_ERROR_CHECK(err_code)  should only be used for err_code that are critical and the operation should be stopped. 
    In your case NRF_ERROR_RESOURCES only means that the buffer of the stack is full. So you don't need to call APP_ERROR_CHECK() on it. Usually we we do this: 

    In addition you may not want to use nrf_delay_ms(10) because it will just simply keep the CPU in busy loop and not putting it to sleep. You will end up having high current consumption. Instead you can put the CPU to sleep using  idle_state_handle();

    You don't even need the  while (ble_ready != true), you can use  if (ble_ready != true) then put the CPU to sleep with idle_state_handle();. The CPU will automatically wake up and continue when you receive BLE_GATTS_EVT_HVN_TX_COMPLETE  event. 

  • Hi Hung,

    one last question: if the stack is full (err_code = NRF_ERROR_RESOURCES) will this not result in a loss of data?

    Thanks a lot for your support. It's working and you helped me to understand what needs to be done. I need to see if this approach achieves the required throughput and then add i2s to this approach, since I need to send audio data. But I think it's more meaningful to open another thread for that.

    Here is the final version of my main() for the reference of others (based on the BLE template app in the SDK examples):

    /**@brief Function for application main entry.
     */
    int main(void)
    {
        bool erase_bonds;
        uint8_t err_code;
    
        // Initialize.
        log_init();
        timers_init();
        buttons_leds_init(&erase_bonds);
        power_management_init();
    
        // Initialize BLE
        ble_stack_init();
        gap_params_init();
        gatt_init();
        services_init();
        advertising_init();
        conn_params_init();
        peer_manager_init();
    
        // Start execution.
        NRF_LOG_INFO("Template example started.");
    
        // Start advertising
        advertising_start(erase_bonds);
    
        // max length of array
        uint8_t max_len = 243;
        // declare write pointer
        uint8_t write_pointer = 0;
    
        // Enter main loop.
        for (;;)
        {
            if (i2s_transfer) 
            {
                while (write_pointer <= max_len)
                {
                    m_array[write_pointer] = rand() % 100;
                    write_pointer++;
                }
    
                if (write_pointer >= max_len)
                {
                    err_code = ble_aas_value_update(&m_aas, &m_array);
                    if ((err_code != NRF_SUCCESS) &&
                        (err_code != NRF_ERROR_INVALID_STATE) &&
                        (err_code != NRF_ERROR_RESOURCES) &&
                        (err_code != NRF_ERROR_BUSY) &&
                        (err_code != BLE_ERROR_GATTS_SYS_ATTR_MISSING)
                       )
                    {
                        APP_ERROR_CHECK(err_code);
                    }
    
                    if (err_code == NRF_ERROR_RESOURCES)
                    {
                        NRF_LOG_DEBUG("error resources");
                        if (ble_ready != true)
                        {
                            idle_state_handle();
                        }
                        ble_ready = false;
                    }
                  
                    write_pointer = 0;
                  
                }
            }
        }    
    }

Related