I am writing a class for a project which will take care of handling any LCD updates for my project. The way I want to handle this is to initialize the LCD object in my main file, and then pass the LCD object on to my own class when initializing it. The LCD object should be declared a private object in my class so several member functions can access it.
The problem is I can't find how to initialize the object correctly in the .h file. Below is what I currently have, but when I try to build it, I get:
LCDController.h:_Clcd1' should be initialized
LCDController.h:_Clcd2' should be initialized
main .ino file:
void setup()
{
//LiquidCrystal lcd(RS,RW,Enable1,Enable2, data3,data2,data1,data0);
LiquidCrystal lcd(12, 11, 7, 6, 5, 4); //declare two LCD's
LiquidCrystal lcd2(12, 10, 7, 6, 5, 4); // Ths is the second
LCDController.init(lcd, lcd2);
}
LCDController.h file:
// LCDController.h
#ifndef _LCDCONTROLLER_h
#define _LCDCONTROLLER_h
#if defined(ARDUINO) && ARDUINO >= 100
#include "Arduino.h"
#else
#include "WProgram.h"
#endif
#include <LiquidCrystal.h>
class LCDControllerClass
{
protected:
public:
void init(LiquidCrystal& Clcd1, LiquidCrystal& Clcd2);
private:
void _UpdateLCD(int iLine, int iPosition, String cText);
LiquidCrystal& _Clcd1;
LiquidCrystal& _Clcd2;
};
extern LCDControllerClass LCDController;
#endif
LCDController.cpp file:
#include "LCDController.h"
#include <LiquidCrystal.h>
void LCDControllerClass::init(LiquidCrystal& Clcd1, LiquidCrystal& Clcd2)
{
_Clcd1 = Clcd1;
_Clcd1.begin(40, 2);
_Clcd1.clear();
_Clcd2 = Clcd2;
_Clcd2.begin(40, 2);
_Clcd2.clear();
}
-
What happens if you move the lcd and lcd2 definitions outside the setup function?rslite– rslite2015年03月14日 01:56:25 +00:00Commented Mar 14, 2015 at 1:56
1 Answer 1
In C++, a class that holds reference members (in your sample, LiquidCrystal& _Clcd1;
and LiquidCrystal& _Clcd2;
) must ensure these references are initialized at construction time, not later.
This means your void init(LiquidCrystal& Clcd1, LiquidCrystal& Clcd2);
method is not the right way to initialize _Clcd1
and _Clcd2
because it will be called after construction time.
You have 2 ways to solve this issue,t hat I will detail further below.
HOWEVER, before we discuss the solutions, you must be aware that your sample code has a huge defect in setup()
method code!
void setup()
{
//LiquidCrystal lcd(RS,RW,Enable1,Enable2, data3,data2,data1,data0);
LiquidCrystal lcd(12, 11, 7, 6, 5, 4); //declare two LCD's
LiquidCrystal lcd2(12, 10, 7, 6, 5, 4); // Ths is the second
LCDController.init(lcd, lcd2);
}
Here, you create lcd
and lcd2
in your setup
method, but their life ends at the end of setup()
, because they have been allocated on the stack. Using these outside setup()
will most likely cause your program to crash as soon as your loop()
tries to access lcd
or lcd2
, indirectly through LCDController
method calls.
To remove this defect, you must declare these variables outside setup
:
//LiquidCrystal lcd(RS,RW,Enable1,Enable2, data3,data2,data1,data0);
LiquidCrystal lcd(12, 11, 7, 6, 5, 4); //declare two LCD's
LiquidCrystal lcd2(12, 10, 7, 6, 5, 4); // Ths is the second
void setup()
{
LCDController.init(lcd, lcd2);
}
Now you can use them anywhere in your program, you are sure they are present and "alive".
Now we can go to the solutions of the original compilation problem.
1. Initialize references at construction by adding a constructor
class LCDControllerClass
{
public:
LCDControllerClass(LiquidCrystal& Clcd1, LiquidCrystal& Clcd2)
: _Clcd1(Clcd1), _Clcd2(Clcd2) {}
...
};
You can still keep an void init()
method to complete the initialization if you don't want it to occur right at construction time, or you can fully integrate the current init()
content into the constructor and re,voe init()
altogether:
class LCDControllerClass
{
public:
LCDControllerClass(LiquidCrystal& Clcd1, LiquidCrystal& Clcd2)
: _Clcd1(Clcd1), _Clcd2(Clcd2)
{
_Clcd1.begin(40, 2);
_Clcd1.clear();
_Clcd2.begin(40, 2);
_Clcd2.clear();
}
...
};
In anycase, you will have to change the way you instantiate your LCDControllerClass LCDController
which you did not show in your sample code, that would be something like this:
LiquidCrystal lcd(...);
LiquidCrystal lcd2(...);
LCDControllerClass LCDController(lcd, lcd2);
That obviously requires that you can instantitate lcd
and lcd2
before creating LCDController
. If that is not possible for you, then you'll have to use the second way.
2. Replace references with pointers
As mentioned before, in a class reference members MUST be initialized at construction time (moreover, they cannot be changed afterwards, to point somewhere else). This is not the case for pointers however:
class LCDControllerClass
{
public:
void init(LiquidCrystal* Clcd1, LiquidCrystal* Clcd2);
private:
void _UpdateLCD(int iLine, int iPosition, String cText);
LiquidCrystal* _Clcd1;
LiquidCrystal* _Clcd2;
};
void LCDControllerClass::init(LiquidCrystal* Clcd1, LiquidCrystal* Clcd2)
{
_Clcd1 = Clcd1;
_Clcd1->begin(40, 2);
_Clcd1->clear();
_Clcd2 = Clcd2;
_Clcd2->begin(40, 2);
_Clcd2->clear();
}
Note that now, you need to use the arrow notation ->
to access _Clcd1
and _Clcd2
members (dot notation cannot work for pointers).
The rest of the code should be slightly modified to reflect this change:
//LiquidCrystal lcd(RS,RW,Enable1,Enable2, data3,data2,data1,data0);
LiquidCrystal lcd(12, 11, 7, 6, 5, 4); //declare two LCD's
LiquidCrystal lcd2(12, 10, 7, 6, 5, 4); // Ths is the second
void setup()
{
LCDController.init(&lcd, &lcd2);
}
Note that we now have to pass the address of lcd
and lcd2
to init()
, by using &
before their names.
-
Thanks for this great answer! Besides fixing my actual problem, you took the time to explain what was going wrong and I also understand the how and why. Thanks for making StackExchange great!Alex– Alex2015年03月14日 09:54:22 +00:00Commented Mar 14, 2015 at 9:54
-
Could you also explain how I can later on in another member function of my
LCDControllerClass
create what I think should be another pointer to either_Clcd1
or_Clcd2
? I have another member function should is in charge of putting text on the LCD, and depending on the line number that is passed to it (can be 1 to 4), it either has to update_Clcd1
or_Clcd2
. I tried to accomplish this by definingLiquidCrystal* CActiveLCD = &_Clcd1
but this does compile but seems to crash my arduino...Alex– Alex2015年03月14日 10:43:30 +00:00Commented Mar 14, 2015 at 10:43 -
@Alex I would suggest you post a new question (it is better than mixing several problems in one question) with the updated code, the explanation of what you want to achieve, and the problem you currently have. Cheers!jfpoilpret– jfpoilpret2015年03月14日 13:12:57 +00:00Commented Mar 14, 2015 at 13:12
-
I was actually thinking if I should post a new question or not :) I just posted arduino.stackexchange.com/questions/9319/…, would be great if you could chime in. Thanks again!Alex– Alex2015年03月15日 00:33:03 +00:00Commented Mar 15, 2015 at 0:33