C++ Problem with a Class

Hi

5 years ago, a user here called jmknapp, created a Library called "NECIRrcv", that enables Receiving and Sending IR Codes.

Despite the fact that 5 years have passed, this Library still works well. (when running it, I just needed to replace "include WProgram.h" with "include Arduino.h", and all worked well).

Specifically, this code works:

#include "NECIRrcv.h"


#define IRPIN 4    // pin that IR detector is connected to


NECIRrcv ir(IRPIN);



void setup()
{
  Serial.begin(9600);
  Serial.println("NEC IR code reception");

  ir.begin();
}


unsigned long ircode;

void loop()
{
  if(ir.available())
  {
    ircode  =ir.read();
    
    Serial.println(ircode,HEX);
  }
}

The library can be downloaded here: http://forum.arduino.cc/index.php/topic,8494.0.html

Now I have a small problem in C++, which I cannot understand what's causing it.

I tried to do a very simply thing:

To create a simple class, that will be called "IR_Receiver", which will contain an object of NECIRrcv.

The reason I want to create this class, is in order to add some more methods of mine.

But now when I run it, the program compiles and runs, but when I press the IR Remote, nothing appears on the screen..

As if the program is not receiving any codes.

Why is that?

Here is my simple class:

class IR_Receiver
{
  public:
  
  NECIRrcv*  m_ir;

  
  
  IR_Receiver(int SetLeg)
  {
    NECIRrcv ir2(SetLeg);
    m_ir  =&ir2;
    
    m_ir->begin();
  }
  
  
  
  //Methods:
  unsigned long ircode;
  unsigned long ReceiveIfAvailable()
  {
    if(m_ir->available())
    {
      ircode  =m_ir->read();
      
      return ircode;
    }
    else
    {
      return 0;    //No Code Available
    }
  }
};



void setup()
{
  Serial.begin(9600);
  Serial.println("Up");
}


IR_Receiver O_IR_Receiver(4);
unsigned long IR_Code;

void loop()
{
  IR_Code  O_IR_Receiver.ReceiveIfAvailable();
  if(IR_Code!=0)
  {
    Serial.println(IR_Code,HEX);
  }
}

I hope someone can see what's the problem..

Thank you

For me the problem is about life of local variable. In the costructor you declare a ir2 object and assign the address to member m_ir. But when constructor code end, local variable ir2 die. I think you need to allocate the object using the new statement, like this code (not tested):

class IR_Receiver
{ public:
     NECIRrcv*  m_ir;
     IR_Receiver(int SetLeg)
     { m_ir = new NECIRrcv(SetLeg);
       m_ir->begin();
     }

card5: Despite the fact that 5 years have passed, this Library still works well.

As long as you keep the data termites away from your hard drive, software doesn't degrade, so why are you so surprised?

card5: I tried to do a very simply thing:

To create a simple class, that will be called "IR_Receiver", which will contain an object of NECIRrcv.

The reason I want to create this class, is in order to add some more methods of mine.

What you need to do is have your class inherit (class inheritance) the NECIRrcv class. Then you will be able to extend it (extend a class) with your own methods. What I gave here is a VERY rough example (won't compile, won't run, and may not even teach you anything) in order to give you something to search on. I've bolded the keywords you'll want to search.

#include "NECIRrcv.h"
class IR_Receiver : public NECIRrcv {
  public:
    // Members:
    NECIRrcv*  m_ir;
    unsigned long ircode;

    //Methods:
    IR_Receiver(int);
    unsigned long ReceiveIfAvailable();
};

// These two class methods could be placed in the IR_Receiver.cpp file.
// Constructor
IR_Receiver::IR_Receiver(int SetLeg)  {
    NECIRrcv ir2(SetLeg);
    m_ir  =&ir2;
    m_ir->begin();
}

unsigned long IR_Receiver::ReceiveIfAvailable()  {
    if(m_ir->available())  {
        ircode = m_ir->read();
        return ircode;
    }
    else  {
        return 0;    //No Code Available
    }
}



void setup()  {
  Serial.begin(9600);
  Serial.println("Up");
}

IR_Receiver O_IR_Receiver(4);
unsigned long IR_Code;

void loop()  {
  IR_Code  O_IR_Receiver.ReceiveIfAvailable();
  if(IR_Code!=0)  {
    Serial.println(IR_Code,HEX);
  }
}

nid69ita: For me the problem is about life of local variable. In the costructor you declare a ir2 object and assign the address to member m_ir. But when constructor code end, local variable ir2 die. I think you need to allocate the object using the new statement, like this code (not tested):

mstanley: Thank you - But I did not want t inherit on purpose: I want to hide anything from the original class, and be able to "start from scratch", that's why I chose to contain an object, instead of inheriting the class.

nid69ita:

Thank you very much.

I changed the Ctor, and it is now:

  IR_Receiver(int SetLeg)
  {
    m_ir  =new NECIRrcv(SetLeg);
    
    m_ir->begin();
  }

I ran it, and still the program receives nothing...

What else can I do?

I should note that the same code, when not inside a class, works without a problem...

card5: mstanley: Thank you - But I did not want t inherit on purpose: I want to hide anything from the original class, and be able to "start from scratch", that's why I chose to contain an object, instead of inheriting the class.

What else can I do?

Then you will need to move NECIRrcv*  m_ir; outside (ahead) of your class. That will make your m_ir object have global scope thus making it available to your class. I believe the wire library uses the same technique to make a global object available. You can see what I am talking about at the bottom of the Wire.cpp file:

// Preinstantiate Objects //////////////////////////////////////////////////////

TwoWire Wire = TwoWire();

mstanley:
Then you will need to move NECIRrcv* m_ir; outside (ahead) of your class.

Hmm…
Why would I want to do that?
I want the object to be contained in the class…

Like I do with every other class that contains some members…

I think you'll find that the problem here is you're calling the begin() function of the NECIRrcv class from within the constructor of your new class. That won't work.

Anything that does things with pinMode, digitalWrite, etc (i.e., any pin control, and many other things too) won't work in the constructor as it happens before the main() routine has initialized the hardware.

You will have to provide a begin() method of your own which calls m_ir->begin(...) which the user can then put in setup() so the pin configurations happen at the right time.

Other than that, having it in the class as a pointer, and using new NECIRrcv(...) is no problem at all.

majenko: I think you'll find that the problem here is you're calling the begin() function of the NECIRrcv class from within the constructor of your new class. That won't work.

You will have to provide a begin() method of your own which calls m_ir->begin(...) which the user can then put in setup() so the pin configurations happen at the right time.

I tested what you said now.

I created my own Begin() method in my class, and moved the m_ir->begin(); line from my Ctor to that Begin() method.

I then call that Begin() method from the program's setup() function.

I ran it, and it worked!!!!

Thank you for identifying the problem that delayed me so much!! :)

majenko: Anything that does things with pinMode, digitalWrite, etc (i.e., any pin control, and many other things too) won't work in the constructor as it happens before the main() routine has initialized the hardware.

Hmm.. Never knew about that, interesting.

I am curious - is there another way to overcome it, which does not require me to add a Begin() method in my class? (and does not require the developer to call that Begin() )

card5:

majenko: I think you'll find that the problem here is you're calling the begin() function of the NECIRrcv class from within the constructor of your new class. That won't work.

You will have to provide a begin() method of your own which calls m_ir->begin(...) which the user can then put in setup() so the pin configurations happen at the right time.

I tested what you said now.

I created my own Begin() method in my class, and moved the m_ir->begin(); line from my Ctor to that Begin() method.

I then call that Begin() method from the program's setup() function.

I ran it, and it worked!!!!

Thank you for identifying the problem that delayed me so much!! :)

You're welcome ;)

majenko: Anything that does things with pinMode, digitalWrite, etc (i.e., any pin control, and many other things too) won't work in the constructor as it happens before the main() routine has initialized the hardware.

Hmm.. Never knew about that, interesting.

I am curious - is there another way to overcome it, which does not require me to add a Begin() method in my class? (and does not require the developer to call that Begin() )

Nope - that's why pretty much every library has a begin() function.

OK

Is there some webpage in the Arduino website that explains this furthermore? I want to read more about it..

So I know what I can do, and where..

Knowing this subject will help me save hours of not knowing why my code dosn't work...

I don't believe there is one. It's all part of what is known as the "static initialization order fiasco" coupled with the fact that the Arduino API is pretty much fully abstracted.

I understand.

OK thank you very much majenko and everyone..

This was really helpful :)