BLE GATT Server

The BleGattServer implements the standard BLE GATT server which has various APIs for controlling the server and adding services. It automatically builds and adds standard battery service and device information service.

API Reference

Header File

Header File

Classes

class BleGattServer : public espp::BaseComponent

BLE GATT Server This class is responsible for creating and managing the BLE GATT server. It is responsible for handling the connection and disconnection of clients. It is also responsible for handling the pairing process. It also manages the different services that are part of the GATT server.

BLE GATT Server Example

    // NOTE: esp-nimble-cpp already depends on nvs_flash and initializes
    //       nvs_flash in the NimBLEDevice::init(), so we don't have to do that
    //       to store bonding info

    // create the GATT server
    espp::BleGattServer ble_gatt_server;
    std::string device_name = "Espp BLE GATT Server";
    ble_gatt_server.set_log_level(espp::Logger::Verbosity::INFO);
    ble_gatt_server.set_callbacks({
        .connect_callback = [&](NimBLEConnInfo &conn_info) { logger.info("Device connected"); },
        .disconnect_callback = [&](auto &conn_info,
                                   auto reason) { logger.info("Device disconnected: {}", reason); },
        .authentication_complete_callback =
            [&](const NimBLEConnInfo &conn_info) { logger.info("Device authenticated"); },
        // NOTE: this is optional, if you don't provide this callback, it will
        // perform the exactly function as below:
        .get_passkey_callback =
            [&]() {
              logger.info("Getting passkey");
              return NimBLEDevice::getSecurityPasskey();
            },
        // NOTE: this is optional, if you don't provide this callback, it will
        // perform the exactly function as below:
        .confirm_passkey_callback =
            [&](const NimBLEConnInfo &conn_info, uint32_t passkey) {
              logger.info("Confirming passkey: {}", passkey);
              NimBLEDevice::injectConfirmPIN(conn_info,
                                             passkey == NimBLEDevice::getSecurityPasskey());
            },
    });
    ble_gatt_server.init(device_name);
#if !CONFIG_BT_NIMBLE_EXT_ADV
    // extended advertisement does not support automatically advertising on
    // disconnect
    ble_gatt_server.set_advertise_on_disconnect(true);
#endif

    // let's configure the security
    bool bonding = true;
    bool mitm = false;
    bool secure_connections = true;
    ble_gatt_server.set_security(bonding, mitm, secure_connections);
    // and some i/o and key config
    NimBLEDevice::setSecurityPasskey(123456);
    ble_gatt_server.set_io_capabilities(BLE_HS_IO_NO_INPUT_OUTPUT);
    ble_gatt_server.set_init_key_distribution(BLE_SM_PAIR_KEY_DIST_ENC | BLE_SM_PAIR_KEY_DIST_ID);
    ble_gatt_server.set_resp_key_distribution(BLE_SM_PAIR_KEY_DIST_ENC | BLE_SM_PAIR_KEY_DIST_ID);

    // you can create a service and add it to the server using
    // ble_gatt_server.server().addService()

    // now start the services
    ble_gatt_server.start_services(); // starts the device info service and battery service
    // NOTE: we could also directly start them ourselves if we wanted to
    //      control the order of starting the services
    // e.g.:
    // ble_gatt_server.battery_service().start();
    // ble_gatt_server.device_info_service().start();

    // now start the gatt server
    ble_gatt_server.start();

    // let's set some of the service data
    auto &battery_service = ble_gatt_server.battery_service();
    battery_service.set_battery_level(99);

    auto &device_info_service = ble_gatt_server.device_info_service();
    uint8_t vendor_source = 0x01;
    uint16_t vid = 0xCafe;
    uint16_t pid = 0xFace;
    uint16_t product_version = 0x0100;
    device_info_service.set_pnp_id(vendor_source, vid, pid, product_version);
    device_info_service.set_manufacturer_name("ESP-CPP");
    device_info_service.set_model_number("esp-ble-01");
    device_info_service.set_serial_number("1234567890");
    device_info_service.set_software_version("1.0.0");
    device_info_service.set_firmware_version("1.0.0");
    device_info_service.set_hardware_version("1.0.0");

    // set the advertising data
    espp::BleGattServer::AdvertisedData adv_data;
    // uint8_t flags = BLE_HS_ADV_F_DISC_LTD;
    uint8_t flags = BLE_HS_ADV_F_DISC_GEN;
    adv_data.setFlags(flags);
    adv_data.setName(device_name);
    adv_data.setAppearance((uint16_t)espp::BleAppearance::GENERIC_COMPUTER);
    adv_data.addTxPower();
    ble_gatt_server.set_advertisement_data(adv_data);

#if CONFIG_COMPILER_CXX_EXCEPTIONS
    // let's test and use the BLE menu (CLI)
    // turn off some of the logs so that it doesn't clutter up the CLI
    ble_gatt_server.set_log_level(espp::Logger::Verbosity::WARN);
    // and make the CLI
    auto ble_menu = espp::BleGattServerMenu(ble_gatt_server);
    cli::SetColor();
    cli::Cli cli(ble_menu.get());
    cli.ExitAction([](auto &out) { out << "Goodbye and thanks for all the fish.\n"; });

    espp::Cli input(cli);
    input.SetInputHistorySize(10);
    input.Start(); // this will not return until the user enters the `exit` command.
#endif

    logger.info("Menu has finished, starting advertising");
    // The menu has finished, so let's go into a loop to keep the device running
    // now lets start advertising
    ble_gatt_server.start_advertising();

    logger.info("Waiting for connection...");
    // now lets update the battery level every second for a little while
    uint8_t battery_level = 99;
    bool was_connected = false;
    bool can_exit = false;
    int num_seconds_to_wait = 30;
    while (true) {
      auto start = std::chrono::steady_clock::now();

      // if we are now connected, but were not, then get the services
      if (ble_gatt_server.is_connected() && !was_connected) {
        was_connected = true;
        can_exit = true;
        auto connected_device_infos = ble_gatt_server.get_connected_device_infos();
        logger.info("Connected devices: {}", connected_device_infos.size());
        std::vector<std::string> connected_device_names;
        std::transform(connected_device_infos.begin(), connected_device_infos.end(),
                       std::back_inserter(connected_device_names),
                       [&](auto &info) { return ble_gatt_server.get_connected_device_name(info); });
        logger.info("            Names: {}", connected_device_names);
      } else if (!ble_gatt_server.is_connected()) {
        was_connected = false;
        if (can_exit) {
          logger.info("No longer connected, exiting");
          break;
        }
      }

      if (!ble_gatt_server.is_connected()) {
        logger.move_up();
        logger.clear_line();
        logger.info("Waiting for connection... {}s", --num_seconds_to_wait);
        if (num_seconds_to_wait == 0) {
          logger.info("No connection, exiting");
          break;
        }
        // sleep
        std::this_thread::sleep_until(start + 1s);
        continue;
      }

      // update the battery level
      battery_service.set_battery_level(battery_level);
      battery_level = (battery_level % 100) + 1;

      // sleep
      std::this_thread::sleep_until(start + 1s);
    }

    // we are done, so stop the server and deinit the BLE stack. NOTE: this will
    // automatically be called by ~BleGattServer(), but we call it here to show
    // manual control and to test calling it multiple times since the destructor
    // will be called immediately after this block ends
    ble_gatt_server.deinit();

Public Types

enum class DisconnectReason : uint8_t

Disconnect reasons for the GATT server. This enum represents the different reasons for disconnection that will be passed to the disconnect callback.

This enum is a simplification of the NimBLE disconnect reasons, and is meant to provide a more user-friendly way to handle disconnections. For more information about the possible reasons for disconnection, see https://mynewt.apache.org/latest/network/ble_hs/ble_hs_return_codes.html.

Values:

enumerator UNKNOWN

Unknown reason for disconnection.

enumerator TIMEOUT

Disconnected due to timeout.

enumerator CONNECTION_TERMINATED

Disconnected due to the connection being terminated.

enumerator REMOTE_USER_TERMINATED

Disconnected due to the remote user terminating the connection.

enumerator REMOTE_DEVICE_TERMINATED

Disconnected due to the remote device terminating the connection (low resources or power off)

enumerator LOCAL_USER_TERMINATED

Disconnected due to the local user terminating the connection.

enumerator AUTHENTICATION_FAILURE

Disconnected due to an authentication failure.

typedef std::function<void(NimBLEConnInfo&)> connect_callback_t

Callback for when a device connects to the GATT server.

Param conn_info

The connection information for the device.

typedef std::function<void(NimBLEConnInfo&, DisconnectReason reason)> disconnect_callback_t

Callback for when a device disconnects from the GATT server.

Param conn_info

The connection information for the device.

Param reason

The reason for the disconnection.

typedef std::function<void(const NimBLEConnInfo&)> authentication_complete_callback_t

Callback for when a device completes authentication.

Param conn_info

The connection information for the device.

typedef std::function<uint32_t(void)> get_passkey_callback_t

Callback to retrieve the passkey for the device.

Return

The passkey for the device.

typedef std::function<void(const NimBLEConnInfo &conn_info, uint32_t)> confirm_passkey_callback_t

Callback for confirming the passkey.

Param conn_info

The connection information for the device.

Param passkey

The passkey for the device.

typedef std::function<void(NimBLEExtAdvertising*, int, uint8_t)> advertisement_stopped_callback_t

Callback for when advertising is stopped. This callback is called when advertising is stopped.

Note

This is only available when CONFIG_BT_NIMBLE_EXT_ADV is enabled.

Param advertising

Pointer to the advertising object.

Param reason

The reason for stopping advertising.

Param instance

The advertising instance that was stopped.

typedef std::function<void(NimBLEExtAdvertising*, uint8_t, NimBLEAddress)> scan_request_callback_t

Callback for when a scan request is received. This callback is called when a scan request is received.

Note

This is only available when CONFIG_BT_NIMBLE_EXT_ADV is enabled.

Param advertising

Pointer to the advertising object.

Param instance

The advertising instance that received the scan request.

Param address

The address of the device that sent the scan request.

typedef std::function<void(NimBLEAdvertising*)> advertisement_complete_callback_t

Callback for when advertising is complete. This callback is called when advertising is complete.

Note

This is called when advertising is complete, not when the device is actually advertising. It will not be called if the advertisement duration is 0 (i.e. no timeout).

Note

This is only available when CONFIG_BT_NIMBLE_EXT_ADV is not enabled.

Param advertising

Pointer to the advertising object.

Public Functions

inline BleGattServer()

Constructor for the GATT server.

inline explicit BleGattServer(const Config &config)

Constructor

Parameters

config – The configuration for the GATT server.

inline ~BleGattServer()

Destructor.

inline bool is_connected() const

Get whether the GATT server is connected to any clients.

Returns

Whether the GATT server is connected to any clients.

inline void set_callbacks(const Callbacks &callbacks)

Set the callbacks for the GATT server.

Parameters

callbacks – The callbacks for the GATT server.

inline bool init(const std::string &device_name)

Initialize the GATT server This method creates the GATT server and sets the callbacks to this class. It also initializes the device info and battery services.

Note

This method must be called before creating any other services or characteristics.

Note

This method must be called before starting the server.

Parameters

device_name – The name of the device.

Returns

Whether the GATT server was initialized successfully.

inline void deinit()

Deinitialize the GATT server This method deletes the server and all associated objects. It also invalidates any references/pointers to the server.

Note

After calling this method, any references/pointers to the server and any created services/characteristics will be invalid.

Note

This method should only be called after NimBLEDevice::deinit(true) has been called, since that will free the memory used by the server.

Note

This method will also deinitialize the device info and battery services.

inline void start_services()

Start the services This method starts the device info and battery services.

inline bool start()

Start the server This method starts the GATT server. This method must be called after the server has been initialized, and after any services / characteristics have been added to the server.

Returns

Whether the server was started successfully.

inline void set_advertise_on_disconnect(bool advertise_on_disconnect)

Set whether to advertise on disconnect

Note

This method is only available when CONFIG_BT_NIMBLE_EXT_ADV is not enabled, ane legacy advertising is used. Otherwise, you will have to manually start advertising after disconnecting.

Parameters

advertise_on_disconnect – Whether to advertise on disconnect

void set_advertisement_data(const AdvertisedData &advertising_data, uint8_t instance = 0)

Set the advertisement data for the device.

Note

This is only available when CONFIG_BT_NIMBLE_EXT_ADV is enabled.

Parameters
  • advertising_data – The advertising data for the device.

  • instance – The advertising instance to set the data for.

void set_scan_response_data(const AdvertisedData &scan_response_data, uint8_t instance = 0)

Set the scan response data for the device.

Note

This is only available when CONFIG_BT_NIMBLE_EXT_ADV is enabled.

Parameters
  • scan_response_data – The scan response data for the device.

  • instance – The advertising instance to set the data for.

void set_advertisement_data(const AdvertisedData &advertising_data)

Set the advertisement data for the device.

Note

This is only available when CONFIG_BT_NIMBLE_EXT_ADV is disabled.

Parameters

advertising_data – The advertising data for the device.

void set_scan_response_data(const AdvertisedData &scan_response_data)

Set the scan response data for the device.

Note

This is only available when CONFIG_BT_NIMBLE_EXT_ADV is disabled.

Parameters

scan_response_data – The scan response data for the device.

inline void stop_advertising()

Stop advertising This method stops advertising.

void stop_advertising(uint8_t instance)

Stop advertising This method stops advertising.

Note

This is only available when CONFIG_BT_NIMBLE_EXT_ADV is enabled.

Parameters

instance – The advertising instance to stop.

bool start_advertising(uint32_t duration_ms = 0, uint8_t instance = 0)

Start Advertising using the previously set advertising data This method simply starts advertising using the previously set advertising data.

Note

This is only available when CONFIG_BT_NIMBLE_EXT_ADV is enabled.

Parameters
  • duration_ms – The duration of the advertising in milliseconds. If 0, the advertising will not timeout. If non-zero, the advertising will stop after the specified duration.

  • instance – The advertising instance to start.

Returns

Whether advertising was started successfully.

bool start_advertising(const AdvertisingParameters &params)

Start Advertising using the previously set advertising data This method simply starts advertising using the previously set advertising data.

Note

This method is only used when CONFIG_BT_NIMBLE_EXT_ADV is not enabled, ane legacy advertising is used. Otherwise, use the NimBLEExtAdvertisement class for advertising and setting the advertising parameters.

Parameters

params – The advertising parameters for the device.

Returns

Whether advertising was started successfully.

bool start_advertising(uint32_t duration_ms = 0, NimBLEAddress *directed_address = nullptr)

Start Advertising using the previously set advertising data This method simply starts advertising using the previously set advertising data.

Note

This method is only used when CONFIG_BT_NIMBLE_EXT_ADV is not enabled, ane legacy advertising is used.

Parameters
  • duration_ms – The duration of the advertising in milliseconds. If 0, the advertising will not timeout. If non-zero, the advertising will stop after the specified duration.

  • directed_address – The address to direct advertising to, if any.

Returns

Whether advertising was started successfully.

inline NimBLEServer *server() const

Get the GATT server.

Returns

The GATT server.

inline DeviceInfoService &device_info_service()

Get the device info service.

inline BatteryService &battery_service()

Get the battery service.

inline void set_device_name(const std::string &device_name)

Set the name of the device.

Parameters

device_name – The name of the device.

inline void set_passkey(uint32_t passkey)

Set the passkey for the device.

Parameters

passkey – The passkey for the device.

inline void set_security(bool bonding, bool mitm, bool secure)

Set the security settings for the device.

Parameters
  • bonding – Whether to use bonding.

  • mitm – Man-in-the-middle protection.

  • secure – Whether to use secure connections.

inline void set_io_capabilities(uint8_t io_capabilities)

Set the IO capabilities for the device.

See also

BLE_HS_IO_NO_INPUT_OUTPUT

See also

BLE_HS_IO_DISPLAY_ONLY

See also

BLE_HS_IO_DISPLAY_YESNO

See also

BLE_HS_IO_KEYBOARD_ONLY

See also

BLE_HS_IO_NO_INPUT_OUTPUT

See also

BLE_HS_IO_KEYBOARD_DISPLAY

Parameters

io_capabilities – The IO capabilities for the device.

inline void set_init_key_distribution(uint8_t key_distribution)

Set the initial key distribution for the device.

See also

BLE_SM_PAIR_KEY_DIST_ENC

See also

BLE_SM_PAIR_KEY_DIST_ID

Parameters

key_distribution – The initial key distribution for the device.

inline void set_resp_key_distribution(uint8_t key_distribution)

Set the response key distribution for the device.

See also

BLE_SM_PAIR_KEY_DIST_ENC

See also

BLE_SM_PAIR_KEY_DIST_ID

Parameters

key_distribution – The response key distribution for the device.

inline std::vector<NimBLEAddress> get_paired_devices()

Get the paired devices

Returns

The paired devices as a vector of Addresses.

inline std::vector<NimBLEAddress> get_connected_device_addresses()

Get the connected device addresses

Returns

The addresses for the connected devices as a vector.

inline std::vector<NimBLEConnInfo> get_connected_device_infos()

Get the NimBLEConnInfo objects for the connected devices

Returns

The connected devices info as a vector of NimBLEConnInfo.

inline std::string get_connected_device_name(NimBLEConnInfo &conn_info)

Get the connected device name

Parameters

conn_info – The connection information for the device.

Returns

The connected device name.

inline std::vector<std::string> get_connected_device_names()

Get the connected device names

Note

This method will connect to each device to get the device name. This may take some time if there are many devices connected.

Returns

The connected device names as a vector of strings.

inline int get_connected_device_rssi(NimBLEConnInfo &conn_info)

Get the RSSI of the connected device

Parameters

conn_info – The connection information for the device.

Returns

The RSSI of the connected device.

inline std::vector<int> get_connected_devices_rssi_values()

Get the RSSI of the connected devices

Note

This method will connect to each device to get the RSSI. This may take some time if there are many devices connected.

Returns

The RSSI of the connected devices as a vector.

inline std::vector<NimBLEAddress> disconnect_all()

Disconnect from all devices This method disconnects from all devices that are currently connected.

Returns

The Addresses of the devices that were disconnected from.

inline std::vector<NimBLEAddress> unpair_all()

Unpair all devices This method unpairs all devices that are currently paired.

Returns

The Addresses of the devices that were unpaired.

inline const std::string &get_name() const

Get the name of the component

Note

This is the tag of the logger

Returns

A const reference to the name of the component

inline void set_log_tag(const std::string_view &tag)

Set the tag for the logger

Parameters

tag – The tag to use for the logger

inline espp::Logger::Verbosity get_log_level() const

Get the log level for the logger

Returns

The verbosity level of the logger

inline void set_log_level(espp::Logger::Verbosity level)

Set the log level for the logger

Parameters

level – The verbosity level to use for the logger

inline void set_log_verbosity(espp::Logger::Verbosity level)

Set the log verbosity for the logger

See also

set_log_level

Note

This is a convenience method that calls set_log_level

Parameters

level – The verbosity level to use for the logger

inline espp::Logger::Verbosity get_log_verbosity() const

Get the log verbosity for the logger

See also

get_log_level

Note

This is a convenience method that calls get_log_level

Returns

The verbosity level of the logger

inline void set_log_rate_limit(std::chrono::duration<float> rate_limit)

Set the rate limit for the logger

Note

Only calls to the logger that have _rate_limit suffix will be rate limited

Parameters

rate_limit – The rate limit to use for the logger

Public Static Functions

static inline constexpr uint16_t interval_ms_to_units(uint16_t interval_ms)

Convert an interval in milliseconds to units of 0.625 ms.

Parameters

interval_ms – The interval in milliseconds.

Returns

The interval in units of 0.625 ms.

static inline constexpr uint16_t interval_units_to_ms(uint16_t interval_units)

Convert an interval in units of 0.625 ms to milliseconds.

Parameters

interval_units – The interval in units of 0.625 ms.

Returns

The interval in milliseconds.

struct AdvertisingParameters

Advertising parameters for the device. This struct contains the advertising parameters for the device.

Note

This struct is only available when CONFIG_BT_NIMBLE_EXT_ADV is not enabled, ane legacy advertising is used. Otherwise, use the NimBLEExtAdvertisement class.

Public Members

bool connectable = true

Whether the device should be connectable.

uint16_t min_interval_ms = 20

Minimum advertising interval. NOTE: this is in milliseconds, but will be converted to units of 0.625 ms. If the value is not a multiple of 0.625 ms, it will be rounded to the nearest multiple and a log message will be printed.

uint16_t max_interval_ms = 40

Maximum advertising interval. NOTE: this is in milliseconds, but will be converted to units of 0.625 ms. If the value is not a multiple of 0.625 ms, it will be rounded to the nearest multiple and a log message will be printed.

bool include_tx_power = false

Whether to include the TX power level.

bool scan_response = false

Whether the device should include scan response data.

bool scan_request_whitelist = false

Whether the device should use the white list when scanning

bool connect_whitelist = false

Whether the device should use the white list when connecting

uint32_t duration_ms = 0

Advertising duration (in ms, 0 for no timeout)

NimBLEAddress *directed_address = nullptr

Address to direct advertising to, if any.

struct Callbacks

Callbacks for the GATT server.

Public Members

connect_callback_t connect_callback = nullptr

Callback for when a device connects to the GATT server.

disconnect_callback_t disconnect_callback = nullptr

Callback for when a device disconnects from the GATT server.

authentication_complete_callback_t authentication_complete_callback = nullptr

Callback for when a device completes authentication.

get_passkey_callback_t get_passkey_callback = nullptr

Callback for getting the passkey.

Note

If not provided, will simply return NimBLEDevice::getSecurityPasskey().

confirm_passkey_callback_t confirm_passkey_callback = nullptr

Callback for confirming the passkey.

Note

Within this function or some point after this call, the user should call NimBLEDevice::injectConfirmPIN(conn_info, true/false) to confirm or reject the passkey.

Note

If not provided, will simply call NimBLEDevice::injectConfirmPIN(conn_info, passkey == NimBLEDevice::getSecurityPasskey()).

advertisement_complete_callback_t advertisement_complete_callback = nullptr

Callback for when advertising is complete.

Note

This is called when advertising is complete, not when the device is actually advertising. It will not be called if the advertisement duration is 0 (i.e. no timeout)

Note

This is only available when CONFIG_BT_NIMBLE_EXT_ADV is not enabled.

advertisement_stopped_callback_t advertisement_stopped_callback = nullptr

Callback for when advertising is stopped.

Note

This is only available when CONFIG_BT_NIMBLE_EXT_ADV is enabled.

scan_request_callback_t scan_request_callback = nullptr

Callback for when a scan request is received.

Note

This is only available when CONFIG_BT_NIMBLE_EXT_ADV is enabled.

struct Config

Configuration for the GATT server.

Public Members

Callbacks callbacks

The callbacks for the GATT server.

espp::Logger::Verbosity log_level = espp::Logger::Verbosity::WARN

The verbosity of the logger_.

Header File

Classes

class BleGattServerCallbacks : public NimBLEServerCallbacks

Class for handling GATT server callbacks, as required by NimBLE

Note

This class is not intended to be used directly by the user

class BleGattServerAdvertisingCallbacks : public NimBLEExtAdvertisingCallbacks

Class for handling GATT server advertising callbacks, as required by NimBLE

Note

This class is not intended to be used directly by the user

Note

This class is only available if CONFIG_BT_NIMBLE_EXT_ADV is enabled

Header File

Classes

class BleGattServerMenu

A CLI menu for interacting with BleGattServer.

This class provides a CLI menu for interacting with a BleGattServer. It provides options for setting the log verbosity, viewing the paired devices, viewing the connected devices, disconnecting devices, and more.

Example

    // NOTE: esp-nimble-cpp already depends on nvs_flash and initializes
    //       nvs_flash in the NimBLEDevice::init(), so we don't have to do that
    //       to store bonding info

    // create the GATT server
    espp::BleGattServer ble_gatt_server;
    std::string device_name = "Espp BLE GATT Server";
    ble_gatt_server.set_log_level(espp::Logger::Verbosity::INFO);
    ble_gatt_server.set_callbacks({
        .connect_callback = [&](NimBLEConnInfo &conn_info) { logger.info("Device connected"); },
        .disconnect_callback = [&](auto &conn_info,
                                   auto reason) { logger.info("Device disconnected: {}", reason); },
        .authentication_complete_callback =
            [&](const NimBLEConnInfo &conn_info) { logger.info("Device authenticated"); },
        // NOTE: this is optional, if you don't provide this callback, it will
        // perform the exactly function as below:
        .get_passkey_callback =
            [&]() {
              logger.info("Getting passkey");
              return NimBLEDevice::getSecurityPasskey();
            },
        // NOTE: this is optional, if you don't provide this callback, it will
        // perform the exactly function as below:
        .confirm_passkey_callback =
            [&](const NimBLEConnInfo &conn_info, uint32_t passkey) {
              logger.info("Confirming passkey: {}", passkey);
              NimBLEDevice::injectConfirmPIN(conn_info,
                                             passkey == NimBLEDevice::getSecurityPasskey());
            },
    });
    ble_gatt_server.init(device_name);
#if !CONFIG_BT_NIMBLE_EXT_ADV
    // extended advertisement does not support automatically advertising on
    // disconnect
    ble_gatt_server.set_advertise_on_disconnect(true);
#endif

    // let's configure the security
    bool bonding = true;
    bool mitm = false;
    bool secure_connections = true;
    ble_gatt_server.set_security(bonding, mitm, secure_connections);
    // and some i/o and key config
    NimBLEDevice::setSecurityPasskey(123456);
    ble_gatt_server.set_io_capabilities(BLE_HS_IO_NO_INPUT_OUTPUT);
    ble_gatt_server.set_init_key_distribution(BLE_SM_PAIR_KEY_DIST_ENC | BLE_SM_PAIR_KEY_DIST_ID);
    ble_gatt_server.set_resp_key_distribution(BLE_SM_PAIR_KEY_DIST_ENC | BLE_SM_PAIR_KEY_DIST_ID);

    // you can create a service and add it to the server using
    // ble_gatt_server.server().addService()

    // now start the services
    ble_gatt_server.start_services(); // starts the device info service and battery service
    // NOTE: we could also directly start them ourselves if we wanted to
    //      control the order of starting the services
    // e.g.:
    // ble_gatt_server.battery_service().start();
    // ble_gatt_server.device_info_service().start();

    // now start the gatt server
    ble_gatt_server.start();

    // let's set some of the service data
    auto &battery_service = ble_gatt_server.battery_service();
    battery_service.set_battery_level(99);

    auto &device_info_service = ble_gatt_server.device_info_service();
    uint8_t vendor_source = 0x01;
    uint16_t vid = 0xCafe;
    uint16_t pid = 0xFace;
    uint16_t product_version = 0x0100;
    device_info_service.set_pnp_id(vendor_source, vid, pid, product_version);
    device_info_service.set_manufacturer_name("ESP-CPP");
    device_info_service.set_model_number("esp-ble-01");
    device_info_service.set_serial_number("1234567890");
    device_info_service.set_software_version("1.0.0");
    device_info_service.set_firmware_version("1.0.0");
    device_info_service.set_hardware_version("1.0.0");

    // set the advertising data
    espp::BleGattServer::AdvertisedData adv_data;
    // uint8_t flags = BLE_HS_ADV_F_DISC_LTD;
    uint8_t flags = BLE_HS_ADV_F_DISC_GEN;
    adv_data.setFlags(flags);
    adv_data.setName(device_name);
    adv_data.setAppearance((uint16_t)espp::BleAppearance::GENERIC_COMPUTER);
    adv_data.addTxPower();
    ble_gatt_server.set_advertisement_data(adv_data);

#if CONFIG_COMPILER_CXX_EXCEPTIONS
    // let's test and use the BLE menu (CLI)
    // turn off some of the logs so that it doesn't clutter up the CLI
    ble_gatt_server.set_log_level(espp::Logger::Verbosity::WARN);
    // and make the CLI
    auto ble_menu = espp::BleGattServerMenu(ble_gatt_server);
    cli::SetColor();
    cli::Cli cli(ble_menu.get());
    cli.ExitAction([](auto &out) { out << "Goodbye and thanks for all the fish.\n"; });

    espp::Cli input(cli);
    input.SetInputHistorySize(10);
    input.Start(); // this will not return until the user enters the `exit` command.
#endif

    logger.info("Menu has finished, starting advertising");
    // The menu has finished, so let's go into a loop to keep the device running
    // now lets start advertising
    ble_gatt_server.start_advertising();

    logger.info("Waiting for connection...");
    // now lets update the battery level every second for a little while
    uint8_t battery_level = 99;
    bool was_connected = false;
    bool can_exit = false;
    int num_seconds_to_wait = 30;
    while (true) {
      auto start = std::chrono::steady_clock::now();

      // if we are now connected, but were not, then get the services
      if (ble_gatt_server.is_connected() && !was_connected) {
        was_connected = true;
        can_exit = true;
        auto connected_device_infos = ble_gatt_server.get_connected_device_infos();
        logger.info("Connected devices: {}", connected_device_infos.size());
        std::vector<std::string> connected_device_names;
        std::transform(connected_device_infos.begin(), connected_device_infos.end(),
                       std::back_inserter(connected_device_names),
                       [&](auto &info) { return ble_gatt_server.get_connected_device_name(info); });
        logger.info("            Names: {}", connected_device_names);
      } else if (!ble_gatt_server.is_connected()) {
        was_connected = false;
        if (can_exit) {
          logger.info("No longer connected, exiting");
          break;
        }
      }

      if (!ble_gatt_server.is_connected()) {
        logger.move_up();
        logger.clear_line();
        logger.info("Waiting for connection... {}s", --num_seconds_to_wait);
        if (num_seconds_to_wait == 0) {
          logger.info("No connection, exiting");
          break;
        }
        // sleep
        std::this_thread::sleep_until(start + 1s);
        continue;
      }

      // update the battery level
      battery_service.set_battery_level(battery_level);
      battery_level = (battery_level % 100) + 1;

      // sleep
      std::this_thread::sleep_until(start + 1s);
    }

    // we are done, so stop the server and deinit the BLE stack. NOTE: this will
    // automatically be called by ~BleGattServer(), but we call it here to show
    // manual control and to test calling it multiple times since the destructor
    // will be called immediately after this block ends
    ble_gatt_server.deinit();

Public Functions

inline explicit BleGattServerMenu(std::reference_wrapper<espp::BleGattServer> server)

Construct a new I2cMenu object.

Parameters

i2c – A reference to the I2c bus to interact with.

inline std::unique_ptr<cli::Menu> get(std::string_view name = "ble", std::string_view description = "BLE GATT Server menu")

Get the BleGattServer menu.

Parameters
  • name – The name of the menu.

  • description – The description of the menu.

Returns

A unique pointer to the BleGattServer menu that you can use to add to a CLI.