Calculator

Hi Friends,

I am making a Calculator with a Arduino Nano ,16x2 LCD Display ,4x4 Matrix Keypad.
It has the following features:

  • It accepts two numbers and performs + or - or * or /
  • It has a Reset button, a Enter button and a Clear button[Reset and Clear are connected to A0 and A1 due to lack of Digital Pins]
  • It powers an LCD Display which displays your actions with the Calculator

I'll take care of the Reset button and Clear button, I need help for the rest of the project and code.

I created a code (I'll ask for any doubts in the loop later)

/*
   The circuit:
 * LCD RS pin to digital pin 12
 * LCD Enable pin to digital pin 11
 * LCD D4 pin to digital pin 5
 * LCD D5 pin to digital pin 4
 * LCD D6 pin to digital pin 3
 * LCD D7 pin to digital pin 2
 * LCD R/W pin to ground
 * LCD VSS pin to ground
 * LCD VCC pin to 5V
 * 10K resistor:
 * ends to +5V and ground
 * wiper to LCD VO pin (pin 3)
 */
#include <Keypad.h>
#include <LiquidCrystal.h>

LiquidCrystal lcd(12,11,5,4,3,0);

const byte ROWS = 4;
const byte COLS = 4;
char keys[ROWS][COLS]={
  {'1','2','3','+'},
  {'4','5','6','-'},
  {'7','8','9','*'},
  {'.','0','E','/'}
};
byte rowPins[ROWS]={13,10,9,8};
byte colPins[COLS]={7,6,2,1};

double ans;

Keypad matrix = Keypad( makeKeymap(keys), rowPins, colPins, ROWS, COLS);

void setup()
{
  lcd.begin(16,2);
  lcd.clear();
}

void inputnum1()
{
  lcd.print("First Number:");
  lcd.setCursor(0,1);

  char num1 = matrix.getKey();
  if (num1)
  {
    lcd.print(num1);
  }

  if (num1=E)
  {
    lcd.clear();
    inputnum1;
  }
}

void inputnum2()
{
  lcd.print("Second Number:");
  lcd.setCursor(0,1);

  char num2 = matrix.getKey();
  if (num2)
  {
    lcd.print(num2);
  }

  if (num2=E)
  {
    lcd.clear();
    inputnum2;
  }
}

void inputop()
{
  lcd.print("Select Operation:");
  lcd.setCursor(0,1);

  char op = matrix.getKey();
  
  if (op==(+))
  {
    lcd.print("Addition");
  }

  if (op==(-))
  {
    lcd.print("Substraction");
  }

  if (op==(*))
  {
    lcd.print("Multiplication");
  }

  if (op==(/))
  {
    lcd.print("Division");
  }
}

void loop() 
{
  inputnum1;
                  //....Contd
}

But it shows a list of errors

Arduino: 1.6.6 (Windows 7), Board: "Arduino Nano, ATmega328"

C:\Users\DEVANAND\Documents\Arduino\Calculator\Calculator.ino: In function 'void inputnum1()':

Calculator:53: error: 'E' was not declared in this scope

if (num1=E)

^

C:\Users\DEVANAND\Documents\Arduino\Calculator\Calculator.ino: In function 'void inputnum2()':

Calculator:71: error: 'E' was not declared in this scope

if (num2=E)

^

C:\Users\DEVANAND\Documents\Arduino\Calculator\Calculator.ino: In function 'void inputop()':

Calculator:85: error: expected primary-expression before ')' token

if (op==(+))

^

Calculator:90: error: expected primary-expression before ')' token

if (op==(-))

^

Calculator:95: error: expected primary-expression before ')' token

if (op==(*))

^

Calculator:100: error: expected primary-expression before '/' token

if (op==(/))

^

Calculator:100: error: expected primary-expression before ')' token

if (op==(/))

^

exit status 1
'E' was not declared in this scope

This report would have more information with
"Show verbose output during compilation"
enabled in File > Preferences.

What do I do for this ?

Please help me out by posting a proper code....

'E' was not declared in this scope

Where is it declared ?

  if (num2=E)

Whay are you setting num2 to the value of E ?

  if (op==(+))

What sort of variable is op ? Will it ever equal + ? Might it equal '+' ?

void loop()
{
  inputnum1;

That's not how you call a function.

You need to use single quotes when comparing to the values in the keys array, like in the declaration. Your code should look like this:

if (op=='+')

Watch out for single versus double equal signs. The former is an assignment, the latter a comparison.

if (num1=E)  // assignment, not comparison and E should be 'E'
if (num1=='E')       // correct comparison

You need parenthesis when calling a function.

inputnum1();

I'm not sure if you really want to call inputnum1 from inside inputnum1 and inputnum2 from inside inputnum2, but you'll have to get it to compile before working on your logic.

This section of code

void inputop()
{
  lcd.print("Select Operation:");
  lcd.setCursor(0,1);

  char op = matrix.getKey();
  
  if (op==(+))
  {
    lcd.print("Addition");
  }

  if (op==(-))
  {
    lcd.print("Substraction");
  }

  if (op==(*))
  {
    lcd.print("Multiplication");
  }

  if (op==(/))
  {
    lcd.print("Division");
  }
}

could be improved by the others comments (to make it work), plus make it more efficient. For example, suppose they enter the add operator. Your code performs 3 more if tests each time, even if the operator is a plus (+) sign. That is, if it is a plus sign, you still do three "impossible" if checks on the operator. A cascading if statement would get rid of that inefficiency, but a better option remains.

A better choice would be a switch statement block:

void inputop()
{
  lcd.print("Select Operation:");
  lcd.setCursor(0,1);

  char op = matrix.getKey();

  switch (op) {   // One test at this point...
    case '+':
      lcd.print("Addition");
      break;

    case '-':
      lcd.print("Substraction");
      break;
      
    case '*':
      lcd.print("Multiplication");
      break;
      
    case '/':
      lcd.print("Division");
      break;
      
    default:
      lcd.print("Unknown operator. Re-enter.");
      break;
  }
}

With this statement block, the test is performed once at the top and, based on the outcome of the test, the code uses a jump table to execute the proper code in the case statement block. The redundant if tests are removed.

A cascading if statement would get rid of that inefficiency, but a better option remains.

I believe the cascaded "if"s use less precious RAM, but I'd have to verify.

Now You see I need to reset the Arduino with an external push button (not the on board one.).

How can I do this?

** I observed that RST to GND resets the Arduino

if (num1=E)

Before you get too bogged down in the Arduino side of things, may I respectfully suggest you learn a bit of C programming? This is an elementary error.

As is this:

  if (op==(+))

I need help for the rest of the project and code.

Work your way through some C tutorials. Before you make a calculator, make something simple.

@AWOL: What did you find out? My test suggests the switch uses 2 bytes less. I think a switch is easier to read and I might favor it over a cascading if block even if I had to pay a small memory penalty.

In my very simple experiment with just ten cases / ifs, they both used exactly the same amount of RAM, but the "if" took twenty more bytes (ten instructions) of program memory.
Fall-though "if" (i.e., no "else") was the worst.

Maybe it is an optimisation effect; I've worked with compilers which would vary the strategy for implementing switch/case, depending on the number of cases or the sparsity, from simple if/else, to vector table to (I suspect) hash.

// Simple, addle-brained "toLower" to test cascaded "if" vs. switch 
//#define USE_CASE
#define CASCADE
//#define DUMB
void setup() 
{
  Serial.begin (115200);
}

void loop() 
{
  if (Serial.available ())
  {
    int inChar = Serial.read ();
#ifdef USE_CASE
    switch (inChar)
    {
      case 'A':  Serial.print ('a'); break;
      case 'B':  Serial.print ('b'); break;
      case 'C':  Serial.print ('c'); break;
      case 'D':  Serial.print ('d'); break;
      case 'E':  Serial.print ('e'); break;
      case 'F':  Serial.print ('f'); break;
      case 'G':  Serial.print ('g'); break;
      case 'H':  Serial.print ('h'); break;
      case 'I':  Serial.print ('i'); break;
      case 'J':  Serial.print ('j'); break;
      default:   Serial.print ('*'); break;
    }
#endif    
#ifdef CASCADE
    if (inChar == 'A')
      Serial.print ('a');
    else  if (inChar == 'B')
      Serial.print ('b');
    else  if (inChar == 'C')
      Serial.print ('c');
    else  if (inChar == 'D')
      Serial.print ('d');
    else  if (inChar == 'E')
      Serial.print ('e');
    else  if (inChar == 'F')
      Serial.print ('f');
    else  if (inChar == 'G')
      Serial.print ('g');
    else  if (inChar == 'H')
      Serial.print ('h');
    else  if (inChar == 'I')
      Serial.print ('i');
    else  if (inChar == 'J')
      Serial.print ('j');
    else
      Serial.print ('*');  
#endif    
#ifdef DUMB
    if (inChar == 'A')
      Serial.print ('a');
    if (inChar == 'B')
      Serial.print ('b');
    if (inChar == 'C')
      Serial.print ('c');
    if (inChar == 'D')
      Serial.print ('d');
    if (inChar == 'E')
      Serial.print ('e');
    if (inChar == 'F')
      Serial.print ('f');
    if (inChar == 'G')
      Serial.print ('g');
    if (inChar == 'H')
      Serial.print ('h');
    if (inChar == 'I')
      Serial.print ('i');
    if (inChar == 'J')
      Serial.print ('j');
    // No default possible  
#endif
  }
}

Judging by the generated code the compiler has made a simple indexed table jump. You may get different results if the cases are not consecutive.

I changed the cases to the first prime numbers and the cascade came out better, but the same RAM.

We aren't really expecting RAM to be consumed are we?

I spent a few months without programming which made me forget a part of the commands used.

Anyway, I have completed my code and the computer says it is okay to upload.
[24% program space and 15% dynamic memory]

Please notify me of any mistakes/problems I might face during run-time or any better options for my code.

Can anyone post the same code with Serial Output instead if LCD.?

/*
   The circuit:
 * LCD RS pin to digital pin 12
 * LCD Enable pin to digital pin 11
 * LCD D4 pin to digital pin 5
 * LCD D5 pin to digital pin 4
 * LCD D6 pin to digital pin 3
 * LCD D7 pin to digital pin 0
 * LCD R/W pin to ground
 * LCD VSS pin to ground
 * LCD VCC pin to 5V
 * 10K resistor:
 * ends to +5V and ground
 * wiper to LCD VO pin (pin 3)
 * 4x4 Matrix Keypad with pins to 13,10,9,8,7,6,2,1
 * Push Buttons to A6 and A7
 */
#include <Keypad.h>
#include <LiquidCrystal.h>

LiquidCrystal lcd(12,11,5,4,3,0);

const byte ROWS = 4;
const byte COLS = 4;
char keys[ROWS][COLS] =
{
  {'1','2','3','+'},
  {'4','5','6','-'},
  {'7','8','9','*'},
  {'.','0','^','/'}
};
byte rowPins[ROWS] = {13,10,9,8};
byte colPins[COLS] = {7,6,2,1};

double num1;
double num2;
char op;
double ans;
byte c;
const byte a = 1;

Keypad keypad = Keypad( makeKeymap(keys), rowPins, colPins, ROWS, COLS);

void setup()
{
  lcd.begin(16,2);
  lcd.clear();
}

void inputnum1()
{
  c = 1;
    
  lcd.print("First Number:");
  lcd.setCursor(0,1);

  num1 = keypad.getKeys();
  if (num1)
  {
    lcd.print(num1);
  }

  if (analogRead(A7) >= 512)
  {
    lcd.clear();
    inputnum1();
  }
}

void inputnum2()
{
  lcd.print("Second Number:");
  lcd.setCursor(0,1);

  num2 = keypad.getKeys();
  if (num2)
  {
    lcd.print(num2);
  }

  if (analogRead(A7) >= 512)
  {
    lcd.clear();
    inputnum2();
  }
}

void inputop()
{
  lcd.print("Select Operation:");
  lcd.setCursor(0,1);

  op = keypad.getKey();
  
  if (op=='+')
  {
    lcd.print("Addition");
    ans = num1 + num2;
  }

  if (op=='-')
  {
    lcd.print("Substraction");
    ans = num1 - num2;
  }

  if (op=='*')
  {
    lcd.print("Multiplication");
    ans = num1 * num2;
  }

  if (op=='/')
  {
    lcd.print("Division");
    ans = num1 / num2;
  }

  if (op=='^')
  {
    lcd.print("Power");
    ans = pow(num1,num2);
  }

  if (analogRead(A7) >= 512)
  {
    lcd.clear();
    inputop();
  }
  
}


void loop() 
{
  if ((c < 4)&&(analogRead(A6) >= 512))
  {
    c = c + 1;
  }

  if ((c == 4)&&(analogRead(A6) >= 512))
  {
    c = c - 3;
  }

  while (c == 1)
  {
    inputnum1();
  }

  while (c == 2)
  {
    inputnum2();
  }

  while (c == 3)
  {
    inputop(); 
  }

  while (c == 4)
  {
    lcd.print("Result:");
    lcd.setCursor(0,1);
    lcd.print(ans);
  }

}

Can anyone post the same code with Serial Output instead if LCD.?

Wherever it says "lcd.", substitute "Serial.", unless it is a cursor, begin, init or clear command, in which case, delete it.
Don't forget to set the serial line speed.

@AWOL:

Don't forget to set the serial line speed.

Great point! Over the years there have been many times where the Serial.begin(9600); statement is different for the OP and the responders, causing a lot of head-scratching miscommunication. Although I rarely use 9600 when I'm coding, I do post with that rate simply because it's the default for the Serial object and likely the one the OP is using. Unless the code requires something different, using that rate in postings/answers might at least remove one potential problem area.

void inputop()
{
...
  if (analogRead(A7) >= 512)
  {
    lcd.clear();
    inputop();
  }

Is this really wise? If you read more than 512 from A7 your function recurses.

void loop()
{
...
  while (c == 1)
  {
    inputnum1();
  }

I think this is going to get out of control really quickly. You need to wait for the number to be input.

Sir why is it like that?