Keypad Entry to String (Unexpected Results)

Hello, I’m currently working on a project to be able to control the position of stepper motors. I have a working prototype with a 10-turn pot as a controller, but its unsteady and inaccurate. I decided to try to incorporate a 3x4 matrix keypad from adafruit, but I have been met with some adversity.

My goal is to run regular operation, until the ‘*’ key is pressed, then it waits (blocking the code) for a key entry, inputs it into a string and repeats until the string is 3 characters long (not including the ‘\0’ at the end) and converts it to a float or int value. Then, when the ‘#’ key is pressed, it will check whether or not the value is within the allotted range and if so, will set that as a the desired position for the motor.

I have the motor operation working on another prototype (using AccelStepper library) so that is not the concern.

The issue is, when I’m done entering the 3 digits, the code will still change the necessary value when other values are pressed (including the ‘#’ which makes it an invalid conversion and outputs 0.00). It does it for anywhere between 6-9 digits then leaves that function. Separate case’#’: doesn’t trigger either. I don’t understand why the case’*’ waits for those other inputs as well. It doesn’t seem to make sense in the context of my code, but I would appreciate your opinions/tips on it.

Thanks.

/*

Keypad_Button Prototype
Perman
02-10-2015

*/


#include <Keypad.h>          // loads keypad library
#include <LiquidCrystal.h>   // loads LCD screen library
#include <AccelStepper.h>    // loads motor controller library

const byte rows = 4;  // Keypad rows, columns
const byte cols = 3;

float x; // generates float

const int buttonPin = 7; // Button Pin

const int dirPin = 8;  // Motor driver pins
const int stepPin = 9;

const int DB7Pin = 43; // LCD Screen pins
const int DB6Pin = 45;
const int DB5Pin = 47;
const int DB4Pin = 49;
const int RSPin = 53;
const int EPin = 51;

int despos = 0;  // Final prototype, this will be set as initial
int setPos = 0;  // motor position so motor will not move on start
                 // up
// char posString[2]; 
//int i = 0;

char keys[rows][cols] = {   // Setting up the Keypad
  {'1','2','3'},
  {'4','5','6'},
  {'7','8','9'},
  {'*','0','#'}
};

byte rowPins[rows] = {38,36,34,32}; // Keypad Pins
byte colPins[cols] = {30,28,26};

Keypad keypad = Keypad(makeKeymap(keys), rowPins, colPins, rows, cols);
LiquidCrystal lcd( RSPin, EPin, DB4Pin, DB5Pin, DB6Pin, DB7Pin);
//AccelStepper motor1(1, stepPin, dirPin);


void setup()
{
 pinMode(buttonPin, INPUT);
 Serial.begin(9600);
 lcd.begin(20,4);
 lcd.setCursor(0,0);
 lcd.print("You entered:  ");
// motor1.steMaxSpeed(500);
// motor1.setCurrentPosition(PotF); 

keypad.addEventListener(keypadEvent);

}

void loop()
{
  char key = keypad.getKey(); // Gets key
  if(int(key) !=0)         // Makes sure key is pressed
  {
//    lcd.setCursor(10,1); // displays key press to LCD
//    lcd.print(key);
    Serial.println(key);   // sends value to serial monitor
  }
  if (digitalRead(buttonPin) == HIGH) // Motor trigger
  {
    despos = setPos;  // sets desired position for motor
  }
}

void keypadEvent(KeypadEvent key){
  switch(key){
    case'*':
 {   
      lcd.setCursor(15,3);  // lets user know when its accepting
      lcd.print("Ready");   // input values
      char posString[4]="   "; // begins and clears string
      
      posString[0] = keypad.waitForKey();  //Waits for key press
      posString[1] = keypad.waitForKey();  // and adds keys to 
      posString[2] = keypad.waitForKey();  // specified 
      posString[3] = '\0';
     
//     
     lcd.setCursor(10,1);  // prints value
     lcd.print(posString);
//     Serial.println(x);
     lcd.setCursor(15,3); // wipes "ready" portion
     lcd.print("     ");
     
     float x = atof(posString); // converts to float and prints
     lcd.setCursor(10,2);
     lcd.print(x);
     break;
 }
   case '#':  // checks value
   {
     if (x>=0 && x<=723)  // within range?
     {
       
       lcd.setCursor(10,2);  //  If yes, prints value and 
       lcd.print(x);         // turns into integer
       Serial.println(x);
       setPos = (int)x;
     }
     else
     {
      lcd.setCursor(7,3);        // Prints "Not in Range
      lcd.print("Not In Range"); // if false, doesn't convert
      delay(1000);
      lcd.setCursor(7,3);
      lcd.print("            ");
     }
     break; 
     }
  } 
}

I've not worked with the keypad device for quite some time, but there are a couple of things that seem odd. First, case statement blocks do not need braces around each case block...they just add clutter. Second, I'm assuming that the keypadEvent() is triggered with each key press. Yet, in the middle of the '' case block, you redefine x, which is already defined as a global float. The instant you leave the '' case block, x disappears, yet you expect it to hang around for use in the '#' case block. Get rid of the redefinition of x in the '*' block and use the global definition.

econjack:
I've not worked with the keypad device for quite some time, but there are a couple of things that seem odd. First, case statement blocks do not need braces around each case block...they just add clutter. Second, I'm assuming that the keypadEvent() is triggered with each key press. Yet, in the middle of the '' case block, you redefine x, which is already defined as a global float. The instant you leave the '' case block, x disappears, yet you expect it to hang around for use in the '#' case block. Get rid of the redefinition of x in the '*' block and use the global definition.

Thank you very much. Removing the float allowed the '#' case to function properly. And the reason I put the brackets was due to the fact that I was getting a jump to case label error without them. With them I am able to compile and deal with the clutter, so I am okay with that.

Unfortunately I'm still getting the additional string overwriting with excessive key presses. With the '#' case working now, it can be ignored if proper procedure is followed, but I'd prefer to be able to not have to worry about that. Do you have any suggestions for that issue?

You may be able to deal with the clutter, but it makes it harder for the rest of us who have to read it. Figure out what the issue is and leave them out. It's also a good idea to make the last block in a switch a default block with something like:

#define DEBUG     // At the top of the program.

// rest of your code...

   default:
#ifdef DEBUG
      Serial.print("Shouldn't be here. In default, key = "); 
      Serial.println(key);
#endif
      break;

Since you should never get there, it helps to debug things. The DEBUG symbolic constant allows you to toggle debug code into and out of the program. If you comment out the #define DEBUG at the top of the program, the prints in the default case are not compiled into the program. This allows you to keep the test code in the source file, but it won't appear in the executable code.

It sounds like you need to count the key presses if you are overrunning something.

Have a look at this link.
http://playground.arduino.cc/Main/KeypadStepper

econjack:
Since you should never get there, it helps to debug things. The DEBUG symbolic constant allows you to toggle debug code into and out of the program. If you comment out the #define DEBUG at the top of the program, the prints in the default case are not compiled into the program. This allows you to keep the test code in the source file, but it won’t appear in the executable code.

It sounds like you need to count the key presses if you are overrunning something.

Thanks again for your thoughts. I inputted the debug code, opened up the serial monitor. With each button press it seems to send it to the monitor 3 times per button press consistently. Then when I hit the ‘#’ case, it outputs the final float value to the monitor 3 times. No idea why it isn’t outputting just once.

Like I said, I haven't done anything with a keypad for years. Could it be a debounce problem? You can find software debounce code on this site.

HazardsMind:
Have a look at this link.
http://playground.arduino.cc/Main/KeypadStepper

Thanks, after struggling with my original sketch, I decided to just restart. I ended up modifying that template and making some necessary changes. This works perfectly and doesn't run into any of the previous issues. I'm thinking there was some bugged interaction at the keypadevent level that was just going to be difficult to work around. My code isn't complete just yet, but it has the basic functionality I needed. So thank you.