switch case/ buttons' input issue

Hi all,

I'm (relatively) new to Arduino and am working on a "calculator" project for an electronics class. Specifically, I'm trying to use two buttons to control 1) the values being calculated and the operation and 2) switching the state of the Arduino, between accepting value inputs and performing calculations. An 8x8 matrix scrolls the result of the calculation as well as the values being input.

I'm having two possibly related problems:
The first is that in my switch statements, when my calculator is in the right "state" it won't always accept a button press (actually, less than 10% of the time).
The second is that it will switch from idle to firstNum just fine, but switching to operation essentially kills it. It doesn't print the statements that it's in that case, nor will the matrix display the operations value (which should be zero). Instead it prints "398", bizarrely. I'm attaching the code below:

#include <Wire.h>
#include <Adafruit_GFX.h>
#include "Adafruit_LEDBackpack.h"
//libraries provided by Adafruit for the LED Matrix/Backpack and the compatible graphics drawing routines found on github
//https://github.com/adafruit/Adafruit_LED_Backpack.git
//https://github.com/adafruit/Adafruit-GFX-Library.git

const int BUTTON = 2;     //define the pins used for the value incrementer and the input switcher
const int SWITCH = 3;     //

enum CalcState {          //enumerate the states possible for the calculator
  idle,
  firstNum,
  operation,
  secondNum,
  calculate,
};
CalcState printstate = idle;      //calculator starts in idle

Adafruit_8x8matrix matrix = Adafruit_8x8matrix();  //initialize the LED matrix
int firstNumber[] = {0};      //store the values in arrays so they'll be accessible to the switch case method
int secondNumber[] = {0};
int operations[] = {0};

//the following four lines of code adapted from the Arduino reference website on button debouncing: https://www.arduino.cc/en/Tutorial/Debounce
int buttonState;             // the current reading from the input pin
int lastButtonState = LOW;   // the previous reading from the input pin
int switchState;
int lastSwitchState = LOW;
unsigned long lastDebounceTime = 0;  // the last time the output pin was toggled
unsigned long lastDebounceSwitchTime = 0;
unsigned long debounceDelay = 100;    // the debounce time; increase if the output flickers


void setup() {
  Serial.begin(9600);
  //  pinMode(OPERATION, INPUT);
  pinMode(BUTTON, INPUT);
  pinMode(SWITCH, INPUT);
  matrix.begin(0x70);
}

static const uint8_t PROGMEM
test_bmp[] =
{ B11111111,
  B10000001,
  B10000001,
  B01000010,
  B01000010,
  B00100100,
  B00011000,
  B11111111
};



void loop() {
  printstate = nextState(printstate, firstNumber, secondNumber, operations);      //check the state and if the buttons have been pressed
  delay(100);  //sanity preserver
}


CalcState nextState (CalcState state, int firstNumber[], int secondNumber[], int operations[]) {
  switch (state) {
    case idle:
      Serial.print("idle");
      Serial.print(stateSwitch());
      if (stateSwitch() == 0) {              //if the state switching button hasn't been pressed, don't do anything
        state = idle;                      //
      } else if (stateSwitch() == 1) {         //otherwise, switch to accepting the first number
        state = firstNum;
      }
      break;

    case firstNum:
      Serial.print("firstNum");

      if (stateSwitch() == 0) {
        if (buttonPress() == 1) {       //if the value incrementing button is being pressed....increment the value.
          firstNumber[0]++;
        } state = firstNum;             //keep accepting value incrementing until the state switches
        matrixPrint(firstNumber[0]);
      }
      else if (stateSwitch() == 1) {
        state = operation;
        Serial.print("switching to operation mode");
        matrixPrint("1 ok");
      }
      break;

    case operation:
      Serial.print("operation");

      if (stateSwitch() == 0) {
        if (buttonPress() == 1) {
          operations[0]++;
        } state = operation;
        matrixPrint("operation[0]");
      }

      else if (stateSwitch() == 1) {
        state = secondNum;
        matrixPrint("O ok");
      }
      break;

    case secondNum:
      Serial.print("secondNum");

      if (stateSwitch() == 0) {
        if (buttonPress() == 1) {
          secondNumber[0]++;
        } state = secondNum;
        matrixPrint(secondNumber[0]);
      }

      else if (stateSwitch() == 1) {
        state = calculate;
        matrixPrint("2 ok");
      }
      break;

    case calculate:
      Serial.print("calculate");
      if (operations[0] % 4 == 0) {                       //only four operations on this calculator, you can use it on a gen chem exam
        matrixPrint(firstNumber[0] + secondNumber[0]);
      } else if (operations[0] % 4 == 1) {
        matrixPrint(firstNumber[0] - secondNumber[0]);
      } else if (operations[0] % 4 == 2) {
        matrixPrint(firstNumber[0] * secondNumber[0]);
      } else matrixPrint(firstNumber[0] / secondNumber[0]);
      if (stateSwitch() == 0) {
        state = calculate;
      } else if (stateSwitch() == 1) {
        state = firstNum;
      }
      break;
  }
  return state;
}

bool buttonPress() {
  int button = digitalRead(BUTTON);
  if (button == 1){
  Serial.println("pressed");
  return true;  
  } else return false;

  
//  int buttonHigh = 0;
//
//  if (button != lastButtonState) {
//    // reset the debouncing timer
//    lastDebounceTime = millis();
//  }
//
//  if ((millis() - lastDebounceTime) > debounceDelay) {
//    // whatever the reading is at, it's been there for longer than the debounce
//    // delay, so take it as the actual current state:
//
//    // if the button state has changed:
//    if (button != buttonState) {
//      buttonState = button;
//
//      // only switch cases if the new button state is HIGH
//      if (buttonState == HIGH) {
//        Serial.println("pressed");
//        buttonHigh = 1;
//      }
//    }
//  }
//
//  // save the reading. Next time through the loop, it'll be the lastButtonState:
//  lastButtonState = button;
//  return buttonHigh;
}

bool stateSwitch() {
  int switching = digitalRead(SWITCH);
  if (switching == 1){
  Serial.println("switched");
  return true;  
  } else return false;
//  int buttonHigh = 0;
//
//  if (switching != lastSwitchState) {
//    lastDebounceSwitchTime = millis();
//  }
//
//  if ((millis() - lastDebounceSwitchTime) > debounceDelay) {
//    if (switching != switchState) {
//      switchState = switching;
//      if (switchState == HIGH) {
//        Serial.println("state switched");
//        buttonHigh = 1;
//      }
//    }
//  }
//
//  lastSwitchState = switching;
//  return buttonHigh;
}

void matrixPrint(int value) {
  matrix.clear();     //the following lines of code in this method have been adapted from one of the example sketches provided by the LED Matrix Library
  matrix.setTextSize(1);
  matrix.setTextWrap(false);  // we dont want text to wrap so it scrolls nicely
  int length = 16;
  if (value != 0) {
    length = value * 8;
  }
  for (int8_t x = 0; x >= -length; x--) {  //to avoid waiting forever for a single digit to scroll, but also allow for longer strings to loop
    matrix.clear();
    matrix.setCursor(x, 0);
    matrix.print(value);
    matrix.writeDisplay();
    delay(100);
  }
}

I'm using this matrix 8x8 matrix, and an Uno board, if that helps. The buttons are wired with pull-down resistors to pins 2 and 3.
My theory is that I've bungled something about the switch case statement, possible where I'm not humanly able to slam the button fast enough for the case to read it, but any other errors you can point out would also be helpful.
Thanks guys!

Try breaking the task into individual problems that correspond to a function each.

  1. A function that blocks until the user presses a key on the keypad.
  2. Functions that simply display a character or a string on the LCD screen.
  3. A function that accumulates characters from the keypad in a string, and displays them on the LCD, until the user hits =.
  4. A function that receives the accumulated string, parses it, separates the numbers from the arithmetic operator, performs the specified calculation and displays the result.

Try this code. I haven't compiled it so there are probably syntax errors in it

// My own function that converts a string to a real number - as far as I can see there is no such function that comes with Arduino IDE
double toReal(const char *strNum, const uint8_t nBase)
{
 int8_t nI = 0, nSign = 0;
 uint8_t nPlaces = 0, nPos = 0, nLen = 0, nPow = 0, nMult = 0;
 double dVal = 0.0, dMult = 0;
 CString strNumber((char*)strNum, strlen(strNum));

 nPos = strNumber.indexOf(".");
 nPlaces = nPos - 1;
 nSign = 1;
 if (strNumber[0] == '-')
 {
 nSign = -1;
 strNumber[0] = '0';
 }
 else
 nSign = 1;

 // EXAMPLE: 123.456e-67
 if ((strstr(strNum, "e-") != NULL) || (strstr(strNum, "e+") != NULL) || (strstr(strNum, "E-") != NULL) || (strstr(strNum, "E+") != NULL))
 {
 for (nI = 0, nLen = strlen(strNum), dMult = pow(nBase, nPlaces); nI < nLen; nI++, dMult /= nBase)
 {
 if (strNumber[nI] == '.')
 dMult *= nBase;
 else if ((strNumber[nI] == 'e') || (strNumber[nI] == 'E'))
 break;
 else if ((strNumber[nI] >= '0') && (strNumber[nI] <= '9'))
 dVal += (strNumber[nI] - '0') * dMult;
 else if ((strNumber[nI] >= 'a') && (strNumber[nI] <= 'z'))
 dVal += (strNumber[nI] - 'W') * dMult;
 else if ((strNumber[nI] >= 'A') && (strNumber[nI] <= 'Z'))
 dVal += (strNumber[nI] - '7') * dMult;
 }
 dVal *= nSign;
 nI = nLen - 1;
 nMult = 1;
 while ((strNumber[nI] != '-') && (strNumber[nI] != '+'))
 {
 if ((strNumber[nI] >= '0') && (strNumber[nI] <= '9'))
 nPow += (strNumber[nI] - '0') * nMult;
 else if ((strNumber[nI] >= 'a') && (strNumber[nI] <= 'z'))
 nPow += (strNumber[nI] - 'W') * nMult;
 else if ((strNumber[nI] >= 'A') && (strNumber[nI] <= 'Z'))
 nPow += (strNumber[nI] - '7') * nMult;

 nMult *= nBase;
 nI--;
 }
 if (strNumber[nI] == '-')
 nSign = -1;
 else
 nSign = 1;

 dVal *= pow(nBase, nPow * nSign);
 }
 // EXAMPLE: 123.456
 else
 {
 for (nI = 0, nLen = strlen(strNum), dMult = pow(nBase, nPlaces); nI <= nLen; nI++, dMult /= nBase)
 {
 if (strNumber[nI] == '.')
 dMult *= nBase;
 else if ((strNumber[nI] >= '0') && (strNumber[nI] <= '9'))
 dVal += (strNumber[nI] - '0') * dMult;
 else if ((strNumber[nI] >= 'a') && (strNumber[nI] <= 'z'))
 dVal += (strNumber[nI] - 'W') * dMult;
 else if ((strNumber[nI] >= 'A') && (strNumber[nI] <= 'Z'))
 dVal += (strNumber[nI] - '7') * dMult;
 }
 dVal *= nSign;
 }
 return dVal;
}

char readChar()
{
 // Blocks until the user hits a key on your keypad and then returns the character pressed.
}

void displayChar(const char cChar)
{
 // Displays the character on your screen
}

void displayString(const char *str)
{
 for int nI = 0; nI < strlen(str); nI++)
 {
 displayChar(str[nI]);
 }
}

int16_t findOneOf(String & strSrc, const char *strFind)
{
 int16_t nResult = -1;
 
 for (uint8_t nI = 0; nI < strFind.length(), nI++)
 {
 nResult = strSrc.indexOf(strFind[nI]);
 if (nResult > -1)
  break;
 }
 return nResult;
}

double doCalculation(String &strCalcIn)
{
 double dResult = 0.0, dNum = 0.0;
 String strNum;
 char cOperator = 0;
 
 uint16_t nPos = 0;
 
 while (nPos > -1)
 {
 // E.G. 12*23+345
 nPos = findOneOf(strCalcIn, "*/+-");
 if (nPos > -1)
 {
 // E.G. 12
 strNum = strCalcIn.substring(0, nPos);
 
 // E.G. *23+345
 strCalcIn.remove(0, nPos)
 }
 else
 strNum = strCalcIn;
 
 dNum = toReal(strNum, 10);
  
 if (cOperator != 0)
 dResult = dNum; // First number from string - no arithmetic operator yet
 else if (cOperator == '-')
 dResult -= dNum
 else if (cOperator == '+')
 dResult += dNum;
 else if (cOperator == '*')
 dResult *= dNum;
 else if (cOperator == '/')
 dResult /= dNum;
 
 // E.G. *
 cOperator = strCalcIn[nPos];
 
 // E.G. 23+345
 strCalcIn.remove(0, 1)
 }
 return dResult;
}

void loop()
{
 char cCharIn = 0;
 static String strCalcIn; // 'static' means that the strCalcIn will retain its value between calls to loop() 

 cCharIn = readChar();
 displayChar(cChar);
 
 if (cChar == '=')
 {
 doCalculation(strNumIn);
  strCalcIn = "";
 }
 else
 {
 strCalcIn += cChar;
 }
}

You will have to fill in the functions related to the keypad and the LCD screen yourself because I have no idea what hardware you have used and how you have them setup, and I have not read your code in detail.

  delay(100);  //sanity preserver

Well, I think that this line might be doing the opposite of what the comment says. It's not obvious but the code will spend 99.9% of its time in the delay() and that doesn't leave much time for you to actually press any buttons.