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

Slow Transfer - Calling ble_nus_string_send multiple times to send a large array

Hi all,

I am using PCA10040 with nRF5_SDK_14.2.0 and using the ble_app_uart example.

Basically I have a large array of size 18750 to send out and having read on the forum, I understand that I need to split up the data into smaller chunks and call ble_nus_string_send multiple times. Most people manage to get it to work by splitting into chuck of 20 bytes.

What I have tried so far is that I can provide a 150 byte array into the ble_nus_string_send function without any problem and its receiving well on a mobile phone. I also made a few changes:

#define MIN_CONN_INTERVAL MSEC_TO_UNITS(7.5, UNIT_1_25_MS) /**< Minimum acceptable connection interval (20 ms), Connection interval uses 1.25 ms units. */
#define MAX_CONN_INTERVAL MSEC_TO_UNITS(7.5, UNIT_1_25_MS) /**< Maximum acceptable connection interval (75 ms), Connection interval uses 1.25 ms units. */

Inside the gatt_init function, I also increase the max MUT size to 247:

err_code = nrf_ble_gatt_att_mtu_periph_set(&m_gatt, 247);

This is what I did in the main function:

// Enter main loop.
    for (;;)
    {
        UNUSED_RETURN_VALUE(NRF_LOG_PROCESS());
        power_manage();
			
				if(start_transfer)
				{
					start_transfer = false;
					uint16_t length = 150;
					uint8_t j = 0;
					uint16_t k = 0;
					for(j=0; j<125; j++)
					{
						do
						{	
							err_code = ble_nus_string_send(&m_nus, mybuffer+k, &length);
							if ( (err_code != NRF_ERROR_INVALID_STATE) && (err_code != NRF_ERROR_BUSY) )
							{
								APP_ERROR_CHECK(err_code);
							}
						} while (err_code == NRF_ERROR_BUSY);
						k = k + 150;
						while(!tx_complete);
						tx_complete = false;
					}
					
				}
    }

Each call to ble_nus_string_send, I am sending out 150 bytes, and I go this in a for loop that iterates for 125 times, so essentially I will transfer all 18750 bytes. After each call to ble_nus_string send, I am checking a flag tx_complete , which is set to true when a BLE_GATTS_EVT_HVN_TX_COMPLETE event happens. 

case BLE_GATTS_EVT_HVN_TX_COMPLETE:
	tx_complete = true;
	break;

This is working well except that the transfer seems to be still fairly slow about 19-20 seconds to send everything over. Is there anything I am doing wrong? How can I make this go much more faster?

Parents
  • New updates:

    I am able to achieve must faster transfer using the following parameters:

    ********************************

    #define MIN_CONN_INTERVAL MSEC_TO_UNITS(8, UNIT_1_25_MS) /**< Minimum acceptable connection interval (20 ms), Connection interval uses 1.25 ms units. */
    #define MAX_CONN_INTERVAL MSEC_TO_UNITS(20, UNIT_1_25_MS) /**< Maximum acceptable connection interval (75 ms), Connection interval uses 1.25 ms units. */

    err_code = nrf_ble_gatt_att_mtu_periph_set(&m_gatt, 64);

    ******************************

    Knowing that the data length has been updated to 123 from the debug output, I send out the data in block of 100 bytes.

    Am I correct to say that it doesn't matter what I set for the MUT size in this line:  err_code = nrf_ble_gatt_att_mtu_periph_set(&m_gatt, 64)" , because at the end of the day, the program will negotiate with the mobile device for a larger MUT and DLE?

    How do I determine what is the optimum value for the connection intervals and also the pack size to send in ble_nus_string_send to achieve the best throughput assuming I know the final data length that has been negotiated?

Reply
  • New updates:

    I am able to achieve must faster transfer using the following parameters:

    ********************************

    #define MIN_CONN_INTERVAL MSEC_TO_UNITS(8, UNIT_1_25_MS) /**< Minimum acceptable connection interval (20 ms), Connection interval uses 1.25 ms units. */
    #define MAX_CONN_INTERVAL MSEC_TO_UNITS(20, UNIT_1_25_MS) /**< Maximum acceptable connection interval (75 ms), Connection interval uses 1.25 ms units. */

    err_code = nrf_ble_gatt_att_mtu_periph_set(&m_gatt, 64);

    ******************************

    Knowing that the data length has been updated to 123 from the debug output, I send out the data in block of 100 bytes.

    Am I correct to say that it doesn't matter what I set for the MUT size in this line:  err_code = nrf_ble_gatt_att_mtu_periph_set(&m_gatt, 64)" , because at the end of the day, the program will negotiate with the mobile device for a larger MUT and DLE?

    How do I determine what is the optimum value for the connection intervals and also the pack size to send in ble_nus_string_send to achieve the best throughput assuming I know the final data length that has been negotiated?

Children
  • I would use this value, which is the max size of 247 bytes. With 3 bytes of overhead, that allows user data of up to 244 bytes per transmission.

        err_code = nrf_ble_gatt_att_mtu_periph_set(&m_gatt, NRF_SDH_BLE_GATT_MAX_MTU_SIZE);
    

    I find that even though the peripheral is using notifications, it is waiting for an ack from central, and central only sends out a single ack per connection interval. Therefore using a longer connection interval - which should be much more effient - actually slows down transmission. I get 6,000 bytes per second with both peripheral and central at 7.5 mSecs connection interval.

    This might clarify what's going on but it doesn't explain why the peripheral waits for an ack rather than just continually transmitting; I expect there is a simple setting for ack which we are missing. Sorry if I already posted this, it's my notes on timing. At 450 x 186-byte packets per second that gives 83,700 bytes per sec, somewhat less in practice..

    // This application uses a packet size of 186 bytes
    //
    // An integral number of packets can be sent in each connection interval using "Write Command". Shortest interval
    // is 7.5mSecs, longest 4,000mSecs in steps of 1.25mSecs
    // 7.5 mSec Connection Interval:
    //  1MHz: 2,116 uSecs @ 7.50mSecs -> 3.5 so maximum of 3 packets per connection interval, 400 packets/sec
    //  2MHz: 1,260 uSecs @ 7.50mSecs -> 5.9 so maximum of 5 packets per connection interval, 666 packets/sec
    // 40.0 mSec Connection Interval (proposed size):
    //  1MHz: 2,116 uSecs @ 40mSecs -> 18.9 so maximum of 18 packets per connection interval, 450 packets/sec
    //  2MHz: 1,260 uSecs @ 40mSecs -> 31.7 so maximum of 31 packets per connection interval, 775 packets/sec
    //
    // Theoretical Maximum 186-byte Packet Throughput - Packet Timing in uSecs, 40mSec Connection Interval
    //  1MHz: 2,116 uSec per packet -> less connection interval 0.9522 -> 2,222 usecs/pkt, 450 packets/sec
    //  2MHz: 1,260 usec per packet -> less connection interval 0.9765 -> 1,290 usecs/pkt, 775 packets/sec
    // Practical Maximum Throughput - quite a bit less, say 50%.
    //
    //    ---[Connection Interval j-1]---[Connection Interval j]---[Connection Interval j+1]---
    //                                  /                      \
    //                                /                          \
    //                             /                                \
    //                          /                                      \
    //                       /                                            \
    //                    /                                                  \
    //                 /                                                        \
    //              /                                                              \
    //           /                                                                    \
    //        /                                                                          \
    //        |<-------------- Packet Cycle --------------------------------------------->|
    //        |                    |  |       |  |                             |  |       |
    //      --[Master->Slave Packet]--[ IFS   ]--[Slave->Master Data Packet    ]--[ IFS   ]--[Master->Slave Packet]--
    //  1MHz: [10 octets -> 80uSec ]  [150uSec]  [217 octets -> 1,736uSecs     ]  [150uSec] -> 2,116 uSec/packet
    //  2MHz: [11 octets -> 88uSec ]  [150uSec]  [218 octets ->   872uSecs     ]  [150uSec] -> 1,260 usec/packet
    //                                         /                                 \
    //                                      /                                       \
    //                                   /                                             \
    //                                /                                                   \
    //                             /                                                         \
    //                          /                                                               \
    //                       /                                                                     \
    //                    /                                                                           \
    //                 /                                                                                 \
    //              /                                                                                       \
    //           /                                                                                             \
    //        /                                                                                                   \
    //     /                                                                                                         \
    //  /                                                                                                               \
    // |<------------------------------------------ Slave -> Master ECG Data Packet ------------------------------------>|
    // |                                                                                                                 |
    // +-----------------------------------------------------------------------------------------------------------------+
    // |                                            BLE Data Packet Min/Max/186-byte                                     |
    // +-----------------------------------------------------------------------------------------------------------------+
    // |                                     1MHz: 10/265/217 octets, 2MHz: 11/266/218 octets                            |
    // +----------+--------+--------------------------------------------------------------------------------------+------+
    // |          | Access |                                                                                      |      |
    // | Preamble | Address|         Protocol Data Unit PDU                                                       | CRCC |
    // +----------+--------+--------------------------------------------------------------------------------------+------+
    // |   1 (2)  |    4   |                                                   2-257                              |  3   |
    // |          |        +-----------+------------------------------------------------------------------+-------+      |
    // |          |        | LL Header |  Payload                                                         |  MIC  |      |
    // |          |        +-----------+------------------------------------------------------------------+ (opt) |      |
    // |          |        |   2       |                                       0-251                      |   4   |      |
    // |          |        |           +--------+---------------------------------------------------------+       |      |
    // |          |        |           | L2CAP  |                                                         |       |      |
    // |          |        |           | Header |  ATT Data                                               |       |      |
    // |          |        |           +--------+---------------------------------------------------------+       |      |
    // |          |        |           |   4    |                              0-247                      |       |      |
    // |          |        |           |        +-----+-------+-------------------------------------------+       |      |
    // |          |        |           |        | ATT | ATT   |                                           |       |      |
    // |          |        |           |        | Op  | Attrib|  ATT Payload                              |       |      |
    // |          |        |           |        +-----+-------+-------------------------------------------+       |      |
    // |  1 (2)   |   4    |  2        |   4    |  1  |  2    |                0-244                      |  (4)  |  3   |
    // +----------+--------+-----------+--------+-----+-------+-------------------------------------------+-------+------+
    //                                                        /                                           \
    //                                                      /                                               \
    //                                                    /                                                   \
    //                                                  /                                                       \
    //                                                /                                                           \
    //                                              /                                                               \
    //                                            |<-------------------- Payload 0-244 bytes ----------------------->|
    //
    //
    // Preamble is 1 byte for LE 1MHz (BLE 4.2) and 2 bytes for 2MHz (BLE 5.0). If the first bit of the ADDRESS is 0 the preamble
    // will be 0xAA otherwise 0x55.
    // The MIC field is optional used when we are using an encrypted connection
    

  • So my calculation above shows 83 Kbytes/sec using 186-byte packet, 1MHz phy, 40 mSecs connection interval. Testing these settings with a modified throughput test on two Nordic nRF52 dev boards (PCA10040) shows 141Kbytes/sec (1024 Kbytes in 7.4 seconds) - which is fast! Suspiciously fast, or is my calculation incorrect, not sure.

  • Hi hmolesworth,

    Thanks for the information. I am still a bit of a beginner with Nordic BLE, so there is a lot of learn for me. I will may need to read up more to understand more about what happens during the actual transmission.

    I am actually still a little confused by the MUT size and data length parameters. Even if I were to set the MUT size to a max size of 247, I think this is still limited by the receiving device, in this case with my Android mobile device, the data length was negotiated to a value of 123.

    At the end of the day, I believe (but maybe i am wrong) its the MUT size, connection interval and size of the data packet to send in each call to ble_nus_string_send that will affect the final transmission rate. But how these parameters should be set/tuned still seems confusing for many. To add to the complexity, one would not be able to control what device is at the receiving end (iphone vs android phone, model etc) and the negotiated data length will always be different.

    I am actually hoping that Nordic could provide a throughput example for dev board to mobile device and guide users on what are the parameters to take note of.

  • You're quite correct that the receiving (Central) device controls the setting; the Peripheral can only make a request which is then often curtailed by the Central - Android SmartPhone in your case. I hadn't picked up on that you were using a SmartPhone. So I imagine you have not seen the Image Processing Demo Nordic kindly provided; well worth a look on Github here. It streams video to a Samsung Galaxy S8 using an MTU size of 244 bytes and BLE 4.2 compatibility (1 Mbit Phy) or BLE 5 (2 Mbit Phy).

    There is a Youtube video here, worth watching. It mentions a bit rate of 1.2 Mbits/sec with 2Mhz Phy

    The App for Android source and iOS source are at the links in GitHub

Related