Install the necessary libraries in the Arduino IDE for the code to work properly.
#include <Wire.h>
#include <Keypad.h>
#include <LiquidCrystal_I2C.h>
#include <Adafruit_LiquidCrystal.h>
// Initialize the I2C LCD with the correct address
LiquidCrystal_I2C lcd(0x27, 16, 2);
const byte ROWS = 4; // Number of rows in the keypad
const byte COLS = 4; // Number of columns in the keypad
// Define the key map for the 4x4 keypad
char hexaKeys[ROWS][COLS] = {
{'1', '2', '3', 'A'},
{'4', '5', '6', 'B'},
{'7', '8', '9', 'C'},
{'*', '0', '#', 'D'}
};
// Pin connections for the rows and columns
byte rowPins[ROWS] = {9, 8, 7, 6};
byte colPins[COLS] = {5, 4, 3, 2};
// Create the Keypad object with the defined keymap and pin configuration
Keypad customKeypad = Keypad(makeKeymap(hexaKeys), rowPins, colPins, ROWS, COLS);
String ageInput = "";
String heightInput = ""; // Variable to store the height input
String weightInput = ""; // Variable to store the weight input
char genderInput = 'M'; // Default gender is Male
boolean isEnteringAge = true; // Flag to track if entering age
boolean isEnteringGender = false; // Flag to track if entering gender
boolean isEnteringHeight = false; // Flag to track if entering height
boolean isEnteringWeight = false; // Flag to track if entering weight
bool isMetric = true; // Flag to track the unit system (Metric or Imperial)
bool buzzerEnabled = true; // Flag to toggle the buzzer on/off
unsigned long keyPressTime = 0; // Time when the key was pressed
bool longPressDetected = false; // Flag to detect long press
const int buzzerPin = 11; // Pin connected to the buzzer
int heightLimit = 4; // Default limit for height input
void setup() {
Serial.begin(9600); // Initialize Serial Monitor
lcd.init(); // Initialize the LCD
lcd.backlight(); // Turn on the LCD backlight
pinMode(buzzerPin, OUTPUT); // Configure the buzzer pin
// Welcome Screen
lcd.setCursor(0, 0);
lcd.print("BMI Calculator");
delay(3000); // Display the welcome screen for 3 seconds
promptForAge();
}
void loop() {
char key = customKeypad.getKey(); // Get the key pressed on the keypad
if (key) {
playTone(1000, 100); // Normal key press tone
if (key == '*') {
keyPressTime = millis(); // Record the time when '*' was pressed
longPressDetected = false;
}
switch (key) {
case '#':
if (isEnteringAge) {
if (isNumeric(ageInput)) {
isEnteringAge = false;
isEnteringGender = true;
promptForGender();
playTone(1500, 150); // Confirmation tone
} else {
displayError("Invalid Age!");
}
} else if (isEnteringGender) {
isEnteringGender = false;
isEnteringHeight = true;
promptForHeight();
playTone(1500, 150); // Confirmation tone
} else if (isEnteringHeight) {
if (isNumeric(heightInput)) {
isEnteringHeight = false;
isEnteringWeight = true;
promptForWeight();
playTone(1500, 150); // Confirmation tone
} else {
displayError("Invalid Height!");
}
} else if (isEnteringWeight) {
if (isNumeric(weightInput)) {
calculateBMI(); // Calculate and display BMI
playTone(1500, 150); // Confirmation tone
delay(7000); // Display BMI for 7 seconds
promptForAge(); // Reset inputs for next calculation
} else {
displayError("Invalid Weight!");
}
}
break;
case '*':
if (millis() - keyPressTime > 2000 && !longPressDetected) { // If '*' is held for more than 2 seconds
playTone(500, 300); // Reset tone
promptForAge(); // Clear inputs and start over
longPressDetected = true;
} else if (!longPressDetected) { // Short press: clear current input
clearCurrentInput(); // Clear the current input field
}
break;
case 'C':
if (isEnteringGender) {
toggleGender(); // Toggle between Male and Female
} else {
switchUnits(); // Toggle between metric and imperial units without resetting inputs
}
break;
case 'D':
handleDecimalInput(); // Add decimal point
break;
case 'A':
showKeyFunctions(); // Display functions of other keys
break;
case 'B':
toggleBuzzer(); // Toggle buzzer on/off
break;
default:
if (isDigit(key)) {
if (isEnteringAge) {
if (ageInput.length() < 3) { // Limit age input to 3 characters
ageInput += key;
lcd.setCursor(0, 1);
lcd.print(ageInput);
} else {
displayError("Limit: 3 digits");
ageInput = ""; // Clear the input so the user can re-enter valid data
}
} else if (isEnteringHeight) {
if (heightInput.length() < heightLimit) { // Adjust limit based on decimal
heightInput += key;
lcd.setCursor(0, 1);
lcd.print(heightInput);
} else {
displayError("Limit: " + String(heightLimit) + " digits");
heightInput = ""; // Clear the input so the user can re-enter valid data
}
} else if (isEnteringWeight) {
// ** Change made here: Handle weight input length based on decimal point **
if (weightInput.indexOf('.') == -1) { // If no decimal point
if (weightInput.length() < 3) { // Limit to 3 characters
weightInput += key;
lcd.setCursor(0, 1);
lcd.print(weightInput);
} else {
displayError("Limit: 3 digits");
weightInput = ""; // Clear the input
}
} else { // If a decimal point exists
if (weightInput.length() < 5) { // Limit to 5 characters
weightInput += key;
lcd.setCursor(0, 1);
lcd.print(weightInput);
} else {
displayError("Limit: 5 digits");
weightInput = ""; // Clear the input
}
}
}
}
break;
}
}
}
void promptForAge() {
ageInput = "";
genderInput = 'M'; // Default gender is Male
heightInput = "";
weightInput = "";
isEnteringAge = true;
isEnteringGender = false;
isEnteringHeight = false;
isEnteringWeight = false;
heightLimit = 4; // Reset height limit
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Enter Age:");
}
void promptForGender() {
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Use C to change");
lcd.setCursor(0, 1);
lcd.print("Gender: Male"); // Default is Male
}
void promptForHeight() {
lcd.clear();
lcd.setCursor(0, 0);
lcd.print(isMetric ? "Height (cm):" : "Height (in):");
lcd.setCursor(0, 1);
}
void promptForWeight() {
lcd.clear();
lcd.setCursor(0, 0);
lcd.print(isMetric ? "Weight (kg):" : "Weight (lbs):");
lcd.setCursor(0, 1);
}
void showKeyFunctions() {
lcd.clear();
const char* keyExplanations[] = {
"B: Sound On/Off",
"C: Toggle Units",
"D: Add Decimal",
"*: Clear/Reset",
"#: Confirm",
"BMI Range",
"U: Under weight",
"H: Healthy",
"O: Over Weight",
"OB: Obesity",
};
for (int i = 0; i < 10; i++) {
lcd.clear();
lcd.setCursor(0, 0);
lcd.print(keyExplanations[i]);
delay(2000); // Display each explanation for 2 seconds
}
// After showing the key functions, return to the current input stage
if (isEnteringAge) {
promptForAge();
} else if (isEnteringGender) {
promptForGender();
} else if (isEnteringHeight) {
promptForHeight();
} else if (isEnteringWeight) {
promptForWeight();
}
}
void toggleGender() {
genderInput = (genderInput == 'M') ? 'F' : 'M'; // Toggle between Male and Female
lcd.setCursor(0, 1);
lcd.print("Gender: ");
lcd.print(genderInput == 'M' ? "Male " : "Female");
}
void toggleBuzzer() {
buzzerEnabled = !buzzerEnabled;
lcd.clear();
lcd.setCursor(0, 0);
lcd.print(buzzerEnabled ? "Buzzer: ON" : "Buzzer: OFF");
delay(1000); // Display the status for 1 second
// Return to the current input stage after toggling
if (isEnteringAge) {
promptForAge();
} else if (isEnteringGender) {
promptForGender();
} else if (isEnteringHeight) {
promptForHeight();
} else if (isEnteringWeight) {
promptForWeight();
}
}
bool isNumeric(String str) {
bool decimalPoint = false;
for (byte i = 0; i < str.length(); i++) {
if (str.charAt(i) == '.') {
if (decimalPoint) return false; // If more than one decimal point, invalid
decimalPoint = true;
} else if (!isDigit(str.charAt(i))) {
return false;
}
}
return true;
}
void switchUnits() {
isMetric = !isMetric;
if (isEnteringHeight) {
promptForHeight(); // Update prompt without resetting inputs
} else if (isEnteringWeight) {
promptForWeight(); // Update prompt without resetting inputs
}
}
void handleDecimalInput() {
if (isEnteringHeight && heightInput.indexOf('.') == -1) {
heightInput += '.';
heightLimit = 5; // Adjust height limit for decimal
lcd.setCursor(0, 1);
lcd.print(heightInput);
} else if (isEnteringWeight && weightInput.indexOf('.') == -1) {
weightInput += '.';
lcd.setCursor(0, 1);
lcd.print(weightInput);
}
}
void calculateBMI() {
float height = heightInput.toFloat(); // Convert height to float
if (isMetric) {
height = height / 100.0; // Convert height from cm to meters
}
float weight = weightInput.toFloat(); // Convert weight to float
float bmi;
if (isMetric) {
bmi = weight / (height * height); // kg/m^2
} else {
bmi = (weight / (height * height)) * 703; // lbs/in^2
}
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("BMI: ");
lcd.print(bmi, 2); // Display BMI with two decimal places
lcd.setCursor(0, 1);
String advice;
if (bmi < 18.5) {
advice = "U: Eat more!";
} else if (bmi < 24.9) {
advice = (genderInput == 'M') ? "H: Looking good!": "H: You rock!";
} else if (bmi < 29.9) {
advice = "O: Cut the junk!";
} else {
advice = "OB: Time to GYM!";
}
lcd.print(advice);
}
void displayError(String message) {
lcd.clear();
lcd.setCursor(0, 0);
lcd.print(message);
playTone(400, 500); // Error tone
delay(2000); // Display error message for 2 seconds
// Return to the current input stage after error
clearCurrentInput();
}
void clearCurrentInput() {
if (isEnteringAge) {
ageInput = "";
promptForAge();
} else if (isEnteringHeight) {
heightInput = "";
promptForHeight();
} else if (isEnteringWeight) {
weightInput = "";
promptForWeight();
}
}
void playTone(int frequency, int duration) {
if (buzzerEnabled) {
tone(buzzerPin, frequency, duration);
}
}