0

I'm trying to create a small battery powered alarm clock using an Arduino MKRZero and a generic SH1106-controlled, 128x64 pixel oled.

I started out just dumping all code into a single file named Clock.ino. As the code got bigger, I started to split out functionality into separate classes. This went fine for timekeeping and alarms, but when I tried to do the same for the display functionality, I ran into an issue.

Now I wanted to split out my display code into a separate class. The working code before the refactor looks a bit like this:

Clock.ino

//Display stuff
#include <U8x8lib.h>
#ifdef U8X8_HAVE_HW_SPI
#include <SPI.h>
#endif
U8X8_SH1106_128X64_NONAME_HW_I2C u8x8(/* reset=*/ U8X8_PIN_NONE);

void setup(void) {
  u8x8.begin();
}

void print_clock([...]) {
  [...]
  u8x8.setCursor(0,3);
  u8x8.print("23:59:59");
  [...]
}

void loop(void) {
  [...]

  // Main clock
  print_clock(clk.datetime);

  [...]
}

I tried to split it out like this:

Clock.ino

#include "Display.h"

Display disp;

void setup(void) {}

void loop(void) {
  [...]

  // Main clock
  disp.print_clock([...]);

  [...]
}

Display.cpp

#include "Display.h"

Display::Display() {
  this->u8x8.begin();
}

void Display::print_clock([...]) {
  [...]
  this->u8x8.setCursor(0,3);
  this->u8x8.print("23:59:59");
  [...]
}

Display.h

//Display stuff
#include <U8x8lib.h>
#ifdef U8X8_HAVE_HW_SPI
#include <SPI.h>
#endif

class Display {
  public:
    Display();
    void print_clock([...]);
  private: 
    U8X8_SH1106_128X64_NONAME_HW_I2C u8x8(/* reset=*/ U8X8_PIN_NONE);
} 

That didn't work, gave me a whole list of errors, all similar to these:

  5 In file included from /home/simon/Arduino/libraries/U8g2/src/U8x8lib.h:45:0,                          
  6                  from sketch/Display.h:9,                                                             
  7                  from sketch/Display.cpp:1:                                                           
  8 /home/simon/Arduino/libraries/U8g2/src/clib/u8x8.h:323:23: error: expected identifier before numeric c    \>onstant                                                                                             
  9  #define U8X8_PIN_NONE 255                                                                            
 10                        ^                                                                              
 11 sketch/Display.h:26:55: note: in expansion of macro 'U8X8_PIN_NONE'                                   
 12      U8X8_SH1106_128X64_NONAME_HW_I2C u8x8(/* reset=*/ U8X8_PIN_NONE);                                
 13                                                        ^~~~~~~~~~~~~                                  
 14 /home/simon/Arduino/libraries/U8g2/src/clib/u8x8.h:323:23: error: expected ',' or '...' before numeric    \> constant                                                                                           
 15  #define U8X8_PIN_NONE 255                                                                            
 16                        ^                                                                              
 17 sketch/Display.h:26:55: note: in expansion of macro 'U8X8_PIN_NONE'                                   
 18      U8X8_SH1106_128X64_NONAME_HW_I2C u8x8(/* reset=*/ U8X8_PIN_NONE);                                
 19                                                        ^~~~~~~~~~~~~                                  
 20 sketch/Display.cpp: In constructor 'Display::Display()':                                              
 21 Display.cpp:8:9: error: invalid use of member function 'U8X8_SH1106_128X64_NONAME_HW_I2C Display::u8x8    \>(int)' (did you forget the '()' ?)                                                                  
 22    this->u8x8.begin();                                                                                
 23    ~~~~~~^~~~                 

So I tried rewriting it in such a way that the u8xu var is global like it was before, defining it in Display.cpp and getting rid of the this-> before the u8x8. method calls in the Display:: methods. That didn't work either. It compiles, but my display stays blank and the arduino gets stuck (does not interface with serial anymore until I hard-reset it).

At some point I rewrote the code such that I passes on the variable to the Constructur for Display, but I think I completely botched that as I couldn't get it to compile.

so, main question: how do I instantiate an object inside my class, so it becomes available as a private variable?

If anyone wants to look at the full code, it's right here: https://github.com/ffective/wake-up/tree/master/Clock

The latest commit is the way of splitting up I described last: using a global variable. The previous commit is the working code with all Display methods just globally in Clock.ino. Unfortunately I didn't save my attempts for putting it as a private variable.

1
  • What you got is the method declaration, not member variable definition. The u8x8 is the private method returning U8X8_SH1106_128X64_NONAME_HW_I2C. And it's failing on that constant in parameter list.
    – KIIV
    Aug 11 '20 at 20:07
0

You need to create a "blank" object in your class and then instantiate it in your constructor initializer list:

class Display { 
    private: 
        U8X8_SH1106_128X64_NONAME_HW_I2C u8x8;

    public: 
        Display() : u8x8(U8X8_PIN_NONE) {} 
        void print_clock([...]); 
};

The object in private is not instantiated until the constructor is called by the calling of the Display() constructor.

From then on you can use u8x8.whatever() as you would normally.

1
  • Also it's possible to use U8X8_SH1106_128X64_NONAME_HW_I2C u8x8 { U8X8_PIN_NONE }; and he doesn't have to use initializer list in the default constructor. It's important to use {} instead of () here
    – KIIV
    Aug 11 '20 at 20:19
0

First errors aren't much helpful, but the last one about member function explains it all. You are not creating member variable of that type, you are trying to create member function (with invalid parameter list) that returns U8X8_SH1106_128X64_NONAME_HW_I2C value.

This code is perfectly valid:

class Display {
  public:
    Display() { u8x8.begin(); }

    // you can also use different initializer for u8x8 object (default one is never executed in this case): 
    Display(uint8_t resetPin) : u8x8{resetPin} { u8x8.begin(); } 

  private:
    U8X8_SH1106_128X64_NONAME_HW_I2C u8x8 { U8X8_PIN_NONE };
};

and in the sketch:

// creating instance:
Display disp{ 5 };

// or with default constructor and reset pin value
Display disp2;

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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