/*
* Arduino Keypad calculator Program
* Written by:
* Gabe (http://rampantrobots.blogspot.com/)
* and Kale (http://kalecloud.com/)
* https://www.instructables.com/Arduino-Calculator/
*/
#include "LiquidCrystal.h" //Header file for LCD from https://www.arduino.cc/en/Reference/LiquidCrystal
#include "Keypad.h" //Header file for Keypad from https://github.com/Chris--A/Keypad
#include "stdlib.h"
#include "math.h"
const byte ROWS = 4; // Four rows
const byte COLS = 4; // Three columns
// Define the Keymap
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 }; // Connect keypad ROW0, ROW1, ROW2 and ROW3 to these Arduino pins.
byte colPins[COLS] = { 4, 5, 6, 7 }; // Connect keypad COL0, COL1 and COL2 to these Arduino pins.
Keypad kpd = Keypad( makeKeymap(keys), rowPins, colPins, ROWS, COLS ); // Create the Keypad
const int rs = 8, en = 9, d4 = 10, d5 = 11, d6 = 12, d7 = 13; //Pins to which LCD is connected
LiquidCrystal lcd(rs, en, d4, d5, d6, d7);
///////////////////////////////////////////////////////VARIABLES///////////////////////////////////////////
//VARIABLES TO PARSE FLOATING ANSWER INTO WHOLE NUMBER AND DECIMAL PARTS///
char outresult[20];
String OutResult;
int decimal_location;
String whole_number;
String decimal_number;
//VARIABLES USED TO SCROLL TOP LINE//
long lasttime =0;
String totalarray = ""; //stores what was input
int length = 0; //length of what was input
int length2 = 0; //length after the first input is added
bool scroll1 = 0; //whether to scroll on line one or not
bool scroll2 = 0; //whether to scroll on line two or not
int scroll_index = 0;
int de=0;
//INPUT VARIABLES//
char input[9]; //buffer for input characters
float firstnumber = 0; //stores first number
float secondnumber = 0; //stores second number
float response = 0; //stores "answer"
bool positive = 1; //indicates positive number
int lcdcolumnindexrow1 = 1; // used to indicate location of input key
//FUNCTION VARIABLES//
int function = 9; //stores currently selected function
const int number_of_functions = 10;
int mode = 2; // select normal operation (1) or mystery function (0)
bool toggle = 1;
int level = 0; // stores level for the game
const int number_of_levels = 10;
void setup()
{
lcd.begin(16,2);
lcd.clear();
lcd.setCursor(0,0);
lcd.print("LOADING");
for(int i=0; i<8; i++){
lcd.setCursor(i,1);
lcd.write(255);
delay(170);
}
lcd.clear();
for(int i = 0; i < 8; i++) { input[i] = '0'; } // initialize input as zero
}
void loop()
{
if(scroll1) // display the input number and function(s) on the top line if math has been done
{
scrollline(750, 0, length);
}
else if(scroll2) // display the input number and function(s) on the second line if awaiting a 2nd number
{
scrollline(750, 1, length2);
}
char key = kpd.getKey(); // read keypad
if (key != NO_KEY)
{
if(de==1){
lcd.clear();
for(int i = 0; i < 8; i++) { input[i] = '0'; }
lcdcolumnindexrow1 = 1;
firstnumber = 0;
secondnumber = 0;
totalarray = "";
scroll1 = 0;
scroll2 = 0;
length = 0;
length2 = 0;
de=0;
if(!mode) {function = 10;} else {function = 9;}
positive = 1;
}
else if(key == 'A'){ //increment function in calculator mode
function = mod(++function, number_of_functions);
displayfunction(function);
}
else if(key == 'B'){ //decrement function in calculator mode
function = mod(--function, number_of_functions);
displayfunction(function);
}else if(key == 'C'){ //clear
lcd.clear();
for(int i = 0; i < 8; i++) { input[i] = '0'; }
lcdcolumnindexrow1 = 1;
firstnumber = 0;
secondnumber = 0;
totalarray = "";
scroll1 = 0;
scroll2 = 0;
length = 0;
length2 = 0;
de=0;
if(!mode) {function = 10;} else {function = 9;}
positive = 1;
}
else if(key == 'D') //enter
{
if(de==1){
}else{
if(function==5 || function==6 || function==7 || function==8){
firstnumber = atof(input); // convert input character array to a float
numintototalarray();
printoperation(function);
response = executefunction(function, firstnumber, 0); // do the math
numintototalarray();
scroll1 = 1; // display the input numbers and function on the top line
lcd.setCursor(0,1);
lcd.print(" ");
lcd.setCursor(0,1);
printresponse(response);
de=1;
}
else if(firstnumber == 0) // this is the first time that enter has been pressed and we have a valid function
{
firstnumber = atof(input); // convert input character array to a float
numintototalarray();
printoperation(function);
lcd.setCursor(0,0);
lcd.print(" "); //clear first line to make space for the second number
lcd.setCursor(0,1);
lcd.print(" "); //clear line
length2 = length;
scroll2 = 1; //print what the user has entered so far on the second line
for(int i = 0; i < 8; i++) { input[i] = '0'; } // reinitialize the input buffer to make space for the second number
lcdcolumnindexrow1 = 1;
positive = 1;
}else{
secondnumber = atof(input); // convert input character array to a float
response = executefunction(function, firstnumber, secondnumber); // do the math
numintototalarray();
totalarray = totalarray + "= ";
length = length + 2;
scroll1 = 1; // display the input numbers and function on the top line
if(function == 3 && secondnumber == 0){
lcd.setCursor(0,1);
lcd.print("ERROR");
de=1;
}else{
lcd.setCursor(0,1);
lcd.print(" "); //clear line
lcd.setCursor(0,1);
printresponse(response); //print answer
positive = 1;
de=1;
}
}
}
}
else if(key == '#' && positive ==1) //negative number read and number is currently positive
{
input[0] = '-';
lcd.setCursor(0,0);
lcd.print(input[0]); //print negative sign
positive = 0; //indicate number is now negative
++length;
}
else if(key == '#' && positive ==0)
{
positive = 1; //indicate number is now negative
input[0] = '0';
lcd.setCursor(0,0);
lcd.print(' '); //remove negative sign
--length;
}
else if(lcdcolumnindexrow1 < 8) //buffer overrun prevention
{
//read in a number or a decimal
{
for(int i =1; i < 7; i++) {input[i] = input[i+1]; } //shift all the values left
if(key == '*') { input[7] = '.'; } else { input[7] = key; } //* is decimal
lcd.setCursor(lcdcolumnindexrow1,0); //print on left of LCD moving right
lcd.print(input[7]);
++lcdcolumnindexrow1;
++length;
}
}
}
}
/////////////////////Function Returns Modulo Even for Negative Numbers//////////////////////////
int mod(int a, int b)
{
return (((a % b) + b) % b);
}
//////////////////////Function Displays Math Functions/////////////////////////////////////////
void displayfunction(int _function)
{
lcd.setCursor(0,1);
switch (_function)
{
case 0:
lcd.print("+ ");
break;
case 1:
lcd.print("- ");
break;
case 2:
lcd.print("x ");
break;
case 3:
lcd.print("/ ");
break;
case 4:
lcd.print("^ ");
break;
case 5:
lcd.print("^2 ");
break;
case 6:
lcd.print("sin ");
break;
case 7:
lcd.print("cos ");
break;
case 8:
lcd.print("tan ");
break;
case 9:
lcd.print(" ");
break;
}
}
//////////////////////Function Decides Whether Output Has 0, 1, 2, 3, or 4 Decimals AND Prints Output/////////////////
void printresponse(float _response)
{
if(abs(_response) < 0.0001 && abs(_response) > 0) {lcd.print("TooSmall");}
else if(_response > 99999999) {lcd.print("Too Big ");}
else if(_response < -9999999) {lcd.print("-Too Big");}
else
{
dtostrf(_response,7,4,outresult); //Convert Float to Character Array
for(int i=0; i<19; i++) //Find the location of the decimal
{
if(outresult[i] == '.')
{
decimal_location = i;
}
}
OutResult = outresult; //Convert Character Array to String
whole_number = OutResult.substring(0,decimal_location); //Read up to the Decimal Location
decimal_number = OutResult.substring(decimal_location+1, decimal_location+5); //Read After the Decimal Location
if(decimal_number[3] =='0' && decimal_number[2] =='0' && decimal_number[1] =='0' && decimal_number[0] =='0') //print only the whole number part
{
lcd.print(whole_number);
}
else if(decimal_number[3] =='0' && decimal_number[2] =='0' && decimal_number[1] =='0') // print one decimal place
{
OutResult = whole_number + '.' + decimal_number[0];
lcd.print(OutResult);
}
else if(decimal_number[3] =='0' && decimal_number[2] =='0') // print two decimal places
{
OutResult = whole_number + '.' + decimal_number[0] + decimal_number[1];
lcd.print(OutResult);
}
else if(decimal_number[3] =='0') // print three decimal places
{
OutResult = whole_number + '.' + decimal_number[0] + decimal_number[1] + decimal_number[2];
lcd.print(OutResult);
}
else {lcd.print(OutResult);} // print four decimal places
}
}
//////////////////////FUNCTION ADDS "+" "-" "/" TO STRING ARRAY/////////////////////////////////////////
void printoperation(int _function)
{
switch (_function)
{
case 0:
totalarray = totalarray + "+";
length = length + 1;
break;
case 1:
totalarray = totalarray + "-";
length = length + 1;
break;
case 2:
totalarray = totalarray + "x";
length = length + 1;
break;
case 3:
totalarray = totalarray + "/";
length = length + 1;
break;
case 4:
totalarray = totalarray + "^";
length = length + 1;
break;
case 5:
totalarray = totalarray + "^2= ";
length = length + 4;
break;
case 6:
totalarray = "sin(" + totalarray + ")= ";
length = length + 7;
break;
case 7:
totalarray = "cos(" + totalarray + ")= ";
length = length + 7;
break;
case 8:
totalarray = "tan(" + totalarray + ")= ";
length = length + 7;
break;
case 9:
break;
case 10:
totalarray = "f(" + totalarray + ")= ";
length = length + 5;
break;
}
}
//////////////////////FUNCTION DOES MATH/////////////////////////////////////////
float executefunction(int _function, float _firstnumber, float _secondnumber)
{
float _output;
switch (_function)
{
case 0:
_output = _firstnumber + _secondnumber;
break;
case 1:
_output = _firstnumber - _secondnumber;
break;
case 2:
_output = _firstnumber * _secondnumber;
break;
case 3:
if(_secondnumber != 0) {_output = _firstnumber / _secondnumber;}
break;
case 4:
_output = pow(_firstnumber, _secondnumber);
_output= round(_output);
break;
case 5:
_output = pow(_firstnumber, 2);
break;
case 6:
_output = sin(_firstnumber);
break;
case 7:
_output = cos(_firstnumber);
break;
case 8:
_output = tan(_firstnumber);
break;
case 9:
break;
}
return _output;
}
///////////////////Function Scrolls a Line////////////////////////////////
void scrollline(int delaytime, int line_number, int _length)
{
if(length > 8) // if the input is too long to fit on the screen, then scroll
{
if(millis() - lasttime > delaytime) // do this only every 750 milliseconds.
{
for(int i =0; i<8; i++)
{
lcd.setCursor(i,line_number);
if(scroll_index+i < _length) {lcd.print(totalarray.charAt(scroll_index+i));} else {lcd.print(" "); }
}
scroll_index = (scroll_index + 1)%(_length-6);
lasttime = millis();
}
}
else // if the input is shorter than 8 characters, no scroll is required
{
lcd.setCursor(0,line_number);
lcd.print(totalarray);
}
}
////////////////////////////////////////Function Scans Input Number into String Total Array/////////////////////////
void numintototalarray()
{
if(positive == 0) { totalarray = totalarray + '-'; }
for(int i = 1; i < 8; i++)
{
if(i > (8 - lcdcolumnindexrow1))
{
totalarray = totalarray + input[i];
}
}
}