#include <stack>
#include <string>
#include <Arduino.h>
#include <Keypad.h>
#include <LiquidCrystal_I2C.h>
using namespace std;
const double divide_by_zero_Error = 1.129345696325;
const double syntax_Error = 2.129345696325;
const uint8_t ROWS = 4;
const uint8_t COLS = 4;
char keys[ROWS][COLS] = {
{ '1', '2', '3', 'A' },
{ '4', '5', '6', 'B' },
{ '7', '8', '9', 'C' },
{ '*', '0', '#', 'D' }
};
uint8_t colPins[COLS] = {13, 12, 14, 27};
uint8_t rowPins[ROWS] = {26, 25, 33, 32};
Keypad keypad = Keypad(makeKeymap(keys), rowPins, colPins, ROWS, COLS);
LiquidCrystal_I2C lcd(0x27, 16, 2);
string user_input = "";
void printErrorSyntax() {
lcd.clear();
lcd.setCursor(5, 0);
lcd.print("Error");
lcd.setCursor(5, 1);
lcd.print("Syntax");
boolean button_pressed = false;
while (!button_pressed) {
if (keypad.getKey()) {
lcd.clear();
lcd.cursor();
lcd.setCursor(1, 0);
button_pressed = true;
}
}
}
void printResult(double result) {
lcd.setCursor(15, 1);
byte cursor = 15;
string result_string = "0" + to_string(round(result * 1000000.0) / 1000000.0);
for (byte i = result_string.size(); i > 0; i--, cursor--) {
if (cursor < 0) {
cursor = 0;
}
if (isdigit(result_string[i]) || result_string[i] == '-' || result_string[i] == '.') {
lcd.setCursor(cursor, 1);
lcd.print(result_string[i]);
Serial.print(result_string[i]);
}
}
boolean button_pressed = false;
while (!button_pressed) {
if (keypad.getKey()) {
lcd.clear();
lcd.cursor();
lcd.setCursor(1, 0);
button_pressed = true;
}
}
}
void printErrorDivisionByZero() {
lcd.clear();
lcd.setCursor(5, 0);
lcd.print("Error");
lcd.setCursor(0, 1);
lcd.print("Division by Zero");
boolean button_pressed = false;
while (!button_pressed) {
if (keypad.getKey()) {
lcd.clear();
lcd.cursor();
lcd.setCursor(1, 0);
button_pressed = true;
}
}
}
bool hasSyntaxErrors(const string& expr) {
int num_operators = 0;
int num_digits = 0;
char last_char = '\0';
bool last_was_operator = true;
for (char c : expr) {
if (isdigit(c) || c == '.') {
num_digits++;
last_was_operator = false;
}
else if (c == '+' || c == '-') {
num_operators++;
if (num_digits == 0 || last_was_operator) {
return true;
}
num_digits = 0;
last_was_operator = true;
}
else if (c == '*' || c == '/') {
num_operators++;
if (num_digits == 0 || last_char == '+' || last_char == '-' || last_char == '*' || last_char == '/') {
return true;
}
num_digits = 0;
last_was_operator = true;
}
else {
return true;
}
last_char = c;
}
if (num_digits == 0 || num_operators == 0) {
return true;
}
return false;
}
bool isOperator(char c) {
return c == '+' || c == '-' || c == '*' || c == '/';
}
bool isOperand(char c) {
return isdigit(c) || c == '.';
}
int getPriority(char op) {
if (op == '+' || op == '-') {
return 1;
} else if (op == '*' || op == '/') {
return 2;
} else {
return 0;
}
}
double calculate(string expr) {
if (hasSyntaxErrors(expr)) {
printErrorSyntax();
return syntax_Error;
}
stack<double> operands;
stack<char> operators;
for (int i = 0; i < expr.length(); i++) {
char c = expr[i];
if (c == ' ') {
continue;
}
if (isOperand(c)) {
string numStr = "";
while (i < expr.length() && isOperand(expr[i])) {
numStr += expr[i];
i++;
}
i--;
double num = stod(numStr);
operands.push(num);
}
else if (isOperator(c)) {
while (!operators.empty() && getPriority(operators.top()) >= getPriority(c)) {
char op = operators.top();
operators.pop();
double op2 = operands.top();
operands.pop();
double op1 = operands.top();
operands.pop();
double result;
switch (op) {
case '+':
result = op1 + op2;
break;
case '-':
result = op1 - op2;
break;
case '*':
result = op1 * op2;
break;
case '/':
if (op2 == 0) {
printErrorDivisionByZero();
return divide_by_zero_Error;
}
result = op1 / op2;
break;
}
operands.push(result);
}
operators.push(c);
}
}
while (!operators.empty()) {
char op = operators.top();
operators.pop();
double op2 = operands.top();
operands.pop();
double op1 = operands.top();
operands.pop();
double result;
switch (op) {
case '+':
result = op1 + op2;
break;
case '-':
result = op1 - op2;
break;
case '*':
result = op1 * op2;
break;
case '/':
if (op2 == 0) {
printErrorDivisionByZero();
return divide_by_zero_Error;
}
result = op1 / op2;
break;
}
operands.push(result);
}
return operands.top();
}
void input(char key_input) {
int cursor_pos = 0;
if (key_input == '=') {
double result = calculate(user_input);
if (result != divide_by_zero_Error && result != syntax_Error) {
printResult(result);
}
user_input = "";
return;
}
user_input += key_input;
lcd.print(key_input);
cursor_pos++;
}
void updateCursor() {
if (millis() / 250 % 2 == 0) {
lcd.cursor();
} else {
lcd.noCursor();
}
}
void showSplashScreen() {
lcd.setCursor(2, 0);
lcd.print("Emil & Nathan");
lcd.setCursor(0, 1);
String message = "Click To Continue";
for (byte i = 0; i < message.length(); i++) {
lcd.print(message[i]);
delay(150);
}
delay(500);
}
void setup() {
Serial.begin(115200);
lcd.init();
lcd.clear();
lcd.backlight();
showSplashScreen();
boolean button_pressed = false;
while (!button_pressed) {
if (keypad.getKey()) {
lcd.clear();
lcd.cursor();
lcd.setCursor(1, 0);
button_pressed = true;
}
}
}
void loop() {
updateCursor();
char key = keypad.getKey();
if (key) {
Serial.println(key);
input(key);
}
}