Skip to content

fix(ble): Fix connection issues in Bluedroid and GAP naming in NimBLE#12318

Open
lucasssvaz wants to merge 3 commits intoespressif:masterfrom
lucasssvaz:fix/bluedroid_adv_sync
Open

fix(ble): Fix connection issues in Bluedroid and GAP naming in NimBLE#12318
lucasssvaz wants to merge 3 commits intoespressif:masterfrom
lucasssvaz:fix/bluedroid_adv_sync

Conversation

@lucasssvaz
Copy link
Member

@lucasssvaz lucasssvaz commented Feb 4, 2026

Description of Change

This pull request introduces several improvements to the BLE library, focusing on connection reliability, async event handling, and logging. The most significant updates include adding retry logic to BLE client connections, improving semaphore and async handling for advertising, and enhancing logging for easier debugging. Additionally, the device name is now properly set for NimBLE servers after initialization, and a method to check scan status has been added.

Connection Reliability and Robustness:

  • Added retry logic to BLEClient::connect() to handle transient BLE stack errors (e.g., busy state), with configurable retries and delays. The method now also validates addresses, checks for existing connections, stops active scans before connecting, and improves error handling and cleanup. [1] [2] [3]

Advertising and Async Handling:

  • Improved async handling in BLEAdvertising::start() by explicitly taking and waiting on semaphores during advertising and scan response data configuration, ensuring proper synchronization with BLE stack events. Added debug logs for configuration steps.
  • In BLEAdvertising::handleGAPEvent(), restored and enhanced semaphore signaling and added debug logging for advertising and scan response data completion events.

NimBLE Server Initialization:

  • Ensured the device name is re-set after starting the NimBLE GATT server, addressing an issue where the name could revert to the SDK default.

Logging Improvements:

  • Added detailed info-level and debug-level logging throughout BLE client and server connection and registration events, including connection attempts, completions, and failures. [1] [2] [3] [4]

API Enhancements:

  • Added a new BLEScan::isScanning() method (and declaration in the header) to allow checking if scanning is active. [1] [2] [3]

Test Scenarios

Tested locally with ESP32 and ESP32-C6

@lucasssvaz lucasssvaz self-assigned this Feb 4, 2026
@lucasssvaz lucasssvaz requested a review from SuGlider as a code owner February 4, 2026 15:42
@lucasssvaz lucasssvaz added the Area: BLE Issues related to BLE label Feb 4, 2026
@github-actions
Copy link
Contributor

github-actions bot commented Feb 4, 2026

Messages
📖 🎉 Good Job! All checks are passing!

👋 Hello lucasssvaz, we appreciate your contribution to this project!


📘 Please review the project's Contributions Guide for key guidelines on code, documentation, testing, and more.

🖊️ Please also make sure you have read and signed the Contributor License Agreement for this project.

Click to see more instructions ...


This automated output is generated by the PR linter DangerJS, which checks if your Pull Request meets the project's requirements and helps you fix potential issues.

DangerJS is triggered with each push event to a Pull Request and modify the contents of this comment.

Please consider the following:
- Danger mainly focuses on the PR structure and formatting and can't understand the meaning behind your code or changes.
- Danger is not a substitute for human code reviews; it's still important to request a code review from your colleagues.
- To manually retry these Danger checks, please navigate to the Actions tab and re-run last Danger workflow.

Review and merge process you can expect ...


We do welcome contributions in the form of bug reports, feature requests and pull requests.

1. An internal issue has been created for the PR, we assign it to the relevant engineer.
2. They review the PR and either approve it or ask you for changes or clarifications.
3. Once the GitHub PR is approved we do the final review, collect approvals from core owners and make sure all the automated tests are passing.
- At this point we may do some adjustments to the proposed change, or extend it by adding tests or documentation.
4. If the change is approved and passes the tests it is merged into the default branch.

Generated by 🚫 dangerJS against 3e8b9b4

@lucasssvaz lucasssvaz changed the title fix(ble): Await for Adv data in Bluedroid and match nimble fix(ble): Fix connection issues in Bluedroid and GAP naming in NimBLE Feb 4, 2026
@lucasssvaz lucasssvaz added the Status: Review needed Issue or PR is awaiting review label Feb 4, 2026
@lucasssvaz lucasssvaz requested a review from me-no-dev February 4, 2026 15:58
@github-actions
Copy link
Contributor

github-actions bot commented Feb 4, 2026

Test Results

 90 files   90 suites   30m 47s ⏱️
 67 tests  67 ✅ 0 💤 0 ❌
683 runs  683 ✅ 0 💤 0 ❌

Results for commit 3e8b9b4.

♻️ This comment has been updated with latest results.

@github-actions
Copy link
Contributor

github-actions bot commented Feb 4, 2026

Memory usage test (comparing PR against master branch)

The table below shows the summary of memory usage change (decrease - increase) in bytes and percentage for each target.

MemoryFLASH [bytes]FLASH [%]RAM [bytes]RAM [%]
TargetDECINCDECINCDECINCDECINC
ESP320⚠️ +8880.00⚠️ +0.08000.000.00
ESP32C30⚠️ +1100.00⚠️ +0.02000.000.00
ESP32C50⚠️ +1100.00⚠️ +0.01000.000.00
ESP32C60⚠️ +1100.00⚠️ +0.01000.000.00
ESP32H20⚠️ +1100.00⚠️ +0.01000.000.00
ESP32P40⚠️ +1100.00⚠️ +0.01000.000.00
ESP32S30⚠️ +1520.00⚠️ +0.02000.000.00
Click to expand the detailed deltas report [usage change in BYTES]
TargetESP32ESP32C3ESP32C5ESP32C6ESP32H2ESP32P4ESP32S3
ExampleFLASHRAMFLASHRAMFLASHRAMFLASHRAMFLASHRAMFLASHRAMFLASHRAM
libraries/BLE/examples/Beacon_Scanner⚠️ +3480⚠️ +1060⚠️ +1060⚠️ +1060⚠️ +1060⚠️ +1060⚠️ +1440
libraries/BLE/examples/Client⚠️ +8760⚠️ +1100⚠️ +1100⚠️ +1100⚠️ +1100⚠️ +1100⚠️ +1160
libraries/BLE/examples/Client_Gamepad⚠️ +8160⚠️ +1100⚠️ +1100⚠️ +1100⚠️ +1100⚠️ +1100⚠️ +1520
libraries/BLE/examples/Client_Server⚠️ +8880⚠️ +1060⚠️ +1060⚠️ +1060⚠️ +1060⚠️ +1060⚠️ +1440
libraries/BLE/examples/Client_multiconnect⚠️ +8840⚠️ +1100⚠️ +1100⚠️ +1100⚠️ +1100⚠️ +1100⚠️ +1480
libraries/BLE/examples/Client_secure_static_passkey⚠️ +7720⚠️ +1060⚠️ +1060⚠️ +1060⚠️ +1060⚠️ +1060⚠️ +1120
libraries/BLE/examples/EddystoneTLM_Beacon⚠️ +4200⚠️ +1060⚠️ +1060⚠️ +1060⚠️ +1060⚠️ +1060⚠️ +1440
libraries/BLE/examples/EddystoneURL_Beacon⚠️ +4080⚠️ +1060⚠️ +1060⚠️ +1060⚠️ +1060⚠️ +1060⚠️ +1440
libraries/BLE/examples/Notify⚠️ +3480⚠️ +1060⚠️ +1060⚠️ +1060⚠️ +1060⚠️ +1060⚠️ +1440
libraries/BLE/examples/Scan⚠️ +3480⚠️ +1060⚠️ +1060⚠️ +1060⚠️ +1060⚠️ +1060⚠️ +1440
libraries/BLE/examples/Server⚠️ +3440⚠️ +1060⚠️ +1060⚠️ +1060⚠️ +1060⚠️ +1060⚠️ +1440
libraries/BLE/examples/Server_Gamepad⚠️ +3200⚠️ +1060⚠️ +1060⚠️ +1060⚠️ +1060⚠️ +1060⚠️ +1440
libraries/BLE/examples/Server_multiconnect⚠️ +3520⚠️ +1060⚠️ +1060⚠️ +1060⚠️ +1060⚠️ +1060⚠️ +1440
libraries/BLE/examples/Server_secure_static_passkey⚠️ +3600⚠️ +1060⚠️ +1060⚠️ +1060⚠️ +1060⚠️ +1060⚠️ +1440
libraries/BLE/examples/UART⚠️ +3480⚠️ +1060⚠️ +1060⚠️ +1060⚠️ +1060⚠️ +1060⚠️ +1440
libraries/BLE/examples/Write⚠️ +3440⚠️ +1060⚠️ +1060⚠️ +1060⚠️ +1060⚠️ +1060⚠️ +1440
libraries/BLE/examples/iBeacon⚠️ +3360⚠️ +1100⚠️ +1100⚠️ +1100⚠️ +1100⚠️ +1100⚠️ +1440
libraries/BLE/examples/Server_secure_authorization--⚠️ +1060⚠️ +1060⚠️ +1060⚠️ +1060--⚠️ +1440

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR enhances BLE connection reliability and robustness by adding retry logic for transient stack errors, improving async event synchronization in advertising, fixing NimBLE device name persistence, and adding enhanced logging throughout the connection flow.

Changes:

  • Added retry logic to BLEClient::connect() to handle transient ESP_ERR_INVALID_STATE errors with configurable retries and delays, plus additional validation and scan stopping before connection attempts
  • Improved semaphore-based synchronization in BLEAdvertising::start() and event handling to ensure proper async operation completion for advertising and scan response data configuration
  • Fixed NimBLE server device name by re-setting it after ble_gatts_start() to prevent reversion to SDK default
  • Made isScanning() method available for both Bluedroid and NimBLE stacks by moving it from NimBLE-specific to common API
  • Added comprehensive debug and info-level logging for connection lifecycle events

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
libraries/BLE/src/BLEClient.cpp Added connection retry logic, address/state validation, automatic scan stopping, enhanced error handling and logging
libraries/BLE/src/BLEAdvertising.cpp Improved async semaphore handling for advertising/scan response data configuration with explicit take/wait/give patterns and debug logging
libraries/BLE/src/BLEServer.cpp Re-sets device name after starting NimBLE GATT server and adds logging for connection/registration events
libraries/BLE/src/BLEScan.h Moved isScanning() method declaration from NimBLE-only section to common API
libraries/BLE/src/BLEScan.cpp Added isScanning() implementation for Bluedroid stack using m_stopped state variable

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +506 to +512
} else {
// Other errors, don't retry
log_e("esp_ble_gattc_open: rc=%d %s", errRc, GeneralUtils::errorToString(errRc));
BLEDevice::removePeerDevice(m_appId, true);
esp_ble_gattc_app_unregister(m_gattc_if);
m_gattc_if = ESP_GATT_IF_NONE;
return false;
Copy link

Copilot AI Feb 4, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The semaphore m_semaphoreOpenEvt is taken at line 485 but is not released before returning false when a non-retryable error occurs. This will leave the semaphore in a taken state and could cause deadlocks or hangs in subsequent connection attempts. The semaphore should be given back with an appropriate error value before cleanup and return.

Copilot uses AI. Check for mistakes.
Comment on lines +516 to 522
// Check if we exhausted retries
if (errRc != ESP_OK) {
log_e("esp_ble_gattc_open: rc=%d %s", errRc, GeneralUtils::errorToString(errRc));
log_e("esp_ble_gattc_open failed after %d retries: rc=%d %s", maxRetries, errRc, GeneralUtils::errorToString(errRc));
BLEDevice::removePeerDevice(m_appId, true);
esp_ble_gattc_app_unregister(m_gattc_if);
m_gattc_if = ESP_GATT_IF_NONE;
return false;
Copy link

Copilot AI Feb 4, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The semaphore m_semaphoreOpenEvt is taken at line 485 in the retry loop but is not released before returning false when retries are exhausted. This will leave the semaphore in a taken state and could cause deadlocks or hangs in subsequent connection attempts. The semaphore should be given back with an appropriate error value before cleanup and return.

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Area: BLE Issues related to BLE Status: Review needed Issue or PR is awaiting review

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant