espressif / arduino-esp32

Arduino core for the ESP32
GNU Lesser General Public License v2.1
13.66k stars 7.42k forks source link

BLE Client Characteristic Configuration descriptor (BLE2902) value is not resetting to default (00 00) on connection #6863

Closed SuperTankMan closed 2 years ago

SuperTankMan commented 2 years ago

Board

az-delivery-devkit-v4

Device Description

ESP32 with stepper motor and few basic level sensors.

Hardware Configuration

Not relevant

Version

v2.0.2

IDE Name

Arduino IDE and PlatformIO

Operating System

All.

Flash frequency

80-240Mhz

PSRAM enabled

no

Upload speed

115200

Description

Hi All, this is my first discussion post so please excuse my manners :p

Recently I have been working on ESP32 BLE where the server sets up a service in which the attributes are read, Write, Notify. hence a BLE2902 is added to the characteristics. Once the client registers for the notification and then after a few notifications being received the client disconnect from the server without disabling notifications.

I have noticed that when the client makes connection again (without being bonded) the notification is still enabled! however the Bluetooth specifications specifically state that it should be reset to default value of notifications and Indications i.e disabled when not bonded. Anyone can test this using nRF connect and a standard service or useing BLE_notify.ino from the examples which has characteristics with notification.

Test procedure: 1.0 Connect to the device. 2.0 Enable Notification on a characteristic. 3.0 Disconnect from the device but not reset. 4.0 Connect to the device without power cycle or rest. 5.0 Check the status of the notification by reading value of BLE2902 it should reset to 00 00 or notification and indication disabled (see ref below) but it does not.

Has anyone noticed this and if so is this how ESP32 BLE is supposed to behave? or is it a bug or an issue?

TankMan-

References: BLUETOOTH SPECIFICATION Version 4.2 [Vol 0]

3.3.3.3 Client Characteristic Configuration

The Client Characteristic Configuration declaration is an optional characteristic descriptor that defines how the characteristic may be configured by a specific client. The Client Characteristic Configuration descriptor value shall be persistent across connections for bonded devices. The Client Characteristic Configuration descriptor value shall be set to the default value at each connection with non-bonded devices. The characteristic descriptor value is a bit field. When a bit is set, that action shall be enabled, otherwise it will not be used. The Client Characteristic Configuration descriptor may occur in any position within the characteristic definition after the Characteristic Value. Only one Client Characteristic Configuration declaration shall exist in a characteristic definition.

Extracted from further down the spec. near Table 3.11 in the specification Configuration Value Description Notification 0x0001 The Characteristic Value shall be notified. This value can only be set if the characteristic’s property has the notify bit set. Indication 0x0002 The Characteristic Value shall be indicated. This value can only be set if the characteristic’s property has the indicate bit set. Reserved for Future Use The following Client Characteristic Configuration bits are defined: The default value for the Client Characteristic Configuration descriptor value shall be 0x0000.

Sketch

Use the BLE_notify.ino example and follow the instructions above

Debug Message

None

Other Steps to Reproduce

Use nRF connect or similar to register for notification or read BLE2902 values.

I have checked existing issues, online documentation and the Troubleshooting Guide

SuGlider commented 2 years ago

@SuperTankMan

You can always set it to zero when the BLE server finds out that the client has disconnected (in a callback). This is up to your application to define how to behave.

For that you can do something like this:

    BLE2902 *ble2902 = new BLE2902();
    ....
    ble2902->setNotifications(false);  // or true to turn it on

Check some the BLE2902 API at https://github.com/espressif/arduino-esp32/blob/master/libraries/BLE/src/BLE2902.h

SuperTankMan commented 2 years ago

@SuGlider Hi SuGlider I have already tried this: ble2902->setNotifications(false); // or true to turn it on and it too does not work although internally getNotifications(bool) does return the value I set using setNotifications(bool) however it does not get reflected to the Client! i.e. when you read BLE2902 it still contains 01 00 when it has been set using setNotifications(true). I need to raise an issue with this too! Thanks for your advice though.

SuGlider commented 2 years ago

@SuperTankMan

Could you please provide minimum example code that demonstrates the issue, for both master and slave.

I'll test it and verify/fix this issue.

SuperTankMan commented 2 years ago

@SuGlider Here is the code: save the code below as xxxxx.ino I don't know if there is any other way to send you the file. Note this file can be used to test both issue... and you need a utility like nRF connect or BLE scanner. Just found out how I can make the files easyer for you...

https://github.com/SuperTankMan/Issue-code

Issue #6863 - As it is just compile and see that fit the LED does not flash. Then use somethign like nRF connect or BLE Scanner mobile app to connect, select service 0x1700 and register the notification. Once the counter starts updating in the characteristics values and LED starts flashing disconnect without deregistering notifications. Then connect again and you'll see that the counter will continue counting and LED flashing without reregistering for the notifications(if the notifications and indications were set to default 0x0000 then this would not happen) also you can read the BLE2902 values using nRF or BLE scanner. Issue #6868 - Uncomment the line in code as below and do the test again youll see that after a disconnect even though the code setNotification(false) the update to the nRF or BLE connect does not happen on reconnect of device however the getNotification works as setNotification(false) seasm to work internally only but does not update the BLE2902 to the client.


//*((BLE2902)myBLE2902)->setNotifications(false); //** Uncomment above line to test if setNotifications(false) updates the Client ****/

//code file starts here... `

include

include

include

include

define LED_BUILTIN 26

/ Define Service / //Custome service UUID

define serviceID BLEUUID((uint16_t)0x1700)

BLEDescriptor *myBLE2902 = new BLE2902(); bool deviceConnected = false;

uint8_t counter = 1; unsigned long previousMillis = 0; // will store last time LED was updated const long interval = 1000; // interval at which to blink (milliseconds)

BLECharacteristic counterCharacteristic { BLEUUID((uint16_t)0x1A00), BLECharacteristic::PROPERTY_READ | BLECharacteristic::PROPERTY_NOTIFY };

class MyServerCallbacks : public BLEServerCallbacks { void onConnect(BLEServer MyServer) { deviceConnected = true; } void onDisconnect(BLEServer MyServer) { deviceConnected = false; MyServer->getAdvertising()->start(); } };

void setup() { pinMode(LED_BUILTIN, OUTPUT);

// Serial.begin(115200);

//Create and name the BLE device BLEDevice::init("ESP32BLETest");

/ Create the BLE server / BLEServer *MyServer = BLEDevice::createServer(); MyServer->setCallbacks(new MyServerCallbacks()); // Set the function that handles Server Callbacks

/ Create service on the server / //BLEService MyService = MyServer->createService(BLEUUID((uint16_t)0x1700)); // A random ID has been selected BLEService MyService = MyServer->createService(serviceID); // A random ID has been selected

/ Add characteristic to the service / MyService->addCharacteristic(&counterCharacteristic); counterCharacteristic.addDescriptor(myBLE2902); counterCharacteristic.setValue(&counter, 2);

/ Configure advertising with the service to advertise / MyServer->getAdvertising()->addServiceUUID(serviceID);

// Start the service MyService->start();

// Start the Server/Advertising MyServer->getAdvertising()->start();

Serial.println("Waiting for a Client to connect..."); }

void loop() { digitalWrite(LED_BUILTIN, HIGH);

if (!deviceConnected) { //Not connected so reset Notifications //((BLE2902*)myBLE2902)->setNotifications(false); // Uncomment above line to test if setNotifications(false) updates the Client / } else { //if registered for Notification then flash LED and send counter value to Client by notification if (((BLE2902*)myBLE2902)->getNotifications()) { //loop to blink without delay unsigned long currentMillis = millis(); if (currentMillis - previousMillis >= interval) { // save the last time you blinked the LED previousMillis = currentMillis; digitalWrite(LED_BUILTIN, !digitalRead(LED_BUILTIN)); counter++; counterCharacteristic.setValue(&counter, 1); counterCharacteristic.notify();
} } } } `

SuGlider commented 2 years ago

@SuperTankMan

Issue https://github.com/espressif/arduino-esp32/issues/6863 - As it is just compile and see that fit the LED does not flash. Then use somethign like nRF connect or BLE Scanner mobile app to connect, select service 0x1700 and register the notification. Once the counter starts updating in the characteristics values and LED starts flashing disconnect without deregistering notifications. Then connect again and you'll see that the counter will continue counting and LED flashing without reregistering for the notifications(if the notifications and indications were set to default 0x0000 then this would not happen) also you can read the BLE2902 values using nRF or BLE scanner. Issue https://github.com/espressif/arduino-esp32/issues/6868 - Uncomment the line in code as below and do the test again youll see that after a disconnect even though the code setNotification(false) the update to the nRF or BLE connect does not happen on reconnect of device however the getNotification works as setNotification(false) seasm to work internally only but does not update the BLE2902 to the client.

onDisconnect(BLEServer* MyServer) is actually never called when the Disconnect button is pressed in the BLE Client APP. Thus, deviceConnected is never false, except when it runs by first time before any connection is started. In order to really disconnect you must shut off the Bluetooth radio of the smartphone or go out of range. When uncommenting that code line, I see that it works as expected, when reconnected.

The counter also stops and starts whenever pressing the Notify button in the BLE Client APP.
Therefore the client is capable of controlling the CCCD in the server.

Changing CCCD in the server doesn't affect the client because in BLE the client is who should control CCCD. I think that the client BLE APP really doesn't care and does not actually read the descriptor.

Therefore calling BLE2902::setNotifications(bool) only changes its bit internally to the server, nothing else. But calling BLE2902::setNotifications(bool) before starting advertising the server will work as default setup for CCCD in the pairing or bonding, allowing the server to persist CCCD state across connections for bonded devices, for example. For such case, the server sketch shall save CCCD state in the NVS using "Preferences.h" or some other method, in order to make it persist and then restore its state when the ESP32 is reset, powered on or gets into range again.

I think that the explanation above may cover all the issues you have reported.

SuperTankMan commented 2 years ago

onDisconnect(BLEServer* MyServer) is actually never called when the Disconnect button is pressed in the BLE Client APP. Thus, deviceConnected is never false, except when it runs by first time before any connection is started. In order to really disconnect you must shut off the Bluetooth radio of the smartphone or go out of range. When uncommenting that code line, I see that it works as expected, when reconnected.

I am not convinced about this the device stops advertising as soon as its connected and when callback onDisconnect(BLEServer MyServer) is called the advertising starts again when disconnected hence it is calling onDisconnect(BLEServer MyServer). Also the fact that when the line below

//((BLE2902*)myBLE2902)->setNotifications(false); //* Uncomment above line to test if setNotifications(false) updates the Client /

is uncommented then the Server stops sending notifications it means working I have also moved this line to the onDisconnect(BLEServer* MyServer) and that too works.

The counter also stops and starts whenever pressing the Notify button in the BLE Client APP. Therefore the client is capable of controlling the CCCD in the server.

Sure it is able to stop however it takes two registration of notification from the client to enable notification in stead of the one and when the Client reads the status of the BLE2902 i.e. registation and notification status it does not read the correct value.

Changing CCCD in the server doesn't affect the client because in BLE the client is who should control CCCD. I think that the client BLE APP really doesn't care and does not actually read the descriptor.

Sure Client can control but its getting wrong information when reading in the case when setNotification(false) is called() and this not acceptable in the sense that the Bluetooth specification clearly states the behaviour to set its self to default as detailed and reference in the issue.

Therefore calling BLE2902::setNotifications(bool) only changes its bit internally to the server, nothing else.

Sure and it look like that is the case however its not helpful to the Client when he needs to know(by reading BLE2902) the status of the registration of notification as he gets false information.

But calling BLE2902::setNotifications(bool) before starting advertising the server will work as default setup for CCCD in the.. pairing or bonding, allowing the server to persist CCCD state across connections for bonded devices, for example. For such case, the server sketch shall save CCCD state in the NVS using "Preferences.h" or some other method, in order to make it persist and then restore its state when the ESP32 is reset, powered on or gets into range again.

Sure persistence can be achieved as you stated however that is not the issue, the issue is that it's not adhering to the Bluetooth spec. and not updating the BLE2902 when setNotification(fbool) is called

I think that the explanation above may cover all the issues you have reported.

While I appreciate your assistance and help I do not think this issue is resolved I do think its a bug and does not adhere to the specification and hence both the issue still stands.

SuGlider commented 2 years ago

Issue confirmed.

There are actually two issues: ESP32 BLE Server doesn't send back Descriptor when read is requested by BLE Client. ESP32 BLE Client doesn't receive Descriptor data when it is sent from BLE Server.

These functionalities need to be implemented in BLE Library: https://github.com/espressif/arduino-esp32/blob/master/libraries/BLE/src/BLEDescriptor.cpp#L185-L194 https://github.com/espressif/arduino-esp32/blob/master/libraries/BLE/src/BLERemoteDescriptor.cpp#L54-L58

SuperTankMan commented 2 years ago

Hi @SuGlider

I am not so sure if issue: L185-L194(ESP32 BLE Server doesn't send back Descriptor when read is requested by BLE Client.) is completely true.

I have done this experiment where I use one client to connect then register for notifications then I read the value of the BLE2902 descriptor and it reads value correctly as expected. I have also tried another experiment where a client connects then registers for notifications and then disconnects. I then use another client device to connect and read the BLE2902 descriptor and then too again is read correctly. What is not happening is that when I internally setNotifications(true or false) and then read the BLE2902 then it does not read correctly! hence not sure of your description of the issue. Also there is the issue that when server is disconnected from an unbonded/unpaired device the BLE2902 is not reset to its default value of 0x0000.

I am also not sure about L54-L58(ESP32 BLE Client doesn't receive Descriptor data when it is sent from BLE Server) I have not done this experiment with ESP32 being the Client so do not know.

Please clarify to me if I am missing something and also that the implanted cure will resolve both my issues? i.e. 1) BLE2902 descriptor not resetting to default(0x0000) on disconnect and 2) BLE2902 not reflecting the internally set value(via setNotifications(bool)) when a read request is received from the client?

Thank you for your assistance and sorry if I have not understood your cure to be implemented.

SuGlider commented 2 years ago

Although there is no Code in L185-L194 of BLE Server to send back Descriptor information, it seems that it would no be necessary anyway because BLE Client send a Read Descriptor UUID Request with the Frame Fields rsp_needed as false, therefore, the client doesn't want the BLE Server to reply back anyway.

Talking about the issue here posted, reseting to (00 00), it can be done, as said, by software. By default, when BLE2902 is created in the Server, it has Notification ON, so if you need the Client to start with it as OFF, turn it off before Advertising.

SuperTankMan commented 2 years ago

@SuGlider

By default, when BLE2902 is created in the Server, it has Notification ON, so if you need the Client to start with it as OFF, turn it off before Advertising.

The above statement is not true the extracted constructor code from BLE2902 clearly sets it notifications and indications to off. i.e. 0x0000 so it is not creating the BLE2902 with Notifications ON!

BLE2902::BLE2902() : BLEDescriptor(BLEUUID((uint16_t) 0x2902)) { uint8_t data[2] = { 0, 0 }; setValue(data, 2); } // BLE2902

I have done a lot of investigation in this and its not working the way it should I do request you understand the issue and test thoroughly as I have done. Please note the code I have send is a simplified version for people to undestabnd the issue but I have a larger project that I am working on. As I said when I setNotification(false) it does not get reflected to the client when it is read by the client. even when you use one client to register for notification and then disconnect and connect using another client, in case they are caching the value, even then the read value by the other client is the same as the previous client! how can this be unless they are reading and being sent the value?

I think this is a bug and the server is sending a value as you can see in the logs of nRF but its sending the incorrect value. its as if the setNotifications(bool) and getNotifications() use one variable and the when Client reads the BLE2902 it send another different variable which the server reads and writes.

SuGlider commented 2 years ago

What is exactly the BLE Client you are using in the testing?

SuperTankMan commented 2 years ago

We have our own Client coded in Kotlin by another team. I use nRF connect on a tablet to act as a client and ESP32 as a server. but I have also used BLE scanner and they both DO NOT get the correct notification value (i.e BLE2902) on connect (i.e 0x0000) or when I have setNotification(false) before advertising as you suggested. As I mentioned before its as if the internal BLE2902 is not sent when I setNotification(false) (both nRF connect and BLE scanner is available from Android play store)

SuGlider commented 2 years ago

Ok. That means you are using an Android BLE Client. Android doesn't disconnect on the GATT level and the ESP32 never gets notified about this disconnection, therefore, it will never change the 2902 descriptor.

You can verify it by just adding a Serial.println("Received disconnect event") on the onDisconnect() callbcack.

SuperTankMan commented 2 years ago

@SuGlider Hi @Rodrigo I already have MyServer->getAdvertising()->start(); in the onDisconnect() callback. Normally BLE device does not start advertising after a disconnect and I have MyServer->getAdvertising()->start(); in the onDisconnect() and when I disconnect I can see the advertisement starting again and if I remove this line then the advertising does not happen. so clearly onDisconnect() works. Anyways I did as you ask and the log is as below when I use nRF connect to connect and disconnect. Note: that the orignal code has // Serial.begin(115200); its commented out so you have to uncomment this line first then it will work! in case you have tried this Serial.println() as you mentioned and it did not work for you, But it clearly work for me.

void onDisconnect(BLEServer* MyServer) { Serial.println(">>>>>>>> Received disconnect event <<<<<<<<<"); deviceConnected = false; MyServer->getAdvertising()->start(); }

Result log: ets Jul 29 2019 12:21:46

rst:0x1 (POWERON_RESET),boot:0x13 (SPI_FAST_FLASH_BOOT) configsip: 0, SPIWP:0xee clk_drv:0x00,q_drv:0x00,d_drv:0x00,cs0_drv:0x00,hd_drv:0x00,wp_drv:0x00 mode:DIO, clock div:1 load:0x3fff0030,len:1324 ho 0 tail 12 room 4 load:0x40078000,len:13508 load:0x40080400,len:3604 entry 0x400805f0 [.....’êm.um•ÍÁÍ2-hal-cpu.c:211] setCpuFrequencyMhz(): PLL: 480 / 2 = 240 Mhz, APB: 80000000 Hz [ 737][D][BLEDevice.cpp:102] gattServerEventHandler(): gattServerEventHandler [esp_gatt_if: 4] ... Unknown [ 738][D][BLEDevice.cpp:102] gattServerEventHandler(): gattServerEventHandler [esp_gatt_if: 4] ... Unknown [ 745][D][BLEService.cpp:225] addCharacteristic(): Adding characteristic: uuid=00001a00-0000-1000-8000-00805f9b34fb to service: UUID: 00001700-0000-1000-8000-00805f9b34fb, handle: 0x0028 [ 761][I][BLEDevice.cpp:577] getAdvertising(): create advertising [ 767][D][BLEDevice.cpp:579] getAdvertising(): get advertising [ 773][D][BLECharacteristic.cpp:90] executeCreate(): Registering characteristic (esp_ble_gatts_add_char): uuid: 00001a00-0000-1000-8000-00805f9b34fb, service: UUID: 00001700-0000-1000-8000-00805f9b34fb, handle: 0x0028 [ 792][D][BLEDevice.cpp:102] gattServerEventHandler(): gattServerEventHandler [esp_gatt_if: 4] ... Unknown [ 802][D][BLEDevice.cpp:102] gattServerEventHandler(): gattServerEventHandler [esp_gatt_if: 4] ... Unknown [ 812][D][BLEDevice.cpp:102] gattServerEventHandler(): gattServerEventHandler [esp_gatt_if: 4] ... Unknown [ 821][D][BLEDevice.cpp:579] getAdvertising(): get advertising [ 826][D][BLEAdvertising.cpp:199] start(): - advertising service: 00001700-0000-1000-8000-00805f9b34fb Waiting for a Client to connect... [ 839][D][BLEDevice.cpp:579] getAdvertising(): get advertising [ 847][D][BLEAdvertising.cpp:506] handleGAPEvent(): handleGAPEvent [event no: 0] [ 852][D][BLEDevice.cpp:579] getAdvertising(): get advertising [ 858][D][BLEAdvertising.cpp:506] handleGAPEvent(): handleGAPEvent [event no: 1] [ 865][D][BLEDevice.cpp:579] getAdvertising(): get advertising [ 871][D][BLEAdvertising.cpp:506] handleGAPEvent(): handleGAPEvent [event no: 6] [ 10247][D][BLEDevice.cpp:102] gattServerEventHandler(): gattServerEventHandler [esp_gatt_if: 4] ... Unknown [ 10248][D][BLEServer.cpp:365] onConnect(): BLEServerCallbacks [ 10252][D][BLEServer.cpp:366] onConnect(): BLEServerCallbacks [ 10257][D][BLEServer.cpp:367] onConnect(): BLEServerCallbacks [ 10731][D][BLEDevice.cpp:579] getAdvertising(): get advertising [ 10731][D][BLEAdvertising.cpp:506] handleGAPEvent(): handleGAPEvent [event no: 20] [ 10998][D][BLEDevice.cpp:102] gattServerEventHandler(): gattServerEventHandler [esp_gatt_if: 4] ... Unknown [ 10999][D][BLEServer.cpp:378] onMtuChanged(): BLEServerCallbacks [ 11003][D][BLEServer.cpp:379] onMtuChanged(): BLEServerCallbacks [ 11009][D][BLEServer.cpp:380] onMtuChanged(): BLEServerCallbacks [ 11057][D][BLEDevice.cpp:579] getAdvertising(): get advertising [ 11057][D][BLEAdvertising.cpp:506] handleGAPEvent(): handleGAPEvent [event no: 20] [ 16240][D][BLEDevice.cpp:102] gattServerEventHandler(): gattServerEventHandler [esp_gatt_if: 4] ... Unknown >>>>>>>> Received disconnect event <<<<<<<<< [ 16251][D][BLEDevice.cpp:579] getAdvertising(): get advertising [ 16251][D][BLEAdvertising.cpp:199] start(): - advertising service: 00001700-0000-1000-8000-00805f9b34fb [ 16262][D][BLEDevice.cpp:579] getAdvertising(): get advertising [ 16264][D][BLEAdvertising.cpp:506] handleGAPEvent(): handleGAPEvent [event no: 0] [ 16271][D][BLEDevice.cpp:579] getAdvertising(): get advertising [ 16277][D][BLEAdvertising.cpp:506] handleGAPEvent(): handleGAPEvent [event no: 1] [ 16284][D][BLEDevice.cpp:579] getAdvertising(): get advertising [ 16290][D][BLEAdvertising.cpp:506] handleGAPEvent(): handleGAPEvent [event no: 6]

SuGlider commented 2 years ago

Ok. For some reason my nRF Connect APP doesn't send anything when I press disconnect button.

Anyway, I turn off Bluetooth radio in Android and get the same log output as you reported.

About Notification, the issue is because Android and iOS use to cache all services, characteristics and descriptors.

When nRF APP reconnects to ESP32 BLE Server, it doesn't refresh its cache.

In order to force such cache update, it is necessary to use Generic Attribute 0x1801 and Characteristic Service Changed 0x2A05 to indicate it to the client.

SuperTankMan commented 2 years ago

Hi @SuGlider

When nRF APP reconnects to ESP32 BLE Server, it doesn't refresh its cache. New test using two separate Clients do not prove your explanations: I have now tried enabling the notification using nRF connect on tablet#1 then I disconnect from tablet#1 the device under test and then rebooted a fresh tablet#2 and started nRF connect on the new tablet, when I connected the new fresh tablet#2 it showed that the BLE2902 value is 0x0100 meaning the notification is ON! if its a tablet#1 cashing issue how can a fresh tablet#2 which has just rebooted and restarted nRF connect and now see the same state of the BLE2902? which tablet#1 was seeing? i.e. notification ON instead of OFF! i.e. the new client on BLE2902 is also seeing 0x0100 if tablet#1 is cashing then how did tablet#2 get the same BLE2902 values??? I think your explanation is not correct please I urge you to look at this again.

SuGlider commented 2 years ago

Issue confirmed. I'll send a fix soon.

SuGlider commented 2 years ago

Please apply the fix #6919 to your local machine. It solves the issue of BLE2902 not resetting to default (00 00) by using this example (similar to your very first submitted sketch):

#include <BLEDevice.h>
#include <BLEServer.h>
#include <BLEUtils.h>
#include <BLE2902.h>

#define LED_BUILTIN 2

/* Define Service */
//Custome service UUID
#define serviceID BLEUUID((uint16_t)0x1700)

BLE2902 *myBLE2902 = new BLE2902();
bool deviceConnected = false;

uint8_t counter = 1;
unsigned long previousMillis = 0; // will store last time LED was updated
const long interval = 1000; // interval at which to blink (milliseconds)

BLECharacteristic counterCharacteristic
{
  BLEUUID((uint16_t)0x1A00),
  BLECharacteristic::PROPERTY_READ |
  BLECharacteristic::PROPERTY_NOTIFY
};

class MyServerCallbacks : public BLEServerCallbacks
{
    void onConnect(BLEServer* MyServer)
    {
      deviceConnected = true;
    }
    void onDisconnect(BLEServer* MyServer)
    {
      //Not connected so reset Notifications
      myBLE2902->setNotifications(false);
      deviceConnected = false;
      MyServer->getAdvertising()->start();
    }
};

void setup()
{
  pinMode(LED_BUILTIN, OUTPUT);

  // Serial.begin(115200);

  //Create and name the BLE device
  BLEDevice::init("ESP32BLETest");

  /* Create the BLE server */
  BLEServer *MyServer = BLEDevice::createServer();
  MyServer->setCallbacks(new MyServerCallbacks()); // Set the function that handles Server Callbacks

  /* Create service on the server */
  //BLEService *MyService = MyServer->createService(BLEUUID((uint16_t)0x1700)); // A random ID has been selected
  BLEService *MyService = MyServer->createService(serviceID); // A random ID has been selected

  /* Add characteristic to the service */
  MyService->addCharacteristic(&counterCharacteristic);
  counterCharacteristic.addDescriptor(myBLE2902);
  counterCharacteristic.setValue(&counter, 2);

  /* Configure advertising with the service to advertise */
  MyServer->getAdvertising()->addServiceUUID(serviceID);

  // Start the service
  MyService->start();

  // Start the Server/Advertising
  MyServer->getAdvertising()->start();

  Serial.println("Waiting for a Client to connect...");
}

void loop()
{
  digitalWrite(LED_BUILTIN, HIGH);

  if (deviceConnected)
  {
    //if registered for Notification then flash LED and send counter value to Client by notification
    if (myBLE2902->getNotifications())
    {
      //loop to blink without delay
      unsigned long currentMillis = millis();
      if (currentMillis - previousMillis >= interval)
      {
        // save the last time you blinked the LED
        previousMillis = currentMillis;
        digitalWrite(LED_BUILTIN, !digitalRead(LED_BUILTIN));
        counter++;
        counterCharacteristic.setValue(&counter, 1);
        counterCharacteristic.notify();
      }
    }
  }
}