Go Down

Topic: Keypad library - Getting String instead of Character - FYI (Read 2 times) previous topic - next topic

robertwagner

Apr 25, 2012, 03:59 pm Last Edit: Apr 30, 2012, 09:35 pm by robertwagner Reason: 1
FYI - I have been able to modify the keypad library to return a String.  This can then be converted into a number and used for calculations.
The code below has existing lines in it so you can see where to add.  Test, let me know if you have any improvements.

Working Sketch:
Code: [Select]
#include <Keypad.h>

//By Robert Wagner 4-23-2012
//keypad - ACT Components 07-30008-000
const byte ROWS = 4; //four rows
const byte COLS = 4; //four columns
char keys[ROWS][COLS] = {  {'1','2','3','^'},  {'4','5','6','V'},  {'7','8','9','R'},  {'<','0','>','E'}};
byte rowPins[ROWS] = {4, 5, 6, 7}; //connect to the row pinouts of the keypad
byte colPins[COLS] = {11, 10, 9, 8}; //connect to the column pinouts of the keypad
Keypad keypad = Keypad( makeKeymap(keys), rowPins, colPins, ROWS, COLS );
//end keypad

void setup()
{
 Serial.begin(9600);                                            //Serial LCD, Parallax 27979.  Hooked to pin 1  4x20
 Serial.write(17);                                               //command flag for backlight stuff - turn on backlight
 delay(10);
}
void loop()
{  

delay(100);
Serial.write(128);                                     //goto position 0,0 on LCD
Serial.print("Enter A String");
String test = keypad.getString(5,'E');             //request string from keypad, 5 characters long, escape when E is pressed
                                                             //Note: test will return after 5 characters are pressed or the Escape character is pressed
Serial.write(12);                                       //clear LCD
Serial.write(148);                                      //goto position 1,0 on LCD
Serial.print("You entered: ");
Serial.print(test);
char test12[10];                                        //create empty character array to copy string into
memset(test12, 0, sizeof(test12));              //clear buffer
test.toCharArray(test12, sizeof(test12), 0);   //copy string into character array
Serial.write(168);                                      //goto position 2,0 on LCD
Serial.print("As a number: ");
Serial.print(test12);
Serial.write(188);                                     //goto position 3,0 on LCD
unsigned long test13=atol(test12);               //convert character array into unsigned long for math
Serial.print("Number *2: ");
Serial.print(test13*2);
}


This required a change to keypad.h

Code: [Select]

.....
char getKey();
String getString(int Length, char EscapeChar);                       //added this line 4-20-2012 RW
KeyState getState();
.....


and additions to keypad.cpp

Code: [Select]

....
char Keypad::getKey() {
// Return the new key value if a keypress was detected. By testing for
// keyStateChanged() we don't return a keypress more than once.
   if( getKeyState()==PRESSED && keyStateChanged() )
       return currentKey;

return NO_KEY; // Otherwise just return the default key value:
}

String Keypad::getString(int Length, char EscapeChar) {                        //added this line 4-20-2012 RW
   String currentString="";                                                                         //added this line 4-20-2012 RW
int count = 0;                                                                        //added this line 4-20-2012 RW
while (getKey() != EscapeChar){                                               //added this line 4-20-2012 RW
delay(100);                                                                  //added this line 4-20-2012 RW
if( getKeyState()==PRESSED && keyStateChanged() )                  //added this line 4-20-2012 RW
if( currentKey != NO_KEY){                                            //added this line 4-20-2012 RW
   if (currentKey != EscapeChar){                                   //added this line 4-20-2012 RW
   currentString += currentKey;                                     //added this line 4-20-2012 RW
count ++;                                                           //added this line 4-20-2012 RW
   if (count == Length){                                               //added this line 4-20-2012 RW
return currentString;                                   //added this line 4-20-2012 RW
}                                                                       //added this line 4-20-2012 RW
}                                                                              //added this line 4-20-2012 RW
else {                                                                        //added this line 4-20-2012 RW
return currentString;                                           //added this line 4-20-2012 RW
}                                                                               //added this line 4-20-2012 RW
}                                                                               //added this line 4-20-2012 RW
}                                                                                       //added this line 4-20-2012 RW
}                                                                                               //added this line 4-20-2012 RW



char Keypad::waitForKey() {
char waitKey = NO_KEY;
....




liudr

I would be more comfortable with this function OUTSIDE the original library. Just keep it in your project code instead. I don't use strings because arduino has too little memory. Every time you add a character to a string, it needs a new place to expand and you run out of memory. Many people have proved string class is not good approach neither is any dynamic memory allocation methods.

Nick Gammon

Just for your information, you can derive your own class from the supplied one, like this:

Code: [Select]
class myKeypad : public Keypad
  {
  public:
   String getString(int Length, char EscapeChar);                     
  };

String myKeypad::getString(int Length, char EscapeChar) {   
  String currentString="";                                     
  int count = 0; 
  char currentKey;

  while ((currentKey = getKey()) != EscapeChar)
  {                       
    delay(100);                                       
    if( getState()==PRESSED)     
      if( currentKey != NO_KEY){                       
        if (currentKey != EscapeChar)
        {               
          currentString += currentKey;                 
          count ++;                                     
          if (count == Length)
          {                         
            return currentString;                     
          }                                             
        }                                                 
        else
        {                                           
          return currentString;                         
        }                                                 
      }                                                 
  }                                                     
}                                                         


That way you don't modify the supplied files.

However I agree with Liudr that your code will probably work OK for half an hour and then crash due to memory fragmentation caused by the String library.
Please post technical questions on the forum, not by personal message. Thanks!

More info:
http://www.gammon.com.au/electronics

robertwagner

Thanks for the suggestions!  This begs a followup question on Strings and memory.  I don't see a method for allocating memory for Strings.  Is there a method for declaring a large string (slightly larger than you need to use), then using it throughout your program without causing havoc with the memory?  Or, each time you modify the string, does it try to get another memory piece?  I originally looked at character arrays, but could not return them from the library.  Are there any debugging variables I can dump to an LCD to show memory use?

Ideally, I believe I should be allocating all my memory during initialization/setup - then having it remain constant through the course of running the program.

brunialti

#4
Apr 26, 2012, 04:59 pm Last Edit: Apr 26, 2012, 05:49 pm by brunialti Reason: 1
about memory usage you can use the following function returning the sram free mem



Code: [Select]

int freeRam () {
 extern int __heap_start, *__brkval;
 int v;
 return (int) &v - (__brkval == 0 ? (int) &__heap_start : (int) __brkval);
 }


variable length string allocation is an hard task with the Harvard memory model choosen by Arduino (and short sram available).
If you have a (limited) set of objects of fixed length, nice work can be done even in Arduino env using dynamic alloc.


Nick Gammon


Thanks for the suggestions!  This begs a followup question on Strings and memory. 


For something like a keypad where you are probably just keying in a door unlock code, a simple array of (say) 10 bytes, with provision to not run off the end, may be adequate.

Or you might specify in the constructor how big a buffer you need and then just malloc it. The idea has merit ... I helped someone in another thread recently handle a keypad where they needed to key in a number followed by "#". In that case I just used a state machine to generate the number on the fly, and didn't need to store any old keypresses.
Please post technical questions on the forum, not by personal message. Thanks!

More info:
http://www.gammon.com.au/electronics

salah911

thank you man .. i used your code and finally after 8 hours writing my program it compiled and i hope every thing works fine with real interface
and as i find your code a great one i wish you declare the string : currentstring" in keypad.cpp as it took me forever to find out that this was the problem with my code :)

Nick Gammon

From above:

Code: [Select]
  String currentString="";

Since that was only used in that function I put the declaration there.
Please post technical questions on the forum, not by personal message. Thanks!

More info:
http://www.gammon.com.au/electronics

Go Up