#include #include #include #include #include #if defined(ESP8266)|| defined(ESP32) || defined(AVR) #include #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; } } } }