Esp32-snippets: How many create service in server?

Created on 11 Feb 2020  路  17Comments  路  Source: nkolban/esp32-snippets

Hi ~ Now I'm trying to make a BLE project using esp32.
But I have some error about this line.

pServer->getAdvertising()->start(); <- not working
create service, characteristic is fine..

I'd like to create 1 server that has 7 services
each service has char below
A service -> 2 char
B service -> 6 char
C service -> 1 char
D service -> 2 char
E service -> 7 char
F service -> 2 char
G service -> 2 char

So, I tried to make a code using example project
Have it limit to create service or characteristic?

All 17 comments

If I created 7 services, It couldn't start advertising
Is it limited number of services and characteristics?

Hi,
i remember there was issue here claiming that someone has server with 10 services or so and about 80 characteristics (maybe numbers are not accurate, but something like that).
Recently i found issue on forum about creating some amount of characteristics and i tested it. There is problem somewhere for sure, maybe menuconfig and number of handles.

My suggestion is to create services with this function:
https://github.com/nkolban/esp32-snippets/blob/master/cpp_utils/BLEServer.h#L67
and adjust handles value.
you need 2 handles per characteristic and 1 per descriptor.

@chegewara Thank you for suggestion!
Now I'm using arduino Ide, I tried to adjust handle value each service. But I still have same problem.
Did you tried to any change sdkconfig file?

No, nothing changed in sdkconfig.

@chegewara

the handle value jump 0x0061 to 0xb33f
it's has limited handle values?

```
14:25:00.007 -> [D][BLECharacteristic.cpp:90] executeCreate(): Registering characteristic (esp_ble_gatts_add_char): uuid: 0000ffc3-0000-1000-8000-00805f9b34fb, service: UUID: 0000ffe2-0000-1000-8000-00805f9b34fb, handle: 0x0061
14:25:00.042 -> [D][FreeRTOS.cpp:189] take(): Semaphore taking: name: CreateEvt (0x3ffe77f0), owner: for executeCreate
14:25:00.042 -> [D][FreeRTOS.cpp:198] take(): Semaphore taken: name: CreateEvt (0x3ffe77f0), owner: executeCreate
14:25:00.076 -> [D][BLEDevice.cpp:102] gattServerEventHandler(): gattServerEventHandler [esp_gatt_if: 4] ... Unknown
14:25:00.076 -> [D][FreeRTOS.cpp:189] take(): Semaphore taking: name: CreateEvt (0x3ffe7a3c), owner: for executeCreate
14:25:00.076 -> [D][FreeRTOS.cpp:198] take(): Semaphore taken: name: CreateEvt (0x3ffe7a3c), owner: executeCreate
14:25:00.076 -> [D][BLEDevice.cpp:102] gattServerEventHandler(): gattServerEventHandler [esp_gatt_if: 4] ... Unknown
14:25:00.111 -> [D][FreeRTOS.cpp:189] take(): Semaphore taking: name: StartEvt (0x3ffe262c), owner: for start
14:25:00.111 -> [D][FreeRTOS.cpp:198] take(): Semaphore taken: name: StartEvt (0x3ffe262c), owner: start
14:25:00.111 -> [D][BLEDevice.cpp:102] gattServerEventHandler(): gattServerEventHandler [esp_gatt_if: 4] ... Unknown
14:25:00.111 -> start pService service
14:25:00.212 -> [D][BLECharacteristic.cpp:90] executeCreate(): Registering characteristic (esp_ble_gatts_add_char): uuid: 0000ff01-0000-1000-8000-00805f9b34fb, service: UUID: 0000ffe3-0000-1000-8000-00805f9b34fb, handle: 0xb33f
14:25:00.246 -> [D][FreeRTOS.cpp:189] take(): Semaphore taking: name: CreateEvt (0x3ffe7cac), owner: for executeCreate
14:25:00.246 -> [D][FreeRTOS.cpp:198] take(): Semaphore taken: name: CreateEvt (0x3ffe7cac), owner: executeCreate

Its either some bug/error or peripheral device has programmed handle 0xb33f.

I do have the same issue. All is well until I try to start the seventh service. Totally freezes at that point - no crash report, just freezes. I simplified my code the bare minimum if anyone wants it to recreate the error.

Could you share code, i am too lazy to build app with so many services/characteristics, but i can test and try to find problem.

// Arduino IDE version: 1.8.12   Board: WEMOS LOLIN32
#include <BLEDevice.h>
#include <BLEUtils.h>
#include <BLEServer.h>

#define NUM_SERVICES 7
#define NUM_SERVICES_TO_START 7   // Make this 6 or fewer it works fine
#define NUM_CHARACTERISTICS 1
#define SERVICE_ADVERTISING services[0]

BLEUUID services[NUM_SERVICES];
BLEUUID characteristics[NUM_SERVICES][NUM_CHARACTERISTICS];

BLEServer* pServer;
BLEService* pService[NUM_SERVICES];
BLECharacteristic* pCharacteristics[NUM_SERVICES][NUM_CHARACTERISTICS];

void setup() {
  Serial.begin(115200);
  Serial.println("Starting");

  services[0] = BLEUUID("ab529100-5310-483a-b4d3-7f1eaa8134a0");
  characteristics[0][0] = BLEUUID("ab529101-5310-483a-b4d3-7f1eaa8134a0");
  services[1] = BLEUUID("ab529200-5310-483a-b4d3-7f1eaa8134a0");
  characteristics[1][0] = BLEUUID("ab529201-5310-483a-b4d3-7f1eaa8134a0");
  services[2] = BLEUUID("ab529300-5310-483a-b4d3-7f1eaa8134a0");
  characteristics[2][0] = BLEUUID("ab529301-5310-483a-b4d3-7f1eaa8134a0");
  services[3] = BLEUUID("ab529400-5310-483a-b4d3-7f1eaa8134a0");
  characteristics[3][0] = BLEUUID("ab529401-5310-483a-b4d3-7f1eaa8134a0");
  services[4] = BLEUUID("ab529500-5310-483a-b4d3-7f1eaa8134a0");
  characteristics[4][0] = BLEUUID("ab529501-5310-483a-b4d3-7f1eaa8134a0");
  services[5] = BLEUUID("ab529600-5310-483a-b4d3-7f1eaa8134a0");
  characteristics[5][0] = BLEUUID("ab529601-5310-483a-b4d3-7f1eaa8134a0");
  services[6] = BLEUUID("ab529700-5310-483a-b4d3-7f1eaa8134a0");
  characteristics[6][0] = BLEUUID("ab529701-5310-483a-b4d3-7f1eaa8134a0");

  BLEDevice::init("S-Test");
  pServer = BLEDevice::createServer();

  //-------------------------------------------------------
  // Define the services with one read characteristic each
  //-------------------------------------------------------
  for(int i = 0; i < NUM_SERVICES; i++) {
    pService[i] = pServer->createService(services[i]);
    if(pService[i] == nullptr) {
      Serial.println("service not created - ABORT");
      while(true);
    }

    pCharacteristics[i][0] = pService[i]->createCharacteristic(characteristics[i][0], BLECharacteristic::PROPERTY_READ);
    if(pCharacteristics[i][0] == nullptr) {
      Serial.println("characteristic not created - ABORT");
      while(true);
    }

    pCharacteristics[i][0]->setValue("abc123");
    Serial.print("Service "); Serial.print(i); Serial.println(" defined");
  }
  Serial.println("All Services defined");

  //----------------------------
  // Now start all the services
  //----------------------------
  for(int i = 0; i < NUM_SERVICES_TO_START; i++) {
    pService[i]->start();     // if NUM_SERVICES_TO_START is > 6 this call hangs
    Serial.print("Service "); Serial.print(i); Serial.println(" started");
  }
  Serial.println("All Services started");

  //-----------------------------
  // Advertise the first service
  //-----------------------------
  BLEAdvertising *pAdvertising = BLEDevice::getAdvertising();
  pAdvertising->addServiceUUID(SERVICE_ADVERTISING);
  pAdvertising->setScanResponse(true);
  pAdvertising->setMinPreferred(0x06);  // functions that help with iPhone connections issue
  pAdvertising->setMinPreferred(0x12);
  BLEDevice::startAdvertising();
  Serial.println("Advertising Service");
}

uint32_t tickMillis = millis();
void loop() {
  // put your main code here, to run repeatedly:
  if(millis() - tickMillis > 60000) {
    Serial.println("tick");
    tickMillis = millis();
  }
}

Ok, i tested it and i have the same observations like @JAICHANGPARK

@chegewara

the handle value jump 0x0061 to 0xb33f
it's has limited handle values?

Just in my case its even higher value:

[V][BLEService.cpp:134] start(): >> start(): Starting service (esp_ble_gatts_start_service): UUID: ab529700-5310-483a-b4d3-7f1eaa8134a0, handle: 0x3ffc

I will try to test it with esp-idf, but i am suspecting it is bug in idf.

This is log from esp-idf:

W (1411) my_gatts_event_handler: custom gatts event handler, event: 0
W (1411) my_gatts_event_handler: custom gatts event handler, event: 7
I (1411) : Service 0 defined
W (1421) my_gatts_event_handler: custom gatts event handler, event: 7
I (1421) : Service 1 defined
W (1431) my_gatts_event_handler: custom gatts event handler, event: 7
I (1431) : Service 2 defined
W (1441) my_gatts_event_handler: custom gatts event handler, event: 7
I (1441) : Service 3 defined
W (1451) my_gatts_event_handler: custom gatts event handler, event: 7
I (1451) : Service 4 defined
W (1461) my_gatts_event_handler: custom gatts event handler, event: 7
I (1461) : Service 5 defined
E (1471) BT_GATT: GATTS_ReserveHandles: no free handle blocks

E (1471) BT_APPL: service creation failed.
W (1481) my_gatts_event_handler: custom gatts event handler, event: 7
I (1481) : Service 6 defined
I (1491) : All Services defined
W (1491) my_gatts_event_handler: custom gatts event handler, event: 12
I (1491) : Service 0 started
W (1501) my_gatts_event_handler: custom gatts event handler, event: 12
I (1501) : Service 1 started
I (1521) : Service 2 started
W (1521) my_gatts_event_handler: custom gatts event handler, event: 12
W (1531) my_gatts_event_handler: custom gatts event handler, event: 12
I (1531) : Service 3 started
W (1541) my_gatts_event_handler: custom gatts event handler, event: 12
I (1541) : Service 4 started
W (1551) my_gatts_event_handler: custom gatts event handler, event: 12
I (1551) : Service 5 started
E (1561) BT_APPL: service not created

I think those lines explain everything:

E (1471) BT_GATT: GATTS_ReserveHandles: no free handle blocks
E (1471) BT_APPL: service creation failed.

ok interesting. Saw your comment that it makes no difference how many handles are consumed in each service. So not running out of space for handles.

I assume the error could be given back to the caller as a nullptr so we can at least know the call failed?

It is esp-idf issue, because there is no error returned from esp_ble_gatts_create_service.
There is also few bugs related to semaphores in this library, like in this function which is causing stuck:

void BLEService::start() {
// We ask the BLE runtime to start the service and then create each of the characteristics.
// We start the service through its local handle which was returned in the ESP_GATTS_CREATE_EVT event
// obtained as a result of calling esp_ble_gatts_create_service().
//
    ESP_LOGD(LOG_TAG, ">> start(): Starting service (esp_ble_gatts_start_service): %s", toString().c_str());
    if (m_handle == NULL_HANDLE) {
        ESP_LOGE(LOG_TAG, "<< !!! We attempted to start a service but don't know its handle!");
        return;
    }

    BLECharacteristic *pCharacteristic = m_characteristicMap.getFirst();

    while (pCharacteristic != nullptr) {
        m_lastCreatedCharacteristic = pCharacteristic;
        pCharacteristic->executeCreate(this);

        pCharacteristic = m_characteristicMap.getNext();
    }
    // Start each of the characteristics ... these are found in the m_characteristicMap.

    m_semaphoreStartEvt.take("start");
    esp_err_t errRc = ::esp_ble_gatts_start_service(m_handle);

    if (errRc != ESP_OK) {
        ESP_LOGE(LOG_TAG, "<< esp_ble_gatts_start_service: rc=%d %s", errRc, GeneralUtils::errorToString(errRc));
m_semaphoreStartEvt.give(); // add this line and should no longer stuck
        return; 
    }
    m_semaphoreStartEvt.wait("start");

    ESP_LOGD(LOG_TAG, "<< start()");
} // start

I see there is few more places where semaphore is not not give/released upon error.

Here is answer:
https://github.com/espressif/esp-idf/issues/5495#issuecomment-651047331

Espressif seems to not take responsibility for returning ESP_OK when new service cant be created due to lack of handles, so we have to keep in mind that we are limited to 6 services in our app.

@chegewara Super Cool
Thank you for supporting

Ok, i think i see the problem in this library now. This code is missing one important check, status:

        // ESP_GATTS_CREATE_EVT
        // Called when a new service is registered as having been created.
        //
        // create:
        // * esp_gatt_status_t status
        // * uint16_t service_handle
        // * esp_gatt_srvc_id_t service_id
        // * - esp_gatt_id id
        // *   - esp_bt_uuid uuid
        // *   - uint8_t inst_id
        // * - bool is_primary
        //
        case ESP_GATTS_CREATE_EVT: {
            if (getUUID().equals(BLEUUID(param->create.service_id.id.uuid)) && m_instId == param->create.service_id.id.inst_id) {
                setHandle(param->create.service_handle);
                m_semaphoreCreateEvt.give();
            }
            break;
        } // ESP_GATTS_CREATE_EVT

I am assuming that status is giving us info if service is created or not.

Ok so we are restricted to 6 services. Luckily I managed to make my project work by picking and choosing which 6 services to start.

Sounds like waiting on the event will at least give a way to pass back a failure code on the seventh attempt. Interesting to see if programmatically stopping and starting services within the 6 limit works.

UPDATE: I used a hack to recognize a bad handle and used pServer->removeService(pService) to remove a service and keep at total of 6 - that works fine:
```
Starting
service 0 handle 28
Service 0 defined
service 1 handle 37
Service 1 defined
service 2 handle 46
Service 2 defined
service 3 handle 55
Service 3 defined
service 4 handle 64
Service 4 defined
service 5 handle 73
Service 5 defined
service 6 handle B33F
Bad handle - throw Service 1 away to make room
service 6 handle 82
Service 6 defined
service 7 handle B33F
Bad handle - throw Service 2 away to make room
service 7 handle 91
Service 7 defined
All Services defined
Service 0 started
Service 3 started
Service 4 started
Service 5 started
Service 6 started
Service 7 started
All Services started
Advertising Service

Was this page helpful?
0 / 5 - 0 ratings

Related issues

mkol5222 picture mkol5222  路  5Comments

JasXSL picture JasXSL  路  3Comments

Tavo7995 picture Tavo7995  路  7Comments

vishnunaik picture vishnunaik  路  6Comments

jim-ber picture jim-ber  路  8Comments