esp-nimble-cpp

repository·master·Indexed 43 days ago

https://github.com/h2zero/esp-nimble-cpp

A high-performance, thread-safe C++ library for ESP32 BLE applications. It replaces the Bluedroid stack with the lightweight Apache NimBLE stack while maintaining API compatibility with nkolban cpp_utils. Key features include the NimBLEStreamClient and NimBLEStreamServer classes for Arduino-style Stream communication, reduced flash and RAM usage, and support for ESP-IDF v4.0+.

Tokens
21K
Snippets
57
Records
84
Agent score
54%

What's inside esp-nimble-cpp

  1. What is esp-nimble-cpp?

    master

    The esp-nimble-cpp library is a C++ BLE library for the ESP32 that utilizes the Apache NimBLE host stack instead of the default Bluedroid stack.

    It is designed to be a drop-in replacement for the original Bluedroid C++ and Arduino BLE APIs, aiming to maintain compatibility while providing:

    • Reduced Resource Usage: Testing shows approximately a 50% reduction in flash usage and ~100kB less RAM consumption compared to Bluedroid.
    • Improved Performance: Better stability and resource management for constrained devices.
    • Compatibility: Uses the same functions and types as the original ESP32 BLE library with only minor changes.
  2. Overview of esp-nimble-cpp

    master

    What is esp-nimble-cpp?

    esp-nimble-cpp is a C++ library for ESP32 that provides a Bluetooth Low Energy (BLE) interface. It is designed to be compatible with the nkolban cpp_utils BLE API while utilizing the NimBLE stack instead of the default Bluedroid stack.

    Key Benefits

    • Resource Efficiency: Testing shows a nearly 50% reduction in flash usage and approximately 100kB less RAM consumption compared to the Bluedroid-based library.
    • Thread Safety: The library is thread-safe; characteristics can be set from any thread.
    • Performance: Improved performance and stability for ESP32 BLE applications.

    Compatibility

    • New Users: If you have never used the original Bluedroid library, refer to the New user guide.
    • Existing Users: If you are migrating from the original library, refer to the Migration guide for breaking changes.
  3. Configure Device Local Name (Advertising vs GATT)

    master

    A device's 'Local Name' is derived from two distinct sources. For consistent behavior across different OSes, it is recommended to set both.

    1. Advertising "Local name": This is the name found in the advertising data payload. It is what the device is seen as before a connection is established.

      • Set via: NimBLEAdvertising::setName()
    2. GATT Device Name: This is the characteristic UUID 0x2A00 in the Generic Access service. This is what the device is seen as after connecting.

      • Set via: NimBLEDevice::init() or NimBLEDevice::setDeviceName()

    Important OS Behavior

    • Caching: OSes cache the GATT Device Name.
    • Name Switching: If the Advertising name is "ABCD" but the GATT name is "12345", the device will appear as "ABCD" until connected, at which point the OS will update the name to "12345".
    • Unnamed Devices: If no Advertising name is set, some OSes (like iOS) may display the device as 'Unnamed' until the connection is established and the GATT Device Name is read.
    // Set Advertising Name
    NimBLEAdvertising* pAdvertising = NimBLEDevice::getAdvertising();
    pAdvertising->setName("ABCD");
    
    // Set GATT Device Name
    NimBLEDevice::init("12345");
  4. How extended advertising changes the NimBLE API

    master

    When CONFIG_BT_NIMBLE_EXT_ADV is enabled, several core classes are replaced or modified to support Bluetooth 5 capabilities:

    • Advertising: NimBLEAdvertising is replaced by NimBLEExtAdvertising. Consequently, NimBLEDevice::getAdvertising() will return a NimBLEExtAdvertising instance.
    • Advertisement Data: NimBLEAdvertisementData is replaced by NimBLEExtAdvertisement. This new class is used to configure advertisement intervals and the advertisement ended callback.
    • Scanning: NimBLEScan::start automatically scans on both the 1M PHY and the CODED PHY standards.
    • Connection: NimBLEClient::connect uses the primary PHY the device is listening on by default. However, NimBLEClient::setConnectPhy becomes available to explicitly specify which PHYs to use for the connection.
  5. Use NimBLEStreamClient for Arduino-style BLE communication

    master

    The NimBLEStreamClient class allows you to communicate with a BLE GATT server using the standard Arduino Stream interface. This enables you to use familiar methods like print(), println(), read(), and available() to send and receive data over a BLE characteristic.

    Key Features:

    • Arduino Stream Interface: Use print() and read() just like you would with Serial.
    • Automatic Lifecycle: Handles server discovery, connection, and automatic reconnection on disconnect.
    • Buffered Communication: Uses ring buffers for both TX (transmit) and RX (receive) to manage data flow.
    • Bidirectional: Supports simultaneous reading and writing.

    Workflow:

    1. The client scans for devices advertising a specific Service UUID.
    2. Once found, it connects and discovers the target Stream Characteristic.
    3. It subscribes to notifications on that characteristic to populate the RX buffer.
    4. Data is then accessible via standard Stream methods.
  6. Set Server Security via Characteristic Properties

    master

    Security is enforced by applying specific NIMBLE_PROPERTY flags to characteristics or descriptors. When a peer attempts to access a protected property, it triggers the pairing process (defaulting to "just-works").

    Available security properties:

    • NIMBLE_PROPERTY::READ_ENC (Encrypted)
    • NIMBLE_PROPERTY::READ_AUTHEN (Authenticated)
    • NIMBLE_PROPERTY::READ_AUTHOR (Authorized)
    • NIMBLE_PROPERTY::WRITE_ENC (Encrypted)
    • NIMBLE_PROPERTY::WRITE_AUTHEN (Authenticated)
    • NIMBLE_PROPERTY::WRITE_AUTHOR (Authorized)
  7. Migrate BLE Client and Remote Services

    master

    Client API Changes

    • NimBLEClient::getServices() now returns a const std::vector<NimBLERemoteService*>&.
    • NimBLEClient::getConnId() is renamed to NimBLEClient::getConnHandle().
    • NimBLEClient::disconnect() now returns bool (true on success).

    Client Callbacks

    • NimBLEClientCallbacks::onConfirmPIN() is renamed to NimBLEClientCallbacks::onConfirmPasskey(). It now takes a NimBLEConnInfo& and returns nothing. Use NimBLEDevice::injectConfirmPasskey to send the response.
    • NimBLEClientCallbacks::onPassKeyRequest() is renamed to NimBLEClientCallbacks::onPassKeyEntry(). It now takes a NimBLEConnInfo& and returns nothing. Use NimBLEDevice::injectPassKey to send the entered passkey.

    Remote Services and Characteristics

    • NimBLERemoteService::getCharacteristics() now returns a const std::vector<NimBLERemoteCharacteristic*>&.
    • NimBLERemoteCharacteristic::getRemoteService() now returns a const NimBLERemoteService*.
    • NimBLERemoteCharacteristic::registerForNotify() is removed. Use NimBLERemoteCharacteristic::subscribe() and unSubscribe() instead.
    • Data Reading: readUInt32, readUInt16, readUInt8, and readFloat are removed. Use the template method readValue<type>() instead.
    // Example of new way to read data
    uint32_t val = pRemoteChar->readValue<uint32_t>();
  8. Enable Bluetooth 5 extended advertising

    master

    To use Bluetooth 5 features like extended advertising, larger data payloads, and new PHYs (2M PHY or CODED PHY), you must enable the CONFIG_BT_NIMBLE_EXT_ADV configuration option.

    Depending on your development environment, enable it as follows:

    • ESP-IDF (menuconfig): Navigate to Component config > Bluetooth > NimBLE options > Enable extended advertising and set it to 1.
    • Arduino: Set the option in nimconfig.h.
    • PlatformIO: Add the configuration to your build_flags.
    # ESP-IDF menuconfig path
    Component config > Bluetooth > NimBLE options > Enable extended advertising -> [x]
  9. Install esp-nimble-cpp in ESP-IDF v4.0+

    master

    To use this library in an ESP-IDF project, follow these steps:

    1. Add the component: Download the library as a .zip and extract it, or clone the repository directly into the components folder of your ESP-IDF project.
    2. Configure Bluetooth: Run menuconfig and navigate to Component config -> Bluetooth.
      • Enable Bluetooth.
      • Set Bluetooth host to NimBLE.
      • Adjust necessary settings under NimBLE Options.
    3. Initialize in code:
      • Include the header: #include "NimBLEDevice.h".
      • Initialize the device in your app_main function using NimBLEDevice::init("");.
    #include "NimBLEDevice.h"
    
    void app_main() {
        NimBLEDevice::init("");
        // ... rest of your application
    }
  10. Set up a NimBLE Stream Client and Server test

    master

    To test bidirectional communication, you need two ESP32 devices: one running the NimBLE_Stream_Server and one running the NimBLE_Stream_Client.

    Steps:

    1. Prepare the Server: Build and flash the NimBLE_Stream_Server example to the first ESP32 using ESP-IDF:
      idf.py build flash monitor
    2. Prepare the Client: Build and flash the NimBLE_Stream_Client example to the second ESP32 using ESP-IDF.
    3. Observe Traffic: Open idf.py monitor on both boards. The client will automatically scan, connect, and begin communicating.

    Expected Behavior:

    • The server sends periodic status messages.
    • The client sends periodic uptime messages.
    • Both devices echo data received from each other.
    • You can manually send data through the idf.py monitor session to test the stream.
    idf.py build flash monitor
  11. Run the NimBLE Stream Echo Example

    master

    The NimBLE Stream Echo example demonstrates the simplest usage of NimBLEStreamServer. It initializes a BLE server that listens for data on a specific characteristic and echoes that data back to the connected client. This is an ideal starting point for learning the Stream interface and testing basic BLE connectivity.

    Steps to Run

    1. Build and flash the example to your ESP32 using the ESP-IDF toolchain:
      idf.py build flash monitor
    2. Use a BLE client application (such as nRF Connect or Serial Bluetooth Terminal) to connect to the ESP32.
    3. Locate the service 0xc0de and the characteristic 0xfeed.
    4. Subscribe to notifications on the characteristic.
    5. Write data to the characteristic; the ESP32 will echo the data back and display it in the idf.py monitor output.
    idf.py build flash monitor
  12. Configure and start BLE Advertising

    master

    To make your server discoverable, you must set up advertising:

    1. Get the advertising instance via NimBLEDevice::getAdvertising().
    2. (Optional) Add the service UUID using pAdvertising->addServiceUUID(uuid).
    3. Set the device name using pAdvertising->setName(name).
    4. Call pAdvertising->start() to begin advertising.
    NimBLEAdvertising *pAdvertising = NimBLEDevice::getAdvertising();
    pAdvertising->addServiceUUID("ABCD");
    pAdvertising->setName("NimBLE");
    pAdvertising->start();