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

Nordic UART Service (NUS) Speed Improvement

Hi,

I am able to transfer data from my custom peripheral device with nRF52840, but data is moving much slower than it should.

I am writing 244 bytes into NUS within my peripheral and it is getting to my Android phone, but not very quickly. On the phone, I am not doing anything with the data, just counting bytes within an onCharacteristicChanged callback. I have reviewed the ble_app_uart example and I have setup my GAP parameters to match (I think). Detail is below. I am writing data as fast as possible using:

// Loop through samples_available and pass them back via NUS.
while(samples_left > 0)
{
    // Send up to 40 samples at a time (40x6=240 bytes .. fits within BLE packet with minimal waste).
    if((samples_left % 40) > 0) { frame_samples = 40; }
    else { frame_samples = samples_left; }
    
    // Setup response len in bytes (6 per sample plus cmd extra, len byte, frame indices). Always <= 244
    cmd_resp_len = (frame_samples * 6) + 4;
    
    // Update header.
    resp_bytes[0] = 0x018;          // Extra response data.
    resp_bytes[1] = (uint8_t) cmd_resp_len;
    resp_bytes[2] = (uint8_t) (sample_idx >> 8);
    resp_bytes[3] = (uint8_t) (sample_idx & 0x00FF);

    // Update payload.
    memcpy(&(resp_bytes[4]), &(xtag_get_ramstore_ptr()[sample_idx]), frame_samples * 6);

    // Send.
    do
    {
      err_code = ble_nus_data_send(&m_nus, resp_bytes, &cmd_resp_len, m_conn_handle);
    } while (err_code == NRF_ERROR_RESOURCES);

    //SEGGER_RTT_printf(0, "Sent sample_idx %d\n", sample_idx);

    // Inc sample_idx based on the number of samples sent.
    sample_idx += frame_samples;
        
    // Dec samples_left based on the number of samples actually sent.
    samples_left -= frame_samples;

    // Debug.
    //nrf_delay_ms(3);         
}

... within a loop with no sleeps. Logging shows that I am able to write out 244 bytes evey 50 mSec (20 per sec).

From what I read here: https://infocenter.nordicsemi.com/index.jsp?topic=%2Fsds_s140%2FSDS%2Fs1xx%2Fs140.html
... I should be able to go much faster.

Setup info ...
Defines:
My codebase:
#define APP_ADV_INTERVAL                1600                                     // Mult of 0.625 ms. Use 1600 for 1 sec.
#define APP_ADV_DURATION                18000                                   // 180 seconds.  Must be mult of 10 mSec.
#define APP_BLE_OBSERVER_PRIO           3                                       // Default
#define APP_BLE_CONN_CFG_TAG            1                                       // SoftDevice BLE configuration.

#define MIN_CONN_INTERVAL               MSEC_TO_UNITS(20, UNIT_1_25_MS)        // Minimum acceptable connection interval (0.1 seconds). 100, 20
#define MAX_CONN_INTERVAL               MSEC_TO_UNITS(40, UNIT_1_25_MS)        // Maximum acceptable connection interval (0.2 second). 200, 75
#define SLAVE_LATENCY                   0                                       // Slave latency. 
#define CONN_SUP_TIMEOUT                MSEC_TO_UNITS(4000, UNIT_10_MS)         // Connection supervisory timeout (4 seconds). 1000

#define FIRST_CONN_PARAMS_UPDATE_DELAY  APP_TIMER_TICKS(5000)                   // Time from initiating event (connect or start of notification) to first time sd_ble_gap_conn_param_update is called (5 seconds). */
#define NEXT_CONN_PARAMS_UPDATE_DELAY   APP_TIMER_TICKS(30000)                  // Time between each call to sd_ble_gap_conn_param_update after the first call (30 seconds). */
#define MAX_CONN_PARAMS_UPDATE_COUNT    3                                       // Number of attempts before giving up the connection parameter negotiation. */

sdk_config.h:
// <i> Requested BLE GAP data length to be negotiated.

#ifndef NRF_SDH_BLE_GAP_DATA_LENGTH
#define NRF_SDH_BLE_GAP_DATA_LENGTH 251
#endif

// <o> NRF_SDH_BLE_PERIPHERAL_LINK_COUNT - Maximum number of peripheral links. 
#ifndef NRF_SDH_BLE_PERIPHERAL_LINK_COUNT
#define NRF_SDH_BLE_PERIPHERAL_LINK_COUNT 2
#endif

// <o> NRF_SDH_BLE_CENTRAL_LINK_COUNT - Maximum number of central links. 
#ifndef NRF_SDH_BLE_CENTRAL_LINK_COUNT
#define NRF_SDH_BLE_CENTRAL_LINK_COUNT 0
#endif

// <o> NRF_SDH_BLE_TOTAL_LINK_COUNT - Total link count. 
// <i> Maximum number of total concurrent connections using the default configuration.

#ifndef NRF_SDH_BLE_TOTAL_LINK_COUNT
#define NRF_SDH_BLE_TOTAL_LINK_COUNT 2
#endif

// <o> NRF_SDH_BLE_GAP_EVENT_LENGTH - GAP event length. 
// <i> The time set aside for this connection on every connection interval in 1.25 ms units.

#ifndef NRF_SDH_BLE_GAP_EVENT_LENGTH
#define NRF_SDH_BLE_GAP_EVENT_LENGTH 6
#endif

// <o> NRF_SDH_BLE_GATT_MAX_MTU_SIZE - Static maximum MTU size. 
#ifndef NRF_SDH_BLE_GATT_MAX_MTU_SIZE
#define NRF_SDH_BLE_GATT_MAX_MTU_SIZE 247 // From UART EG
#endif

// <o> NRF_SDH_BLE_GATTS_ATTR_TAB_SIZE - Attribute Table size in bytes. The size must be a multiple of 4. 
#ifndef NRF_SDH_BLE_GATTS_ATTR_TAB_SIZE
#define NRF_SDH_BLE_GATTS_ATTR_TAB_SIZE 1408
#endif

// <o> NRF_SDH_BLE_VS_UUID_COUNT - The number of vendor-specific UUIDs. 
#ifndef NRF_SDH_BLE_VS_UUID_COUNT
#define NRF_SDH_BLE_VS_UUID_COUNT 1
#endif

// <q> NRF_SDH_BLE_SERVICE_CHANGED  - Include the Service Changed characteristic in the Attribute Table.
 

#ifndef NRF_SDH_BLE_SERVICE_CHANGED
#define NRF_SDH_BLE_SERVICE_CHANGED 1
#endif

Inits:
In main():
// Init BLE elements.
ble_stack_init();
gap_params_init();
gatt_init();
xtag_adv_config(true);
services_init();
conn_params_init();

Implementations:
// Gap params init from egs.
void gap_params_init(void)
{
  ret_code_t              err_code;
  ble_gap_conn_params_t   gap_conn_params;
  ble_gap_conn_sec_mode_t sec_mode;
  uint8_t id[BLE_GAP_ADDR_LEN];

  BLE_GAP_CONN_SEC_MODE_SET_OPEN(&sec_mode);

  // Get the address from the device.
  ble_gap_addr_t cur_gap_addr;
  err_code = sd_ble_gap_addr_get(&cur_gap_addr);
  APP_ERROR_CHECK(err_code);

  // Save off the MAC format as our ID (local and in xtag_data).
  memcpy(id, cur_gap_addr.addr, BLE_GAP_ADDR_LEN);
  xtag_set_id(id);

  // Save back ensuring type is public.
  cur_gap_addr.addr_type = BLE_GAP_ADDR_TYPE_PUBLIC;
  sd_ble_gap_addr_set(&cur_gap_addr);
  APP_ERROR_CHECK(err_code);

  // Setup and use a device name that includes the ID suffix bytes.
  char name_w_id[strlen(DEVICE_NAME) + DEVICE_NAME_SUFFIX_LEN];
  memset(name_w_id, 0, sizeof(name_w_id));
  sprintf(name_w_id, "%s %02X%02X%02X%02X%02X%02X", DEVICE_NAME, id[5], id[4], id[3], id[2], id[1], id[0]);
  err_code = sd_ble_gap_device_name_set(&sec_mode, (const uint8_t *)name_w_id, strlen(name_w_id));
  APP_ERROR_CHECK(err_code);

  // We will expose ourselves as a generic "tag".
  err_code = sd_ble_gap_appearance_set(BLE_APPEARANCE_GENERIC_TAG);
  APP_ERROR_CHECK(err_code);

  memset(&gap_conn_params, 0, sizeof(gap_conn_params));

  gap_conn_params.min_conn_interval = MIN_CONN_INTERVAL;
  gap_conn_params.max_conn_interval = MAX_CONN_INTERVAL;
  gap_conn_params.slave_latency     = SLAVE_LATENCY;
  gap_conn_params.conn_sup_timeout  = CONN_SUP_TIMEOUT;

  err_code = sd_ble_gap_ppcp_set(&gap_conn_params);
  APP_ERROR_CHECK(err_code);
}

/// Init GATT
void gatt_init(void)
{
  ret_code_t err_code = nrf_ble_gatt_init(&m_gatt, NULL);
  APP_ERROR_CHECK(err_code);

  err_code = nrf_ble_gatt_att_mtu_periph_set(&m_gatt, NRF_SDH_BLE_GATT_MAX_MTU_SIZE);
  APP_ERROR_CHECK(err_code);
}

// Connection parameters.
static void on_conn_params_evt(ble_conn_params_evt_t * p_evt)
{
  ret_code_t err_code;

  if (p_evt->evt_type == BLE_CONN_PARAMS_EVT_FAILED)
  {
    err_code = sd_ble_gap_disconnect(m_conn_handle, BLE_HCI_CONN_INTERVAL_UNACCEPTABLE);
    APP_ERROR_CHECK(err_code);
  }
}

// Connection parameters error hander.
static void conn_params_error_handler(uint32_t nrf_error) { APP_ERROR_HANDLER(nrf_error); }

// Function for initializing the Connection Parameters module.
void conn_params_init(void)
{
  ret_code_t             err_code;
  ble_conn_params_init_t cp_init;

  memset(&cp_init, 0, sizeof(cp_init));

  cp_init.p_conn_params                  = NULL;
  cp_init.first_conn_params_update_delay = FIRST_CONN_PARAMS_UPDATE_DELAY;
  cp_init.next_conn_params_update_delay  = NEXT_CONN_PARAMS_UPDATE_DELAY;
  cp_init.max_conn_params_update_count   = MAX_CONN_PARAMS_UPDATE_COUNT;
  cp_init.start_on_notify_cccd_handle    = BLE_GATT_HANDLE_INVALID;
  cp_init.disconnect_on_fail             = false;
  cp_init.evt_handler                    = on_conn_params_evt;
  cp_init.error_handler                  = conn_params_error_handler;

  err_code = ble_conn_params_init(&cp_init);
  APP_ERROR_CHECK(err_code);
}

// Init the BLE stack.
void ble_stack_init(void)
{
  ret_code_t err_code;

  // CRITICAL CHANGE !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
  // nrf_sdh_enable_request implementation changed to support use of the rc clock in the Laird DK.
  // Original clock setup witin nrf_sdh_enable_request (in nrf_sdh.c) has been commented out
  // and changed for rc clock support. Replace updates with original code to get back external
  // 32.xxkHz crystal support as is typical in nRF DKs and our xTag design.
  err_code = nrf_sdh_enable_request();
  APP_ERROR_CHECK(err_code);

  // Configure the BLE stack using the default settings.
  // Fetch the start address of the application RAM.
  uint32_t ram_start = 0; // RAM_START 0x20002210 in project (common) linker settings (Section Placement Macros) 
  err_code = nrf_sdh_ble_default_cfg_set(APP_BLE_CONN_CFG_TAG, &ram_start);
  APP_ERROR_CHECK(err_code);

  // Enable BLE stack.
  err_code = nrf_sdh_ble_enable(&ram_start);
  APP_ERROR_CHECK(err_code);

  // Register a handler for BLE events.
  NRF_SDH_BLE_OBSERVER(m_ble_observer, APP_BLE_OBSERVER_PRIO, ble_evt_handler, NULL);
}



Some notes.
I am not enabling the peer manager. No pairing or bonding required.
I have not changed the phy. I understand I can go a little faster by selecting 2 Mbit/s, but I think that I should resolve timing or other issues first.
I am using a scheduler to process NUS IO on the main thread. When I receive a "command" over NUS from Android, I schedule a task using the scheduler. In this task, I read from NUS (a couple of commands) and then write 24 kBytes back (see do loop in code above). This 24kB transfer is slow. 
static void nus_data_handler(ble_nus_evt_t * p_evt)
{
  if (p_evt->type == BLE_NUS_EVT_RX_DATA)
  {
    // Get the incomming cmd bytes.
    memcpy(nus_cmd, p_evt->params.rx_data.p_data, p_evt->params.rx_data.length);

    // Schedule handling on main() via scheduler. Scheduler will just call back
    // nus_shed_data_handler() below.
    app_sched_event_put(nus_cmd,  p_evt->params.rx_data.length, nus_on_main_event_sched_handler);
  }
}


Are there some config changes that I can make to get the speed up? It looks like I am only getting 1 packet processed in the interval (or slot?) and that this slot time is around 50 mSec. I would expect to be able to get multiple packets per slot and multiple slots every 50 mSec.
In general, this seems very slow and I should be able to speed it up quite dramatically.

Thanks in advance for suggestions. I am happy to provide more info as required.

Mark J

Parents Reply Children
No Data
Related