Arduino calculator with LCD and keypad

Introduction

In this project, you will learn to design a calculator with the help of Arduino, 4×4 calculator keypad, and i2c LCD. I have already discussed how i2c LCD works with Arduino in one of my previous articles. In this project, you will learn more advanced Arduino coding and a lot of other things. So let’s have some fun.

First of all, let me tell you what my calculator will be capable of calculating. It can perform addition, subtraction, multiplication, and division. The user interface will first ask for the first number. After entering the first number you have to press “=”. Then it will ask for the second number and the process is the same. After entering both numbers you have to enter what operation you have to perform. Watch the video.

Keypad and LCD connection for Arduino calculator

The connection of the keypad and LCD to the Arduino for the calculator is shown in the figure below.

As you can see keypad has 16 buttons in 4 rows and 4 columns. Each row and columns have one terminal to connect to the Arduino. So we have 8 pins from the keypad which connects to Arduino digital pins 0-7. You can see that I also pulled all row lines down. You will know the reason in some time. On the i2c port, I have connected an i2c LCD for the user interface. I2C LCD will save some pins of Arduino. To understand the keypad function and how it controls 16 pins using just 8 pins, you have to see its internal connections.

Keypad structure and internal connection

To understand it you have to understand the connections of the button on the keypad. The circuit diagram of the button below represents the internal structure and connection of the keypad.

As you can see that connection is so simple. I connected one terminal to a row line and another terminal to a column line of every button according to its position.

How arduino detects the key press on keypad

Now consider a case where you pressed any button. Then you have to train the Arduino to detect which button you pressed with the help of rows and columns.

Let’s say you pulled every column line high. So if you press any button then the equivalent row line will be also high. But the problem here is that if you pressed any button in the same row then you will not be able to differentiate which button was it. So you can only detect the row of the buttons right now.

Now think about what will happen when you don’t pull all the column lines high at the same time but simultaneously one by one. I mean to say that you first pulled column 1 high then you checked all the 4 rows. If any row is high then you can find the row number and column number as well. So you can find the button. If any row is not high for column 1 then you can move on to the next column. This scanning of rows and columns will be so fast that it can detect any human button interaction.

Let’s have an example. Let’s say you pressed a button of column 4 and row C. So Arduino will first check for all rows for column 1. In this column, there is not any button press. So Arduino will move on to the next column up to column 4. As Arduino will reach column 4 then row C will be high. So Arduino will detect the button.

The row lines are inputs to the Arduino. If you don’t pull it low then it will float and pick any random state. This way you will get incorrect button detection. That’s why I pulled it low.

Arduino code for the calculator with LCD and keypad

Here is the Arduino code.

#include 
#include 

String Num1, Num2;              //to store the number inputs
char keymap[4][4] =             //Keypad keys lookup table
{ {'7', '8', '9', '/'},
  {'4', '5', '6', 'x'},
  {'1', '2', '3', '-'},
  {'C', '0', '=', '+'}
};

//Operations 
String operation[4] = {"div", "mul", "sub", "add"};

LiquidCrystal_I2C lcd(0x20, 16, 2); // set the LCD address to 0x27 for a 16 chars and 2 line display

void setup()
{
  //DDRD = data direction (inpu/output) register of port D (digital pins 0-7 are port D)
  DDRD = B00001111;                //digital pins 7,6,5 and 4 are set as input and 3,2,1 and 0 are set as output
  
  // initialize the lcd
  lcd.init();                      
  lcd.backlight();
  lcd.cursor();
  lcd.blink();
}

void loop()
{
  Num1 = "0";               //Resetting the numbers to zero
  Num2 = "0";
  
  lcd.setCursor(0, 0);
  //First number input
  lcd.print("First No.= ");   //Print the message on lcd

  while (readKey() != '=') {  //Reads the keypress till '=' is pressed
    char N1 = readKey();
    
    //Build the first number if input is not empty, C, +, -, * and /
    if (N1 != 0 && N1 != 'C' && N1 != '+' && N1 != '-' && N1 != 'x' && N1 != '/') {
      lcd.print(N1);
      Num1 += String(N1);     //add digits to string 
    }

    delay(300);
  }
  
  delay(500);
  
  lcd.setCursor(0, 1);

  //Second number input
  lcd.print("Second No.= ");

  while (readKey() != '=') {
    char N2 = readKey();

    if (N2 != 0 && N2 != 'C' && N2 != '+' && N2 != '-' && N2 != 'x' && N2 != '/') {
      lcd.print(N2);
      Num2 += String(N2);
    }

    delay(300);
  }

  lcd.clear();
  lcd.setCursor(0, 0);
  //Operation
  lcd.print("Operation= ");

//Scanning for the operation keys
  char O;
  while (1) {                 //Infinite loop
    O = readKey();            
    if (O == '+' || O == '-' || O == 'x' || O == '/')   //Read if pressed key is operational
      break;                 //Break the infinite loop
  }

  for (int i = 0; i < 4; i++) {
    if (O == keymap[i][3]) {             //Finding the operation in lookup table
      lcd.print(operation[i]);           //Print the name of operation on LCD using opration table
      lcd.setCursor(0, 1);
      lcd.print("Answer= ");
      if (O ==  '+')
        lcd.print(Num1.toInt() + Num2.toInt());     //Convert string number to integer number to perform mathematical operations
      if (O ==  '-')
        lcd.print(Num1.toInt() - Num2.toInt());
      if (O ==  'x')
        lcd.print(Num1.toInt() * Num2.toInt());
      if (O ==  '/')
        lcd.print(Num1.toFloat() / Num2.toFloat());
    }
  }

  while (readKey() != 'C');                     //This will hold the code flow here till C button is pressed

  lcd.clear();
}

char readKey() {

  for (int i = 0; i < 4; i++) {                
    PORTD = B1000 >> i;                       //Toggle digital pins 3 to 0 one by one to scan the columns
    for (int j = 0; j < 4; j++) {
      if (digitalRead(7 - j) == HIGH) {       //Check pins 4,5,6,7 for high input to scan rows.
        return keymap[j][i];                  //Find key value using numbers of row and column in lookup table and return that
      }
    }
  }
  return 0;
}

Code explanation

I will give you a summary of what the code is doing. I have included two libraries to control the i2c LCD. Then I have declared two string types of variables to store the input number. I have taken the string type of variables because in string data type it is easy to concatenate (add) new characters. So number digits will easily get concatenate that are coming character by character. This way you don’t have to implement any algorithm to build up input numbers. I defined a two-dimensional array to store the key values of the keypad. I will use it to take reference for the key when Arduino detects the keypress using row and column numbers. Then stored the operations name in a string array.

Then in the setup function, I have used the DDRD function to define input or output pins of port D. Here bits of the binary number given to the function represent the physical pins of port D of ATMEGA328. In Arduino, the port D of this chip are digital pins 0 to 7. Bit value 0 of binary number sets the equivalent pin as Input and 1 sets the equivalent pin as output. There are 8 bits in the binary number, so you set any pin of port D as input or output. Then I initialized the LCD.

In the loop section, I have first set the cursor to the home position of the first row. Then asked for the first number and started a while loop. This while loop will take input till you press the “=” key is. In the while loop, I implemented the if statement to check whether the keypress is not “C” or operational keys. So the code inside the if statement will add the character in the number string only when you press the number button. This way the number string will build up. As you press the “=” button while loop will break and LCD will go to the second row and ask for the second number. Also in the second number input, the process is the same.

Then I have scanned only for the operational keys using another while loop. This will loop won’t exit till you press any operation key. If you press any operation key then the code inside while loop will store that operation value in a variable. Then in for loop, an if statement will check the operation value in the lookup table of keymap exactly where that operation value is located. Using this reference it will print the operation name from the operation array on the LCD.

Then I implemented four if statements to check the operation and perform the operation accordingly after converting the number from string to integer. Then it will print the answer.

Now come to the readKey() function. I have Implemented the for loop which will iterate 4 times. Inside for loop, I used the PORTD function to make pins of port D high or low instead of the digitalWrite function. I used a four-bit number to toggle the pins, so it will affect only the lower four pins (i.e. 0, 1, 2, and 3). “>>” is the bitwise right shift operator which will shift that 1 in the number from 4th position to 1st position one by one after every loop iteration. This will Scan the columns of the keypad.

Inside this for loop, there is another for loop which will iterate 4 times for every outer loop iteration. The inner loop will check all 4 rows for every column and if any row is high at any time, this will check the value in the lookup table for that column and row number. After locating the value it will return that. If you don’t press any key then this function will return 0 (null character).

Leave a Comment

Your email address will not be published. Required fields are marked *