@@ -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;
@@ -955,7 +978,27 @@ int BLECharacteristic::handleGATTServerEvent(uint16_t conn_handle, uint16_t attr
955978 rc = ble_gap_conn_find (conn_handle, &desc);
956979 assert (rc == 0 );
957980 pCharacteristic->setValue (buf, len);
958- pCharacteristic->m_pCallbacks ->onWrite (pCharacteristic, &desc);
981+ 982+ // Defer the onWrite callback to maintain backwards compatibility with Bluedroid.
983+ // In Bluedroid, the write response is sent BEFORE the onWrite callback is invoked.
984+ // In NimBLE, the response is sent implicitly when this function returns.
985+ // By deferring the callback to a separate task, we ensure the response is sent first.
986+ // See: https://github.com/espressif/arduino-esp32/issues/11938
987+ DeferredWriteCallback *pCallback = new DeferredWriteCallback ();
988+ pCallback->pCharacteristic = pCharacteristic;
989+ pCallback->desc = desc;
990+ 991+ // Create a one-shot task to execute the callback after the response is sent
992+ // Using priority 1 (low priority) and sufficient stack for callback operations
993+ // Note: Stack must be large enough to handle notify() calls from within onWrite()
994+ xTaskCreate (
995+ processDeferredWriteCallback,
996+ " BLEWriteCB" ,
997+ 4096 , // Stack size - increased to handle notify() operations
998+ pCallback,
999+ 1 , // Priority (low)
1000+ NULL // Task handle (not needed for one-shot task)
1001+ );
9591002
9601003 return 0 ;
9611004 }
0 commit comments