Kids love calculators so i made an Arduino based calculator with a generic 4×4 keypad that uses 8 digital pins. It comes with a sticker, so it was easy to attach it to a plywood laser cut box. The only pity is that the buttons are labelled A,B,C,D; we need to use small stickers to cover them with the math operators. It works as a normal calculator, but also has a hidden function of a maths quiz inside, which gives you 60 seconds to answers as many sums as you can, keeping a high score. I used a cheap 16×2 LCD screen that uses the standard LiquidCrystal library. It looks better than the above picture suggests.

The code is not very complicated, but not trivial either. I started from this example that works with integers, changed the display, and added a decimal point function to it, and a couple of other features to limit the numbers to the display size (16 digits per line), and prevent overflow. I am using float type numbers, so their precision is usually limited to 6-7 digits.

At startup, it functions as a normal calculator, until you enter the magic number ‘321’, then the quiz starts. The quiz asks simple math questions, and counts how many you get right in 60 seconds. The questions become increasingly difficult as your score goes up. It alternates between +, -, *, /

Below a video of the calculator function, and the quiz.

I started with a breadboard prototype as below.  As this was working well and the kids loved it, i designed a simple PCB for the Atmega328P, with a header for the 16-pin LCD screen. The buzzer sounds at startup, at the beginning and end of the quiz, and at each key press: normal sound for valid key, low beep for a wrong key. At first i hoped to power the project with 2x 1.5V batteries, but the LCD screen needs at least 4V so i ended up with 3x AAA batteries. Schematics of the project: The code is below:

[wordpress does not show the includes: EEPROM.h   Keypad.h   LiquidCrystal.h]

``````/*
Arduino calculator with keypad and LCD, and quiz
Tom Tobback Oct 2017

|| @version 1.0
|| @author Andrew Mascolo
|| @date May 15, 2013
|| @description
|| Simple use of keypad and LCD

use LCD 16x2 with pot
buzzer on D13
use * as decimal point

starts as normal calculator
enter magic number to start quiz: 60 seconds
enter reset code to reset high score
*/

#include        // for high score
#include
#include

// initialize the library with the numbers of the interface pins
LiquidCrystal lcd(12, 11, A5, A4, A3, A2);

float first = 0;
float second = 0;
float total = 0;
boolean mult = false;       // multiplication flag to avoid overflow
boolean first_decimal = false;  // does the first number have a decimal point
int first_decimal_pos = 0; // how many digits after decimal point
boolean second_decimal = false;  // does the second number have a decimal point
int second_decimal_pos = 0; // how many digits after decimal point
int digits_first = 0;
int digits_second = 0;
const long magic_number = 321;     // to enter the quiz state
const long magic_reset = 332211;   // to reset high score
boolean quiz = false;             // quiz state
int score = 0;
unsigned long timestamp = 0;      // start of quiz
int high_score;
char customKey;
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] = {2, 3, 4, 5}; //connect to the row pinouts of the keypad
byte colPins[COLS] = {6, 7, 8, 9}; //connect to the column pinouts of the keypad

//initialize an instance of class NewKeypad

void setup()
{
//  EEPROM.write(10, 0);            // OPTIONAL reset high score to 0
if (high_score == 255) {
EEPROM.write(10, 0);            // reset high score to 0
high_score = 0;
}

Serial.begin(9600);
lcd.begin(16, 2);
lcd.setCursor(2, 0);
lcd.print("BuffaloLabs");
lcd.setCursor(0, 1);
lcd.print("Calculator+Quiz");
tone(13, 500);
delay(100);
tone(13, 1000);
delay(100);
noTone(13);
delay(2000);
lcd.clear();
}

void loop()
{

if (quiz) {                                 // quiz waiting for answer
lcd.setCursor(14, 1);
int remaining = 60 - (millis() - timestamp) / 1000;
if (remaining < 10) lcd.print(" "); lcd.print(remaining); if (remaining == 0) { // quiz FINISHED if (score > high_score) {                       // new high score
high_score = score;
EEPROM.write(10, high_score);
lcd.clear();
lcd.print("NEW HIGH SCORE= ");
lcd.setCursor(0, 1);
lcd.print(high_score);
for (int i = 1000; i < 4000; i += 5) { // siren tone(13, i); delay(1); } for (int i = 4000; i > 1000; i -= 5) {
tone(13, i);
delay(1);
}
noTone(13);
}
else {                                          // no new high score
for (int i = 0; i < 10; i++) {
tone(13, 2000);
delay(50);
noTone(13);
delay(50);
}
}
beep();
score = 0;
showHighscore();
timestamp = millis();
}

switch (customKey)
{
case '0' ... '9':
if (digits_answer < 4) { answer = answer * 10 + (customKey - '0'); // if integer (from ascii to numerical value) lcd.setCursor(digits_answer, 1); lcd.print(customKey); digits_answer++; beep(); Serial.println(answer, 6); } else { lowBeep(); } break; case '=': if (digits_answer > 0) {
beepYes();
score++;
}
else {
beepNo();
score--;
if (score < 0) score = 0;
}
}
else {
lowBeep();
}
break;
case '+':
lowBeep();
break;
case '-':
lowBeep();
break;
case '*':
lowBeep();
break;
case '/':
lowBeep();
break;
case '.':
lowBeep();
break;
}
}

else {                                      // normal calculator
switch (customKey)
{
case '0' ... '9': // This keeps collecting the first value until a operator is pressed "+-*/"
if (digits_first == 0) lcd.clear();
if (digits_first < 6) { if (first_decimal) { first_decimal_pos++; float divider = pow(10, first_decimal_pos); first = first + (customKey - '0') / divider; } else { first = first * 10 + (customKey - '0'); // if integer (from ascii to numerical value) } lcd.print(customKey); digits_first++; beep(); Serial.println(first, 6); } else { lowBeep(); } if (first == magic_number) { // escape to quiz showHighscore(); correct_answer = quizQuestion(); quiz = true; timestamp = millis(); } if (first == magic_reset) { // reset quiz high score showReset(); char rst = customKeypad.waitForKey(); if (rst == '0') { EEPROM.write(10, 0); high_score = 0; longBeep(); } else { lowBeep(); } showHighscore(); correct_answer = quizQuestion(); quiz = true; timestamp = millis(); } break; case '+': if (digits_first == 0) { lowBeep(); break; } lcd.print("+"); beep(); second = SecondNumber(); // get the collected the second number lcd.print("="); beep(); total = first + second; printTotal(); break; case '-': if (digits_first == 0) { lowBeep(); break; } lcd.print("-"); beep(); second = SecondNumber(); lcd.print("="); beep(); total = first - second; printTotal(); break; case '*': if (digits_first == 0) { lowBeep(); break; } lcd.print("*"); beep(); mult = true; second = SecondNumber(); lcd.print("="); beep(); total = first * second; printTotal(); mult = false; break; case '/': if (digits_first == 0) { lowBeep(); break; } lcd.print("/"); beep(); second = SecondNumber(); lcd.print("="); beep(); second == 0 ? lcd.print("Invalid") : total = (float)first / (float)second; printTotal(); break; case '.': if (digits_first == 0) { lowBeep(); break; } if (first_decimal) { // already has decimal point lowBeep(); } else { lcd.setCursor(digits_first, 0); // add decimal point lcd.print(customKey); digits_first++; beep(); first_decimal = true; } break; case '=': lowBeep(); resetC(); lcd.clear(); break; } } } float SecondNumber() { while ( 1 ) { customKey = customKeypad.getKey(); if (customKey >= '0' && customKey <= '9') {
if (digits_second < 6) {
if ((mult && (digits_first + digits_second) < 10) || !mult) {
lcd.print(customKey);
beep();
if (second_decimal) {
second_decimal_pos++;
float divider = pow(10, second_decimal_pos);
second = second + (customKey - '0') / divider;
}
else {
second = second * 10 + (customKey - '0');  // if integer    (from ascii to numerical value)
}
digits_second++;
Serial.println(second, 6);
}
else {
lowBeep();
}
}
else {
lowBeep();
}
}
if (customKey == '=') break; //return second;

if (customKey == '.') {
if (second_decimal) {                    // already has decimal point
lowBeep();
}
else {
lcd.print(customKey);
digits_second++;
beep();
second_decimal = true;
}

}
if (customKey == '+' || customKey == '-' || customKey == '*' || customKey == '/' ) lowBeep();

}
return second;
}

void resetC() {
total = 0;
digits_first = 0; digits_second = 0;
first = 0, second = 0;  // reset values back to zero for next use
first_decimal = false;
first_decimal_pos = 0;
second_decimal = false;
second_decimal_pos = 0;
}

void printTotal() {
int decimalPlaces;
float temp = total;
for (decimalPlaces = 0; decimalPlaces < 7; decimalPlaces++) { if (temp == (long)temp) break; temp *= 10.0; // Shift left one decimal digit } Serial.println(total, decimalPlaces); lcd.setCursor(0, 1); lcd.print(total, decimalPlaces); resetC(); } void beep() { tone(13, 500); delay(10); noTone(13); } void lowBeep() { tone(13, 50); // low beep delay(10); noTone(13); } void longBeep() { tone(13, 500); // long beep delay(1000); noTone(13); } /////////////////////////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////////////////////// int quizQuestion() { delay(random(0, 100)); randomSeed(analogRead(A0)); lcd.clear(); answer = 0; digits_answer = 0; int upper_limit = score / 2 + 5; first = random(score / 2 + 1, upper_limit); delay(random(0, 100)); randomSeed(analogRead(A0)); delay(random(0, 100)); int op = score % 4; // 0 1 2 3 switch (op) { case 0: lcd.print(first, 0); // float but show as int lcd.print("+"); second = random(1, upper_limit - 1); correct_answer = first + second; break; case 1: lcd.print(first, 0); // float but show as int lcd.print("-"); second = random(1, first + 1); correct_answer = first - second; break; case 2: lcd.print(first, 0); // float but show as int lcd.print("*"); if (first > 10) {
second = 2;
}
else {
second = random(0, 11);
}
break;
case 3:                    // correct_answer = first >> (first * second) / second = first
second = random(1, first + 1);
lcd.print(first * second, 0);              // float but show as int
lcd.print("/");
break;
}
lcd.print(second, 0);              // float but show as int
lcd.print("=");

lcd.setCursor(8, 0);
lcd.print("score:");
if (score < 10) lcd.print(" ");
lcd.print(score);

lcd.setCursor(9, 1);
lcd.print("time:");

lcd.setCursor(0, 1);
}

///////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////////

void beepYes() {
for (int i = 0; i < 3; i++) {
tone(13, 1000);
delay(50);
noTone(13);
delay(50);
}
}

void beepNo() {
for (int i = 0; i < 3; i++) {
tone(13, 100);
delay(50);
noTone(13);
delay(50);
}
}

void showHighscore() {
lcd.clear();
lcd.print("HIGH SCORE= ");
lcd.print(high_score);
lcd.setCursor(0,1);
lcd.print("starting quiz..");
delay(2000);
lcd.clear();

tone(13, 500);
delay(100);
tone(13, 1000);
delay(100);
noTone(13);
}

void showReset() {
lcd.clear();
lcd.print("HIGH SCORE= ");
lcd.print(high_score);
lcd.setCursor(0, 1);
lcd.print("Press 0 to reset...");
}

``````

4 thoughts on “Arduino calculator with quiz”

• 28/11/2018 at 23:40

ITS Soo cool, thanks for the tutorial and I want to try!

• 11/01/2019 at 23:01

Did you try this code?
‘beep’ was not declared in this scope

• 15/01/2019 at 07:28
• 