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

use Uart sample to TX/RX long data > 20 bytes ?

Hello,

Two issues want to ask, after tried the BLE_NUS example code. From other posts, still does not exist clear solution (example, pictures) ? Sure has an application note, document guide for such problems.

  1. TX character ,received data from client (MCP) want to support get more data > 20 bytes . I tried to use the "Request(queued) write" property for the TX characteristic but still only get data bytes less than 20 bytes ? FW code should be fine (from the sample of ble_app_hrs---LongWrite) , could be the Android or MCP problem ?

  2. RX character, send data to client (MCP), want to support send more data > 20 bytes. I tried to add "Read" property in addition to the notify property of RX characteristic.

It seems the sd_ble_gatts_value_set() will split data to multiple chunk automatically, but it require manual to read data by MCP or APP. I still prefer to use the notify method, but has big problem if many data need to send. Getting the error code 0x3004, and BLE reset(error) because I am transmitting data too quickly ? I tried to use the TX_COMPLETE event to control the in/out data flow but still fail. Here is my test code for the problem:

static volatile bool tx_buffer_is_free=true;                                                         void uart_event_handle(app_uart_evt_t * p_event) {
static uint8_t data_array[BLE_MAX_DATA_LEN];
static uint8_t index = 0;
uint32_t       err_code;

switch (p_event->evt_type)
{
    case APP_UART_DATA_READY:
        if (tx_buffer_is_free)
		UNUSED_VARIABLE(app_uart_get(&data_array[index]));
    else
		break;
        index++;
        // for test each byte notify
        //if ((data_array[index - 1] == '\n') || (index >= (BLE_MAX_DATA_LEN))) 
        {
           
            tx_buffer_is_free=false;
            err_code = ble_nus_string_send(&m_nus, data_array, index);
            if (err_code != NRF_ERROR_INVALID_STATE)
            {
                APP_ERROR_CHECK(err_code);
            }
            if (err_code == NRF_SUCCESS)
                  tx_buffer_is_free=true;

            index = 0;
        }
        break;

    case APP_UART_COMMUNICATION_ERROR:
        APP_ERROR_HANDLER(p_event->data.error_communication);
        break;

    case APP_UART_FIFO_ERROR:
        APP_ERROR_HANDLER(p_event->data.error_code);
        break;

    default:
        break;
}  }   

 static void on_ble_evt(ble_evt_t * p_ble_evt) {
	
uint32_t                         err_code;

switch (p_ble_evt->header.evt_id)
{
	case BLE_EVT_TX_COMPLETE:
		tx_buffer_is_free=true;
		break;
         .......
        .......
}}

When I sent more than 8 bytes of data in one time from Uart console, BLE_NUS will reset. below 8 is ok.

What's the right way to handle such problem (sent as many bytes through notification) over ble_nus ?

Appreciate any response , thanks.

  • FormerMember
    0 FormerMember

    1) For regular write operations, it is easier and faster to use write commands than queued writes. The purpose of queued writes is the following:

    This is normally not a function that you want to use, especially if high bandwidth is crucial in your application. Here's the explanation of this feature from the Core v4.2 specification:

    The purpose of queued writes is to queue up writes of values of multiple attributes in a first-in first-out queue and then execute the write on all of them in a single atomic operation.

    This function is aimed towards altering several attributes in a "big bang" update, like disabling/enabling all notifications in an application. Think synchronous motor applications, where you can get a signal to turn off all motors with the same event instead of waiting for several events and handling this start/stop in the application state machine.

    A queued write operation on-air consists of two commands:

    • prepare write

    • execute write

    The central will do a given number of "prepare write" command over-air, and finally "execute write".

    Underneath the hood, this has more overhead compared to manually segmenting your buffer into 20-byte chunks and sending them like a normal write command.

    Each "prepare write" will consist of a header with opcode/handle/value offset, which reduces your overall throughput.

    For a regular write command, the maximum amount of data is 20 bytes, and for the prepare write (queued write) it is 18 bytes ( Bluetooth core specification v.4.2, vol 3, part F, chapter 3.4.5.3 and 3.4.6.1). I would therefore recommend you to use regular write commands instead of queued writes.

    2) As defined in the Bluetooth core specification v.4.2, vol 3, part F, chapter 3.4.7.1, the maximum amount of data per notification is 20 bytes. The fastest way to transmit notifications is to loop over sd_ble_gatts_hvx(..) as long as it doesn't return any error. It may be useful to take a look at this post as well regarding BLE_EVT_TX_COMPLETE.

    The reason for the reset can be that there is an error handled by APP_ERROR_HANDLER. I would recommend you to run the chip in debug mode and put a breakpoint where the chip receives the data from the UART and see if you can find the source of the error. I would also recommend you to take a look at this thread regarding chip is resetting.

    Update 24.06.16: It should not be any difference between using BLE_GATTS_VLOC_STACK or BLE_GATTS_VLOC_USER.

    When using queued writes, all the data is transferred with a set of prepare write requests, but the data is not available to the application before the event BLE_EVT_USER_MEM_RELEASE. At the event BLE_EVT_USER_MEM_RELEASE, all the transferred data is available to the application at the same time. With that in mind, the introduction in the core specification makes a little more sense(?): The purpose of queued writes is to queue up writes of values of multiple attributes in a first-in first-out queue and then execute the write on all of them in a single atomic operation.

    After the event BLE_EVT_USER_MEM_RELEASE, the data is available in the memory block provided in sd_ble_user_mem_reply(..)

    Update 21.06.16:

    1) Could you try an unmodified version of ble_app_hrs--LongWrite and check if you receive all the data the data that you send? When I do it here, I receive all the data. Do you have an extra nRF51-DK or nRF51 Dongle? If so, could you use the sniffer to track what is being transferred over the air? And upload the sniffer trace here?

    The sniffer should be used with Wireshark, and it works best with version 1.10, not one of the newer versions. Wireshark can be downloaded here:

    When the queued write occurs it should look similar to this: image description

    2) Which function returns NRF_ERROR_NO_MEM?

    3) It should work fine to loop over sd_ble_gatts_hvx(..) and still receive UART events, as long as the context where sd_ble_gatts_hvx(..) is being called has lower priority than the UART events. This example on github shows how to transfer data fast, in data_send(). The example was made for an old version of the SDK. However, the principle will still be the same.

  • Hi Kristin , Thanks your explanation. In my 1st question, you clear explained the purpose of write command and write request , but my problem is always only got 18 bytes when I used the write queues attribute if I sent 43 bytes data through the MCP. I have debug output in the BLE_GATTS_EVT_WRITE, BLE_EVT_USER_MEM_REQUEST and BLE_EVT_USER_MEM_RELEASE events to know these event happened , but only happened one time in each event. also only one chunk(offset 0, 18bytes) in the queue. I can't figure out what's wrong ? Is it MCP or Android problem ? or my FW code still not the right way to handle queued write ? (from the sample code of ble_app_hrs---LongWrite). And asking the advise from experts.

    The 2nd question: I did the debug to know the error (0x3004 NRF_ERROR_NO_MEM ?) meaning, a tried to use a flag and BLE_EVT_TX_COMPLETE event to avoid that but still failure. That's why I wan t to know the proper method to deal such issue and tried to use a flag and BLE_EVT_TX_COMPLETE event to avoid that but still failure. That's why I wan t to know the proper methods to detail such issue. Use the loop over sd_ble_gatts_hvx(..) as long as it doesn't return any error seems not a good method seems it will block the process of uart_event_handle() , any comments ?

  • FormerMember
    0 FormerMember in reply to FormerMember

    I have updated the answer to answer your questions.

  • Hi Kristin,

    Thanks again, I can try but I don't have nRF51 Dongle, I just have pca10028 and MCP. about 1st question, "long write failure", I check the sample code "ble_app_hrs---LongWrite" , it has R/W attribute of "rate_measurement" characteristic and use BLE_GATTS_VLOC_STACK, but I didn't find code that moving data from queued buffer to the buff of character, why ?

    case BLE_EVT_USER_MEM_REQUEST:

            mem_block.len = QUEUED_WRITE_BUFFER_SIZE;
            mem_block.p_mem = &queued_write_buffer[0];
            err_code = sd_ble_user_mem_reply(m_conn_handle, &mem_block);
    					//simple_uart_putstring("User mem request \r\n");
            break;
    

    case BLE_EVT_USER_MEM_RELEASE:

            if ((p_ble_evt->evt.common_evt.params.user_mem_release.mem_block.p_mem == mem_block.p_mem)&&(p_ble_evt->evt.common_evt.params.user_mem_release.mem_block.len == mem_block.len))
            {
                //memory released do nothing. 
    	//simple_uart_putstring("User mem released \r\n");
            }
    					break;
    case BLE_GATTS_EVT_WRITE:
    	//simple_uart_putstring("Evt Write \r\n");
                // should have code moving data from queue to character buffer ?
            break;
        default:
            // No implementation needed.
            break;
    

    also , I use BLE_GATTS_VLOC_STACK not BLE_GATTS_VLOC_USER , any different for the long write usage ? I still can't figure out how the SD fill the queued buffer from long write request of peer ? (through sd_ble_user_mem_reply() ? and how many data and start where in queue (offset) ? and application how to know and data receiving finished (from BLE_GATTS_EVT_WRITE event and p_evt_write->op == BLE_GATTS_OP_EXEC_WRITE_REQ_NOW ) ? and how many data received for this characteristic in the queue ? , there is no explanation in the sample code ( ble_app_hrs---LongWrite).

    Thanks!

  • FormerMember
    0 FormerMember in reply to FormerMember

    I have updated the answer to answer your question.

Related