BLE Connection Issue with Multiple Simultaneous Peripherals

I have a BLE system where multiple sensors operate as peripherals and send data to a BLE central. These sensors enter sleep mode after transmitting data and later wake up to send new information.

I have observed an issue when increasing the number of sensors connected to the same central. Specifically, when two sensors become available for connection simultaneously, communication starts to fail. This affects the system's stability and impacts the reliability of data transmission.

Currently, I establish connections using the UUID of the NUS service, while other BLE parameters, such as the device name, remain identical among the sensors.

Communication Flow:

  1. The central identifies a peripheral through advertising.

  2. The central establishes a connection with the peripheral.

  3. The central stops the scanning process.

  4. The peripheral and central exchange information.

  5. The central disconnects from the peripheral.

  6. The central restarts scanning while two peripherals are advertising simultaneously.

Observed Behavior:

  • If only one sensor is available for connection, the process works correctly.

  • If two sensors are available simultaneously, communication fails, and the central cannot manage connections properly.

  • When this issue occurs, communication starts timing out, there are unexpected disconnections, and intermittent failures appear, such as the following:

Questions:

  1. Are there any known limitations in managing multiple sequential connections with peripherals that have similar parameters?

  2. Are there any recommendations to handle this scenario better? Any specific configurations in Zephyr or SoftDevice that could help?

  3. Any best practices to differentiate peripherals and avoid connection conflicts?

I appreciate any guidance that can help resolve this issue.

Best regards,

Parents
  • Hi Arthur, 

    We will need to take a look at how you handle multiple connections in your central code. 
    Please show your code and your project configuration. 

    Have you looked at this issue: 
    00> [01:58:58.610,198] <err> nus_c: bt_nus_handles_assign: Missing NUS TX characteristic.
    00> [01:58:58.610,198] <err> nus_c: bt_nus_subscribe_receive: Subscribe failed (err -128)


    Have you made sure you do service discovery correctly when connect to a new peripheral ? 

    I would suggest to get familiar with the nRF Sniffer. It's very useful for debugging.  

  • Thanks for your response.

    Currently, our central does not support multiple connections. It simply connects to the first peripheral that appears during the scanning process and performs the entire data exchange procedure with that device.

    We’ve only observed the issue when we have too many sensors operating at the same time — specifically when two or more peripherals are advertising simultaneously at the exact moment the central starts scanning. I’ve attached the central_ble file so you can take a look.

    In this case, is implementing multiple simultaneous connections strictly necessary to avoid this failure? If so, would you happen to have an example suited to this use case?

    Our idea is to keep the current approach: connect to the first sensor that appears, perform the data exchange, and disconnect. The other peripherals would only send a command or flag indicating they should wait or disconnect if not selected.

    Let me know your thoughts. Thanks again for the support!

    Best regards,

    Arthur

  • Hi Arthur, 

    What you describe is a very common use case for a central device that it need to scan for a peripheral and connect to it. I am not aware of any issue related to the central has problem when multiple peripherals advertise. I don't think implementing multi connection handling is the way to go here. 

    From what I can see in your log is that right after connection the link is terminated: 

    But I don't fully understand this: 
    00> [01:58:58.393,951] <inf> MAIN: set_state: [STATE_BLE_DISCONNECTION] -> [STATE_BLE_DISCOVERY]
    00> [01:58:58.494,049] <inf> MAIN: ble_discovery_state: BLE Discovery State

    What is this discovery state ? Can you show the code ? 

    After the device get disconnected the process of discovery, subscribe to the characteristic or sending data should be stopped. But from the log it was not. The state machine continued after the disconnection. So you need to check your state machine and should not continue to call bluetooth_central_send_data() if the link is disconnected or if bt_nus_subscribe_receive() return failed. 

    Do you see the same problem when you test with our central_uart sample ?

  • Hi Hung,

    Thanks again for your previous feedback.

    I’m following up with more information and a detailed timeline of the issue I’m facing. Our setup involves a BLE central that connects to one sensor at a time, performs data exchange (about 100 packets, 98 of them with 492 bytes each), and then disconnects. After disconnecting, the central restarts scanning. Each sensor goes into deep sleep for 60 seconds after completing communication before reattempting advertising.

    Both the central and peripherals are using CODED PHY.

    Here’s a simplified timeline illustrating the expected behavior with 3 sensors, which works reliably:

    Each communication lasts about 20 seconds, and this sequencing has worked well with up to 3 sensors. However, when a 4th sensor is introduced, failures start to appear. Below is a log extract showing several disconnection reasons and related error codes

    Key Observations:

    • Some connections fail early during data exchange with disconnection reason 8, often at varying packet counts (e.g., packet 70, 43, or even 0).

    • In certain cases, the NUS service cannot be properly discovered:

    • Disconnection reason 19 is also seen, though it usually indicates normal disconnection after successful communication.

    Below is a summarized log highlighting key connection and disconnection events for each sensor, including failure reasons and relevant error messages.

    The full log with all BLE events and debug messages follows right after the summary.

    The following is the complete log output, including all BLE events, debug messages, and data transfer notifications observed during the test.

    For reference, the MAC addresses of the sensors are:

    • Sensor 1 → C8:71:CB:20:81:0A

    • Sensor 2 → D3:D4:C0:F4:AC:94

    • Sensor 3 → E1:35:E1:4C:E8:10

    • Sensor 4 → E7:EA:8B:10:13:35

    Behavior Summary:

    • The central continues scanning and connecting even after failures, though there’s a slight delay due to a connection timeout.

    • Sensors advertise for 45 seconds, waiting for a connection. If none occurs, they enter deep sleep.

    • The system works well with 3 sensors but shows intermittent failures when a 4th sensor is added — even though the timing should, in theory, allow it.

    I’m currently working on capturing a sensor-side log during a failed connection attempt to provide further insight from both ends of the communication.

    Please let me know if there’s any additional configuration or timing consideration that could affect system stability when multiple peripherals are active. Any suggestions regarding the bt_nus_subscribe_receive error (-128) or best practices for handling service discovery during quick reconnects would also be greatly appreciated.

    Thanks again for your support!

    Best regards,

    Central Bluetooth.c


    Central main.c

    Peripheral Bluetooth.c

    Peripheral main.c

  • Hi Arthur, 
    I think there was something wrong with your state machine. 
    This transition should never happen: 
    set_state: [STATE_BLE_DISCONNECTION] -> [STATE_BLE_DISCOVERY]

    When the peripheral is already disconnected there is no point (and not possible) to do service discovery. 
    So you should check that and stop the state machine to continue if the link is disconnected. 

    The next step is to check why the link is terminated. I would suggest to use the sniffer to track that. Have you found a reliable way to reproduce the disconnection ? 

  • Thanks for the clarification!

    Just to avoid confusion: the states within MAIN, such as STATE_BLE_DISCOVERY, STATE_BLE_CONNECTED, and STATE_BLE_DISCONNECTION, are not directly related to the actual BLE stack state. These are legacy names carried over from other applications, and their naming might be misleading.

    For example, STATE_BLE_DISCOVERY does not perform any BLE service discovery. It simply waits for a semaphore to be released before proceeding to the next logical step. Similarly, the actual BLE handling—such as connection, service discovery, subscription, and data exchange—is entirely managed in the module labeled as BLE in the logs, specifically within the central_bluetooth.c file I previously attached.

    At the moment, I don’t have access to a sniffer to capture and analyze the BLE packets, but I’ll work on setting that up in order to better investigate the disconnection behavior.

    Thanks again for your support!

    Best regards,

  • Hi Arthur, 

    Arthur J Sary said:
    At the moment, I don’t have access to a sniffer to capture and analyze the BLE packets, but I’ll work on setting that up in order to better investigate the disconnection behavior.

    You just need to have a NRF52 DK to use at the sniffer backend. Having the ability to sniffer the connection will help a lot on debugging BLE issue. Strongly recommend, it will save you a lot of time. 

    I had to ask about the discovery state because in the log I can see :

    So "Missing NUS TX characteristic" and "Subscribe failed (err -128)" come after the disconnection. If you just started the discovery and subscription right before the disconnection then it's normal. If you start it after the disconnection then something need to be changed. 

    But we would also need to investigate if these error also come when the connection has not been terminated. Then there must be something wrong with the peer device or the discovery process. 

    We need to break the issues down, separate the disconnection issue and the service discovery issue. 

    Are you saying that if you only do 1 peripheral you never seen a disconnection but if you  have 2 peripherals you start seeing disconnection ? 
    Please also show the connection parameter, for example the timeout and the connection interval. 

    Please be aware that the CODED PHY when it has longer range it also has longer packet size (x8) so there will be more chance of collision if you have a lot of interference. Please do the test with 1Mbps for comparison. 

Reply
  • Hi Arthur, 

    Arthur J Sary said:
    At the moment, I don’t have access to a sniffer to capture and analyze the BLE packets, but I’ll work on setting that up in order to better investigate the disconnection behavior.

    You just need to have a NRF52 DK to use at the sniffer backend. Having the ability to sniffer the connection will help a lot on debugging BLE issue. Strongly recommend, it will save you a lot of time. 

    I had to ask about the discovery state because in the log I can see :

    So "Missing NUS TX characteristic" and "Subscribe failed (err -128)" come after the disconnection. If you just started the discovery and subscription right before the disconnection then it's normal. If you start it after the disconnection then something need to be changed. 

    But we would also need to investigate if these error also come when the connection has not been terminated. Then there must be something wrong with the peer device or the discovery process. 

    We need to break the issues down, separate the disconnection issue and the service discovery issue. 

    Are you saying that if you only do 1 peripheral you never seen a disconnection but if you  have 2 peripherals you start seeing disconnection ? 
    Please also show the connection parameter, for example the timeout and the connection interval. 

    Please be aware that the CODED PHY when it has longer range it also has longer packet size (x8) so there will be more chance of collision if you have a lot of interference. Please do the test with 1Mbps for comparison. 

Children
  • Hi Hung,

    Thanks again for the detailed explanation and suggestions.

    I understand the concern regarding whether the discovery and subscription happen after the disconnection, and I agree that this would be a problem. However, based on the logs I sent earlier, it seems clear that the disconnection happens after the discovery/subscription attempt fails — not before.

    For example, in the snippet below, the timeline is quite clear:

    We can see that the Missing NUS TX characteristic and the subscribe failed errors happen before the disconnection event is logged. So, the state transitions and discovery logic seem to be happening in the correct order.

    Regarding your other questions:

    Are you saying that if you only do 1 peripheral you never see a disconnection but if you have 2 peripherals you start seeing disconnection?

    No, the system works perfectly fine with up to 3 peripherals. The problem only starts to appear when I introduce a 4th peripheral, and all of them are communicating within the same 60-second window. This increases the central's BLE activity to almost 100% utilization, with very little idle scanning time between connections.

    As mentioned before, the peripherals advertise for 45 seconds, and if not connected, they go into deep sleep for 60 seconds. This pattern keeps the advertising period tight and overlapping when multiple sensors are active.

    If I switch to 1 Mbps PHY instead of CODED PHY, I need to increase the number of peripherals proportionally to reproduce the same failure — presumably because communication becomes faster and less congested, allowing more idle time between sessions.

    Regarding the connection parameters — I’m not entirely sure of the exact values being used, but they should be defined within the bluetooth.c files I previously attached.

    From what I found:

    For the peripheral, advertising is configured using:

    For the central, the scan and connection parameters are:

    So it looks like I'm using the default Zephyr macros for fast scanning and advertising intervals, but I haven’t explicitly overridden connection interval, latency, or supervision timeout. If needed, I can extract the resolved values during runtime using logging or update the config with fixed parameters for more control.

    Let me know if you'd recommend using specific values here to help avoid collisions or improve handling under high peripheral load.

    Let me know if you’d like me to extract a few more specific log points, or if you think it would be useful to experiment with slightly staggered advertising windows between the sensors.

    Thanks again for all the support!

    Best regards,

  • Hi Arthur, 
    Could you show your central's code ? 
    I'm interested to see the create_param you feeding to bt_conn_le_create() function if you do call that function. 
    My suggestion is to increase the connection interval (in the bt_conn_le_create_param create_param ).

    Unless your application has a requirement on the latency, the interval of around 100ms and above should be OK. 

    Regarding the problem with discovery the NUS service. The disconnection and the discovery failed came at almost the same time, so it's hard to tell which come first without the sniffer trace. 

    You can see the time the log printed out is only a few ns apart. So it's not certain which event come first. And which event is the result of the other event. But they seem to be related. 

  • Hi Hung,

    Thanks again for your help and suggestions.

    I'm attaching both the central and peripheral source files so you can review the actual implementation and parameter configurations.

    Regarding your question, yes — the central does use bt_conn_le_create(), and you'll find the bt_conn_le_create_param setup inside the central code.

    As for the NUS discovery failure, I understand your point about the log timestamps being too close to definitively determine the event order. Hopefully, once I set up the sniffer as discussed earlier, I’ll be able to trace that interaction more precisely.


    Central Bluetooth.c



    Central main.c

    Peripheral Bluetooth.c

    Peripheral main.c

    Thanks again, and I appreciate your continued support.

  • Hi Arthur, 
    The BT_GAP_SCAN_FAST_INTERVAL is 60ms which is quite OK. You can try to increase the interval you put in conn_params in scan_filter_match(). 
    A sniffer trace can reveal more info. 
    You may want to check the memory buffer in your central's prj.conf to see if you are hitting any limit