Skip to content

Instantly share code, notes, and snippets.

@AnthonyDiGirolamo
Created November 29, 2011 15:38
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save AnthonyDiGirolamo/1405214 to your computer and use it in GitHub Desktop.
Save AnthonyDiGirolamo/1405214 to your computer and use it in GitHub Desktop.
Arduino Algebraic Calculator using a PS2Keyboard and LiquidCrystal Libraries
#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