Introduction: Arduino Touchscreen Calculator

Hello! This is a project to make a touchscreen calculator using an Arduino Uno and a TFT LCD shield. I came up with the concept for my homeschool programming class, and the experience in building this project was very interesting. This calculator can do the four simple mathematical operations (addition, subtraction, multiplication, and division). It also displays up to two decimal points for the division answers that have them. Let's dive right in! The supplies for this project are listed below.

Supplies

- Arduino Uno

- 2.4 TFT LCD Shield (here's where I bought it: https://www.banggood.com/2_4-Inch-TFT-LCD-Shield-...

- USB A to B cable (cord to connect Arduino to computer)

- Computer with Arduino IDE installed

- You'll also need to download two libraries: MCUFRIEND_kbv and Touchscreen. The first one you can find on github (link: https://github.com/prenticedavid/MCUFRIEND_kbv) or you can use the library zip file I've included below. The second is in the Arduino Library manager for installation.

Step 1: Hardware Connections

The connection of the touchscreen shield to the Arduino Uno is simple and quick. All you have to do is line up the lowest pins on the shield with the lowest pins on the Arduino and push the shield into the pins. The top 5V pin and the unlabeled pin on the power side shouldn't have pins from the shield in them, with the same parameters applying for the pins labeled SCL and SDA on the other side of the board. Now, we're ready to code!

Step 2: The Code: Global Definitions & Setup

#include

MCUFRIEND_kbv tft; // hard-wired for UNO shields anyway.

#include

#define YP A3

#define XM A2

#define YM 9

#define XP 8

TouchScreen ts = TouchScreen(XP, YP, XM, YM, 300);

#define MINPRESSURE 10

This is the beginning of the code, where we include libraries (MCUFRIEND_kbv & Touchscreen), define the X and Y pins, set up the touchscreen parameters, and define minimum pressure needed for the Arduino to register a user press.

int ID;

int user_selection;

float saved_number = 0;

float term1;

int op_num;

float result;

int cursorLocX = 5;

int cursorLocY = 20;

Right before setup, we need to set up some global variables. ID helps with getting the touchscreen up and running. user_selection holds a number that corresponds to the key the user selects when pressing the touchscreen. saved_number is the variable that we print to the screen after a user entry(more on this in the loop). It is a float so it can hold decimal numbers as well as integers. term1 is the variable that the first number of the equation is saved in after an operand is selected. op_num saves the operand as a number (1 for addition, 2 for subtraction, 3 for multiplication, and 4 for division). result is the variable that is printed to the screen after the user has pressed the equals sign. It is also a float. cursorLocX and cursorLocY are the mapping points on the touchscreen where the cursor is set to multiple times (it is located in the gray bar at the top, otherwise known as the results field).

void setup(){

tft.reset();

ID = tft.readID();

tft.begin(ID);

tft.setRotation(0);

tft.fillScreen(TFT_DARKGREY);

squares();

numbers();

tft.setTextSize(3);

tft.setTextColor(TFT_BLUE, TFT_DARKGREY);

}

Our setup function first contains the initialization for the touchscreen shield (lines 1-3). The orientation of the shield is set using the tft.setRotation() command, with 0 being upright. The entire screen is colored dark grey with the tft.fillScreen() command, which we'll write over the top of (except for the results field). The squares() and numbers() functions draw the squares of the calculator, color the squares black and white in a checkerboard pattern, and write numbers/operands on the squares in blue. We'll get to those in the next step. The tft.setTextSize() command sets the text size of the results field to 3, which is a medium font. The tft.setTextColor() command sets the text color of the results field to blue, which is written over the dark grey field.

Step 3: The Code: Loop

void loop() {
numberSelect();

delay(100);

if (user_selection == 16){

;

}else{

if (user_selection < 10){

saved_number = saved_number * 10 + user_selection;

tft.setCursor(cursorLocX, cursorLocY);

tft.print(saved_number);

}else if (user_selection > 10){

switch (user_selection){

case 11:

op_num = 1;

tft.setCursor(cursorLocX, cursorLocY);

tft.print("+ ");

term1 = saved_number;

saved_number = 0;

break;

case 12:

op_num = 2;

tft.setCursor(cursorLocX, cursorLocY);

tft.print("- ");

term1 = saved_number;

saved_number = 0;

break;

case 13:

op_num = 3;

tft.setCursor(cursorLocX, cursorLocY);

tft.print("X ");

term1 = saved_number;

saved_number = 0;

break;

case 14:

op_num = 4;

tft.setCursor(cursorLocX, cursorLocY);

tft.print("/ ");

term1 = saved_number;

saved_number = 0;

break;

case 15:

saved_number = 0;

term1 = 0;

op_num = 0;

tft.setCursor(cursorLocX, cursorLocY);

tft.print(" ");

break;

}

tft.setCursor(cursorLocX, cursorLocY);


This is a lot to chew, so I'll explain what's above. We start by calling the numberSelect() function, which assigns a number to each square on the touchscreen. When a user presses one of those squares, the function sets the user_selection variable to the number of the square. The first if statement is to only run through the loop if a valid user selection has been made. If it is, the next if statement asks if user_selection has a number less than 10 saved into it (the numbers 0-9). If it does, saved_number is multiplied by 10 and the number in user_selection is added to saved_number, which is printed in the results field on the touchscreen. If it doesn't, the next if statement asks if user_selection has a number greater than 10 saved into it (the operand numbers: 11 for +, 12 for -, 13 for X, 14 for /, and 15 for the clear screen square). A switch function takes care of each case (determined by user_selection). The variable op_num is given a number that corresponds to the operand that was selected (1 for +, 2 for -, 3 for X, and 4 for /). The value in saved_number is saved into the variable term1 so that the saved_number variable can be used for the second half of the equation. The operand symbol is printed on the screen along with clearing any numbers in the results field. The only exception is the clear screen square, which resets all of the calculation variables and clears the results field of anything on it.

}else{

switch(op_num){

case 1:

result = term1 + saved_number;

tft.setCursor(cursorLocX, cursorLocY);

tft.print(double(result));

break;

case 2:

result = term1 - saved_number;

tft.setCursor(cursorLocX, cursorLocY);

tft.print(double(result));

break;

case 3:

result = term1 * saved_number;

tft.setCursor(cursorLocX, cursorLocY);

tft.print(double(result));

break;

case 4:

result = float(term1) / float(saved_number);

tft.setCursor(cursorLocX, cursorLocY);

tft.print(result);

break;

}

tft.setCursor(cursorLocX, cursorLocY);

saved_number = result;

term1 = 0;

op_num = 0;

delay(1000);

}

}

}

The last part of the loop deals with the event of the user selecting the equals sign (user_selection == 10). Another switch function works through the four mathematical functions (determined by op_num). The addition case (case 1) adds term1 and saved_number together and saves the number into the result variable. The result is printed to the results field as a double.The subtraction case (case 2) subtracts saved_number from term1 and saves the number into the result variable. The result is printed to the results field as a double. The multiplication case (case 3) multiplies term1 by saved_number and saves the number into the result variable. The result is printed to the results field as a double.The division case (case 4) divides term1 by saved_number together and saves the number into the result variable. The result is printed to the results field as a float (because the division answers can be decimal numbers). After the event of either a number, operand, or result being printed to the screen, the cursor is reset, saved_number is set to the previous result, and term1 & op_num are reset.

A few notes: the user cannot enter decimal numbers into the calculator due to the lack of a decimal point square. Also, the user can only do one equation at a time. You cannot calculate a result and then add/subtract/multiply/divide that result. In the numberSelect() function, there is a function that clears the screen after a result has been printed if a user has pressed another square.

Step 4: The Code: Squares Function

void squares (){

// black and white squares alternate on each row and the first and third rows have an opposite pattern than the second and fourth rows

tft.fillRect(0,60,60,65,TFT_BLACK); // first row of squares starts, black to white tft.fillRect(60,60,60,65,TFT_WHITE);

tft.fillRect(120,60,60,65,TFT_BLACK);

tft.fillRect(180,60,60,65,TFT_WHITE); // first row of squares ends

tft.fillRect(0,125,60,65,TFT_WHITE); // second row of squares starts, white to black tft.fillRect(60,125,60,65,TFT_BLACK);

tft.fillRect(120,125,60,65,TFT_WHITE);

tft.fillRect(180,125,60,65,TFT_BLACK); // second row of squares ends

tft.fillRect(0,190,60,65,TFT_BLACK); // third row of squares starts, black to white tft.fillRect(60,190,60,65,TFT_WHITE);

tft.fillRect(120,190,60,65,TFT_BLACK);

tft.fillRect(180,190,60,65,TFT_WHITE); // third row of squares ends

tft.fillRect(0,255,60,65,TFT_WHITE); // fourth row of squares starts, white to black tft.fillRect(60,255,60,65,TFT_BLACK);

tft.fillRect(120,255,60,65,TFT_WHITE);

tft.fillRect(180,255,60,65,TFT_BLACK); // fourth row of squares ends

}

The squares() function is pretty straightforward. The tft.fillRect(X1,Y1,X2,Y2,TFT_COLOR) command draws a rectangle according to the parameters passed to it, which are x and y's first positions, x and y's second positions, and the color that the rectangle is filled with. This function draws all four rows of squares (technically rectangles) and fills each squares with the color passed to it.

Step 5: The Code: Numbers Function

void numbers (){

tft.setTextColor(TFT_BLUE); // sets number/character color to blue

tft.setTextSize(5); // sets number/character size to 5

tft.setCursor(18, 75); // sets cursor for first line of numbers/characters

tft.print("7 8 9 /"); // prints first line of numbers/characters

tft.setCursor(18, 140); // sets cursor for second line of numbers/characters

tft.print("4 5 6 X"); // prints second line of numbers/characters

tft.setCursor(18, 205); // sets cursor for third line of numbers/characters

tft.print("1 2 3 -"); // prints third line of numbers/characters

tft.setCursor(18, 270); // sets cursor for fourth line of numbers/characters

tft.print("C 0 = +"); // prints fourth line of numbers/characters

}


The numbers() function is also straightforward. The first two lines set the text size bigger and the color to blue. The tft.setCursor() command sets the cursor to the position on each row where the writing of the numbers start from. Then the tft.print() command prints the numbers/characters over the squares.

Step 6: The Code: NumberSelect Function

void numberSelect(){

TSPoint p = ts.getPoint();

pinMode(XM, OUTPUT);

pinMode(YP, OUTPUT);

if (p.z > MINPRESSURE){

p.x = map(p.x, 250, 845, 0, 239);

p.y = map(p.y, 245, 860, 0, 319);

if (result != 0){

result = 0;

saved_number = 0;

tft.print("CLEAR VALUES ");

delay(500);

tft.setCursor(cursorLocX, cursorLocY);

tft.print(" ");

tft.setCursor(cursorLocX, cursorLocY);

}

To start off the numberSelect() function, we ask for user input from the touchscreen with the ts.getPoint() command. Once that data has been collected, we check to see if the minimum pressure has been exceeded (or, in other words, if the user has pressed somewhere on the touchscreen). If it is, the x and y coordinates are mapped from Cartesian coordinates to touchscreen-specific coordinates. (0,0) is the top left hand corner of the touchscreen, with the x axis going across and the y axis going down. The next part checks to see if there is a number saved in result. If there is, result and saved_number are reset to 0. The message "CLEAR VALUES " is printed over the results field, and the screen is cleared with the cursor back to its starting position.

if (p.y < 125 && p.y > 60){ // first row of squares

if (p.x < 60)

user_selection = 7;

else if (p.x < 120)

user_selection = 8;

else if (p.x < 180)

user_selection = 9;

else user_selection = 14;

}else if (p.y < 190 && p.y > 125){ // second row of squares

if (p.x < 60)

user_selection = 4;

else if (p.x < 120)

user_selection = 5;

else if (p.x < 180)

user_selection = 6;

else user_selection = 13;

}else if (p.y < 255 && p.y > 190){ // third row of squares

if (p.x < 60)

user_selection = 1;

else if (p.x < 120)

user_selection = 2;

else if (p.x < 180)

user_selection = 3;

else user_selection = 12;

}else if (p.y > 255){ // fourth row of squares

if (p.x < 60)

user_selection = 15;

else if (p.x < 120)

user_selection = 0;

else if (p.x < 180)

user_selection = 10;

else user_selection = 11;

}

}else{

user_selection = 16; // user_selection is set to 16 (nothing variable)

}

}

This is the part that determines which button has been selected. Starting with the top row of squares and ending with the bottom row, the Arduino looks for where the screen was actually pressed at. It then assigns the square a number and saves that number into user_selection. The numbers 0-9 correspond to the number squares, the numbers 11-15 correspond to the operand squares and the clear square, and the number 10 corresponds to the equals sign square. If no square has been selected, then user_selection is set to 16, which will make the loop start over again (see loop function).

Step 7: Enjoy Your Finished Project!

There you have it! You now have a touchscreen calculator that can do addition, subtraction, multiplication, and division. This project changed the whole way I thought a calculator worked. As I was working on this project, I remember telling my instructor at class, "I'll never look at a calculator the same way again!" The functions you as a user think are easy are somewhat difficult when you're behind the computer trying to emulate your idea. I hope you enjoyed the project, and I hope your mindset of how a calculator works has been changed as well!

Here's the entire code for your convenience. It is filled with comments so if you have any problems they should show you what each line does.