[BREAKING] Refactor NimBLEServer

* General code cleanup
* `NimBLEServerCallbacks::onMTUChanged` renamed to `NimBLEServerCallbacks::onMTUChange` to be consistent with the client callback.
* `NimBLEServer::getPeerIDInfo` renamed to `NimBLEServer::getPeerInfoByHandle` to better describe it's use.
* Use a std::array to store client connection handles instead of std::vector to reduce memory allocation.
* `NimBLEServer::disconnect` now returns `bool`, true = success,  instead of `int` to be consistent with the rest of the library.
This commit is contained in:
h2zero 2024-11-24 19:59:56 -07:00 committed by h2zero
parent c4c9f7913a
commit d9178cfa9b
5 changed files with 448 additions and 510 deletions

View file

@ -199,12 +199,7 @@ bool NimBLEAdvertising::start(uint32_t duration, const NimBLEAddress* dirAddr) {
# if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL) # if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
NimBLEServer* pServer = NimBLEDevice::getServer(); NimBLEServer* pServer = NimBLEDevice::getServer();
if (pServer != nullptr) { if (pServer != nullptr) {
if (!pServer->m_gattsStarted) { pServer->start(); // make sure the GATT server is ready before advertising
pServer->start();
} else if (pServer->getConnectedCount() >= NIMBLE_MAX_CONNECTIONS) {
NIMBLE_LOGE(LOG_TAG, "Unable to advertise; Max connections reached");
return false;
}
} }
# endif # endif

View file

@ -62,9 +62,7 @@ bool NimBLEExtAdvertising::setInstanceData(uint8_t instId, NimBLEExtAdvertisemen
# if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL) # if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
NimBLEServer* pServer = NimBLEDevice::getServer(); NimBLEServer* pServer = NimBLEDevice::getServer();
if (pServer != nullptr) { if (pServer != nullptr) {
if (!pServer->m_gattsStarted) { pServer->start(); // make sure the GATT server is ready before advertising
pServer->start();
}
} }
int rc = ble_gap_ext_adv_configure( int rc = ble_gap_ext_adv_configure(

View file

@ -27,134 +27,119 @@
# include "nimble/nimble/host/services/gatt/include/services/gatt/ble_svc_gatt.h" # include "nimble/nimble/host/services/gatt/include/services/gatt/ble_svc_gatt.h"
# endif # endif
#include <limits.h>
#include <algorithm>
# define NIMBLE_SERVER_GET_PEER_NAME_ON_CONNECT_CB 0 # define NIMBLE_SERVER_GET_PEER_NAME_ON_CONNECT_CB 0
# define NIMBLE_SERVER_GET_PEER_NAME_ON_AUTH_CB 1 # define NIMBLE_SERVER_GET_PEER_NAME_ON_AUTH_CB 1
static const char* LOG_TAG = "NimBLEServer"; static const char* LOG_TAG = "NimBLEServer";
static NimBLEServerCallbacks defaultCallbacks; static NimBLEServerCallbacks defaultCallbacks;
/** /**
* @brief Construct a %BLE Server * @brief Construct a BLE Server
* *
* This class is not designed to be individually instantiated. Instead one should create a server by asking * This class is not designed to be individually instantiated.
* the NimBLEDevice class. * Instead it should be created the NimBLEDevice API.
*/ */
NimBLEServer::NimBLEServer() { NimBLEServer::NimBLEServer()
memset(m_indWait, BLE_HS_CONN_HANDLE_NONE, sizeof(m_indWait)); : m_gattsStarted{false},
// m_svcChgChrHdl = 0xffff; // Future Use m_getPeerNameOnConnect{false},
m_pServerCallbacks = &defaultCallbacks; m_svcChanged{false},
m_gattsStarted = false; m_deleteCallbacks{false},
# if !CONFIG_BT_NIMBLE_EXT_ADV # if !CONFIG_BT_NIMBLE_EXT_ADV
m_advertiseOnDisconnect = true; m_advertiseOnDisconnect{false},
# endif # endif
m_svcChanged = false; m_pServerCallbacks{&defaultCallbacks},
m_deleteCallbacks = true; m_svcVec{},
m_getPeerNameOnConnect = false; m_notifyChrVec{} {
m_connectedPeers.fill(BLE_HS_CONN_HANDLE_NONE);
m_indWait.fill(BLE_HS_CONN_HANDLE_NONE);
} // NimBLEServer } // NimBLEServer
/** /**
* @brief Destructor: frees all resources / attributes created. * @brief Destructor: frees all resources / attributes created.
*/ */
NimBLEServer::~NimBLEServer() { NimBLEServer::~NimBLEServer() {
for(const auto &it : m_svcVec) { for (const auto& svc : m_svcVec) {
delete it; delete svc;
} }
if(m_deleteCallbacks && m_pServerCallbacks != &defaultCallbacks) { if (m_deleteCallbacks) {
delete m_pServerCallbacks; delete m_pServerCallbacks;
} }
} }
/** /**
* @brief Create a %BLE Service. * @brief Create a BLE Service.
* @param [in] uuid The UUID of the new service. * @param [in] uuid The UUID of the new service.
* @return A reference to the new service object. * @return A pointer to the new service object.
*/ */
NimBLEService* NimBLEServer::createService(const char* uuid) { NimBLEService* NimBLEServer::createService(const char* uuid) {
return createService(NimBLEUUID(uuid)); return createService(NimBLEUUID(uuid));
} // createService } // createService
/** /**
* @brief Create a %BLE Service. * @brief Create a BLE Service.
* @param [in] uuid The UUID of the new service. * @param [in] uuid The UUID of the new service.
* @return A reference to the new service object. * @return A pointer to the new service object.
*/ */
NimBLEService* NimBLEServer::createService(const NimBLEUUID& uuid) { NimBLEService* NimBLEServer::createService(const NimBLEUUID& uuid) {
NIMBLE_LOGD(LOG_TAG, ">> createService - %s", uuid.toString().c_str());
// Check that a service with the supplied UUID does not already exist.
if(getServiceByUUID(uuid) != nullptr) {
NIMBLE_LOGW(LOG_TAG, "Warning creating a duplicate service UUID: %s",
std::string(uuid).c_str());
}
NimBLEService* pService = new NimBLEService(uuid); NimBLEService* pService = new NimBLEService(uuid);
m_svcVec.push_back(pService); m_svcVec.push_back(pService);
serviceChanged(); serviceChanged();
NIMBLE_LOGD(LOG_TAG, "<< createService");
return pService; return pService;
} // createService } // createService
/** /**
* @brief Get a %BLE Service by its UUID * @brief Get a BLE Service by its UUID
* @param [in] uuid The UUID of the service. * @param [in] uuid The UUID of the service.
* @param instanceId The index of the service to return (used when multiple services have the same UUID). * @param instanceId The index of the service to return (used when multiple services have the same UUID).
* @return A pointer to the service object or nullptr if not found. * @return A pointer to the service object or nullptr if not found.
*/ */
NimBLEService* NimBLEServer::getServiceByUUID(const char* uuid, uint16_t instanceId) { NimBLEService* NimBLEServer::getServiceByUUID(const char* uuid, uint16_t instanceId) const {
return getServiceByUUID(NimBLEUUID(uuid), instanceId); return getServiceByUUID(NimBLEUUID(uuid), instanceId);
} // getServiceByUUID } // getServiceByUUID
/** /**
* @brief Get a %BLE Service by its UUID * @brief Get a BLE Service by its UUID
* @param [in] uuid The UUID of the service. * @param [in] uuid The UUID of the service.
* @param instanceId The index of the service to return (used when multiple services have the same UUID). * @param instanceId The index of the service to return (used when multiple services have the same UUID).
* @return A pointer to the service object or nullptr if not found. * @return A pointer to the service object or nullptr if not found.
*/ */
NimBLEService* NimBLEServer::getServiceByUUID(const NimBLEUUID &uuid, uint16_t instanceId) { NimBLEService* NimBLEServer::getServiceByUUID(const NimBLEUUID& uuid, uint16_t instanceId) const {
uint16_t position = 0; uint16_t position = 0;
for (auto &it : m_svcVec) { for (const auto& svc : m_svcVec) {
if (it->getUUID() == uuid) { if (svc->getUUID() == uuid) {
if (position == instanceId) { if (position == instanceId) {
return it; return svc;
} }
position++; position++;
} }
} }
return nullptr; return nullptr;
} // getServiceByUUID } // getServiceByUUID
/** /**
* @brief Get a %BLE Service by its handle * @brief Get a BLE Service by its handle
* @param handle The handle of the service. * @param handle The handle of the service.
* @return A pointer to the service object or nullptr if not found. * @return A pointer to the service object or nullptr if not found.
*/ */
NimBLEService *NimBLEServer::getServiceByHandle(uint16_t handle) { NimBLEService* NimBLEServer::getServiceByHandle(uint16_t handle) const {
for (auto &it : m_svcVec) { for (const auto& svc : m_svcVec) {
if (it->getHandle() == handle) { if (svc->getHandle() == handle) {
return it; return svc;
} }
} }
return nullptr;
}
return nullptr;
}
# if CONFIG_BT_NIMBLE_EXT_ADV # if CONFIG_BT_NIMBLE_EXT_ADV
/** /**
* @brief Retrieve the advertising object that can be used to advertise the existence of the server. * @brief Retrieve the advertising object that can be used to advertise the existence of the server.
* @return An advertising object. * @return A pinter to an advertising object.
*/ */
NimBLEExtAdvertising* NimBLEServer::getAdvertising() { NimBLEExtAdvertising* NimBLEServer::getAdvertising() const {
return NimBLEDevice::getAdvertising(); return NimBLEDevice::getAdvertising();
} // getAdvertising } // getAdvertising
# endif # endif
@ -162,9 +147,9 @@ NimBLEExtAdvertising* NimBLEServer::getAdvertising() {
# if !CONFIG_BT_NIMBLE_EXT_ADV || defined(_DOXYGEN_) # if !CONFIG_BT_NIMBLE_EXT_ADV || defined(_DOXYGEN_)
/** /**
* @brief Retrieve the advertising object that can be used to advertise the existence of the server. * @brief Retrieve the advertising object that can be used to advertise the existence of the server.
* @return An advertising object. * @return A pointer to an advertising object.
*/ */
NimBLEAdvertising* NimBLEServer::getAdvertising() { NimBLEAdvertising* NimBLEServer::getAdvertising() const {
return NimBLEDevice::getAdvertising(); return NimBLEDevice::getAdvertising();
} // getAdvertising } // getAdvertising
# endif # endif
@ -178,17 +163,16 @@ void NimBLEServer::serviceChanged() {
ble_svc_gatt_changed(0x0001, 0xffff); ble_svc_gatt_changed(0x0001, 0xffff);
resetGATT(); resetGATT();
} }
} } // serviceChanged
/** /**
* @brief Start the GATT server. Required to be called after setup of all * @brief Start the GATT server.
* services and characteristics / descriptors for the NimBLE host to register them. * @details Required to be called after setup of all services and characteristics / descriptors
* for the NimBLE host to register them.
*/ */
void NimBLEServer::start() { void NimBLEServer::start() {
if (m_gattsStarted) { if (m_gattsStarted) {
NIMBLE_LOGW(LOG_TAG, "Gatt server already started"); return; //already started
return;
} }
int rc = ble_gatts_start(); int rc = ble_gatts_start();
@ -200,29 +184,17 @@ void NimBLEServer::start() {
# if CONFIG_NIMBLE_CPP_LOG_LEVEL >= 4 # if CONFIG_NIMBLE_CPP_LOG_LEVEL >= 4
ble_gatts_show_local(); ble_gatts_show_local();
# endif # endif
/*** Future use ***
* TODO: implement service changed handling
ble_uuid16_t svc = {BLE_UUID_TYPE_16, 0x1801};
ble_uuid16_t chr = {BLE_UUID_TYPE_16, 0x2a05};
rc = ble_gatts_find_chr(&svc.u, &chr.u, NULL, &m_svcChgChrHdl);
if(rc != 0) {
NIMBLE_LOGE(LOG_TAG, "ble_gatts_find_chr: rc=%d, %s", rc,
NimBLEUtils::returnCodeToString(rc));
abort();
}
NIMBLE_LOGI(LOG_TAG, "Service changed characterisic handle: %d", m_svcChgChrHdl);
*/
// Get the assigned service handles and build a vector of characteristics // Get the assigned service handles and build a vector of characteristics
// with Notify / Indicate capabilities for event handling // with Notify / Indicate capabilities for event handling
for(auto &svc : m_svcVec) { for (const auto& svc : m_svcVec) {
if (svc->getRemoved() == 0) { if (svc->getRemoved() == 0) {
rc = ble_gatts_find_svc(svc->getUUID().getBase(), &svc->m_handle); rc = ble_gatts_find_svc(svc->getUUID().getBase(), &svc->m_handle);
if (rc != 0) { if (rc != 0) {
NIMBLE_LOGW(LOG_TAG, "GATT Server started without service: %s, Service %s", NIMBLE_LOGW(LOG_TAG,
svc->getUUID().toString().c_str(), svc->isStarted() ? "missing" : "not started"); "GATT Server started without service: %s, Service %s",
svc->getUUID().toString().c_str(),
svc->isStarted() ? "missing" : "not started");
continue; // Skip this service as it was not started continue; // Skip this service as it was not started
} }
} }
@ -230,16 +202,12 @@ void NimBLEServer::start() {
for (auto& chr : svc->m_vChars) { for (auto& chr : svc->m_vChars) {
// if Notify / Indicate is enabled but we didn't create the descriptor // if Notify / Indicate is enabled but we didn't create the descriptor
// we do it now. // we do it now.
if((chr->m_properties & BLE_GATT_CHR_F_INDICATE) || if ((chr->m_properties & BLE_GATT_CHR_F_INDICATE) || (chr->m_properties & BLE_GATT_CHR_F_NOTIFY)) {
(chr->m_properties & BLE_GATT_CHR_F_NOTIFY)) {
m_notifyChrVec.push_back(chr); m_notifyChrVec.push_back(chr);
} }
for (auto& desc : chr->m_vDescriptors) { for (auto& desc : chr->m_vDescriptors) {
ble_gatts_find_dsc(svc->getUUID().getBase(), ble_gatts_find_dsc(svc->getUUID().getBase(), chr->getUUID().getBase(), desc->getUUID().getBase(), &desc->m_handle);
chr->getUUID().getBase(),
desc->getUUID().getBase(),
&desc->m_handle);
} }
} }
} }
@ -247,24 +215,20 @@ void NimBLEServer::start() {
m_gattsStarted = true; m_gattsStarted = true;
} // start } // start
/** /**
* @brief Disconnect the specified client with optional reason. * @brief Disconnect the specified client with optional reason.
* @param [in] connId Connection Id of the client to disconnect. * @param [in] connHandle Connection handle of the client to disconnect.
* @param [in] reason code for disconnecting. * @param [in] reason code for disconnecting.
* @return NimBLE host return code. * @return True if successful.
*/ */
int NimBLEServer::disconnect(uint16_t connId, uint8_t reason) { bool NimBLEServer::disconnect(uint16_t connHandle, uint8_t reason) const {
NIMBLE_LOGD(LOG_TAG, ">> disconnect()"); int rc = ble_gap_terminate(connHandle, reason);
if (rc != 0 && rc != BLE_HS_ENOTCONN && rc != BLE_HS_EALREADY) {
int rc = ble_gap_terminate(connId, reason); NIMBLE_LOGE(LOG_TAG, "ble_gap_terminate failed: rc=%d %s", rc, NimBLEUtils::returnCodeToString(rc));
if(rc != 0){ return false;
NIMBLE_LOGE(LOG_TAG, "ble_gap_terminate failed: rc=%d %s", rc,
NimBLEUtils::returnCodeToString(rc));
} }
NIMBLE_LOGD(LOG_TAG, "<< disconnect()"); return true;
return rc;
} // disconnect } // disconnect
/** /**
@ -273,17 +237,17 @@ int NimBLEServer::disconnect(uint16_t connId, uint8_t reason) {
* @param [in] reason code for disconnecting. * @param [in] reason code for disconnecting.
* @return NimBLE host return code. * @return NimBLE host return code.
*/ */
int NimBLEServer::disconnect(const NimBLEConnInfo &connInfo, uint8_t reason) { bool NimBLEServer::disconnect(const NimBLEConnInfo& connInfo, uint8_t reason) const {
return disconnect(connInfo.getConnHandle(), reason); return disconnect(connInfo.getConnHandle(), reason);
} // disconnect } // disconnect
# if !CONFIG_BT_NIMBLE_EXT_ADV || defined(_DOXYGEN_) # if !CONFIG_BT_NIMBLE_EXT_ADV || defined(_DOXYGEN_)
/** /**
* @brief Set the server to automatically start advertising when a client disconnects. * @brief Set the server to automatically start advertising when a client disconnects.
* @param [in] aod true == advertise, false == don't advertise. * @param [in] enable true == advertise, false == don't advertise.
*/ */
void NimBLEServer::advertiseOnDisconnect(bool aod) { void NimBLEServer::advertiseOnDisconnect(bool enable) {
m_advertiseOnDisconnect = aod; m_advertiseOnDisconnect = enable;
} // advertiseOnDisconnect } // advertiseOnDisconnect
# endif # endif
@ -300,57 +264,78 @@ void NimBLEServer::getPeerNameOnConnect(bool enable) {
* @brief Return the number of connected clients. * @brief Return the number of connected clients.
* @return The number of connected clients. * @return The number of connected clients.
*/ */
size_t NimBLEServer::getConnectedCount() { uint8_t NimBLEServer::getConnectedCount() const {
return m_connectedPeersVec.size(); size_t count = 0;
for (const auto& peer : m_connectedPeers) {
if (peer != BLE_HS_CONN_HANDLE_NONE) {
count++;
}
}
return count;
} // getConnectedCount } // getConnectedCount
/** /**
* @brief Get the vector of the connected client ID's. * @brief Get a vector of the connected client handles.
* @return A vector of the connected client handles.
*/ */
std::vector<uint16_t> NimBLEServer::getPeerDevices() { std::vector<uint16_t> NimBLEServer::getPeerDevices() const {
return m_connectedPeersVec; std::vector<uint16_t> peers{};
} // getPeerDevices for (const auto& peer : m_connectedPeers) {
if (peer != BLE_HS_CONN_HANDLE_NONE) {
peers.push_back(peer);
}
}
return peers;
} // getPeerDevices
/** /**
* @brief Get the connection information of a connected peer by vector index. * @brief Get the connection information of a connected peer by vector index.
* @param [in] index The vector index of the peer. * @param [in] index The vector index of the peer.
* @return A NimBLEConnInfo instance with the peer connection information, or an empty instance if not found.
*/ */
NimBLEConnInfo NimBLEServer::getPeerInfo(size_t index) { NimBLEConnInfo NimBLEServer::getPeerInfo(uint8_t index) const {
if (index >= m_connectedPeersVec.size()) { if (index >= m_connectedPeers.size()) {
NIMBLE_LOGE(LOG_TAG, "No peer at index %u", index); NIMBLE_LOGE(LOG_TAG, "Invalid index %u", index);
return NimBLEConnInfo(); return NimBLEConnInfo{};
} }
return getPeerIDInfo(m_connectedPeersVec[index]); auto count = 0;
} // getPeerInfo for (const auto& peer : m_connectedPeers) {
if (peer != BLE_HS_CONN_HANDLE_NONE) {
if (count == index) {
return getPeerInfoByHandle(m_connectedPeers[count]);
}
count++;
}
}
return NimBLEConnInfo{};
} // getPeerInfo
/** /**
* @brief Get the connection information of a connected peer by address. * @brief Get the connection information of a connected peer by address.
* @param [in] address The address of the peer. * @param [in] address The address of the peer.
* @return A NimBLEConnInfo instance with the peer connection information, or an empty instance if not found.
*/ */
NimBLEConnInfo NimBLEServer::getPeerInfo(const NimBLEAddress& address) { NimBLEConnInfo NimBLEServer::getPeerInfo(const NimBLEAddress& address) const {
NimBLEConnInfo peerInfo; NimBLEConnInfo peerInfo{};
int rc = ble_gap_conn_find_by_addr(address.getBase(), &peerInfo.m_desc); if (ble_gap_conn_find_by_addr(address.getBase(), &peerInfo.m_desc) != 0) {
if (rc != 0) {
NIMBLE_LOGE(LOG_TAG, "Peer info not found"); NIMBLE_LOGE(LOG_TAG, "Peer info not found");
} }
return peerInfo; return peerInfo;
} // getPeerInfo } // getPeerInfo
/** /**
* @brief Get the connection information of a connected peer by connection ID. * @brief Get the connection information of a connected peer by connection handle.
* @param [in] id The connection id of the peer. * @param [in] connHandle The connection handle of the peer.
* @return A NimBLEConnInfo instance with the peer connection information, or an empty instance if not found.
*/ */
NimBLEConnInfo NimBLEServer::getPeerIDInfo(uint16_t id) { NimBLEConnInfo NimBLEServer::getPeerInfoByHandle(uint16_t connHandle) const {
NimBLEConnInfo peerInfo; NimBLEConnInfo peerInfo{};
if (ble_gap_conn_find(connHandle, &peerInfo.m_desc) != 0) {
int rc = ble_gap_conn_find(id, &peerInfo.m_desc);
if (rc != 0) {
NIMBLE_LOGE(LOG_TAG, "Peer info not found"); NIMBLE_LOGE(LOG_TAG, "Peer info not found");
} }
@ -361,13 +346,10 @@ NimBLEConnInfo NimBLEServer::getPeerIDInfo(uint16_t id) {
* @brief Callback that is called after reading from the peer name characteristic. * @brief Callback that is called after reading from the peer name characteristic.
* @details This will check the task pointer in the task data struct to determine * @details This will check the task pointer in the task data struct to determine
* the action to take once the name has been read. If there is a task waiting then * the action to take once the name has been read. If there is a task waiting then
* it will be woken, if not, the the RC value is checked to determine which callback * it will be resumed, if not, the the RC value is checked to determine which callback
* should be called. * should be called.
*/ */
int NimBLEServer::peerNameCB(uint16_t conn_handle, int NimBLEServer::peerNameCB(uint16_t connHandle, const ble_gatt_error* error, ble_gatt_attr* attr, void* arg) {
const struct ble_gatt_error *error,
struct ble_gatt_attr *attr,
void *arg) {
NimBLETaskData* pTaskData = (NimBLETaskData*)arg; NimBLETaskData* pTaskData = (NimBLETaskData*)arg;
std::string* name = (std::string*)pTaskData->m_pBuf; std::string* name = (std::string*)pTaskData->m_pBuf;
int rc = error->status; int rc = error->status;
@ -383,7 +365,7 @@ int NimBLEServer::peerNameCB(uint16_t conn_handle,
if (pTaskData->m_flags != -1) { if (pTaskData->m_flags != -1) {
NimBLEServer* pServer = (NimBLEServer*)pTaskData->m_pInstance; NimBLEServer* pServer = (NimBLEServer*)pTaskData->m_pInstance;
NimBLEConnInfo peerInfo{}; NimBLEConnInfo peerInfo{};
ble_gap_conn_find(conn_handle, &peerInfo.m_desc); ble_gap_conn_find(connHandle, &peerInfo.m_desc);
// check the flag to indicate which callback should be called. // check the flag to indicate which callback should be called.
if (pTaskData->m_flags == NIMBLE_SERVER_GET_PEER_NAME_ON_CONNECT_CB) { if (pTaskData->m_flags == NIMBLE_SERVER_GET_PEER_NAME_ON_CONNECT_CB) {
@ -408,30 +390,25 @@ int NimBLEServer::peerNameCB(uint16_t conn_handle,
} }
/** /**
* @brief Internal method that sends the read command. * @brief Implementation of the function that sends the read command.
*/ */
std::string NimBLEServer::getPeerNameInternal(uint16_t conn_handle, int cb_type) { std::string NimBLEServer::getPeerNameImpl(uint16_t connHandle, int cbType) const {
std::string* buf = new std::string{}; std::string* buf = new std::string{};
NimBLETaskData *pTaskData = new NimBLETaskData(this, cb_type, buf); NimBLETaskData* pTaskData = new NimBLETaskData(const_cast<NimBLEServer*>(this), cbType, buf);
ble_uuid16_t uuid{{BLE_UUID_TYPE_16}, BLE_SVC_GAP_CHR_UUID16_DEVICE_NAME}; ble_uuid16_t uuid{{BLE_UUID_TYPE_16}, BLE_SVC_GAP_CHR_UUID16_DEVICE_NAME};
int rc = ble_gattc_read_by_uuid(conn_handle, int rc = ble_gattc_read_by_uuid(connHandle, 1, 0xffff, &uuid.u, NimBLEServer::peerNameCB, pTaskData);
1,
0xffff,
&uuid.u,
NimBLEServer::peerNameCB,
pTaskData);
if (rc != 0) { if (rc != 0) {
NIMBLE_LOGE(LOG_TAG, "ble_gattc_read_by_uuid rc=%d, %s", rc, NimBLEUtils::returnCodeToString(rc)); NIMBLE_LOGE(LOG_TAG, "ble_gattc_read_by_uuid rc=%d, %s", rc, NimBLEUtils::returnCodeToString(rc));
NimBLEConnInfo peerInfo{}; NimBLEConnInfo peerInfo{};
ble_gap_conn_find(conn_handle, &peerInfo.m_desc); ble_gap_conn_find(connHandle, &peerInfo.m_desc);
if (cb_type == NIMBLE_SERVER_GET_PEER_NAME_ON_CONNECT_CB) { if (cbType == NIMBLE_SERVER_GET_PEER_NAME_ON_CONNECT_CB) {
m_pServerCallbacks->onConnect(this, peerInfo, *buf); m_pServerCallbacks->onConnect(const_cast<NimBLEServer*>(this), peerInfo, *buf);
} else if (cb_type == NIMBLE_SERVER_GET_PEER_NAME_ON_AUTH_CB) { } else if (cbType == NIMBLE_SERVER_GET_PEER_NAME_ON_AUTH_CB) {
m_pServerCallbacks->onAuthenticationComplete(peerInfo, *buf); m_pServerCallbacks->onAuthenticationComplete(peerInfo, *buf);
} }
delete buf; delete buf;
delete pTaskData; delete pTaskData;
} else if (cb_type == -1) { } else if (cbType == -1) {
NimBLEUtils::taskWait(*pTaskData, BLE_NPL_TIME_FOREVER); NimBLEUtils::taskWait(*pTaskData, BLE_NPL_TIME_FOREVER);
rc = pTaskData->m_flags; rc = pTaskData->m_flags;
std::string name{*(std::string*)pTaskData->m_pBuf}; std::string name{*(std::string*)pTaskData->m_pBuf};
@ -454,29 +431,22 @@ std::string NimBLEServer::getPeerNameInternal(uint16_t conn_handle, int cb_type)
* @returns A string containing the name. * @returns A string containing the name.
* @note This is a blocking call and should NOT be called from any callbacks! * @note This is a blocking call and should NOT be called from any callbacks!
*/ */
std::string NimBLEServer::getPeerName(const NimBLEConnInfo& connInfo) { std::string NimBLEServer::getPeerName(const NimBLEConnInfo& connInfo) const {
std::string name = getPeerNameInternal(connInfo.getConnHandle()); std::string name = getPeerNameImpl(connInfo.getConnHandle());
return name; return name;
} }
/** /**
* @brief Handle a GATT Server Event. * @brief Gap event handler.
*
* @param [in] event
* @param [in] gatts_if
* @param [in] param
*
*/ */
/*STATIC*/ int NimBLEServer::handleGapEvent(ble_gap_event* event, void* arg) {
int NimBLEServer::handleGapEvent(struct ble_gap_event *event, void *arg) {
NIMBLE_LOGD(LOG_TAG, ">> handleGapEvent: %s", NimBLEUtils::gapEventToString(event->type)); NIMBLE_LOGD(LOG_TAG, ">> handleGapEvent: %s", NimBLEUtils::gapEventToString(event->type));
int rc = 0; int rc = 0;
NimBLEConnInfo peerInfo; NimBLEConnInfo peerInfo{};
NimBLEServer* pServer = NimBLEDevice::getServer(); NimBLEServer* pServer = NimBLEDevice::getServer();
switch (event->type) { switch (event->type) {
case BLE_GAP_EVENT_CONNECT: { case BLE_GAP_EVENT_CONNECT: {
if (event->connect.status != 0) { if (event->connect.status != 0) {
/* Connection failed; resume advertising */ /* Connection failed; resume advertising */
@ -490,21 +460,23 @@ int NimBLEServer::handleGapEvent(struct ble_gap_event *event, void *arg) {
return 0; return 0;
} }
pServer->m_connectedPeersVec.push_back(event->connect.conn_handle); for (auto& peer : pServer->m_connectedPeers) {
if (peer == BLE_HS_CONN_HANDLE_NONE) {
peer = event->connect.conn_handle;
break;
}
}
if (pServer->m_getPeerNameOnConnect) { if (pServer->m_getPeerNameOnConnect) {
pServer->getPeerNameInternal(event->connect.conn_handle, pServer->getPeerNameImpl(event->connect.conn_handle, NIMBLE_SERVER_GET_PEER_NAME_ON_CONNECT_CB);
NIMBLE_SERVER_GET_PEER_NAME_ON_CONNECT_CB);
} else { } else {
pServer->m_pServerCallbacks->onConnect(pServer, peerInfo); pServer->m_pServerCallbacks->onConnect(pServer, peerInfo);
} }
} }
return 0; break;
} // BLE_GAP_EVENT_CONNECT } // BLE_GAP_EVENT_CONNECT
case BLE_GAP_EVENT_DISCONNECT: { case BLE_GAP_EVENT_DISCONNECT: {
// If Host reset tell the device now before returning to prevent // If Host reset tell the device now before returning to prevent
// any errors caused by calling host functions before resync. // any errors caused by calling host functions before resync.
@ -520,73 +492,70 @@ int NimBLEServer::handleGapEvent(struct ble_gap_event *event, void *arg) {
break; break;
} }
pServer->m_connectedPeersVec.erase(std::remove(pServer->m_connectedPeersVec.begin(), for (auto& peer : pServer->m_connectedPeers) {
pServer->m_connectedPeersVec.end(), if (peer == event->disconnect.conn.conn_handle) {
event->disconnect.conn.conn_handle), peer = BLE_HS_CONN_HANDLE_NONE;
pServer->m_connectedPeersVec.end()); break;
}
}
if (pServer->m_svcChanged) { if (pServer->m_svcChanged) {
pServer->resetGATT(); pServer->resetGATT();
} }
NimBLEConnInfo peerInfo(event->disconnect.conn); peerInfo.m_desc = event->disconnect.conn;
pServer->m_pServerCallbacks->onDisconnect(pServer, peerInfo, event->disconnect.reason); pServer->m_pServerCallbacks->onDisconnect(pServer, peerInfo, event->disconnect.reason);
# if !CONFIG_BT_NIMBLE_EXT_ADV # if !CONFIG_BT_NIMBLE_EXT_ADV
if (pServer->m_advertiseOnDisconnect) { if (pServer->m_advertiseOnDisconnect) {
pServer->startAdvertising(); pServer->startAdvertising();
} }
# endif # endif
return 0; break;
} // BLE_GAP_EVENT_DISCONNECT } // BLE_GAP_EVENT_DISCONNECT
case BLE_GAP_EVENT_SUBSCRIBE: { case BLE_GAP_EVENT_SUBSCRIBE: {
NIMBLE_LOGI(LOG_TAG, "subscribe event; attr_handle=%d, subscribed: %s", NIMBLE_LOGI(LOG_TAG,
"subscribe event; attr_handle=%d, subscribed: %s",
event->subscribe.attr_handle, event->subscribe.attr_handle,
(event->subscribe.cur_notify ? "true" : "false")); (event->subscribe.cur_notify ? "true" : "false"));
for(auto &it : pServer->m_notifyChrVec) { for (const auto& chr : pServer->m_notifyChrVec) {
if(it->getHandle() == event->subscribe.attr_handle) { if (chr->getHandle() == event->subscribe.attr_handle) {
rc = ble_gap_conn_find(event->subscribe.conn_handle, &peerInfo.m_desc); rc = ble_gap_conn_find(event->subscribe.conn_handle, &peerInfo.m_desc);
if (rc != 0) { if (rc != 0) {
break; break;
} }
if(((it->getProperties() & BLE_GATT_CHR_F_READ_AUTHEN) || auto chrProps = chr->getProperties();
(it->getProperties() & BLE_GATT_CHR_F_READ_AUTHOR) || if (!peerInfo.isEncrypted() &&
(it->getProperties() & BLE_GATT_CHR_F_READ_ENC)) && (chrProps & BLE_GATT_CHR_F_READ_AUTHEN || chrProps & BLE_GATT_CHR_F_READ_AUTHOR ||
!peerInfo.isEncrypted()) { chrProps & BLE_GATT_CHR_F_READ_ENC)) {
NimBLEDevice::startSecurity(event->subscribe.conn_handle); NimBLEDevice::startSecurity(event->subscribe.conn_handle);
} }
it->setSubscribe(event, peerInfo); chr->setSubscribe(event, peerInfo);
break; break;
} }
} }
return 0; break;
} // BLE_GAP_EVENT_SUBSCRIBE } // BLE_GAP_EVENT_SUBSCRIBE
case BLE_GAP_EVENT_MTU: { case BLE_GAP_EVENT_MTU: {
NIMBLE_LOGI(LOG_TAG, "mtu update event; conn_handle=%d mtu=%d", NIMBLE_LOGI(LOG_TAG, "mtu update event; conn_handle=%d mtu=%d", event->mtu.conn_handle, event->mtu.value);
event->mtu.conn_handle, if (ble_gap_conn_find(event->mtu.conn_handle, &peerInfo.m_desc) == 0) {
event->mtu.value); pServer->m_pServerCallbacks->onMTUChange(event->mtu.value, peerInfo);
rc = ble_gap_conn_find(event->mtu.conn_handle, &peerInfo.m_desc);
if (rc != 0) {
return 0;
} }
pServer->m_pServerCallbacks->onMTUChange(event->mtu.value, peerInfo); break;
return 0;
} // BLE_GAP_EVENT_MTU } // BLE_GAP_EVENT_MTU
case BLE_GAP_EVENT_NOTIFY_TX: { case BLE_GAP_EVENT_NOTIFY_TX: {
NimBLECharacteristic* pChar = nullptr; NimBLECharacteristic* pChar = nullptr;
for(auto &it : pServer->m_notifyChrVec) { for (auto& chr : pServer->m_notifyChrVec) {
if(it->getHandle() == event->notify_tx.attr_handle) { if (chr->getHandle() == event->notify_tx.attr_handle) {
pChar = it; pChar = chr;
} }
} }
@ -602,28 +571,24 @@ int NimBLEServer::handleGapEvent(struct ble_gap_event *event, void *arg) {
} }
pChar->m_pCallbacks->onStatus(pChar, event->notify_tx.status); pChar->m_pCallbacks->onStatus(pChar, event->notify_tx.status);
break;
return 0;
} // BLE_GAP_EVENT_NOTIFY_TX } // BLE_GAP_EVENT_NOTIFY_TX
case BLE_GAP_EVENT_ADV_COMPLETE: {
case BLE_GAP_EVENT_ADV_COMPLETE:
# if CONFIG_BT_NIMBLE_EXT_ADV # if CONFIG_BT_NIMBLE_EXT_ADV
case BLE_GAP_EVENT_SCAN_REQ_RCVD: case BLE_GAP_EVENT_SCAN_REQ_RCVD:
return NimBLEExtAdvertising::handleGapEvent(event, arg); return NimBLEExtAdvertising::handleGapEvent(event, arg);
# else # else
return NimBLEAdvertising::handleGapEvent(event, arg); return NimBLEAdvertising::handleGapEvent(event, arg);
# endif # endif
// BLE_GAP_EVENT_ADV_COMPLETE | BLE_GAP_EVENT_SCAN_REQ_RCVD } // BLE_GAP_EVENT_ADV_COMPLETE | BLE_GAP_EVENT_SCAN_REQ_RCVD
case BLE_GAP_EVENT_CONN_UPDATE: { case BLE_GAP_EVENT_CONN_UPDATE: {
rc = ble_gap_conn_find(event->connect.conn_handle, &peerInfo.m_desc); if (ble_gap_conn_find(event->connect.conn_handle, &peerInfo.m_desc) == 0) {
if (rc != 0) { pServer->m_pServerCallbacks->onConnParamsUpdate(peerInfo);
return 0;
} }
pServer->m_pServerCallbacks->onConnParamsUpdate(peerInfo); break;
return 0;
} // BLE_GAP_EVENT_CONN_UPDATE } // BLE_GAP_EVENT_CONN_UPDATE
case BLE_GAP_EVENT_REPEAT_PAIRING: { case BLE_GAP_EVENT_REPEAT_PAIRING: {
@ -653,12 +618,12 @@ int NimBLEServer::handleGapEvent(struct ble_gap_event *event, void *arg) {
} }
if (pServer->m_getPeerNameOnConnect) { if (pServer->m_getPeerNameOnConnect) {
pServer->getPeerNameInternal(event->enc_change.conn_handle, pServer->getPeerNameImpl(event->enc_change.conn_handle, NIMBLE_SERVER_GET_PEER_NAME_ON_AUTH_CB);
NIMBLE_SERVER_GET_PEER_NAME_ON_AUTH_CB);
} else { } else {
pServer->m_pServerCallbacks->onAuthenticationComplete(peerInfo); pServer->m_pServerCallbacks->onAuthenticationComplete(peerInfo);
} }
return 0;
break;
} // BLE_GAP_EVENT_ENC_CHANGE } // BLE_GAP_EVENT_ENC_CHANGE
case BLE_GAP_EVENT_IDENTITY_RESOLVED: { case BLE_GAP_EVENT_IDENTITY_RESOLVED: {
@ -668,7 +633,7 @@ int NimBLEServer::handleGapEvent(struct ble_gap_event *event, void *arg) {
} }
pServer->m_pServerCallbacks->onIdentity(peerInfo); pServer->m_pServerCallbacks->onIdentity(peerInfo);
return 0; break;
} // BLE_GAP_EVENT_IDENTITY_RESOLVED } // BLE_GAP_EVENT_IDENTITY_RESOLVED
# if CONFIG_BT_NIMBLE_EXT_ADV # if CONFIG_BT_NIMBLE_EXT_ADV
@ -678,9 +643,7 @@ int NimBLEServer::handleGapEvent(struct ble_gap_event *event, void *arg) {
return BLE_ATT_ERR_INVALID_HANDLE; return BLE_ATT_ERR_INVALID_HANDLE;
} }
pServer->m_pServerCallbacks->onPhyUpdate(peerInfo, pServer->m_pServerCallbacks->onPhyUpdate(peerInfo, event->phy_updated.tx_phy, event->phy_updated.rx_phy);
event->phy_updated.tx_phy,
event->phy_updated.rx_phy);
return 0; return 0;
} // BLE_GAP_EVENT_PHY_UPDATE_COMPLETE } // BLE_GAP_EVENT_PHY_UPDATE_COMPLETE
# endif # endif
@ -709,22 +672,20 @@ int NimBLEServer::handleGapEvent(struct ble_gap_event *event, void *arg) {
} }
pServer->m_pServerCallbacks->onConfirmPassKey(peerInfo, event->passkey.params.numcmp); pServer->m_pServerCallbacks->onConfirmPassKey(peerInfo, event->passkey.params.numcmp);
//TODO: Handle out of band pairing
} else if (event->passkey.params.action == BLE_SM_IOACT_OOB) { } else if (event->passkey.params.action == BLE_SM_IOACT_OOB) {
static uint8_t tem_oob[16] = {0}; // TODO: Handle out of band pairing
pkey.action = event->passkey.params.action; // static uint8_t tem_oob[16] = {0};
for (int i = 0; i < 16; i++) { // pkey.action = event->passkey.params.action;
pkey.oob[i] = tem_oob[i]; // for (int i = 0; i < 16; i++) {
} // pkey.oob[i] = tem_oob[i];
rc = ble_sm_inject_io(event->passkey.conn_handle, &pkey); // }
NIMBLE_LOGD(LOG_TAG, "BLE_SM_IOACT_OOB; ble_sm_inject_io result: %d", rc); // rc = ble_sm_inject_io(event->passkey.conn_handle, &pkey);
////////////////////////////////// // NIMBLE_LOGD(LOG_TAG, "BLE_SM_IOACT_OOB; ble_sm_inject_io result: %d", rc);
} else if (event->passkey.params.action == BLE_SM_IOACT_NONE) { } else if (event->passkey.params.action == BLE_SM_IOACT_NONE) {
NIMBLE_LOGD(LOG_TAG, "No passkey action required"); NIMBLE_LOGD(LOG_TAG, "No passkey action required");
} }
NIMBLE_LOGD(LOG_TAG, "<< handleGATTServerEvent"); break;
return 0;
} // BLE_GAP_EVENT_PASSKEY_ACTION } // BLE_GAP_EVENT_PASSKEY_ACTION
default: default:
@ -735,27 +696,25 @@ int NimBLEServer::handleGapEvent(struct ble_gap_event *event, void *arg) {
return 0; return 0;
} // handleGapEvent } // handleGapEvent
/** /**
* @brief STATIC callback to handle events from the NimBLE stack. * @brief GATT event handler.
*/ */
int NimBLEServer::handleGattEvent(uint16_t conn_handle, uint16_t attr_handle, int NimBLEServer::handleGattEvent(uint16_t connHandle, uint16_t attrHandle, ble_gatt_access_ctxt* ctxt, void* arg) {
struct ble_gatt_access_ctxt *ctxt, void *arg) { NIMBLE_LOGD(LOG_TAG,
NIMBLE_LOGD(LOG_TAG, "Gatt %s event", (ctxt->op == BLE_GATT_ACCESS_OP_READ_CHR || "Gatt %s event",
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);
const auto& val = pAtt->getAttVal(); const auto& val = pAtt->getAttVal();
NimBLEConnInfo peerInfo{}; NimBLEConnInfo peerInfo{};
ble_gap_conn_find(conn_handle, &peerInfo.m_desc); ble_gap_conn_find(connHandle, &peerInfo.m_desc);
switch (ctxt->op) { switch (ctxt->op) {
case BLE_GATT_ACCESS_OP_READ_DSC: case BLE_GATT_ACCESS_OP_READ_DSC:
case BLE_GATT_ACCESS_OP_READ_CHR: { case BLE_GATT_ACCESS_OP_READ_CHR: {
// If the packet header is only 8 bytes this is a follow up of a long read // If the packet header is only 8 bytes this is a follow up of a long read
// so we don't want to call the onRead() callback again. // so we don't want to call the onRead() callback again.
if(ctxt->om->om_pkthdr_len > 8 || if (ctxt->om->om_pkthdr_len > 8 || connHandle == BLE_HS_CONN_HANDLE_NONE ||
conn_handle == BLE_HS_CONN_HANDLE_NONE || val.size() <= (ble_att_mtu(connHandle) - 3)) {
val.size() <= (ble_att_mtu(conn_handle) - 3)) {
pAtt->readEvent(peerInfo); pAtt->readEvent(peerInfo);
} }
@ -801,7 +760,7 @@ int NimBLEServer::handleGattEvent(uint16_t conn_handle, uint16_t attr_handle,
/** /**
* @brief Set the server callbacks. * @brief Set the server callbacks.
* *
* As a %BLE server operates, it will generate server level events such as a new client connecting or a previous client * As a BLE server operates, it will generate server level events such as a new client connecting or a previous client
* disconnecting. This function can be called to register a callback handler that will be invoked when these * disconnecting. This function can be called to register a callback handler that will be invoked when these
* events are detected. * events are detected.
* *
@ -814,10 +773,10 @@ void NimBLEServer::setCallbacks(NimBLEServerCallbacks* pCallbacks, bool deleteCa
m_deleteCallbacks = deleteCallbacks; m_deleteCallbacks = deleteCallbacks;
} else { } else {
m_pServerCallbacks = &defaultCallbacks; m_pServerCallbacks = &defaultCallbacks;
m_deleteCallbacks = false;
} }
} // setCallbacks } // setCallbacks
/** /**
* @brief Remove a service from the server. * @brief Remove a service from the server.
* *
@ -864,8 +823,7 @@ void NimBLEServer::removeService(NimBLEService* service, bool deleteSvc) {
# if !CONFIG_BT_NIMBLE_EXT_ADV # if !CONFIG_BT_NIMBLE_EXT_ADV
NimBLEDevice::getAdvertising()->removeServiceUUID(service->getUUID()); NimBLEDevice::getAdvertising()->removeServiceUUID(service->getUUID());
# endif # endif
} } // removeService
/** /**
* @brief Adds a service which was either already created but removed from availability,\n * @brief Adds a service which was either already created but removed from availability,\n
@ -877,8 +835,7 @@ void NimBLEServer::removeService(NimBLEService* service, bool deleteSvc) {
void NimBLEServer::addService(NimBLEService* service) { void NimBLEServer::addService(NimBLEService* service) {
// Check that a service with the supplied UUID does not already exist. // Check that a service with the supplied UUID does not already exist.
if (getServiceByUUID(service->getUUID()) != nullptr) { if (getServiceByUUID(service->getUUID()) != nullptr) {
NIMBLE_LOGW(LOG_TAG, "Warning creating a duplicate service UUID: %s", NIMBLE_LOGW(LOG_TAG, "Warning creating a duplicate service UUID: %s", std::string(service->getUUID()).c_str());
std::string(service->getUUID()).c_str());
} }
// If adding a service that was not removed add it and return. // If adding a service that was not removed add it and return.
@ -892,7 +849,6 @@ void NimBLEServer::addService(NimBLEService* service) {
serviceChanged(); serviceChanged();
} }
/** /**
* @brief Resets the GATT server, used when services are added/removed after initialization. * @brief Resets the GATT server, used when services are added/removed after initialization.
*/ */
@ -923,33 +879,29 @@ void NimBLEServer::resetGATT() {
m_svcChanged = false; m_svcChanged = false;
m_gattsStarted = false; m_gattsStarted = false;
} } // resetGATT
# if CONFIG_BT_NIMBLE_EXT_ADV # if CONFIG_BT_NIMBLE_EXT_ADV
/** /**
* @brief Start advertising. * @brief Start advertising.
* @param [in] inst_id The extended advertisement instance ID to start. * @param [in] instId The extended advertisement instance ID to start.
* @param [in] duration How long to advertise for in milliseconds, 0 = forever (default). * @param [in] duration How long to advertise for in milliseconds, 0 = forever (default).
* @param [in] max_events Maximum number of advertisement events to send, 0 = no limit (default). * @param [in] maxEvents Maximum number of advertisement events to send, 0 = no limit (default).
* @return True if advertising started successfully. * @return True if advertising started successfully.
* @details Start the server advertising its existence. This is a convenience function and is equivalent to * @details Start the server advertising its existence. This is a convenience function and is equivalent to
* retrieving the advertising object and invoking start upon it. * retrieving the advertising object and invoking start upon it.
*/ */
bool NimBLEServer::startAdvertising(uint8_t inst_id, bool NimBLEServer::startAdvertising(uint8_t instId, int duration, int maxEvents) const {
int duration, return getAdvertising()->start(instId, duration, maxEvents);
int max_events) {
return getAdvertising()->start(inst_id, duration, max_events);
} // startAdvertising } // startAdvertising
/** /**
* @brief Convenience function to stop advertising a data set. * @brief Convenience function to stop advertising a data set.
* @param [in] inst_id The extended advertisement instance ID to stop advertising. * @param [in] instId The extended advertisement instance ID to stop advertising.
* @return True if advertising stopped successfully. * @return True if advertising stopped successfully.
*/ */
bool NimBLEServer::stopAdvertising(uint8_t inst_id) { bool NimBLEServer::stopAdvertising(uint8_t instId) const {
return getAdvertising()->stop(inst_id); return getAdvertising()->stop(instId);
} // stopAdvertising } // stopAdvertising
/** /**
@ -1005,86 +957,78 @@ bool NimBLEServer::getPhy(uint16_t connHandle, uint8_t* txPhy, uint8_t* rxPhy) {
* @details Start the server advertising its existence. This is a convenience function and is equivalent to * @details Start the server advertising its existence. This is a convenience function and is equivalent to
* retrieving the advertising object and invoking start upon it. * retrieving the advertising object and invoking start upon it.
*/ */
bool NimBLEServer::startAdvertising(uint32_t duration) { bool NimBLEServer::startAdvertising(uint32_t duration) const {
return getAdvertising()->start(duration); return getAdvertising()->start(duration);
} // startAdvertising } // startAdvertising
#endif
/** /**
* @brief Stop advertising. * @brief Stop advertising.
* @return True if advertising stopped successfully. * @return True if advertising stopped successfully.
*/ */
bool NimBLEServer::stopAdvertising() { bool NimBLEServer::stopAdvertising() const {
return getAdvertising()->stop(); return getAdvertising()->stop();
} // stopAdvertising } // stopAdvertising
# endif
/** /**
* @brief Get the MTU of the client. * @brief Get the MTU value of a client connection.
* @returns The client MTU or 0 if not found/connected. * @param [in] connHandle The connection handle of the client to get the MTU value for.
* @returns The MTU or 0 if not found/connected.
*/ */
uint16_t NimBLEServer::getPeerMTU(uint16_t conn_id) { uint16_t NimBLEServer::getPeerMTU(uint16_t connHandle) const {
return ble_att_mtu(conn_id); return ble_att_mtu(connHandle);
} // getPeerMTU } // getPeerMTU
/** /**
* @brief Request an Update the connection parameters: * @brief Request an Update the connection parameters:
* * Can only be used after a connection has been established. * * Can only be used after a connection has been established.
* @param [in] conn_handle The connection handle of the peer to send the request to. * @param [in] connHandle The connection handle of the peer to send the request to.
* @param [in] minInterval The minimum connection interval in 1.25ms units. * @param [in] minInterval The minimum connection interval in 1.25ms units.
* @param [in] maxInterval The maximum connection interval in 1.25ms units. * @param [in] maxInterval The maximum connection interval in 1.25ms units.
* @param [in] latency The number of packets allowed to skip (extends max interval). * @param [in] latency The number of packets allowed to skip (extends max interval).
* @param [in] timeout The timeout time in 10ms units before disconnecting. * @param [in] timeout The timeout time in 10ms units before disconnecting.
*/ */
void NimBLEServer::updateConnParams(uint16_t conn_handle, void NimBLEServer::updateConnParams(
uint16_t minInterval, uint16_t maxInterval, uint16_t connHandle, uint16_t minInterval, uint16_t maxInterval, uint16_t latency, uint16_t timeout) const {
uint16_t latency, uint16_t timeout) ble_gap_upd_params params = {.itvl_min = minInterval,
{ .itvl_max = maxInterval,
ble_gap_upd_params params; .latency = latency,
.supervision_timeout = timeout,
.min_ce_len = BLE_GAP_INITIAL_CONN_MIN_CE_LEN,
.max_ce_len = BLE_GAP_INITIAL_CONN_MAX_CE_LEN};
params.latency = latency; int rc = ble_gap_update_params(connHandle, &params);
params.itvl_max = maxInterval; // max_int = 0x20*1.25ms = 40ms
params.itvl_min = minInterval; // min_int = 0x10*1.25ms = 20ms
params.supervision_timeout = timeout; // timeout = 400*10ms = 4000ms
params.min_ce_len = BLE_GAP_INITIAL_CONN_MIN_CE_LEN; // Minimum length of connection event in 0.625ms units
params.max_ce_len = BLE_GAP_INITIAL_CONN_MAX_CE_LEN; // Maximum length of connection event in 0.625ms units
int rc = ble_gap_update_params(conn_handle, &params);
if (rc != 0) { if (rc != 0) {
NIMBLE_LOGE(LOG_TAG, "Update params error: %d, %s", rc, NimBLEUtils::returnCodeToString(rc)); NIMBLE_LOGE(LOG_TAG, "Update params error: %d, %s", rc, NimBLEUtils::returnCodeToString(rc));
} }
} // updateConnParams } // updateConnParams
/** /**
* @brief Request an update of the data packet length. * @brief Request an update of the data packet length.
* * Can only be used after a connection has been established. * * Can only be used after a connection has been established.
* @details Sends a data length update request to the peer. * @details Sends a data length update request to the peer.
* The Data Length Extension (DLE) allows to increase the Data Channel Payload from 27 bytes to up to 251 bytes. * The Data Length Extension (DLE) allows to increase the Data Channel Payload from 27 bytes to up to 251 bytes.
* The peer needs to support the Bluetooth 4.2 specifications, to be capable of DLE. * The peer needs to support the Bluetooth 4.2 specifications, to be capable of DLE.
* @param [in] conn_handle The connection handle of the peer to send the request to. * @param [in] connHandle The connection handle of the peer to send the request to.
* @param [in] tx_octets The preferred number of payload octets to use (Range 0x001B-0x00FB). * @param [in] octets The preferred number of payload octets to use (Range 0x001B-0x00FB).
*/ */
void NimBLEServer::setDataLen(uint16_t conn_handle, uint16_t tx_octets) { void NimBLEServer::setDataLen(uint16_t connHandle, uint16_t octets) const {
# if defined(CONFIG_NIMBLE_CPP_IDF) && !defined(ESP_IDF_VERSION) || \ # if defined(CONFIG_NIMBLE_CPP_IDF) && !defined(ESP_IDF_VERSION) || \
(ESP_IDF_VERSION_MAJOR * 100 + ESP_IDF_VERSION_MINOR * 10 + ESP_IDF_VERSION_PATCH) < 432 (ESP_IDF_VERSION_MAJOR * 100 + ESP_IDF_VERSION_MINOR * 10 + ESP_IDF_VERSION_PATCH) < 432
return; return;
# else # else
uint16_t tx_time = (tx_octets + 14) * 8; uint16_t tx_time = (octets + 14) * 8;
int rc = ble_gap_set_data_len(conn_handle, tx_octets, tx_time); int rc = ble_gap_set_data_len(connHandle, octets, tx_time);
if (rc != 0) { if (rc != 0) {
NIMBLE_LOGE(LOG_TAG, "Set data length error: %d, %s", rc, NimBLEUtils::returnCodeToString(rc)); NIMBLE_LOGE(LOG_TAG, "Set data length error: %d, %s", rc, NimBLEUtils::returnCodeToString(rc));
} }
# endif # endif
} // setDataLen } // setDataLen
bool NimBLEServer::setIndicateWait(uint16_t connHandle) {
bool NimBLEServer::setIndicateWait(uint16_t conn_handle) {
for (auto i = 0; i < CONFIG_BT_NIMBLE_MAX_CONNECTIONS; i++) { for (auto i = 0; i < CONFIG_BT_NIMBLE_MAX_CONNECTIONS; i++) {
if(m_indWait[i] == conn_handle) { if (m_indWait[i] == connHandle) {
return false; return false;
} }
} }
@ -1092,17 +1036,15 @@ bool NimBLEServer::setIndicateWait(uint16_t conn_handle) {
return true; return true;
} }
void NimBLEServer::clearIndicateWait(uint16_t connHandle) {
void NimBLEServer::clearIndicateWait(uint16_t conn_handle) {
for (auto i = 0; i < CONFIG_BT_NIMBLE_MAX_CONNECTIONS; i++) { for (auto i = 0; i < CONFIG_BT_NIMBLE_MAX_CONNECTIONS; i++) {
if(m_indWait[i] == conn_handle) { if (m_indWait[i] == connHandle) {
m_indWait[i] = BLE_HS_CONN_HANDLE_NONE; m_indWait[i] = BLE_HS_CONN_HANDLE_NONE;
return; return;
} }
} }
} }
/** 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");
@ -1112,8 +1054,7 @@ void NimBLEServerCallbacks::onConnect(NimBLEServer* pServer, NimBLEConnInfo& con
NIMBLE_LOGD("NimBLEServerCallbacks", "onConnect(): Default"); NIMBLE_LOGD("NimBLEServerCallbacks", "onConnect(): Default");
} // onConnect } // onConnect
void NimBLEServerCallbacks::onDisconnect(NimBLEServer* pServer, void NimBLEServerCallbacks::onDisconnect(NimBLEServer* pServer, NimBLEConnInfo& connInfo, int reason) {
NimBLEConnInfo& connInfo, int reason) {
NIMBLE_LOGD("NimBLEServerCallbacks", "onDisconnect(): Default"); NIMBLE_LOGD("NimBLEServerCallbacks", "onDisconnect(): Default");
} // onDisconnect } // onDisconnect

View file

@ -12,123 +12,122 @@
* Author: kolban * Author: kolban
*/ */
#ifndef MAIN_NIMBLESERVER_H_ #ifndef NIMBLE_CPP_SERVER_H_
#define MAIN_NIMBLESERVER_H_ #define NIMBLE_CPP_SERVER_H_
#include "nimconfig.h" #include "nimconfig.h"
#if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL) #if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
class NimBLEServer; # if defined(CONFIG_NIMBLE_CPP_IDF)
class NimBLEServerCallbacks; # include "host/ble_gap.h"
#include "NimBLEUtils.h"
#include "NimBLEAddress.h"
#if CONFIG_BT_NIMBLE_EXT_ADV
#include "NimBLEExtAdvertising.h"
# else # else
#include "NimBLEAdvertising.h" # include "nimble/nimble/host/include/host/ble_gap.h"
# endif # endif
#include "NimBLEService.h"
#include "NimBLECharacteristic.h" /**** FIX COMPILATION ****/
#include "NimBLEConnInfo.h" # undef min
# undef max
/**************************/
# include <string>
# include <vector>
# include <array>
# define NIMBLE_ATT_REMOVE_HIDE 1 # define NIMBLE_ATT_REMOVE_HIDE 1
# define NIMBLE_ATT_REMOVE_DELETE 2 # define NIMBLE_ATT_REMOVE_DELETE 2
#define onMtuChanged onMTUChange class NimBLEService;
class NimBLEServerCallbacks;
class NimBLEUUID;
class NimBLEConnInfo;
class NimBLEAddress;
class NimBLEService;
class NimBLECharacteristic;
# if CONFIG_BT_NIMBLE_EXT_ADV
class NimBLEExtAdvertising;
# else
class NimBLEAdvertising;
# endif
/** /**
* @brief The model of a %BLE server. * @brief The model of a BLE server.
*/ */
class NimBLEServer { class NimBLEServer {
public: public:
size_t getConnectedCount(); void start();
uint8_t getConnectedCount() const;
bool disconnect(uint16_t connHandle, uint8_t reason = BLE_ERR_REM_USER_CONN_TERM) const;
bool disconnect(const NimBLEConnInfo& connInfo, uint8_t reason = BLE_ERR_REM_USER_CONN_TERM) const;
void setCallbacks(NimBLEServerCallbacks* pCallbacks, bool deleteCallbacks = true);
void updateConnParams(uint16_t connHandle, uint16_t minInterval, uint16_t maxInterval, uint16_t latency, uint16_t timeout) const;
NimBLEService* createService(const char* uuid); NimBLEService* createService(const char* uuid);
NimBLEService* createService(const NimBLEUUID& uuid); NimBLEService* createService(const NimBLEUUID& uuid);
NimBLEService* getServiceByUUID(const char* uuid, uint16_t instanceId = 0) const;
NimBLEService* getServiceByUUID(const NimBLEUUID& uuid, uint16_t instanceId = 0) const;
NimBLEService* getServiceByHandle(uint16_t handle) const;
void removeService(NimBLEService* service, bool deleteSvc = false); void removeService(NimBLEService* service, bool deleteSvc = false);
void addService(NimBLEService* service); void addService(NimBLEService* service);
void setCallbacks(NimBLEServerCallbacks* pCallbacks, uint16_t getPeerMTU(uint16_t connHandle) const;
bool deleteCallbacks = true); std::vector<uint16_t> getPeerDevices() const;
NimBLEConnInfo getPeerInfo(uint8_t index) const;
NimBLEConnInfo getPeerInfo(const NimBLEAddress& address) const;
NimBLEConnInfo getPeerInfoByHandle(uint16_t connHandle) const;
std::string getPeerName(const NimBLEConnInfo& connInfo) const;
void getPeerNameOnConnect(bool enable);
void advertiseOnDisconnect(bool enable);
void setDataLen(uint16_t connHandle, uint16_t tx_octets) const;
# if CONFIG_BT_NIMBLE_EXT_ADV # if CONFIG_BT_NIMBLE_EXT_ADV
NimBLEExtAdvertising* getAdvertising(); NimBLEExtAdvertising* getAdvertising() const;
bool startAdvertising(uint8_t inst_id, bool startAdvertising(uint8_t instanceId, int duration = 0, int maxEvents = 0) const;
int duration = 0, bool stopAdvertising(uint8_t instanceId) const;
int max_events = 0);
bool stopAdvertising(uint8_t inst_id);
bool updatePhy(uint16_t connHandle, uint8_t txPhysMask, uint8_t rxPhysMask, uint16_t phyOptions); bool updatePhy(uint16_t connHandle, uint8_t txPhysMask, uint8_t rxPhysMask, uint16_t phyOptions);
bool getPhy(uint16_t connHandle, uint8_t* txPhy, uint8_t* rxPhy); bool getPhy(uint16_t connHandle, uint8_t* txPhy, uint8_t* rxPhy);
# endif # endif
# if !CONFIG_BT_NIMBLE_EXT_ADV || defined(_DOXYGEN_) # if !CONFIG_BT_NIMBLE_EXT_ADV || defined(_DOXYGEN_)
NimBLEAdvertising* getAdvertising(); NimBLEAdvertising* getAdvertising() const;
bool startAdvertising(uint32_t duration = 0); bool startAdvertising(uint32_t duration = 0) const;
#endif bool stopAdvertising() const;
bool stopAdvertising();
void start();
NimBLEService* getServiceByUUID(const char* uuid, uint16_t instanceId = 0);
NimBLEService* getServiceByUUID(const NimBLEUUID &uuid, uint16_t instanceId = 0);
NimBLEService* getServiceByHandle(uint16_t handle);
int disconnect(uint16_t connID,
uint8_t reason = BLE_ERR_REM_USER_CONN_TERM);
int disconnect(const NimBLEConnInfo &connInfo,
uint8_t reason = BLE_ERR_REM_USER_CONN_TERM);
void updateConnParams(uint16_t conn_handle,
uint16_t minInterval, uint16_t maxInterval,
uint16_t latency, uint16_t timeout);
void setDataLen(uint16_t conn_handle, uint16_t tx_octets);
uint16_t getPeerMTU(uint16_t conn_id);
std::vector<uint16_t> getPeerDevices();
NimBLEConnInfo getPeerInfo(size_t index);
NimBLEConnInfo getPeerInfo(const NimBLEAddress& address);
NimBLEConnInfo getPeerIDInfo(uint16_t id);
std::string getPeerName(const NimBLEConnInfo& connInfo);
void getPeerNameOnConnect(bool enable);
#if !CONFIG_BT_NIMBLE_EXT_ADV || defined(_DOXYGEN_)
void advertiseOnDisconnect(bool);
# endif # endif
private: private:
NimBLEServer();
~NimBLEServer();
friend class NimBLECharacteristic;
friend class NimBLEService;
friend class NimBLEDevice; friend class NimBLEDevice;
friend class NimBLEAdvertising; friend class NimBLEService;
friend class NimBLECharacteristic;
# if CONFIG_BT_NIMBLE_EXT_ADV # if CONFIG_BT_NIMBLE_EXT_ADV
friend class NimBLEExtAdvertising; friend class NimBLEExtAdvertising;
friend class NimBLEExtAdvertisementData; # else
friend class NimBLEAdvertising;
# endif # endif
bool m_gattsStarted; NimBLEServer();
~NimBLEServer();
bool m_gattsStarted : 1;
bool m_getPeerNameOnConnect : 1;
bool m_svcChanged : 1;
bool m_deleteCallbacks : 1;
# if !CONFIG_BT_NIMBLE_EXT_ADV # if !CONFIG_BT_NIMBLE_EXT_ADV
bool m_advertiseOnDisconnect; bool m_advertiseOnDisconnect : 1;
# endif # endif
bool m_getPeerNameOnConnect;
bool m_svcChanged;
NimBLEServerCallbacks* m_pServerCallbacks; NimBLEServerCallbacks* m_pServerCallbacks;
bool m_deleteCallbacks;
uint16_t m_indWait[CONFIG_BT_NIMBLE_MAX_CONNECTIONS];
std::vector<uint16_t> m_connectedPeersVec;
// uint16_t m_svcChgChrHdl; // Future use
std::vector<NimBLEService*> m_svcVec; std::vector<NimBLEService*> m_svcVec;
std::vector<NimBLECharacteristic*> m_notifyChrVec; std::vector<NimBLECharacteristic*> m_notifyChrVec;
std::array<uint16_t, CONFIG_BT_NIMBLE_MAX_CONNECTIONS> m_connectedPeers;
std::array<uint16_t, CONFIG_BT_NIMBLE_MAX_CONNECTIONS> m_indWait;
static int handleGapEvent(struct ble_gap_event* event, void* arg); static int handleGapEvent(struct ble_gap_event* event, void* arg);
static int peerNameCB(uint16_t conn_handle, const struct ble_gatt_error *error, static int handleGattEvent(uint16_t connHandle, uint16_t attrHandle, ble_gatt_access_ctxt* ctxt, void* arg);
struct ble_gatt_attr *attr, void *arg); static int peerNameCB(uint16_t connHandle, const ble_gatt_error* error, ble_gatt_attr* attr, void* arg);
std::string getPeerNameInternal(uint16_t conn_handle, int cb_type = -1); std::string getPeerNameImpl(uint16_t connHandle, int cb_type = -1) const;
void serviceChanged(); void serviceChanged();
void resetGATT(); void resetGATT();
bool setIndicateWait(uint16_t conn_handle); bool setIndicateWait(uint16_t connHandle);
void clearIndicateWait(uint16_t conn_handle); void clearIndicateWait(uint16_t connHandle);
static int handleGattEvent(uint16_t conn_handle, uint16_t attr_handle,
struct ble_gatt_access_ctxt *ctxt, void *arg);
}; // NimBLEServer }; // NimBLEServer
/** /**
* @brief Callbacks associated with the operation of a %BLE server. * @brief Callbacks associated with the operation of a %BLE server.
*/ */
@ -232,5 +231,5 @@ public:
# endif # endif
}; // NimBLEServerCallbacks }; // NimBLEServerCallbacks
#endif /* CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_PERIPHERAL */ #endif // CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_PERIPHERAL
#endif /* MAIN_NIMBLESERVER_H_ */ #endif // NIMBLE_CPP_SERVER_H_

View file

@ -17,8 +17,13 @@
#include "nimconfig.h" #include "nimconfig.h"
#if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL) #if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
# include "NimBLEDevice.h"
# include "NimBLEService.h" # include "NimBLEService.h"
# if CONFIG_BT_NIMBLE_EXT_ADV
# include "NimBLEExtAdvertising.h"
# else
# include "NimBLEAdvertising.h"
# endif
# include "NimBLEDevice.h"
# include "NimBLEUtils.h" # include "NimBLEUtils.h"
# include "NimBLELog.h" # include "NimBLELog.h"