Peripheral UART - 20 Byte Limitation

Hi folks,

I'm making an application which needs to transmit a few kB of config data (for a configuration utility), and I was hoping to do it via UART.

To that end, I loaded up the peripheral uart sample (nrf/samples/bluetooth/peripheral_uart), and I'm running into a limitation regarding a 20-Byte maximum transmission.

How can I increase that limit? Alternatively, how can I configure the UART to send batches of 20-Bytes one-at-a-time?

---------

In terms of question 1: "how can I increase that limit?"

I've integrated config options from the BLE throughput sample to increase the MTU.

I've increased the CONFIG_UART_BUFFER_SIZE.

I've read most of the posts about the 20 byte limit and all seem to circle around to adjusting the MTU.

In terms of question 2: "How can I send my large transmission in batches?"

I had hoped fifo buffers in this sample would take care of that automatically.

When they didn't, I followed the instructions in the README.rst and enabled the async module, which caused more problems.

-----------

I'm worried I'm going about this in a convoluted way, so I'd like to ask from a blank slate: what's the right way to transmit a few kB of data in and out of my device?

My environment is: 

BOARD = nrf52840dk_nrf52840

nRF Connect Toolchain v2.5.0

(in vsCode, testing BT behaviors with nRF Connect for Android, building in a Windows 11 environment via nRF Connect CLI build commands)

As always, I appreciate your time and am grateful for your support.

Best,

    - Finn

Parents
  • n peripheral_uart sample, it looks like CONFIG_BT_NUS_UART_BUFFER_SIZE plays a role in fifo size used to save data while BT or UART is busy. Need to reconfigure this size aswell to get bigger chunk transfers.

    To increase the limit of data you can send over BLE, you can adjust the Maximum Transmission Unit (MTU) size. The MTU size determines the maximum amount of data that can be sent in a single packet. By default, the MTU size is set to 23 bytes, which includes the L2CAP header of 4 bytes, leaving 20 bytes for payload. However, the MTU size can be increased up to 247 bytes.
    In the prj.conf file of your project, you can add or modify the following configuration options:
    CONFIG_BT_USER_DATA_LEN_UPDATE=y
    CONFIG_BT_L2CAP_TX_MTU=247
    CONFIG_BT_BUF_ACL_TX_SIZE=251
    CONFIG_BT_BUF_ACL_RX_SIZE=251
    CONFIG_BT_CTLR_DATA_LENGTH_MAX=251
    This will increase the MTU size to 247 bytes, allowing you to send more data in a single packet.
    Regarding your second question, if you want to send data in batches, you can use the UART driver's asynchronous API. This API provides functions for sending and receiving data in a non-blocking manner. You can use the uart_tx function to send data, and the uart_callback_set function to set a callback that will be called when the transmission is complete. This way, you can send the next batch of data in the callback function.
    Here is an example of how you can use the asynchronous API:
    struct uart_data_t *buf;
    
    buf = k_malloc(sizeof(*buf));
    if (buf) {
    buf->len = 0;
    } else {
       LOG_WRN("Not able to allocate UART receive buffer");
       k_work_reschedule(&uart_work, UART_WAIT_FOR_BUF_DELAY);
       return;
    }
    
    uart_rx_enable(uart, buf->data, sizeof(buf->data), UART_WAIT_FOR_RX);
    In this example, a buffer is allocated and then passed to the uart_rx_enable function, which starts the reception of data. When the reception is complete, the callback function set with uart_callback_set will be called.
    Please note that the actual implementation may vary depending on your specific requirements and the structure of your code.

Reply
  • n peripheral_uart sample, it looks like CONFIG_BT_NUS_UART_BUFFER_SIZE plays a role in fifo size used to save data while BT or UART is busy. Need to reconfigure this size aswell to get bigger chunk transfers.

    To increase the limit of data you can send over BLE, you can adjust the Maximum Transmission Unit (MTU) size. The MTU size determines the maximum amount of data that can be sent in a single packet. By default, the MTU size is set to 23 bytes, which includes the L2CAP header of 4 bytes, leaving 20 bytes for payload. However, the MTU size can be increased up to 247 bytes.
    In the prj.conf file of your project, you can add or modify the following configuration options:
    CONFIG_BT_USER_DATA_LEN_UPDATE=y
    CONFIG_BT_L2CAP_TX_MTU=247
    CONFIG_BT_BUF_ACL_TX_SIZE=251
    CONFIG_BT_BUF_ACL_RX_SIZE=251
    CONFIG_BT_CTLR_DATA_LENGTH_MAX=251
    This will increase the MTU size to 247 bytes, allowing you to send more data in a single packet.
    Regarding your second question, if you want to send data in batches, you can use the UART driver's asynchronous API. This API provides functions for sending and receiving data in a non-blocking manner. You can use the uart_tx function to send data, and the uart_callback_set function to set a callback that will be called when the transmission is complete. This way, you can send the next batch of data in the callback function.
    Here is an example of how you can use the asynchronous API:
    struct uart_data_t *buf;
    
    buf = k_malloc(sizeof(*buf));
    if (buf) {
    buf->len = 0;
    } else {
       LOG_WRN("Not able to allocate UART receive buffer");
       k_work_reschedule(&uart_work, UART_WAIT_FOR_BUF_DELAY);
       return;
    }
    
    uart_rx_enable(uart, buf->data, sizeof(buf->data), UART_WAIT_FOR_RX);
    In this example, a buffer is allocated and then passed to the uart_rx_enable function, which starts the reception of data. When the reception is complete, the callback function set with uart_callback_set will be called.
    Please note that the actual implementation may vary depending on your specific requirements and the structure of your code.

Children
  • Hi Susheel,

    Thanks for getting back to me!

    Unfortunately, there may be more than just enabling the KConfig MTU options involved in fixing this.

    Before my first post, I experimented with the config options that seemed to make sense until I got frustrated with my lack of progress. I followed the options within the ble_throughput sample and didn't manage to succeed at increasing the allowed send size.

    -----------------------------------

    To that end, please find attached my application "peripheral_uart_ext (zip)"

    This application is the peripheral_uart sample, modified to include the config options for increasing MTU size. I also organized the prj.conf differently than in the sample.

    Checking the autoconf.h after building, I find that the MTU size is updated. In this sample, one cannot send more than 20 bytes from app-> remote, and cannot send more than 20 bytes from remote -> app, which is unchanged by the MTU KConfig options.

    Grateful for advice on how to proceed.

    ---------------------------------

    Application build instructions

    • zip file contains source and config options
    • west build command contained in scripts/dk.sh
      • (either run this command on CLI from nrfConnect terminal, or use information within for build configuration options)

    Behavior replication instructions

    • Build and flash attached application to nrf52840dk
    • Connect to NORDIC_UART_SERVICE using external application
      • I used nRF Connect Android App
    • Subscribe to Nordic UART Service TX Characteristic
    • Send string of 20 characters to RX Characteristic
      • Observe successful send to device and print to logging
    • Send string of 21+ characters to RX characteristic
      • Observe failure to send
      • App Logs Error: "Error 6 (0x6): GATT REQ NOT SUPPORTED"
    • Open Serial connection to Dev Kit
    • Send 20 char string (18 + '\r' + '\n')
      • Observe success of transfer to external application (TX Characteristic)
    • Send 21+ char string
      • Observe failure to send
      • Example warnings from sending following string "0000000000000000000\r\n" (19 zeros, \r\n = 21 char)
      • <wrn> bt_att: No ATT channel for MTU 24
        <wrn> bt_gatt: No buffer available to send notification
        <wrn> peripheral_uart: Failed to send data over BLE connection

    ----------------------------------

    Did you find success with the modifications you suggested? If so, I wonder if something else is wrong with my app setup, build system, or environment.

    Thank you for your advice and time.

    Best,

        - Finn

    peripheral_uart_ext.zip

  • Hi again Susheel,

    I've located what I was missing.

    I needed to either:

    • Add MTU negotiation to the .connected callback
    • OR
    • Add
      • # Auto negotiate MTU on connection
        CONFIG_BT_GATT_CLIENT=y
        CONFIG_BT_GATT_AUTO_UPDATE_MTU=y

    I opted to add the two config options above. Now, my sniffer trace indicates that the MTU negotiations happen immediately on connection, and my messages are sent as blocks limited by the option

    • # UART Chunk Size (40 original --> increase)
      CONFIG_BT_NUS_UART_BUFFER_SIZE=80

    Thank you very much for looking at my post -- I'll mark this reply as a solution and hope this helps other people who encounter this in the future.

    Enjoy your week!

    Best,

        - Finn

Related