Keypad function() causing restart (Mega)

I seem to have run into a bit of a snag with a project, and I’d like some insight into why this may be happening.

My project requires the use of a keypad, and to start I added some simple code to test functionality before getting in too deep.

The function I’m having problems with is called editRevokedLIST().

In my main program loop, if I place my keypad function where I would like it to be (deep within some if statements), it will restart my Mega once it reaches that function (goes through void setup() and all). However, it only does this when I try to use code relating to the keypad. If I put in code, say, to print something to Serial or the LCD, it works fine. I also tried running that function by itself (with the keypad code), and it works as advertised. Also note that the rest of the program also seems to be working as it should.

Main loop as I would like it to be (ignore all the debug and SRAMfree functions everywhere, I thought perhaps I was running out of SRAM, but that doesn’t seem to be the case.)
You can see the function about halfway through the code:

//EDIT REVOKE LIST------------------------------------------------------------------------------------------------------
void editRevokedLIST()
{
  for(int i=0; i<5; i++)
  {
  char key = keypad.waitForKey();
  
  if (key){
    lcd.clear();
    lcd.setCursor(7,0);
    lcd.print(key);
  }
  }
}

//MAIN PROGRAM LOOP -------------------------------------------------------------------------------------------------------

void loop()
{ 
  standBy();
  if(standBy() == true)
  {
    instructionCue();
    if(buttonState == HIGH)
    {
      SRAMfree();
      currentMillis = millis();
      previousTimeoutMillis = currentMillis;
      timeout = false;
      scanCue();
      readTagID();
      if(timeout == false)
      { 
        readTagCODE();                                       // read tag CODE
        if(timeout == false)
        {
          readTagNAMELENGTH();                               // read number of stored NAME characters
          if(timeout == false)
          {
            addressEND();                                    // calculate ending address for stored NAME characters
            readTagNAME();                                   // read stored NAME characters
            if(timeout == false)
            {
              if(checkReadERROR() == true)
              {
                prepareID();
                if(checkAdmin() == true)
                {
                  cueSuccessADMIN();
                  openGate();
                  logCard();
                  SRAMfree();
                  return;
                }
                else
                {
                  if(checkEdit() == true)
                  {
                    SRAMfree();
                    cueSuccessEDIT();
                    editRevokedLIST();
                    return;
                  }
                  else
                  {
                    if(checkCurrentMEMBER() == true)
                    {
                      if(checkMemberSTATUS() == true)
                      {
                        Debug();
                        cueSuccess();
                        openGate();
                        logCard();
                        return;
                      }
                      else
                      {
                        Debug();
                        cueRevoked();
                        SRAMfree();
                        return;
                      }
                    }       
                    else
                    {
                      Debug();
                      cueExpired();
                      return;
                    }
                  }
                }
              }
              else
              {
                cueFail();
                Debug();
                return;
              }
            }
            else if(timeout == true)
            {
              cueCardMissing();
              return;
            }
          }
          else if(timeout == true)
          {
            cueCardMissing();
            return;
          }
        }
        else if(timeout == true)
        {
          cueCardMissing();
          return;
        }

      }
      else if(timeout == true)
      {
        cueCardMissing();
        return;
      }
    }
  }
}

Loop with only the keypad function (works):

//EDIT REVOKE LIST------------------------------------------------------------------------------------------------------
void editRevokedLIST()
{
  for(int i=0; i<5; i++)
  {
  char key = keypad.waitForKey();
  
  if (key){
    lcd.clear();
    lcd.setCursor(7,0);
    lcd.print(key);
  }
  }
}

//MAIN PROGRAM LOOP -------------------------------------------------------------------------------------------------------

void loop()
{ 
  standBy();
  if(standBy() == true)
  {
    instructionCue();
    if(buttonState == HIGH)
    {
      editRevokedLIST();
    }
  }
}

There is a lot of code there - is it possible to reproduce the problem in a simpler sketch? It will make it easier to track down, and along the way learning which pieces of code affect the problem will be useful information. Also, it's not obvious which section of code is the one which causes the problem to occur. If you know, can you point it out? Also please post the complete code, not just the parts that you think are relevant - if there's code which you know is irrelevant then remove it.

Do you have the hardware watchdog enabled?

PeterH:
There is a lot of code there - is it possible to reproduce the problem in a simpler sketch? It will make it easier to track down, and along the way learning which pieces of code affect the problem will be useful information. Also, it's not obvious which section of code is the one which causes the problem to occur. If you know, can you point it out? Also please post the complete code, not just the parts that you think are relevant - if there's code which you know is irrelevant then remove it.

Do you have the hardware watchdog enabled?

Thanks for your reply, I will try to be more clear, and have edited my original post to reflect this. I do not have a hardware watchdog timer. I will have to edit down the full code, it is too long to post.

How much free RAM do you have?

Nested function calls consume RAM (the stack).

Without seeing the rest of your code it is hard to comment.

Where did you get this keypad library? What does "waitForKey" do? (apart from the obvious).

I have approx 5.5kb of RAM free right before running the function (while nested), according to the memory check library I used. It seems accurate, as when I add or remove strings from the code the available RAM adjusts accordingly.

I will divy up and post the rest of the code after work.

I purchased they keypad from adafruit, and am using their library. waitForKey blocks all code until a keypress is registerred. I have tried the other commands as well(getKey), with no change in result.

Full Code:

Setup:

#include <Adafruit_VC0706.h>
#include <MemoryFree.h>
#include <Keypad.h>
#include <Adafruit_CharacterOLED.h>
#include <SoftwareSerial.h>
#include <Math.h>
#include <SD.h>
#include <Wire.h>
#include "RTClib.h"
#include <SoftwareSerial.h>
#define A_SIZEOF(array)     (sizeof(array)/sizeof(array[0]))
#define txPin 63
#define rxPin 62

Adafruit_CharacterOLED                  lcd(43, 44, 45, 46, 47, 48, 49);
SoftwareSerial                          RFIDserial(rxPin, txPin);
const uint8_t   pinLED_GREEN            = 26;                    // Visual Cue Pass
const uint8_t   pinLED_RED              = 24;                    // Visual Cue Fail
const uint8_t   pinLED_BLUE             = 28;                    // Visual Cue Working
const uint8_t   ON                      = LOW;
const uint8_t   OFF                     = HIGH;
const uint8_t   pinSPEAKER              = 22;                    // speaker
const uint8_t   buttonPin               = 30;
const uint8_t   RFID_READ               = 0x01;                  // Parallax RFID read command
const uint8_t   chipSelect              = 53;
int             buffID[5];                                       // tag ID array
char            buffCODE[5];                                     // pass code array
uint8_t         nameLength              = 0;                     // amount of characters to be passed to buffNAME
uint8_t         address                 = 0;
uint8_t         addressEnd              = 0;                     // last address to read/write
uint8_t         writeindex              = 0;                     // counter for next (buffNAME) write/read location
uint8_t         buttonState             = 0;
char            buffNAME[33];                                    // array of up to 32 characters
File            logFile;
File            revokedFile;
File            tempFile;
File            adminFile;
File            editFile;
RTC_DS1307      RTC;
long            previousDisplayMillis   = 0;
long            previousTimeoutMillis   = 0;
long            displayInterval         = 1650;
long            timeoutInterval         = 6000;
unsigned long   currentMillis           = millis();
boolean         wait                    = false;
boolean         timeout                 = true;
boolean         revoked                 = false;
boolean         admin                   = false;
boolean         edit                    = false;
char            character;
char            cardNUM[9];
char            revokedNUM[9];
char            adminNUM[9];
char            editNUM[9];
const byte      ROWS                     = 4;
const byte      COLS                     = 3;
char keys[ROWS][COLS] = 
{
  {'1','2','3'},
  {'4','5','6'},
  {'7','8','9'},
  {'*','0','#'}
};
byte rowPins[ROWS] = {38, 37, 36, 35}; 
byte colPins[COLS] = {34, 33, 32}; 
Keypad keypad = Keypad( makeKeymap(keys), rowPins, colPins, ROWS, COLS );


//---------------------------------------------------------------------------------------

void setup()
{

  // ... Serial Setup ...
  Serial.begin(9600);
  RFIDserial.begin(9600);

  // ... RTC Setup ...

  Wire.begin();
  RTC.begin();
  //RTC.adjust(DateTime(__DATE__, __TIME__));                    // Syncs Date/Time with compiling computer.

  // ... Pin Modes ...

  pinMode(pinLED_GREEN, OUTPUT);
  pinMode(pinLED_RED, OUTPUT);
  pinMode(pinLED_BLUE, OUTPUT);
  pinMode(buttonPin, INPUT);
  pinMode(txPin, OUTPUT);
  pinMode(rxPin, INPUT);
  pinMode(53, OUTPUT);                                          // make sure that the default chip select pin is set to
  // output, even if you don't use it

  // ... OLED SCREEN ... 

  lcd.begin(16, 2);

  // ... SD CARD ...

  lcd.clear();
  lcd.setCursor(2, 0);
  lcd.print("Initializing");
  lcd.setCursor(4,1);
  lcd.print("SD Card...");
  delay(1500);

  if (!SD.begin(chipSelect)) 
  {
    lcd.clear();
    lcd.setCursor(2,0);
    lcd.print("Card failed,");
    lcd.setCursor(1,1);
    lcd.print("or not present");
    delay(2000);
  }
  else
  {
    lcd.clear();
    lcd.setCursor(0,0);
    lcd.print("Card Initialized");
    delay(1500);
  }

  // ... KEYPAD ...

  keypad.setDebounceTime(10);
}

Functions:

void SRAMfree()
{
  Serial.print("freeMemory()=");
    Serial.println(freeMemory());
}

// READ READ-ONLY TAG ID --------------------------------------------------------------------------------------------------

void readTagID()
{
  int err;                                          // define an int for the error code
  do                                             
  {
    while(RFIDserial.available() > 0)
    {
      RFIDserial.read();
    }
    currentMillis = millis();
    if(currentMillis - previousTimeoutMillis > timeoutInterval)     // time out if no card present or card not being read
    {
      timeout = true;
      return;
    }
    else
    {
      RFIDserial.print("!RW");                        //Call for RFID tag address
      RFIDserial.write(byte(RFID_READ));
      RFIDserial.write(byte(32));
      while(RFIDserial.available() < 1);             // wait for data to become available
      err = RFIDserial.read();
    }
  } 
  while(err != 1);                                 // repeat above until error code is 1 (ERR:OK)
  while(RFIDserial.available() < 4);               // wait for all 4 bytes of data to become available
  int count = 0;
  do
  {
    buffID[count++] = RFIDserial.read();           //read bytes until array size is reached
  } 
  while(count < 4);
}

// READ STORED PASS CODE -------------------------------------------------------------------------------------------------

void readTagCODE()
{
  int err;                                          // define an int for the error code
  do
  {
    while(RFIDserial.available() > 0)
    {
      RFIDserial.read();
    }
    currentMillis = millis();
    if(currentMillis - previousTimeoutMillis > timeoutInterval)     // time out if no card present or card not being read
    {
      timeout = true;
      return;
    }
    else
    {
      RFIDserial.print("!RW");                        //Call for RFID tag address
      RFIDserial.write(byte(RFID_READ));
      RFIDserial.write(byte(3));
      while(RFIDserial.available() < 1);              // wait for data to become available
      err = RFIDserial.read();
    }
  } 
  while(err != 1);                                  // repeat above until error code is 1 (ERR:OK)
  while(RFIDserial.available() < 4);                // wait for all 4 bytes of data to become available
  int count = 0;
  do
  {
    buffCODE[count++] = RFIDserial.read();          //read bytes until array size is reached
  } 
  while(count < 4);
}

//READ NUMBER OF CHARACTERS STORED FOR NAME ARRAY -----------------------------------------------------------------------------

void readTagNAMELENGTH()
{
  int err;
  do
  {
    while(RFIDserial.available() > 0)
    {
      RFIDserial.read();
    }
    currentMillis = millis();
    if(currentMillis - previousTimeoutMillis > timeoutInterval)     // time out if no card present or card not being read
    {
      timeout = true;
      return;
    }
    else
    {
      RFIDserial.print("!RW");                        //Call for RFID tag address
      RFIDserial.write(byte(RFID_READ));
      RFIDserial.write(byte(31));
      while(RFIDserial.available() < 1);             // wait for data to become available
      err = RFIDserial.read();
    }
  } 
  while(err != 1);                                 // repeat above until error code is 1 (ERR:OK)

  while(RFIDserial.available() < 4);               // wait for all 4 bytes of data to become available
  nameLength = RFIDserial.read();              
}

//CALCULATE ENDING ADDRESS FOR NAME STORAGE---------------------------------------------------------------------------

void addressEND()                                  // calculate the ending address for the name
{                                                 // stored on the tag
  addressEnd = 0;
  if(nameLength == 4 ||
    nameLength == 8  ||
    nameLength == 12 ||
    nameLength == 16 ||
    nameLength == 20 ||
    nameLength == 24 ||
    nameLength == 28 ||
    nameLength == 32)
  {
    addressEnd = (4 + (nameLength/4));
  }
  else
  {
    addressEnd = (5 + (nameLength/4));
  }
}

//READ NAME ARRAY ------------------------------------------------------------------------------------------------------

void readTagNAME()
{
  int err;
  writeindex = 0;                                    // reset array index to zero

  for(                                              // select which addresses to write the name to
  address = 4; 
  address < addressEnd; 
  address++)
  {
    do                                               
    {
      while(RFIDserial.available() > 0)
      {
        RFIDserial.read();
      }
      currentMillis = millis();
      if(currentMillis - previousTimeoutMillis > timeoutInterval)     // time out if no card present or card not being read
      {
        timeout = true;
        return;
      }
      else
      {
        RFIDserial.print("!RW");                        //Call for RFID tag address
        RFIDserial.write(byte(RFID_READ));
        RFIDserial.write(byte(address));
        while(RFIDserial.available() < 1);             // wait for data to become available
        err = RFIDserial.read();
      }
    } 
    while(err != 1);                                 // repeat above until error code is 1 (ERR:OK)

    while(RFIDserial.available() < 4);                // wait for all 4 bytes of data to become available

    int count = 0;
    do
    {
      buffNAME[writeindex++] = RFIDserial.read();      //read bytes until array size is reached
      count++;
    }
    while(count < 4 && writeindex < nameLength);
  }
}

//------------------------------------------------------------------------------------------------------------------------

Functions part 2:

//STANDBY MODE-----------------------------------------------------------------------------------------------------

boolean standBy()                                        
{
  digitalWrite(pinLED_RED,OFF);
  digitalWrite(pinLED_GREEN, OFF);
  digitalWrite(pinLED_BLUE, OFF);
  return true;
}

//SCAN CARD LCD INSTRUCTIONS-----------------------------------------------------------------------------------------

void instructionCue()                                        
{
  currentMillis = millis();
  if(currentMillis - previousDisplayMillis > displayInterval) 
  {
    previousDisplayMillis = currentMillis;
    if(wait == false)
    {
      wait = true;
      placeCard();
    }
    else
    {
      wait = false;
      pressButton();
    }
  }
  digitalWrite(pinLED_RED,OFF);
  digitalWrite(pinLED_GREEN, OFF);
  digitalWrite(pinLED_BLUE, OFF);
  buttonState = digitalRead(buttonPin);
}


//TEST CARD READ CORRECTLY----------------------------------------------------------------------------------------

boolean checkReadERROR()
{
  if(buffID[0]==buffCODE[0]                          // test if card was read correctly
  && buffID[1]==buffCODE[1] 
    && buffID[2]==buffCODE[2])                          
  {     
    return false;
  }
  else if (buffID[0] == 0 
    && buffID[1] == 0
    && buffID[2] == 0)
  {       
    return false;
  }
  else
  {                                                   
    return true;
  }
}

//LOG SCANNED CARD TO SD--------------------------------------------------------------------------------------

void logCard()                                          
{
  logFile = SD.open("logfile.txt", FILE_WRITE);
  if (!logFile) 
  {
    lcd.clear();
    lcd.setCursor(1,0);
    lcd.print("Error Opening");
    lcd.setCursor(4,1);
    lcd.print("SD Card!");
    delay(2000);
    return;
  }
  logFile.println();
  DateTime now = RTC.now();
  logFile.print(now.year(), DEC);
  logFile.print('/');
  if (now.month() < 10)
  {
    logFile.print('0');
  }
  logFile.print(now.month(), DEC);
  logFile.print('/');
  if (now.day() < 10)
  {
    logFile.print('0');
  }
  logFile.print(now.day(), DEC);
  logFile.print(' ');
  if (now.hour() < 10)
  {
    logFile.print('0');
  }
  logFile.print(now.hour(), DEC);
  logFile.print(':');
  if (now.minute() < 10)
  {
    logFile.print('0');
  }
  logFile.print(now.minute(), DEC);
  logFile.print(':');
  if (now.second() < 10)
  {
    logFile.print('0');
  }
  logFile.print(now.second(), DEC);
  logFile.println();
  for(int i = 0; i < 4; i++)      
  {
    logFile.print(buffID[i]);
  }
  logFile.println();
  for(int i = 0; i < nameLength; i++)     
  {
    logFile.write(byte(buffNAME[i]));
  }
  logFile.println();
  logFile.println();
  logFile.println("---------------------------------------");
  logFile.flush();
  logFile.close();
}

Functions Part 3:

//PREPARE SCANNED ID FOR COMPARISON---------------------------------------------------------------------------------

void prepareID()                                          
{
  int count = 0;
  tempFile = SD.open("temp.txt", FILE_WRITE);
  for(int i = 0; i < 4; i++)      
  {
    tempFile.print(buffID[i]);
  }
  tempFile.println();
  tempFile.close();
  tempFile = SD.open("temp.txt");
  if (!tempFile) 
  {
    tempFileMISSING();
    delay(2000);
    return;
  }
  while(tempFile.available())
  {
    character = tempFile.read();
    cardNUM[count++] = character;
    if(character == '\n')
    {
      Serial.println("Scanned Card:");
      for(int i = 0; i < 9; i++)      
      {
        Serial.print(cardNUM[i]);
      }
      Serial.println();
      tempFile.close();
    }
  }
  SD.remove("temp.txt");
}

//CHECK IF CARD IS ADMIN--------------------------------------------------------------------------------------------

boolean checkAdmin()                                          
{
  admin = false;
  int count = 0;
  adminFile = SD.open("admin.txt");
  if (!adminFile) 
  {
    adminFileMISSING();
    delay(2000);
    return true;
  }
  while(adminFile.available()) 
  {
    character = adminFile.read();
    adminNUM[count++] = character;   
    if(character == '\n')
    {
      if(cardNUM[0] == adminNUM[0] &&
        cardNUM[1] == adminNUM[1] &&
        cardNUM[2] == adminNUM[2] &&
        cardNUM[3] == adminNUM[3] &&
        cardNUM[4] == adminNUM[4] &&
        cardNUM[5] == adminNUM[5] &&
        cardNUM[6] == adminNUM[6] &&
        cardNUM[7] == adminNUM[7] &&
        cardNUM[8] == adminNUM[8])
      {
        count = 0;
        admin = true;
      }
      else
      {
        count = 0;
      }
      Serial.println("Compare Admin:");
      for(int i = 0; i < 9; i++)      
      {
        Serial.print(adminNUM[i]);
      }
      Serial.println();
    }
  }
  adminFile.close();
  if(admin == true)
  {
    return true;
  }
  else
  {
    return false;
  }
}

//CHECK IF CARD IS EDIT--------------------------------------------------------------------------------------------

boolean checkEdit()                                          
{
  edit = false;
  int count = 0;
  editFile = SD.open("editcard.txt");
  if (!editFile) 
  {
    editFileMISSING();
    delay(2000);
    return false;
  }
  while(editFile.available()) 
  {
    character = editFile.read();
    editNUM[count++] = character;   
    if(character == '\n')
    {
      if(cardNUM[0] == editNUM[0] &&
        cardNUM[1] == editNUM[1] &&
        cardNUM[2] == editNUM[2] &&
        cardNUM[3] == editNUM[3] &&
        cardNUM[4] == editNUM[4] &&
        cardNUM[5] == editNUM[5] &&
        cardNUM[6] == editNUM[6] &&
        cardNUM[7] == editNUM[7] &&
        cardNUM[8] == editNUM[8])
      {
        count = 0;
        edit = true;
      }
      else
      {
        count = 0;
      }
      Serial.println("Compare Edit:");
      for(int i = 0; i < 9; i++)      
      {
        Serial.print(editNUM[i]);
      }
      Serial.println();
    }
  }
  editFile.close();
  if(edit == true)
  {
    return true;
  }
  else
  {
    return false;
  }
}

//CHECK IF MEMBERSHIP IS VALID-------------------------------------------------------------------------------------

boolean checkMemberSTATUS()                                          
{
  revoked = false;
  int count = 0;
  revokedFile = SD.open("revoked.txt");
  if (!revokedFile) 
  {
    revokedFileMISSING();
    delay(2000);
    return true;
  }
  while(revokedFile.available()) 
  {
    character = revokedFile.read();
    revokedNUM[count++] = character;   
    if(character == '\n')
    {
      if(cardNUM[0] == revokedNUM[0] &&
        cardNUM[1] == revokedNUM[1] &&
        cardNUM[2] == revokedNUM[2] &&
        cardNUM[3] == revokedNUM[3] &&
        cardNUM[4] == revokedNUM[4] &&
        cardNUM[5] == revokedNUM[5] &&
        cardNUM[6] == revokedNUM[6] &&
        cardNUM[7] == revokedNUM[7] &&
        cardNUM[8] == revokedNUM[8])
      {
        count = 0;
        revoked = true;
        revokedFile.close();
      }
      else
      {
        count = 0;
      }
      Serial.println("Compare Revoked:");
      for(int i = 0; i < 9; i++)      
      {
        Serial.print(revokedNUM[i]);
      }
      Serial.println();
    }
  }
  revokedFile.close();
  if(revoked == true)
  {
    return false;
  }
  else
  {
    return true;
  }
}

//EDIT REVOKE LIST------------------------------------------------------------------------------------------------------
void editRevokedLIST()
{
  SRAMfree();
  for(int i=0; i<5; i++)
  {
  char key = keypad.waitForKey();
  
  if (key){
    lcd.clear();
    lcd.setCursor(7,0);
    lcd.print(key);
  }
  }
}

I did not include functions like cueFail, cueSuccess etc. as all they do is print a few words to an LCD. And of course the main program loop as already been posted.

Turns out my problem was String related. I followed the instructions here:

http://forum.arduino.cc/index.php/topic,145765

and everything seems to be working fine now.