I am currently writing a custom library for the LoRa SX1278 but am stuck on a receive subroutine.
I first pass a function that i want to run when the module receives a packet to the ContinuousRXSetup function and set this function to a class wide variable _onReceive.
I then attach my onDIO0Rise function to the corresponding pin. In this function i then call OnCall, to minimise interruption times, finally OnCall resets some registers and calls the _onReceive function. Does anyone have any ideas how to better implement this as clearly I have made a mistake.
Well you learn something new every day! I had not realized before that the value of a pointer to a function would be the same as the value of the function name.
johnwasser:
Well you learn something new every day! I had not realized before that the value of a pointer to a function would be the same as the value of the function name.
I'm glad, after learning so much from you...
example here:
class CallBackCLass {
public:
CallBackCLass(void(*action)(void)) : callback(action){}
void action() {
if (callback) { // can inspect callback, here, if not null pointer
callback(); // can operate callback
Serial.println(F("Action Completed"));
} else {
Serial.println(F("Nothing to do here"));
}
}
private:
void(*callback)(void);
};
void someAction(void);
void setup() {
Serial.begin(9600);
CallBackCLass myInstance = someAction; // equivalent to: CallBackCLass myInstance(someAction);
myInstance.action();
CallBackCLass myOtherInstance = nullptr;
myOtherInstance.action(); // nothing happens here...
}
void loop() {
}
void someAction(void) {
Serial.println(F("Some Action"));
}
I have implemented those changes as to fit my library but since I don't want to initialise the class in the library but do still want to maintain the global scope of onReceive within that library the code returns the following errors:
Arduino: 1.8.5 (Windows 10), Board: "Arduino Nano, ATmega328P"
...\Arduino\libraries\CustomLoRa\CustomLoRa.cpp: In member function 'uint8_t LoRaClass::ContinousRXSetup(void (*)(), uint8_t)':
...\Arduino\libraries\CustomLoRa\CustomLoRa.cpp:317:85: error: only constructors take member initializers
uint8_t LoRaClass::ContinousRXSetup(void(*onReceive)(void), uint8_t sizeOfPacket) : callback(onReceive) {
^
...\Arduino\libraries\CustomLoRa\CustomLoRa.cpp:322:78: error: invalid use of non-static member function
attachInterrupt(digitalPinToInterrupt(_dio0), LoRaClass::onDio0Rise, RISING);
^
exit status 1
Error compiling for board Arduino Nano.
I have attached my library with the amendments according to your syntax.
Ok i see how that works but is there not a more simple method (according to the perspective of the user of the library) as having to include the line: fan1->setup(fan1pin, {fan1->handleInterrupt();}, FALLING); is not very appealing to novices that wish to use the library.
The reason why I ask is because the library that I ported from (available via the library manager) LoRa by Sandeep Mistery has a identical method for an interrupt but has a much simpler setup method without any "work around". But does not verify in my library. However Sandeep's library compiles perfectly fine, here is an extract of what Sandeep has done (I have removed all the registry activities):
LoRaClass::LoRaClass() :
_spiSettings(8E6, MSBFIRST, SPI_MODE0),
_ss(LORA_DEFAULT_SS_PIN), _reset(LORA_DEFAULT_RESET_PIN), _dio0(LORA_DEFAULT_DIO0_PIN),
_frequency(0),
_packetIndex(0),
_implicitHeaderMode(0),
_onReceive(NULL) //not sure what this exactly does in the initialisation of the class
{
// overide Stream timeout value
setTimeout(0);
}
void LoRaClass::onReceive(void(*callback)(int)) //in setup the user can simply type onReceive(a_function); where a_function function is called every time a packet is received
{
_onReceive = callback;
if (callback) {
attachInterrupt(digitalPinToInterrupt(_dio0), LoRaClass::onDio0Rise, RISING);
}
else {
detachInterrupt(digitalPinToInterrupt(_dio0));
Serial.println("Failed to attach interrupt");
}
}
void LoRaClass::handleDio0Rise()
{
if (_onReceive) {
_onReceive(packetLength);
}
}
void LoRaClass::onDio0Rise()
{
LoRa.handleDio0Rise();
}
//corresponding stuff from header file
class LoRaClass : public Stream {
public:
LoRaClass();
void onReceive(void(*callback)(int));
private:
static void onDio0Rise();
void handleDio0Rise();
private:
void (*_onReceive)(int);
Is the above a valid method of doing what I am i trying to achieve, if so why does it not work for me, or is it incorrect but simply not caught by the compiler.
I hope you can see the source of my confusion.
bakerw71:
Ok i see how that works but is there not a more simple method (according to the perspective of the user of the library) as having to include the line: fan1->setup(fan1pin, {fan1->handleInterrupt();}, FALLING); is not very appealing to novices that wish to use the library.
The reason why I ask is because the library that I ported from (available via the library manager) LoRa by Sandeep Mistery has a identical method for an interrupt but has a much simpler setup method without any "work around". But does not verify in my library. However Sandeep's library compiles perfectly fine, here is an extract of what Sandeep has done (I have removed all the registry activities):
I hope you can see the source of my confusion.
yes, as I mentioned now twice:
You can make the function static
like the author of the code did...
private:
static void onDio0Rise();
because , as I mentioned in my first reply, a static member function does not use the this pointer, so passing a function pointer with a void(*)(void) signature is possible.
If you are only ever creating exactly one instance of this class, that may work for you. Example:
Out of curiosity, is it possible to define a function that expects as its argument a pointer to a class instance method? Let’s say that instance method doesn’t expect any arguments. What would such a definition look like?
gfvalvo:
Out of curiosity, is it possible to define a function that expects as its argument a pointer to a class instance method? Let’s say that instance method doesn’t expect any arguments. What would such a definition look like?
BulldogLowell:
I'm not sure what you mean... maybe try to write what you want to try in pseudo code.
I guess what I’m asking is as follows. Here’s one way of providing a class instance with a call back function. It only works with “regular” functions (i.e. no implicit ‘this’ pointer in the signature).
So, my question, is it possible to define a pointer to an instance function (i.e. expecting the implicit ‘this’ pointer in the signature)? So, you could do something like this:
When I have a need to do something like this, I use static methods, and the class has a mechanism to register the instance who's method is to be called. The interrupt handler calls the static function which calls the appropriate member function for the correct instance.
gfvalvo:
I guess what I’m asking is as follows. Here’s one way of providing a class instance with a call back function. It only works with “regular” functions (i.e. no implicit ‘this’ pointer in the signature).
My impulse would be so solve the issue using a functor, overloading the paren-paren operator ():
class Example {
public:
Example(void(*action)(void)) : callback(action){}
int operator() (Example& A) {
callback();
A.value = 4; // can modify by reference
return A.value + value; // state of passed object "A" depends on instance state fo the caller
}
private:
int value = 2;
void(*callback)(void);
};
Example first([](){Serial.println("FIRST");});
Example second([](){Serial.println("SECOND");});
void setup() {
Serial.begin(9600);
Serial.println(first(second));
}
void loop() {
}
Note: this isn't a developed solution for the attachInterrupt() issue, just an attempt at answering @gfvalvo's question!
Yes, I see how the lambda creates a function pointer with proper signature to make the Example class’s constructor happy. So, between this and PaulS’s suggestion (static class function) there at least two ways of doing it, neither being the technique I asked about.
So, I’m coming to the conclusion that the way I wanted to do it isn’t possible. But, still haven’t yet heard why it isn’t
Yes, I see how the lambda creates a function pointer with proper signature to make the Example class’s constructor happy. So, between this and PaulS’s suggestion (static class function) there at least two ways of doing it, neither being the technique I asked about.
So, I’m coming to the conclusion that the way I wanted to do it isn’t possible. But, still haven’t yet heard why it isn’t
to be honest, I'm not sure what you are asking... your pseudo example didn't contain any call to attachInterrupt().
BulldogLowell:
to be honest, I'm not sure what you are asking... your pseudo example didn't contain any call to attachInterrupt().
My question isn’t about attachInterrupt(). I simply want to provide a way to specify a class that has a call back feature for each instance. The call back function provided to each instance (either in the constructor or with an attach() method) happens to be an instance method of a different class rather than a "regular" function. Look at my pseudo code again:
Each instance of Class1 needs to be provided with a call back function. I want that call back function for instance1 (of Class1) to be the instanceFunction() of instance2 (of Class2).