Push Buttons for my calculator

Hello everyone, currently building an arduino calculator and i wanted to add a few push buttons for additional operations as parenthesis sin cos and a decimal point.
When i click a button at the moment i get no input on the screen, so i guess its either not connected properly or the code itself is not right.
Any help is appreciated.

#include <StandardCplusplus.h>
#include <system_configuration.h>
#include <unwind-cxx.h>
#include <utility.h>
#include <stack>
#include <cmath>
#include <Keypad.h>
#include <Wire.h>
#include <LiquidCrystal_I2C.h>

using namespace std;

// Function to check if a character is an operator
bool isOperator(char c) {
    return c == '+' || c == '-' || c == '*' || c == '/' || c == 'c' || c == 's' || c == '=';
}

// Function to check if a string represents a function
bool isFunction(const String& str) {
    return str == "cos" || str == "sin";
}

// Define operator precedence using an array
int operatorPrecedence[256] = {};

void initializeOperatorPrecedence() {
    operatorPrecedence['+'] = 1;
    operatorPrecedence['-'] = 1;
    operatorPrecedence['*'] = 2;
    operatorPrecedence['/'] = 2;
    operatorPrecedence['c'] = 3;
    operatorPrecedence['s'] = 3;
    operatorPrecedence['='] = 0; // '=' is for evaluating the expression
}

// Function to check if op1 has higher precedence than op2
bool hasHigherPrecedence(char op1, char op2) {
    return operatorPrecedence[op1] > operatorPrecedence[op2];
}

// Function to perform the operation based on the operator
double performOperation(double operand1, double operand2, char op) {
    switch (op) {
    case '+':
        return operand1 + operand2;
    case '-':
        return operand1 - operand2;
    case '*':
        return operand1 * operand2;
    case '/':
        return operand1 / operand2;
    case 'c':
        return cos(operand1);
    case 's':
        return sin(operand1);
    default:
        return 0.0; // Handle other cases as needed
    }
}

// Function to perform a trigonometric function
double performFunction(const String& func, double arg) {
    if (func == "cos") {
        return cos(arg * (M_PI / 180));
    }
    else if (func == "sin") {
        return sin(arg * (M_PI / 180));
    }
    else {
        return 0.0; // Handle other functions as needed
    }
}

double evaluateExpression(const String& expression) {
    stack<double> numStack;
    stack<String> opStack;

    size_t i = 0;
    while (i < expression.length()) {
        if (isdigit(expression[i]) || expression[i] == '.') {
            // If the character is a digit or a decimal point, extract the entire number
            size_t j = i;
            while (j < expression.length() && (isdigit(expression[j]) || expression[j] == '.')) {
                j++;
            }
            String numStr = expression.substring(i, j);
            double num = numStr.toDouble(); // Convert the string to a double
            numStack.push(num);
            i = j;
        }
        else if (isAlpha(expression[i])) {
            // If the character is a letter, it might be a function name
            size_t j = i;
            while (j < expression.length() && isAlpha(expression[j])) {
                j++;
            }
            String funcName = expression.substring(i, j);
            if (isFunction(funcName)) {
                opStack.push(funcName); // Push the function name onto the operator stack
            }
            else {
                return 0.0; // Handle unrecognized functions
            }
            i = j;
        }
        else if (isOperator(expression[i])) {
            // If the character is an operator, handle precedence and parentheses
            while (!opStack.empty() && opStack.top() != "(" && hasHigherPrecedence(opStack.top()[0], expression[i])) {
                String op = opStack.top();
                opStack.pop();
                if (op == "cos" || op == "sin") {
                    double arg = numStack.top();
                    numStack.pop();
                    double result = performFunction(op, arg);
                    numStack.push(result);
                }
                else {
                    double operand2 = numStack.top();
                    numStack.pop();
                    double operand1 = numStack.top();
                    numStack.pop();
                    double result = performOperation(operand1, operand2, op[0]);
                    numStack.push(result);
                }
            }
            opStack.push(String(expression[i]));
            i++;
        }
        else if (expression[i] == '(') {
            // If an opening parenthesis is encountered, push it onto the operator stack
            opStack.push("(");
            i++;
        }
        else if (expression[i] == ')') {
            // If a closing parenthesis is encountered, evaluate the expression inside the parentheses
            while (!opStack.empty() && opStack.top() != "(") {
                String op = opStack.top();
                opStack.pop();
                if (op == "cos" || op == "sin") {
                    double arg = numStack.top();
                    numStack.pop();
                    double result = performFunction(op, arg);
                    numStack.push(result);
                }
                else {
                    double operand2 = numStack.top();
                    numStack.pop();
                    double operand1 = numStack.top();
                    numStack.pop();
                    double result = performOperation(operand1, operand2, op[0]);
                    numStack.push(result);
                }
            }
            // Pop the opening parenthesis from the operator stack
            if (!opStack.empty() && opStack.top() == "(") {
                opStack.pop();
            }
            else {
                return 0.0; // Mismatched parentheses
            }
            i++;
        }
        else {
            return 0.0; // Invalid character
        }
    }

    // Evaluate any remaining operators
    while (!opStack.empty()) {
        String op = opStack.top();
        opStack.pop();
        if (op == "cos" || op == "sin") {
            double arg = numStack.top();
            numStack.pop();
            double result = performFunction(op, arg);
            numStack.push(result);
        }
        else {
            double operand2 = numStack.top();
            numStack.pop();
            double operand1 = numStack.top();
            numStack.pop();
            double result = performOperation(operand1, operand2, op[0]);
            numStack.push(result);
        }
    }

    // The final result will be at the top of the number stack
    return numStack.top();
}

// Define the I2C address and LCD dimensions
#define I2C_ADDRESS 0x27
#define LCD_COLUMNS 16
#define LCD_ROWS 2

// Create a LiquidCrystal_I2C object
LiquidCrystal_I2C lcd(I2C_ADDRESS, LCD_COLUMNS, LCD_ROWS);

// Define the keypad layout
const byte ROWS = 4; //four rows
const byte COLS = 4; //four columns
char keys[ROWS][COLS] = {
  {'1','2','3','+'},
  {'4','5','6','-'},
  {'7','8','9','*'},
  {'c','0','=','/'}
};
byte rowPins[ROWS] = {9, 8, 7, 6}; //connect to the row pinouts of the keypad
byte colPins[COLS] = {5, 4, 3, 2}; //connect to the column pinouts of the keypad

// Define the additional operation buttons
const int openParenButton = A0;
const int closeParenButton = A1;
const int decimalPointButton = A2;
const int sinButton = A3;
const int cosButton = A4;
const int sqrtButton = A5;

// Create a Keypad object
Keypad keypad = Keypad( makeKeymap(keys), rowPins, colPins, ROWS, COLS );

void setup() {
    // Initialize the LCD
    lcd.init();
    lcd.backlight();
    lcd.clear();

    // Initialize operator precedence
    initializeOperatorPrecedence();

    // Initialize the serial communication (for debugging)
    Serial.begin(9600);

    // Set the additional operation buttons as inputs
    pinMode(openParenButton, INPUT_PULLUP);
    pinMode(closeParenButton, INPUT_PULLUP);
    pinMode(decimalPointButton, INPUT_PULLUP);
    pinMode(sinButton, INPUT_PULLUP);
    pinMode(cosButton, INPUT_PULLUP);
    pinMode(sqrtButton, INPUT_PULLUP);
}

void loop() {
    String expression;
    lcd.clear(); // Clear the LCD screen

    // Display a message on the LCD
    lcd.setCursor(0, 0);
    lcd.print("Enter an expr:");

    // Use the keypad for input
    char key = keypad.getKey();
    while (key != '=') { // '=' key will evaluate the expression
        if (key) {
            expression += key;
            lcd.setCursor(expression.length() - 1, 1);
            lcd.print(key);
        }
        key = keypad.getKey();
    }

    // Check for additional operation buttons
    if (digitalRead(openParenButton) == LOW) {
        expression += "(";
        lcd.setCursor(expression.length() - 1, 1);
        lcd.print("(");
    }
    if (digitalRead(closeParenButton) == LOW) {
        expression += ")";
        lcd.setCursor(expression.length() - 1, 1);
        lcd.print(")");
    }
    if (digitalRead(decimalPointButton) == LOW) {
        expression += ".";
        lcd.setCursor(expression.length() - 1, 1);
        lcd.print(".");
    }
    if (digitalRead(sinButton) == LOW) {
        expression += "s";
        lcd.setCursor(expression.length() - 1, 1);
        lcd.print("s");
    }
    if (digitalRead(cosButton) == LOW) {
        expression += "c";
        lcd.setCursor(expression.length() - 1, 1);
        lcd.print("c");
    }
    if (digitalRead(sqrtButton) == LOW) {
        expression += "sqrt";
        lcd.setCursor(expression.length() - 1, 1);
        lcd.print("sqrt");
    }

    // Evaluate the expression
    double result = evaluateExpression(expression);

    // Display the result on the LCD
    lcd.setCursor(0, 1);
    lcd.print("Result: ");
    lcd.print(result, 2); // Display result with 2 decimal places

    // Print the result to serial for debugging
    Serial.print("Result: ");
    Serial.println(result);

    // Wait for a moment before clearing the LCD
    delay(2000);
    lcd.clear();
}

int operatorPrecedence[256] = {};

void initializeOperatorPrecedence()
{
    operatorPrecedence['+'] = 1;
    operatorPrecedence['-'] = 1;
    operatorPrecedence['*'] = 2;
    operatorPrecedence['/'] = 2;
    operatorPrecedence['c'] = 3;
    operatorPrecedence['s'] = 3;
    operatorPrecedence['='] = 0;  // '=' is for evaluating the expression
}

What a waste of memory. 512 bytes used to hold just 7 values

I note too that you have #included multiple libraries in the sketch, many of which would appear to be redundant. Did you write code or did you copy it from somewhere ?

Does a basic "read the keypad and print the input" sketch work without all of the complications of the calculator ? Start small and build on it

shouldn't the code detect the press of a button, the transition from HIGH to LOW and perform some action once?

sorry, if you're using the KeyPad library, why is the code checking specific inputs and not using the key value from the getKey()

you may also be interested in chapter 8 of the unix programming environment

Keypad characters and extra button characters are all part of expression when it is sent for evaluation.

    char key = keypad.getKey();
    while (key != '=') { // '=' key will evaluate the expression
        if (key) {
            expression += key;

@frontabl3 can you write or dig up a simple set of sketches that just tests the hardware?

a7

The

#include <system_configuration.h>
#include <unwind-cxx.h>
#include <utility.h>

libraries are added automatically once i add the standardcplusplus header.

What environment are you compiling the sketch in ? Why the need for the standardcplusplus library ?

Arduino IDE.
The c++ lib, helps out with the stack and cmath libs so i can implement the reverse polish notation.

I'm not sure if I understand correctly.
Is it to make a program to see if the push buttons work the way they are connected.
If so they do work with:

const int buttonPin = A2; 

void setup() {
  pinMode(buttonPin, INPUT_PULLUP); 
  Serial.begin(9600); 
}

void loop() {
  int buttonState = digitalRead(buttonPin);
  
  if (buttonState == LOW) {
   
    Serial.println("Button is pressed");
  } else {
    
    Serial.println("Button is not pressed");
  }

  delay(100);
}

I'm suggesting that you stop guessing.

Write some sketches that expand on your little test of one pushbutton.

Forget about the calculator logic for now. Just see if you can print to the LCD the keypad buttons you push, and print the extra functions buttons you press.

For testing just the keypad and buttons, you can even leave aside any complications that the LCD is introducing by using the serial monitor.

Either way…

Just report that you have pressed buttons, Postpone interpretation of the expression until you have reliable low level functioning of inputs and outputs.

a7

The keypad buttons work perfectly. I can type with them and calculate with them. Its just the push buttons themself that are not being processed.

    // Check for additional operation buttons
    if (digitalRead(openParenButton) == LOW) {

why isn't openParenButton part of the keypad?

why does it repeatedly invoke that action as long as the button is pressed instead of just once when it is pressed?

Try adding some serial printing, and use the serial mobitor to check the values of variables and to confirm the flow in your sketch.

    if (digitalRead(sqrtButton) == LOW) {
        expression += "sqrt";

Serial.println("SQRT");

        lcd.setCursor(expression.length() - 1, 1);
        lcd.print("sqrt");
    }

    // Evaluate the expression

Serial.print("          "); Serial.println(expression);

    double result = evaluateExpression(expression);

Otherwise you are flying blind. I think you'll see some sunrises if you look carefully at what your code is actually doing.

a7

I've made some progress to reading the push buttons.
Updated the while loop to read the buttons with the keypad.
Now that they work when i try to use either sin or cos in any equation i always end up with 0.

#include <StandardCplusplus.h>
#include <system_configuration.h>
#include <unwind-cxx.h>
#include <utility.h>
#include <stack>
#include <cmath>
#include <Keypad.h>
#include <Wire.h>
#include <LiquidCrystal_I2C.h>

using namespace std;

// Function to check if a character is an operator
bool isOperator(char c) {
    return c == '+' || c == '-' || c == '*' || c == '/' || c == 'c' || c == 's' || c == '=';
}

// Function to check if a string represents a function
bool isFunction(const String& str) {
    return str == "cos" || str == "sin";
}

// Define operator precedence using an array
int operatorPrecedence[256] = {};

void initializeOperatorPrecedence() {
    operatorPrecedence['+'] = 1;
    operatorPrecedence['-'] = 1;
    operatorPrecedence['*'] = 2;
    operatorPrecedence['/'] = 2;
    operatorPrecedence['c'] = 3;
    operatorPrecedence['s'] = 3;
    operatorPrecedence['='] = 0; // '=' is for evaluating the expression
}

// Function to check if op1 has higher precedence than op2
bool hasHigherPrecedence(char op1, char op2) {
    return operatorPrecedence[op1] > operatorPrecedence[op2];
}

// Function to perform the operation based on the operator
double performOperation(double operand1, double operand2, char op) {
    switch (op) {
    case '+':
        return operand1 + operand2;
    case '-':
        return operand1 - operand2;
    case '*':
        return operand1 * operand2;
    case '/':
        return operand1 / operand2;
    case 'c':
        return cos(operand1);
    case 's':
        return sin(operand1);
    default:
        return 0.0; // Handle other cases as needed
    }
}

// Function to perform a trigonometric function
double performFunction(const String& func, double arg) {
    if (func == "cos") {
        // Use the math library's cos function
        return cos(arg * (M_PI / 180.0));
    }
    else if (func == "sin") {
        // Use the math library's sin function
        return sin(arg * (M_PI / 180.0));
    }
    else {
        return 0.0; // Handle other functions as needed
    }
}

double evaluateExpression(const String& expression) {
    stack<double> numStack;
    stack<String> opStack;

    size_t i = 0;
    while (i < expression.length()) {
        if (isdigit(expression[i]) || expression[i] == '.') {
            // If the character is a digit or a decimal point, extract the entire number
            size_t j = i;
            while (j < expression.length() && (isdigit(expression[j]) || expression[j] == '.')) {
                j++;
            }
            String numStr = expression.substring(i, j);
            double num = numStr.toDouble(); // Convert the string to a double
            numStack.push(num);
            i = j;
        }
        else if (isAlpha(expression[i])) {
            // If the character is a letter, it might be a function name
            size_t j = i;
            while (j < expression.length() && isAlpha(expression[j])) {
                j++;
            }
            String funcName = expression.substring(i, j);
            if (isFunction(funcName)) {
                opStack.push(funcName); // Push the function name onto the operator stack
            }
            else {
                return 0.0; // Handle unrecognized functions
            }
            i = j;
        }
        else if (isOperator(expression[i])) {
            // If the character is an operator, handle precedence and parentheses
            while (!opStack.empty() && opStack.top() != "(" && hasHigherPrecedence(opStack.top()[0], expression[i])) {
                String op = opStack.top();
                opStack.pop();
                if (op == "cos" || op == "sin") {
                    double arg = numStack.top();
                    numStack.pop();
                    double result = performFunction(op, arg);
                    numStack.push(result);
                }
                else {
                    double operand2 = numStack.top();
                    numStack.pop();
                    double operand1 = numStack.top();
                    numStack.pop();
                    double result = performOperation(operand1, operand2, op[0]);
                    numStack.push(result);
                }
            }
            opStack.push(String(expression[i]));
            i++;
        }
        else if (expression[i] == '(') {
            // If an opening parenthesis is encountered, push it onto the operator stack
            opStack.push("(");
            i++;
        }
        else if (expression[i] == ')') {
            // If a closing parenthesis is encountered, evaluate the expression inside the parentheses
            while (!opStack.empty() && opStack.top() != "(") {
                String op = opStack.top();
                opStack.pop();
                if (op == "cos" || op == "sin") {
                    double arg = numStack.top();
                    numStack.pop();
                    double result = performFunction(op, arg);
                    numStack.push(result);
                }
                else {
                    double operand2 = numStack.top();
                    numStack.pop();
                    double operand1 = numStack.top();
                    numStack.pop();
                    double result = performOperation(operand1, operand2, op[0]);
                    numStack.push(result);
                }
            }
            // Pop the opening parenthesis from the operator stack
            if (!opStack.empty() && opStack.top() == "(") {
                opStack.pop();
            }
            else {
                return 0.0; // Mismatched parentheses
            }
            i++;
        }
        else {
            return 0.0; // Invalid character
        }
    }

    // Evaluate any remaining operators
    while (!opStack.empty()) {
        String op = opStack.top();
        opStack.pop();
        if (op == "cos" || op == "sin") {
            double arg = numStack.top();
            numStack.pop();
            double result = performFunction(op, arg);
            numStack.push(result);
        }
        else {
            double operand2 = numStack.top();
            numStack.pop();
            double operand1 = numStack.top();
            numStack.pop();
            double result = performOperation(operand1, operand2, op[0]);
            numStack.push(result);
        }
    }

    // The final result will be at the top of the number stack
    return numStack.top();
}

// Define the I2C address and LCD dimensions
#define I2C_ADDRESS 0x27
#define LCD_COLUMNS 16
#define LCD_ROWS 2

// Create a LiquidCrystal_I2C object
LiquidCrystal_I2C lcd(I2C_ADDRESS, LCD_COLUMNS, LCD_ROWS);

// Define the keypad layout
const byte ROWS = 4; //four rows
const byte COLS = 4; //four columns
char keys[ROWS][COLS] = {
  {'1','2','3','+'},
  {'4','5','6','-'},
  {'7','8','9','*'},
  {'c','0','=','/'}
};
byte rowPins[ROWS] = {9, 8, 7, 6}; //connect to the row pinouts of the keypad
byte colPins[COLS] = {5, 4, 3, 2}; //connect to the column pinouts of the keypad

// Create a Keypad object
Keypad keypad = Keypad( makeKeymap(keys), rowPins, colPins, ROWS, COLS );

void setup() {
    // Initialize the LCD
    lcd.init();
    lcd.backlight();
    lcd.clear();

    // Initialize operator precedence
    initializeOperatorPrecedence();

    // Enable internal pull-up resistors for the button pins
    pinMode(A0, INPUT_PULLUP);
    pinMode(A1, INPUT_PULLUP);

    // Initialize the serial communication (for debugging)
    Serial.begin(9600);

    // Wait for serial connection to be established
    while (!Serial) {
        ; // Wait for Serial Monitor to open
    }
}

void loop() {
    String expression;
    lcd.clear(); // Clear the LCD screen

    // Display a message on the LCD
    lcd.setCursor(0, 0);
    lcd.print("Enter an expr:");

    // Use the keypad for input
    char key = keypad.getKey();
    while (key != '=') { // '=' key will evaluate the expression
        if (key) {
            expression += key;
            lcd.clear(); // Clear the LCD before displaying the expression
            lcd.setCursor(0, 1);
            lcd.print(expression); // Print the entire expression
        }

        // Check if the 'sin' button (connected to pin A0) is pressed
        if (digitalRead(A0) == LOW) {
            // Add 's' to the expression
            expression += 's';

            // Debugging: Print to console
            Serial.println("Sin button pressed");
            delay(500); // Debounce the button press
        }

        // Check if the 'cos' button (connected to pin A1) is pressed
        if (digitalRead(A1) == LOW) {
            // Add 'c' to the expression
            expression += 'c';

            // Debugging: Print to console
            Serial.println("Cos button pressed");
            delay(500); // Debounce the button press
        }

        key = keypad.getKey();
    }

    // Evaluate the expression
    double result = evaluateExpression(expression);

    // Display the result on the LCD
    lcd.setCursor(0, 1);
    lcd.print("Result: ");
    lcd.print(result, 2); // Display result with 2 decimal places

    // Print the result to serial for debugging
    Serial.print("Result: ");
    Serial.println(result);

    // Wait for a moment before clearing the LCD
    delay(2000);
    lcd.clear();
}

Since you

    // Initialize the serial communication (for debugging)
    Serial.begin(9600);

why not use some serial printing, right where you calculate the sin or cosine, to see what you are feeding to cos() or sin() and to check if it even ever calls those functions and returns a plausible result?

I'm confused between your use of 'c' and "cos" and 's' and "sin". I'm under the umbrella so I can neither examine your code probably or run it myself.

But I know I'd be printing stuff all over the place to see if it was doing what I thought it should and not just what I wrote, sometimes a different thing altogether.

a7

Say what !