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,96 @@ 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 A pointer to the gattRegisterCallbackArgs struct used to track the
201+ * service and characteristic being registered.
202+ */
203+ void NimBLEServer::gattRegisterCallback (ble_gatt_register_ctxt* ctxt, void * arg) {
204+ gattRegisterCallbackArgs* args = static_cast <gattRegisterCallbackArgs*>(arg);
205+
206+ if (ctxt->op == BLE_GATT_REGISTER_OP_SVC ) {
207+ NimBLEUUID uuid (ctxt->svc .svc_def ->uuid );
208+ args->pSvc = nullptr ;
209+ for (auto pSvc : NimBLEDevice::getServer ()->m_svcVec ) {
210+ if (!pSvc->getRemoved () && pSvc->m_handle == 0 && pSvc->getUUID () == uuid) {
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 (auto pChr : args->pSvc ->m_vChars ) {
234+ if (!pChr->getRemoved () && pChr->m_handle == 0 && pChr->getUUID () == uuid) {
235+ pChr->m_handle = ctxt->chr .val_handle ;
236+ // Set the arg to the characteristic so we know that the following descriptors belong to this characteristic
237+ args->pChar = pChr;
238+ NIMBLE_LOGD (LOG_TAG ,
239+ " Characteristic registered: %s, def_handle=%d, val_handle=%d" ,
240+ uuid.toString ().c_str (),
241+ ctxt->chr .def_handle ,
242+ ctxt->chr .val_handle );
243+ break ;
244+ }
245+ }
246+
247+ return ;
248+ }
249+
250+ if (ctxt->op == BLE_GATT_REGISTER_OP_DSC ) {
251+ if (args->pChar == nullptr ) {
252+ NIMBLE_LOGE (LOG_TAG , " Descriptor registered with unknown characteristic, skipping" );
253+ return ;
254+ }
255+
256+ NimBLEUUID uuid (ctxt->dsc .dsc_def ->uuid );
257+ for (auto pDsc : args->pChar ->m_vDescriptors ) {
258+ if (!pDsc->getRemoved () && pDsc->m_handle == 0 && pDsc->getUUID () == uuid) {
259+ pDsc->m_handle = ctxt->dsc .handle ;
260+ NIMBLE_LOGD (LOG_TAG , " Descriptor registered: %s, handle=%d" , uuid.toString ().c_str (), ctxt->dsc .handle );
261+ return ;
262+ }
263+ }
264+ }
265+ }
266+
191267/* *
192268 * @brief Start the GATT server.
193269 * @details Required to be called after setup of all services and characteristics / descriptors
194270 * for the NimBLE host to register them.
195271 */
196272void NimBLEServer::start () {
273+ if (m_svcChanged && !getConnectedCount ()) {
274+ NIMBLE_LOGD (LOG_TAG , " Services have changed since last start, resetting GATT server" );
275+ resetGATT ();
276+ }
277+
197278 if (m_gattsStarted) {
198279 return ; // already started
199280 }
200281
282+ ble_hs_cfg.gatts_register_cb = NimBLEServer::gattRegisterCallback;
283+ gattRegisterCallbackArgs args{};
284+ ble_hs_cfg.gatts_register_arg = &args;
285+
201286 int rc = ble_gatts_start ();
202287 if (rc != 0 ) {
203288 NIMBLE_LOGE (LOG_TAG , " ble_gatts_start; rc=%d, %s" , rc, NimBLEUtils::returnCodeToString (rc));
@@ -206,29 +291,20 @@ void NimBLEServer::start() {
206291
207292# if MYNEWT_VAL(NIMBLE_CPP_LOG_LEVEL) >= 4
208293 ble_gatts_show_local ();
209- # endif
210294
211- // Get the assigned service handles and build a vector of characteristics
212- // with Notify / Indicate capabilities for event handling
295+ // Check that all services were registered and log if any are missing.
213296 for (const auto & svc : m_svcVec) {
214297 if (svc->getRemoved () == 0 ) {
215- rc = ble_gatts_find_svc (svc->getUUID ().getBase (), &svc-> m_handle );
298+ rc = ble_gatts_find_svc (svc->getUUID ().getBase (), NULL );
216299 if (rc != 0 ) {
217- NIMBLE_LOGW (LOG_TAG ,
300+ NIMBLE_LOGD (LOG_TAG ,
218301 " GATT Server started without service: %s, Service %s" ,
219302 svc->getUUID ().toString ().c_str (),
220303 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 );
229304 }
230305 }
231306 }
307+ # endif
232308
233309 // If the services have changed indicate it now
234310 if (m_svcChanged) {
@@ -427,10 +503,6 @@ int NimBLEServer::handleGapEvent(ble_gap_event* event, void* arg) {
427503 }
428504# endif
429505
430- if (pServer->m_svcChanged ) {
431- pServer->resetGATT ();
432- }
433-
434506 peerInfo.m_desc = event->disconnect .conn ;
435507 pServer->m_pServerCallbacks ->onDisconnect (pServer, peerInfo, event->disconnect .reason );
436508# if !MYNEWT_VAL(BLE_EXT_ADV)
@@ -784,33 +856,55 @@ void NimBLEServer::addService(NimBLEService* service) {
784856 * @brief Resets the GATT server, used when services are added/removed after initialization.
785857 */
786858void NimBLEServer::resetGATT () {
787- if (getConnectedCount () > 0 ) {
788- return ;
789- }
790-
791859# if MYNEWT_VAL(BLE_ROLE_BROADCASTER)
792860 NimBLEDevice::stopAdvertising ();
793861# endif
862+
794863 ble_gatts_reset ();
795864 ble_svc_gap_init ();
796865 ble_svc_gatt_init ();
797866
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- }
867+ m_gattsStarted = false ;
868+
869+ for (auto svcIt = m_svcVec.begin (); svcIt != m_svcVec.end ();) {
870+ auto * pSvc = *svcIt;
871+ if (pSvc->getRemoved () == NIMBLE_ATT_REMOVE_DELETE ) {
872+ delete pSvc;
873+ svcIt = m_svcVec.erase (svcIt);
806874 continue ;
807875 }
808876
809- (*it)->start ();
810- ++it;
811- }
877+ for (auto chrIt = pSvc->m_vChars .begin (); chrIt != pSvc->m_vChars .end ();) {
878+ auto * pChr = *chrIt;
879+ if (pChr->getRemoved () == NIMBLE_ATT_REMOVE_DELETE ) {
880+ delete pChr;
881+ chrIt = pSvc->m_vChars .erase (chrIt);
882+ continue ;
883+ }
812884
813- m_gattsStarted = false ;
885+ for (auto dscIt = pChr->m_vDescriptors .begin (); dscIt != pChr->m_vDescriptors .end ();) {
886+ auto * pDsc = *dscIt;
887+ if (pDsc->getRemoved () == NIMBLE_ATT_REMOVE_DELETE ) {
888+ delete pDsc;
889+ dscIt = pChr->m_vDescriptors .erase (dscIt);
890+ continue ;
891+ }
892+
893+ pDsc->m_handle = 0 ;
894+ ++dscIt;
895+ }
896+
897+ pChr->m_handle = 0 ;
898+ ++chrIt;
899+ }
900+
901+ if (pSvc->getRemoved () == 0 ) {
902+ pSvc->start ();
903+ }
904+
905+ pSvc->m_handle = 0 ;
906+ ++svcIt;
907+ }
814908} // resetGATT
815909
816910/* *
0 commit comments