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..3d3dfb87e 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; }