@@ -904,6 +904,29 @@ void BLECharacteristicCallbacks::onWrite(BLECharacteristic *pCharacteristic, esp
904904
905905#if defined(CONFIG_NIMBLE_ENABLED)
906906
907+ /* *
908+ * @brief Process a deferred write callback.
909+ *
910+ * This function is called as a FreeRTOS task to execute the onWrite callback
911+ * after the write response has been sent to the client. This maintains backwards
912+ * compatibility with Bluedroid, where the write response is sent before the
913+ * onWrite callback is invoked.
914+ *
915+ * See: https://github.com/espressif/arduino-esp32/issues/11938
916+ */
917+ void BLECharacteristic::processDeferredWriteCallback (void *pvParameters) {
918+ DeferredWriteCallback *pCallback = (DeferredWriteCallback *)pvParameters;
919+
920+ // Call the onWrite callback now that the response has been sent
921+ pCallback->pCharacteristic ->m_pCallbacks ->onWrite (pCallback->pCharacteristic , &pCallback->desc );
922+
923+ // Free the allocated memory
924+ delete pCallback;
925+
926+ // Delete this one-shot task
927+ vTaskDelete (NULL );
928+ }
929+
907930int BLECharacteristic::handleGATTServerEvent (uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt *ctxt, void *arg) {
908931 const ble_uuid_t *uuid;
909932 int rc;
@@ -958,14 +981,27 @@ int BLECharacteristic::handleGATTServerEvent(uint16_t conn_handle, uint16_t attr
958981 // Set write context flag to defer notifications
959982 pCharacteristic->m_inWriteContext = true ;
960983 pCharacteristic->setValue (buf, len);
961- pCharacteristic->m_pCallbacks ->onWrite (pCharacteristic, &desc);
962- pCharacteristic->m_inWriteContext = false ;
963984
964- // Execute any deferred notifications after write context ends
965- if (pCharacteristic->m_deferNotifications ) {
966- pCharacteristic->m_deferNotifications = false ;
967- pCharacteristic->notifyDeferred ();
968- }
985+ // Defer the onWrite callback to maintain backwards compatibility with Bluedroid.
986+ // In Bluedroid, the write response is sent BEFORE the onWrite callback is invoked.
987+ // In NimBLE, the response is sent implicitly when this function returns.
988+ // By deferring the callback to a separate task, we ensure the response is sent first.
989+ // See: https://github.com/espressif/arduino-esp32/issues/11938
990+ DeferredWriteCallback *pCallback = new DeferredWriteCallback ();
991+ pCallback->pCharacteristic = pCharacteristic;
992+ pCallback->desc = desc;
993+
994+ // Create a one-shot task to execute the callback after the response is sent
995+ // Using priority 1 (low priority) and sufficient stack for callback operations
996+ // Note: Stack must be large enough to handle notify() calls from within onWrite()
997+ xTaskCreate (
998+ processDeferredWriteCallback,
999+ " BLEWriteCB" ,
1000+ 4096 , // Stack size - increased to handle notify() operations
1001+ pCallback,
1002+ 1 , // Priority (low)
1003+ NULL // Task handle (not needed for one-shot task)
1004+ );
9691005
9701006 return 0 ;
9711007 }
0 commit comments