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 

  • 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

  • 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

Reply Children
  • 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

  • I have created a peripheral and central app that - sort of shows the issue. First, the apps.

    I have simply updated the ble_app_uart peripheral example under my SDK folder: nRF5_SDK_15.3.0_59ac345\examples\ble_peripheral\ble_app_uart. Zip here.

    8422.ble_app_uart.zip

    This exposes my service for connection and when the central app connects to it and sends a simple command (automatically) it notifies the central with 240 bytes (rotating byte pattern) every 25 mSec. It stops automatically after 401 notifications (10 sec).

    I have also updated the ble_app_uart_c central example under my SDK folder: nRF5_SDK_15.3.0_59ac345\examples\ble_central\ble_app_uart_c. Zip here:

    7848.ble_app_uart_c.zip

    This central scans for the peripheral and automatically connects to it, queries services and registers for notifications. After this, it sends a simple 0xAA, 0xBB command to the peripheral - which starts the peripheral sending notifications (for 10 sec).

    NRF_SDH_BLE_GAP_EVENT_LENGTH is set to 8 (10 mSec) for both peripheral and central and an MTU and data length is negotiated upon connection that is larger than the required 240 bytes (on both peripheral and central). Data extension should be enabled on both as well (no error response).

    If you set a notification interval on both peripheral and central by setting min and max (equal) of 80 mSec or greater, you will see that some notifications are missed (i.e. you won't get the 401 notifications expected at the central). On the peripheral, sd_ble_gatts_hvx() will return NRF_ERROR_RESOURCES on occasion when it cannot send a notification. If you use a notification interval of 70 sec or less, you will get all 401 notifications at the central.

    The issue in my app that I am trying to recreate in these examples is similar - but not identical. I get the same NRF_ERROR_RESOURCES response from sd_ble_gatts_hvx() on my peripherals when they cannot send notifications, but I am seeing this issue even when I use connection interval of 25 mSec or greater. In my app, I must keep the connection interval below 25 mSec and in examples, it must be kept below 70 or 75 mSec. I need to support a connection interval of 50 mSec or greater in my app to be able to service 4 connections with event length of 10 mSec.

    My app (peripheral and central) NRF_SDH_BLE_GAP_EVENT_LENGTH, MTU and data length match the attached examples. The biggest difference between the attached ble_app_uart example and my peripheral app is that my app also interacts with an accelerometer over spi to get sample data. The attached example simply transfers stored memory with fake data.

    I suspect the attached example fails whenever the connection interval is more than 3x the notification period of 25 mSec (i.e. it can only queue 3 notifications). Maybe this queue in my app is only 1 and that is why the connection interval in my app must be less than 25 mSec?

    If not related to notification queuing, the issue may simply relate to the fact that I am using more processor cycles to interact with an accelerometer.

    To run the examples, just place them under SDK 15.3 "examples" folders shown above, compile them and then run them in debug. Just start the ble_app_uart example and then the ble_app_uart_c example and watch the debug output from both.

    Regards,

    Mark J

Related