diff --git a/src/NimBLECharacteristic.cpp b/src/NimBLECharacteristic.cpp index 39e7edf..f3965a4 100644 --- a/src/NimBLECharacteristic.cpp +++ b/src/NimBLECharacteristic.cpp @@ -45,14 +45,15 @@ NimBLECharacteristic::NimBLECharacteristic(const char* uuid, uint16_t properties * @param [in] pService - pointer to the service instance this characteristic belongs to. */ NimBLECharacteristic::NimBLECharacteristic(const NimBLEUUID &uuid, uint16_t properties, NimBLEService* pService) { - m_uuid = uuid; - m_handle = NULL_HANDLE; - m_properties = properties; - m_pCallbacks = &defaultCallback; - m_pService = pService; - m_value = ""; - m_valMux = portMUX_INITIALIZER_UNLOCKED; - m_timestamp = 0; + m_uuid = uuid; + m_handle = NULL_HANDLE; + m_properties = properties; + m_pCallbacks = &defaultCallback; + m_pService = pService; + m_value = ""; + m_valMux = portMUX_INITIALIZER_UNLOCKED; + m_timestamp = 0; + m_removed = 0; } // NimBLECharacteristic /** @@ -95,21 +96,63 @@ NimBLEDescriptor* NimBLECharacteristic::createDescriptor(const NimBLEUUID &uuid, } addDescriptor(pDescriptor); - return pDescriptor; } // createDescriptor /** * @brief Add a descriptor to the characteristic. - * @param [in] A pointer to the descriptor to add. + * @param [in] pDescriptor A pointer to the descriptor to add. */ void NimBLECharacteristic::addDescriptor(NimBLEDescriptor *pDescriptor) { + bool foundRemoved = false; + + if(pDescriptor->m_removed > 0) { + for(auto& it : m_dscVec) { + if(it == pDescriptor) { + foundRemoved = true; + pDescriptor->m_removed = 0; + } + } + } + + if(!foundRemoved) { + m_dscVec.push_back(pDescriptor); + } + pDescriptor->setCharacteristic(this); - m_dscVec.push_back(pDescriptor); + NimBLEDevice::getServer()->serviceChanged(); } +/** + * @brief Remove a descriptor from the characterisitc. + * @param[in] pDescriptor A pointer to the descriptor instance to remove from the characterisitc. + * @param[in] deleteDsc If true it will delete the descriptor instance and free it's resources. + */ +void NimBLECharacteristic::removeDescriptor(NimBLEDescriptor *pDescriptor, bool deleteDsc) { + // Check if the descriptor was already removed and if so, check if this + // is being called to delete the object and do so if requested. + // Otherwise, ignore the call and return. + if(pDescriptor->m_removed > 0) { + if(deleteDsc) { + for(auto it = m_dscVec.begin(); it != m_dscVec.end(); ++it) { + if ((*it) == pDescriptor) { + delete *it; + m_dscVec.erase(it); + break; + } + } + } + + return; + } + + pDescriptor->m_removed = deleteDsc ? NIMBLE_ATT_REMOVE_DELETE : NIMBLE_ATT_REMOVE_HIDE; + NimBLEDevice::getServer()->serviceChanged(); +} // removeDescriptor + + /** * @brief Return the BLE Descriptor for the given UUID. * @param [in] uuid The UUID of the descriptor. diff --git a/src/NimBLECharacteristic.h b/src/NimBLECharacteristic.h index 7685b2a..6008d12 100644 --- a/src/NimBLECharacteristic.h +++ b/src/NimBLECharacteristic.h @@ -99,6 +99,7 @@ public: NimBLEDescriptor* getDescriptorByUUID(const char* uuid); NimBLEDescriptor* getDescriptorByUUID(const NimBLEUUID &uuid); NimBLEDescriptor* getDescriptorByHandle(uint16_t handle); + void removeDescriptor(NimBLEDescriptor *pDescriptor, bool deleteDsc = false); std::string getValue(time_t *timestamp = nullptr); size_t getDataLength(); @@ -152,6 +153,7 @@ private: std::vector m_dscVec; portMUX_TYPE m_valMux; time_t m_timestamp; + uint8_t m_removed; std::vector> m_subscribedVec; }; // NimBLECharacteristic diff --git a/src/NimBLEDescriptor.cpp b/src/NimBLEDescriptor.cpp index db7aea5..f946766 100644 --- a/src/NimBLEDescriptor.cpp +++ b/src/NimBLEDescriptor.cpp @@ -53,6 +53,7 @@ NimBLEDescriptor::NimBLEDescriptor(NimBLEUUID uuid, uint16_t properties, uint16_ m_value.attr_value = (uint8_t*) calloc(max_len,1); // Allocate storage for the value. m_valMux = portMUX_INITIALIZER_UNLOCKED; m_properties = 0; + m_removed = 0; if (properties & BLE_GATT_CHR_F_READ) { // convert uint16_t properties to uint8_t m_properties |= BLE_ATT_F_READ; diff --git a/src/NimBLEDescriptor.h b/src/NimBLEDescriptor.h index d325c7b..5dc0ce8 100644 --- a/src/NimBLEDescriptor.h +++ b/src/NimBLEDescriptor.h @@ -93,6 +93,7 @@ private: uint8_t m_properties; attr_value_t m_value; portMUX_TYPE m_valMux; + uint8_t m_removed; }; // NimBLEDescriptor diff --git a/src/NimBLEServer.cpp b/src/NimBLEServer.cpp index 41f15dc..082c51a 100644 --- a/src/NimBLEServer.cpp +++ b/src/NimBLEServer.cpp @@ -91,12 +91,7 @@ NimBLEService* NimBLEServer::createService(const NimBLEUUID &uuid, uint32_t numH NimBLEService* pService = new NimBLEService(uuid, numHandles, this); m_svcVec.push_back(pService); // Save a reference to this service being on this server. - - if(m_gattsStarted) { - ble_svc_gatt_changed(0x0001, 0xffff); - m_svcChanged = true; - resetGATT(); - } + serviceChanged(); NIMBLE_LOGD(LOG_TAG, "<< createService"); return pService; @@ -157,6 +152,18 @@ NimBLEAdvertising* NimBLEServer::getAdvertising() { } // getAdvertising +/** + * @brief Sends a service changed notification and resets the GATT server. + */ +void NimBLEServer::serviceChanged() { + if(m_gattsStarted) { + m_svcChanged = true; + ble_svc_gatt_changed(0x0001, 0xffff); + resetGATT(); + } +} + + /** * @brief Start the GATT server. Required to be called after setup of all * services and characteristics / descriptors for the NimBLE host to register them. @@ -630,7 +637,7 @@ void NimBLEServer::removeService(NimBLEService* service, bool deleteSvc) { if(service->m_removed > 0) { if(deleteSvc) { for(auto it = m_svcVec.begin(); it != m_svcVec.end(); ++it) { - if ((*it)->getUUID() == service->getUUID()) { + if ((*it) == service) { delete *it; m_svcVec.erase(it); break; @@ -646,11 +653,8 @@ void NimBLEServer::removeService(NimBLEService* service, bool deleteSvc) { return; } - service->m_removed = deleteSvc ? 2 : 1; - m_svcChanged = true; - - ble_svc_gatt_changed(0x0001, 0xffff); - resetGATT(); + service->m_removed = deleteSvc ? NIMBLE_ATT_REMOVE_DELETE : NIMBLE_ATT_REMOVE_HIDE; + serviceChanged(); NimBLEDevice::getAdvertising()->removeServiceUUID(service->getUUID()); } @@ -677,10 +681,7 @@ void NimBLEServer::addService(NimBLEService* service) { } service->m_removed = 0; - m_svcChanged = true; - - ble_svc_gatt_changed(0x0001, 0xffff); - resetGATT(); + serviceChanged(); } @@ -699,7 +700,7 @@ void NimBLEServer::resetGATT() { for(auto it = m_svcVec.begin(); it != m_svcVec.end(); ) { if ((*it)->m_removed > 0) { - if ((*it)->m_removed == 2) { + if ((*it)->m_removed == NIMBLE_ATT_REMOVE_DELETE) { delete *it; it = m_svcVec.erase(it); } else { diff --git a/src/NimBLEServer.h b/src/NimBLEServer.h index 9fdf1bf..ebcf39f 100644 --- a/src/NimBLEServer.h +++ b/src/NimBLEServer.h @@ -20,6 +20,9 @@ #include "nimconfig.h" #if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL) +#define NIMBLE_ATT_REMOVE_HIDE 1 +#define NIMBLE_ATT_REMOVE_DELETE 2 + #include "NimBLEUtils.h" #include "NimBLEAddress.h" #include "NimBLEAdvertising.h" @@ -69,6 +72,7 @@ private: NimBLEServer(); ~NimBLEServer(); friend class NimBLECharacteristic; + friend class NimBLEService; friend class NimBLEDevice; friend class NimBLEAdvertising; @@ -86,6 +90,7 @@ private: std::vector m_notifyChrVec; static int handleGapEvent(struct ble_gap_event *event, void *arg); + void serviceChanged(); void resetGATT(); bool setIndicateWait(uint16_t conn_handle); void clearIndicateWait(uint16_t conn_handle); diff --git a/src/NimBLEService.cpp b/src/NimBLEService.cpp index 422eefe..9c43e90 100644 --- a/src/NimBLEService.cpp +++ b/src/NimBLEService.cpp @@ -35,7 +35,7 @@ static const char* LOG_TAG = "NimBLEService"; // Tag for logging. * @brief Construct an instance of the NimBLEService * @param [in] uuid The UUID of the service. * @param [in] numHandles The maximum number of handles associated with the service. - * @param [in] a pointer to the server instance that this service belongs to. + * @param [in] pServer A pointer to the server instance that this service belongs to. */ NimBLEService::NimBLEService(const char* uuid, uint16_t numHandles, NimBLEServer* pServer) : NimBLEService(NimBLEUUID(uuid), numHandles, pServer) { @@ -46,7 +46,7 @@ NimBLEService::NimBLEService(const char* uuid, uint16_t numHandles, NimBLEServer * @brief Construct an instance of the BLEService * @param [in] uuid The UUID of the service. * @param [in] numHandles The maximum number of handles associated with the service. - * @param [in] a pointer to the server instance that this service belongs to. + * @param [in] pServer A pointer to the server instance that this service belongs to. */ NimBLEService::NimBLEService(const NimBLEUUID &uuid, uint16_t numHandles, NimBLEServer* pServer) { m_uuid = uuid; @@ -118,7 +118,12 @@ NimBLEUUID NimBLEService::getUUID() { */ bool NimBLEService::start() { NIMBLE_LOGD(LOG_TAG, ">> start(): Starting service: %s", toString().c_str()); - int rc = 0; + + // Rebuild the service definition if the server attributes have changed. + if(getServer()->m_svcChanged && m_pSvcDef != nullptr) { + delete(m_pSvcDef); + m_pSvcDef = nullptr; + } if(m_pSvcDef == nullptr) { // Nimble requires an array of services to be sent to the api @@ -132,8 +137,23 @@ bool NimBLEService::start() { svc[0].uuid = &m_uuid.getNative()->u; svc[0].includes = NULL; - size_t numChrs = m_chrVec.size(); + int removedCount = 0; + for(auto it = m_chrVec.begin(); it != m_chrVec.end(); ) { + if ((*it)->m_removed > 0) { + if ((*it)->m_removed == NIMBLE_ATT_REMOVE_DELETE) { + delete *it; + it = m_chrVec.erase(it); + } else { + ++removedCount; + ++it; + } + continue; + } + ++it; + } + + size_t numChrs = m_chrVec.size() - removedCount; NIMBLE_LOGD(LOG_TAG,"Adding %d characteristics for service %s", numChrs, toString().c_str()); if(!numChrs){ @@ -142,40 +162,60 @@ bool NimBLEService::start() { // Nimble requires the last characteristic to have it's uuid = 0 to indicate the end // of the characteristics for the service. We create 1 extra and set it to null // for this purpose. - pChr_a = new ble_gatt_chr_def[numChrs+1]; - NimBLECharacteristic* pCharacteristic = *m_chrVec.begin(); + pChr_a = new ble_gatt_chr_def[numChrs + 1]; + uint8_t i = 0; + for(auto chr_it = m_chrVec.begin(); chr_it != m_chrVec.end(); ++chr_it) { + if((*chr_it)->m_removed > 0) { + continue; + } - for(uint8_t i=0; i < numChrs;) { - uint8_t numDscs = pCharacteristic->m_dscVec.size(); + removedCount = 0; + for(auto it = (*chr_it)->m_dscVec.begin(); it != (*chr_it)->m_dscVec.end(); ) { + if ((*it)->m_removed > 0) { + if ((*it)->m_removed == NIMBLE_ATT_REMOVE_DELETE) { + delete *it; + it = (*chr_it)->m_dscVec.erase(it); + } else { + ++removedCount; + ++it; + } + continue; + } + + ++it; + } + + size_t numDscs = (*chr_it)->m_dscVec.size() - removedCount; if(!numDscs){ pChr_a[i].descriptors = NULL; } else { // Must have last descriptor uuid = 0 so we have to create 1 extra pDsc_a = new ble_gatt_dsc_def[numDscs+1]; - NimBLEDescriptor* pDescriptor = *pCharacteristic->m_dscVec.begin(); - for(uint8_t d=0; d < numDscs;) { - pDsc_a[d].uuid = &pDescriptor->m_uuid.getNative()->u; - pDsc_a[d].att_flags = pDescriptor->m_properties; + uint8_t d = 0; + for(auto dsc_it = (*chr_it)->m_dscVec.begin(); dsc_it != (*chr_it)->m_dscVec.end(); ++dsc_it ) { + if((*dsc_it)->m_removed > 0) { + continue; + } + pDsc_a[d].uuid = &(*dsc_it)->m_uuid.getNative()->u; + pDsc_a[d].att_flags = (*dsc_it)->m_properties; pDsc_a[d].min_key_size = 0; pDsc_a[d].access_cb = NimBLEDescriptor::handleGapEvent; - pDsc_a[d].arg = pDescriptor; - d++; - pDescriptor = *(pCharacteristic->m_dscVec.begin() + d); + pDsc_a[d].arg = (*dsc_it); + ++d; } pDsc_a[numDscs].uuid = NULL; pChr_a[i].descriptors = pDsc_a; } - pChr_a[i].uuid = &pCharacteristic->m_uuid.getNative()->u; + pChr_a[i].uuid = &(*chr_it)->m_uuid.getNative()->u; pChr_a[i].access_cb = NimBLECharacteristic::handleGapEvent; - pChr_a[i].arg = pCharacteristic; - pChr_a[i].flags = pCharacteristic->m_properties; + pChr_a[i].arg = (*chr_it); + pChr_a[i].flags = (*chr_it)->m_properties; pChr_a[i].min_key_size = 0; - pChr_a[i].val_handle = &pCharacteristic->m_handle; - i++; - pCharacteristic = *(m_chrVec.begin() + i); + pChr_a[i].val_handle = &(*chr_it)->m_handle; + ++i; } pChr_a[numChrs].uuid = NULL; @@ -187,7 +227,7 @@ bool NimBLEService::start() { m_pSvcDef = svc; } - rc = ble_gatts_count_cfg((const ble_gatt_svc_def*)m_pSvcDef); + int rc = ble_gatts_count_cfg((const ble_gatt_svc_def*)m_pSvcDef); if (rc != 0) { NIMBLE_LOGE(LOG_TAG, "ble_gatts_count_cfg failed, rc= %d, %s", rc, NimBLEUtils::returnCodeToString(rc)); return false; @@ -239,17 +279,63 @@ NimBLECharacteristic* NimBLEService::createCharacteristic(const NimBLEUUID &uuid std::string(uuid).c_str()); } - // Remember this characteristic in our vector of characteristics. - m_chrVec.push_back(pCharacteristic); - + addCharacteristic(pCharacteristic); return pCharacteristic; } // createCharacteristic +/** + * @brief Add a characteristic to the service. + * @param[in] pCharacteristic A pointer to the characteristic instance to add to the service. + */ void NimBLEService::addCharacteristic(NimBLECharacteristic* pCharacteristic) { + bool foundRemoved = false; + + if(pCharacteristic->m_removed > 0) { + for(auto& it : m_chrVec) { + if(it == pCharacteristic) { + foundRemoved = true; + pCharacteristic->m_removed = 0; + } + } + } + + if(!foundRemoved) { + m_chrVec.push_back(pCharacteristic); + } + pCharacteristic->setService(this); - m_chrVec.push_back(pCharacteristic); -} + getServer()->serviceChanged(); +} // addCharacteristic + + +/** + * @brief Remove a characteristic from the service. + * @param[in] pCharacteristic A pointer to the characteristic instance to remove from the service. + * @param[in] deleteChr If true it will delete the characteristic instance and free it's resources. + */ +void NimBLEService::removeCharacteristic(NimBLECharacteristic* pCharacteristic, bool deleteChr) { + // Check if the characteristic was already removed and if so, check if this + // is being called to delete the object and do so if requested. + // Otherwise, ignore the call and return. + if(pCharacteristic->m_removed > 0) { + if(deleteChr) { + for(auto it = m_chrVec.begin(); it != m_chrVec.end(); ++it) { + if ((*it) == pCharacteristic) { + m_chrVec.erase(it); + delete *it; + break; + } + } + } + + return; + } + + pCharacteristic->m_removed = deleteChr ? NIMBLE_ATT_REMOVE_DELETE : NIMBLE_ATT_REMOVE_HIDE; + getServer()->serviceChanged(); +} // removeCharacteristic + /** * @brief Get a pointer to the characteristic object with the specified UUID. diff --git a/src/NimBLEService.h b/src/NimBLEService.h index 57e92c0..ebf913d 100644 --- a/src/NimBLEService.h +++ b/src/NimBLEService.h @@ -60,6 +60,7 @@ public: NIMBLE_PROPERTY::WRITE); void addCharacteristic(NimBLECharacteristic* pCharacteristic); + void removeCharacteristic(NimBLECharacteristic* pCharacteristic, bool deleteChr = false); NimBLECharacteristic* getCharacteristic(const char* uuid, uint16_t instanceId = 0); NimBLECharacteristic* getCharacteristic(const NimBLEUUID &uuid, uint16_t instanceId = 0); NimBLECharacteristic* getCharacteristicByHandle(uint16_t handle);