3939static const char * LOG_TAG = " NimBLEServer" ;
4040static NimBLEServerCallbacks defaultCallbacks;
4141
42+ struct gattRegisterCallbackArgs {
43+ NimBLEService* pSvc{nullptr };
44+ NimBLECharacteristic* pChar{nullptr };
45+ };
46+
4247/* *
4348 * @brief Construct a BLE Server
4449 *
@@ -188,16 +193,98 @@ void NimBLEServer::serviceChanged() {
188193 }
189194} // serviceChanged
190195
196+ /* *
197+ * @brief Callback for GATT registration events,
198+ * used to obtain the assigned handles for services, characteristics, and descriptors.
199+ * @param [in] ctxt The context of the registration event.
200+ * @param [in] arg Unused.
201+ */
202+ void NimBLEServer::gattRegisterCallback (ble_gatt_register_ctxt* ctxt, void * arg) {
203+ gattRegisterCallbackArgs* args = static_cast <gattRegisterCallbackArgs*>(arg);
204+
205+ if (ctxt->op == BLE_GATT_REGISTER_OP_SVC ) {
206+ NimBLEUUID uuid (ctxt->svc .svc_def ->uuid );
207+ args->pSvc = nullptr ;
208+ for (size_t i = 0 ; i < NimBLEDevice::getServer ()->m_svcVec .size (); i++) {
209+ auto pSvc = NimBLEDevice::getServer ()->getServiceByUUID (uuid, i);
210+ if (pSvc != nullptr && pSvc->m_handle == 0 ) {
211+ pSvc->m_handle = ctxt->svc .handle ;
212+ NIMBLE_LOGD (LOG_TAG , " Service registered: %s, handle=%d" , uuid.toString ().c_str (), ctxt->svc .handle );
213+ // Set the arg to the service so we know that the following
214+ // characteristics and descriptors belong to this service
215+ args->pSvc = pSvc;
216+ break ;
217+ }
218+ }
219+
220+ return ;
221+ }
222+
223+ if (args->pSvc == nullptr ) {
224+ // If the service is not found then this is likely a characteristic or descriptor that was registered as
225+ // part of the GATT server setup and not found in the service vector
226+ NIMBLE_LOGD (LOG_TAG , " Skipping characteristic or descriptor registered with unknown service" );
227+ return ;
228+ }
229+
230+ if (ctxt->op == BLE_GATT_REGISTER_OP_CHR ) {
231+ NimBLEUUID uuid (ctxt->chr .chr_def ->uuid );
232+ args->pChar = nullptr ;
233+ for (size_t i = 0 ; i < args->pSvc ->m_vChars .size (); i++) {
234+ auto pChr = args->pSvc ->getCharacteristic (uuid, i);
235+ if (pChr != nullptr && pChr->m_handle == 0 ) {
236+ pChr->m_handle = ctxt->chr .val_handle ;
237+ // Set the arg to the characteristic so we know that the following descriptors belong to this characteristic
238+ args->pChar = pChr;
239+ NIMBLE_LOGD (LOG_TAG ,
240+ " Characteristic registered: %s, def_handle=%d, val_handle=%d" ,
241+ uuid.toString ().c_str (),
242+ ctxt->chr .def_handle ,
243+ ctxt->chr .val_handle );
244+ break ;
245+ }
246+ }
247+
248+ return ;
249+ }
250+
251+ if (ctxt->op == BLE_GATT_REGISTER_OP_DSC ) {
252+ if (args->pChar == nullptr ) {
253+ NIMBLE_LOGE (LOG_TAG , " Descriptor registered with unknown characteristic, skipping" );
254+ return ;
255+ }
256+
257+ NimBLEUUID uuid (ctxt->dsc .dsc_def ->uuid );
258+ for (auto i = 0 ; i < args->pChar ->m_vDescriptors .size (); i++) {
259+ auto pDsc = args->pChar ->getDescriptorByUUID (uuid, i);
260+ if (pDsc != nullptr && pDsc->m_handle == 0 ) {
261+ pDsc->m_handle = ctxt->dsc .handle ;
262+ NIMBLE_LOGD (LOG_TAG , " Descriptor registered: %s, handle=%d" , uuid.toString ().c_str (), ctxt->dsc .handle );
263+ return ;
264+ }
265+ }
266+ }
267+ }
268+
191269/* *
192270 * @brief Start the GATT server.
193271 * @details Required to be called after setup of all services and characteristics / descriptors
194272 * for the NimBLE host to register them.
195273 */
196274void NimBLEServer::start () {
275+ if (m_svcChanged && !getConnectedCount ()) {
276+ NIMBLE_LOGD (LOG_TAG , " Services have changed since last start, resetting GATT server" );
277+ resetGATT ();
278+ }
279+
197280 if (m_gattsStarted) {
198281 return ; // already started
199282 }
200283
284+ ble_hs_cfg.gatts_register_cb = NimBLEServer::gattRegisterCallback;
285+ gattRegisterCallbackArgs args{};
286+ ble_hs_cfg.gatts_register_arg = &args;
287+
201288 int rc = ble_gatts_start ();
202289 if (rc != 0 ) {
203290 NIMBLE_LOGE (LOG_TAG , " ble_gatts_start; rc=%d, %s" , rc, NimBLEUtils::returnCodeToString (rc));
@@ -206,29 +293,20 @@ void NimBLEServer::start() {
206293
207294# if MYNEWT_VAL(NIMBLE_CPP_LOG_LEVEL) >= 4
208295 ble_gatts_show_local ();
209- # endif
210296
211- // Get the assigned service handles and build a vector of characteristics
212- // with Notify / Indicate capabilities for event handling
297+ // Check that all services were registered and log if any are missing.
213298 for (const auto & svc : m_svcVec) {
214299 if (svc->getRemoved () == 0 ) {
215- rc = ble_gatts_find_svc (svc->getUUID ().getBase (), &svc-> m_handle );
300+ rc = ble_gatts_find_svc (svc->getUUID ().getBase (), NULL );
216301 if (rc != 0 ) {
217- NIMBLE_LOGW (LOG_TAG ,
302+ NIMBLE_LOGD (LOG_TAG ,
218303 " GATT Server started without service: %s, Service %s" ,
219304 svc->getUUID ().toString ().c_str (),
220305 svc->isStarted () ? " missing" : " not started" );
221- continue ; // Skip this service as it was not started
222- }
223- }
224-
225- // Set the descriptor handles now as the stack does not set these when the service is started
226- for (const auto & chr : svc->m_vChars ) {
227- for (auto & desc : chr->m_vDescriptors ) {
228- ble_gatts_find_dsc (svc->getUUID ().getBase (), chr->getUUID ().getBase (), desc->getUUID ().getBase (), &desc->m_handle );
229306 }
230307 }
231308 }
309+ # endif
232310
233311 // If the services have changed indicate it now
234312 if (m_svcChanged) {
@@ -427,10 +505,6 @@ int NimBLEServer::handleGapEvent(ble_gap_event* event, void* arg) {
427505 }
428506# endif
429507
430- if (pServer->m_svcChanged ) {
431- pServer->resetGATT ();
432- }
433-
434508 peerInfo.m_desc = event->disconnect .conn ;
435509 pServer->m_pServerCallbacks ->onDisconnect (pServer, peerInfo, event->disconnect .reason );
436510# if !MYNEWT_VAL(BLE_EXT_ADV)
@@ -784,32 +858,60 @@ void NimBLEServer::addService(NimBLEService* service) {
784858 * @brief Resets the GATT server, used when services are added/removed after initialization.
785859 */
786860void NimBLEServer::resetGATT () {
787- if (getConnectedCount () > 0 ) {
788- return ;
789- }
790-
791861# if MYNEWT_VAL(BLE_ROLE_BROADCASTER)
792862 NimBLEDevice::stopAdvertising ();
793863# endif
864+
794865 ble_gatts_reset ();
795866 ble_svc_gap_init ();
796867 ble_svc_gatt_init ();
797868
798- for ( auto it = m_svcVec. begin (); it != m_svcVec. end ();) {
799- if ((*it)-> getRemoved () > 0 ) {
800- if ((*it)-> getRemoved () == NIMBLE_ATT_REMOVE_DELETE ) {
801- delete *it;
802- it = m_svcVec. erase (it) ;
803- } else {
804- ++it ;
805- }
869+ // We clear the flag so the services can be started in the loop below
870+ m_svcChanged = false ;
871+
872+ for ( auto svcIt = m_svcVec. begin (); svcIt != m_svcVec. end ();) {
873+ auto * pSvc = *svcIt ;
874+ if (pSvc-> getRemoved () == NIMBLE_ATT_REMOVE_DELETE ) {
875+ delete pSvc ;
876+ svcIt = m_svcVec. erase (svcIt);
806877 continue ;
807878 }
808879
809- (*it)->start ();
810- ++it;
880+ for (auto chrIt = pSvc->m_vChars .begin (); chrIt != pSvc->m_vChars .end ();) {
881+ auto * pChr = *chrIt;
882+ if (pChr->getRemoved () == NIMBLE_ATT_REMOVE_DELETE ) {
883+ delete pChr;
884+ chrIt = pSvc->m_vChars .erase (chrIt);
885+ continue ;
886+ }
887+
888+ for (auto dscIt = pChr->m_vDescriptors .begin (); dscIt != pChr->m_vDescriptors .end ();) {
889+ auto * pDsc = *dscIt;
890+ if (pDsc->getRemoved () == NIMBLE_ATT_REMOVE_DELETE ) {
891+ delete pDsc;
892+ dscIt = pChr->m_vDescriptors .erase (dscIt);
893+ continue ;
894+ }
895+
896+ pDsc->m_handle = 0 ;
897+ ++dscIt;
898+ }
899+
900+ pChr->m_handle = 0 ;
901+ ++chrIt;
902+ }
903+
904+ if (pSvc->getRemoved () == 0 ) {
905+ pSvc->start ();
906+ }
907+
908+ pSvc->m_handle = 0 ;
909+ ++svcIt;
811910 }
812911
912+ // Restore the flag to indicate the services have changed so that the
913+ // GATT server start will send a service changed indication.
914+ m_svcChanged = true ;
813915 m_gattsStarted = false ;
814916} // resetGATT
815917
0 commit comments