Multi file code compiles, but code does not run.

Hello there, Arduino forums.
I just created my account so if I’ve missed something and done something wrong, please do tell me so I can fix it =)

Anyway, on to the question. I first posted it to stackOverflow as it’s a sole programming question, but then decided to post it here as well since people here probably have more specific knowledge about this issue.

I’ve had some problems with a testing project I’m working on with my Arduino Uno.
I have a 20x4 5x8 character LCD and three buttons connected, the LCD by the LiquidCrystal tutorial, and the momentary buttons by the Button tutorial.

I’m still not too good at C/C++ as I’ve basically just started with it, but I have programming experience from languages like C#, Java and Ruby, but I’m pretty sure that it’s some small mistake that I’m missing that makes the code not run.

I expect my code to initialize the MainStatusScreen into an array of CScreen’s, run initialize() in gotoScreen() and then loop, running the update() function on the selected screenID.

If it’d be Java or C#, I’d have CScreen as an interface and MainStatusScreen as a class derived from that.

Another thing I’d like is to have the LiquidCrystal object to be saved into each child of the CScreen class upon initialization, so that it can be access from all it’s functions instead of me having to reference them in each function like I currently do.

Here is my code if anyone feels like helping me out.

// include libraries
#include <LiquidCrystal.h>
#include "CScreen.cpp"

#define scrnWidth 20
#define scrnHeight 4

// Forward declarations
class CScreen;
class MainStatusScreen;

LiquidCrystal lcd(12, 11, 5, 4, 3, 2);
// The screen id of the current screen to be shown.
// 0 = Main Status
// 1 = Test Screen (Not Used)
// 2 = Button Test Screen
int screenID;

// Deltatime vars
unsigned long start, delta, msCount;

// Custom Icons
// 0 = Battery icons / AC

// ID 0: Power Icons
byte bat0[8] = {B01110, B10001, B10001, B10001, B10001, B10001, B10001, B11111};
byte bat1[8] = {B01110, B10001, B10001, B10001, B10001, B10001, B11111, B11111};
byte bat2[8] = {B01110, B10001, B10001, B10001, B10001, B11111, B11111, B11111};
byte bat3[8] = {B01110, B10001, B10001, B10001, B11111, B11111, B11111, B11111};
byte bat4[8] = {B01110, B10001, B10001, B11111, B11111, B11111, B11111, B11111};
byte bat5[8] = {B01110, B10001, B11111, B11111, B11111, B11111, B11111, B11111};
byte bat6[8] = {B01110, B11111, B11111, B11111, B11111, B11111, B11111, B11111};
byte batac[8] = {B01010, B01010, B11111, B11111,  B11111, B01110, B00100, B00100};

// ID 1: Locked Screen
byte lock[8] = { B00000, B01110, B10001, B11111, B11011, B11011, B111111, B00000 };

CScreen screens[] = {
  MainStatusScreen()
};

void setup()
{
  // Setup the buttons
  pinMode(8, INPUT);
  pinMode(9, INPUT);
  pinMode(10, INPUT);

  // Setup the LCD screen
  lcd.createChar(0, batac);
  lcd.createChar(1, lock);
  lcd.begin(scrnWidth, scrnHeight);

  gotoScreen(0);
}

void loop()
{
  start = millis();

  if (screens[screenID].gotoRequest != -1)
  {
    gotoScreen(screens[screenID].gotoRequest);
  }

  screens[screenID].update(lcd);

  msCount += delta;
  delta = millis() - start;
}

void gotoScreen(int screen)
{
  lcd.clear();
  screenID = screen;
  screens[screen].initialize(lcd);
}
#include <LiquidCrystal.h>

#define scrnWidth 20
#define scrnHeight 4

// Screen Base class
class CScreen
{
  public:
    int gotoRequest;
    virtual void initialize(LiquidCrystal) { gotoRequest = -1; return; }
    virtual void update(LiquidCrystal) { return; }
    virtual void draw(LiquidCrystal) { return; }
};

class MainStatusScreen : public CScreen {
  virtual void initialize(LiquidCrystal lcd)
  {
    lcd.setCursor(0, 0);
    lcd.print("CPU");
    lcd.setCursor(0, 1);
    lcd.print("RAM:");

    lcd.setCursor(scrnWidth - 10, 0);
    lcd.print("HDD1:");
    lcd.setCursor(scrnWidth - 10, 1);
    lcd.print("HDD2:");
  }

  virtual void update(LiquidCrystal lcd)
  {
    lcd.setCursor(5, 0);
    lcd.print("###%");
    lcd.setCursor(5, 1);
    lcd.print("###%");

    lcd.setCursor(scrnWidth - 4, 0);
    lcd.print("###%");
    lcd.setCursor(scrnWidth - 4, 1);
    lcd.print("###%");
  }

  virtual void draw(LiquidCrystal lcd)
  {

  }
};

EDIT: I’ve not found out that it properly runs the CScreen class code if I for example put an lcd print command before any of it’s returns. So it’s something with the overriding that doens’t work.

If possible, I suggest you use the Serial port to print out status messages to tell you what is happening inside your sketch.

Is your derived class MainStatusScreen::initialize actually overriding CScreen::initialize ? Debug output would tell you which initialize method was actually being invoked. MainStatusScreen::initialize doesn't initialize gotoRequest so if it does override the base implementatoin, you need to call the parent class's initialize method, or do the initialization yourself.

Since the base implementation of initialize doesn't do anything useful (lcd is unused, and you could easily declare gotoRequest with an initial value) you could make all the interface methods abstract to ensure that you don't accidentally use the base implementation. That mght not suit how you want to derive different sub-classes of screen, but subclassing non-abstract methods can be problematic if you don't match the method signature exactly. (For example, you don't name the LiquidCrystal formal argument in the base class methods.)

As far as I can see, your array of screens is of size one and only holds a single screen, which is an instance of MainStatusScreen. The code defining the screens array looks wrong - the MainStatusScreen constructor does not take any args and you don't need parenthesis when you call a no-args constructor.

I would prefer to see a sanity check of the new screen in gotoScreen() to make sure you don't go beyond the bounds of the array - some Serial debug output would show whether you're trying to do that for any reason.

If you don't want to have to pass in a copy of the LiquidCrystal object each time you call a screen function then pass a reference/pointer in to the initialize method and save that in an instance variable.

Like I said in the edit on my last post bottom, it seems to work if I change the initialize function in CScreen to
virtual void initialize(LiquidCrystal lcd) { lcd.print("TEST"); gotoRequest = -1; return; } so it seems to be an override problem.

I’ve tried using abstract methodds like this

// Screen Base class
class CScreen
{
  public:
    int gotoRequest;
    abstract void initialize(LiquidCrystal lcd);
    abstract void update(LiquidCrystal lcd);
    abstract void draw(LiquidCrystal lcd);
};

But the compiler errors on me, saying that

CScreen.cpp:11: error: ISO C++ forbids declaration of 'abstract' with no type
CScreen.cpp:11: error: expected ';' before 'void'
CScreen.cpp:12: error: ISO C++ forbids declaration of 'abstract' with no type
CScreen.cpp:12: error: expected ';' before 'void'
CScreen.cpp:13: error: ISO C++ forbids declaration of 'abstract' with no type
CScreen.cpp:13: error: expected ';' before 'void'

Also, about the no-args constructor, if I remove it’s paranthesis so it’s

CScreen screen[] = {
  MainStatusScreen
};

it says that it “expected primary-expression before ‘}’ token”

Furthermore, if I change the CScreen’s virtual functions into (apparently) so called “pure virtual functions” by ending them with = 0; instead of { return; }, I get this error at “CScreen screens = {” line

LCD_Test:34: error: invalid abstract type 'CScreen' for 'screens'
/CScreen.cpp:8: note:   because the following virtual functions are pure within 'CScreen':
/CScreen.cpp:11: note: 	virtual void CScreen::initialize(LiquidCrystal)
/CScreen.cpp:13: note: 	virtual void CScreen::update(LiquidCrystal)
/CScreen.cpp:14: note: 	virtual void CScreen::draw(LiquidCrystal)
LCD_Test:36: error: cannot allocate an object of abstract type 'CScreen'
/CScreen.cpp:8: note:   since type 'CScreen' has pure virtual functions

Like said, I’m very nooby with C and C++, but I’m quite experienced with C# and Java so I know what I’m trying to do, I just don’t know how to do it.

With a few more brain cells online this morning, it’s suddenly obvious that you can’t have an array of objects which are not all the same type - which I assume is your intention. What you can do is have an array of pointers to a base type and then initialise that with pointers to objects which are sub-classes of the base type.

Haha yeah sometimes you run on just 70% brain =P

It's my intention that I make child classes from CScreen wich will then be stored into the array of screens, yes.

I was playing around with using pointers instead, and tried this

CScreen* screens[] = {
  &MainStatusScreen()
}

I don't know if that's correct or what though. It does compile though. Of course I changed the dots into "->" where it was accessing the screens, but now, the LCD lines 0 and 2 are flickering, no characters or anything, just the full character space.

Here are a bit more overviewable versions of my current files.
Main Sketch file
CScreen.cpp

I know for a fact that it isn't my breadboard wiring as it works with another(much simpler) test project I wrote to test my buttons.

EDIT:
I got it to work!
First off, I changed it so that it was an array of CScreen pointers, adding new screens with the new keyword.
Also it was apparently constantly changing between the screens because the gotoRequest if in the loop wasn't resetting the gotorequest.
Thank you for trying to help me nonetheless! I would add karma on you but I can't xD