NimBLE-Arduino icon indicating copy to clipboard operation
NimBLE-Arduino copied to clipboard

When two services are added only first is visible (advertises)

Open vovagorodok opened this issue 1 year ago • 12 comments

Used two arduino platformio libraries:
https://github.com/vovagorodok/ArduinoBleChess
https://github.com/vovagorodok/ArduinoBleOTA
Like:

inline void initBle(const std::string &deviceName)
{
    BLEDevice::init(deviceName);
}

inline void advertizeBle()
{
    auto* server = BLEDevice::createServer();
    auto* advertising = server->getAdvertising();
    advertising->setScanResponse(true);
    advertising->setMinPreferred(0x06); // functions that help with iPhone connections issue
    advertising->setMaxPreferred(0x12);
    if (not advertising->start())
        LOG_ERROR << "Ble advertising error";
}

void setup()
{
    initBle(DEVICE_NAME);
    if (not ArduinoBleChess.begin(peripheral))
        LOG_ERROR << "Ble chess initialization error";
    if (not ArduinoBleOTA.begin(InternalStorage))
        LOG_ERROR << "Ble ota initialization error";
    advertizeBle();
}

Where:

bool ArduinoBleChessClass::begin(BleChessPeripheral& peripheral)
{
    auto* server = BLEDevice::createServer();
    bleChessConnection.registerPeripheral(peripheral);
    server->setCallbacks(this);
    auto* service = server->createService(CHESS_SERVICE_UUID);

    auto* rxCharacteristic = service->createCharacteristic(
        CHESS_CHARACTERISTIC_UUID_RX,
        NIMBLE_PROPERTY::WRITE
    );
    rxCharacteristic->setCallbacks(this);

    auto* txCharacteristic = service->createCharacteristic(
        CHESS_CHARACTERISTIC_UUID_TX,
        NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::NOTIFY
    );
    this->txCharacteristic = txCharacteristic;

    auto* advertising = server->getAdvertising();
    advertising->addServiceUUID(CHESS_SERVICE_UUID);
    return service->start();
}
bool ArduinoBleOTAClass::begin(OTAStorage& storage)
{
    auto* server = BLEDevice::createServer();
    BLEDevice::setMTU(BLE_OTA_MTU_SIZE);

    bleOtaUploader.begin(storage);
    auto* service = server->createService(OTA_SERVICE_UUID);

    auto* rxCharacteristic = service->createCharacteristic(
        OTA_CHARACTERISTIC_UUID_RX,
        NIMBLE_PROPERTY::WRITE_NR
    );
    rxCharacteristic->setCallbacks(this);

    auto* txCharacteristic = service->createCharacteristic(
        OTA_CHARACTERISTIC_UUID_TX,
        NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::NOTIFY
    );
    this->txCharacteristic = txCharacteristic;

    auto* advertising = server->getAdvertising();
    advertising->addServiceUUID(OTA_SERVICE_UUID);
    return service->start();
}

Only first service is visible: image

When I change the order of initialization, than only second service is visible All return service->start(); returns true. No error logs After connect both services are visible. Something with advertising?

vovagorodok avatar Sep 20 '22 21:09 vovagorodok

There is only room for one 128bit uuid in the advertisement. The current code does not create a second instance of a service uuid list in the scan response using those methods.

You should be able to accomplish this by using the advertisement data class and setting the advertisement and scan response data with that.

h2zero avatar Sep 20 '22 22:09 h2zero

Have you an example for that?

vovagorodok avatar Sep 20 '22 23:09 vovagorodok

The iBeacon example shows the use of this.

https://github.com/h2zero/NimBLE-Arduino/blob/release/1.4/examples/Refactored_original_examples/BLE_iBeacon/BLE_iBeacon.ino

h2zero avatar Sep 20 '22 23:09 h2zero

Trying something like:

inline void advertizeBle(const std::string &deviceName)
{
    auto* server = BLEDevice::createServer();
    auto* advertising = server->getAdvertising();

    NimBLEAdvertisementData advertisementData{};
    advertisementData.setName(deviceName);
    // advertisementData.addTxPower();
    advertisementData.setFlags(BLE_HS_ADV_F_DISC_GEN | BLE_HS_ADV_F_BREDR_UNSUP);
    advertisementData.setCompleteServices(NimBLEUUID(CHESS_SERVICE_UUID));
    advertisementData.setCompleteServices(NimBLEUUID(OTA_SERVICE_UUID));
    advertising->setAdvertisementData(advertisementData);
    advertising->setScanResponseData(advertisementData);

    // advertising->setScanResponse(true);
    advertising->setMinPreferred(0x06); // functions that help with iPhone connections issue
    advertising->setMaxPreferred(0x12);

    if (not advertising->start())
        LOG_ERROR << "Ble advertising error";
}

But as I see can be added only one 128 uuid:

void NimBLEAdvertisementData::setCompleteServices(const NimBLEUUID &uuid) {
    setServices(true, uuid.bitSize(), {uuid});
} // setCompleteServices

Its Bluetooth LE or library limit? In nRF Connect I see doubled CHESS_SERVICE_UUID

vovagorodok avatar Sep 21 '22 01:09 vovagorodok

You need to create 2 instances of advertisement data, one for the advertisement and the other for scan response. Each should have a different service uuid.

h2zero avatar Sep 21 '22 01:09 h2zero

Changed to:

inline void advertizeBle(const std::string& deviceName)
{
    auto* server = BLEDevice::createServer();
    auto* advertising = server->getAdvertising();

    NimBLEAdvertisementData chessAdvertisementData{};
    NimBLEAdvertisementData otaAdvertisementData{};
    otaAdvertisementData.setShortName(deviceName);
    otaAdvertisementData.setFlags(BLE_HS_ADV_F_DISC_GEN | BLE_HS_ADV_F_BREDR_UNSUP);
    otaAdvertisementData.setCompleteServices(NimBLEUUID(OTA_SERVICE_UUID));
    chessAdvertisementData.setShortName(deviceName);
    chessAdvertisementData.setFlags(BLE_HS_ADV_F_DISC_GEN | BLE_HS_ADV_F_BREDR_UNSUP);
    chessAdvertisementData.setCompleteServices(NimBLEUUID(CHESS_SERVICE_UUID));
    advertising->setAdvertisementData(otaAdvertisementData);
    advertising->setScanResponseData(chessAdvertisementData);

    // advertising->setScanResponse(true);
    advertising->setMinPreferred(0x06); // functions that help with iPhone connections issue
    advertising->setMaxPreferred(0x12);

    if (not advertising->start())
        LOG_ERROR << "Ble advertising error";
}

In nRF Connect I see that uuis are removed from advertising data. Looks that no enough space for two 128 uuids and name and flags. When I left only two 128 uuids and flags only for otaAdvertisementData it works. Adding even deviceName removes uuids. Any possibility to increase that space or it limited by Bloetooth LE?

vovagorodok avatar Sep 21 '22 07:09 vovagorodok

Decided to add: first 128bit uuid + name (max 11 chars) to AdvertisementData and second 128bit uuid + flags to ResponseData. From BLE documentation I see that each AdvertisementData can be max 31 bytes size.

  1. Can I somehow concatenate this two blocks to one 62bytes i order to place larger names? Beacons do that?
  2. Should be added something else to this datas in order to have correct work?
  3. About this lines:
advertising->setScanResponse(true);
advertising->setMinPreferred(0x06); // functions that help with iPhone connections issue
advertising->setMaxPreferred(0x12);

How they should be handled in this case?

vovagorodok avatar Sep 21 '22 14:09 vovagorodok

Standard convention would be to have the flags and main uuid in the advertisement, then the name and secondary data in the scan response.

Can I somehow concatenate this two blocks to one 62bytes i order to place larger names? Beacons do that?

This is not possible, the 2 data sets are completely separate.

advertising->setMinPreferred(0x06); // functions that help with iPhone connections issue advertising->setMaxPreferred(0x12);

These can be removed to save space, I haven't experienced any negative consequences in doing so.

h2zero avatar Sep 21 '22 14:09 h2zero

Like that?:

constexpr auto MAX_ADVERTISEMENT_DATA_SIZE = 31;
constexpr auto UUID_128_BIT_SIZE = 16;
constexpr auto LENGTH_AND_TYPE_BYTES_SIZE = 2;
constexpr auto TOTAL_OVERHEAD = UUID_128_BIT_SIZE + LENGTH_AND_TYPE_BYTES_SIZE * 2;
constexpr auto MAX_NAME_SIZE = MAX_ADVERTISEMENT_DATA_SIZE - TOTAL_OVERHEAD;

inline bool advertizeBle(const std::string& deviceName,
                         const std::string& primaryUUID,
                         const std::string& secondaryUUID)
{
    if (deviceName.size() > MAX_NAME_SIZE)
        return false;

    auto* server = BLEDevice::createServer();
    auto* advertising = server->getAdvertising();

    NimBLEAdvertisementData primaryAdvertisementData{};
    primaryAdvertisementData.setFlags(BLE_HS_ADV_F_DISC_GEN | BLE_HS_ADV_F_BREDR_UNSUP);
    primaryAdvertisementData.setCompleteServices(NimBLEUUID(primaryUUID));
    advertising->setAdvertisementData(primaryAdvertisementData);

    NimBLEAdvertisementData secondaryAdvertisementData{};
    secondaryAdvertisementData.setShortName(deviceName);
    secondaryAdvertisementData.setCompleteServices(NimBLEUUID(secondaryUUID));    
    advertising->setScanResponseData(secondaryAdvertisementData);

    return advertising->start();
}

According, to:

advertising->setMinPreferred(0x06); // functions that help with iPhone connections issue
advertising->setMaxPreferred(0x12);

It was in examples and I decided to add it everywhere. Should work with iPhone without this lines? I haven't iPhone to test that.

According, to:

advertising->setScanResponse(true);

Should stay added or remove?

vovagorodok avatar Sep 21 '22 15:09 vovagorodok

Actually i think bluetooth specs allows to "merge" advertising and scan response, but i never been trying it. You can try to build long advertising packet, up to 62 bytes, then split it and use as a raw data.

chegewara avatar Sep 21 '22 15:09 chegewara

Actually i think bluetooth specs allows to "merge" advertising and scan response, but i never been trying it. You can try to build long advertising packet, up to 62 bytes, then split it and use as a raw data.

Hmm. Good idea to just try it :) There no possibility to simply do it. Should be created NimBLEAdvertisementData like class for that. 11 bytes name for me is enough for this moment. I think i'll back to this experiment in future.

According to advertizeBle() function and questions below. All looks ok? I'll add this function and update multiservice examples in my libraries

vovagorodok avatar Sep 21 '22 16:09 vovagorodok

I'm not sure if that would be possible with NimBLE as the functions don't really allow for it.

h2zero avatar Sep 21 '22 16:09 h2zero