Add support for creating a NimBLEClient from a NimBLEServer peer.

This allows the NimBLEServer instance to create a NimBLEClient instance to read/write form/to a connected peer.
Only one instance is supported subsequent calls will overwrite the previous client connection information and data.
This commit is contained in:
h2zero 2024-12-01 17:08:12 -07:00 committed by h2zero
parent a55489fee2
commit ac3d3575cc
4 changed files with 79 additions and 63 deletions

View file

@ -551,62 +551,6 @@ uint16_t NimBLEClient::getConnHandle() const {
return m_connHandle; return m_connHandle;
} // getConnHandle } // getConnHandle
/**
* @brief Clear the connection information for this client.
* @note This is designed to be used to reset the connection information after
* calling setConnection(), and should not be used to disconnect from a peer.
* To disconnect from a peer, use disconnect().
*/
void NimBLEClient::clearConnection() {
m_connHandle = BLE_HS_CONN_HANDLE_NONE;
m_peerAddress = NimBLEAddress{};
} // clearConnection
/**
* @brief Set the connection information for this client.
* @param [in] connInfo The connection information.
* @return True on success.
* @note Sets the connection established flag to true.
* @note If the client is already connected to a peer, this will return false.
* @note This is designed to be used when a connection is made outside of the
* NimBLEClient class, such as when a connection is made by the
* NimBLEServer class and the client is passed the connection info.
* This enables the GATT Server to read the attributes of the client connected to it.
*/
bool NimBLEClient::setConnection(const NimBLEConnInfo& connInfo) {
if (isConnected()) {
NIMBLE_LOGE(LOG_TAG, "Already connected");
return false;
}
m_peerAddress = connInfo.getAddress();
m_connHandle = connInfo.getConnHandle();
return true;
} // setConnection
/**
* @brief Set the connection information for this client.
* @param [in] connHandle The connection handle.
* @note Sets the connection established flag to true.
* @note This is designed to be used when a connection is made outside of the
* NimBLEClient class, such as when a connection is made by the
* NimBLEServer class and the client is passed the connection handle.
* This enables the GATT Server to read the attributes of the client connected to it.
* @note If the client is already connected to a peer, this will return false.
* @note This will look up the peer address using the connection handle.
*/
bool NimBLEClient::setConnection(uint16_t connHandle) {
// we weren't provided the peer address, look it up using ble_gap_conn_find
NimBLEConnInfo connInfo;
int rc = ble_gap_conn_find(connHandle, &connInfo.m_desc);
if (rc != 0) {
NIMBLE_LOGE(LOG_TAG, "Connection info not found");
return false;
}
return setConnection(connInfo);
} // setConnection
/** /**
* @brief Retrieve the address of the peer. * @brief Retrieve the address of the peer.
* @return A NimBLEAddress instance with the peer address data. * @return A NimBLEAddress instance with the peer address data.

View file

@ -60,9 +60,6 @@ class NimBLEClient {
void setClientCallbacks(NimBLEClientCallbacks* pClientCallbacks, bool deleteCallbacks = true); void setClientCallbacks(NimBLEClientCallbacks* pClientCallbacks, bool deleteCallbacks = true);
std::string toString() const; std::string toString() const;
uint16_t getConnHandle() const; uint16_t getConnHandle() const;
void clearConnection();
bool setConnection(const NimBLEConnInfo& connInfo);
bool setConnection(uint16_t connHandle);
uint16_t getMTU() const; uint16_t getMTU() const;
bool exchangeMTU(); bool exchangeMTU();
bool secureConnection(bool async = false) const; bool secureConnection(bool async = false) const;
@ -140,6 +137,7 @@ class NimBLEClient {
ble_gap_conn_params m_connParams; ble_gap_conn_params m_connParams;
friend class NimBLEDevice; friend class NimBLEDevice;
friend class NimBLEServer;
}; // class NimBLEClient }; // class NimBLEClient
/** /**
@ -155,7 +153,7 @@ class NimBLEClientCallbacks {
*/ */
virtual void onConnect(NimBLEClient* pClient); virtual void onConnect(NimBLEClient* pClient);
/** /**
* @brief Called when a connection attempt fails. * @brief Called when a connection attempt fails.
* @param [in] pClient A pointer to the connecting client object. * @param [in] pClient A pointer to the connecting client object.
* @param [in] reason Contains the reason code for the connection failure. * @param [in] reason Contains the reason code for the connection failure.

View file

@ -19,6 +19,10 @@
# include "NimBLEDevice.h" # include "NimBLEDevice.h"
# include "NimBLELog.h" # include "NimBLELog.h"
# if defined(CONFIG_BT_NIMBLE_ROLE_CENTRAL)
# include "NimBLEClient.h"
# endif
# if defined(CONFIG_NIMBLE_CPP_IDF) # if defined(CONFIG_NIMBLE_CPP_IDF)
# include "services/gap/ble_svc_gap.h" # include "services/gap/ble_svc_gap.h"
# include "services/gatt/ble_svc_gatt.h" # include "services/gatt/ble_svc_gatt.h"
@ -63,6 +67,10 @@ NimBLEServer::~NimBLEServer() {
if (m_deleteCallbacks) { if (m_deleteCallbacks) {
delete m_pServerCallbacks; delete m_pServerCallbacks;
} }
if (m_pClient != nullptr) {
delete m_pClient;
}
} }
/** /**
@ -169,7 +177,7 @@ void NimBLEServer::serviceChanged() {
*/ */
void NimBLEServer::start() { void NimBLEServer::start() {
if (m_gattsStarted) { if (m_gattsStarted) {
return; //already started return; // already started
} }
int rc = ble_gatts_start(); int rc = ble_gatts_start();
@ -497,6 +505,11 @@ int NimBLEServer::handleGapEvent(ble_gap_event* event, void* arg) {
} }
} }
if (pServer->m_pClient->m_connHandle == event->disconnect.conn.conn_handle) {
// If this was also the client make sure it's flagged as disconnected.
pServer->m_pClient->m_connHandle = BLE_HS_CONN_HANDLE_NONE;
}
if (pServer->m_svcChanged) { if (pServer->m_svcChanged) {
pServer->resetGATT(); pServer->resetGATT();
} }
@ -705,8 +718,8 @@ int NimBLEServer::handleGattEvent(uint16_t connHandle, uint16_t attrHandle, ble_
NIMBLE_LOGD(LOG_TAG, NIMBLE_LOGD(LOG_TAG,
"Gatt %s event", "Gatt %s event",
(ctxt->op == BLE_GATT_ACCESS_OP_READ_CHR || ctxt->op == BLE_GATT_ACCESS_OP_READ_DSC) ? "Read" : "Write"); (ctxt->op == BLE_GATT_ACCESS_OP_READ_CHR || ctxt->op == BLE_GATT_ACCESS_OP_READ_DSC) ? "Read" : "Write");
auto pAtt = static_cast<NimBLELocalValueAttribute*>(arg); auto pAtt = static_cast<NimBLELocalValueAttribute*>(arg);
auto val = pAtt->getAttVal(); auto val = pAtt->getAttVal();
NimBLEConnInfo peerInfo{}; NimBLEConnInfo peerInfo{};
ble_gap_conn_find(connHandle, &peerInfo.m_desc); ble_gap_conn_find(connHandle, &peerInfo.m_desc);
@ -1029,6 +1042,54 @@ void NimBLEServer::setDataLen(uint16_t connHandle, uint16_t octets) const {
# endif # endif
} // setDataLen } // setDataLen
# if defined(CONFIG_BT_NIMBLE_ROLE_CENTRAL)
/**
* @brief Create a client instance from the connection handle.
* @param [in] connHandle The connection handle to create a client instance from.
* @return A pointer to the NimBLEClient instance or nullptr if there was an error.
* @note Only one instance is supported subsequent calls will overwrite the previous
* client connection information and data.
*/
NimBLEClient* NimBLEServer::getClient(uint16_t connHandle) {
NimBLEConnInfo connInfo;
int rc = ble_gap_conn_find(connHandle, &connInfo.m_desc);
if (rc != 0) {
NIMBLE_LOGE(LOG_TAG, "Client info not found");
return nullptr;
}
return getClient(connInfo);
} // getClient
/**
* @brief Create a client instance from the NimBLEConnInfo reference.
* @param [in] connInfo The connection info to create a client instance from.
* @return A pointer to the NimBLEClient instance or nullptr if there was an error.
* @note Only one instance is supported subsequent calls will overwrite the previous
* client connection information and data.
*/
NimBLEClient* NimBLEServer::getClient(const NimBLEConnInfo& connInfo) {
if (m_pClient == nullptr) {
m_pClient = new NimBLEClient(connInfo.getAddress());
}
m_pClient->deleteServices(); // Changed peer connection delete the database.
m_pClient->m_peerAddress = connInfo.getAddress();
m_pClient->m_connHandle = connInfo.getConnHandle();
return m_pClient;
} // getClient
/**
* @brief Delete the NimBLEClient instance that was created with `getClient()`
*/
void NimBLEServer::deleteClient() {
if (m_pClient != nullptr) {
delete m_pClient;
m_pClient = nullptr;
}
} // deleteClient
# endif
/** Default callback handlers */ /** Default callback handlers */
void NimBLEServerCallbacks::onConnect(NimBLEServer* pServer, NimBLEConnInfo& connInfo) { void NimBLEServerCallbacks::onConnect(NimBLEServer* pServer, NimBLEConnInfo& connInfo) {
NIMBLE_LOGD("NimBLEServerCallbacks", "onConnect(): Default"); NIMBLE_LOGD("NimBLEServerCallbacks", "onConnect(): Default");

View file

@ -48,6 +48,9 @@ class NimBLEExtAdvertising;
# else # else
class NimBLEAdvertising; class NimBLEAdvertising;
# endif # endif
# if defined(CONFIG_BT_NIMBLE_ROLE_CENTRAL)
class NimBLEClient;
# endif
/** /**
* @brief The model of a BLE server. * @brief The model of a BLE server.
@ -77,6 +80,12 @@ class NimBLEServer {
void advertiseOnDisconnect(bool enable); void advertiseOnDisconnect(bool enable);
void setDataLen(uint16_t connHandle, uint16_t tx_octets) const; void setDataLen(uint16_t connHandle, uint16_t tx_octets) const;
# if defined(CONFIG_BT_NIMBLE_ROLE_CENTRAL)
NimBLEClient* getClient(uint16_t connHandle);
NimBLEClient* getClient(const NimBLEConnInfo& connInfo);
void deleteClient();
# endif
# if CONFIG_BT_NIMBLE_EXT_ADV # if CONFIG_BT_NIMBLE_EXT_ADV
NimBLEExtAdvertising* getAdvertising() const; NimBLEExtAdvertising* getAdvertising() const;
bool startAdvertising(uint8_t instanceId, int duration = 0, int maxEvents = 0) const; bool startAdvertising(uint8_t instanceId, int duration = 0, int maxEvents = 0) const;
@ -115,6 +124,10 @@ class NimBLEServer {
std::vector<NimBLEService*> m_svcVec; std::vector<NimBLEService*> m_svcVec;
std::array<uint16_t, CONFIG_BT_NIMBLE_MAX_CONNECTIONS> m_connectedPeers; std::array<uint16_t, CONFIG_BT_NIMBLE_MAX_CONNECTIONS> m_connectedPeers;
# if defined(CONFIG_BT_NIMBLE_ROLE_CENTRAL)
NimBLEClient* m_pClient{nullptr};
# endif
static int handleGapEvent(struct ble_gap_event* event, void* arg); static int handleGapEvent(struct ble_gap_event* event, void* arg);
static int handleGattEvent(uint16_t connHandle, uint16_t attrHandle, ble_gatt_access_ctxt* ctxt, void* arg); static int handleGattEvent(uint16_t connHandle, uint16_t attrHandle, ble_gatt_access_ctxt* ctxt, void* arg);
static int peerNameCB(uint16_t connHandle, const ble_gatt_error* error, ble_gatt_attr* attr, void* arg); static int peerNameCB(uint16_t connHandle, const ble_gatt_error* error, ble_gatt_attr* attr, void* arg);