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

Incease BLE throughput in nrf51822

Hi guys, 

I am developing a application and I want to increase the BLE throughput to atleast 1.3kB/s.

I have already read this thread: https://devzone.nordicsemi.com/f/nordic-q-a/12935/how-to-transmit-6-packets-in-s130-uart-central

and this thread: https://devzone.nordicsemi.com/f/nordic-q-a/1105/how-do-i-calculate-throughput-for-a-ble-link

With android, I decrease the interval connection down to 9ms and for ios it seems like can not go further than 24ms. 

So the speed in android is so much faster than in ios. 

I assumed that if I want to increase BLE throughput I have to increase number of packets per connection. 

I used sd130 v2 so I know that I can reach 6 packets and 4 packets for android and ios respectively. 

I have changed my ble_stack_init() so it looks like this: 

nrf_clock_lf_cfg_t clock_lf_cfg = NRF_CLOCK_LFCLKSRC;

		// Initialize SoftDevice.
		SOFTDEVICE_HANDLER_INIT(&clock_lf_cfg, NULL);
		
		NRF_LOG_PRINTF("\r\nINIT CRYSTAL SUCCESS\r\n");
		ble_conn_bw_counts_t conn_bw_counts = {
			.tx_counts = {.high_count = 1, .mid_count = 0, .low_count = 0},
			.rx_counts = {.high_count = 1, .mid_count = 0, .low_count = 0}
		};
		ble_enable_params_t ble_enable_params;
		memset(&ble_enable_params, 0x00, sizeof(ble_enable_params));

		
		ble_enable_params.common_enable_params.p_conn_bw_counts = &conn_bw_counts;
		
		
		softdevice_enable_get_default_config(CENTRAL_LINK_COUNT,
		PERIPHERAL_LINK_COUNT,
		&ble_enable_params);


		//Check the ram settings against the used number of links
		//CHECK_RAM_START_ADDR(CENTRAL_LINK_COUNT, PERIPHERAL_LINK_COUNT);
		// Enable BLE stack.
		softdevice_enable(&ble_enable_params);


		// Subscribe for BLE events.
		softdevice_ble_evt_handler_set(ble_evt_dispatch);
		

		softdevice_sys_evt_handler_set(sys_evt_dispatch);

and also in ble_advertising.c file: I added this code before sd_ble_gap_adv_start(&adv_params);

    ble_opt_t ble_opt;
    ble_common_opt_conn_bw_t conn_bw;
    memset(&conn_bw, 0x00, sizeof(conn_bw));
    memset(&ble_opt, 0x00, sizeof(ble_opt));

    // if this set to mid this will work but setting it to high will not
    conn_bw.conn_bw.conn_bw_rx = BLE_CONN_BW_HIGH;
    conn_bw.conn_bw.conn_bw_tx = BLE_CONN_BW_HIGH;
    conn_bw.role = BLE_GAP_ROLE_CENTRAL;

    // gap option is missing?
    ble_opt.common_opt.conn_bw = conn_bw;
		NRF_LOG_PRINTF("sd_ble_opt_set");
    sd_ble_opt_set(BLE_COMMON_OPT_CONN_BW, &ble_opt);

I can compile but it seemed not work since the speed is still very low. How can I know how many packets transferred every connection event and how can I make it work?  

Thank you for your time. 

Parents
  • Hi,

    ble_enable_params is "memset" in softdevice_enable_get_default_config() so your settings did not get applied. Should work if you place "ble_enable_params.common_enable_params.p_conn_bw_counts = &conn_bw_counts;"  after softdevice_enable_get_default_config() in ble_stack_init. 

    I tried with the same settings here and got 7 packets per connection @30 ms intervals with an iphone 7. 

Reply
  • Hi,

    ble_enable_params is "memset" in softdevice_enable_get_default_config() so your settings did not get applied. Should work if you place "ble_enable_params.common_enable_params.p_conn_bw_counts = &conn_bw_counts;"  after softdevice_enable_get_default_config() in ble_stack_init. 

    I tried with the same settings here and got 7 packets per connection @30 ms intervals with an iphone 7. 

Children
  • Hi Vidar Berg,

    Thank you very much for your response. 

    I did what you said and here is what it looks like in ble_stack_init();

    	nrf_clock_lf_cfg_t clock_lf_cfg = NRF_CLOCK_LFCLKSRC;
    
    	// Initialize SoftDevice.
    	SOFTDEVICE_HANDLER_INIT(&clock_lf_cfg, NULL);
    
    	ble_enable_params_t ble_enable_params;
    	ble_conn_bw_counts_t conn_bw_counts = {
    		.tx_counts = {.high_count = 1, .mid_count = 0, .low_count = 0},
    		.rx_counts = {.high_count = 1, .mid_count = 0, .low_count = 0}};
    	
    
    	softdevice_enable_get_default_config(CENTRAL_LINK_COUNT,
    										 PERIPHERAL_LINK_COUNT,
    										 &ble_enable_params);
    	ble_enable_params.common_enable_params.p_conn_bw_counts = &conn_bw_counts;
    
    	// Enable BLE stack.
    	softdevice_enable(&ble_enable_params);
    
    	// Subscribe for BLE events.
    	softdevice_ble_evt_handler_set(ble_evt_dispatch);
    
    	softdevice_sys_evt_handler_set(sys_evt_dispatch);

    inside softdevice_handler: here is what the default config function looks like: 

    uint32_t softdevice_enable_get_default_config(uint8_t central_links_count,
                                                  uint8_t periph_links_count,
                                                  ble_enable_params_t * p_ble_enable_params)
    {
        memset(p_ble_enable_params, 0, sizeof(ble_enable_params_t));
        p_ble_enable_params->common_enable_params.vs_uuid_count   = 2;
        p_ble_enable_params->gatts_enable_params.attr_tab_size    = SOFTDEVICE_GATTS_ATTR_TAB_SIZE;
        p_ble_enable_params->gatts_enable_params.service_changed  = SOFTDEVICE_GATTS_SRV_CHANGED;
        p_ble_enable_params->gap_enable_params.periph_conn_count  = periph_links_count;
        p_ble_enable_params->gap_enable_params.central_conn_count = central_links_count;
        if (p_ble_enable_params->gap_enable_params.central_conn_count != 0)
        {
            p_ble_enable_params->gap_enable_params.central_sec_count  = SOFTDEVICE_CENTRAL_SEC_COUNT;
        }
    
        return NRF_SUCCESS;
    }

    And here is the result I got when I use sniffer to see what happen: 

    as far as I understand, delta time is the time between 2 packets and here I got 27ms, which is pretty much what I expected when I set interval connection is 30 and using iPhone which have interval connection is 24 after negotiation.

    More Data mean there is more packet in that interval, so base on this, I assume most of the time there is only 1 packet per interval connection? So basically it's not work? 

    May I ask is this because I set sd_ble_opt_set(BLE_COMMON_OPT_CONN_BW, &ble_opt); inside the ble_advertising_start();? Where should I put those code then?

    uint32_t ble_advertising_start(ble_adv_mode_t advertising_mode)
    {
        uint32_t             err_code;
        ble_gap_adv_params_t adv_params;
    
        m_adv_mode_current = advertising_mode;
    
        // Verify if there are any pending flash operations. If so, delay starting advertising until
        // the flash operations are complete.
        if(flash_access_in_progress())
        {
            m_advertising_start_pending = true;
            return NRF_SUCCESS;
        }
    
        ADV_LOG("[ADV]: no flash operations in progress, prepare advertising.\r\n");
        // Fetch the peer address.
        ble_advertising_peer_address_clear();
    
        if (  ((m_adv_modes_config.ble_adv_directed_enabled)      && (m_adv_mode_current == BLE_ADV_MODE_DIRECTED))
            ||((m_adv_modes_config.ble_adv_directed_slow_enabled) && (m_adv_mode_current == BLE_ADV_MODE_DIRECTED))
            ||((m_adv_modes_config.ble_adv_directed_slow_enabled) && (m_adv_mode_current == BLE_ADV_MODE_DIRECTED_SLOW))
           )
        {
            if (m_evt_handler != NULL)
            {
                m_peer_addr_reply_expected = true;
                m_evt_handler(BLE_ADV_EVT_PEER_ADDR_REQUEST);
            }
            else
            {
                m_peer_addr_reply_expected = false;
            }
        }
    
        // If a mode is disabled, continue to the next mode. I.e fast instead of direct, slow instead of fast, idle instead of slow.
        if (  (m_adv_mode_current == BLE_ADV_MODE_DIRECTED)
            &&(!m_adv_modes_config.ble_adv_directed_enabled || !peer_address_exists(m_peer_address.addr)))
        {
            m_adv_mode_current = BLE_ADV_MODE_DIRECTED_SLOW;
        }
        if (  (m_adv_mode_current == BLE_ADV_MODE_DIRECTED_SLOW)
            &&(!m_adv_modes_config.ble_adv_directed_slow_enabled || !peer_address_exists(m_peer_address.addr)))
        {
            m_adv_mode_current = BLE_ADV_MODE_FAST;
        }
        if (!m_adv_modes_config.ble_adv_fast_enabled && m_adv_mode_current == BLE_ADV_MODE_FAST)
        {
            m_adv_mode_current = BLE_ADV_MODE_SLOW;
        }
        if (!m_adv_modes_config.ble_adv_slow_enabled && m_adv_mode_current == BLE_ADV_MODE_SLOW)
        {
            m_adv_mode_current = BLE_ADV_MODE_IDLE;
            m_adv_evt          = BLE_ADV_EVT_IDLE;
        }
    
        // Fetch the whitelist.
        if (   (m_evt_handler != NULL)
            && (m_adv_mode_current == BLE_ADV_MODE_FAST || m_adv_mode_current == BLE_ADV_MODE_SLOW)
            && (m_adv_modes_config.ble_adv_whitelist_enabled)
            && (!m_whitelist_temporarily_disabled))
        {
            m_whitelist_reply_expected = true;
            m_evt_handler(BLE_ADV_EVT_WHITELIST_REQUEST);
        }
        else
        {
            m_whitelist_reply_expected = false;
        }
    
        // Initialize advertising parameters with default values.
        memset(&adv_params, 0, sizeof(adv_params));
    
        adv_params.type        = BLE_GAP_ADV_TYPE_ADV_IND;
        adv_params.p_peer_addr = NULL;
        adv_params.fp          = BLE_GAP_ADV_FP_ANY;
        adv_params.p_whitelist = NULL;
    
        // Set advertising parameters and events according to selected advertising mode.
        switch (m_adv_mode_current)
        {
            case BLE_ADV_MODE_DIRECTED:
                ADV_LOG("[ADV]: Starting direct advertisement.\r\n");
                adv_params.p_peer_addr = &m_peer_address; // Directed advertising.
                adv_params.type        = BLE_GAP_ADV_TYPE_ADV_DIRECT_IND;
                adv_params.timeout     = 0;
                adv_params.interval    = 0;
                m_adv_evt              = BLE_ADV_EVT_DIRECTED;
                break;
    
            case BLE_ADV_MODE_DIRECTED_SLOW:
                ADV_LOG("[ADV]: Starting direct advertisement.\r\n");
                adv_params.p_peer_addr = &m_peer_address; // Directed advertising.
                adv_params.type        = BLE_GAP_ADV_TYPE_ADV_DIRECT_IND;
                adv_params.timeout     = m_adv_modes_config.ble_adv_directed_slow_timeout;
                adv_params.interval    = m_adv_modes_config.ble_adv_directed_slow_interval;
                m_adv_evt              = BLE_ADV_EVT_DIRECTED_SLOW;
                break;
    
            case BLE_ADV_MODE_FAST:
                adv_params.timeout  = m_adv_modes_config.ble_adv_fast_timeout;
                adv_params.interval = m_adv_modes_config.ble_adv_fast_interval;
    
                if (   whitelist_has_entries(&m_whitelist)
                    && m_adv_modes_config.ble_adv_whitelist_enabled
                    && !m_whitelist_temporarily_disabled)
                {
                    adv_params.fp          = BLE_GAP_ADV_FP_FILTER_CONNREQ;
                    adv_params.p_whitelist = &m_whitelist;
                    m_advdata.flags        = BLE_GAP_ADV_FLAG_BR_EDR_NOT_SUPPORTED;
                    err_code               = ble_advdata_set(&m_advdata, NULL);
                    VERIFY_SUCCESS(err_code);
    
                    m_adv_evt = BLE_ADV_EVT_FAST_WHITELIST;
                    ADV_LOG("[ADV]: Starting fast advertisement with whitelist.\r\n");
                }
                else
                {
                    m_adv_evt = BLE_ADV_EVT_FAST;
                    ADV_LOG("[ADV]: Starting fast advertisement.\r\n");
                }
                break;
    
            case BLE_ADV_MODE_SLOW:
                adv_params.interval = m_adv_modes_config.ble_adv_slow_interval;
                adv_params.timeout  = m_adv_modes_config.ble_adv_slow_timeout;
    
                if (   whitelist_has_entries(&m_whitelist)
                    && m_adv_modes_config.ble_adv_whitelist_enabled
                    && !m_whitelist_temporarily_disabled)
                {
                    adv_params.fp          = BLE_GAP_ADV_FP_FILTER_CONNREQ;
                    adv_params.p_whitelist = &m_whitelist;
                    m_advdata.flags        = BLE_GAP_ADV_FLAG_BR_EDR_NOT_SUPPORTED;
                    err_code               = ble_advdata_set(&m_advdata, NULL);
                    VERIFY_SUCCESS(err_code);
    
                    m_adv_evt = BLE_ADV_EVT_SLOW_WHITELIST;
                    ADV_LOG("[ADV]: Starting slow advertisement with whitelist.\r\n");
                }
                else
                {
                    m_adv_evt = BLE_ADV_EVT_SLOW;
                    ADV_LOG("[ADV]: Starting slow advertisement.\r\n");
                }
                break;
    
            default:
                break;
        }
        if (m_adv_mode_current != BLE_ADV_MODE_IDLE)
        {
        ble_opt_t ble_opt;
        ble_common_opt_conn_bw_t conn_bw;
        memset(&conn_bw, 0x00, sizeof(conn_bw));
        memset(&ble_opt, 0x00, sizeof(ble_opt));
    
        // if this set to mid this will work but setting it to high will not
        conn_bw.conn_bw.conn_bw_rx = BLE_CONN_BW_HIGH;
        conn_bw.conn_bw.conn_bw_tx = BLE_CONN_BW_HIGH;
        // conn_bw.conn_bw.conn_bw_rx = BLE_CONN_BW_MID; // conn_bw.conn_bw.conn_bw_tx = BLE_CONN_BW_MID;
        conn_bw.role = BLE_GAP_ROLE_CENTRAL;
    
        // gap option is missing?
        ble_opt.common_opt.conn_bw = conn_bw;
    		NRF_LOG_PRINTF("sd_ble_opt_set");
        sd_ble_opt_set(BLE_COMMON_OPT_CONN_BW, &ble_opt);
            err_code = sd_ble_gap_adv_start(&adv_params);
            VERIFY_SUCCESS(err_code);
        }
        if (m_evt_handler != NULL)
        {
            m_evt_handler(m_adv_evt);
        }
    
        return NRF_SUCCESS;
    }

    And what is the software that you use to determine the number of packets per connection interval?

  • Looks like you have correctly enabled high BW mode for the connection now. To maximize throughput it is also important to keep the notification/write queue full, are you doing that in your code? Attached is the project I used for test (modified ble_app_uart example in sdk 12.3.0). 

    Steps to test:

    1. unzip zip file and copy folder to nRF5_SDK_12.3.0_d7731ad\examples\ble_peripheral

    2. Build and flash the FW

    3. Connect to the peripheral with your iphone and enable notifications

    4. Open a serial client (Tera term,etc ), open the Jlink virtual com port, set baudrate to 115200, then transfer a larger chunk of data. A file for instance. 

    5. Observe the packet transfer with the sniffer to determine the number of packets per event. The number of packets can be 1-7 depending on phone. 

    ble_app_uart_mod.zip

    I used an Ellisys sniffer, but it's not required for determining the number of packets per event. 

  • Thank you very much, I have achieved it. Turn out it is because on the mobile phone, it is in write with response mode, so it can not achieve multiple packets per interval connection. When I switched to write without response, it's okay now. 

    But the thing is, because of the consistency and security, I need to keep the mode write with response, is there any way else I still can achieve this multiple packets? 

    Thank you very much for your help, I really appreciate it, it's very valuable to me. 

  • I didn't notice that you were sending write requests before now. Write requests are significantly slower compared to Write commands as the requests need to be acknowledged by server at the application layer before next write can be requested (both are acknowledged at the link layer). In other words, you will only get one request per connection event. So I'd recommend to use write command to improve throughput.  

    Hust said:

    What do you mean here? How can I do it? Please be patient with me. 

    I could have been more clear. I have limited experienced with app development on the iphone, but the "write" API should be asynchronous - write packets are added to an output queue by the write API and later de-queued by the BT stack when the packet is sent on air. So I meant that you should make sure to have multiple packets in the output queue in order to allow the stack to send multiple packets per connection event.     

  • Unfortunately, it's not possible to get more packets per event if you use write requests. That said, you could consider using a combination of these two to both achieve higher throughput and acknowledgment at the application layer. Possibly something similar to the BLE DFU transport where we have one  "control point" characteristic for commmands and another characteristic for receiving the data. Then you could send write requests to the control point for  enabling reception of x number of bytes on the data characteristic.   

Related