ArduinoBLE icon indicating copy to clipboard operation
ArduinoBLE copied to clipboard

How to setEventHandler in Custom Class?

Open JAICHANGPARK opened this issue 4 years ago • 16 comments

Hello I made my custom class I used example code about callback led


#include <ArduinoBLE.h>
const int ledPin = LED_BUILTIN; // pin to use for the LED


class Sample0 {
  private:
  BLEService _ledService;
  BLEByteCharacteristic _switchCharacteristic;
public:
Sample0() : _ledService("19B10000-E8F2-537E-4F6C-D104768A1214"), _switchCharacteristic("19B10001-E8F2-537E-4F6C-D104768A1214", BLERead | BLEWrite){

  Serial.println("init Sample0");
}



  void init(){
// begin initialization
  if (!BLE.begin()) {
    Serial.println("starting BLE failed!");

    while (1)
      ;
  }

  // set the local name peripheral advertises
  BLE.setLocalName("LEDCallback");
  // set the UUID for the service this peripheral advertises
  BLE.setAdvertisedService(_ledService);

  // add the characteristic to the service
  _ledService.addCharacteristic(_switchCharacteristic);

  // add service
  BLE.addService(_ledService);

  // assign event handlers for connected, disconnected to peripheral
  BLE.setEventHandler(BLEConnected, blePeripheralConnectHandler);
  BLE.setEventHandler(BLEDisconnected, blePeripheralDisconnectHandler);

  // assign event handlers for characteristic
  _switchCharacteristic.setEventHandler(BLEWritten, switchCharacteristicWritten);
  // set an initial value for the characteristic
  _switchCharacteristic.setValue(0);

  // start advertising
  BLE.advertise();  

  Serial.println(("Bluetooth device active, waiting for connections..."));

  }

   void blePeripheralConnectHandler(BLEDevice central) {
  // central connected event handler
  Serial.print("Connected event, central: ");
  Serial.println(central.address());
}

 void blePeripheralDisconnectHandler(BLEDevice central) {
  // central disconnected event handler
  Serial.print("Disconnected event, central: ");
  Serial.println(central.address());
}

 void switchCharacteristicWritten(BLEDevice central, BLECharacteristic characteristic) {
  // central wrote new value to characteristic, update LED
  Serial.print("Characteristic event, written: ");

  if (characteristic.value()) {
    Serial.println("LED on");
    // digitalWrite(ledPin, HIGH);
  } else {
    Serial.println("LED off");
    // digitalWrite(ledPin, LOW);
  }
}
};

// BLEService ledService("19B10000-E8F2-537E-4F6C-D104768A1214"); // create service
// BLEByteCharacteristic switchCharacteristic("19B10001-E8F2-537E  4F6C-D104768A1214", BLERead | BLEWrite);

void setup() {
  Serial.begin(9600);
  while (!Serial);
  pinMode(ledPin, OUTPUT);  // use the LED pin as an output

   Sample0 sample;
   sample.init();
}

void loop() {
  // poll for BLE events
  BLE.poll();
}

Error code

error: invalid use of non-static member function

JAICHANGPARK avatar May 27 '21 03:05 JAICHANGPARK

error: invalid use of non-static member function 'void Sample0 ::blePeripheralConnectHandler(BLEDevice)'
     BLE.setEventHandler(BLEConnected, blePeripheralConnectHandler);

JAICHANGPARK avatar May 27 '21 04:05 JAICHANGPARK

Hi @JAICHANGPARK , as the error says, the callback should be a static function

polldo avatar May 27 '21 12:05 polldo

Hi @JAICHANGPARK.

A simple way to solve this is to store the instance of your class in a static variable, and then retrieve it in you static callback.

For example, your class could be:

class Sample0 {
  private:
  BLEService _ledService;
  BLEByteCharacteristic _switchCharacteristic;
public:
Sample0() : _ledService("19B10000-E8F2-537E-4F6C-D104768A1214"), _switchCharacteristic("19B10001-E8F2-537E-4F6C-D104768A1214", BLERead | BLEWrite){
  static Sample0* myInstance = this;
  this->_switchCharacteristic.setEventHandler(BLERead, onSwitchRead);
  Serial.println("init Sample0");
}
  void test(uint8_t* value) {
   // whatever
}
}

And the callback would be something like:

static void onSwitchRead(BLECentral central, BLECharacteristic characteristic) {
  Sample0::myInstance->test(characteristic.value());
}

Of course, you may need to store a pointer to your instance in some other way, or have a singleton Sample0, etc. But the basic idea will be the same. Since the callback needs to be static, you need a way to statically retrieve your instance.

Hope this helps!

glsubri avatar Jul 08 '21 06:07 glsubri

Does the ArduinoBLE plan to extend implementation for multiple Bluetooth modules available on the one Arduino device?

mklemarczyk avatar Apr 06 '22 19:04 mklemarczyk

Agree with @mklemarczyk the events going through a static only good for 1 device and necessitates the needs for statics.

In NimBLE32 for the ESP32, they define a NimBLEServerCallbacks that is used as a vehicle to send the devices.

In your implementation, in stead of calling the _eventsHandler make a call to an instance of the NimBLECallback

On the implementation side, override the NimBLEServerCallback class from above and pick up on onConnected or onDisconnected

https://github.com/lathoub/Arduino-BLE-MIDI/blob/8dee1f7d257aa71aeb0e956632da2dd2ae9ed496/src/hardware/BLEMIDI_ESP32_NimBLE.h#L72-L94

Happy to help

lathoub avatar Jun 05 '22 05:06 lathoub

Here is a rough implementation: https://github.com/lathoub/ArduinoBLE Changes: https://github.com/arduino-libraries/ArduinoBLE/compare/master...lathoub:master

usage:

template <class _Settings>
class MyServerCallbacks : public BLELocalDeviceCallbacks
{
public:
    MyServerCallbacks(BLEMIDI_ArduinoBLE<_Settings> *bluetooth)
        : _bluetooth(bluetooth)
    {
    }

protected:
    BLEMIDI_ArduinoBLE<_Settings> *_bluetooth = nullptr;

    void onConnect(void *)
    {
        if (_bluetooth)
            _bluetooth->connected();
    };

    void onDisconnect(void *)
    {
        if (_bluetooth)
            _bluetooth->disconnected();
    }
};
   BLE.setCallbacks(new MyServerCallbacks<_Settings>(this));

lathoub avatar Jun 05 '22 08:06 lathoub

@glsubri @mklemarczyk @lathoub Thanks all!!

JAICHANGPARK avatar Jun 05 '22 09:06 JAICHANGPARK

Thanks @lathoub What do you think about making a contribution to the library? I think to extend it later by the BLERead and BLEWritten events for BLECharacteristic.

mklemarczyk avatar Jun 26 '22 11:06 mklemarczyk

Thanks @lathoub What do you think about making a contribution to the library? I think to extend it later by the BLERead and BLEWritten events for BLECharacteristic.

Not sure what you mean @mklemarczyk

lathoub avatar Jun 27 '22 07:06 lathoub

@lathoub The contribution that is presented by you adds a new method setCallbacks for the BLEDevice class. It handles two events that are possible to bind via setEventHandler method. Which is cool and you can bind your instance methods that way to your class instance.

That improvement provide solution only for the onConnect and onDisconnect events of BLEDevice class. There is yet another class congaing events and setEventHandler method. That class is called BLECharacteristic end expose two other events onRead and onWrite when the characteristic is being read or wrote from external. You can not bind your instance methods to it as well. I propose to handle it as well with introduction of the new method setCallbacks in BLECharacteristic class.

Another question, do you plan to create PR with your improvements to the ArduinoBLE repository?

mklemarczyk avatar Jul 02 '22 09:07 mklemarczyk

@mklemarczyk ah, I see what you mean now. Let me revisit the code to get back into it. No promisses.

lathoub avatar Jul 02 '22 20:07 lathoub

I have implemented the above in https://github.com/lathoub/ArduinoBLE (device and char callbacks). I'll add some callback examples now, to see how it behaves (but i will need help on this, I'm not a BLE expert)

lathoub avatar Jul 03 '22 07:07 lathoub

added examples (central and peripheral) , but i don't have the hardware here, so need help testing

lathoub avatar Jul 03 '22 09:07 lathoub

@mklemarczyk gentle nudge

lathoub avatar Jul 13 '22 05:07 lathoub

@lathoub Thank you, this is exactly what I was looking for. Sorry for the late response, I am stuck on problem with ArduinoBLE and FastLED libraries. I will test the improvements on my side next weekend.

Do you think it can be integrated in the official repo?

mklemarczyk avatar Aug 07 '22 19:08 mklemarczyk

Do you think it can be integrated in the official repo?

There is fair chance, the code is incremental with little impact on the rest - and - it makes the event listening more flexible and extendible. @per1234 is listening is, so Arduino is aware .

Looking forward to your testing results @mklemarczyk

lathoub avatar Aug 08 '22 14:08 lathoub