Skip to content

Commit

Permalink
Add mode 4x4 5x3 6x2 8x1 (#14)
Browse files Browse the repository at this point in the history
* Add mode 4x4 5x3 6x2 8x1
* Add examples
* update readme.md
  • Loading branch information
RobTillaart authored Sep 29, 2022
1 parent dfcc5a0 commit 39dcef1
Show file tree
Hide file tree
Showing 10 changed files with 457 additions and 31 deletions.
140 changes: 138 additions & 2 deletions I2CKeyPad .cpp
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
//
// FILE: I2CKeyPad.cpp
// AUTHOR: Rob Tillaart
// VERSION: 0.3.1
// VERSION: 0.3.2
// PURPOSE: Arduino library for 4x4 KeyPad connected to an I2C PCF8574
// URL: https://github.com/RobTillaart/I2CKeyPad
//
Expand All @@ -14,8 +14,12 @@
// 0.2.0 2021-05-06 MultiWire ... (breaking interface)
// 0.2.1 2021-05-06 add _read(0xF0) to begin() to enable PCF8574
// interrupts. (#5 thanks to JohnMac1234)
//
// 0.3.0 2021-11-04 add key mapping functions.
// 0.3.1 2021-12-19 update library.json, license, minor edits
// 0.3.2 2022-09-19 experimental version
// add 5x3, 6x2 and 8x1 support
// moved all code to .cpp file.


#include "I2CKeyPad.h"
Expand All @@ -26,6 +30,7 @@ I2CKeyPad::I2CKeyPad(const uint8_t deviceAddress, TwoWire *wire)
_lastKey = I2C_KEYPAD_NOKEY;
_address = deviceAddress;
_wire = wire;
_mode = I2C_KEYPAD_4x4;
}


Expand All @@ -51,10 +56,20 @@ bool I2CKeyPad::begin()

uint8_t I2CKeyPad::getKey()
{
if (_mode == I2C_KEYPAD_5x3) return _getKey5x3();
if (_mode == I2C_KEYPAD_6x2) return _getKey6x2();
if (_mode == I2C_KEYPAD_8x1) return _getKey8x1();
// default.
return _getKey4x4();
}


uint8_t I2CKeyPad::getLastKey()
{
return _lastKey;
};


// to check "press any key"
bool I2CKeyPad::isPressed()
{
Expand All @@ -71,15 +86,46 @@ bool I2CKeyPad::isConnected()
}


uint8_t I2CKeyPad::getChar()
{
return _keyMap[getKey()];
};


uint8_t I2CKeyPad::getLastChar()
{
return _keyMap[_lastKey];
};


void I2CKeyPad::loadKeyMap(char * keyMap)
{
_keyMap = keyMap;
}


void I2CKeyPad::setKeyPadMode(uint8_t mode)
{
if ((mode == I2C_KEYPAD_5x3) ||
(mode == I2C_KEYPAD_6x2) ||
(mode == I2C_KEYPAD_8x1))
{
_mode = mode;
return;
}
_mode = I2C_KEYPAD_4x4;
}


uint8_t I2CKeyPad::getKeyPadMode()
{
return _mode;
}


//////////////////////////////////////////////////////
//
// PRIVATE
// PROTECTED
//
uint8_t I2CKeyPad::_read(uint8_t mask)
{
Expand Down Expand Up @@ -129,5 +175,95 @@ uint8_t I2CKeyPad::_getKey4x4()
}


// not tested
uint8_t I2CKeyPad::_getKey5x3()
{
// key = row + 5 x col
uint8_t key = 0;

// mask = 5 rows as input pull up, 3 columns as output
uint8_t rows = _read(0xF8);
// check if single line has gone low.
if (rows == 0xF8) return I2C_KEYPAD_NOKEY;
else if (rows == 0xF0) key = 0;
else if (rows == 0xE8) key = 1;
else if (rows == 0xD8) key = 2;
else if (rows == 0xB8) key = 3;
else if (rows == 0x78) key = 4;
else return I2C_KEYPAD_FAIL;

// 3 columns as input pull up, 5 rows as output
uint8_t cols = _read(0x07);
// check if single line has gone low.
if (cols == 0x07) return I2C_KEYPAD_NOKEY;
else if (cols == 0x06) key += 0;
else if (cols == 0x05) key += 5;
else if (cols == 0x03) key += 10;
else return I2C_KEYPAD_FAIL;

_lastKey = key;

return key; // 0..14
}


// not tested
uint8_t I2CKeyPad::_getKey6x2()
{
// key = row + 6 x col
uint8_t key = 0;

// mask = 6 rows as input pull up, 2 columns as output
uint8_t rows = _read(0xFC);
// check if single line has gone low.
if (rows == 0xFC) return I2C_KEYPAD_NOKEY;
else if (rows == 0xF8) key = 0;
else if (rows == 0xF4) key = 1;
else if (rows == 0xEC) key = 2;
else if (rows == 0xDC) key = 3;
else if (rows == 0xBC) key = 4;
else if (rows == 0x7C) key = 5;
else return I2C_KEYPAD_FAIL;

// 2 columns as input pull up, 6 rows as output
uint8_t cols = _read(0x03);
// check if single line has gone low.
if (cols == 0x03) return I2C_KEYPAD_NOKEY;
else if (cols == 0x02) key += 0;
else if (cols == 0x01) key += 6;
else return I2C_KEYPAD_FAIL;

_lastKey = key;

return key; // 0..11
}


// not tested
uint8_t I2CKeyPad::_getKey8x1()
{
// key = row
uint8_t key = 0;

// mask = 8 rows as input pull up, 0 columns as output
uint8_t rows = _read(0xFF);
// check if single line has gone low.
if (rows == 0xFF) return I2C_KEYPAD_NOKEY;
else if (rows == 0xFE) key = 0;
else if (rows == 0xFD) key = 1;
else if (rows == 0xFB) key = 2;
else if (rows == 0xF7) key = 3;
else if (rows == 0xEF) key = 4;
else if (rows == 0xDF) key = 5;
else if (rows == 0xBF) key = 6;
else if (rows == 0x7F) key = 7;
else return I2C_KEYPAD_FAIL;

_lastKey = key;

return key; // 0..7
}


// -- END OF FILE --

34 changes: 25 additions & 9 deletions I2CKeyPad.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
//
// FILE: I2CKeyPad.h
// AUTHOR: Rob Tillaart
// VERSION: 0.3.1
// VERSION: 0.3.2
// PURPOSE: Arduino library for 4x4 KeyPad connected to an I2C PCF8574
// URL: https://github.com/RobTillaart/I2CKeyPad

Expand All @@ -11,11 +11,17 @@
#include "Wire.h"


#define I2C_KEYPAD_LIB_VERSION (F("0.3.1"))
#define I2C_KEYPAD_LIB_VERSION (F("0.3.2"))

#define I2C_KEYPAD_NOKEY 16
#define I2C_KEYPAD_FAIL 17

// experimental
#define I2C_KEYPAD_4x4 44
#define I2C_KEYPAD_5x3 53
#define I2C_KEYPAD_6x2 62
#define I2C_KEYPAD_8x1 81


class I2CKeyPad
{
Expand All @@ -29,26 +35,36 @@ class I2CKeyPad

// get raw key's 0..15
uint8_t getKey();
uint8_t getLastKey() { return _lastKey; };
uint8_t getLastKey();

bool isPressed();
bool isPressed();
bool isConnected();

// get 'translated' keys
// user must load KeyMap self, there is no check.
uint8_t getChar() { return _keyMap[getKey()]; };
uint8_t getLastChar() { return _keyMap[_lastKey]; };
void loadKeyMap(char * keyMap); // char[19]
// user must load KeyMap, there is no check.
uint8_t getChar();
uint8_t getLastChar();
void loadKeyMap(char * keyMap); // char[19]

// mode functions - experimental
void setKeyPadMode(uint8_t mode = I2C_KEYPAD_4x4);
uint8_t getKeyPadMode();


protected:
uint8_t _address;
uint8_t _lastKey;
uint8_t _mode;
uint8_t _read(uint8_t mask);
uint8_t _getKey4x4();

// experimental - could be public ?!
uint8_t _getKey5x3();
uint8_t _getKey6x2();
uint8_t _getKey8x1();

TwoWire* _wire;

char * _keyMap = NULL;
};

Expand Down
56 changes: 43 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,17 @@ Arduino library for 4x4 KeyPad connected to an I2C PCF8574.

The I2CKeyPad library implements the reading of a 4x4 keypad by means of a PCF8574.
Smaller keypads, meaning less columns or rows (4x3) can be read with it too.
A 5x3 keypad would require modification (issue pending to support this).

Since 0.3.2 the library allows a 5x3, 6x2 or 8x1 or smaller keypad to be connected too.

Relates to https://github.com/RobTillaart/I2CKeyPad8x8. which is an 8x8 version using PCF8575.


## Connection

The PCF8574 is connected between the processor and the 4x4 keypad, see the conceptual
below. It might take some trying to get the correct pins connected.
The PCF8574 is connected between the processor and the (default) 4x4 keypad.
See the conceptual schema below.
It might take some trying to get the correct pins connected.

```
PROC PCF8574 KEYPAD
Expand All @@ -41,23 +45,45 @@ below. It might take some trying to get the correct pins connected.

## Interface

- **I2CKEYPAD keypad(const uint8_t deviceAddress, TwoWire \*wire = &Wire)**
- **I2CKEYPAD(const uint8_t deviceAddress, TwoWire \*wire = &Wire)**
The constructor sets the device address and optionally
allows to selects the I2C bus to use.
- **bool keyPad.begin()** The return value shows if the PCF8574 with the given address is connected properly.
- **bool begin()** The return value shows if the PCF8574 with the given address is connected properly.
- **bool begin(uint8_t sda, uint8_t scl)** for ESP32.
The return value shows if the PCF8574 with the given address is connected properly.
- **keyPad.isConnected()** returns false if the PCF8574 cannot be connected to.
- **uint8_t keyPad.getKey()** Returns 0..15 for regular keys, 16 if no key is pressed
and 17 in case of an error.
- **keyPad.getLastKey()** Returns the last **valid** key pressed 0..15. Initially it will return 16 (noKey).
- **keyPad.isPressed()** Returns true if one or more keys of the keyPad is pressed,
- **bool isConnected()** returns false if the PCF8574 cannot be connected to.
- **uint8_t getKey()** Returns default 0..15 for regular keys,
Returns 16 if no key is pressed and 17 in case of an error.
- **uint8_t getLastKey()** Returns the last **valid** key pressed 0..15. Initially it will return 16 (noKey).
- **bool isPressed()** Returns true if one or more keys of the keyPad is pressed,
however it is not checked if multiple keys are pressed.


#### Mode functions

Note: experimental

- **void setKeyPadMode(uint8_t mode = I2C_KEYPAD_4x4)** sets the mode, default 4x4.
This mode can also be used for 4x3 or 4x2.
Invalid values are mapped to 4x4.
- **uint8_t getKeyPadMode()** returns the current mode.

**Supported modi**

There are 4 modi supported, and every mode also supports smaller keypads.
E.g. a 4x3 keypad can be read in mode 4x4 or in mode 5x3.

| modi | value | definition | notes |
|:------:|:-------:|:-----------------|:----------|
| 4x4 | 44 | I2C_KEYPAD_4x4 | default |
| 5x3 | 53 | I2C_KEYPAD_5x3 |
| 6x2 | 62 | I2C_KEYPAD_6x2 |
| 8x1 | 81 | I2C_KEYPAD_8x1 | not real matrix, connect pins to switch to GND.


#### KeyMap functions

**loadKeyMap()** must be called first!
**loadKeyMap()** must be called before **getChar()** and **getLastChar()**!

- **char getChar()** returns the char corresponding to mapped key pressed.
- **char getLastChar()** returns the last char pressed.
Expand All @@ -79,11 +105,13 @@ char diag_keymap[19] = "1 2 3 4NF"; // diagonal keys only

In the examples above a 'space' key might be just meant to ignore.
However functionality there is no limit how one wants to use the key mapping.
It is even possible to change the mapping runtime.
It is even possible to change the mapping runtime after each key.

Note: a keyMap char array may be longer than 18 characters, but only the first 18 are used.
The length is **NOT** checked upon loading.

Note: The 5x3, 6x2 and the 8x1 modi also uses a keymap of length 18.


#### Basic working

Expand All @@ -92,6 +120,9 @@ After the **keypad.begin()** the sketch calls the **keyPad.getKey()** to read va
- If the read value is not valid, e.g. two keys pressed, **I2CKEYPAD_FAIL** code (17) is returned.
- Otherwise a number 0..15 is returned.

Note NOKEY and FAIL bot have bit 4 set, all valid keys don't.
This allows fast checking for valid keys.

Only if a key map is loaded, the user can call **getChar()** and **getLastChar()** to get mapped keys.


Expand All @@ -111,5 +142,4 @@ See examples
## Future

- update documentation
- investigate 5x3 keypad and other 'formats'
- test key mapping functions.
Loading

0 comments on commit 39dcef1

Please sign in to comment.