I/O Expander with LCD and 4x4 Keypad

I am confronted by an interesting problem. I would like to use an LCD and a 4x4 keypad in a project. I require quite a few pins for the rest of the project so I decided to try hooking up the LCD and the keypad to an MCP23017 I/O Expander Chip. I use the LiquidTWI2 library to control the LCD and Keypad_MC17 library to control the keypad. The serial output shows that the keypad is functioning as expected. Furthermore, the LCD lights up and a start up message displays based on code in the setup() section of my code. Here is the code:

#include <Keypad_MC17.h>
#include <Wire.h>
#include <Keypad.h>
#include <LiquidTWI2.h>

#define I2CADDR 0x20
const byte ROWS = 4; //four rows
const byte COLS = 4; //three columns
char keys[ROWS][COLS] = {
  {'1','2','3','A'},
  {'4','5','6','B'},
  {'7','8','9','C'},
  {'*','0','#','D'}
};
byte rowPins[ROWS] = {3, 2, 1, 0}; //connect to the row pinouts of the keypad
byte colPins[COLS] = {7, 6, 5, 4}; //connect to the column pinouts of the keypad
Keypad_MC17 keypad(makeKeymap(keys), rowPins, colPins, ROWS, COLS, I2CADDR );

LiquidTWI2 lcd(0);

void setup() {
  Serial.begin(9600);
  keypad.begin();
  lcd.setMCPType(LTI_TYPE_MCP23017);
  lcd.begin(20,4);
  lcd.print("Testing...");
  Serial.print("Testing...");
}

void loop() {
  char key = keypad.getKey();
  if (key) {
    changeDisplay(key);
  }
}

void changeDisplay(char k) {
  Serial.println(k);
  lcd.clear();
  lcd.print(k);
}

The problem is that the key presses do not show up on the LCD - as a matter of fact, nothing that I do to change the LCD display in the loop() does anything. I have played around with delays and separating the display changes into separate functions (as you can see in the above code) to no avail. Finally I removed the getKey() command and simply used a lcd.print(millis()) command to simulate a "Hello, World!" example and it finally worked.

So obviously, the problem lies with the getKey() command. It somehow locks out the MCP23017 into only accepting input from the B register to which the keypad is connected. Does anybody have experience with this library? Is there a way to get around this "lock out" to access the A pins?

I have not attached images of my wiring, because there seems to be no problem there. If you need it, however, just let me know and I'll upload some images.

Thanks in advance.

Casper

I suggest that if you are having trouble with a library, that is not a standard Arduino library, that you include a link to or attach the library that you are using so everyone is on the same page. As it is I have to search for the libraries and hope that they are the ones you are using.

Sorry for the oversight:

LiquidTWI2 - GitHub - lincomatic/LiquidTWI2: A lean, high speed I2C LCD Library for Arduino, which supports MCP23008 and MCP23017
Keypad_MC17 - GitHub - joeyoung/arduino_keypads: arduino libraries for keypad interface on I2C bus

Casper

I had a further look at the documentation for the Keypad_MC17 library. At the end it says this:

Other Features
The Keypad_MC17 library has four functions which will enable some sharing of the I2C port between a
keypad that doesn't use all 16 of the MCP23017's i/o pins and other digital i/o. For example, if the keypad is
a commonly available 12-key version such as is described for the HelloKeypad_MC17 example, then there
are 9 extra pins available on the port chip that could be used—say for driving LED indicator circuits, or
whatever. But keep in mind the drive capability of the MCP23017—it's comparable to the arduino's digital
output pin drivers. The high output can source 25 mA, the low output can sink 25mA. Inputs have internal
pullups which are turned on in the kpd.begin() function, so external pullups are not required.
Because the I2C chip's registers must be written all at once, these functions are needed so that the operation
of the keypad can be kept separated from the operation of the spare pin(s).
iodir_read( ) (no parameters, returns a word) is called to get the current state of all 16 bits of the
MCP23017's IODIRA, B registers which defines the port pins as inputs or outputs at each bit position.
iodir_write( value ) writes all 16 direction-defining bits to the IODIRA, B register pair. Each of the 16
bits of the word value determines if the corresponding pin is input (bit = 1) or output (bit = 0).
pinState_set( ) (no parameters, returns a word) is called to get the current state of all 16 bits of the port,
and port_write( value ) writes all 16 bits of value to the port and returns nothing.
The following example of a function to toggle the spare pin (bit 7) illustrates the use of these four
Keypad_MC17 member functions (first line setting bit 7 as output goes in setup() section):

kpd.iodir_write( kpd.iodir_read( ) & 0xff7f ); // gp0 msb output for led

word portState; // to hold local copy of kpd port
const word ledPin = 0x0080; // msbit gp0 to be used for LED drive
void toggleLEDpin( void ) {
portState = kpd.pinState_set( ); // get current kpd port state into portState
if( portState & ledPin ) {
portState &= ~ledPin;
} else {
portState |= ledPin;
}
kpd.port_write( portState ); // write modified state to port
} // toggleLEDpin( )

If I read this correctly, it means that you can query the chip as to its pin states. I added some code to use .iodir_read() and .pinState_set() in order to get some info through the serial monitor. I expected unique numbers (conforming to binary 0's and 1's) for each key press - but that is not what I got. With .iodir_read() I get a single number (65535) for every key; with .pinState_set() I get numbers that change not only for different keys, but for multiple presses of the same key (e.g. key 1 or key A). I think that solving my problem lies somewhere in this, but I have no idea where to go from here.

1 Like

OK, I have a solution (sort of). It is not very elegant, but it works in a simple test sketch. I'll have to be creative in implementing it in a real sketch, but it should do for now. I simply restart the lcd before a key value is passed to the LCD.

void loop() {
  char key = keypad.getKey();
  if (key) {
    lcd.begin(20,4);
    changeDisplay(key);
  }
}

Far from perfect, but at least I know that my suspicion about some kind of lock out with .getKey() is in play. If anyone has another suggestions, please let me know!

Casper

Sorry, I know it is been more than three years since you posted. But I am facing exactly the same problem. my MCP23017 connected to a Nano is being reseted from the getKey() from the Keypad_M17 and for this reason I cant use the other GP-Bank on the MCP23017

Any Ideas?
new post here:
https://forum.arduino.cc/index.php?topic=505888.0