arduino-libraries / ArduinoBLE

ArduinoBLE library for Arduino
GNU Lesser General Public License v2.1
314 stars 205 forks source link

How to setEventHandler in Custom Class? #182

Open JAICHANGPARK opened 3 years ago

JAICHANGPARK commented 3 years ago

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 commented 3 years ago
error: invalid use of non-static member function 'void Sample0 ::blePeripheralConnectHandler(BLEDevice)'
     BLE.setEventHandler(BLEConnected, blePeripheralConnectHandler);
polldo commented 3 years ago

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

glsubri commented 3 years ago

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!

mklemarczyk commented 2 years ago

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

lathoub commented 2 years ago

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 commented 2 years ago

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));
JAICHANGPARK commented 2 years ago

@glsubri @mklemarczyk @lathoub Thanks all!!

mklemarczyk commented 2 years ago

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.

lathoub commented 2 years ago

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

mklemarczyk commented 2 years ago

@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?

lathoub commented 2 years ago

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

lathoub commented 2 years ago

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 commented 2 years ago

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

lathoub commented 2 years ago

@mklemarczyk gentle nudge

mklemarczyk commented 2 years ago

@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?

lathoub commented 2 years ago

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