234 lines
6.0 KiB
C++
234 lines
6.0 KiB
C++
#include <WiFi.h>
|
|
#include <Firebase_ESP_Client.h>
|
|
#include <HX711_ADC.h>
|
|
#include <Wire.h>
|
|
#include <LiquidCrystal_I2C.h>
|
|
#if defined(ESP8266)|| defined(ESP32) || defined(AVR)
|
|
#include <EEPROM.h>
|
|
#endif
|
|
|
|
// === WIFI & FIREBASE ===
|
|
#define WIFI_SSID "..."
|
|
#define WIFI_PASSWORD "12345678"
|
|
#define API_KEY "AIzaSyDQ0zYJrQqCMrAU0-iO0qtCmXCR3wnG0wc"
|
|
#define DATABASE_URL "https://timbangan-ae018-default-rtdb.firebaseio.com/"
|
|
#define LEGACY_TOKEN "svzBUSZ6M8ANPs1uGuI4sxNeU2MLq3pEaDiYalgB"
|
|
|
|
FirebaseData fbdo;
|
|
FirebaseAuth auth;
|
|
FirebaseConfig config;
|
|
|
|
// === SENSOR BERAT ===
|
|
const int HX711_dout = 23;
|
|
const int HX711_sck = 4;
|
|
HX711_ADC LoadCell(HX711_dout, HX711_sck);
|
|
|
|
// === SENSOR ULTRASONIK ===
|
|
#define TRIG_PIN 5
|
|
#define ECHO_PIN 18
|
|
|
|
LiquidCrystal_I2C lcd(0x27, 16, 2);
|
|
|
|
const int calVal_eepromAdress = 0;
|
|
unsigned long t = 0;
|
|
float calibrationFactor = 21.69;
|
|
long customOffset = 0;
|
|
|
|
#define NUM_SAMPLES 5
|
|
float beratBuffer[NUM_SAMPLES];
|
|
int bufferIndex = 0;
|
|
bool isWeightLocked = false;
|
|
|
|
float getSmoothedWeight(float newReading) {
|
|
beratBuffer[bufferIndex] = newReading;
|
|
bufferIndex = (bufferIndex + 1) % NUM_SAMPLES;
|
|
float total = 0;
|
|
for (int i = 0; i < NUM_SAMPLES; i++) {
|
|
total += beratBuffer[i];
|
|
}
|
|
return total / NUM_SAMPLES;
|
|
}
|
|
|
|
long readDistanceCM() {
|
|
digitalWrite(TRIG_PIN, LOW);
|
|
delayMicroseconds(2);
|
|
digitalWrite(TRIG_PIN, HIGH);
|
|
delayMicroseconds(10);
|
|
digitalWrite(TRIG_PIN, LOW);
|
|
long duration = pulseIn(ECHO_PIN, HIGH, 30000); // timeout 30ms
|
|
long distance = duration * 0.034 / 2;
|
|
return distance;
|
|
}
|
|
|
|
double hitungBMI(double berat, double tinggi) {
|
|
if (tinggi == 0) return 0;
|
|
double tinggiMeter = tinggi / 100.0; // Konversi tinggi ke meter
|
|
return berat / (tinggiMeter * tinggiMeter); // Rumus BMI
|
|
}
|
|
|
|
String kategoriBMI(double bmi) {
|
|
if (bmi < 18.5) return "Kurus";
|
|
if (bmi <= 24.9) return "Normal";
|
|
if (bmi <= 29.9) return "Berlebih";
|
|
return "Obesitas";
|
|
}
|
|
|
|
void setup() {
|
|
Serial.begin(115200);
|
|
Serial.println("Memulai...");
|
|
|
|
// Inisialisasi pin ultrasonik
|
|
pinMode(TRIG_PIN, OUTPUT);
|
|
pinMode(ECHO_PIN, INPUT);
|
|
|
|
// LCD
|
|
lcd.init();
|
|
lcd.backlight();
|
|
lcd.clear();
|
|
lcd.setCursor(0, 0);
|
|
lcd.print("Menghubungkan...");
|
|
|
|
// WiFi
|
|
WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
|
|
unsigned long start = millis();
|
|
while (WiFi.status() != WL_CONNECTED) {
|
|
delay(500);
|
|
Serial.print(".");
|
|
if (millis() - start > 20000) {
|
|
Serial.println("Gagal Koneksi WiFi!");
|
|
lcd.clear();
|
|
lcd.setCursor(0, 0);
|
|
lcd.print("Gagal WiFi");
|
|
return;
|
|
}
|
|
}
|
|
Serial.println("WiFi Terhubung!");
|
|
lcd.clear();
|
|
lcd.setCursor(0, 0);
|
|
lcd.print("WiFi Terhubung");
|
|
|
|
// Firebase
|
|
config.api_key = API_KEY;
|
|
config.database_url = DATABASE_URL;
|
|
config.signer.tokens.legacy_token = LEGACY_TOKEN;
|
|
Firebase.begin(&config, &auth);
|
|
Firebase.reconnectWiFi(true);
|
|
|
|
// LoadCell
|
|
LoadCell.begin();
|
|
unsigned long stabilizingtime = 2000;
|
|
boolean _tare = true;
|
|
LoadCell.start(stabilizingtime, _tare);
|
|
if (LoadCell.getTareTimeoutFlag() || LoadCell.getSignalTimeoutFlag()) {
|
|
Serial.println("Timeout HX711");
|
|
lcd.clear();
|
|
lcd.setCursor(0, 0);
|
|
lcd.print("HX711 Error");
|
|
while (1);
|
|
}
|
|
LoadCell.setCalFactor(calibrationFactor);
|
|
while (!LoadCell.update());
|
|
}
|
|
void loop() {
|
|
static boolean newDataReady = 0;
|
|
const int serialPrintInterval = 3000;
|
|
|
|
if (LoadCell.update()) newDataReady = true;
|
|
|
|
if (newDataReady && millis() > t + serialPrintInterval && Firebase.ready()) {
|
|
// ====== BACA BERAT ======
|
|
float berat = LoadCell.getData();
|
|
if (berat < 0) berat = 0;
|
|
float beratKg = berat / 1000.0;
|
|
float rataRataBerat = getSmoothedWeight(beratKg);
|
|
if (!isWeightLocked && abs(rataRataBerat - beratKg) < 0.1) {
|
|
isWeightLocked = true;
|
|
}
|
|
|
|
// ====== BACA TINGGI DARI ULTRASONIK ======
|
|
long jarak = readDistanceCM(); // cm
|
|
float tinggi = 201.0 - jarak;
|
|
if (jarak > 201 || tinggi < 0) {
|
|
tinggi = 0;
|
|
}
|
|
|
|
// ====== HITUNG BMI ======
|
|
double bmi = hitungBMI(rataRataBerat, tinggi);
|
|
|
|
// ====== TAMPILKAN DI LCD ======
|
|
lcd.clear();
|
|
lcd.setCursor(0, 0);
|
|
lcd.print("B: ");
|
|
lcd.print(rataRataBerat, 1);
|
|
lcd.print("kg T: ");
|
|
lcd.print(tinggi, 0);
|
|
lcd.print("cm");
|
|
|
|
lcd.setCursor(0, 1);
|
|
char output[10];
|
|
dtostrf(bmi, 4, 1, output); // Convert to string with 1 decimal point
|
|
lcd.print("BMI: ");
|
|
lcd.print(output); // Display BMI on LCD
|
|
|
|
Serial.printf("Berat: %.2f kg | Tinggi: %.0f cm (Jarak: %ld cm) | BMI: %.1f\n", rataRataBerat, tinggi, jarak, bmi);
|
|
|
|
// ====== KIRIM KE FIREBASE ======
|
|
Firebase.RTDB.setFloat(&fbdo, "/sensor/berat", rataRataBerat);
|
|
Firebase.RTDB.setFloat(&fbdo, "/sensor/tinggi", tinggi);
|
|
Firebase.RTDB.setFloat(&fbdo, "/sensor/bmi", bmi); // Kirim BMI ke Firebase
|
|
|
|
newDataReady = 0;
|
|
t = millis();
|
|
}
|
|
|
|
if (Serial.available() > 0) {
|
|
char inByte = Serial.read();
|
|
if (inByte == 't') LoadCell.tareNoDelay();
|
|
else if (inByte == 'r') calibrate();
|
|
else if (inByte == 'c') changeSavedCalFactor();
|
|
}
|
|
|
|
if (LoadCell.getTareStatus() == true) {
|
|
Serial.println("Tare complete");
|
|
}
|
|
}
|
|
|
|
void calibrate() {
|
|
Serial.println("* Start calibration... Kirim 't' untuk tare");
|
|
}
|
|
|
|
void changeSavedCalFactor() {
|
|
float oldVal = LoadCell.getCalFactor();
|
|
boolean _resume = false;
|
|
Serial.print("Current: "); Serial.println(oldVal);
|
|
Serial.println("Kirim nilai baru:");
|
|
float newVal;
|
|
while (!_resume) {
|
|
if (Serial.available() > 0) {
|
|
newVal = Serial.parseFloat();
|
|
if (newVal != 0) {
|
|
LoadCell.setCalFactor(newVal);
|
|
_resume = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
_resume = false;
|
|
Serial.println("Simpan ke EEPROM? y/n");
|
|
while (!_resume) {
|
|
if (Serial.available() > 0) {
|
|
char inByte = Serial.read();
|
|
if (inByte == 'y') {
|
|
EEPROM.begin(512);
|
|
EEPROM.put(calVal_eepromAdress, newVal);
|
|
EEPROM.commit();
|
|
Serial.println("Disimpan.");
|
|
_resume = true;
|
|
} else if (inByte == 'n') {
|
|
Serial.println("Tidak disimpan.");
|
|
_resume = true;
|
|
}
|
|
}
|
|
}
|
|
}
|