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

Hid startServices results in missing onDisconnect

Open VisaJE opened this issue 11 months ago • 4 comments

I've had this curious issue on ESP32-S3 (on a proprietary board) not disconnecting correctly when device exits the range of bluetooth with NimBLE-Arduino 2.1.2. Also seems to happen on 2.2.3.

Took a good while to replicate, but it turns out the issue only appears if NimBLEHIDDevice::startServices gets called after a call to NimBLEHIDDevice::getInputReport.

To replicate,

  1. upload the minimal code to esp32-s3,
  2. open the serial port to observe connection,
  3. pair a device (Im testing with an android phone),
  4. walk out of bluetooth range,
  5. come back and find out the esp32-s3 still thinks it is connected. Now, move the startServices and repeat to find that the disconnection works.

main.cpp:

#include "NimBLEDevice.h"
#include <Arduino.h>
#include <NimBLEHIDDevice.h>
#include <NimBLEServer.h>

struct Cbs : public NimBLEServerCallbacks {
  void onDisconnect(NimBLEServer *pServer, NimBLEConnInfo &, int i) override {
    Serial.printf("Disconnected due to %d\n", i);
    NimBLEDevice::startAdvertising();
  }
  void onConnect(NimBLEServer *pServer, NimBLEConnInfo &) override {
    Serial.printf("onConnect\n");
    NimBLEDevice::stopAdvertising();
  }
};

NimBLEServer *server;
HardwareSerial mySerial(0);

NimBLECharacteristic *charac0;
void setup() {
  Serial.begin(115200);
  NimBLEDevice::init(":)");

  server = NimBLEDevice::createServer();
  static Cbs cbs{};
  server->setCallbacks(&cbs);
  // Low power so its easy to disconnect
  NimBLEDevice::setPower(-21);

  static NimBLEHIDDevice hidDevice{server};

  hidDevice.setManufacturer("myself");

  // hidDevice.startServices();

  NimBLEAdvertising *pAdvertising = server->getAdvertising();
  pAdvertising->setAppearance(HID_MOUSE);
  pAdvertising->setName(":)");
  pAdvertising->addServiceUUID(hidDevice.getHidService()->getUUID());

  hidDevice.setBatteryLevel(100);

  charac0 = hidDevice.getInputReport(1);

  // Here lies the issue
  hidDevice.startServices();

  charac0->setValue(":)");

  NimBLEDevice::startAdvertising();
}

void loop() {
  delay(1000);
  charac0->notify();
  Serial.printf("connected: %d\n", server->getConnectedCount());
}

platformio.ini:

[env:default]
monitor_speed=115200
platform = espressif32 @ 6.2.0
board = esp32-s3-devkitc-1
framework = arduino

build_flags =
    -std=gnu++17

board_build.f_cpu = 160000000L

build_src_filter =
  +<main.cpp>

lib_deps =
  h2zero/[email protected]

build_unflags =
    -std=gnu++11

VisaJE avatar Mar 05 '25 15:03 VisaJE

Looks like the HID does not work at all if startServices is called before getInputReport, so it is not a fix to just move the startServices.

VisaJE avatar Mar 05 '25 15:03 VisaJE

startServices must be called after the first call to getInputReport in order to create the characteristic and have it added to the service, otherwise it will just create it but not register it with the GATT stack. The disconnect issue is something unrelated to the HID class and my be an upstream problem.

h2zero avatar Mar 06 '25 18:03 h2zero

Fair, and we did since found more issues with gatt on esp s3 that were fixed by reverting to 1.4.3.

An additional quirk is that calling startServices both before and after getInputReport did not enable the characteristics, despite my quick read of the source hinting that the characteristics would be reinitialized.

VisaJE avatar Mar 08 '25 15:03 VisaJE

Could you share the updated source code that isn't working?

h2zero avatar Mar 21 '25 22:03 h2zero

@VisaJE any update on this?

h2zero avatar Sep 02 '25 22:09 h2zero

No, but I'll test today.

VisaJE avatar Sep 04 '25 07:09 VisaJE

Okay, I tested on nimble-arduino 2.3.5 and platformio/platform-espressif32 @ 6.12.0: The issue is fixed.

VisaJE avatar Sep 04 '25 08:09 VisaJE