/* Arduino calculator with keypad and LCD, and quiz Tom Tobback Oct 2017 keypad library: || @version 1.0 || @author Andrew Mascolo || @date May 15, 2013 || @description || Simple use of keypad and LCD taken from https://playground.arduino.cc/Main/KeypadCalculator use LCD 16x2 with pot buzzer on D13 keypad on D2-D9 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 answer; // quiz answer int correct_answer; // correct quiz answer int digits_answer = 0; // count digits of answer 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 Keypad customKeypad = Keypad( makeKeymap(keys), rowPins, colPins, ROWS, COLS); void setup() { // EEPROM.write(10, 0); // OPTIONAL reset high score to 0 high_score = EEPROM.read(10); if (high_score == 255) { EEPROM.write(10, 0); // reset high score to 0 high_score = 0; } randomSeed(analogRead(A0)); 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() { customKey = customKeypad.getKey(); 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); } } customKeypad.waitForKey(); // new quiz beep(); score = 0; showHighscore(); correct_answer = quizQuestion(); 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) { if (answer == correct_answer) { beepYes(); score++; } else { beepNo(); score--; if (score < 0) score = 0; } correct_answer = quizQuestion(); } 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); } correct_answer = first * second; break; case 3: // correct_answer = first >> (first * second) / second = first second = random(1, first + 1); correct_answer = first; 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); return correct_answer; } /////////////////////////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////////////////////// 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..."); }