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

Connection Parameter Selection for Central Device with 10 Peripheral Simultaneous Connections

Hi,

I am developing both Central and Peripheral devices with nRF52840. For consistency, both are using SDK 15.3.0 and S140 V7.

I am able to connect to peripherals and negotiate MTU ATT size of 247 and I am able to negotiate 2 Mbps phy. Logging on both peripheral and central devices confirm this is working.

My challenge is getting notification data from my peripherals to my central. If I set up (for example) 2 peripherals to notify my central device every 500 mSec with approx 160 bytes of data, all data comes into the BLE event handler (BLE_GATTC_EVT_HVX) within my central device just fine. If I double the data speed on peripherals (ie. 160 bytes every 250 mSec from each of the 2 devices), I only get BLE_GATTC_EVT_HVX events notifying me of incoming data from the first peripheral only.

I believe that I am not getting correct connection service intervals setup after peripheral connection.

For a scenario where a central is talking to 10 peripherals and getting notification data from each every 250 mSec, what would good connection interval, slave latency values be? I cannot seem to find a good reference for setup of connection intervals for multiple simultaneous peripheral connections to a central device - where each peripheral is notifying the central independently.

Note that my connection event length is 6 x 1.25 mSec or 7.5 mSec. This should be plenty long enough to get an MTU of 247 bytes transferred.

I have been using default min and max connection intervals of 7.5 and 30 mSec respectively and a slave latency of 0 (for both central and peripheral devices).

Suggestions for my scenario would be much appreciated.

Thanks in advance,

Mark J

Parents
  • Hello Mark,

    Sorry for the late reply. We are quite short staffed on support due to summer holidays. I am sorry, I didn't have time to reply before the weekend. Answering only your first post first:

    Are you sure that you are only receiving data from one peripheral connection? Perhaps you are spending too much time in the interrupt from the first device, so that the second device is not handling. I assume (based on your case history) that you are using the Nordic Uart Service (NUS). Do you print all the incoming data on the UART? What happens if you only try to leave the interrupt immediately, and just print something like: NRF_LOG_INFO("notification received from conn_handle %02x", conn_handle) whenever you receive data? Do you get both devices then?

  • Hi 

    As per my last entry: Increase Supported Links to Peripherals on a Central Device

    ... I took out NUS as it would not work in providing me a proper reference. I am now using direct connection handles and this is working better in that I always seem to get the correct reference to the connected peripheral sending me notification data.

    I am leaving the interrupt quickly and running my code on the main thread using a scheduler.  Here is my main loop:

     // Enter main loop.
      while (true)
      {
        // Proc USB events.
        while (app_usbd_event_queue_process()) { /* Nothing to do */  }
    
        // if(tempCnt++ >= 300000) { SEGGER_RTT_printf(0, "X\n"); tempCnt = 0;}
    
        // Handled scheduler actions.
        app_sched_execute();
    
        // Manage power.
        // nrf_pwr_mgmt_run(); 
      }

    By printing "x" I can see that my loop iterates < 10 uSec, so I am leaving the interrupt quickly and able to service the scheduler quickly on the main thread. I suspected that scheduling delay and handler execution time (ie. too long in a handler) may be the issue, but I am quite convinced it is related to the radio. More in my next reply below.

    IMPORTANTLY - I am not interested in scanning when transferring data. I am only scanning to setup connections. I scan, stop scanning, connect to my 10 peripherals and then I request that they notify me every 250 mSec. I have to setup a ble_gap_scan_params_t* to pass to sd_ble_gap_connect() - when connecting to each peripheral. I have been using the same scan interval and window within this ble_gap_scan_params_t object. Maybe that is my issue? If I want no scanning after connection, can I set this ble_gap_scan_params_t* to null or some other value that drastically or totally eliminates scanning after connections?

    Regards,

    Mark J 

  • Hello Mark,

    Try to enable debug logging (NRF_LOG_ENABLED 1 and NRF_LOG_DEFAULT_LEVEL 4 in sdk_config.h) and connect the devices. If you do this e.g. in the ble_app_uart + ble_app_uart_c examples, you will see the logging from the BLE_GAP_EVT_DATA_LENGTH_UPDATE event printed from nrf_ble_gatt.c:

    <debug> nrf_ble_gatt: Data length updated to 251 on connection 0x0.
    <debug> nrf_ble_gatt: max_rx_octets: 251
    <debug> nrf_ble_gatt: max_tx_octets: 251
    <debug> nrf_ble_gatt: max_rx_time: 2120
    <debug> nrf_ble_gatt: max_tx_time: 2120

    The time is given in µs. Is the connection rx time and tx time what you expect them to be?

    When you say that you are loosing packets, do you claim that packets that are queued using sd_ble_gatts_hvx() are queued successfully (returns NRF_SUCCESS), but the packets never occur?

    BR,

    Edvin

  • HI ,

    I have enabled logging of the length update on both peripherals and central (Segger logging). For both, I am simply handling the ble event:

    case BLE_GAP_EVT_DATA_LENGTH_UPDATE:
    {
      p_datalen_evt = &p_ble_evt->evt.gap_evt.params.data_length_update;
    
      SEGGER_RTT_printf(0, "Data length (PDU) update. Max Tx octets: %d, Max Rx octets %d, Max Tx uSec: %d, Max Rx uSec: %d\n",
        p_datalen_evt->effective_params.max_tx_octets, p_datalen_evt->effective_params.max_rx_octets, 
        p_datalen_evt->effective_params.max_tx_time_us, p_datalen_evt->effective_params.max_rx_time_us);
    
        break;
    }

    Logger output is the same for peripherals and central:

    Data length (PDU) update. Max Tx octets: 251, Max Rx octets 251, Max Tx uSec: 2120, Max Rx uSec: 2120

    I am setting the connection event length identically for peripheral and central in sdk_config.h:

    #ifndef NRF_SDH_BLE_GAP_EVENT_LENGTH
    #define NRF_SDH_BLE_GAP_EVENT_LENGTH 8  // See xtag
    #endif  

    The 8 x 1.25 mSec entered should stipulate tx and rx max times of 10 mSec and not 2.12 mSec as logged, shouldn't they? Maybe there is an issue in how the connection event length is setup (ie more is required beyond setting NRF_SDH_BLE_GAP_EVENT_LENGTH  to 8).

    To directly answer your last question ... yes. I am getting NRF_SUCCESS returned after calling sd_ble_gatts_hvx() on peripherals to notify the central with data. On the central, I am processing BLE_GATTC_EVT_HVX events resulting from these notifications. When I continuously write data (240 bytes discussed above) I count sd_ble_gatts_hvx() calls on peripherals and resulting BLE_GATTC_EVT_HVX events on the central. If I don't send notifications too fast, counts on peripherals and central match. Only when I send data too quickly (as discussed in this thread), do I see count mismatches. The count is higher on the peripherals than central - due to some notifications not getting through.

    Is there any code change suggested to get the connection event length setup correctly (assuming that the 2120 uSec is incorrect given that I set NRF_SDH_BLE_GAP_EVENT_LENGTH  to 8)?

    Thanks,

    Mark J

  • ... just to confirm ....

    I am calling nrf_sdh_ble_default_cfg_set() with NRF_SDH_BLE_TOTAL_LINK_COUNT > 0 on both peripherals and central. Within the implementation of nrf_sdh_ble_default_cfg_set(), my NRF_SDH_BLE_GAP_EVENT_LENGTH  value of 8 is used. So, I think that I am setting connection event length correctly to 10 mSec.

    Thanks,

    Mark J

  • The event length sets the maximum event length. The length printed in the event is the time set aside to send 251 octets. 

    I would like to reproduce this from my side. How do I do that? Preferably, you can zip and send the projects that you use to replicate this issue. I don't remember if I have asked for this before. It has been a while since we discussed this ticket (before your post 3 days ago). I would like to see what the lost notifications look like.

  • Hi ,

    Providing my full central and peripheral project code would be a bad idea as it would include a lot of code. I will create a new central and new peripheral project - each set up to run on the nrf52 DK (pca10056). These will include the same BLE calls and will be setup to show how changing connection event times, etc are influencing throughput and leading to missing packets. I will just cut and paste into these projects from my current code.

    How can I send this code to you? I could include a Google Drive link in a dev zone reply and simply remove the code from the linked Google folder after you have retrieved it. Is there a better way?

    Regards,

    Mark J

Reply
  • Hi ,

    Providing my full central and peripheral project code would be a bad idea as it would include a lot of code. I will create a new central and new peripheral project - each set up to run on the nrf52 DK (pca10056). These will include the same BLE calls and will be setup to show how changing connection event times, etc are influencing throughput and leading to missing packets. I will just cut and paste into these projects from my current code.

    How can I send this code to you? I could include a Google Drive link in a dev zone reply and simply remove the code from the linked Google folder after you have retrieved it. Is there a better way?

    Regards,

    Mark J

Children
  • Hi ,

    I have just realized that I have 2 nRF52 DKs that are rev pca10040 and not pca10056, so I will setup projects for these DKs. I assume that you can get access to pca10040 DKs - so this selection should not be an issue.

    Regards,

    Mark J

  • Mark J said:
    I assume that you can get access to pca10040 DKs

     Yes I can. 

    Please note that the pca10040 DKs are not nRF52840. They are nRF52832. 

    You can upload .zip folders here. Just drag'n'drop to the text box that you are writing to here. Alternatively, you can use insert->image/video/file-> and press "upload"

    After a DevZone update this no longer looks like a button, so I understand that it is not trivial, but it is:

    What you can do is to zip the project folder that you are working on. If it was the ble_app_hrs example, just zip that folder. It contains the main.c file, and the sdk_config.h file. If you have some custom files, please include them there, and make sure that the project file is linked to them. That is, if you unzip your folder into an unmodified SDK, please check that it compiles, and behaves the way that it does in your original project. If not, it means that you have modified files outside this project folder, and I will not see the same as you do. 

    Best regards,

    Edvin

  • Hi ,

    I am still porting code to ble_app_uart (peripheral) and ble_app_uart_c (central) pca10040 projects (ie replacing the code in this projects that is setup for pca10040 with my code). I am about 2/3 finished and will have working projects that you can run on nrf52 Dev Kits tomorrow. 

    When moving my code to these test projects, I noticed something that might by an issue. I have been assuming that the data processed within my central when each BLE_GATTC_EVT_HVX  event occurs is a single packet with length of 243 bytes or less. 

    case BLE_GATTC_EVT_HVX:
    {
      // Setup reusable vars.
      ble_gattc_evt_t const * p_gatt_evt = &p_ble_evt->evt.gattc_evt;
    
      // Process p_gatt_evt->params.hvx.data with length p_gatt_evt->params.hvx.len
      // Assume that processing is for a single BLE packet with length < max MTU size.
    
      break;
    }

    I have assumed that there will be a BLE_GATTC_EVT_HVX event generated for each notification that I send. Today I noticed that the p_gatt_evt->params.hvx.len is a uint16_t. If, on my peripheral, I call sd_ble_gatts_hvx() twice in rapid succession, each time with a hvx_params.p_len of 240 bytes, can I get a SINGLE event at my central (BLE_GATTC_EVT_HVX) that has a length of 480? This could explain my issue as I am casting p_gatt_evt->params.hvx.len to a uint8_t assuming it would always be 240 bytes or less. I could be getting more than 1 packet in a single BLE_GATTC_EVT_HVX event with a p_gatt_evt->params.hvx.len that is greater than 255. When this happens, I would ignore the data beyond 255 bytes.

    Please advise if multiple received packets can be consolidated into a single BLE_GATTC_EVT_HVX event.

    Thanks,

    Mark J

  • I assumed it would generate the same number of events as the number of sd_ble_gatts_hvx(). But you should check the

    p_ble_evt->evt.gattc_evt.params.hvx.len

    I suppose it may be that they are merged if they originate from one notification. (a notification may be larger than the MTU, I think). 

    BR,

    Edvin

  • Given the importance of understanding if the sd_ble_gatts_hvx() call supports more than 1 packet and understanding if multiple packets could result in a single BLE_GATTC_EVT_HVX event, I checked S140 sd_ble_gatts_hvx() Details and BLE_GATTC_EVT_HVX Details. These descriptions seem pretty clear. Only 1 packet can be sent with sd_ble_gatts_hvx() and BLE_GATTC_EVT_HVX results when each packet is received (not multiple packets). Given that this is critically important - I would suggest that it be confirmed (ie the docs could be wrong or I may misunderstand them).

    Thanks,

    Mark J

Related