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

Request of setting notify_callback as class instance.

Open asukiaaa opened this issue 3 years ago • 5 comments

I tried to set notify_callback with using class member method but could not. (scanCompleteCB also) Could you support CharacteristicCallbacks class to set notify_callback like AdvertisedDeviceCallbacks or ClientCallbacks?

Is there any way to do that current functions?

My use case:

class Controller() {
 public:
  bool afterConnect(NimBLEClient* pClient) {
    for (auto pService : *pClient->getServices(true)) {
      auto sUuid = pService->getUUID();
      if (!sUuid.equals(uuidServiceHid)) {
        continue;  // skip
      }
      Serial.println(pService->toString().c_str());
      for (auto pChara : *pService->getCharacteristics(true)) {
        charaRead(pChara);
        charaSubscribeNotification(pChara);
      }
    }

    return true;
  }

  void charaSubscribeNotification(NimBLERemoteCharacteristic* pChara) {
    if (pChara->canNotify()) {
      charaPrintId(pChara);
      Serial.println(" canNotify ");
      if (pChara->subscribe(true, notifyCB, true)) { // <<<<<< ========== Here
        Serial.println("set notifyCb");
        // return true;
      } else {
        Serial.println("failed to subscribe");
      }
    }
  }

  void notifyCB(NimBLERemoteCharacteristic* pRemoteCharacteristic, uint8_t* pData,
                size_t length, bool isNotify) {
    // update controller state
  }
}

Controllelr myController;

Thank you for sharing an useful library.

asukiaaa avatar Jan 29 '22 08:01 asukiaaa

You can use std::bind to set a class member function as callback function:

  void charaSubscribeNotification(NimBLERemoteCharacteristic* pChara) {
    if (pChara->canNotify()) {
      charaPrintId(pChara);
      Serial.println(" canNotify ");
      if (pChara->subscribe(true, std::bind(&Controller::notifyCB, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4), true)) { 
        Serial.println("set notifyCb");
      } else {
        Serial.println("failed to subscribe");
      }
    }

sivar2311 avatar Jan 29 '22 09:01 sivar2311

@sivar2311 Thank you for the information. I could call!

I tried calling scanEndCB with using the bind function but I cannot pass compilcation. Could you give me some advice?

class Controller {
  void startScan() {
    scanning = true;
    auto pScan = NimBLEDevice::getScan();
    pScan->setAdvertisedDeviceCallbacks(advDeviceCBs);
    pScan->setInterval(45);
    pScan->setWindow(15);
    Serial.println("Start scan");
    // pScan->start(scanTime, scanEndedCB);
    pScan->start(scanTime,
                 std::bind(&Core::scanEndedCB, this, std::placeholders::_1)); // <<<<<<<<<< ======== Here
  }

  void scanEndedCB(NimBLEScanResults results) {
    Serial.println("Scan Ended");
    scanning = false;
  }
};

I got the following error.

In file included from src/main.cpp:3:0:
/home/asuki/pio/shared/XboxSeriesXControllerESP32/src/XboxSeriesXControllerESP32.hpp: In member function 'void XboxSeriesXControllerESP32::Core::startScan()':
/home/asuki/pio/shared/XboxSeriesXControllerESP32/src/XboxSeriesXControllerESP32.hpp:203:76: error: no matching function for call to 'NimBLEScan::start(uint32_t&, std::_Bind_helper<false, void (XboxSeriesXControllerESP32::Core::*)(NimBLEScanResults), XboxSeriesXControllerESP32::Core*, const std::_Placeholder<1>&>::type)'
                  std::bind(&Core::scanEndedCB, this, std::placeholders::_1));
                                                                            ^
In file included from /home/asuki/pio/shared/NimBLE-Arduino/src/NimBLEDevice.h:22:0,
                 from /home/asuki/pio/shared/XboxSeriesXControllerESP32/src/XboxSeriesXControllerESP32.hpp:3,
                 from src/main.cpp:3:
/home/asuki/pio/shared/NimBLE-Arduino/src/NimBLEScan.h:65:25: note: candidate: bool NimBLEScan::start(uint32_t, void (*)(NimBLEScanResults), bool)
     bool                start(uint32_t duration, void (*scanCompleteCB)(NimBLEScanResults), bool is_continue = false);

I triec casting but it did not solve the problem.

    pScan->start(scanTime,
                 (void (*)(NimBLEScanResults))std::bind(
                     &Core::scanEndedCB, this, std::placeholders::_1));
In file included from src/main.cpp:3:0:
/home/asuki/pio/shared/XboxSeriesXControllerESP32/src/XboxSeriesXControllerESP32.hpp: In member function 'void XboxSeriesXControllerESP32::Core::startScan()':
/home/asuki/pio/shared/XboxSeriesXControllerESP32/src/XboxSeriesXControllerESP32.hpp:204:69: error: invalid cast from type 'std::_Bind_helper<false, void (XboxSeriesXControllerESP32::Core::*)(NimBLEScanResults), XboxSeriesXControllerESP32::Core*, const std::_Placeholder<1>&>::type {aka std::_Bind<std::_Mem_fn<void (XboxSeriesXControllerESP32::Core::*)(NimBLEScanResults)>(XboxSeriesXControllerESP32::Core*, std::_Placeholder<1>)>}' to type 'void (*)(NimBLEScanResults)'
                      &Core::scanEndedCB, this, std::placeholders::_1));
                                                                     ^
*** [.pio/build/esp32dev/src/main.cpp.o] Error 1

asukiaaa avatar Jan 29 '22 10:01 asukiaaa

I found the reason because scanCompleteCB is not defined with std::function. https://github.com/h2zero/NimBLE-Arduino/pull/343/files

asukiaaa avatar Jan 29 '22 11:01 asukiaaa

Sorry, I was away for a few hours.

For a "pure" function pointer std::bind does not work. That's why I prefer to use std::function as a callback parameter. This also allows the use of lambda functions.

sivar2311 avatar Jan 29 '22 12:01 sivar2311

Thank you for the information. I also succeeded in compilation with using lambda expression.

      if (pChara->subscribe(
              true,
              [this](NimBLERemoteCharacteristic* pRemoteCharacteristic,
                     uint8_t* pData, size_t length, bool isNotify) {
                notifyCB(pRemoteCharacteristic, pData, length, isNotify);
              },
              true)) {
        Serial.println("set notifyCb");
      } else {
        Serial.println("failed to subscribe");
      }

std::function and std::bind are new for me. Thank you.

asukiaaa avatar Jan 29 '22 13:01 asukiaaa