Sequential BLE messages with "ble_nus_c_string_send"

Hello there!

I need to send via BLE NUS an array of at least 3000 values (coming from ADC sampling) from a central device to a peripheral.

I saw that in order to use "ble_nus_c_string_send" I need to write the values in a string and the maximum length of the string is BLE_NUS_MAX_DATA_LEN.

So, the idea is split the string in substring with lenght BLE_NUS_MAX_DATA_LEN and send all of them consequently.

The sysyem crash with errore 0x13.

I think that it is a problem of queue of the BLE messages (the SoftDevice should handle the queue autonomously).

I also try to insert a delay between each call of ble_nus_c_string_send (nrf_delay_ms(100)), without any success.

Any idea?

The snipped code is inside a custom function so I think I can't exploit BLE_EVT_TX_COMPLETE flag.

uint8_t iterations = (storage_lenght->packet_lenght) / BLE_NUS_MAX_DATA_LEN;
uint8_t reminder = (storage_lenght->packet_lenght) % BLE_NUS_MAX_DATA_LEN;
for (int i=0; i<iterations; i++)
{
    do
    {
    ret_val = ble_nus_c_string_send(m_ble_nus_, TempStorageBuffer_PacketCounter+(BLE_NUS_MAX_DATA_LEN*i), BLE_NUS_MAX_DATA_LEN);
    if ((ret_val != NRF_ERROR_INVALID_STATE) &&
    (ret_val != NRF_ERROR_RESOURCES) &&
    (ret_val != NRF_SUCCESS) &&
    (ret_val != NRF_ERROR_BUSY) &&
    (ret_val != NRF_ERROR_NOT_FOUND))
    {
    APP_ERROR_CHECK(ret_val);
    }
    } while (ret_val == NRF_ERROR_BUSY || ret_val == NRF_ERROR_RESOURCES);
}

Parents
  • Hi

    I need to send via BLE NUS an array of at least 3000 values (coming from ADC sampling) from a central device to a peripheral.

    How many packets will this be split into when you run the test?

    The sysyem crash with errore 0x13.

    Which function call returns this error?

    0x13 corresponds to the NRF_ERROR_RESOURCES error, which typically happens when the buffers in the SoftDevice are full, but you would expect to get this error when calling ble_nus_c_string_send(..), since you are sending a lot of data at once. Also, I notice you are not calling APP_ERROR_CHECK() if ble_nus_c_string_send() returns NRF_ERROR_RESOURCES, so I assume it must be some other function that returns this?

    The snipped code is inside a custom function so I think I can't exploit BLE_EVT_TX_COMPLETE flag.

    I am not sure I understand this comment. As long as you can add some custom code to the BLE_EVT_TX_COMPLETE handler it should be fine to use this mechanism. 

    Running the ble_nus_c_string_send() in a loop like you do is a bit risky, since it will block other code from being able to run. I hope this code is not running from an interrupt context?

    Best regards
    Torbjørn

  • Hi Torbjørn!

    So

    How many packets will this be split into when you run the test?

    My typical array is from 5k to 10k bytes, so i need at least 16-17 different packets to be sent with ble_nus_c_string_send.

    but you would expect to get this error when calling ble_nus_c_string_send(..), since you are sending a lot of data at once. Also, I notice you are not calling APP_ERROR_CHECK() if ble_nus_c_string_send() returns NRF_ERROR_RESOURCES, so I assume it must be some other function that returns this?

    Yes, the error comes from ble_nus_c_string_send, more precisely it comes from 

    I am not sure I understand this comment. As long as you can add some custom code to the BLE_EVT_TX_COMPLETE handler it should be fine to use this mechanism. 

    I don't get your point. I am in a custom function where I have only the peripheral connection handler as an argument (m_ble_nus_), so I don't know how to exploit BLE_EVT_TX_COMPLETE.

    Running the ble_nus_c_string_send() in a loop like you do is a bit risky, since it will block other code from being able to run. I hope this code is not running from an interrupt context?

    The function is inside a timer interrupt handler. Actually, I need to send these packets when a timer expires.

    Thanks!

  • Hi

    AS_Kalpa said:
    I don't get your point. I am in a custom function where I have only the peripheral connection handler as an argument (m_ble_nus_), so I don't know how to exploit BLE_EVT_TX_COMPLETE.

    Can't you add a second custom function that is responsible for continuing a transfer, that you in turn can call from the BLE_EVT_TX_COMPLETE handler?

    Essentially you need one function that the application can use to start a transaction, and a second function that the stack can use to continue a transaction. 

    In order for this to work you need some global status variables that can be shared between the two functions, in order to synchronize how many packets are left, where the data is stored etc. 

    AS_Kalpa said:
    The function is inside a timer interrupt handler. Actually, I need to send these packets when a timer expires.

    This could be the issue. Are you able to try and run your function from the main context instead and see if it works better?

    Either just set a flag in the timer handler that you can check in the main loop, or use the app_scheduler library to schedule a function to be run from main. 

    Best regards
    Torbjørn

Reply
  • Hi

    AS_Kalpa said:
    I don't get your point. I am in a custom function where I have only the peripheral connection handler as an argument (m_ble_nus_), so I don't know how to exploit BLE_EVT_TX_COMPLETE.

    Can't you add a second custom function that is responsible for continuing a transfer, that you in turn can call from the BLE_EVT_TX_COMPLETE handler?

    Essentially you need one function that the application can use to start a transaction, and a second function that the stack can use to continue a transaction. 

    In order for this to work you need some global status variables that can be shared between the two functions, in order to synchronize how many packets are left, where the data is stored etc. 

    AS_Kalpa said:
    The function is inside a timer interrupt handler. Actually, I need to send these packets when a timer expires.

    This could be the issue. Are you able to try and run your function from the main context instead and see if it works better?

    Either just set a flag in the timer handler that you can check in the main loop, or use the app_scheduler library to schedule a function to be run from main. 

    Best regards
    Torbjørn

Children
  • Hi!

    Can't you add a second custom function that is responsible for continuing a transfer, that you in turn can call from the BLE_EVT_TX_COMPLETE handler?

    Essentially you need one function that the application can use to start a transaction, and a second function that the stack can use to continue a transaction. 

    In order for this to work you need some global status variables that can be shared between the two functions, in order to synchronize how many packets are left, where the data is stored etc. 

    So, I tried to have a look in my code to implement your suggestion.

    I found this one

    I think this is the function you mean when you refer to "BLE_EVT_TX_COMPLETE handler".

    At the moment, there is no implementation for the BLE_EVT_TX_COMPLETE case so I have to implement it from scratch I think.

    I noticed that I can not use BLE_EVT_TX_COMPLETE but I think I should use BLE_GATTC_EVT_WRITE_CMD_TX_COMPLETE, am I wrong?

    Just a question, when would the BLE_GATTC_EVT_WRITE_CMD_TX_COMPLETE case fire? Only with custom sendings (like the ones I do with ble_nus_c_string_send) or also with the protocol sendings from stack (like for example the keep alive messages sent directly by SoftDevice)? In this case, I need a strategy to filter out only the interrupts I want (the ones produced by ble_nus_c_string_send).

    Thanks!

  • Hi

    Yes, in the ble_evt_handler() function you can add more cases, in order to handle different types of events coming from the SoftDevice. 

    I agree that in your case the right event to wait for would be the BLE_GATTC_EVT_WRITE_CMD_TX_COMPLETE event, since in this case you are sending write commands from the client side, rather than notifications from a server. 

    AS_Kalpa said:
    Just a question, when would the BLE_GATTC_EVT_WRITE_CMD_TX_COMPLETE case fire?

    This event will only fire when write commands from the GATT client has been sent, and this only happens if the application sends write without response packets on one of the services that support it. In the standard ble_app_uart_c example this should only happen if you call ble_nus_c_string_send(..), but in theory you could have multiple different commands sending write packets on different characteristics and services. 

    In either case it is not very important why the event occurs. Once this event occurs you know that there are more buffers available in the SoftDevice, which means this is a good time to try and upload more data to the stack. 

    Best regards
    Torbjørn

Related