0.3.2 I2CKeyPad

This commit is contained in:
rob tillaart 2022-09-29 16:17:30 +02:00
parent 362cb3723b
commit 6c77f95fee
10 changed files with 457 additions and 31 deletions

View File

@ -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
//
@ -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"
@ -26,6 +30,7 @@ I2CKeyPad::I2CKeyPad(const uint8_t deviceAddress, TwoWire *wire)
_lastKey = I2C_KEYPAD_NOKEY;
_address = deviceAddress;
_wire = wire;
_mode = I2C_KEYPAD_4x4;
}
@ -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()
{
@ -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)
{
@ -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 --

View File

@ -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
@ -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
{
@ -29,26 +35,36 @@ public:
// 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;
};

View File

@ -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
@ -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.
@ -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
@ -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.
@ -111,5 +142,4 @@ See examples
## Future
- update documentation
- investigate 5x3 keypad and other 'formats'
- test key mapping functions.

View File

@ -0,0 +1,65 @@
//
// FILE: I2Ckeypad_5x3.ino
// AUTHOR: Rob Tillaart
// PURPOSE: demo 5x3 or smaller keypad
// URL: https://github.com/RobTillaart/I2CKeyPad
//
// PCF8574
// pin p0-p4 rows
// pin p5-p7 columns
#include "Wire.h"
#include "I2CKeyPad.h"
const uint8_t KEYPAD_ADDRESS = 0x38;
I2CKeyPad keyPad(KEYPAD_ADDRESS);
uint32_t start, stop;
uint32_t lastKeyPressed = 0;
void setup()
{
Serial.begin(115200);
Serial.println(__FILE__);
Wire.begin();
Wire.setClock(400000);
if (keyPad.begin() == false)
{
Serial.println("\nERROR: cannot communicate to keypad.\nPlease reboot.\n");
while(1);
}
keyPad.setKeyPadMode(I2C_KEYPAD_5x3);
}
void loop()
{
uint32_t now = millis();
// adjust keymap if needed
char keys[] = "1234567890ABCDE NF"; // N = NoKey, F = Fail
if (now - lastKeyPressed >= 100)
{
lastKeyPressed = now;
start = micros();
uint8_t index = keyPad.getKey();
stop = micros();
Serial.print(millis());
Serial.print("\t");
Serial.print(index);
Serial.print("\t");
Serial.print(keys[index]);
Serial.print("\t");
Serial.println(stop - start);
}
}
// -- END OF FILE --

View File

@ -0,0 +1,65 @@
//
// FILE: I2Ckeypad_6x2.ino
// AUTHOR: Rob Tillaart
// PURPOSE: demo 6x2 or smaller keypad
// URL: https://github.com/RobTillaart/I2CKeyPad
//
// PCF8574
// pin p0-p5 rows
// pin p6-p7 columns
#include "Wire.h"
#include "I2CKeyPad.h"
const uint8_t KEYPAD_ADDRESS = 0x38;
I2CKeyPad keyPad(KEYPAD_ADDRESS);
uint32_t start, stop;
uint32_t lastKeyPressed = 0;
void setup()
{
Serial.begin(115200);
Serial.println(__FILE__);
Wire.begin();
Wire.setClock(400000);
if (keyPad.begin() == false)
{
Serial.println("\nERROR: cannot communicate to keypad.\nPlease reboot.\n");
while (1);
}
keyPad.setKeyPadMode(I2C_KEYPAD_6x2);
}
void loop()
{
uint32_t now = millis();
// adjust keymap if needed
char keys[] = "1234567890ABC NF"; // N = NoKey, F = Fail
if (now - lastKeyPressed >= 100)
{
lastKeyPressed = now;
start = micros();
uint8_t index = keyPad.getKey();
stop = micros();
Serial.print(millis());
Serial.print("\t");
Serial.print(index);
Serial.print("\t");
Serial.print(keys[index]);
Serial.print("\t");
Serial.println(stop - start);
}
}
// -- END OF FILE --

View File

@ -0,0 +1,65 @@
//
// FILE: I2Ckeypad_8x1.ino
// AUTHOR: Rob Tillaart
// PURPOSE: demo 8x1 or smaller keypad
// URL: https://github.com/RobTillaart/I2CKeyPad
//
// PCF8574
// pin p0-p7 rows
//
#include "Wire.h"
#include "I2CKeyPad.h"
const uint8_t KEYPAD_ADDRESS = 0x38;
I2CKeyPad keyPad(KEYPAD_ADDRESS);
uint32_t start, stop;
uint32_t lastKeyPressed = 0;
void setup()
{
Serial.begin(115200);
Serial.println(__FILE__);
Wire.begin();
Wire.setClock(400000);
if (keyPad.begin() == false)
{
Serial.println("\nERROR: cannot communicate to keypad.\nPlease reboot.\n");
while(1);
}
keyPad.setKeyPadMode(I2C_KEYPAD_8x1);
}
void loop()
{
uint32_t now = millis();
// adjust keymap if needed
char keys[] = "12345678 NF"; // N = NoKey, F = Fail
if (now - lastKeyPressed >= 100)
{
lastKeyPressed = now;
start = micros();
uint8_t index = keyPad.getKey();
stop = micros();
Serial.print(millis());
Serial.print("\t");
Serial.print(index);
Serial.print("\t");
Serial.print(keys[index]);
Serial.print("\t");
Serial.println(stop - start);
}
}
// -- END OF FILE --

View File

@ -15,6 +15,8 @@ loadKeyMap KEYWORD2
getChar KEYWORD2
getLastChar KEYWORD2
setKeyPadMode KEYWORD2
getKeyPadMode KEYWORD2
# Instances (KEYWORD2)
@ -24,3 +26,9 @@ I2C_KEYPAD_LIB_VERSION LITERAL1
I2C_KEYPAD_NOKEY LITERAL1
I2C_KEYPAD_FAIL LITERAL1
I2C_KEYPAD_4x4 LITERAL1
I2C_KEYPAD_5x3 LITERAL1
I2C_KEYPAD_6x2 LITERAL1
I2C_KEYPAD_8x1 LITERAL1

View File

@ -1,7 +1,7 @@
{
"name": "I2CKeyPad",
"keywords": "I2C,KeyPad, 4x4, PCF8574",
"description": "Arduino library for 4x4 KeyPad connected to a PCF8574. 4x4 or smaller only.",
"keywords": "I2C,KeyPad, 4x4, 5x3, 6x2, 8x1, PCF8574",
"description": "Arduino library for a KeyPad connected to a PCF8574. 4x4, 5x3, 6x2, 8x1 or smaller.",
"authors":
[
{
@ -15,7 +15,7 @@
"type": "git",
"url": "https://github.com/RobTillaart/I2CKeyPad.git"
},
"version": "0.3.1",
"version": "0.3.2",
"license": "MIT",
"frameworks": "arduino",
"platforms": "*",

View File

@ -1,9 +1,9 @@
name=I2CKeyPad
version=0.3.1
name=I2CKeyPad
version=0.3.2
author=Rob Tillaart <rob.tillaart@gmail.com>
maintainer=Rob Tillaart <rob.tillaart@gmail.com>
sentence=Arduino library for 4x4 KeyPad connected to a PCF8574
paragraph=4x4 or smaller only.
sentence=Arduino library for a KeyPad connected to a PCF8574.
paragraph=4x4, 5x3, 6x2, 8x1 or smaller.
category=Signal Input/Output
url=https://github.com/RobTillaart/I2CKeyPad
architectures=*

View File

@ -43,6 +43,11 @@ unittest(test_constants)
{
assertEqual(16, I2C_KEYPAD_NOKEY);
assertEqual(17, I2C_KEYPAD_FAIL);
assertEqual(44, I2C_KEYPAD_4x4);
assertEqual(53, I2C_KEYPAD_5x3);
assertEqual(62, I2C_KEYPAD_6x2);
assertEqual(81, I2C_KEYPAD_8x1);
}
@ -59,6 +64,42 @@ unittest(test_constructor)
}
unittest(test_mode)
{
const uint8_t KEYPAD_ADDRESS = 0x38;
I2CKeyPad keyPad(KEYPAD_ADDRESS);
assertEqual(I2C_KEYPAD_4x4, keyPad.getKeyPadMode());
keyPad.setKeyPadMode(I2C_KEYPAD_5x3);
assertEqual(I2C_KEYPAD_5x3, keyPad.getKeyPadMode());
keyPad.setKeyPadMode(I2C_KEYPAD_4x4);
assertEqual(I2C_KEYPAD_4x4, keyPad.getKeyPadMode());
keyPad.setKeyPadMode(I2C_KEYPAD_6x2);
assertEqual(I2C_KEYPAD_6x2, keyPad.getKeyPadMode());
keyPad.setKeyPadMode(I2C_KEYPAD_8x1);
assertEqual(I2C_KEYPAD_8x1, keyPad.getKeyPadMode());
// invalid are mapped to 4x4
keyPad.setKeyPadMode(00);
assertEqual(I2C_KEYPAD_4x4, keyPad.getKeyPadMode());
}
unittest(test_KeyMap)
{
const uint8_t KEYPAD_ADDRESS = 0x38;
I2CKeyPad keyPad(KEYPAD_ADDRESS);
char keymap[19] = "123A456B789C*0#DNF";
keyPad.loadKeyMap(keymap);
assertEqual('N', keyPad.getLastChar());
}
// Issues with Wire - to be investigated...
//
// unittest(test_read)