The program below nicely returns two digit numbers.
Serial output for pressing for example '1' and then '2':
DCCpp_turntable_controller_v3
myValue = 1
i = 1
myValue = 12
i = 2
full value of two digits obtained : 12
Now I would like letters (A, B, C or D, as well as * and #), individually, to be output to a separate variable, different from where the number is stored.
Each letter is to be pressed just once, and stored as an idividual letter. Not two subsequent letters.
I am hitting a dead-end right now, so any usefull pointer or indication would be most appreciated.
Here is the current working code for return of two-digit numbers:
#include "Wire.h"
#include "I2CKeyPad.h" // https://github.com/RobTillaart/I2CKeyPad
const uint8_t KEYPAD_ADDRESS = 0x38; // PCF8574 = 0x20 PCF8574AP = 0x38
char keys[] = "123A456B789C*0#DNF"; // N = NoKey, F = Fail (e.g. >1 keys pressed)
uint8_t i;
I2CKeyPad keyPad(KEYPAD_ADDRESS);
void setup() {
Serial.begin(9600);
Serial.println("DCCpp_turntable_controller_v3");
Wire.begin();
if (keyPad.begin() == false)
{
Serial.println("\nERROR: cannot communicate to keypad.\nPlease reboot.\n");
while (1);
}
}
void loop() {
uint8_t inputInt = 0;
static bool keyPress = 0;
static bool secNum = 0;
static int myValue = 0;
static uint8_t caseValue = 0;
if (!keyPad.isPressed()) keyPress = true; // set flag keyPress: keypad released
if (keyPad.isPressed () && keyPress == true) // execute only once
{
keyPress = false;
myValue = getKeypadInteger(myValue);
Serial.print("myValue = ");
Serial.println(myValue);
Serial.print("i = ");
Serial.println(i);
}
if (i == 2) // 2 valid digits have been entered on keypad
{
i = 0;
Serial.print("full value of two digits obtained : ");
Serial.println(myValue);
caseValue = myValue; // convert myValue to character
myValue = 0;
stepperAction(caseValue); // activate stepper use keypad keystroke to select corresponding new position
}
}
int getKeypadInteger(int value)
{
char keyVal = 0; // the key press
bool isNum = 0;
bool isLetter = 0;
static int index = 0;
do
{
index = keyPad.getKey(); // input the key
keyVal = keys[index];
isNum = (keyVal >= '0' && keyVal <= '9'); // is it a digit?
if (isNum)
{
i = i + 1;
//Serial.print(keyVal - '0');
value = value * 10 + keyVal - '0'; // accumulate the input number
}
}
while ( !keyVal); // until not a digit or while no key pressed
return value;
}
void stepperAction(char keyStroke)
{
switch (keyStroke)
{
case 01:
{
}
}
}
here's code i put into and customize in almost all programs to help debug, exercise sub-functions or execute commands based on the value accumulated from numeric characters
// pcRead - debugging using serial monitor
const char version [] = "PcRead 201114a";
int debug = 0;
// ---------------------------------------------------------
// toggle output bit
int
readString (
char *s,
int maxChar )
{
int n = 0;
Serial.print ("> ");
do {
if (Serial.available()) {
int c = Serial.read ();
if ('\n' == c)
break;
s [n++] = c;
if (maxChar == n)
break;
}
} while (true);
return n;
}
// -----------------------------------------------------------------------------
// process single character commands from the PC
#define MAX_CHAR 10
char s [MAX_CHAR] = {};
int analogPin = 0;
void
pcRead (void)
{
static int val = 0;
if (Serial.available()) {
int c = Serial.read ();
switch (c) {
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
val = c - '0' + (10 * val);
break;
case 'A':
analogPin = val;
Serial.print ("analogPin = ");
Serial.println (val);
val = 0;
break;
case 'D':
debug ^= 1;
break;
case 'I':
pinMode (val, INPUT);
Serial.print ("pinMode ");
Serial.print (val);
Serial.println (" INPUT");
val = 0;
break;
case 'O':
pinMode (val, OUTPUT);
Serial.print ("pinMode ");
Serial.print (val);
Serial.println (" OUTPUT");
val = 0;
break;
case 'P':
pinMode (val, INPUT_PULLUP);
Serial.print ("pinMode ");
Serial.print (val);
Serial.println (" INPUT_PULLUP");
val = 0;
break;
case 'a':
Serial.print ("analogRead: ");
Serial.println (analogRead (val));
val = 0;
break;
case 'c':
digitalWrite (val, LOW);
Serial.print ("digitalWrite: LOW ");
Serial.println (val);
val = 0;
break;
case 'p':
#if !defined(ARDUINO_ARCH_ESP32)
analogWrite (analogPin, val);
Serial.print ("analogWrite: pin ");
Serial.print (analogPin);
Serial.print (", ");
Serial.println (val);
val = 0;
#endif
break;
case 'r':
Serial.print ("digitalRead: pin ");
Serial.print (val);
Serial.print (", ");
Serial.println (digitalRead (val));
val = 0;
break;
case 's':
digitalWrite (val, HIGH);
Serial.print ("digitalWrite: HIGH ");
Serial.println (val);
val = 0;
break;
case 't':
Serial.print ("pinToggle ");
Serial.println (val);
digitalWrite (val, ! digitalRead (val));
val = 0;
break;
case 'v':
Serial.print ("\nversion: ");
Serial.println (version);
break;
case '\n': // ignore
break;
case '"':
while ('\n' != Serial.read ()) // discard linefeed
;
readString (s, MAX_CHAR-1);
Serial.println (s);
break;
case '?':
Serial.println ("\npcRead:\n");
Serial.println (" [0-9] append to #");
Serial.println (" A # - set analog pin #");
Serial.println (" D # - set debug to #");
Serial.println (" I # - set pin # to INPUT");
Serial.println (" O # - set pin # to OUTPUT");
Serial.println (" P # - set pin # to INPUT_PULLUP");
Serial.println (" a # - analogRead (pin #)");
Serial.println (" c # - digitalWrite (pin #, LOW)");
Serial.println (" p # -- analogWrite (analogPin, #)");
Serial.println (" r # - digitalRead (pin #)");
Serial.println (" s - digitalWrite (pin #, HIGH)");
Serial.println (" t -- toggle pin # output");
Serial.println (" v - print version");
Serial.println (" \" - read string");
Serial.println (" ? - list of commands");
break;
default:
Serial.print ("unknown char ");
Serial.println (c,HEX);
break;
}
}
}
// -----------------------------------------------------------------------------
void
loop (void)
{
pcRead ();
}
// -----------------------------------------------------------------------------
void
setup (void)
{
Serial.begin(115200);
Serial.println (version);
#if defined(ARDUINO_ARCH_ESP32)
Serial.println ("esp32");
#endif
}
@Delta_G : thank you for that suggestion;
"else" will allow the processing for other keypad strokes than numerical ones.
However, I still need to store this non-numerical value into a different variable than the numerical.
For that I use the fact that a numerical requires two strokes. Hence an additional if condition after the function call: if two strokes were made then store in numerical variable; if just one stroke was needed then another storage variable.
I add the resulting program below; if you would want to look over it to see whether a different approach would be better?
One error I cannot seem to get rid of is that either the value for i = 2 hence a numerical entry was made, or i = 5 hence a character entry was made. But whenever the first of two numericals was entered the display shows "no valid keypad entry".
how to get rid of that display after a correct numerical entry
would it be necessary at all to have this display to be shown in case of an invalid character?
/*
Read keypad: if number pressed then two keypad strokes required; else just one stroke
Store keypad numerical entry for
1. either turntable index position (moveTo)
2. or store in EEPROM after 'A' is pressed
Store character entry for:
A = Assign current position eeprom for index position (to be entered)
B = not assigned yet
C = calibration of '0' index position
D = not assigned yet
# = no assigned yet
* = not assigned yet
How to input 2 digit numbers on keypad https://forum.arduino.cc/t/keypad-input-2-digit-numbers/604861
*/
#include "Wire.h"
#include "I2CKeyPad.h" // https://github.com/RobTillaart/I2CKeyPad
const uint8_t KEYPAD_ADDRESS = 0x38; // PCF8574 = 0x20 PCF8574AP = 0x38
char keys[] = "123A456B789C*0#DNF"; // N = NoKey, F = Fail (e.g. >1 keys pressed)
uint8_t i;
uint8_t ttMaxPos = 15; // maximum number of indexed turntable positions
I2CKeyPad keyPad(KEYPAD_ADDRESS);
///|||||||||||||||||||||||||||||||||||||||||||||||||||///
/// setup ///
///|||||||||||||||||||||||||||||||||||||||||||||||||||///
void setup() {
Serial.begin(9600);
Serial.println("DCCpp_turntable_controller_v3");
Wire.begin();
if (keyPad.begin() == false)
{
Serial.println("\nERROR: cannot communicate to keypad.\nPlease reboot.\n");
while (1);
}
}
///|||||||||||||||||||||||||||||||||||||||||||||||||||///
/// loop ///
///|||||||||||||||||||||||||||||||||||||||||||||||||||///
void loop() {
uint8_t inputInt = 0;
static bool keyPress = 0;
static bool secNum = 0;
static int myValue = 0;
static uint8_t caseValue = 0;
if (!keyPad.isPressed()) keyPress = true; // set flag keyPress: keypad released
if (keyPad.isPressed () && keyPress == true) // execute only once
{
keyPress = false;
myValue = getKeypadInteger(myValue); // read keypad being pressed
Serial.print("myValue = ");
Serial.println(myValue);
Serial.print("i = ");
Serial.println(i);
if (i == 2) // 2 valid digits have been entered on keypad
{
i = 0;
Serial.print("full value of two digits obtained : ");
Serial.println(myValue);
caseValue = myValue; // convert myValue to character
myValue = 0;
stepperAction(caseValue); // activate stepper use keypad keystroke to select corresponding new position
}
else if (i == 5) // a character or # or * was pressed
{
i = 0;
char str = myValue; // convert number to character
myValue = 0;
Serial.print("letter obtained : ");
Serial.println(str);
stepperAction(str);
}
else
{
Serial.println("no valid keypad entry");
}
}
}
///|||||||||||||||||||||||||||||||||||||||||||||||||||///
/// get integer from keypad, 2 values ///
///|||||||||||||||||||||||||||||||||||||||||||||||||||///
int getKeypadInteger(int value)
{
char keyVal = 0; // the key press
bool isNum = 0;
bool isLetter = 0;
static int index = 0;
do
{
index = keyPad.getKey(); // input the key
keyVal = keys[index];
isNum = (keyVal >= '0' && keyVal <= '9'); // is it a digit?
if (isNum)
{
i = i + 1;
//Serial.print(keyVal - '0');
value = value * 10 + keyVal - '0'; // accumulate the input number
}
if (keyVal == 'A' || keyVal == 'B' || keyVal == 'C' || keyVal == 'D' || keyVal == '*' || keyVal == '#')
{
i = 5;
value = keyVal;
}
}
while ( !keyVal); // until not a digit or while no key pressed
return value;
}
///|||||||||||||||||||||||||||||||||||||||||||||||||||///
/// keypad activated stepper ///
///|||||||||||||||||||||||||||||||||||||||||||||||||||///
void stepperAction(char keyStroke)
{
// i = 0;
switch (keyStroke)
{
case 01:
{
}
}
}
shouldn't you have a function that returns the keypad character, not integer and then separate process values that '0'...'9', 'A'...'F' and '*' and '#'.
the numeric characters can be used to accumulate a value -- val = 10*val + c-'0', the '*'/'#' either reset or process val and the non-digits are commands processed independently of one another
Do you mean that getKeypadInteger() should be used to return whatever is typed, and a separate process to separate integers from A...D and * and # characters?