File transfer/large data transfer example NRF Connect sdk

Hi,

I am trying to transfer a dataset of over 100k, via a custom service from a peripheral device.

There are many posts in this forum that describe ways to do this but none seem to apply to the newer nrf Connect SDK. I am using nrf connect 1.7.1 with the VSCode extension.

The peripheral_uart example is often quoted as being a good starting point, but i can't seem to find where i would start some sort of Send data->wait for data sent event-> send next part, kind of cycle. The throughput example appears to transfer a large data set, but i am struggling to interpret that service as it is quite large. 

Is the advised way of doing this still calling notify on a characteristic? I have attempted to call Notify inside an on_sent callback but the code doesn't appear to call callbacks when already executing inside of one.

I would be very grateful if anybody could point me in the right direction towards initiating a large data transfer from a peripheral device.

Parents
  • Thank you for the reply Simon. 

    I see that this is a good solution for handling ISR generated callbacks.

    The questions that i still need answering are as follows:

    1. what is a standard way to implement large data transfer from a peripheral to a central device using nrf connect sdk? Should i initiate a read and then have the peripheral notify again and again? how will i know that i am not starting another notify before the previous has been fully sent? Is notify even the right thing to do? could i not just continue to send data chunks from a single data read request? Does this require a characteristic in the central device for the peripheral to write to in a loop?

    2. Using the suggested work queue worked fine, but waiting for the on_sent notification before sending another notify was very slow. It took a minute to transfer the word "hello", a 500 times... a payload throughput of 48 Bps. I understand that the data length and mtu can be increased, but even if the payload size was 251, 8 transactions per second is a tiny number. 

    3. I see that there is also something referred to as a 'long read' in the peripheral example. What is this and how does it work? should i be using this instead of notify?

    I am using the nrf52832 on the nRF52 dk.

    Cheers

  • Hi Simon,

    Were the measurements that you have taken done with nRF connect SDK? I know that there is plenty of support on this forum for Nrf5 SDK but i am struggling to get examples for the connect SDK, using Zephyr's Bluetooth implementation. As that is now the recommended way of developing moving forward for Nordic, I started developing using the NCS. I know the BLE theory is the same, but how you setup characteristics etc is not. 

    The answer you linked to This answer, appears to reference a softdevice. The Zephyr implementation doesn't use a soft device correct? it's an open source implementation software implementation that is built along with the zephyr OS? 

    How can i find the size of the TX buffers? I assume these can be altered?

    Your test only sends 2000 bytes (or 200 packets @ 20 mtu size. What you have demonstrated is 36kbps but on a data set that is smaller than the tx buffer size. If it were larger, you probably could not add in a for loop as you are showing, without the function failing when it fills. I will try this example until it fails and continue to add the next packet until it is successful. This way, the throughput should be the same. Though, it does seem a shame to not a more event driven method of doing this.

    I guess, if i know the TX buffer size, i also know based on the already measured throughput, how long it will take to empty the buffer. So once i receive a fail, i can sleep until i expect it to be say half empty, before attempting to add the next packets. This will give a yield point instead of being stuck in an endless loop of attempting to add to the transmit queue.

    Any news on the Long read?

Reply
  • Hi Simon,

    Were the measurements that you have taken done with nRF connect SDK? I know that there is plenty of support on this forum for Nrf5 SDK but i am struggling to get examples for the connect SDK, using Zephyr's Bluetooth implementation. As that is now the recommended way of developing moving forward for Nordic, I started developing using the NCS. I know the BLE theory is the same, but how you setup characteristics etc is not. 

    The answer you linked to This answer, appears to reference a softdevice. The Zephyr implementation doesn't use a soft device correct? it's an open source implementation software implementation that is built along with the zephyr OS? 

    How can i find the size of the TX buffers? I assume these can be altered?

    Your test only sends 2000 bytes (or 200 packets @ 20 mtu size. What you have demonstrated is 36kbps but on a data set that is smaller than the tx buffer size. If it were larger, you probably could not add in a for loop as you are showing, without the function failing when it fills. I will try this example until it fails and continue to add the next packet until it is successful. This way, the throughput should be the same. Though, it does seem a shame to not a more event driven method of doing this.

    I guess, if i know the TX buffer size, i also know based on the already measured throughput, how long it will take to empty the buffer. So once i receive a fail, i can sleep until i expect it to be say half empty, before attempting to add the next packets. This will give a yield point instead of being stuck in an endless loop of attempting to add to the transmit queue.

    Any news on the Long read?

Children
  • cosmotic_instant said:
    Were the measurements that you have taken done with nRF connect SDK? I know that there is plenty of support on this forum for Nrf5 SDK but i am struggling to get examples for the connect SDK, using Zephyr's Bluetooth implementation. As that is now the recommended way of developing moving forward for Nordic, I started developing using the NCS. I know the BLE theory is the same, but how you setup characteristics etc is not. 

    I used the nRF Connect SDK, see https://github.com/nrfconnect/sdk-nrf/tree/main/samples/bluetooth.

    cosmotic_instant said:
    The answer you linked to This answer, appears to reference a softdevice. The Zephyr implementation doesn't use a soft device correct? it's an open source implementation software implementation that is built along with the zephyr OS? 

    In the reply I linked to, the nRF5 SDK was used. It was not meant as a guide exactly how to improve the speed in Zephyr/NCS, but rather what parameters you should use. In NCS BLE samples, we use the SoftDevice Controller (similar to the SoftDevice). You can also use the Zephyr BLE controller.

    cosmotic_instant said:

    How can i find the size of the TX buffers? I assume these can be altered?

    Your test only sends 2000 bytes (or 200 packets @ 20 mtu size. What you have demonstrated is 36kbps but on a data set that is smaller than the tx buffer size. If it were larger, you probably could not add in a for loop as you are showing, without the function failing when it fills. I will try this example until it fails and continue to add the next packet until it is successful. This way, the throughput should be the same. Though, it does seem a shame to not a more event driven method of doing this.

    I guess, if i know the TX buffer size, i also know based on the already measured throughput, how long it will take to empty the buffer. So once i receive a fail, i can sleep until i expect it to be say half empty, before attempting to add the next packets. This will give a yield point instead of being stuck in an endless loop of attempting to add to the transmit queue.

    Any news on the Long read?

    I will get back to you on these question, and will try to put off some time the next days to try to achive a speed close to 1400 kbps.

  • I got some progress here, I added these to the peripheral_uart sample (NCS v1.8.0):

    CONFIG_BT_BUF_ACL_RX_SIZE=251
    CONFIG_BT_ATT_PREPARE_COUNT=2
    CONFIG_BT_L2CAP_TX_BUF_COUNT=10
    CONFIG_BT_L2CAP_TX_MTU=247
    CONFIG_BT_CTLR_RX_BUFFERS=2
    CONFIG_BT_BUF_ACL_TX_COUNT=10
    CONFIG_BT_BUF_ACL_TX_SIZE=251
    CONFIG_BT_CTLR_DATA_LENGTH_MAX=251

    and changed MTU_SIZE to 244, such that bt_nus_send would send 244 block at the time. Then I achieved a speed of 46511.6 bytes/second=372kbps. I communicated with the nRF Connect Mobile app. From the mobile app you have to enable notification for the TX char and request an MTU of 250 bytes.

    I also increased TEST_BUF_SIZE to 20000, such that a total of 20k bytes would be sent.

    Is this fast enough speed for your purpose? If not try to increase the connection interval to 400ms (tell me if you need help with this).

     Here is the changes I applied to the peripheral_uart sample:

    diff --git a/samples/bluetooth/peripheral_uart/prj.conf b/samples/bluetooth/peripheral_uart/prj.conf
    index e5262de64..d3e77f24e 100644
    --- a/samples/bluetooth/peripheral_uart/prj.conf
    +++ b/samples/bluetooth/peripheral_uart/prj.conf
    @@ -1,50 +1,65 @@
    -#
    -# Copyright (c) 2018 Nordic Semiconductor
    -#
    -# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause
    -#
    -
    -# Enable the UART driver
    -CONFIG_UART_ASYNC_API=y
    -CONFIG_NRFX_UARTE0=y
    -CONFIG_SERIAL=y
    -
    -CONFIG_GPIO=y
    -
    -# Make sure printk is printing to the UART console
    -CONFIG_CONSOLE=y
    -CONFIG_UART_CONSOLE=y
    -
    -CONFIG_HEAP_MEM_POOL_SIZE=2048
    -
    -CONFIG_BT=y
    -CONFIG_BT_PERIPHERAL=y
    -CONFIG_BT_DEVICE_NAME="Nordic_UART_Service"
    -CONFIG_BT_DEVICE_APPEARANCE=833
    -CONFIG_BT_MAX_CONN=1
    -CONFIG_BT_MAX_PAIRED=1
    -
    -# Enable the NUS service
    -CONFIG_BT_NUS=y
    -
    -# Enable bonding
    -CONFIG_BT_SETTINGS=y
    -CONFIG_FLASH=y
    -CONFIG_FLASH_PAGE_LAYOUT=y
    -CONFIG_FLASH_MAP=y
    -CONFIG_NVS=y
    -CONFIG_SETTINGS=y
    -
    -# Enable DK LED and Buttons library
    -CONFIG_DK_LIBRARY=y
    -
    -# This example requires more workqueue stack
    -CONFIG_SYSTEM_WORKQUEUE_STACK_SIZE=2048
    -
    -# Config logger
    -CONFIG_LOG=y
    -CONFIG_USE_SEGGER_RTT=y
    -CONFIG_LOG_BACKEND_RTT=y
    -CONFIG_LOG_BACKEND_UART=n
    -
    -CONFIG_ASSERT=y
    +#
    +# Copyright (c) 2018 Nordic Semiconductor
    +#
    +# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause
    +#
    +
    +# Enable the UART driver
    +CONFIG_UART_ASYNC_API=y
    +CONFIG_NRFX_UARTE0=y
    +CONFIG_SERIAL=y
    +
    +CONFIG_GPIO=y
    +
    +# Make sure printk is printing to the UART console
    +CONFIG_CONSOLE=y
    +CONFIG_UART_CONSOLE=y
    +
    +CONFIG_HEAP_MEM_POOL_SIZE=2048
    +
    +CONFIG_BT=y
    +CONFIG_BT_PERIPHERAL=y
    +CONFIG_BT_DEVICE_NAME="Nordic_UART_Service"
    +CONFIG_BT_DEVICE_APPEARANCE=833
    +CONFIG_BT_MAX_CONN=1
    +CONFIG_BT_MAX_PAIRED=1
    +
    +# Enable the NUS service
    +CONFIG_BT_NUS=y
    +
    +# Enable bonding
    +CONFIG_BT_SETTINGS=y
    +CONFIG_FLASH=y
    +CONFIG_FLASH_PAGE_LAYOUT=y
    +CONFIG_FLASH_MAP=y
    +CONFIG_NVS=y
    +CONFIG_SETTINGS=y
    +
    +# Enable DK LED and Buttons library
    +CONFIG_DK_LIBRARY=y
    +
    +# This example requires more workqueue stack
    +CONFIG_SYSTEM_WORKQUEUE_STACK_SIZE=2048
    +
    +# Config logger
    +CONFIG_LOG=y
    +CONFIG_USE_SEGGER_RTT=y
    +CONFIG_LOG_BACKEND_RTT=y
    +CONFIG_LOG_BACKEND_UART=n
    +
    +CONFIG_ASSERT=y
    +CONFIG_BT_USER_DATA_LEN_UPDATE=y
    +
    +
    +CONFIG_BT_BUF_ACL_RX_SIZE=251
    +#CONFIG_BT_GATT_CLIENT=y
    +CONFIG_BT_ATT_PREPARE_COUNT=2
    +#CONFIG_BT_CONN_TX_MAX=10
    +CONFIG_BT_L2CAP_TX_BUF_COUNT=10
    +CONFIG_BT_L2CAP_TX_MTU=247
    +#CONFIG_BT_L2CAP_DYNAMIC_CHANNEL=y
    +#CONFIG_BT_CTLR_PHY_2M=y
    +CONFIG_BT_CTLR_RX_BUFFERS=2
    +CONFIG_BT_BUF_ACL_TX_COUNT=10
    +CONFIG_BT_BUF_ACL_TX_SIZE=251
    +CONFIG_BT_CTLR_DATA_LENGTH_MAX=251
    \ No newline at end of file
    diff --git a/samples/bluetooth/peripheral_uart/src/main.c b/samples/bluetooth/peripheral_uart/src/main.c
    index 2acac0051..37ad3724b 100644
    --- a/samples/bluetooth/peripheral_uart/src/main.c
    +++ b/samples/bluetooth/peripheral_uart/src/main.c
    @@ -240,7 +240,7 @@ static int uart_init(void)
     		return -ENXIO;
     	}
     
    -	if (IS_ENABLED(CONFIG_USB_DEVICE_STACK)) {
    +	if (IS_ENABLED(CONFIG_USB)) {
     		err = usb_enable(NULL);
     		if (err) {
     			LOG_ERR("Failed to enable USB");
    @@ -319,6 +319,30 @@ static int uart_init(void)
     	return uart_rx_enable(uart, rx->data, sizeof(rx->data), 50);
     }
     
    +static void le_param_updated(struct bt_conn *conn, uint16_t interval,
    +			     uint16_t latency, uint16_t timeout)
    +{
    +	printk("Connection parameters updated.\n"
    +	       " interval: %d, latency: %d, timeout: %d\n",
    +	       interval, latency, timeout);
    +
    +}
    +
    +static void le_data_length_updated(struct bt_conn *conn,
    +				   struct bt_conn_le_data_len_info *info)
    +{
    +	/*if (!data_length_req) {
    +		return;
    +	}*/
    +
    +	printk("LE data len updated: TX (len: %d time: %d)"
    +	       " RX (len: %d time: %d)\n", info->tx_max_len,
    +	       info->tx_max_time, info->rx_max_len, info->rx_max_time);
    +
    +	/*data_length_req = false;
    +	k_sem_give(&throughput_sem);*/
    +}
    +
     static void connected(struct bt_conn *conn, uint8_t err)
     {
     	char addr[BT_ADDR_LE_STR_LEN];
    @@ -377,6 +401,8 @@ static void security_changed(struct bt_conn *conn, bt_security_t level,
     static struct bt_conn_cb conn_callbacks = {
     	.connected    = connected,
     	.disconnected = disconnected,
    +	.le_param_updated = le_param_updated,
    +	.le_data_len_updated = le_data_length_updated,
     #ifdef CONFIG_BT_NUS_SECURITY_ENABLED
     	.security_changed = security_changed,
     #endif
    @@ -415,6 +441,18 @@ static void auth_cancel(struct bt_conn *conn)
     }
     
     
    +static void pairing_confirm(struct bt_conn *conn)
    +{
    +	char addr[BT_ADDR_LE_STR_LEN];
    +
    +	bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));
    +
    +	bt_conn_auth_pairing_confirm(conn);
    +
    +	LOG_INF("Pairing confirmed: %s", log_strdup(addr));
    +}
    +
    +
     static void pairing_complete(struct bt_conn *conn, bool bonded)
     {
     	char addr[BT_ADDR_LE_STR_LEN];
    @@ -441,6 +479,7 @@ static struct bt_conn_auth_cb conn_auth_callbacks = {
     	.passkey_display = auth_passkey_display,
     	.passkey_confirm = auth_passkey_confirm,
     	.cancel = auth_cancel,
    +	.pairing_confirm = pairing_confirm,
     	.pairing_complete = pairing_complete,
     	.pairing_failed = pairing_failed
     };
    @@ -555,9 +594,29 @@ static void configure_gpio(void)
     		LOG_ERR("Cannot init LEDs (err: %d)", err);
     	}
     }
    +#define MTU_SIZE 244
    +#define BYTES_TO_SEND 20000
    +
    +uint8_t dummy[MTU_SIZE];
    +uint64_t stamp;
    +int64_t delta;
    +
    +#define PIN_GPIO  (31UL)
     
     void main(void)
     {
    +	for(int x=0; x< MTU_SIZE;x++){
    +		dummy[x] = x;
    +		//printk("dummy[%d] = %d\n", x, dummy[x]);
    +	}
    +
    +	NRF_GPIO->PIN_CNF[PIN_GPIO] = (GPIO_PIN_CNF_DIR_Output << GPIO_PIN_CNF_DIR_Pos) |
    +							(GPIO_PIN_CNF_DRIVE_S0S1 << GPIO_PIN_CNF_DRIVE_Pos) |
    +							(GPIO_PIN_CNF_INPUT_Connect << GPIO_PIN_CNF_INPUT_Pos) |
    +							(GPIO_PIN_CNF_PULL_Disabled << GPIO_PIN_CNF_PULL_Pos) |
    +							(GPIO_PIN_CNF_SENSE_Disabled << GPIO_PIN_CNF_SENSE_Pos);
    +
    +	//LOG_INF("LOG_INF, Delta: %lld ms", delta);
     	int blink_status = 0;
     	int err = 0;
     
    @@ -615,12 +674,36 @@ void ble_write_thread(void)
     		/* Wait indefinitely for data to be sent over bluetooth */
     		struct uart_data_t *buf = k_fifo_get(&fifo_uart_rx_data,
     						     K_FOREVER);
    +		LOG_INF("Start transmission of %d bytes", BYTES_TO_SEND);
    +		stamp = k_uptime_get_32();
    +		NRF_GPIO->OUTSET = (1UL << PIN_GPIO);  //turns led (P0.17) off
    +		NRF_GPIO->OUTCLR = (1UL << PIN_GPIO); 
    +
    +		for(int y =0; y< BYTES_TO_SEND; y+=MTU_SIZE){
    +			//LOG_INF("y=%d/%d\n", y, BYTES_TO_SEND);
    +			if (bt_nus_send(NULL, dummy, MTU_SIZE)) {
    +				LOG_WRN("."); //failed
    +			}else{
    +				LOG_INF("-"); //success
    +			}
    +		}
    +		delta = k_uptime_delta(&stamp); //k_uptime_delta(&stamp);
    +		//LOG_INF("Should be 3000 ms| it is %" PRId64"  ms", delta);
    +		printk("Delta: %lld ms\n", delta);
    +		LOG_INF("Transmission ended");
    +		NRF_GPIO->OUTSET = (1UL << PIN_GPIO);  //turns led (P0.17) off
    +		NRF_GPIO->OUTCLR = (1UL << PIN_GPIO); 
    +		printk("Sent %d bytes (%u KB) in %lld ms\n",
    +	       BYTES_TO_SEND, BYTES_TO_SEND / 1024, delta); //, ((uint64_t)data * 8 / delta));
    +		/* Wait indefinitely for data to be sent over bluetooth */
    +		/*struct uart_data_t *buf = k_fifo_get(&fifo_uart_rx_data,
    +						     K_FOREVER);
     
     		if (bt_nus_send(NULL, buf->data, buf->len)) {
     			LOG_WRN("Failed to send data over BLE connection");
     		}
     
    -		k_free(buf);
    +		k_free(buf);*/
     	}
     }
     
    

Related