// BMI - Arduino
// v1.0
// LIBRARIES
#include "HX711.h"
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include <Keypad.h>
#include <SPI.h>
#include <SD.h>
#include <Adafruit_MLX90614.h>
// CONSTANTS
const float LOAD_CELL_CALIBRATION_FACTOR = 0.1;
const float US_HEIGHT = 180.00; // 180cm
const unsigned long BLINK_DELAY = 500; // 500ms
const float TRIGGER_WEIGHT = 10.00; // 10kg
const float TRIGGER_HEIGHT = 30.00; // 30cm
const float TRIGGER_TEMPERATURE = 32.00; // 32C
const unsigned long WEIGHT_DELAY = 3000; // 3seconds
const unsigned long HEIGHT_DELAY = 3000; // 3seconds
const unsigned long TEMPERATURE_DELAY = 3000; // 3seconds
const unsigned long SENSOR_DELAY = 500; // 500milliseconds
const byte KEYPAD_ROWS = 4;
const byte KEYPAD_COLS = 4;
const char KEYPAD_KEYS[KEYPAD_ROWS][KEYPAD_COLS] = {
{'1', '2', '3', 'A'},
{'4', '5', '6', 'B'},
{'7', '8', '9', 'C'},
{'*', '0', '#', 'D'}
};
const int LOAD_CELL_DATA_PIN = 6;
const int LOAD_CELL_CLOCK_PIN = 7;
const byte KEYPAD_ROW_PINS[KEYPAD_ROWS] = {5, 4, 3, 2};
const byte KEYPAD_COL_PINS[KEYPAD_COLS] = {11, 10, 9, 8};
const int US_TRIG_PIN = 26;
const int US_ECHO_PIN = 27;
const int SD_CS_PIN = 53;
const int SCREEN_WEIGHT = 1;
const int SCREEN_ID = 2;
const int SCREEN_HEIGHT = 3;
const int SCREEN_MLX = 4;
const int SCREEN_SAVE = 5;
// VARIABLES
HX711 scale;
LiquidCrystal_I2C lcd(0x27, 20, 4);
Keypad keypad = Keypad( makeKeymap(KEYPAD_KEYS), KEYPAD_ROW_PINS, KEYPAD_COL_PINS, KEYPAD_ROWS, KEYPAD_COLS );
Adafruit_MLX90614 mlx = Adafruit_MLX90614();
File myFile;
float weight = 0.00;
float height = 0.00;
float temperature = 0.00;
float bmi = 0.00;
char id[16] = "";
String bmiString = "";
String bmiStringShortened = "";
String dataString = "";
bool isDisplayed = false;
int screen = 0;
// SETUP
void setup() {
pinMode(US_TRIG_PIN, OUTPUT);
pinMode(US_ECHO_PIN, INPUT);
Serial.begin(115200);
initDataOnSD();
//initMLX();
scale.begin(LOAD_CELL_DATA_PIN, LOAD_CELL_CLOCK_PIN);
scale.set_scale(LOAD_CELL_CALIBRATION_FACTOR);
scale.tare(20);
lcd.init();
lcd.backlight();
displayTitle();
screen = SCREEN_WEIGHT;
}
// LOOP
void loop() {
weightRoutine();
idRoutine();
heightRoutine();
mlxRoutine();
saveRoutine();
}
// FUNCTIONS
void displayTitle() {
lcd.clear();
lcd.setCursor(0 , 0);
lcd.print("Automated BMI");
lcd.setCursor(0 , 1);
lcd.print("Calculator");
lcd.setCursor(0 , 2);
lcd.print("with");
lcd.setCursor(0 , 3);
lcd.print("Thermal Scanner");
delay(3000);
}
void weightRoutine() {
if (screen == SCREEN_WEIGHT) {
static unsigned long measureTime = 0;
static bool triggered = false;
if (!isDisplayed) {
isDisplayed = true;
triggered = false;
weight = 0;
lcd.clear();
lcd.setCursor(0 , 0);
lcd.print("Weight: ");
}
if (scale.is_ready()) {
weight = getWeight();
displayWeight(weight);
}
if (weight >= TRIGGER_WEIGHT && !triggered) {
measureTime = millis();
triggered = true;
}
if (triggered) {
if (millis() - measureTime >= WEIGHT_DELAY) {
blinkWeightDisplay(weight);
screen = SCREEN_ID;
isDisplayed = false;
}
}
}
}
void idRoutine() {
if (screen == SCREEN_ID) {
static int idCursor = 0;
if (!isDisplayed) {
isDisplayed = true;
idCursor = 0;
lcd.setCursor(0, 1);
lcd.print("ID: ");
}
char key = keypad.getKey();
if (key) {
Serial.println(key);
if (key != '*' &&
key != '#' &&
key != 'A' &&
key != 'B' &&
key != 'C' &&
key != 'D') {
if (idCursor + 4 <= 19) {
lcd.setCursor(idCursor + 4, 1);
lcd.print(key);
id[idCursor] = key;
idCursor++;
}
} else if (key == '#') {
id[idCursor] = '\0';
screen = SCREEN_HEIGHT;
} else if (key == '*') {
screen = SCREEN_ID;
}
if (key == '#' || key == '*') {
isDisplayed = false;
}
}
}
}
void heightRoutine() {
if (screen == SCREEN_HEIGHT) {
static unsigned long measureTime = millis();
static unsigned long readTime = millis();
static bool triggered = false;
if (!isDisplayed) {
isDisplayed = true;
triggered = false;
height = 0;
lcd.setCursor(0, 2);
lcd.print("Height: ");
}
if (millis() - readTime >= SENSOR_DELAY) {
readTime = millis();
height = getHeight();
displayHeight(height);
}
if (height >= TRIGGER_HEIGHT && !triggered) {
measureTime = millis();
triggered = true;
}
if (triggered) {
if (millis() - measureTime >= HEIGHT_DELAY) {
blinkHeightDisplay(height);
screen = SCREEN_MLX;
isDisplayed = false;
}
}
}
}
void mlxRoutine() {
if (screen == SCREEN_MLX) {
static unsigned long measureTime = millis();
static unsigned long readTime = millis();
static bool triggered = false;
if (!isDisplayed) {
isDisplayed = true;
triggered = false;
temperature = 0;
lcd.setCursor(0, 3);
lcd.print("Temp: ");
}
if (millis() - readTime >= SENSOR_DELAY) {
readTime = millis();
temperature = getTemperature();
displayTemperature(temperature);
}
if (temperature >= TRIGGER_TEMPERATURE && !triggered) {
measureTime = millis();
triggered = true;
}
if (triggered) {
if (millis() - measureTime >= TEMPERATURE_DELAY) {
blinkTemperatureDisplay(temperature);
screen = SCREEN_SAVE;
isDisplayed = false;
}
}
}
}
void saveRoutine() {
if (screen == SCREEN_SAVE) {
char key = keypad.getKey();
if (key) {
Serial.println(key);
if (key == '#') {
Serial.println("Saved! Clear device!");
bmi = getBMI();
bmiString = getConvertedBMI(bmi);
bmiStringShortened = getConvertedBMIShortened(bmi);
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Weight: ");
lcd.print(weight, 2);
lcd.print("kg");
lcd.setCursor(0, 1);
lcd.print("Height: ");
lcd.print(height, 2);
lcd.print("cm");
lcd.setCursor(0, 2);
lcd.print("Temp: ");
lcd.print(temperature, 2);
lcd.print("*C");
lcd.setCursor(0, 3);
lcd.print("BMI: ");
lcd.print(bmi, 2);
lcd.print(" ");
lcd.print(bmiStringShortened);
processData();
saveDataToSD(dataString);
delay(10000);
screen = SCREEN_WEIGHT;
isDisplayed = false;
}
}
}
}
float getWeight() {
float reading = scale.get_units(1) / 1000.00;
if (reading < 0) {
reading = 0.00;
}
Serial.print("Weight: ");
Serial.println(reading);
return reading;
}
void displayWeight(float w) {
lcd.setCursor(8, 0);
lcd.print(w, 2);
lcd.print("kg ");
}
void blinkWeightDisplay(float w) {
lcd.setCursor(8, 0);
lcd.print(" ");
delay(BLINK_DELAY);
displayWeight(w);
delay(BLINK_DELAY);
lcd.setCursor(8, 0);
lcd.print(" ");
delay(BLINK_DELAY);
displayWeight(w);
delay(BLINK_DELAY);
}
float getDistance() {
digitalWrite(US_TRIG_PIN, LOW);
delayMicroseconds(2);
digitalWrite(US_TRIG_PIN, HIGH);
delayMicroseconds(10);
digitalWrite(US_TRIG_PIN, LOW);
long duration = pulseIn(US_ECHO_PIN, HIGH);
float distance = duration * 0.034 / 2;
Serial.print("Distance: ");
Serial.println(distance);
return distance;
}
float getHeight() {
float distance = getDistance();
float reading = US_HEIGHT - distance;
if (reading < 0) {
reading = 0.00;
}
Serial.print("Height: ");
Serial.println(reading);
return reading;
}
void displayHeight(float h) {
lcd.setCursor(8, 2);
lcd.print(h, 2);
lcd.print("cm ");
}
void blinkHeightDisplay(float h) {
lcd.setCursor(8, 2);
lcd.print(" ");
delay(BLINK_DELAY);
displayHeight(h);
delay(BLINK_DELAY);
lcd.setCursor(8, 2);
lcd.print(" ");
delay(BLINK_DELAY);
displayHeight(h);
delay(BLINK_DELAY);
}
float getMLXTest() {
int reading = analogRead(A0);
return map(reading, 0, 1023, 0, 100);
}
float getTemperature() {
float reading = getMLXTest();
//float reading = mlx.readObjectTempC();
if (reading < 0) {
reading = 0.00;
}
Serial.print("Temperature: ");
Serial.println(reading);
return reading;
}
void displayTemperature(float t) {
lcd.setCursor(6, 3);
lcd.print(t, 2);
lcd.print("*C ");
}
void blinkTemperatureDisplay(float t) {
lcd.setCursor(6, 3);
lcd.print(" ");
delay(BLINK_DELAY);
displayTemperature(t);
delay(BLINK_DELAY);
lcd.setCursor(6, 3);
lcd.print(" ");
delay(BLINK_DELAY);
displayTemperature(t);
delay(BLINK_DELAY);
}
void processData() {
dataString = "";
dataString += (String) id;
dataString += ",";
dataString += (String) weight;
dataString += ",";
dataString += (String) height;
dataString += ",";
dataString += (String) temperature;
dataString += ",";
dataString += (String) bmi;
dataString += ",";
dataString += bmiString;
dataString += "\n";
}
void initDataOnSD() {
Serial.print("Initializing SD card...");
if (!SD.begin(SD_CS_PIN)) {
Serial.println("initialization failed!");
while (1);
}
Serial.println("initialization done.");
if (SD.exists("datalog.txt")) {
Serial.println("datalog.txt exists.");
} else {
Serial.println("datalog.txt doesn't exist.");
// open a new file and immediately close it:
Serial.println("Creating datalog.txt...");
myFile = SD.open("datalog.txt", FILE_WRITE);
myFile.close();
}
}
void saveDataToSD(String data) {
// open the file. note that only one file can be open at a time,
// so you have to close this one before opening another.
myFile = SD.open("datalog.txt", O_APPEND);
// if the file is available, write to it:
if (myFile) {
myFile.println(data);
myFile.close();
// print to the serial port too:
Serial.println(data);
}
// if the file isn't open, pop up an error:
else {
Serial.println("error opening datalog.txt");
}
}
void initMLX() {
if (!mlx.begin()) {
Serial.println("Error connecting to MLX sensor. Check wiring.");
while (1);
};
}
float getBMI() {
float hInM = height/100.00;
float output = weight/(hInM*hInM);
Serial.print("BMI: ");
Serial.println(output);
return output;
}
String getConvertedBMI(float b) {
String bs = "";
if (b < 18.5) {
bs = "Underweight";
} else if (b >= 18.5 && b < 25) {
bs = "Normal";
} else if (b >= 25 && b < 30) {
bs = "Overweight";
} else {
bs = "Very Overweight";
}
Serial.print("BMI String: ");
Serial.println(bs);
return bs;
}
String getConvertedBMIShortened(float b) {
String bs = "";
if (b < 18.5) {
bs = "UNDRWGT";
} else if (b >= 18.5 && b < 25) {
bs = "NORMAL";
} else if (b >= 25 && b < 30) {
bs = "OVRWGT";
} else {
bs = "VOVRWGT";
}
Serial.print("BMI String Shortened: ");
Serial.println(bs);
return bs;
}