Adding hardware selection to my matrix keypad library

I'm trying to update the keypad library so that it can use the Arduino pins or an I2C port expander but I can't get past a problem with the Arduino IDE. It won't pass a #define from the sketch into the library.

What I'm trying to do is let the user #define USE_I2C_PINS so the keypad will use the port expander. Otherwise it just uses the arduino pins. The other thing is that I would like to be able to add new hardware without making any changes to the keypad library itself, just to hal.h.

In the following code I have cut it to the bare minimum to demonstrate what I am trying to accomplish. Let me know if I should post the entire source code for the keypad library.

minimal.ino sketch

#include <Reduced.h>
#include <Wire.h>

#define USE_I2C_PINS // This is where I would like to select the hardware but it makes no difference what I do.

Reduced red;

void setup() {
Serial.begin(9600);
red.setHoldTime(42); // Prints one thing when using arduinoPins and something else for I2CxPins.
}

void loop() {}

Inside hal.h is where I do the heavy lifting. It should select the correct header file and then create a pins object that can be used later by Reduced.cpp.

// hal.h
#include "Arduino.h" // for digitalRead, digitalWrite, etc

#define USE_I2C_PINS // This works but it's not ideal. It should be passed in from the sketch but it doesn't work that way.

#if defined USE_I2C_PINS
#include "I2CxPins.h"
I2CxPins pins;
#else
#include "arduinoPins.h"
arduinoPins pins;
#endif

This is Reduced.cpp where I #include "hal.h" which should select either arduinoPins.h or I2CxPins.h. I call pins.pin_mode which will Serial.print() something different depending on which header file was loaded.

#include <Reduced.h>

#include "utility/hal.h" // Provides the "pins" object.

void Reduced::setHoldTime(unsigned int hold) {
holdTime = hold;
pins.pin_mode(); // Prints one thing when using arduinoPins and something else for I2CxPins.
}

So how can I get this to work if the Arduino IDE won't pass through the #define's from the sketch?

Reduced.zip (77.8 KB)

It's not the IDE's fault.

You have two "compilation units" here ... things that are compiled.

minimal.ino - gets compiled with "#define USE_I2C_PINS" - that will affect that file.

Reduced.cpp - gets compiled separately, it doesn't know nor care what defines you have in miminal.ino.

All I can suggest is adding a begin() function, and passing to that whether you want to use I2C or not.

Thank you Nick. I've spent the whole day conisidering how to implement your idea. It seems I will just have to fall back on the standard answer of creating a new class then inheriting and extending the old one. I don't think I actually have to use begin() to do it but the user will just have to instantiate the right object to use I2C instead of the arduino pins.

The simple method would be to make a base class that does most of the work, and then derive two classes from that. One which uses I2C to scan pins, and the other which uses the other method. The base class would have a virtual method isButtonDown, or something like that, which both classes override.

That's almost what I was thinking. I'm still undecided but was thinking that my base class could use the default arduino pin functions tucked away inside some virtual functions for example:

virtual void pin_mode(int pinNum, int direction) {
      pinMode(pinNum, direction);
}

and then just inherit class Keypad and override the virtual functions when using I2C.

I suppose it's just a minor difference in logic.