nkolban / esp32-snippets

Sample ESP32 snippets and code fragments
https://leanpub.com/kolban-ESP32
Apache License 2.0
2.37k stars 710 forks source link

VERY STRANGE behavior with multiple services and characteristic with NOTIFY #1151

Closed Sladerix closed 1 year ago

Sladerix commented 1 year ago

Hi everyone, its the first time I make a project that involve using more than one service.

This is the setup

Service A

pServiceA = pServer->createService(BLEUUID(SERVICE_A_UUID), 6, 0);
CharacteristicA = pServiceA->createCharacteristic(UUID_C_A, BLECharacteristic::PROPERTY_WRITE);
CharacteristicA->setCallbacks(new accessCodeCallbacks());

CharacteristicB = pServiceA->createCharacteristic(UUID_C_B, BLECharacteristic::PROPERTY_NOTIFY | BLECharacteristic::PROPERTY_READ);
CharacteristicB->addDescriptor(new BLE2902());

pServiceA->start();    

Service B

pServiceB = pServer->createService(BLEUUID(LEDSERVICE_UUID), 17, 0);
led1Characteristic = pServiceB->createCharacteristic(LED_1_UUID,
                                                       BLECharacteristic::PROPERTY_WRITE |
                                                       BLECharacteristic::PROPERTY_READ);

led2Characteristic = pServiceB->createCharacteristic(LED_2_UUID,
                                                       BLECharacteristic::PROPERTY_WRITE |
                                                       BLECharacteristic::PROPERTY_READ);

// .... 8 identical characteristics with different UUIDs

// !!!! IMPORTANT !!!! This service is not started!
// pServiceB->start()     <---- Not called

Now.. what's the problem?

How you can see CharacteristicB has the notify property because I want to asynchronously update the value on client-side... WELL when the client (e.g. any smartphone with "nRF Connect" app) subscribes to receive updates, and I call

CharacteristicB->setValue("Test");
CharacteristicB->notify();

the ESP32 crashes O_O....

BUT if from the app I DON'T subscribe to receive updates it does NOT crash!!!!! and I can tap on "read characteristic" button with out any problem.

The problem occurs only if I call ->notify() when the client has subscribed to receive the notifications.. otherwise everything works fine.

Even stranger thing: Why I said "with multiple services"?

Well.. if I delete from the source code everything related to the ServiceB, everything start working properly, the client receives notification from CharacteristicB of ServiceA without the ESP crashing

Other informations

I tried to start also the ServiceB from the beginning.. and in this way it works... seems that is not possibile to have some services started and other stopped... is this true?? Because I need to start the ServiceB in another moment... not from the boot.

WTH is happening? Anyone has worked with multiple services and notifications?? I hope to find help because this thing is very frustrating 😭

Sladerix commented 1 year ago

Ok. I found a workaround.. Instead of calling only pServiceB->start() in a second moment, I create and start ALL the service, in a second moment.

Setup BLE function to advertise ONE service initially

void setupBLE(std::string device_name) {
    BLEDevice::init(device_name);
    pServer = BLEDevice::createServer(); 
    pServer->setCallbacks(new MyServerCallbacks()); 

    pService = pServer->createService(BLEUUID(SERVICE_UUID), 6, 0);
    {
      accesscodeCharacteristic = pService->createCharacteristic(ACCESS_CODE_UUID, BLECharacteristic::PROPERTY_WRITE);
      accesscodeCharacteristic->setCallbacks(new accessCodeCallbacks());

      accesscoderesultCharacteristic = pService->createCharacteristic(ACCESS_CODE_RESULT_UUID, BLECharacteristic::PROPERTY_NOTIFY | BLECharacteristic::PROPERTY_READ);
      accesscoderesultCharacteristic->addDescriptor(new BLE2902()); 
    }
    pService->start(); 

    BLEAdvertising *pAdvertising = BLEDevice::getAdvertising();
    BLEAdvertisementData pAdvertisementData;
    pAdvertisementData.setManufacturerData(getBluetoothAddress());
    pAdvertisementData.setAppearance(ESP_BLE_APPEARANCE_GENERIC_COMPUTER);
    pAdvertisementData.setName(device_name);

    pAdvertising->setAdvertisementData(pAdvertisementData);
    pAdvertising->addServiceUUID(BLEUUID(SERVICE_UUID));
    pAdvertising->setScanResponse(true);
    pAdvertising->setMinPreferred(0x06); 
    pAdvertising->setMinPreferred(0x12);

    Serial.println("Attendo connessione di un client...");
}

Then I've two function, one to start the second service when I want, and the other to stop it.

Start the second service

void startLedService(){
  pLedService = pServer->createService(BLEUUID(LEDSERVICE_UUID), 18, 0);
  {
    led1Characteristic = pLedService->createCharacteristic(LED_1_UUID,
                                                          BLECharacteristic::PROPERTY_WRITE |
                                                          BLECharacteristic::PROPERTY_READ);

    led2Characteristic = pLedService->createCharacteristic(LED_2_UUID,
                                                          BLECharacteristic::PROPERTY_WRITE |
                                                          BLECharacteristic::PROPERTY_READ);

    led3Characteristic = pLedService->createCharacteristic(LED_3_UUID,
                                                          BLECharacteristic::PROPERTY_WRITE |
                                                          BLECharacteristic::PROPERTY_READ);

    led1Characteristic->setCallbacks(new ledCallbacks());
    led2Characteristic->setCallbacks(new ledCallbacks());
    led3Characteristic->setCallbacks(new ledCallbacks());
  }
  pLedService->start();

  BLEAdvertising *pAdvertising = BLEDevice::getAdvertising();
  pAdvertising->addServiceUUID(BLEUUID(LEDSERVICE_UUID));

  leds_started = true; 
}

Stop the second service

void stopLedService(){
  pLedService->executeDelete();

  leds_started = false; 
}