1

I have a 40x4 LCD which internally consists of 2 40x2 LCDs. To control this LCD I have to create 2 LiquidCrystal objects and than later on in code decide which one to use depending on which of the 4 lines I want to change.

Due to the size of my project, I split all the LCD handling off to a separate class. To make my code as readable and easy as possible, I want to create a member method of this class, to which I can just pass line number 1-4, and have this method take care of deciding which LiquidCrystal object to use.

LCDController.h:

#include <LiquidCrystal.h>
class LCDControllerClass
{
 protected:

 public:
        void init(LiquidCrystal* Clcd1, LiquidCrystal* Clcd2);
 private:
   void _UpdateLCD(int _iLine, int _iPosition, String _strText, int _iFieldLength);
   LiquidCrystal* _Clcd1;
   LiquidCrystal* _Clcd2;
};
extern LCDControllerClass LCDController;

My 2 LiquidCrystal objects are initialized in my main .ino file like so (before the setup() loop) :

LiquidCrystal lcd(12, 11, 7, 6, 5, 4);  //declare two LCD's
LiquidCrystal lcd2(12, 10, 7, 6, 5, 4); // Ths is the second

These two objects are then passed as pointers to my LCDControllerClass like so:

LCDController.init(&lcd, &lcd2);

The init() member method of my LCDControllerClass then puts these pointers in private variables like so:

void LCDControllerClass::init(LiquidCrystal* Clcd1, LiquidCrystal* Clcd2)
{
   _Clcd1 = Clcd1;
   _Clcd1->begin(40, 2);
   _Clcd1->clear();

   _Clcd2 = Clcd2;
   _Clcd2->begin(40, 2);
   _Clcd2->clear();
}

Then I have another private member method which should take care of updating the correct LCD object:

void LCDControllerClass::_UpdateLCD(int _iLine, int _iPosition, String _strText, int _iFieldLength)
{
   LiquidCrystal* CActiveLCD;
   if (_iLine > 2)
   {
      //DisplayLine is higher than 2, this means we need to update the 2nd LCD
      LiquidCrystal** CActiveLCD = &_Clcd2;
      _iLine = _iLine - 2; //Convert line number to correct line number for 2nd display
   }
   else
   {
      //LiquidCrystal** CActiveLCD = &_Clcd1;
   }
   /* we have to convert this number
      to a real display line (0-1) by substracting 1 again
   */
   _iLine = _iLine - 1;

   CActiveLCD->setCursor(_iPosition, _iLine);
   CActiveLCD->print(_strText);
}

Now in this last member mehthor my code goes into the woods for some reason. The LCD is not updated at all and it seems the arduino is freezing as well since I put a small Serial.print() test in the main loop and it's not printing anything...

What is the correct way to pass the correct LiquidCrystal object to the CActiveLCD object?

1 Answer 1

2

The problem is that you're declaring multiple pointer variables called CActiveLCD in the _UpdateLCD() function. You do it once at the start of the function like this:

LiquidCrystal* CActiveLCD;

You then declare a new and totally separate pointer inside the if block like this:

LiquidCrystal** CActiveLCD = &_Clcd2;

This is called masking. The original pointer you declared at the start of the function still exists, but you can't access it because it's temporarily replaced by the new one. When the if block finishes, that new pointer is destroyed (it goes out of scope) and the old one is available again.

All of that means you never actually store a value in the original pointer variable. The result is that it contains an arbitrary memory address, so attempting to use it at the end of the function results in undefined behaviour.

When you are assigning to the pointer inside your if block, you need to do it like this:

CActiveLCD = _Clcd2;

Notice that this doesn't declare a new CActiveLCD pointer. It simply copies _Clcd2 into the existing one. Additionally, it's important not to use the address-of operator here (&). Adding that in this context gives you a pointer-to-a-pointer. It is technically legitimate in C++, and sometimes does serve a useful purpose, but it's not what you want to do here.

As a side note, when declaring a pointer variable it's usually a good idea to initialise it to something. If you're not assigning an actual address to it right away then initialise it to 0, like this:

LiquidCrystal* CActiveLCD = 0;

This means you can check the pointer later on before using it. If it's 0 then you know it's not got a valid address yet so it's not safe to use. This isn't always strictly necessary, but it can help avoid some frustrating bugs.

1
  • Thanks a million, your answer solved my problem and explained the reason behind it!
    – Alex
    Commented Mar 15, 2015 at 16:46

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.