How can I disconnect client from server side with Nimble-Arduino?

I have a XIAO ESP32C3 board,

I’m using Nimble-Arduino and I’m trying to disconnect all the connected peers but I get flaky results: My disconnect routine doesn’t work most of the time but sometimes the connection drops.

My results:

23:54:47.695 -> Connected
23:54:49.686 -> Disconnected
23:54:51.677 -> Connected
23:54:53.703 -> Connected
23:54:55.695 -> Connected
23:54:57.718 -> Connected
23:54:59.707 -> Connected
23:55:01.730 -> Connected
23:55:03.719 -> Connected
23:55:05.739 -> Connected
23:55:07.729 -> Connected
23:55:09.750 -> Connected
23:55:11.742 -> Connected
23:55:13.734 -> Disconnected
23:55:15.756 -> Connected
23:55:17.743 -> Connected
23:55:19.763 -> Connected
23:55:21.784 -> Connected
23:55:23.772 -> Connected

Any help would be appreciated.

The sketch

#include "HIDKeyboardTypes.h"
#include "HIDTypes.h"
#include "sdkconfig.h"
#include "nimconfig.h"
#include "NimBLECharacteristic.h"
#include "NimBLEHIDDevice.h"
#include <NimBLEDevice.h>
#include <NimBLEServer.h>
#include <NimBLEUtils.h>
#include <driver/adc.h>

static const uint8_t  BLE_CONSUMER_REPORT_ID    = 0x01;
static const uint16_t BLE_HID_REMOTE_APPEARANCE = 0x0180;
static const uint16_t BLE_VEND_ID               = 0x0000;
static const uint8_t  BLE_VEND_SRC              = 0x02;
static const uint16_t BLE_PROD_ID               = 0x0000;
static const uint16_t BLE_PROD_VER              = 0x0001;
static const uint8_t _hidReportDescriptor[] = {
  USAGE_PAGE(1), 0x0C,                  // USAGE_PAGE (Consumer)
  USAGE(1), 0x01,                       // USAGE (Consumer Control)
  COLLECTION(1), 0x01,                  // COLLECTION (Application)
  REPORT_ID(1), BLE_CONSUMER_REPORT_ID, //   REPORT_ID (1)
  LOGICAL_MINIMUM(1), 0x00,             //   LOGICAL_MINIMUM (0)
  LOGICAL_MAXIMUM(1), 0x01,             //   LOGICAL_MAXIMUM (1)
  REPORT_SIZE(1), 0x01,                 //   REPORT_SIZE (1)
  REPORT_COUNT(1), 0x0B,                //   REPORT_COUNT (11)
  USAGE(1), 0x30,                       //   Power
  USAGE(1), 0x41,                       //   Menu Pick (Select)
  USAGE(1), 0x46,                       //   Menu Escape (Back)
  USAGE(2), 0x23, 0x02,                 //   Home
  USAGE(1), 0xE9,                       //   Volume Increase
  USAGE(1), 0xEA,                       //   Volume Decrease
  USAGE(1), 0xE2,                       //   Volume Mute
  USAGE(1), 0x42,                       //   Menu Up
  USAGE(1), 0x43,                       //   Menu Down
  USAGE(1), 0x44,                       //   Menu Left
  USAGE(1), 0x45,                       //   Menu Right
  HIDINPUT(1), 0x02,                    //   INPUT (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
  REPORT_SIZE(1), 0x05,                 //   REPORT_SIZE (5) - Padding 5 bit
  REPORT_COUNT(1), 0x01,                //   REPORT_COUNT (1)
  HIDINPUT(1), 0x03,                    //   INPUT
  END_COLLECTION(0)                     // END COLLECTION
};

// ************ STATUS
class BLEStatus : public NimBLEServerCallbacks {
public:
  bool connected = false;

  BLEStatus(void) {};

  void onConnect(NimBLEServer* pServer, ble_gap_conn_desc* desc) {
    this->connected = true;
  };

  void onDisconnect(NimBLEServer* pServer) {
    this->connected = false;
  };
  NimBLECharacteristic* inputConsumer;
};

// *********** BLE
class BLERemote {
private:
  NimBLEServer*         bleServer;
  BLEStatus*            connectionStatus;
  NimBLEHIDDevice*      hid;
  NimBLECharacteristic* inputConsumer;
  std::string           deviceManufacturer;
  std::string           deviceName;
  static void           taskServer(void* pvParameter) {
    BLERemote* BLERemoteInstance = (BLERemote*)pvParameter;
    NimBLEDevice::init(BLERemoteInstance->deviceName);
    NimBLEServer* pServer = NimBLEDevice::createServer();
    pServer->setCallbacks(BLERemoteInstance->connectionStatus);
    pServer->advertiseOnDisconnect(true);
    BLERemoteInstance->bleServer                       = pServer;
    BLERemoteInstance->hid                             = new NimBLEHIDDevice(pServer);
    BLERemoteInstance->inputConsumer                   = BLERemoteInstance->hid->inputReport(BLE_CONSUMER_REPORT_ID);
    BLERemoteInstance->connectionStatus->inputConsumer = BLERemoteInstance->inputConsumer;
    BLERemoteInstance->hid->manufacturer()->setValue(BLERemoteInstance->deviceManufacturer);
    BLERemoteInstance->hid->pnp(BLE_VEND_SRC, BLE_VEND_ID, BLE_PROD_ID, BLE_PROD_VER);
    BLERemoteInstance->hid->hidInfo(0x00, 0x01);
    NimBLEDevice::setSecurityAuth(true, false, false);
    BLERemoteInstance->hid->reportMap((uint8_t*)_hidReportDescriptor, sizeof(_hidReportDescriptor));
    BLERemoteInstance->hid->startServices();
    BLERemoteInstance->onStarted(pServer);
    NimBLEAdvertising* pAdvertising = pServer->getAdvertising();
    pAdvertising->setAppearance(BLE_HID_REMOTE_APPEARANCE);
    pAdvertising->addServiceUUID(BLERemoteInstance->hid->hidService()->getUUID());
    pAdvertising->start();
    BLERemoteInstance->hid->setBatteryLevel(100);
    vTaskDelay(portMAX_DELAY);
  };

public:
  BLERemote(void) {};

  void setup(std::string deviceName, std::string deviceManufacturer) {
    this->hid                 = 0;
    this->deviceName          = deviceName;
    this->deviceManufacturer  = deviceManufacturer;
    this->connectionStatus    = new BLEStatus();
    xTaskCreate(this->taskServer, "server", 20000, (void*)this, 1, NULL);
  };

  bool isConnected(void) {
    return this->connectionStatus->connected;
  };

  void disconnect(void) {
    size_t peersNum = this->bleServer->getConnectedCount();
    for (int i = 0; i < peersNum; i++) {
      uint16_t connID = this->bleServer->getPeerInfo(i).getConnHandle();
      this->bleServer->disconnect(connID);
    }
  };

protected:
  virtual void onStarted(NimBLEServer* pServer) {};
};

// ********* SKETCH

BLERemote blRemote;

void setup() {
  Serial.begin(115200);
  delay(1000);
  blRemote.setup("Test", "Test");
}

void loop() {
  if (!blRemote.isConnected()) {
    Serial.println("Disconnected");
    delay(2000);
    return;
  }
  Serial.println("Connected");
  delay(2000);
  blRemote.disconnect();
}

Hi there,
So I see what I left off and added, appears to work Good.
You can use the “Deinit”
I used Nrf_connect for desktop to test it (see pic)
HTH
GL :slight_smile: PJ

//#include "NimBLE.h"
#include "HIDKeyboardTypes.h"
#include "HIDTypes.h"
#include "sdkconfig.h"
#include "nimconfig.h"
#include "NimBLECharacteristic.h"
#include "NimBLEHIDDevice.h"
#include <NimBLEDevice.h>
#include <NimBLEServer.h>
#include <NimBLEUtils.h>
#include <driver/adc.h>

static const uint8_t  BLE_CONSUMER_REPORT_ID    = 0x01;
static const uint16_t BLE_HID_REMOTE_APPEARANCE = 0x0180;
static const uint16_t BLE_VEND_ID               = 0x0000;
static const uint8_t  BLE_VEND_SRC              = 0x02;
static const uint16_t BLE_PROD_ID               = 0x0000;
static const uint16_t BLE_PROD_VER              = 0x0001;
static const uint8_t _hidReportDescriptor[] = {
  USAGE_PAGE(1), 0x0C,                  // USAGE_PAGE (Consumer)
  USAGE(1), 0x01,                       // USAGE (Consumer Control)
  COLLECTION(1), 0x01,                  // COLLECTION (Application)
  REPORT_ID(1), BLE_CONSUMER_REPORT_ID, //   REPORT_ID (1)
  LOGICAL_MINIMUM(1), 0x00,             //   LOGICAL_MINIMUM (0)
  LOGICAL_MAXIMUM(1), 0x01,             //   LOGICAL_MAXIMUM (1)
  REPORT_SIZE(1), 0x01,                 //   REPORT_SIZE (1)
  REPORT_COUNT(1), 0x0B,                //   REPORT_COUNT (11)
  USAGE(1), 0x30,                       //   Power
  USAGE(1), 0x41,                       //   Menu Pick (Select)
  USAGE(1), 0x46,                       //   Menu Escape (Back)
  USAGE(2), 0x23, 0x02,                 //   Home
  USAGE(1), 0xE9,                       //   Volume Increase
  USAGE(1), 0xEA,                       //   Volume Decrease
  USAGE(1), 0xE2,                       //   Volume Mute
  USAGE(1), 0x42,                       //   Menu Up
  USAGE(1), 0x43,                       //   Menu Down
  USAGE(1), 0x44,                       //   Menu Left
  USAGE(1), 0x45,                       //   Menu Right
  HIDINPUT(1), 0x02,                    //   INPUT (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
  REPORT_SIZE(1), 0x05,                 //   REPORT_SIZE (5) - Padding 5 bit
  REPORT_COUNT(1), 0x01,                //   REPORT_COUNT (1)
  HIDINPUT(1), 0x03,                    //   INPUT
  END_COLLECTION(0)                     // END COLLECTION
};

// ************ STATUS
class BLEStatus : public NimBLEServerCallbacks {
public:
  bool connected = false;

  BLEStatus(void) {};

  void onConnect(NimBLEServer* pServer, ble_gap_conn_desc* desc) {
    this->connected = true;
  };

  void onDisconnect(NimBLEServer* pServer) {
    this->connected = false;
  };
  NimBLECharacteristic* inputConsumer;
};

// *********** BLE
class BLERemote {
private:
  NimBLEServer*         bleServer;
  BLEStatus*            connectionStatus;
  NimBLEHIDDevice*      hid;
  NimBLECharacteristic* inputConsumer;
  std::string           deviceManufacturer;
  std::string           deviceName;
  static void           taskServer(void* pvParameter) {
    BLERemote* BLERemoteInstance = (BLERemote*)pvParameter;
    NimBLEDevice::init(BLERemoteInstance->deviceName);
    NimBLEServer* pServer = NimBLEDevice::createServer();
    pServer->setCallbacks(BLERemoteInstance->connectionStatus);
    pServer->advertiseOnDisconnect(true);
    BLERemoteInstance->bleServer                       = pServer;
    BLERemoteInstance->hid                             = new NimBLEHIDDevice(pServer);
    BLERemoteInstance->inputConsumer                   = BLERemoteInstance->hid->inputReport(BLE_CONSUMER_REPORT_ID);
    BLERemoteInstance->connectionStatus->inputConsumer = BLERemoteInstance->inputConsumer;
    BLERemoteInstance->hid->manufacturer()->setValue(BLERemoteInstance->deviceManufacturer);
    BLERemoteInstance->hid->pnp(BLE_VEND_SRC, BLE_VEND_ID, BLE_PROD_ID, BLE_PROD_VER);
    BLERemoteInstance->hid->hidInfo(0x00, 0x01);
    NimBLEDevice::setSecurityAuth(true, false, false);
    BLERemoteInstance->hid->reportMap((uint8_t*)_hidReportDescriptor, sizeof(_hidReportDescriptor));
    BLERemoteInstance->hid->startServices();
    BLERemoteInstance->onStarted(pServer);
    NimBLEAdvertising* pAdvertising = pServer->getAdvertising();
    pAdvertising->setAppearance(BLE_HID_REMOTE_APPEARANCE);
    pAdvertising->addServiceUUID(BLERemoteInstance->hid->hidService()->getUUID());
    pAdvertising->start();
    BLERemoteInstance->hid->setBatteryLevel(100);
    vTaskDelay(portMAX_DELAY);
  };

public:
  BLERemote(void) {};

  void setup(std::string deviceName, std::string deviceManufacturer) {
    this->hid                 = 0;
    this->deviceName          = deviceName;
    this->deviceManufacturer  = deviceManufacturer;
    this->connectionStatus    = new BLEStatus();
    xTaskCreate(this->taskServer, "server", 20000, (void*)this, 1, NULL);
  };

  bool isConnected(void) {
    return this->connectionStatus->connected;
  };

  void disconnect(void) {
    size_t peersNum = this->bleServer->getConnectedCount();
    for (int i = 0; i < peersNum; i++) {
      uint16_t connID = this->bleServer->getPeerInfo(i).getConnHandle();
      this->bleServer->disconnect(connID);
    }
  };

protected:
  virtual void onStarted(NimBLEServer* pServer) {};
};

// ********* SKETCH

BLERemote blRemote;

void setup() {
  Serial.begin(115200);
  delay(2000);
  Serial.println("Starting BLE");
  //NimBLE.begin();
  NimBLEDevice::init("NimBLE");
 
  blRemote.setup("Test", "Hello Seeed");
 
}

void loop() {
  delay(30000);
        restartESP32();
      }


void restartESP32() {                
    Serial.println("Disconecting and ReBooting ESP32...");
    size_t numClients = NimBLEDevice::getClientListSize();
    if (numClients > 0) {
        std::list<NimBLEClient*> *clientList = NimBLEDevice::getClientList();
        for (auto it = clientList->begin(); it != clientList->end(); it++) {
            if ((*it)->isConnected()) {
                Serial.println((*it)->getPeerAddress().toString().c_str());
                Serial.println("Disconnecting");
                (*it)->disconnect();
            }
        }
    }
    NimBLEDevice::deinit();
    delay(30000);
    Serial.println("REBOOTING");
    ESP.restart();
    return;
}

serial output,

14:27:54.192 -> Starting BLE
14:28:24.240 -> Disconecting and ReBooting ESP32...
14:28:54.250 -> REBOOTING

1 Like