espressif / esp-nimble

A fork of NimBLE stack, for use with ESP32 and ESP-IDF
Apache License 2.0
76 stars 49 forks source link

Can't discover services after connecting 5 times without bonding #25

Closed Teu97 closed 3 years ago

Teu97 commented 3 years ago

Hi all,

I'm implementing the nimble Bluetooth stack (esp-idf v4.1.1) into my ESP32-WROOM-32D project which has been set as BLE peripheral. After connecting 5 times using the NRF connect application (Android or IOS does not matter), I cannot discover services any longer. I have already tried several things but I can't figure out what goes wrong.

The settings:

void CEspNimbleBluetoothDevice::Init()
{
  esp_err_t result;
  result = esp_nimble_hci_and_controller_init();
  Assert(result == ESP_OK);

  nimble_port_init();

  ble_hs_cfg.reset_cb = PeripheralOnResetCallback;
  ble_hs_cfg.sync_cb = PeripheralOnSyncCallback;
  ble_hs_cfg.gatts_register_cb = GattRegisterEventCallback;
  ble_hs_cfg.gatts_register_arg = reinterpret_cast<void*>(this);
  ble_hs_cfg.store_status_cb = ble_store_util_status_rr;

  ble_hs_cfg.sm_io_cap = BLE_SM_IO_CAP_NO_IO; // No input/output capabilities (just works)
  ble_hs_cfg.sm_bonding = 1; // Enable bonding
  ble_hs_cfg.sm_sc = 1; // Use secure connection
  ble_hs_cfg.sm_our_key_dist = BLE_SM_PAIR_KEY_DIST_ENC | BLE_SM_PAIR_KEY_DIST_ID; // Enable LTK + IRK
  ble_hs_cfg.sm_their_key_dist = BLE_SM_PAIR_KEY_DIST_ENC | BLE_SM_PAIR_KEY_DIST_ID; // Enable LTK + IRK

  ble_svc_gap_init();
  ble_svc_gatt_init();

  memset(m_serviceTable, 0, sizeof(m_serviceTable));
  memset(m_characteristicRegistrationTable, 0, sizeof(m_characteristicRegistrationTable));
}

The SDK config is set as follows:

Decreasing the number of concurrent connections or number of bonds in the SDK config does not change the steps to reproduce.

Steps that I execute:

  1. Push pairing button, so that the ESP accepts new connections by advertising with its name.
  2. Scan and connect with the NRF connect application.
  3. See if services can be discovered under the client tab.
  4. Disconnect.

After repeating this 5 times, step 3 will not display any services. Logging in the application shows that discovering resulted in a nill. And also in the ESP logging does not give me any information about discovering.

Hope someone can help, thanks in advance!

prasad-alatkar commented 3 years ago

Hi @Teu97 , I tried to reproduce the issue using bleprph example app and I could not face any issue. Is it possible for you to list out key differences between bleprph example and your custom example ? Can you please share sdkconfig file ? Can you please also share console logs with DEBUG prints enabled (menuconfg --> Components --> Log Component --> Log verbosity --> DEBUG) ?

Teu97 commented 3 years ago

Hi @Teu97 , I tried to reproduce the issue using bleprph example app and I could not face any issue. Is it possible for you to list out key differences between bleprph example and your custom example ? Can you please share sdkconfig file ? Can you please also share console logs with DEBUG prints enabled (menuconfg --> Components --> Log Component --> Log verbosity --> DEBUG) ?

Hi @prasad-alatkar, thanks for picking up this so quickly. I hope to answer your questions as best as possible:

Is it possible for you to list out key differences between bleprph example and your custom example ?

It is hard to tell the exact differences between both examples. The key differences in my opinion is the initialize as shared above. With the fact that I only want to allow a maximum number of 6 devices to bond with the ESP32. But the case is that I only connect my device and never execute a bond. I will try to minimize the number of differences to give you a better answer to this question.

Can you please share sdkconfig file ?

sdkconfig.txt

Can you please also share console logs with DEBUG prints enabled?

logging.txt The most interesting part starts from 86424ms. At that moment I pressed the pairing button for the 6th time and connected the application with the ESP32. Then discovering seems to fail.

Teu97 commented 3 years ago

Hi @prasad-alatkar, I cleared all my events from the GapEventCallback and added them one by one. I noticed that the connect event was the problem. What happened was that we had a characteristic which keeps track if pairing was allowed. So if a connect event occurred pairing is no longer allowed and this could be notified if someone subscribed to the characteristic.

In our NotifyValue function we had:

os_mbuf* pBuffer = ble_hs_mbuf_from_flat(rMessage.data(), size);
for (uint16_t connectionHandle : m_notificationConnections[rNotifyCharacteristicUuid])
{
  ble_gattc_notify_custom(connectionHandle, m_notificationHandles[rNotifyCharacteristicUuid], pBuffer);
}

But it had to be:

for (uint16_t connectionHandle : m_notificationConnections[rNotifyCharacteristicUuid])
{
  os_mbuf* pBuffer = ble_hs_mbuf_from_flat(rMessage.data(), size);
  ble_gattc_notify_custom(connectionHandle, m_notificationHandles[rNotifyCharacteristicUuid], pBuffer);
}

In the code samples m_notificationConnections[rNotifyCharacteristicUuid] will be empty if no one is subscribed. So I ques that if no one is subscribed, notify is never called, and therefore the buffer is never freed. Discover will probably use the same buffer to allocate its messages, but it could not allocate any memory after repeating the sequence 5 times. Resulting in the fact that a discover would fail.

Do you know if I still have to check if pBuffer is not NULL or does it not matter for the ble_gattc_notify_custom?

prasad-alatkar commented 3 years ago

@Teu97 If I am not wrong bringing pBuffer inside the for loop resolved your issue of not able to discover services on 6th successful connection, right ?

m_notificationConnections[rNotifyCharacteristicUuid] will be empty if no one is subscribed

Is it empty or zero valued ? I mean, 0 is a valid conn_handle (theoretically).

So I ques that if no one is subscribed, notify is never called, and therefore the buffer is never freed.

If I understand it correctly, I guess by moving pBuffer inside for loop, you have made sure that pBuffer is not wasted. If you keep outside of for loop, I think that should also be okay provided after sending notifications you check if pBuffer != NULL then call os_mbuf_free. This way you may not end up allocating os_mbuf for each conn_handle. You can also think of increasing CONFIG_BT_NIMBLE_MSYS1_BLOCK_COUNT, should you need more os_mbufs.

Do you know if I still have to check if pBuffer is not NULL or does it not matter for the ble_gattc_notify_custom?

If pBuffer is NULL then the corresponding characteristic's value is notified i.e. it will be as good as calling ble_gap_notify.

but it could not allocate any memory after repeating the sequence 5 times. Resulting in the fact that a discover would fail.

Can you please tell me approximate size of each rMessage.data() to understand if it is really the case.

To summarize my response, I am a little confused if your original issue of not able to discover services on 6th connection are solved or not. If we don't have sufficient number of os_mbufs then please increase MSYS1_BLOCK_COUNT as mentioned above. Please let me know if it helps you resolve the issue.

Teu97 commented 3 years ago

Hi @prasad-alatkar, sorry that i'm not clear. Yes my issue has been solved, thanks for your help and support! My answers on your questions are below:

@Teu97 If I am not wrong bringing pBuffer inside the for loop resolved your issue of not able to discover services on 6th successful connection, right ?

Yes that is right.

Is it empty or zero valued ? I mean, 0 is a valid conn_handle (theoretically).

It is empty valued, if no one is subscribed it will never call ble_gattc_notify_custom. Which caused my issue because the allocated buffer was never freed. m_notificationConnections[rNotifyCharacteristicUuid]) is a map containing a vector of subscribed handles for each uuid.

If I understand it correctly, I guess by moving pBuffer inside for loop, you have made sure that pBuffer is not wasted. If you keep outside of for loop, I think that should also be okay provided after sending notifications you check if pBuffer != NULL then call os_mbuf_free. This way you may not end up allocating os_mbuf for each conn_handle. You can also think of increasing CONFIG_BT_NIMBLE_MSYS1_BLOCK_COUNT, should you need more os_mbufs.

No I think that wont work because what I saw was that I had to call ble_hs_mbuf_from_flat each time I wanted to send a notification. I could not use pBuffer multiple times. Which can be right because ble_gattc_notify_custom also frees pBuffer.

To summarize my response, I am a little confused if your original issue of not able to discover services on 6th connection are solved or not. If we don't have sufficient number of os_mbufs then please increase MSYS1_BLOCK_COUNT as mentioned above. Please let me know if it helps you resolve the issue.

What made it so hard to found was that I did not expect the allocation for a notification to be the issue of discovering services not able to work.

Again, thanks for the support!

prasad-alatkar commented 3 years ago

@Teu97 Glad that the issue is resolved, and yes ble_gattc_notify_custom does free the os_mbuf.