A function to get input from a keypad, and return char[x]

I’d like to make a function to that will get characters entered from a 4x3 keypad. The info is sometimes numbers, sometimes numbers and letters. I’d like to call the function by maybe passing it 2 arguments. The first is the max number of chars, the second could be a boolean set to true if it is to get numbers only. The largest size it would ever be is 20 characters.
I (think) I can work out the code for the function, but I don’t understand working with pointers and references (*, &). So I’m not sure how to pass the result back. If my function was like this:

char getInput(boolean numOnly, int maxSize) { 
//code to get key events
//return the result
}

Below is a portion of my code that gets a filename from the user. But I have this repeated again and again to get numbers and other data. It’s rather clumsy, I’m just a self taught Arduino DIY.

void Add_Part() {
  disp.lcd_cls(); //clear display
  disp.lcd_print(F("ENTER NEW PART NAME"));
  disp.lcd_rowcol(1, 3); //row, col
  disp.lcd_print(F("8 LETTERS MAX"));
  if (!continueMsg()) return; //wait for OK
  boolean valComplete = false;
  char newFileName[13]; //holder for new part name
  memset(newFileName, 0, sizeof(newFileName)); //delete the previous value.
  int charCount = 0; //counter for which letter we are on
  int currentLetter = 65; //ascii val for the current letter we are working with
  newFileName[0] = 'A'; //start with something
  disp.lcd_cls(); //clear display
  disp.lcd_print(F("NEW PART NAME:")); //print what ever part of the name they made so far
  disp.lcd_rowcol(3, 0); //row, col
  disp.lcd_print(F("* DONE      CANCEL #"));

  while (!valComplete) { //loop until the name is complete
    disp.lcd_rowcol(1, 6); //row, col
    disp.lcd_print(newFileName); //print what ever part of the name they made so far
    disp.lcd_print(F("       ")); //cover up old letters
    //0-9 are asci 48-57; hyphen is 45
    while (1) { //get letters
      byte k = checkKeyBuffer(); //get a keypress from the user
      if (k == keyPound) {
        cancelMsg();
        return; //user canceled
      }
      else if (k == keyAsterisk) { //user finished
        valComplete = true;
        break;
      }
      else if (k == 2) { // = up arrow; letter step increase
        currentLetter += 1;
        if (currentLetter > 90) currentLetter = 45; //loop to the lowest val
        if (currentLetter == 46) currentLetter = 48; //jump from hyphen up to 0
        if (currentLetter == 58) currentLetter = 65; //jump from 9 up to A
        newFileName[charCount] = currentLetter; //save the current letter to the name
        break; //exit to refresh screen
      }
      else if (k == 8) { // = down arrow; letter step decrease
        currentLetter -= 1;
        if (currentLetter == 64) currentLetter = 57; //jump from A down to 9
        if (currentLetter == 47) currentLetter = 45; //jump from 0 down to hyphen
        if (currentLetter < 45) currentLetter = 90; //loop to the highest val
        newFileName[charCount] = currentLetter; //save the current letter to the name
        break; //exit to refresh screen
      }
      else if (k == 6) { // = right arrow; advance to the next letter
        charCount += 1; //move to the next letter
        if (charCount > 7) { //8 chars max
          charCount = 7;
        }
        newFileName[charCount] = currentLetter; //save the current letter to the name
        break;
      }
      else if (k == 4) { // = left arrow; advance back a letter
        newFileName[charCount] = (char)0; //remove the current letter before backspacing
        charCount -= 1; //move back a letter
        if (charCount < 0) charCount = 0; //stop going back when we get to the first letter
        break;
      }
    } //end of getting a letter
  } //end of file name complete
  if (charCount < 1) { //2 chars minimum for the filename
    disp.lcd_cls(); //clear display
    disp.lcd_print(F("FILENAME MUST BE"));
    disp.lcd_rowcol(1, 0); //row, col
    disp.lcd_print(F("3 LETTERS MINIMUM"));
    continueMsg(); //wait for OK
    cancelMsg();
    return;
  }
  //now add the extension
  newFileName[charCount + 1] = '.';
  newFileName[charCount + 2] = 't';
  newFileName[charCount + 3] = 'x';
  newFileName[charCount + 4] = 't';
  if (SD.exists(newFileName)) {
    disp.lcd_cls(); //clear display
    disp.lcd_print(F("NAME ALREADY EXISTS"));
    delay(2000);
    cancelMsg();
    return;
  }

  File dataFile = SD.open(newFileName, FILE_WRITE);
  dataFile.close();

Ps. I forgot to mention, SRAM is of importance, so I was hoping to be able to do this without using global variables.

SouthernAtHeart:
Ps. I forgot to mention, SRAM is of importance, so I was hoping to be able to do this without using global variables.

Local variables occupy SRAM too.

void getInput(boolean numOnly, int maxSize, char *whereToStoreTheData)
{
   // write not more than maxSize characters to the whereToStoreTheData array
}

PaulS:

void getInput(boolean numOnly, int maxSize, char *whereToStoreTheData)

{
  // write not more than maxSize characters to the whereToStoreTheData array
}

I kinda understand this: So you're not really passing a string or characters back, you're just using the getInput function to put data in a variable that is in existence in the previous function. I tried making a test sketch, but I must have something wrong:

void setup() {
  Serial.begin(9600);
}

void loop() {
  char myBuf[10];
  getInput(true, 8, myBuf); //get 8 chars, numbers only, response in myBuf
  Serial.println(myBuf);
  delay(1000);

}

void getInput(boolean numOnly, int maxSize, char *whereToStoreTheData) {
  char test[] = {1, 2, 3, 4, 5, 6};   //for testing just makeup something
  whereToStoreTheData = test;
}
void getInput(boolean numOnly, int maxSize, char *whereToStoreTheData) {
  char test[] = {1, 2, 3, 4, 5, 6};   //for testing just makeup something
  whereToStoreTheData = test;
}

It does no good to point at a local variable that immediately goes out of scope.

   whereToPutTheData[0] = 'C';
   whereToPutTheData[1] = 'a';
   whereToPutTheData[2] = 't';
   whereToPutTheData[3] = '\0';

Thanks Paul! I think I got it. AND -- I think I understand it. I like being able to understand what's happening...

I came up with this sample that is kinda like what happens in my real code:

void setup() {
  // put your setup code here, to run once:
  Serial.begin(9600);
}



void loop() {
  char a[20];
  Serial.println(a);
  getdat(a);
  Serial.println(a);
  delay(1000);
}


void getdat(char *theData)
{
  char newData[20] = {'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h'}; //this would actually come from an SD filename
  sprintf(theData, newData);

}

ps. I'm still at a loss on that other topic you helped with today - how to 'mask the int to get those bits'

  sprintf(theData, newData);

strcpy() would be better.

But, that is not really how your code will work, is it? Just store the data read from the keypad in the array you get the pointer to. The method really does need to know how big the array is, so it does not write beyond the end of the array.

What I’ve learned today from you, in my mind, has revolutionized the way I’ll think about functions! I’ve just always worked with global variables, but now I’m seeing how this is much better. My project is ‘sort of’ up and running - a cnc type machine with SDcard for data, roboclaw motor controller, I2C Keypad/LCD display… It was 30+ K of sketch, with 80% of my dynamic memory used up, but now I’m already down to 25K & 60% dynamic memory, and the more I rework it the better I think it’ll get.
Here’s my updated function for selecting a filename from the user. Because I can display 3 filenames at a time on the 4X20 LCD, I had three global chars[20], but now I don’t have to have any globals to do that job. And this is just one little step in the whole process, so I’m going to go thru and re-write it all again.

void getFileName(char *theData) { //selects a file from the the SD file and returns it
  char fileNames[3][13];  //names of the next 3 files
  char displayName[9]; //display name for the LCD (no extension)
  disp.lcd_cls(); //clear display
  dir = SD.open("/"); //open the main directory
  while (1) { //keep looping through names until they select one
    for (int a = 0; a < 3; a++) { //loop thru 3 files
      File entry =  dir.openNextFile();
      if (!entry) { //there isn't a next file
        entry.close(); //close the entry
        dir.rewindDirectory(); //rewind
        entry =  dir.openNextFile(); //get the first file
      }
      strcpy(fileNames[a], entry.name());
      entry.close();
      int  len = strlen(fileNames[a]); //how long is the file name?
      memset(displayName, '\0', sizeof(displayName)); //delete the previous value.
      strncpy(displayName, fileNames[a], len - 4);
      //Display the name on the LCD
      disp.lcd_rowcol(a, 0); //row, col
      disp.lcd_print(a + 1); //the selection number
      disp.lcd_print(F(": ")); //the selection number suffix
      disp.lcd_print(displayName); //the filename without the extension
      disp.lcd_print(F("     ")); //3 char min for name, so clear any extra 5 chars
    }
    disp.lcd_rowcol(4, 0); //row, col
    disp.lcd_print(F("4. MORE  5. CANCEL")); //4 OR 5 TO SEE MORE FILE NAMES OF CANCEL

    while (1) { //wait for key input
      byte k = checkKeyBuffer(); //get a keypress from the user
      if (k >= 1 && k <= 3) { //user chose 1,2 or 3
        dir.close();
        sprintf(theData, fileNames[k - 1]);
        return;
      }
      else if (k == 4) {
        break; //exit the key input loop to show the next 3 entries
      }
      else if (k == 5) { //cancel
        dir.close();
        theData[0] = '\0'; //add null terminator
        return; //return nothing
      }
    } //end of wait for key stroke
  } //end of while
}

so would it be better to replace
sprintf(theData, fileNames[k - 1]);
with
strcpy(theData, fileNames[k - 1]);

so would it be better to replace

Yes. You are using only a tiny fraction of the capability of sprintf() to do what strcpy() can do, using less Flash memory.

Thanks. I see I saved 14 bytes just in that one line by changing it to strcpy.