Created
November 29, 2011 15:38
-
-
Save AnthonyDiGirolamo/1405214 to your computer and use it in GitHub Desktop.
Arduino Algebraic Calculator using a PS2Keyboard and LiquidCrystal Libraries
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <stdio.h> | |
#include <stdlib.h> | |
#include <ctype.h> | |
#include <math.h> | |
#include <PS2Keyboard.h> | |
#include <LiquidCrystal.h> | |
#define PS2DATA 2 | |
#define PS2IRQ 3 | |
#define CHECKMALLOC(var) if((var) == NULL) {Serial.println("ERROR: malloc\n");abort();} | |
#define MAXOPSTACK 32 | |
#define MAXNUMSTACK 32 | |
#define EXPRSIZE 128 | |
#define LCDCOLS 16 | |
PS2Keyboard keyboard; | |
LiquidCrystal lcd(4,5,6,7,8,9); | |
double numstack[MAXNUMSTACK]; | |
int nnumstack=0; | |
char *expression; | |
int expr_size; | |
int lcd_c, lcd_r; | |
char buffer1[16], buffer2[16]; | |
char get_keypress() { | |
char c = keyboard.read(); | |
/* | |
if (c == PS2_ENTER) { | |
Serial.println(); | |
} else if (c == PS2_TAB) { | |
Serial.println("[Tab]"); | |
} else if (c == PS2_ESC) { | |
Serial.println("[ESC]"); | |
} else if (c == PS2_PAGEDOWN) { | |
Serial.println("[PgDn]"); | |
} else if (c == PS2_PAGEUP) { | |
Serial.println("[PgUp]"); | |
} else if (c == PS2_LEFTARROW) { | |
Serial.println("[Left]"); | |
} else if (c == PS2_RIGHTARROW) { | |
Serial.println("[Right]"); | |
} else if (c == PS2_UPARROW) { | |
Serial.println("[Up]"); | |
} else if (c == PS2_DOWNARROW) { | |
Serial.println("[Down]"); | |
} else if (c == PS2_DELETE) { | |
Serial.println("[Del]"); | |
} else { | |
Serial.print(c); | |
} | |
*/ | |
return c; | |
} | |
void clear_stack(){ | |
for (int i=0; i<MAXNUMSTACK; i++){ | |
numstack[i]=0; | |
} | |
nnumstack=0; | |
} | |
double eval_add(double a1, double a2) { return a1+a2; } | |
double eval_sub(double a1, double a2) { return a1-a2; } | |
double eval_uminus(double a1, double a2) { return -a1; } | |
double eval_exp(double a1, double a2) { return a2<0 ? 0 : (a2==0?1:a1*eval_exp(a1, a2-1)); } | |
double eval_mul(double a1, double a2) { return a1*a2; } | |
double eval_div(double a1, double a2) { | |
if(!a2) { | |
Serial.println("ERROR: Division by zero\n"); | |
return NULL; | |
} | |
return a1/a2; | |
} | |
double eval_mod(double a1, double a2) { | |
if(!a2) { | |
Serial.println("ERROR: Division by zero\n"); | |
return NULL; | |
} | |
return fmod(a1, a2); | |
} | |
enum {ASSOC_NONE=0, ASSOC_LEFT, ASSOC_RIGHT}; | |
struct operator_type { | |
char op; | |
int prec; | |
int assoc; | |
int unary; | |
double (*eval)(double a1, double a2); | |
} operators[]={ | |
{'_', 10, ASSOC_RIGHT, 1, eval_uminus}, | |
{'^', 9, ASSOC_RIGHT, 0, eval_exp}, | |
{'*', 8, ASSOC_LEFT, 0, eval_mul}, | |
{'/', 8, ASSOC_LEFT, 0, eval_div}, | |
{'%', 8, ASSOC_LEFT, 0, eval_mod}, | |
{'+', 5, ASSOC_LEFT, 0, eval_add}, | |
{'-', 5, ASSOC_LEFT, 0, eval_sub}, | |
{'(', 0, ASSOC_NONE, 0, NULL}, | |
{')', 0, ASSOC_NONE, 0, NULL} | |
}; | |
struct operator_type *getop(char ch) { | |
int i; | |
for(i=0; i<sizeof operators/sizeof operators[0]; ++i) { | |
if(operators[i].op==ch) return operators+i; | |
} | |
return NULL; | |
} | |
struct operator_type *opstack[MAXOPSTACK]; | |
int nopstack=0; | |
void push_opstack(struct operator_type *op) { | |
if(nopstack>MAXOPSTACK-1) { | |
Serial.println("ERROR: Operator stack overflow\n"); | |
exit(1); | |
} | |
opstack[nopstack++]=op; | |
} | |
struct operator_type *pop_opstack() { | |
if(!nopstack) { | |
Serial.println("ERROR: Operator stack empty\n"); | |
exit(1); | |
} | |
return opstack[--nopstack]; | |
} | |
void push_numstack(double num) { | |
if(nnumstack>MAXNUMSTACK-1) { | |
Serial.println("ERROR: Number stack overflow\n"); | |
exit(1); | |
} | |
numstack[nnumstack++]=num; | |
} | |
double pop_numstack() { | |
if(!nnumstack) { | |
Serial.println("ERROR: Number stack empty\n"); | |
exit(1); | |
} | |
return numstack[--nnumstack]; | |
} | |
void shunt_op(struct operator_type *op) { | |
struct operator_type *pop; | |
double n1, n2; | |
if(op->op=='(') { | |
push_opstack(op); | |
return; | |
} else if(op->op==')') { | |
while(nopstack>0 && opstack[nopstack-1]->op!='(') { | |
pop=pop_opstack(); | |
n1=pop_numstack(); | |
if(pop->unary) push_numstack(pop->eval(n1, 0)); | |
else { | |
n2=pop_numstack(); | |
push_numstack(pop->eval(n2, n1)); | |
} | |
} | |
if(!(pop=pop_opstack()) || pop->op!='(') { | |
Serial.println("ERROR: Stack error. No matching \'(\'\n"); | |
exit(1); | |
} | |
return; | |
} | |
if(op->assoc==ASSOC_RIGHT) { | |
while(nopstack && op->prec<opstack[nopstack-1]->prec) { | |
pop=pop_opstack(); | |
n1=pop_numstack(); | |
if(pop->unary) push_numstack(pop->eval(n1, 0)); | |
else { | |
n2=pop_numstack(); | |
push_numstack(pop->eval(n2, n1)); | |
} | |
} | |
} else { | |
while(nopstack && op->prec<=opstack[nopstack-1]->prec) { | |
pop=pop_opstack(); | |
n1=pop_numstack(); | |
if(pop->unary) push_numstack(pop->eval(n1, 0)); | |
else { | |
n2=pop_numstack(); | |
push_numstack(pop->eval(n2, n1)); | |
} | |
} | |
} | |
push_opstack(op); | |
} | |
int isdigit_or_decimal(int c) { | |
if (c == '.' || isdigit(c)) | |
return 1; | |
else | |
return 0; | |
} | |
double eval_expression() { | |
char *expr; | |
char *tstart=NULL; | |
struct operator_type startop={'X', 0, ASSOC_NONE, 0, NULL}; /* Dummy operator to mark start */ | |
struct operator_type *op=NULL; | |
double n1, n2; | |
struct operator_type *lastop=&startop; | |
for (expr=expression; *expr; ++expr) { | |
if (!tstart) { | |
if ((op=getop(*expr))) { | |
if (lastop && (lastop == &startop || lastop->op != ')')) { | |
if (op->op == '-') | |
op=getop('_'); | |
else if (op->op!='(') { | |
Serial.print("ERROR: Illegal use of binary operator "); | |
Serial.println(op->op); | |
return NULL; | |
} | |
} | |
shunt_op(op); | |
lastop=op; | |
} else if (isdigit_or_decimal(*expr)) tstart = expr; | |
else if (!isspace(*expr)) { | |
Serial.print("ERROR: Syntax error\n"); | |
return NULL; | |
} | |
} else { | |
if (isspace(*expr)) { | |
push_numstack(atof(tstart)); | |
tstart=NULL; | |
lastop=NULL; | |
} else if ((op=getop(*expr))) { | |
push_numstack(atof(tstart)); | |
tstart=NULL; | |
shunt_op(op); | |
lastop=op; | |
} else if (!isdigit_or_decimal(*expr) ) { | |
Serial.print("ERROR: Syntax error\n"); | |
return NULL; | |
} | |
} | |
} | |
if(tstart) push_numstack(atof(tstart)); | |
while(nopstack) { | |
op=pop_opstack(); | |
n1=pop_numstack(); | |
if(op->unary) push_numstack(op->eval(n1, 0)); | |
else { | |
n2=pop_numstack(); | |
push_numstack(op->eval(n2, n1)); | |
} | |
} | |
if(nnumstack!=1) { | |
Serial.print("ERROR: Number stack has "); | |
Serial.print(nnumstack); | |
Serial.print(" elements after evaluation. Should be 1.\n"); | |
clear_stack(); | |
return NULL; | |
} | |
Serial.println(numstack[0]); | |
return numstack[0]; | |
} | |
void setup() { | |
// Serial Debug | |
Serial.begin(9600); | |
// Keyboard init | |
keyboard.begin(PS2DATA, PS2IRQ); | |
// LCD init | |
lcd.begin(16, 2); | |
lcd.noAutoscroll(); | |
lcd.setCursor(0, 0); | |
lcd.print("Welcome to"); | |
lcd.setCursor(0, 1); | |
lcd.print(" ABICOMP!"); | |
lcd.setCursor(0, 0); | |
expression = (char*) malloc(EXPRSIZE * sizeof(char)); | |
CHECKMALLOC(expression); | |
for (int i=0; i<EXPRSIZE; i++) { | |
expression[i] = ' '; | |
} | |
expression[EXPRSIZE-1]='\0'; | |
lcd_c = 0; | |
lcd_r = 0; | |
expr_size = 0; | |
} | |
int eval = 0; | |
double result = 0; | |
char c; | |
int window_start, window_end; | |
void loop() { | |
if (keyboard.available()) { | |
c = get_keypress(); | |
if (c == PS2_BACKSPACE) { | |
expression[expr_size-1]= ' '; | |
expr_size--; | |
} | |
else if (c == PS2_ENTER) { | |
eval = 1; | |
} | |
else if (c == PS2_ESC) { | |
for (int i=0; i<EXPRSIZE; i++) { | |
expression[i] = ' '; | |
} | |
expression[EXPRSIZE-1]='\0'; | |
expr_size = 0; | |
lcd.clear(); | |
lcd.setCursor(0, 0); | |
} | |
else if (expr_size < EXPRSIZE-1) { | |
expression[expr_size] = c; | |
expr_size++; | |
} | |
/* | |
Serial.print("updated expr["); | |
Serial.print(expr_size); | |
Serial.print("]: \""); | |
Serial.print(expression); | |
Serial.println("\""); | |
*/ | |
/* | |
sprintf(buffer1, "%.*s\n", LCDCOLS, expression); | |
Serial.print("buffer1: \""); | |
Serial.print(buffer1); | |
Serial.println("\""); | |
*/ | |
window_start = 0; | |
if (expr_size-LCDCOLS > 0){ | |
window_start = expr_size-LCDCOLS; | |
} | |
window_end = window_start+16; | |
if (window_end > (EXPRSIZE-1)){ | |
window_end = EXPRSIZE-1; | |
} | |
/* | |
Serial.println(window_start); | |
Serial.println(window_end); | |
*/ | |
lcd.clear(); | |
lcd.setCursor(0, 0); | |
for (int i=window_start; i<window_end; i++){ | |
lcd.write(expression[i]); | |
} | |
} | |
if (eval) { | |
result = eval_expression(); | |
if (result != NULL) { | |
/* | |
Serial.print("result: "); | |
Serial.println(result); | |
*/ | |
lcd.clear(); | |
lcd.setCursor(0, 0); | |
for (int i=window_start; i<window_end; i++){ | |
lcd.write(expression[i]); | |
} | |
lcd.setCursor(0, 1); | |
lcd.print(result); | |
clear_stack(); | |
} | |
eval = 0; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment