h2zero / NimBLE-Arduino

A fork of the NimBLE library structured for compilation with Arduino, for use with ESP32, nRF5x.
https://h2zero.github.io/NimBLE-Arduino/
Apache License 2.0
711 stars 147 forks source link

Advertised name change not working - like issue 360? #400

Closed paulhamsh closed 4 months ago

paulhamsh commented 2 years ago

Hi I have read this issue: https://github.com/h2zero/NimBLE-Arduino/issues/360 I have a similar / same problem. The ESP32 advertised device name doesn't seem to update if it is long. In my code below, using "AName" was ok, but using "ABCD0X-1 v0.0.0 XX01" didn't update in LightBlue. The local name in Advertisement Data seems to change, but not the 'main' name. It works in Bluedroid library. Is it the same problem as issue 360? (I am running on a M5Core 2 and a Heltec WIFI - both have the same issue)

On an other note, Bluedroid seems limited to 7 services in total (perhaps per server) but NimBLE doesn't seem to be - did you notice that limitation on Bluedroid as you created NimBLE and, if so, how did you remove it?

#include <NimBLEDevice.h>
#include <M5Core2.h>
void setup()
{
    M5.begin();

    NimBLEDevice::init("ABCD0X-1 v0.0.0 XX01");
    //NimBLEDevice::init("AName");    

    NimBLEServer *pServer = NimBLEDevice::createServer();
    NimBLEService *pService = pServer->createService("34452F38-9E44-46AB-B171-0CC578FEB928");
    NimBLECharacteristic *pCharacteristic = pService->createCharacteristic("CAD0C949-7DCE-4A04-9D80-E767C796B392");

    pService->start();
    pCharacteristic->setValue("Hello BLE");

    NimBLEAdvertising *pAdvertising = NimBLEDevice::getAdvertising();
    //pAdvertising->setName("AnotherName");
    pAdvertising->addServiceUUID("34452F38-9E44-46AB-B171-0CC578FEB928"); 
    pAdvertising->start(); 
}

void loop() {
  delay(200);
}

This works

//#include <NimBLEDevice.h>

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

#include <M5Core2.h>
void setup()
{
    M5.begin();

    BLEDevice::init("ABCD0X-1 v0.0.0 XX01");
    //BLEDevice::init("AName");    

    BLEServer *pServer = BLEDevice::createServer();
    BLEService *pService = pServer->createService("34452F38-9E44-46AB-B171-0CC578FEB928");
//    BLECharacteristic *pCharacteristic = pService->createCharacteristic("CAD0C949-7DCE-4A04-9D80-E767C796B392");
    BLECharacteristic *pCharacteristic = pService->createCharacteristic("CAD0C949-7DCE-4A04-9D80-E767C796B392", BLECharacteristic::PROPERTY_READ || BLECharacteristic::PROPERTY_WRITE);    
    pService->start();
    pCharacteristic->setValue("Hello BLE");

    BLEAdvertising *pAdvertising = BLEDevice::getAdvertising();
    //pAdvertising->setName("AnotherName");
    pAdvertising->addServiceUUID("34452F38-9E44-46AB-B171-0CC578FEB928"); 
    pAdvertising->start(); 
}

void loop() {
  delay(200);
}

Thanks Paul

h2zero commented 2 years ago

The name provided in the init call sets the value of the name characteristic 0x2A00 which is read from the device by your phone when connected to it. Most of the time the phone will store this value and use it on subsequent scans which will only update when connected again.

The advertised name is different as it's in the broadcast data and can be changed anytime, but phones won't use it when they've already connected and have the name from the characteristic.

I didn't do anything to remove the services count limit, it came with the NimBLE stack 😀.

paulhamsh commented 2 years ago

Thanks. Is there a limit to the number of characters? It works for AName but not my main example. Or it may not allow spaces or ‘-‘?

On 10 May 2022, at 00:26, h2zero @.***> wrote:

 The name provided in the init call sets the value of the name characteristic 0x2A00 which is read from the device by your phone when connected to it. Most of the time the phone will store this value and use it on subsequent scans which will only update when connected again.

The advertised name is different as it's in the broadcast data and can be changed anytime, but phones won't use it when they've already connected and have the name from the characteristic.

I didn't do anything to remove the services count limit, it came with the NimBLE stack 😀.

— Reply to this email directly, view it on GitHub, or unsubscribe. You are receiving this because you authored the thread.

paulhamsh commented 2 years ago

So, further investigation shows something pretty weird. In all cases the advertised 'local name' is correct. nRF connect always shows the name put in BLEDevice::init(") on ios and Android. Lightblue on ios shows the name if it is 19 characters or less and compiled with NimBLE. If it is 20 characters, it is ignored (not truncated, just Lightblue uses the old name) and the previous name shown in the scan and 'Peripheral' screen. BUT if you compile the same code under Bluedroid it works in ios Lightblue. And it is ok using Lightblue on Android.

Which implies it is an interaction between ios Lightblue and NimBLE that causes the problem - not sure how that could be. And how can using Bluedroid change it on the esp32 so that ios Lightblue then sees it properly!

                                          Lightblue                      nRFConnect
                                          iOS       Android              iOS        Android
  Using NimBLE:
  Setting to "AAA50A-0 A0.0.0 0A0X"        N         Y                    Y          Y
  Setting to "AAA50A-0 A0.0.0 0A0"         Y         Y                    Y          Y
  Using Bluedroid:
  Setting to "AAA50A-0 A0.0.0 0A0X"        Y         Y                    Y          Y
  Setting to "AAA50A-0 A0.0.0 0A0"         Y         Y                    Y          Y  
h2zero commented 2 years ago

I tested this and I believe it's just the way Lightblue handles the names, if the name is longer than it can display it truncates and appends "...", when you click show advertisement data it shows the full name.

paulhamsh commented 2 years ago

Hi As always, thanks for looking into it.

The key thing is that it doesn’t ‘cache‘ the name in this instance - in the other cases, if the name has changed then the new name gets ‘cached’ and Lightblue / nRF shows that name on its scan list. For this single case, Lightblue on ios still shows the old name in the scan list and at the top of the screen once connected. Like it is just ignoring it. But nRF shows the new name.

Also in this case the app I am trying to connect to the esp32 doesn’t see it - but it does for the other three examples. (For ios it only ‘sees’ it once Lightblue has scanned and connected and disconnected - no idea why, but it is a commercial app so can’t investigate that.)

On 13 May 2022, at 15:05, h2zero @.***> wrote:

 I tested this and I believe it's just the way Lightblue handles the names, if the name is longer than it can display it truncates and appends "...", when you click show advertisement data it shows the full name.

— Reply to this email directly, view it on GitHub, or unsubscribe. You are receiving this because you authored the thread.

h2zero commented 2 years ago

Yes I can see the name is changed if re-flashed as well, seems the apps are working differently than they used to.

Unfortunately there is not much I can suggest to help with this. The code behind it is very simple, the name is copied to a buffer and provided to the device when requested. How the client handles that is up to it. If the issue is with NimBLE it's certainly going to be an upstream matter and I don't see anything brought up there about it.

paulhamsh commented 2 years ago

Is there a limit to the length of the buffer? My investigations show the following code path below. It seems to fail at 19 characters, but the buffer size here is set to 31.

In NimBLEDevice.cpp

        rc = ble_svc_gap_device_name_set(deviceName.c_str());

In ble_svc_gap.cpp

int
ble_svc_gap_device_name_set(const char *name)
{
    int len;

    len = strlen(name);
    if (len > BLE_SVC_GAP_NAME_MAX_LEN) {
        return BLE_HS_EINVAL;
    }

    memcpy(ble_svc_gap_name, name, len);
    ble_svc_gap_name[len] = '\0';

    return 0;
}

Max name length is 31.
ble_svc_gap_name

In ble_svc_gap

static char ble_svc_gap_name[BLE_SVC_GAP_NAME_MAX_LEN + 1] =
        MYNEWT_VAL(BLE_SVC_GAP_DEVICE_NAME);

static int
ble_svc_gap_device_name_write_access(struct ble_gatt_access_ctxt *ctxt)
{
#if MYNEWT_VAL(BLE_SVC_GAP_DEVICE_NAME_WRITE_PERM) < 0
    assert(0);
    return 0;
#else
    uint16_t om_len;
    int rc;

    om_len = OS_MBUF_PKTLEN(ctxt->om);
    if (om_len > BLE_SVC_GAP_NAME_MAX_LEN) {
        return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN;
    }

    rc = ble_hs_mbuf_to_flat(ctxt->om, ble_svc_gap_name, om_len, NULL);
    if (rc != 0) {
        return BLE_ATT_ERR_UNLIKELY;
    }

    ble_svc_gap_name[om_len] = '\0';

    if (ble_svc_gap_chr_changed_cb_fn) {
        ble_svc_gap_chr_changed_cb_fn(BLE_SVC_GAP_CHR_UUID16_DEVICE_NAME);
    }

    return rc;
#endif
}
h2zero commented 2 years ago

The only other limit is 512 bytes for the max characteristic size.

h2zero commented 4 months ago

After all this time I think this simply came down to the MTU being 23, to read the full characteristic a long read would need to be done unless the MTU is increased.