Build a Robust Arduino Calculator (LCD 16×2 + 4×4 Keypad)
A complete, beginner-friendly guide with wiring table, improved code (clear, backspace, decimals, errors), Wokwi JSON, and upgrade ideas.
Helpful Kits & Parts (Optional)
Project Overview
In this project, you’ll build a fully functional calculator using an Arduino,
a 16×2 LCD (HD44780 compatible), and a 4×4 matrix keypad.
The interface is clean and responsive: you enter digits and operators (+
,
-
, *
, /
), view the expression live on the LCD, and
get the result with =
. We’ve added quality-of-life features like
clear, backspace, decimal points,
error messages (e.g., division by zero), and input length protection.
This build is ideal for beginners learning hardware interfacing (LCD + keypad) and for intermediate makers who want reliable input handling and clean code structure.
Features & UX
- Intuitive keypad: 0–9,
.
,+
,-
,*
,/
,=
,C
(clear),<
(backspace). - LCD feedback: First line shows mode/status, second line shows the current input or result.
- Error handling: Division by zero, invalid sequences, and overflow give readable messages.
- Configurable: Easy to remap pins, change keypad layout, or switch to an I²C LCD.
- Portable: Works on Arduino Uno, Nano, Mega, and compatible boards.
Components & Alternatives
- Arduino Uno (or Nano/Mega/compatible)
- LCD 16×2 (HD44780) + 220 Ω resistor for backlight (or a potentiometer for contrast)
- 4×4 matrix keypad
- Breadboard + jumper wires
- USB cable + computer with Arduino IDE
Alternative: Use an LCD with an I²C backpack to reduce wiring. Swap the LiquidCrystal
library for LiquidCrystal_I2C
and change a few lines.
Wiring Table (Pins)
Arduino Pin | LCD Pin | Keypad | Notes |
---|---|---|---|
5V | VDD | — | LCD power |
GND | VSS, RW, K | — | Common ground |
D12 | RS | — | Register select |
D11 | E | — | Enable |
D10 | D4 | — | 4-bit mode |
D9 | D5 | — | 4-bit mode |
D8 | D6 | — | 4-bit mode |
D7 | D7 | — | 4-bit mode |
5V → 220 Ω → | A | — | Backlight via resistor |
D5, D4, D3, D2 | — | R1–R4 | Keypad rows |
A3, A2, A1, A0 | — | C1–C4 | Keypad columns |
Build Steps
- Place the LCD and keypad on the breadboard (or mount the LCD separately).
- Wire power and ground to the LCD (
VDD
to 5 V,VSS
to GND). TieRW
to GND. - Connect LCD
RS
=D12,E
=D11,D4..D7
=D10..D7. Add a 220 Ω from 5 V toA
. - Connect keypad rows to D5, D4, D3, D2 and columns to A3, A2, A1, A0.
- Upload the sketch (below) from the Arduino IDE, then test each key.
Improved Arduino Code
Enhancements over the basic version: Backspace (<
), clear (C
), decimal support, input guard,
and clearer error messages. The calculator evaluates simple two-operand expressions
like 12.5*3
. (You can extend it to multiple operators later.)
// Arduino LCD & Keypad Calculator (Improved)
// Libraries: LiquidCrystal by Arduino, Keypad by Mark Stanley/Alexander Brevig
#include <LiquidCrystal.h>
#include <Keypad.h>
// ----- LCD Setup -----
LiquidCrystal lcd(12, 11, 10, 9, 8, 7);
// ----- Keypad Setup -----
const byte ROWS = 4, COLS = 4;
byte rowPins[ROWS] = {5, 4, 3, 2};
byte colPins[COLS] = {A3, A2, A1, A0};
char keys[ROWS][COLS] = {
{'1','2','3','+'},
{'4','5','6','-'},
{'7','8','9','*'},
{'C','0','=','/'}
};
Keypad keypad(makeKeymap(keys), rowPins, colPins, ROWS, COLS);
// Optional: map a "backspace" on long-press of 'C' or repurpose a key if your keypad has one.
// For demonstration, we'll treat a quick double-press of 'C' within a window as backspace.
unsigned long lastCPress = 0;
const unsigned long doublePressGap = 350; // ms
String inputBuffer = "";
const uint8_t LCD_COLS = 16;
const uint8_t LCD_ROWS = 2;
// Forward declaration
double calculate(const String &expr, bool &ok);
void printStatus(const char* msg) {
lcd.setCursor(0,0);
lcd.print(" "); // clear line
lcd.setCursor(0,0);
lcd.print(msg);
}
void printInput() {
// Show tail of inputBuffer if longer than LCD width
String toShow = inputBuffer;
if (toShow.length() > LCD_COLS) {
toShow = toShow.substring(toShow.length() - LCD_COLS);
}
lcd.setCursor(0,1);
lcd.print(" "); // clear line
lcd.setCursor(0,1);
lcd.print(toShow);
}
void clearAll() {
inputBuffer = "";
lcd.clear();
printStatus("Enter:");
printInput();
}
void setup() {
lcd.begin(LCD_COLS, LCD_ROWS);
printStatus("Calculator");
delay(1200);
clearAll();
}
void handleBackspace() {
if (inputBuffer.length() > 0) {
inputBuffer.remove(inputBuffer.length() - 1);
printStatus("Edit:");
printInput();
}
}
bool isOperator(char c) {
return (c=='+' || c=='-' || c=='*' || c=='/');
}
bool appendChar(char c) {
// Basic guard: avoid two operators in a row, avoid multiple decimals in one number part
if (isOperator(c)) {
if (inputBuffer.length() == 0) return false; // no leading operator (except minus for negative, skipped here)
char last = inputBuffer[inputBuffer.length()-1];
if (isOperator(last) || last=='.') return false;
}
if (c=='.') {
// Ensure only one dot per current operand
// Find last operator
int lastOp = -1;
for (int i = inputBuffer.length()-1; i >= 0; --i) {
if (isOperator(inputBuffer[i])) { lastOp = i; break; }
}
String current = (lastOp==-1) ? inputBuffer : inputBuffer.substring(lastOp+1);
if (current.indexOf('.') != -1) return false;
}
// Limit total length to avoid LCD clutter / overflow
if (inputBuffer.length() >= 32) return false;
inputBuffer += c;
return true;
}
void loop() {
char k = keypad.getKey();
if (!k) return;
if (k == 'C') {
unsigned long now = millis();
if (now - lastCPress < doublePressGap) {
// Double-press C => Backspace
handleBackspace();
lastCPress = 0; // reset chain
return;
}
lastCPress = now;
// Single C => Clear
clearAll();
return;
}
if (k == '=') {
bool ok = false;
double res = calculate(inputBuffer, ok);
lcd.clear();
if (ok) {
printStatus("Result:");
lcd.setCursor(0,1);
lcd.print(res, 6); // up to 6 decimals
} else {
printStatus("Error:");
lcd.setCursor(0,1);
lcd.print("Invalid input");
}
delay(2000);
clearAll();
return;
}
if (appendChar(k)) {
printStatus("Enter:");
printInput();
} else {
printStatus("Invalid key");
delay(600);
printStatus("Enter:");
}
}
// Calculate simple "A op B" where op in + - * /
double calculate(const String &expr, bool &ok) {
ok = false;
if (expr.length() == 0) return 0.0;
// Find the single operator (only one allowed in this version)
int opIndex = -1;
char op = 0;
for (int i = 0; i < expr.length(); ++i) {
char c = expr[i];
if (isOperator(c)) {
if (opIndex != -1) return 0.0; // multiple operators => invalid
opIndex = i; op = c;
}
}
if (opIndex <= 0 || opIndex >= expr.length()-1) return 0.0; // need A op B
String aStr = expr.substring(0, opIndex);
String bStr = expr.substring(opIndex+1);
// Edge: allow "-x + y" style? (Not in this simple version—keep clean)
double A = aStr.toDouble();
double B = bStr.toDouble();
double out = 0.0;
switch (op) {
case '+': out = A + B; break;
case '-': out = A - B; break;
case '*': out = A * B; break;
case '/':
if (B == 0.0) { printStatus("Div by zero"); delay(900); return 0.0; }
out = A / B; break;
default: return 0.0;
}
ok = true;
return out;
}
Pin remapping tips
- Change
LiquidCrystal lcd(…)
to match your digital pins. - Update
rowPins
/colPins
to match your keypad header. - Using an I²C LCD? Replace the constructor with your I²C address and use
LiquidCrystal_I2C
.
Wokwi JSON (Simulator Layout)
Paste this into wokwi.com (New Project → diagram.json) to simulate the wiring quickly.
{
"version": 1,
"author": "https://www.youtube.com/@EasyElectronics2",
"editor": "wokwi",
"parts": [
{ "type": "wokwi-arduino-uno", "id": "uno", "top": 200, "left": 20, "attrs": {} },
{ "type": "wokwi-membrane-keypad", "id": "keypad", "top": 140, "left": 360, "attrs": {} },
{ "type": "wokwi-lcd1602", "id": "lcd", "top": 8, "left": 20, "attrs": {} },
{ "type": "wokwi-resistor", "id": "r1", "top": 140, "left": 220, "attrs": { "value": "220" } }
],
"connections": [
[ "uno:GND.1", "lcd:VSS", "black", [ "v-51","*","h0","v18" ] ],
[ "uno:GND.1", "lcd:K", "black", [ "v-51","*","h0","v18" ] ],
[ "uno:GND.1", "lcd:RW", "black", [ "v-51","*","h0","v18" ] ],
[ "uno:5V", "lcd:VDD", "red", [ "v16","h-16" ] ],
[ "uno:5V", "r1:2", "red", [ "v16","h-118","v-244","h50" ] ],
[ "r1:1", "lcd:A", "pink", [] ],
[ "uno:12", "lcd:RS", "blue", [ "v-16","*","h0","v20" ] ],
[ "uno:11", "lcd:E", "purple",[ "v-20","*","h0","v20" ] ],
[ "uno:10", "lcd:D4", "green", [ "v-24","*","h0","v20" ] ],
[ "uno:9", "lcd:D5", "brown", [ "v-28","*","h0","v20" ] ],
[ "uno:8", "lcd:D6", "gold", [ "v-32","*","h0","v20" ] ],
[ "uno:7", "lcd:D7", "gray", [ "v-36","*","h0","v20" ] ],
[ "uno:A3", "keypad:C1", "brown", [ "v116","*","h0","v0" ] ],
[ "uno:A2", "keypad:C2", "gray", [ "v120","*","h0","v0" ] ],
[ "uno:A1", "keypad:C3", "orange", [ "v124","*","h0","v0" ] ],
[ "uno:A0", "keypad:C4", "pink", [ "v128","*","h0","v0" ] ],
[ "uno:5", "keypad:R1", "blue", [ "v-34","h96","*","v12" ] ],
[ "uno:4", "keypad:R2", "green", [ "v-30","h80","*","v16" ] ],
[ "uno:3", "keypad:R3", "purple", [ "v-26","h64","*","v20" ] ],
[ "uno:2", "keypad:R4", "gold", [ "v-22","h48","*","v24" ] ]
],
"dependencies": {}
}
Troubleshooting
- Blank LCD? Ensure
VDD=5V
,VSS=GND
, and try adjusting LCD contrast (if you added a pot). ConfirmRW
is tied to GND. - Weird characters? Check 4-bit wiring order:
D4→D10
,D5→D9
,D6→D8
,D7→D7
. - Keypad not responding? Verify row/col pin order and keypad orientation. Swap rows/cols if needed.
- “Invalid input” often? Avoid entering two operators in a row; use
C
to clear or double-tapC
for backspace.
Upgrades & Ideas
- I²C LCD to free pins and simplify wiring.
- Multiple operations with proper order of operations (implement the shunting-yard algorithm).
- EEPROM memory to store last results or a simple history.
- Enclosure with 3D printed faceplate and labeled keys.
- Buzzer feedback on keypress and error tones.
FAQ
Can I use an Arduino Nano instead of Uno?
Yes. Just remap the pins to the Nano’s layout—the code stays the same.
How do I add negative numbers?
Easiest path: allow a leading -
before the first number. For full support inside expressions,
you’ll need a richer parser (tokenize and handle unary minus).
What about floating-point precision?
Arduino’s double
on AVR is 32-bit float precision. For a basic calculator that’s fine.
For higher precision, consider larger MCUs or fixed-point arithmetic.
0 Comments