How to use class as member variable with arduino

Ok, this question is harder then it actually sounds in the title.

I currently have a class keyblock.h

class Keyblock {  
private:
    unsigned _rows = 4;
    unsigned _columns = 3;

    char _keys[4][3] = {
      {'1','2','3'},
      {'4','5','6'},
      {'7','8','9'},
      {'*','0','#'}
    };

    byte _keypadRowPins[4] = { 33, 34, 35, 36 };
    byte _keypadColPins[3] = { 37, 38, 39 };

    Keypad _keypad;
    Debug &_debug;
public:
    Keyblock(Debug &debug);
    void init();
    void getKey();
};

which I initialize via a constructor

Keyblock::Keyblock(Debug &debug):
   _keypad(makeKeymap(_keys), _keypadRowPins, _keypadColPins, _rows, _columns),
  _debug(debug)
{}

Keypad library available to view @ Arduino Playground - Keypad Library

However this does not work (as in it compiles fine but runs with issues on the arduino) because it gets initialized via Box class which is a global class...which happens before setup() and loop() start.. the hardware isn't initialized yet (as I understand it) causes the problems.

Main.ino

#include "Box.h"

Box box();

void setup(){ debug.init(); box.setup(); }
void loop(){ box.loop(); }

So....my thoughts on how to solve this

  1. Move the initialization of _keypad to Keyblock::init() (which gets called in Box::setup()). However the Keypad library constructor requires all parameters AND if I want the class to be member variable on Keyblock I HAVE to initialize it on construction. So I have no idea how to do this...that is a chicken and egg issue.

  2. In Main.ino I could move Box out of the global space. However I don't know how to pass it between setup() and loop() in that situation. So maybe so sort of hack that initializes it/sets it up only once inside loop....just feels so ugly / I'm sure is wrong.

Any ideas?

but in Keypad examples it is initialized before setup() too. so what problems?
where do you call _keypad.begin()? it should be in keyblock.init(), which should be called in box.setup().

btw Arduino recommends the name begin(), you have init() and setup()

@Juraj : Ok...good question...maybe I jumped to conclusions

So, the test script [ HelloKeypad \ Learning \ Wiring ] works perfect.

When I use the exact same code in my Keyblock class I get bogus keypress values. key 1 = 49, key 2= 50, # = 35, * = 42

'1' is a character with ASCII code 49. use 1 if you want 1. or compare key == '1' or use case '1':, not case 1:

char key = getKey();
Serial.println(key); // will print 1 for '1' because of the type char

_keypad.begin() gets called in the constructor. I don't believe there is a reason to call it twice. Thank you for the note about using begin()...I will switch.

So the test script worked perfectly...all I am doing currently is

void Keyblock::getKey() {
  char key = _keypad.getKey();
  if (key != NO_KEY){
    _debug.write("Key Pressed : ").line(key);
  }
}

So maybe the issue is actually something do with my debug class?

class Debug {
  private:
    bool _state = false;
  public:
    void init();
    Debug& line(const char *str);
    Debug& line(int);
    Debug& write(const char *str);
    Debug& write(int);
    void newline();
};

void Debug::init(){
  if (!_state){
    Serial.begin(9600);
    delay(1000);
    _state = true;
    line("Debug Started");
  }
}
Debug& Debug::line(const char *messageChar) {
  if (_state){
    const char *p;
    p = messageChar;
    while (*p) {
        Serial.print(*p);
        p++;
    }
    newline();
  }
  return *this;
}
Debug& Debug::line(int messageInt) {
  if (_state){
    Serial.print(messageInt);
    newline();
  }
  return *this;
}
Debug& Debug::write(const char *messageChar) {
  if (_state){
    const char *p;
    p = messageChar;
    while (*p) {
        Serial.print(*p);
        p++;
    } 
  }
  return *this;
}
Debug& Debug::write(int messageInt) {
  if (_state){
    Serial.print(messageInt);
  }
  return *this;
}
void Debug::newline() {
  if (_state){
    Serial.println();
  }
}

you don't have line(char) so char will be converted to int and printed as number not as character

Ok...so is it best practice to write a bunch of methods for each variable type or is there a way to have a method that covers both const char and char (as an example).

Thanks for your fast help, I really appreciate it

Arduino best practice would be to use Serial directly or use some wrapper class based on Print class which only requires to implement write(byte). But the API of Print class is different then yours. There are libraries which create similar printing API as your Debug class.

EDIT: const type parameter can always be used with not const input

Ok, I will look into that more. Debug was mostly a learning thing for me. Once again...I can't tell you how much I appreciate your help. This stuff is hard!

This topic was automatically closed 120 days after the last reply. New replies are no longer allowed.