From c6c891d1c3fe8f3c922e516e927dc8ab42472bc4 Mon Sep 17 00:00:00 2001 From: William Emfinger Date: Thu, 6 Feb 2025 10:38:54 -0600 Subject: [PATCH 1/2] feat(ble): Update `BleGattServer` and `esp-nimble-cpp` submodule * Add some new methods to `BleGattServer` * Remove logging from many methods which was unnecessary, and mark those methods `const` * Update `hid_service` example to print Manufacturer name, model number, and PnpId for remote device * Update `hid_service` example to print connected info more clearly and have cleaner code * Update `hid_service` example to wait until after auth to read values from the remote * Update `DeviceInfoService` to have static methods for parsing received PnpId * Update `DeviceInfoService` and `BatteryService` so UUIDs are public * Update `esp-nimble-cpp` submodule to latest, brining in improvements as well as new feature to register store callback for managing bonding info when it fills up. Allows you to read more information from the remotely connected device, if it is supported. Allows you to better control which bonds are deleted / how bonds are managed when the bond storage space fills up * Build and run `hid_service/exmaple` on QtPy ESP32s3 and test with iPhone and Android * Build and run `ble_gatt_server/example` on QtPy ESP32s3 and test with Android. --- .../example/main/ble_gatt_server_example.cpp | 44 +++-- .../include/battery_service.hpp | 10 +- .../include/ble_gatt_server.hpp | 186 ++++++++++++++---- .../include/device_info_service.hpp | 52 ++++- .../src/ble_gatt_server_callbacks.cpp | 83 ++++---- components/esp-nimble-cpp | 2 +- .../example/main/hid_service_example.cpp | 38 ++-- 7 files changed, 294 insertions(+), 121 deletions(-) diff --git a/components/ble_gatt_server/example/main/ble_gatt_server_example.cpp b/components/ble_gatt_server/example/main/ble_gatt_server_example.cpp index 7dc07bfca..8ed77e1ed 100644 --- a/components/ble_gatt_server/example/main/ble_gatt_server_example.cpp +++ b/components/ble_gatt_server/example/main/ble_gatt_server_example.cpp @@ -130,20 +130,40 @@ extern "C" void app_main(void) { bool was_connected = false; bool can_exit = false; int num_seconds_to_wait = 30; + auto loop_start = std::chrono::steady_clock::now(); 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) { + auto connected_device_infos = ble_gatt_server.get_connected_device_infos(); + // check to make sure the first connection has bonded at least. + // + // NOTE: if we are not bonded, then the name that will be read out will be + // generic, such as "iPhone". If we are bonded, then the name will be the + // actual device name, such as "iPhone 14 Plus William". + const auto &first_device = connected_device_infos.front(); + if (first_device.isBonded()) { + // if it was, mark connected and print all the device infos + was_connected = true; + can_exit = true; + logger.info("Connected devices: {}", connected_device_infos.size()); + for (const auto &info : connected_device_infos) { + auto rssi = ble_gatt_server.get_connected_device_rssi(info); + auto name = ble_gatt_server.get_connected_device_name(info); + auto mfg_name = ble_gatt_server.get_connected_device_manufacturer_name(info); + auto model_number = ble_gatt_server.get_connected_device_model_number(info); + auto pnp_id = ble_gatt_server.get_connected_device_pnp_id(info); + logger.info(" RSSI: {}", rssi); + logger.info(" Name: {}", name); + // NOTE: these are optionals, so they may not be set + logger.info(" Mfg: {}", mfg_name); + logger.info(" Model: {}", model_number); + logger.info(" PnP: {}", pnp_id); + } + } 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 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) { @@ -155,13 +175,15 @@ extern "C" void app_main(void) { 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) { + auto now = std::chrono::steady_clock::now(); + auto elapsed = std::chrono::duration_cast(now - loop_start); + logger.info("Waiting for connection... {}s", num_seconds_to_wait - elapsed.count()); + if (elapsed.count() > num_seconds_to_wait) { logger.info("No connection, exiting"); break; } // sleep - std::this_thread::sleep_until(start + 1s); + std::this_thread::sleep_until(start + 100ms); continue; } @@ -170,7 +192,7 @@ extern "C" void app_main(void) { battery_level = (battery_level % 100) + 1; // sleep - std::this_thread::sleep_until(start + 1s); + std::this_thread::sleep_until(start + 100ms); } // we are done, so stop the server and deinit the BLE stack. NOTE: this will diff --git a/components/ble_gatt_server/include/battery_service.hpp b/components/ble_gatt_server/include/battery_service.hpp index c78083409..72a2c9a3c 100644 --- a/components/ble_gatt_server/include/battery_service.hpp +++ b/components/ble_gatt_server/include/battery_service.hpp @@ -26,6 +26,11 @@ namespace espp { /// device. class BatteryService : public BaseComponent { public: + static constexpr uint8_t BATTERY_LEVEL_MAX = 100; ///< Maximum battery level + static constexpr uint16_t BATTERY_LEVEL_UNIT = 0x27AD; ///< Unit is percentage + static constexpr uint16_t BATTERY_SERVICE_UUID = 0x180F; ///< Battery Service UUID + static constexpr uint16_t BATTERY_LEVEL_CHAR_UUID = 0x2A19; ///< Battery Level Characteristic UUID + /// Constructor /// \param log_level The log level for the component explicit BatteryService(espp::Logger::Verbosity log_level = espp::Logger::Verbosity::WARN) @@ -103,11 +108,6 @@ class BatteryService : public BaseComponent { } protected: - static constexpr uint8_t BATTERY_LEVEL_MAX = 100; ///< Maximum battery level - static constexpr uint16_t BATTERY_LEVEL_UNIT = 0x27AD; ///< Unit is percentage - static constexpr uint16_t BATTERY_SERVICE_UUID = 0x180F; ///< Battery Service UUID - static constexpr uint16_t BATTERY_LEVEL_CHAR_UUID = 0x2A19; ///< Battery Level Characteristic UUID - void make_service(NimBLEServer *server) { service_ = server->createService(NimBLEUUID(BATTERY_SERVICE_UUID)); if (!service_) { diff --git a/components/ble_gatt_server/include/ble_gatt_server.hpp b/components/ble_gatt_server/include/ble_gatt_server.hpp index 8698bcd7a..883abf8fa 100644 --- a/components/ble_gatt_server/include/ble_gatt_server.hpp +++ b/components/ble_gatt_server/include/ble_gatt_server.hpp @@ -219,6 +219,7 @@ class BleGattServer : public BaseComponent { /// Get whether the GATT server is connected to any clients. /// @return Whether the GATT server is connected to any clients. + /// @note This will return false if the server is not created. bool is_connected() const { if (!server_) { return false; @@ -240,7 +241,6 @@ class BleGattServer : public BaseComponent { /// @return Whether the GATT server was initialized successfully. bool init(const std::string &device_name) { if (server_) { - logger_.info("Server already created, not initializing again"); return true; } @@ -249,7 +249,6 @@ class BleGattServer : public BaseComponent { NimBLEDevice::init(device_name); // create the server - logger_.info("Creating server"); server_ = NimBLEDevice::createServer(); if (!server_) { logger_.error("Failed to create server"); @@ -259,11 +258,9 @@ class BleGattServer : public BaseComponent { // set the server callbacks server_->setCallbacks(new BleGattServerCallbacks(this)); - logger_.info("Creating device info service"); // create the device info service device_info_service_.init(server_); - logger_.info("Creating battery service"); // create the battery service battery_service_.init(server_); @@ -281,7 +278,6 @@ class BleGattServer : public BaseComponent { /// services. void deinit() { if (!server_) { - logger_.info("Server is nullptr; already deinitialized, not deinitializing again"); return; } @@ -310,7 +306,6 @@ class BleGattServer : public BaseComponent { /// @return Whether the server was started successfully. bool start() { if (!server_) { - logger_.error("Server not created"); return false; } server_->start(); @@ -325,7 +320,6 @@ class BleGattServer : public BaseComponent { /// manually start advertising after disconnecting. void set_advertise_on_disconnect(bool advertise_on_disconnect) { if (!server_) { - logger_.error("Server not created"); return; } // set whether to advertise on disconnect @@ -363,7 +357,6 @@ class BleGattServer : public BaseComponent { /// This method stops advertising. void stop_advertising() { if (!server_) { - logger_.error("Server not created"); return; } auto advertising = NimBLEDevice::getAdvertising(); @@ -431,17 +424,19 @@ class BleGattServer : public BaseComponent { /// @brief Set the name of the device. /// @param device_name The name of the device. - void set_device_name(const std::string &device_name) { NimBLEDevice::setDeviceName(device_name); } + void set_device_name(const std::string &device_name) const { + NimBLEDevice::setDeviceName(device_name); + } /// @brief Set the passkey for the device. /// @param passkey The passkey for the device. - void set_passkey(uint32_t passkey) { NimBLEDevice::setSecurityPasskey(passkey); } + void set_passkey(uint32_t passkey) const { NimBLEDevice::setSecurityPasskey(passkey); } /// Set the security settings for the device. /// @param bonding Whether to use bonding. /// @param mitm Man-in-the-middle protection. /// @param secure Whether to use secure connections. - void set_security(bool bonding, bool mitm, bool secure) { + void set_security(bool bonding, bool mitm, bool secure) const { NimBLEDevice::setSecurityAuth(bonding, mitm, secure); } @@ -453,7 +448,7 @@ class BleGattServer : public BaseComponent { /// @see BLE_HS_IO_KEYBOARD_ONLY /// @see BLE_HS_IO_NO_INPUT_OUTPUT /// @see BLE_HS_IO_KEYBOARD_DISPLAY - void set_io_capabilities(uint8_t io_capabilities) { + void set_io_capabilities(uint8_t io_capabilities) const { NimBLEDevice::setSecurityIOCap(io_capabilities); } @@ -461,7 +456,7 @@ class BleGattServer : public BaseComponent { /// @param key_distribution The initial key distribution for the device. /// @see BLE_SM_PAIR_KEY_DIST_ENC /// @see BLE_SM_PAIR_KEY_DIST_ID - void set_init_key_distribution(uint8_t key_distribution) { + void set_init_key_distribution(uint8_t key_distribution) const { NimBLEDevice::setSecurityInitKey(key_distribution); } @@ -469,13 +464,13 @@ class BleGattServer : public BaseComponent { /// @param key_distribution The response key distribution for the device. /// @see BLE_SM_PAIR_KEY_DIST_ENC /// @see BLE_SM_PAIR_KEY_DIST_ID - void set_resp_key_distribution(uint8_t key_distribution) { + void set_resp_key_distribution(uint8_t key_distribution) const { NimBLEDevice::setSecurityRespKey(key_distribution); } /// Get the paired devices /// @return The paired devices as a vector of Addresses. - std::vector get_paired_devices() { + std::vector get_paired_devices() const { std::vector paired_devices; auto num_bonds = NimBLEDevice::getNumBonds(); for (int i = 0; i < num_bonds; i++) { @@ -485,11 +480,22 @@ class BleGattServer : public BaseComponent { return paired_devices; } + /// Get the connected handles + /// @return The connected handles. + /// @note This will return an empty vector if the server is not created. + /// @note This will return the handles of the connected devices. These handles + /// can be used to retrieve the connection information for the devices. + std::vector get_connected_device_handles() const { + if (!server_) { + return {}; + } + return server_->getPeerDevices(); + } + /// Get the connected device addresses /// @return The addresses for the connected devices as a vector. - std::vector get_connected_device_addresses() { + std::vector get_connected_device_addresses() const { if (!server_) { - logger_.error("Server not created"); return {}; } std::vector connected_addresses; @@ -503,9 +509,8 @@ class BleGattServer : public BaseComponent { /// Get the NimBLEConnInfo objects for the connected devices /// @return The connected devices info as a vector of NimBLEConnInfo. - std::vector get_connected_device_infos() { + std::vector get_connected_device_infos() const { if (!server_) { - logger_.error("Server not created"); return {}; } std::vector connected_devices_info; @@ -520,9 +525,8 @@ class BleGattServer : public BaseComponent { /// Get the connected device name /// @param conn_info The connection information for the device. /// @return The connected device name. - std::string get_connected_device_name(const NimBLEConnInfo &conn_info) { + std::string get_connected_device_name(const NimBLEConnInfo &conn_info) const { if (!server_) { - logger_.error("Server not created"); return {}; } // since this connection is handled by the server, we won't manually @@ -534,18 +538,15 @@ class BleGattServer : public BaseComponent { // now get Generic Access Service auto gas = client->getService(NimBLEUUID("1800")); if (!gas) { - logger_.error("Failed to get Generic Access Service"); return {}; } // now get the Device Name characteristic auto name_char = gas->getCharacteristic(NimBLEUUID("2A00")); if (!name_char) { - logger_.error("Failed to get Device Name characteristic"); return {}; } // make sure we can read it if (!name_char->canRead()) { - logger_.error("Failed to read Device Name characteristic"); return {}; } // and read it @@ -553,13 +554,125 @@ class BleGattServer : public BaseComponent { return name; } + /// Get the connected device PnP ID + /// @param conn_info The connection information for the device. + /// @return The connected device PnP ID. + /// @note This method will connect to the device to get the PnP ID. This may + /// take some time if the device is not already connected. + /// @note The remote device may not expose a Device Information Service, in + /// which case this method will return an empty optional. + std::optional + get_connected_device_pnp_id(const NimBLEConnInfo &conn_info) const { + if (!server_) { + return {}; + } + // since this connection is handled by the server, we won't manually + // connect, and instead inform the client that we are already connected + // using this conn handle + auto client = server_->getClient(conn_info); + // refresh the services + client->getServices(true); + // now get Device Information Service + auto dis = client->getService(NimBLEUUID(DeviceInfoService::SERVICE_UUID)); + if (!dis) { + return {}; + } + // now get the PnP ID characteristic + auto characteristic = dis->getCharacteristic(NimBLEUUID(DeviceInfoService::PNP_CHAR_UUID)); + if (!characteristic) { + return {}; + } + // make sure we can read it + if (!characteristic->canRead()) { + return {}; + } + // and read it + auto value = characteristic->readValue(); + return DeviceInfoService::parse_pnp_id(value); + } + + /// Get the connected device Manufacturer Name string + /// @param conn_info The connection information for the device. + /// @return The connected device Manufacturer Name string. + /// @note This method will connect to the device to read the characteristic. + /// This may take some time if the device is not already connected. + /// @note The remote device may not expose a Device Information Service, in + /// which case this method will return an empty optional. + std::optional + get_connected_device_manufacturer_name(const NimBLEConnInfo &conn_info) const { + if (!server_) { + return {}; + } + // since this connection is handled by the server, we won't manually + // connect, and instead inform the client that we are already connected + // using this conn handle + auto client = server_->getClient(conn_info); + // refresh the services + client->getServices(true); + // now get Device Information Service + auto dis = client->getService(NimBLEUUID(DeviceInfoService::SERVICE_UUID)); + if (!dis) { + return {}; + } + // now get the characteristic + auto characteristic = + dis->getCharacteristic(NimBLEUUID(DeviceInfoService::MANUFACTURER_NAME_CHAR_UUID)); + if (!characteristic) { + return {}; + } + // make sure we can read it + if (!characteristic->canRead()) { + return {}; + } + // and read it + auto value = characteristic->readValue(); + return value; + } + + /// Get the connected device Model Number string + /// @param conn_info The connection information for the device. + /// @return The connected device Model Number string. + /// @note This method will connect to the device to read the characteristic. + /// This may take some time if the device is not already connected. + /// @note The remote device may not expose a Device Information Service, in + /// which case this method will return an empty optional. + std::optional + get_connected_device_model_number(const NimBLEConnInfo &conn_info) const { + if (!server_) { + return {}; + } + // since this connection is handled by the server, we won't manually + // connect, and instead inform the client that we are already connected + // using this conn handle + auto client = server_->getClient(conn_info); + // refresh the services + client->getServices(true); + // now get Device Information Service + auto dis = client->getService(NimBLEUUID(DeviceInfoService::SERVICE_UUID)); + if (!dis) { + return {}; + } + // now get the characteristic + auto characteristic = + dis->getCharacteristic(NimBLEUUID(DeviceInfoService::MODEL_NUMBER_CHAR_UUID)); + if (!characteristic) { + return {}; + } + // make sure we can read it + if (!characteristic->canRead()) { + return {}; + } + // and read it + auto value = characteristic->readValue(); + return value; + } + /// Get the connected device names /// @return The connected device names as a vector of strings. /// @note This method will connect to each device to get the device name. /// This may take some time if there are many devices connected. - std::vector get_connected_device_names() { + std::vector get_connected_device_names() const { if (!server_) { - logger_.error("Server not created"); return {}; } std::vector connected_device_names; @@ -569,36 +682,27 @@ class BleGattServer : public BaseComponent { auto peer_name = get_connected_device_name(peer); if (!peer_name.empty()) { connected_device_names.push_back(peer_name); - } else { - logger_.error("Failed to get device name for connected device {}", - peer.getAddress().toString()); } } - logger_.info("Connected device names: {}", connected_device_names); return connected_device_names; } /// Get the RSSI of the connected device /// @param conn_info The connection information for the device. /// @return The RSSI of the connected device. - int get_connected_device_rssi(const NimBLEConnInfo &conn_info) { + int get_connected_device_rssi(const NimBLEConnInfo &conn_info) const { if (!server_) { - logger_.error("Server not created"); return {}; } if (!is_connected()) { - logger_.error("Not connected to any devices"); return {}; } - auto peer_address = conn_info.getAddress(); - logger_.debug("Getting RSSI for connected device {}", peer_address.toString()); // since this connection is handled by the server, we won't manually // connect, and instead inform the client that we are already connected // using this conn handle auto client = server_->getClient(conn_info); // and read the RSSI from the client auto rssi = client->getRssi(); - logger_.info("RSSI for connected device {}: {}", peer_address.toString(), rssi); return rssi; } @@ -606,9 +710,8 @@ class BleGattServer : public BaseComponent { /// @return The RSSI of the connected devices as a vector. /// @note This method will connect to each device to get the RSSI. /// This may take some time if there are many devices connected. - std::vector get_connected_devices_rssi_values() { + std::vector get_connected_devices_rssi_values() const { if (!server_) { - logger_.error("Server not created"); return {}; } std::vector connected_device_rssi; @@ -618,16 +721,14 @@ class BleGattServer : public BaseComponent { auto peer_rssi = get_connected_device_rssi(peer); connected_device_rssi.push_back(peer_rssi); } - logger_.info("Connected device RSSI: {}", connected_device_rssi); return connected_device_rssi; } /// Disconnect from all devices /// This method disconnects from all devices that are currently connected. /// @return The Addresses of the devices that were disconnected from. - std::vector disconnect_all() { + std::vector disconnect_all() const { if (!server_) { - logger_.error("Server not created"); return {}; } std::vector disconnected_devices; @@ -645,12 +746,11 @@ class BleGattServer : public BaseComponent { /// @return The Addresses of the devices that were unpaired. std::vector unpair_all() { if (!server_) { - logger_.error("Server not created"); return {}; } std::vector unpaired_devices; auto num_bonds = NimBLEDevice::getNumBonds(); - logger_.info("Unpairing {} devices", num_bonds); + logger_.debug("Unpairing {} devices", num_bonds); for (int i = 0; i < num_bonds; i++) { auto bond_addr = NimBLEDevice::getBondedAddress(i); logger_.debug("Unpairing device {}", bond_addr.toString()); diff --git a/components/ble_gatt_server/include/device_info_service.hpp b/components/ble_gatt_server/include/device_info_service.hpp index 1e7740cb5..c46df334e 100644 --- a/components/ble_gatt_server/include/device_info_service.hpp +++ b/components/ble_gatt_server/include/device_info_service.hpp @@ -34,6 +34,15 @@ namespace espp { /// used with any device. class DeviceInfoService : public BaseComponent { public: + static constexpr uint16_t SERVICE_UUID = 0x180A; + static constexpr uint16_t PNP_CHAR_UUID = 0x2A50; + static constexpr uint16_t MANUFACTURER_NAME_CHAR_UUID = 0x2A29; + static constexpr uint16_t MODEL_NUMBER_CHAR_UUID = 0x2A24; + static constexpr uint16_t SERIAL_NUMBER_CHAR_UUID = 0x2A25; + static constexpr uint16_t SOFTWARE_VERSION_CHAR_UUID = 0x2A28; + static constexpr uint16_t FIRMWARE_VERSION_CHAR_UUID = 0x2A26; + static constexpr uint16_t HARDWARE_VERSION_CHAR_UUID = 0x2A27; + /// Plug and Play ID struct PnpId { uint8_t vendor_id_source = 0x01; ///< 0x01 for Bluetooth SIG, 0x02 for USB @@ -160,6 +169,26 @@ class DeviceInfoService : public BaseComponent { pnp_->setValue(pnp_id, sizeof(pnp_id)); } + static PnpId parse_pnp_id(const std::vector &data) { + return parse_pnp_id(data.data(), data.size()); + } + + /// Parse the PnP ID data + /// \param data The PnP ID data + /// \param size The size of the data + /// \return The PnP ID + static PnpId parse_pnp_id(const uint8_t *data, size_t size) { + PnpId pnp_id; + if (size < 7) { + return pnp_id; + } + pnp_id.vendor_id_source = data[0]; + pnp_id.vendor_id = (data[2] << 8) | data[1]; + pnp_id.product_id = (data[4] << 8) | data[3]; + pnp_id.product_version = (data[6] << 8) | data[5]; + return pnp_id; + } + /// Set the manufacturer name /// \param name The manufacturer name void set_manufacturer_name(const std::string &name) { @@ -221,15 +250,6 @@ class DeviceInfoService : public BaseComponent { } protected: - static constexpr uint16_t SERVICE_UUID = 0x180A; - static constexpr uint16_t PNP_CHAR_UUID = 0x2A50; - static constexpr uint16_t MANUFACTURER_NAME_CHAR_UUID = 0x2A29; - static constexpr uint16_t MODEL_NUMBER_CHAR_UUID = 0x2A24; - static constexpr uint16_t SERIAL_NUMBER_CHAR_UUID = 0x2A25; - static constexpr uint16_t SOFTWARE_VERSION_CHAR_UUID = 0x2A28; - static constexpr uint16_t FIRMWARE_VERSION_CHAR_UUID = 0x2A26; - static constexpr uint16_t HARDWARE_VERSION_CHAR_UUID = 0x2A27; - void make_service(NimBLEServer *server) { service_ = server->createService(NimBLEUUID(SERVICE_UUID)); if (!service_) { @@ -269,4 +289,18 @@ class DeviceInfoService : public BaseComponent { }; } // namespace espp +// for formatting using libfmt +template <> struct fmt::formatter { + constexpr auto parse(format_parse_context &ctx) const { return ctx.begin(); } + + template + auto format(const espp::DeviceInfoService::PnpId &pnp_id, FormatContext &ctx) const { + return fmt::format_to(ctx.out(), + "PnpId {{ vendor_id_source: 0x{:02X}, vendor_id: 0x{:04X}, " + "product_id: 0x{:04X}, product_version: 0x{:04X} }}", + pnp_id.vendor_id_source, pnp_id.vendor_id, pnp_id.product_id, + pnp_id.product_version); + } +}; + #endif // CONFIG_BT_NIMBLE_ENABLED || defined(_DOXYGEN_) diff --git a/components/ble_gatt_server/src/ble_gatt_server_callbacks.cpp b/components/ble_gatt_server/src/ble_gatt_server_callbacks.cpp index f3eaee039..ff5eed31d 100644 --- a/components/ble_gatt_server/src/ble_gatt_server_callbacks.cpp +++ b/components/ble_gatt_server/src/ble_gatt_server_callbacks.cpp @@ -6,55 +6,60 @@ using namespace espp; void BleGattServerCallbacks::onConnect(NimBLEServer *server, NimBLEConnInfo &conn_info) { - if (server_ && server_->callbacks_.connect_callback) { - server_->callbacks_.connect_callback(conn_info); + if (server_) { + if (server_->callbacks_.connect_callback) { + server_->callbacks_.connect_callback(conn_info); + } } } void BleGattServerCallbacks::onDisconnect(NimBLEServer *server, NimBLEConnInfo &conn_info, int reason) { - if (server_ && server_->callbacks_.disconnect_callback) { - // convert the reason to a espp::BleGattServer::DisconnectReason. For more - // information, see - // https://mynewt.apache.org/latest/network/ble_hs/ble_hs_return_codes.html - BleGattServer::DisconnectReason disconnect_reason = BleGattServer::DisconnectReason::UNKNOWN; - switch (reason) { - case BLE_HS_ETIMEOUT_HCI: - case (0x200 + BLE_ERR_CONN_SPVN_TMO): - case (0x200 + BLE_ERR_CONN_ACCEPT_TMO): - disconnect_reason = BleGattServer::DisconnectReason::TIMEOUT; - break; - case BLE_HS_EAUTHEN: - case BLE_HS_EAUTHOR: - case BLE_HS_EENCRYPT: - case (0x200 + BLE_ERR_AUTH_FAIL): - disconnect_reason = BleGattServer::DisconnectReason::AUTHENTICATION_FAILURE; - break; - case (0x0200 + BLE_ERR_CONN_TERM_MIC): - disconnect_reason = BleGattServer::DisconnectReason::CONNECTION_TERMINATED; - break; - case (0x0200 + BLE_ERR_REM_USER_CONN_TERM): - disconnect_reason = BleGattServer::DisconnectReason::REMOTE_USER_TERMINATED; - break; - case (0x0200 + BLE_ERR_RD_CONN_TERM_RESRCS): - case (0x0200 + BLE_ERR_RD_CONN_TERM_PWROFF): - disconnect_reason = BleGattServer::DisconnectReason::REMOTE_DEVICE_TERMINATED; - break; - case (0x0200 + BLE_ERR_CONN_TERM_LOCAL): - disconnect_reason = BleGattServer::DisconnectReason::LOCAL_USER_TERMINATED; - break; - default: - fmt::print("Unknown disconnect reason: {}\n", reason); - disconnect_reason = BleGattServer::DisconnectReason::UNKNOWN; - break; + if (server_) { + if (server_->callbacks_.disconnect_callback) { + // convert the reason to a espp::BleGattServer::DisconnectReason. For more + // information, see + // https://mynewt.apache.org/latest/network/ble_hs/ble_hs_return_codes.html + BleGattServer::DisconnectReason disconnect_reason = BleGattServer::DisconnectReason::UNKNOWN; + switch (reason) { + case BLE_HS_ETIMEOUT_HCI: + case (0x200 + BLE_ERR_CONN_SPVN_TMO): + case (0x200 + BLE_ERR_CONN_ACCEPT_TMO): + disconnect_reason = BleGattServer::DisconnectReason::TIMEOUT; + break; + case BLE_HS_EAUTHEN: + case BLE_HS_EAUTHOR: + case BLE_HS_EENCRYPT: + case (0x200 + BLE_ERR_AUTH_FAIL): + disconnect_reason = BleGattServer::DisconnectReason::AUTHENTICATION_FAILURE; + break; + case (0x0200 + BLE_ERR_CONN_TERM_MIC): + disconnect_reason = BleGattServer::DisconnectReason::CONNECTION_TERMINATED; + break; + case (0x0200 + BLE_ERR_REM_USER_CONN_TERM): + disconnect_reason = BleGattServer::DisconnectReason::REMOTE_USER_TERMINATED; + break; + case (0x0200 + BLE_ERR_RD_CONN_TERM_RESRCS): + case (0x0200 + BLE_ERR_RD_CONN_TERM_PWROFF): + disconnect_reason = BleGattServer::DisconnectReason::REMOTE_DEVICE_TERMINATED; + break; + case (0x0200 + BLE_ERR_CONN_TERM_LOCAL): + disconnect_reason = BleGattServer::DisconnectReason::LOCAL_USER_TERMINATED; + break; + default: + disconnect_reason = BleGattServer::DisconnectReason::UNKNOWN; + break; + } + server_->callbacks_.disconnect_callback(conn_info, disconnect_reason); } - server_->callbacks_.disconnect_callback(conn_info, disconnect_reason); } } void BleGattServerCallbacks::onAuthenticationComplete(NimBLEConnInfo &conn_info) { - if (server_ && server_->callbacks_.authentication_complete_callback) { - server_->callbacks_.authentication_complete_callback(conn_info); + if (server_) { + if (server_->callbacks_.authentication_complete_callback) { + server_->callbacks_.authentication_complete_callback(conn_info); + } } } uint32_t BleGattServerCallbacks::onPassKeyDisplay() { diff --git a/components/esp-nimble-cpp b/components/esp-nimble-cpp index e843c6811..b6379848a 160000 --- a/components/esp-nimble-cpp +++ b/components/esp-nimble-cpp @@ -1 +1 @@ -Subproject commit e843c6811c458139f2cc05ba89b52ba3ea5f816d +Subproject commit b6379848ae9712815d25817672a0631d7a69b217 diff --git a/components/hid_service/example/main/hid_service_example.cpp b/components/hid_service/example/main/hid_service_example.cpp index 408a50846..b674b5dec 100644 --- a/components/hid_service/example/main/hid_service_example.cpp +++ b/components/hid_service/example/main/hid_service_example.cpp @@ -167,21 +167,33 @@ extern "C" void app_main(void) { while (true) { auto start = std::chrono::steady_clock::now(); - // if we are now connected, but were not, then get the services + // if we are now connected + bonded, but were not, then get the services if (ble_gatt_server.is_connected() && !was_connected) { - was_connected = true; auto connected_device_infos = ble_gatt_server.get_connected_device_infos(); - logger.info("Connected devices: {}", connected_device_infos.size()); - std::vector 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); - std::vector connected_device_rssis; - std::transform(connected_device_infos.begin(), connected_device_infos.end(), - std::back_inserter(connected_device_rssis), - [&](auto &info) { return ble_gatt_server.get_connected_device_rssi(info); }); - logger.info(" RSSIs: {}", connected_device_rssis); + // check to make sure the first connection has bonded at least. + // + // NOTE: if we are not bonded, then the name that will be read out will be + // generic, such as "iPhone". If we are bonded, then the name will be the + // actual device name, such as "iPhone 14 Plus William". + const auto &first_device = connected_device_infos.front(); + if (first_device.isBonded()) { + // if it was, mark connected and print all the device infos + was_connected = true; + logger.info("Connected devices: {}", connected_device_infos.size()); + for (const auto &info : connected_device_infos) { + auto rssi = ble_gatt_server.get_connected_device_rssi(info); + auto name = ble_gatt_server.get_connected_device_name(info); + auto mfg_name = ble_gatt_server.get_connected_device_manufacturer_name(info); + auto model_number = ble_gatt_server.get_connected_device_model_number(info); + auto pnp_id = ble_gatt_server.get_connected_device_pnp_id(info); + logger.info(" RSSI: {}", rssi); + logger.info(" Name: {}", name); + // NOTE: these are optionals, so they may not be set + logger.info(" Mfg: {}", mfg_name); + logger.info(" Model: {}", model_number); + logger.info(" PnP: {}", pnp_id); + } + } } else if (!ble_gatt_server.is_connected()) { was_connected = false; } From 8499fec6b92d3f145d84935aa28f66a2380935b3 Mon Sep 17 00:00:00 2001 From: William Emfinger Date: Thu, 6 Feb 2025 10:43:41 -0600 Subject: [PATCH 2/2] fix sa --- components/ble_gatt_server/include/device_info_service.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/ble_gatt_server/include/device_info_service.hpp b/components/ble_gatt_server/include/device_info_service.hpp index c46df334e..3d3dfb87e 100644 --- a/components/ble_gatt_server/include/device_info_service.hpp +++ b/components/ble_gatt_server/include/device_info_service.hpp @@ -178,7 +178,7 @@ class DeviceInfoService : public BaseComponent { /// \param size The size of the data /// \return The PnP ID static PnpId parse_pnp_id(const uint8_t *data, size_t size) { - PnpId pnp_id; + PnpId pnp_id = {}; if (size < 7) { return pnp_id; }