Go Down

Topic: difficulty using attachInterrupt that uses a member function in a library  (Read 58 times) previous topic - next topic

Charlie1985

I know that there are literally dozens of posts on this already and I have read them but I just don't seem to understand what exactly they are saying.  I am hoping that one of you our there in the genius sphere can explain it like you were teaching a small semi-literate child.

I have been reading and experimenting all day and the more I read on the forums the more confused I get and the more I experiment the more frustrated I am becoming.  I am completely baffled as to what exactly is the problem and why I can't make this work.

Here's my issue.  I am writing a library that includes an interrupt.  Inside this interrupt I need to call a function from the library.  Every time I try it throws out different error message which don't make any since to me.  For instance it says that it can't call member function without object but the member function being called is in an object.  I know I'm doing something stupid because there is no way it is this difficult.  In Arduino this would be a piece of cake but trying to move some of my code over to libraries has proven to be quite a different issue.  Here's a sample code of what I want to do.  Can someone please tell me what in the world I need to do to make it work.

*.h
Code: [Select]

class CharlieLibrary{
public:

CharlieLibrary();

void DoStuff();
static void InterruptStuff();
};


*.cpp
Code: [Select]

#include <CharlieLibrary.h>

CharlieLibrary::CharlieLibrary(){
attachInterrupt(2,InterruptStuff,RISING);
}

void CharlieLibrary::DoStuff(){
// do important stuff

Serial.println("please God compile")
}

static void CharlieLibrary::InterruptStuff(){
DoStuff();
}




Arduino code
Code: [Select]


CharlieLibrary test;

void Setup(){
Serial.begin(9600);
}


Delta_G

Quote
I am hoping that one of you our there in the genius sphere can explain it like you were teaching a small semi-literate child.
You can't use a member function as an ISR unless it is a static member.  It's that simple. 

If it is a static member then it doesn't have access to any of the non-static member variables. 

The problem is like this:

Imagine you have an animal class, and it defines an interrupt.  You have two instances, cat and dog.  An ISR can take no parameters and it can return no values.  All it can do is run code.  So if there is an ISR in the animal class and it gets triggered, which instance of the function should it call?  The one in cat or the one in dog?  You'd have to pass an argument to tell it which one and an ISR can't take any arguments. 

In your case you have a static member function.  great, so all instances of the class share that function.  There's no ambiguity and that function can be an ISR. 

But that function tries to call a member function.  You can't do that without having an instance of the class.

And that's the why.

How to fix is simple.  Define a function that isn't part of the class that calls the class function you want to call.  Unless your library already defines an instance of the class, then this will probably have to be done in the sketch because you'll need the instance. 

OR

Make the whole class static.  That means everything.  That means that you can only ever have one instance of the class in any program.  That might be your intention.  I don't know. 

SO do you want to be able to have more than one CharlieLibrary instance in your program?  Or will there only ever be one?

If one, then you can make the whole thing static, but it would probably be easier to just write the library without the class. 

|| | ||| | || | ||  ~Woodstock

Please do not PM with technical questions or comments.  Keep Arduino stuff out on the boards where it belongs.

Charlie1985

Thank you so much for the rapid response and I apologize for not replying back sooner, I might have fallen asleep at my desk.  I think I understand the concept that you are conveying, and I don't want to sound overly dumb or intentionally obtuse but I can't quite grasp the solution you provided for how to fix it.  I will start by answering your question about what my goal or rather what I'm trying to accomplish is.  Currently my code is 8000+ lines across 12 different tabs in Arduino controlling an army of automated functions and pieces of the code are used across a variable menagerie of devices.  I want to take a few thousand lines of that code that is used throughout my systems and convert it to various libraries to make each of the individual device codes less cumbersome and easy to trouble shoot without dealing with code that has nothing to do with the current task.

While my ISR is short it still needs to modify some global variables, read or write to one of my spi devices, and flip some flags that I have.  I am only calling a single instance of the class to which all of these functions are members, but there are a number of global variables that get changed within the class.  I will openly admit that I am probably the worlds weakest programmer when it comes to classes and objects.

I just can't seem to translate the solutions you provided into actual code. 

In my mind I read your solution and I think ok this must be the solution, but it just causes other errors.

*.h
Code: [Select]


static void InterruptStuff();

class CharlieLibrary{
public:

CharlieLibrary();

void DoStuff();
};




*.cpp
Code: [Select]


#include <CharlieLibrary.h>

static void InterruptStuff(){
CharlieLibrary::DoStuff();
}


CharlieLibrary::CharlieLibrary(){
attachInterrupt(2,InterruptStuff,RISING);
}

void CharlieLibrary::DoStuff(){
// do important stuff

Serial.println("please God compile")
}





Arduino Code:
Code: [Select]



CharlieLibrary test;

void Setup(){
Serial.begin(9600);
}



Is there any chance you know of an example code online somewhere that shows this being done.  Please forgive me for not knowing or understanding this.  I am very much still learning.

MorganS

Serial is a good example of the kind of thing you need to do. There is only ever one Serial. You don't create it. It's already created for you. It uses interrupts to do its work. Those interrupts call the one-and-only Serial object to do work.

Not that I'm suggesting you dig into the Serial library to understand it - it's too complex for a beginner. Something like SPI may be easier to understand. If you open up the SPI library, you will see how it creates an SPI object for you inside the .h file.

So your complex system that you don't want to share, does it have only one of these special objects? Or do you create a new one for each sensor or whatever?
"The problem is in the code you didn't post."

Delta_G

You're going to hate me for this.  But I'm going to tell you how to fix it and then I'm going to tell you why you need to start over instead of fixing it. 

Code: [Select]
static void InterruptStuff(){
CharlieLibrary::DoStuff();
}


Since DoStuff() isn't static, you can't call it like that.  You have to have an instance of the class. 

Step 1:  Get rid of the constructor and put this line in a function you can call from setup:

Code: [Select]
CharlieLibrary::CharlieLibrary(){
attachInterrupt(2,InterruptStuff,RISING);
}



Then add the static keyword to DoStuff and have your interrupt call that directly. 

But wait, next post has more.  You got bigger problems. 

|| | ||| | || | ||  ~Woodstock

Please do not PM with technical questions or comments.  Keep Arduino stuff out on the boards where it belongs.

Delta_G

Quote
global variables that get changed within the class
No no no no no no no no no.

Classes ONLY modify what they own.  Classes should not need anything that doesn't live in them.  While it will technically work that way, it's really bad practice and it will bite you in the end.  This is a recipe for very confusing code.  Far worse than just having a big huge thing that's hard to look for.  Nothing is worse than a variable getting modified where you don't expect it to.   

If this code needs to interact with the rest of your code in that way then you need to just make it not a class.  You can make a library that doesn't contain a class.  Most do, but it isn't a rule. 

I would make it a namespace so you know it won't clash with any other names.  So you can still have that advantage of the class. 

But if it needs to interact with global variables then it either needs to take pointers to those as members or it needs to not be a class.  In your case, it really needs to be not a class. 


Quote
While my ISR is short it still needs to modify some global variables, read or write to one of my spi devices, and flip some flags that I have.
Be very sure you know what you're doing before you mess with SPI in the ISR.  It's not like Serial where you can't do it at all.  But there are some considerations there if you don't want to lose transmissions. 
|| | ||| | || | ||  ~Woodstock

Please do not PM with technical questions or comments.  Keep Arduino stuff out on the boards where it belongs.

Delta_G

Code: [Select]


static void InterruptStuff();

class CharlieLibrary{
public:

CharlieLibrary();

void DoStuff();
};


One last item I forgot.  InterruptStuff shouldn't be static.  Since it isn't part of a class the static means something different here.  Here it means that it is only available inside this one file.  So that will keep you from calling it from your main code. 
|| | ||| | || | ||  ~Woodstock

Please do not PM with technical questions or comments.  Keep Arduino stuff out on the boards where it belongs.

Charlie1985

Hi Morgan,

Thank you for the recommendation on the SPI library, and fair enough for calling me out on not sharing my entire code.  I unfortunately can't share it because the person I developed it for is a little paranoid about IP and I'm trying to respect his wishes, even though he ignored me when I told him to hire a professional programmer rather than a physicist.  Plus who would want to sort through that monstrosity of a program :). I figured the example code I provided would demonstrate both my problem and my objective.  I apologize if it is inadequate to the task.

This is object is the only one that has to handle interrupts.  All of the other ones do far more simple tasks, just lots and lots of different ones.  The object in questions is handling my wireless communications and dynamic mesh networking.   Using it in the Arduino code works flawlessly but my plan to simplify my code by making it into libraries has had pretty much the exact opposite effect and made it far more complicated.  Well for now anyway.



Hi Delta_G,

Perhaps I misspoke or misunderstand objects (which is truly more likely).  All of the variables being modified are called and created within the object.  Inputs and outputs from methods being used by other functions libraries or the main code are done using pointers.  I like the idea of not using classes at all.  I am definitely going to look into that. 

if I'm being honest though another hour of this frustration and I will just leave the massive code sections exactly as they are with a whole slew of extra tabs.  It's just irritating if I do change something in one Arduino program to have to go device code by device code and make the same change to the same tab in each of the different programs.

Delta_G

Quote
All of the variables being modified are called and created within the object.
So why you call them globals then?


Quote
I unfortunately can't share it because the person I developed it for is a little paranoid about IP and I'm trying to respect his wishes,
Sorry I don't work on things that aren't open source.  You'll have to find someone else to help you. 


Tell your friend he is stupid.  Hiding the code isn't how you make money in this game.  If you hide the code someone like me will copy it.  It is inevitable.  I don't need to be able to see your source to copy your code.  I can just look at how the device works if I want it.  Keeping the code a secret used to be the way, but it just doesn't work like that anymore.  Unless you're microsoft or google or someone with armies of coders that can stay ahead of people like me, then all you're doing by keeping the code secret is guaranteeing that it won't sell long.  Pretty soon they'll be building copies with my free software.  Or someone like me. 

And judging from the types of questions you are asking, you're not going to write code I (or someone like me) can't just replicate.  Protect your IP, but the code isn't the part that matters.  I can have that if you share it or not.  At least if it is open source you get to keep the credit. 

|| | ||| | || | ||  ~Woodstock

Please do not PM with technical questions or comments.  Keep Arduino stuff out on the boards where it belongs.

Go Up