Skip to content

Make Serial.onReceive() accept std::function #6302

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
1 task done
gonzabrusco opened this issue Feb 18, 2022 · 10 comments · Fixed by #6364
Closed
1 task done

Make Serial.onReceive() accept std::function #6302

gonzabrusco opened this issue Feb 18, 2022 · 10 comments · Fixed by #6364
Assignees
Labels
Type: Feature request Feature request for Arduino ESP32

Comments

@gonzabrusco
Copy link
Contributor

Related area

UART

Hardware specification

ESP32 DevKit

Is your feature request related to a problem?

Hello. It would be useful if you could make onReceive() on HardwareSerial accept std:function so I can point it to a class method with std:bind. You would need to include .

This request could be extended to other callbacks in the framework (like Wifi.onEvent() ).

If you don't want to include by default, could you at least provide us with a MACRO that enables or disables this?

Thanks

Describe the solution you'd like

Make callbacks use functional

Describe alternatives you've considered

Global function. But if I'm programming in pure C++ and I need to access privates variables of a class from the callback, it gets weird. I would prefer to be able to call a class method directly to avoid exposing data outside the class.

Additional context

No response

I have checked existing list of Feature requests and the Contribution Guide

  • I confirm I have checked existing list of Feature requests and Contribution Guide.
@gonzabrusco gonzabrusco added the Type: Feature request Feature request for Arduino ESP32 label Feb 18, 2022
@SuGlider
Copy link
Collaborator

@gonzabrusco

What about this C++ approach?

class Receiver {
  public:
  static void gotUARTdata(void);
};

void Receiver::gotUARTdata(void) {
  Serial.println("Got Data on UART...");
}

void setup() {
  Serial.begin(115200);
  Serial.onReceive(Receiver::gotUARTdata);
}

void loop() {
  // Nothing here as this time.
}

@SuGlider
Copy link
Collaborator

SuGlider commented Feb 19, 2022

I also think that Arduino doesn't support Standard C++
Thus Functional won't work at all...

Could you provide a simple example with std::function that compiles in ESP Arduino?

@SuGlider SuGlider self-assigned this Feb 19, 2022
@SuGlider
Copy link
Collaborator

SuGlider commented Feb 19, 2022

@gonzabrusco

Global function. But if I'm programming in pure C++ and I need to access privates variables of a class from the callback, it gets weird. I would prefer to be able to call a class method directly to avoid exposing data outside the class.

Try this approach:

class Receiver {
  private:
    int a;
    
  public:
    static void gotUARTdata(void);
    void set(int i);
    int get();
};
Receiver r;

void Receiver::gotUARTdata(void) {
  Serial.println("Got Data on UART...");
  Serial.print("Private Receiver::a = ");
  Serial.println(r.get());
  r.set(r.get() + 1);
}

void Receiver::set(int i) {
  a = i;
}

int Receiver::get(void) {
  return a;
}

void setup() {
  r.set(10);   // starting point
  Serial.begin(115200);
  Serial.onReceive(Receiver::gotUARTdata);
}

void loop() {
  // Nothing here as this time.
}

Output --> just hit Enter on Serial Monitor

Got Data on UART...
Private Receiver::a = 10
Got Data on UART...
Private Receiver::a = 11
Got Data on UART...
Private Receiver::a = 12
Got Data on UART...
Private Receiver::a = 13
Got Data on UART...
Private Receiver::a = 14
Got Data on UART...
Private Receiver::a = 15
Got Data on UART...
Private Receiver::a = 16
Got Data on UART...
Private Receiver::a = 17
Got Data on UART...
Private Receiver::a = 18
Got Data on UART...
Private Receiver::a = 19

@atanisoft
Copy link
Collaborator

atanisoft commented Feb 19, 2022

@SuGlider Arduino-ESP32 does in fact support C++11 (or later) which supports std::function. I use it quite extensively in the OpenMRNLite library (latest published version only compiles on 1.0.6, 2.0.x support is complete but not published yet).

ArduinoOTA library (in this repo) also uses std::function today.

@SuGlider
Copy link
Collaborator

Nice @atanisoft !
I'll take a look.

@gonzabrusco
Copy link
Contributor Author

Yes, I see the trick you did there. By declaring as static inside the class the function behaves as a global function for all the objects of that class. I see no real difference between using your method and a global function with global objects (with methods to access class variables). The advantage of functional is that you can bind a non-static class method and be able to access class variables directly without the need of having global objects or specific getters or setters.

Functional works in Arduino and I use it in multiple libraries inside my project. Other examples of libraries already using functional:
https://github.com/emelianov/modbus-esp8266
https://github.com/256dpi/arduino-mqtt

@SuGlider
Copy link
Collaborator

SuGlider commented Feb 19, 2022

I agree that a static function in a class is just a global function for all objects.
But this is the best way to make it work with the C HAL and IDF layers used in Arduino-ESP, not having to refactor everything there.

Using sdt::function has a big problem... make it work with esp32-hal.uart.c
It's a C layer of code which is used to perform everything around UART.

HardwareSerial.cpp is just a CPP adaptation layer to esp32-hal-uart.c

As far as I know, it's not possible to convert std:function to a C function pointer.
@atanisoft @gonzabrusco Any idea to get it done?

@SuGlider
Copy link
Collaborator

I think that the way to make onReceive(std::function) work will be by moving all the processing of events to the HardwareSerial.cpp part of code...

But it will take some time to be done. I think it is a nice improvenment, but it's low priority at this time.
@gonzabrusco, please feel free to provide a PR for this feature, if you feel like helping on it.

@gonzabrusco
Copy link
Contributor Author

Thanks @SuGlider

If I have some time I will try to propose a solution to this. Thanks
Gonzalo

@hreintke
Copy link
Contributor

@SuGlider : A not complex solution is (and largely used) is to add a parameter to the onReceive function.
Like HardwareSerial.onReceive(function, parameter); The onReceive Callback then includes that parameter
uart->onreceive(parameter)

The application looks like this :

#include "Arduino.h"

class SerialHandler
{
	SerialHandler(HardwareSerial hs)
	{
		hs.onReceive(staticReceiveData,this);
	}
	static void staticReceiveData(void* onReceiveParam)
	{
		((SerialHandler*)onReceiveParam)->onReceiveData();
	}
	void onReceiveData()
	{
		hs.printf("Received data\n");
	}
};

void setup()
{
}

void loop()
{
}

I can make a PR for you if like the idea.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Type: Feature request Feature request for Arduino ESP32
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants