From 58dc9bf8ce34604a4b9362d3c516b12057d01309 Mon Sep 17 00:00:00 2001 From: filifa Date: Tue, 19 Mar 2024 20:35:01 -0500 Subject: [PATCH] Initial commit --- Token.cpp | 30 +++++++ Token.h | 20 +++++ calculator.ino | 211 +++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 261 insertions(+) create mode 100644 Token.cpp create mode 100644 Token.h create mode 100644 calculator.ino diff --git a/Token.cpp b/Token.cpp new file mode 100644 index 0000000..02e8131 --- /dev/null +++ b/Token.cpp @@ -0,0 +1,30 @@ +#include "Arduino.h" +#include "Token.h" + +Token::Token(){} + +Token::Token(String d, String t) +{ + data = d; + dType = t; +} + +String Token::getData() +{ + return data; +} + +String Token::getDataType() +{ + return dType; +} + +void Token::setData(String d) +{ + data = d; +} + +void Token::setDataType(String t) +{ + dType = t; +} diff --git a/Token.h b/Token.h new file mode 100644 index 0000000..8b879ae --- /dev/null +++ b/Token.h @@ -0,0 +1,20 @@ +#ifndef Token_h +#define Token_h + +#include "Arduino.h" + +class Token +{ +private: + String data; + String dType; +public: + Token(); + Token(String d, String t); + String getData(); + String getDataType(); + void setData(String d); + void setDataType(String t); +}; + +#endif diff --git a/calculator.ino b/calculator.ino new file mode 100644 index 0000000..3fc60e2 --- /dev/null +++ b/calculator.ino @@ -0,0 +1,211 @@ +/* + * LCD Wiring + * Pin 1: Ground + * Pin 2: +5V + * Pin 3: Connect to wiper of potentiometer + * Pin 4: RS + * Pin 5: RW (Connect to Ground) + * Pin 6: EN + * Pins 7-10: DB0-DB3 (Unused) + * Pin 11: DB4 + * Pin 12: DB5 + * Pin 13: DB6 + * Pin 14: DB7 + * Pin 15: Backlight (Connect to +5V) + * Pin 16: Red + * Pin 17: Green + * Pin 18: Blue + */ + +#include +#define RSPIN 13 +#define ENPIN 12 +#define DB4PIN 8 +#define DB5PIN 7 +#define DB6PIN 6 +#define DB7PIN 5 + +#define REDLIGHT 11 +#define GREENLIGHT 10 +#define BLUELIGHT 9 + +#include +#define ROWS 4 +#define COLS 4 + +/* NOTE: program malfunctions if BUFFER is too big---see shunting function */ +#define BUFFER 16 +#include "Token.h" + +LiquidCrystal lcd(RSPIN, ENPIN, DB4PIN, DB5PIN, DB6PIN, DB7PIN); + +char keys[ROWS][COLS] = {{'1','2','3','A'}, + {'4','5','6','B'}, + {'7','8','9','C'}, + {'*','0','#','D'}}; +byte rowPins[ROWS] = {0,1,2,3}; +byte colPins[COLS] = {A0,A1,A2,A3}; +Keypad keypad = Keypad(makeKeymap(keys), rowPins, colPins, ROWS, COLS); + +void setBacklight(int r, int g, int b) +{ + r = map(r, 0, 255, 255, 0); + g = map(g, 0, 255, 255, 0); + b = map(b, 0, 255, 255, 0); + + analogWrite(REDLIGHT, r); + analogWrite(GREENLIGHT, g); + analogWrite(BLUELIGHT, b); +} + +bool isNum(char c) +{ + return c == '1' || c == '2' || c == '3' || c == '4' || c == '5' || + c == '6' || c == '7' || c == '8' || c == '9' || c == '0'; +} + +bool isOp(char c) +{ + return c == '+' || c == '-' || c == '*' || c == '/'; +} + +void tokenize(String input, Token *store, int *storeLen) +{ + int currLen = 0; + for (int i = 0; i < input.length(); i++) { + /* + * NOTE: When BUFFER is too big, some characters + * are seen as a block, so the if statements fail + */ + if (isNum(input[i])) { + /* + * Scans input until non-number is read, then adds to + * token + */ + String n; + for (int j = i; isNum(input[j]); j++) { + n += input[j]; + i = j; + } + store[currLen++] = Token(n, "Number"); + } else if (isOp(input[i])) { + store[currLen++] = Token((String)input[i], "Operator"); + } + } + + *storeLen = currLen; +} + +void shunting(Token *tokenInput, Token *rpnStore, int len) +{ + int place = 0; + Token opStack[BUFFER]; + int opLen = 0; + + for (int i = 0; i < len; i++) { + Token currToken = tokenInput[i]; + if (currToken.getDataType() == "Number") { + rpnStore[place++] = currToken; + } else if (currToken.getDataType() == "Operator") { + String currOp = currToken.getData(); + while (opLen > 0) { + String topOp = opStack[opLen-1].getData(); + // These two if statements could be combined + if (currOp == "+" || currOp == "-") + rpnStore[place++] = opStack[--opLen]; + else if ((currOp == "*" || currOp == "/") && + (topOp == "*" || topOp == "/")) + rpnStore[place++] = opStack[--opLen]; + else + break; + } + opStack[opLen++] = currToken; + } + } + + while (opLen > 0) + rpnStore[place++] = opStack[--opLen]; +} + +void calculate(Token *rpnInput, int len) +{ + float numStack[BUFFER]; + int numStackLen = 0; + for (int i = 0; i < len; i++) { + Token t = rpnInput[i]; + if (t.getDataType() == "Number") { + numStack[numStackLen++] = t.getData().toFloat(); + } else { + float sNum = numStack[--numStackLen]; + float fNum = numStack[--numStackLen]; + if (numStackLen < 0) { + lcd.print("ERROR"); + return; + } + if (t.getData() == "+") + numStack[numStackLen++] = fNum + sNum; + else if (t.getData() == "-") + numStack[numStackLen++] = fNum - sNum; + else if (t.getData() == "*") + numStack[numStackLen++] = fNum * sNum; + else if (t.getData() == "/") + numStack[numStackLen++] = fNum / sNum; + } + } + + lcd.print(numStack[0]); +} + +void getAns(String s) +{ + Token parsedInput[BUFFER]; + Token rpnParsedInput[BUFFER]; + int inputLen; + + tokenize(s, parsedInput, &inputLen); + shunting(parsedInput, rpnParsedInput, inputLen); + calculate(rpnParsedInput, inputLen); +} + +void setup() +{ + lcd.begin(16, 2); + pinMode(REDLIGHT, OUTPUT); + pinMode(GREENLIGHT, OUTPUT); + pinMode(BLUELIGHT, OUTPUT); + setBacklight(0, 128, 64); +} + +void loop() +{ + static String expr = ""; + + char keypress = keypad.waitForKey(); + switch (keypress) { + case '#': + lcd.setCursor(0,1); + getAns(expr); + + keypad.waitForKey(); + case '*': + expr = ""; + break; + case 'A': + expr += '/'; + break; + case 'B': + expr += '*'; + break; + case 'C': + expr += '-'; + break; + case 'D': + expr += '+'; + break; + default: + expr += keypress; + break; + } + lcd.clear(); + lcd.print(expr); +}