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!

Parents
  • Hi Mortiz, 

    I don't see any problem doing what you plan to do. 
    You can just call ble_cus_custom_value_update() when you receive the full amount of data buffer. 

    If you have a look at the ble_app_uart example you can find this:

    ble_nus_data_send (which is similar to ble_cus_custom_value_update() ) is called when the index (number of UART data received) is equal or above m_ble_nus_mas_data_len then the GATT update and notification will be sent. 

    As long as the interrupt handler is running at APP_LOW level or lower priority then it should be fine calling a BLE API. 

  • Hi Hung, thanks for your answer.

    As you suggested I implemented the following:

    1. On BLE_CUS_EVT_NOTIFICATION_ENABLED, I call my function generate_data(). I don't call app_timer_start() as it is done in the tutorial.
    2. The generate_data() function populates m_array (a global variable) with random numbers. Once the write_pointer passes the last index, the array is passed to ble_cus_value_update() as shown in the code below.

    static void generate_data()
    {
        ret_code_t err_code;
    
        NRF_LOG_INFO("Starting data generation");
    
        // max length of array
        uint8_t max_len = 244;
        // declare write pointer
        uint8_t write_pointer = 0;
    
        for (;;)
        {
            m_array[write_pointer] = rand() % 100;
            write_pointer++;
    
            if (write_pointer > max_len)
            {
                write_pointer = 0;
    
                err_code = ble_cus_value_update(&m_aas, &m_array);
                APP_ERROR_CHECK(err_code);
            }
        }
    }

    Unfortunately, after entering the if-clause for the first time, calling ble_cus_value_update() and re-entering the for-loop again, the program stops at the NRF_BREAKPOINT_COND (line 100 in app_error_weak.c).

    My guess is that sending the data via BLE has not finished yet when the for-loop is entered again. I tried handling NRF_ERROR_RESOURCES as shown in your screenshot above, but so far without any success.

    Do you have any idea what might be wrong and how I could fix this?

  • Hi Hung,

    I'm calling generate_data() from on_cus_evt() the main.c file and I call it when the BLE_CUS_EVT_NOTIFICATION_ENABLED event is received (see also line 395 here: https://github.com/NordicPlayground/nRF52-Bluetooth-Course/blob/master/main.c).

    After passing the array to ble_cus_value_update(), I don't receive any error (error code 0). I implemented it now this way:

    static void generate_data()
    {
        ret_code_t err_code;
    
        NRF_LOG_INFO("Starting data generation");
    
        // max length of array
        uint8_t max_len = 243;
        // declare write pointer
        uint8_t write_pointer = 0;
    
        for (;;)
        {
            while (write_pointer <= max_len)
            {
                m_array[write_pointer] = rand() % 100;
                write_pointer++;
            }
    
            if (write_pointer >= max_len)
            {
                do 
                {
                    err_code = ble_aas_value_update(&m_aas, &m_array);
                    APP_ERROR_CHECK(err_code);
                    nrf_delay_ms(10);
                } while (err_code == NRF_ERROR_RESOURCES);
    
                write_pointer = 0;
            }
        }
    }
    

    The APP_ERROR_CHECK(err_code) still leads to the NRF_BREAKPOINT_COND (line 100 in app_error_weak.c). If I outcomment the APP_ERROR_CHECK(err_code), the code keeps running, but I only receive the first data block (afterwards, I don't receive anything new).

    Any ideas by what this could be caused?

  • Hi Mortitz, 

    So you are calling generate_data() from an interrupt handler ? 
    It's not a good practice since you have an infinite loop inside generate_data() this will keep the CPU running in interrupt handler context and won't be able to pull more events from the softdevice. 


    If you want to do an infinite loop you should better do that inside main context. 
    It's better to stop sending when you receive NRF_ERROR_RESOURCES and then continue when you receive BLE_GATTS_EVT_HVN_TX_COMPLETE  event. 

  • 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;
                  
                }
            }
        }    
    }

Reply
  • 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;
                  
                }
            }
        }    
    }

Children
No Data
Related