Program Pengisian Air Otomatis Lanjut dengan Arduino

program Arduino untuk pengisian air otomatis dengan fitur lengkap dan advance, termasuk sensor level air, sensor aliran, pompa, LCD, dan opsi kalibrasi.


Program Pengisian Air Otomatis Lanjut dengan Arduino

Program ini dirancang untuk sistem pengisian air otomatis yang cerdas, mampu mengisi tangki hingga level yang ditentukan, memantau aliran, dan menyediakan antarmuka pengguna melalui LCD dan tombol. Fitur kalibrasi volume memungkinkan akurasi yang lebih baik.

Komponen yang Dibutuhkan

  • Arduino Uno/Mega

  • Sensor Level Air Ultrasonik (HC-SR04): Untuk mendeteksi ketinggian air.

  • Sensor Aliran Air (YF-S201): Untuk mengukur volume air yang mengalir.

  • Relay Modul 1 Channel: Untuk mengontrol pompa air.

  • Pompa Air DC (sesuai kebutuhan)

  • LCD I2C 16x2 atau 20x4: Untuk menampilkan informasi.

  • Push Button (3 buah): Untuk navigasi menu dan pengaturan.

  • Resistor (untuk pull-down jika diperlukan)

  • Kabel Jumper

  • Power Supply Eksternal (untuk pompa jika diperlukan)

Skema Pengkabelan (Contoh)

  • Sensor Ultrasonik (HC-SR04):

    • VCC ke 5V

    • GND ke GND

    • Trig ke Pin Digital 9

    • Echo ke Pin Digital 10

  • Sensor Aliran Air (YF-S201):

    • VCC ke 5V

    • GND ke GND

    • Signal ke Pin Digital 2 (Interrupt 0)

  • Relay:

    • VCC ke 5V

    • GND ke GND

    • IN ke Pin Digital 4

  • LCD I2C:

    • SDA ke SDA (Analog Pin A4 pada Uno)

    • SCL ke SCL (Analog Pin A5 pada Uno)

    • VCC ke 5V

    • GND ke GND

  • Push Button:

    • Button UP ke Pin Digital 5

    • Button SELECT ke Pin Digital 6

    • Button DOWN ke Pin Digital 7

    • Hubungkan sisi lain setiap tombol ke GND (gunakan internal pull-up atau eksternal pull-down resistor 10K).

Library yang Dibutuhkan

Pastikan Anda telah menginstal library berikut di Arduino IDE Anda:

  • LiquidCrystal_I2C: Untuk LCD I2C. Anda dapat menginstalnya melalui Sketch > Include Library > Manage Libraries... cari "LiquidCrystal I2C".

  • NewPing: Untuk sensor ultrasonik HC-SR04. Cari "NewPing" di Library Manager.


Kode Program

C++
#include <LiquidCrystal_I2C.h>
#include <NewPing.h>
#include <EEPROM.h> // Untuk menyimpan data kalibrasi di EEPROM

// --- Pengaturan Sensor Level Air (Ultrasonik) ---
#define TRIGGER_PIN 9
#define ECHO_PIN 10
#define MAX_DISTANCE 200 // Jarak maksimum dalam cm yang diharapkan dari sensor ke dasar tangki
#define TANK_HEIGHT 30   // Tinggi tangki sebenarnya dalam cm
NewPing sonar(TRIGGER_PIN, ECHO_PIN, MAX_DISTANCE);

// --- Pengaturan Sensor Aliran Air ---
#define FLOW_SENSOR_PIN 2 // Pin interrupt (Interrupt 0 pada Arduino Uno)
volatile unsigned long pulseCount = 0;
float calibrationFactor = 4.5; // Faktor kalibrasi default (pulses per mL). Sesuaikan!
unsigned long startTime = 0;
float totalVolumeML = 0.0; // Total volume terakumulasi dalam mL

// --- Pengaturan Pompa ---
#define PUMP_PIN 4

// --- Pengaturan LCD ---
#define LCD_ADDR 0x27 // Alamat I2C LCD (biasanya 0x27 atau 0x3F)
#define LCD_COLS 16
#define LCD_ROWS 2
LiquidCrystal_I2C lcd(LCD_ADDR, LCD_COLS, LCD_ROWS);

// --- Pengaturan Tombol ---
#define BUTTON_UP_PIN 5
#define BUTTON_SELECT_PIN 6
#define BUTTON_DOWN_PIN 7

// --- Variabel State ---
enum MenuState {
  STATE_HOME,
  STATE_SET_TARGET_LEVEL,
  STATE_CALIBRATE_FLOW,
  STATE_FILLING,
  STATE_CALIBRATING_RUN
};
MenuState currentMenuState = STATE_HOME;

float targetLevelPercentage = 80.0; // Level target dalam persen (default 80%)
float currentLevelPercentage = 0.0;

// --- Variabel Kalibrasi ---
#define CALIBRATION_VOLUME_ML 1000 // Volume air yang digunakan untuk kalibrasi (1 Liter)
#define CALIBRATION_EEPROM_ADDR 0  // Alamat EEPROM untuk menyimpan faktor kalibrasi

// --- Fungsi Interrupt untuk Sensor Aliran ---
void flowISR() {
  pulseCount++;
}

// --- Fungsi Baca Jarak Ultrasonik ---
float getDistanceCm() {
  delay(50); // Beri waktu sensor untuk stabil
  unsigned int uS = sonar.ping_cm();
  if (uS == 0) { // Jika tidak ada echo atau di luar jangkauan
    return MAX_DISTANCE;
  }
  return uS;
}

// --- Fungsi Konversi Jarak ke Persen Level ---
float getLevelPercentage(float distanceCm) {
  // Jarak dari sensor ke permukaan air adalah 'distanceCm'
  // Ketinggian air = TANK_HEIGHT - distanceCm
  // Level persen = (Ketinggian air / TANK_HEIGHT) * 100
  float waterHeight = TANK_HEIGHT - distanceCm;
  if (waterHeight < 0) waterHeight = 0; // Pastikan tidak negatif
  float level = (waterHeight / TANK_HEIGHT) * 100.0;
  if (level > 100) level = 100; // Pastikan tidak lebih dari 100%
  return level;
}

// --- Fungsi Kontrol Pompa ---
void turnPumpOn() {
  digitalWrite(PUMP_PIN, HIGH);
  lcd.setCursor(0, 1);
  lcd.print("Pompa: ON         ");
}

void turnPumpOff() {
  digitalWrite(PUMP_PIN, LOW);
  lcd.setCursor(0, 1);
  lcd.print("Pompa: OFF        ");
  totalVolumeML = 0; // Reset volume setelah pengisian selesai/dihentikan
}

// --- Fungsi Tampilan LCD ---
void displayHome() {
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print("Level: ");
  lcd.print(currentLevelPercentage, 1);
  lcd.print("% ");
  lcd.print("Trgt: ");
  lcd.print(targetLevelPercentage, 0);
  lcd.print("%");
  lcd.setCursor(0, 1);
  lcd.print("Mode: Home        ");
}

void displaySetTargetLevel() {
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print("Set Level: ");
  lcd.print(targetLevelPercentage, 0);
  lcd.print("%   ");
  lcd.setCursor(0, 1);
  lcd.print("< Up  Sel  Down >");
}

void displayCalibrateFlow() {
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print("Kalibrasi Flow");
  lcd.setCursor(0, 1);
  lcd.print("Vol: ");
  lcd.print(CALIBRATION_VOLUME_ML);
  lcd.print("mL ");
}

void displayFilling() {
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print("Isi: ");
  lcd.print(currentLevelPercentage, 1);
  lcd.print("%  ");
  lcd.print("Vol:");
  lcd.print(totalVolumeML, 0);
  lcd.print("mL");
  lcd.setCursor(0, 1);
  lcd.print("Menunggu...");
}

void displayCalibratingRun() {
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print("Kalibrasi...");
  lcd.setCursor(0, 1);
  lcd.print("Pulses: ");
  lcd.print(pulseCount);
}

// --- Fungsi Baca Tombol ---
bool readButton(int pin) {
  return digitalRead(pin) == LOW; // Asumsi tombol terhubung ke GND
}

// --- Setup ---
void setup() {
  Serial.begin(9600);

  // Inisialisasi LCD
  lcd.init();
  lcd.backlight();
  lcd.print("Sistem Air Otomatis");
  delay(2000);

  // Pin mode
  pinMode(PUMP_PIN, OUTPUT);
  digitalWrite(PUMP_PIN, LOW); // Pastikan pompa mati saat start

  pinMode(BUTTON_UP_PIN, INPUT_PULLUP);
  pinMode(BUTTON_SELECT_PIN, INPUT_PULLUP);
  pinMode(BUTTON_DOWN_PIN, INPUT_PULLUP);

  // Interrupt untuk sensor aliran
  attachInterrupt(digitalPinToInterrupt(FLOW_SENSOR_PIN), flowISR, RISING);

  // Muat faktor kalibrasi dari EEPROM
  EEPROM.get(CALIBRATION_EEPROM_ADDR, calibrationFactor);
  if (calibrationFactor < 1.0 || calibrationFactor > 10.0) { // Batasan untuk nilai wajar
    calibrationFactor = 4.5; // Setel ke default jika nilai tidak valid
    EEPROM.put(CALIBRATION_EEPROM_ADDR, calibrationFactor);
  }
  Serial.print("Faktor Kalibrasi Dimuat: ");
  Serial.println(calibrationFactor);

  displayHome();
}

// --- Loop ---
void loop() {
  // Baca level air secara kontinu (kecuali saat kalibrasi)
  if (currentMenuState != STATE_CALIBRATING_RUN) {
    float distance = getDistanceCm();
    currentLevelPercentage = getLevelPercentage(distance);
    Serial.print("Jarak: ");
    Serial.print(distance);
    Serial.print("cm, Level: ");
    Serial.print(currentLevelPercentage);
    Serial.println("%");
  }

  // Handle Menu State
  switch (currentMenuState) {
    case STATE_HOME:
      displayHome();
      if (readButton(BUTTON_SELECT_PIN)) {
        delay(200); // Debounce
        currentMenuState = STATE_SET_TARGET_LEVEL;
        displaySetTargetLevel();
      } else if (readButton(BUTTON_UP_PIN)) { // Akses Kalibrasi dari Home
        delay(200);
        currentMenuState = STATE_CALIBRATE_FLOW;
        displayCalibrateFlow();
      }
      break;

    case STATE_SET_TARGET_LEVEL:
      displaySetTargetLevel();
      if (readButton(BUTTON_UP_PIN)) {
        delay(200);
        targetLevelPercentage = min(100.0, targetLevelPercentage + 5.0);
        displaySetTargetLevel();
      }
      if (readButton(BUTTON_DOWN_PIN)) {
        delay(200);
        targetLevelPercentage = max(0.0, targetLevelPercentage - 5.0);
        displaySetTargetLevel();
      }
      if (readButton(BUTTON_SELECT_PIN)) {
        delay(200);
        currentMenuState = STATE_HOME; // Kembali ke home setelah set
        displayHome();
      }
      break;

    case STATE_CALIBRATE_FLOW:
      displayCalibrateFlow();
      if (readButton(BUTTON_SELECT_PIN)) {
        delay(200);
        // Mulai proses kalibrasi
        currentMenuState = STATE_CALIBRATING_RUN;
        pulseCount = 0; // Reset pulse count
        totalVolumeML = 0; // Reset total volume
        turnPumpOn();
        startTime = millis();
        displayCalibratingRun();
      } else if (readButton(BUTTON_DOWN_PIN)) {
        delay(200);
        currentMenuState = STATE_HOME;
        displayHome();
      }
      break;

    case STATE_CALIBRATING_RUN:
      displayCalibratingRun();
      // Kalibrasi berjalan sampai tombol SELECT ditekan atau waktu habis (misal 30 detik untuk keamanan)
      // Disarankan untuk mengisi volume yang ditentukan secara manual dan kemudian menekan SELECT.
      if (readButton(BUTTON_SELECT_PIN) || (millis() - startTime > 30000 && pulseCount > 0)) { // 30 detik timeout jika lupa tekan
        delay(200);
        turnPumpOff();
        if (pulseCount > 0) {
          calibrationFactor = (float)pulseCount / CALIBRATION_VOLUME_ML;
          EEPROM.put(CALIBRATION_EEPROM_ADDR, calibrationFactor);
          lcd.clear();
          lcd.setCursor(0, 0);
          lcd.print("Kalibrasi Selesai!");
          lcd.setCursor(0, 1);
          lcd.print("Faktor: ");
          lcd.print(calibrationFactor, 2);
          Serial.print("Faktor Kalibrasi Baru: ");
          Serial.println(calibrationFactor, 2);
          delay(3000);
        } else {
          lcd.clear();
          lcd.setCursor(0, 0);
          lcd.print("Kalibrasi Gagal!");
          lcd.setCursor(0, 1);
          lcd.print("Tidak ada pulsa.");
          delay(3000);
        }
        currentMenuState = STATE_HOME;
        displayHome();
      }
      break;

    case STATE_FILLING:
      displayFilling();
      // Hitung volume air yang mengalir
      totalVolumeML = (float)pulseCount / calibrationFactor;

      // Logika pengisian
      if (currentLevelPercentage < targetLevelPercentage) {
        if (digitalRead(PUMP_PIN) == LOW) { // Jika pompa mati, nyalakan
          turnPumpOn();
        }
      } else {
        turnPumpOff(); // Matikan pompa jika level sudah tercapai
        currentMenuState = STATE_HOME;
        displayHome();
      }

      // Tombol SELECT untuk menghentikan pengisian secara manual
      if (readButton(BUTTON_SELECT_PIN)) {
        delay(200);
        turnPumpOff();
        currentMenuState = STATE_HOME;
        displayHome();
      }
      break;
  }

  // Logika otomatis pengisian dari mode HOME
  if (currentMenuState == STATE_HOME) {
    if (currentLevelPercentage < (targetLevelPercentage - 2.0)) { // Mulai mengisi jika di bawah target (dengan histeresis)
      currentMenuState = STATE_FILLING;
      pulseCount = 0; // Reset pulse count sebelum memulai pengisian baru
      totalVolumeML = 0;
      turnPumpOn();
      displayFilling();
    }
  }

  // Debounce umum untuk semua tombol
  delay(100);
}

Penjelasan Kode dan Fitur Lanjutan

  1. Pengaturan Komponen:

    • #define: Digunakan untuk pin dan konstanta. Memudahkan perubahan jika Anda menggunakan pin yang berbeda atau ukuran tangki yang lain.

    • NewPing: Library ini khusus untuk HC-SR04, memberikan pembacaan jarak yang lebih stabil dan fitur debounce.

    • LiquidCrystal_I2C: Untuk antarmuka LCD. Pastikan alamat I2C LCD Anda benar (0x27 atau 0x3F).

  2. Sensor Level Air (Ultrasonik):

    • Fungsi getDistanceCm() membaca jarak dari sensor ke permukaan air.

    • Fungsi getLevelPercentage() mengkonversi jarak tersebut menjadi persentase level air di dalam tangki, berdasarkan TANK_HEIGHT yang Anda definisikan. Penting untuk mengukur tinggi tangki Anda dengan akurat.

  3. Sensor Aliran Air:

    • Menggunakan Interrupt: Sensor aliran terhubung ke pin interrupt (FLOW_SENSOR_PIN). Setiap kali ada pulsa dari sensor, fungsi flowISR() akan dipanggil secara otomatis dan cepat, meningkatkan akurasi penghitungan pulsa.

    • calibrationFactor: Ini adalah kunci untuk akurasi pengukuran volume. Nilai ini menunjukkan berapa banyak pulsa yang dihasilkan sensor untuk setiap mililiter air. Ini harus dikalibrasi.

    • totalVolumeML: Menghitung volume air yang telah mengalir berdasarkan pulseCount dan calibrationFactor.

  4. Kontrol Pompa:

    • Menggunakan modul Relay untuk mengontrol pompa DC, yang biasanya membutuhkan tegangan dan arus lebih tinggi daripada yang bisa disediakan langsung oleh pin Arduino.

    • Fungsi turnPumpOn() dan turnPumpOff() untuk memudahkan kontrol.

  5. LCD dan Antarmuka Pengguna:

    • LiquidCrystal_I2C: Menampilkan informasi level air, target, volume, dan status sistem.

    • enum MenuState: Mengelola status menu yang berbeda (HOME, SET_TARGET_LEVEL, CALIBRATE_FLOW, FILLING, CALIBRATING_RUN). Ini membuat navigasi menu lebih terstruktur.

    • Fungsi displayX(): Setiap fungsi bertanggung jawab untuk menampilkan informasi yang relevan dengan state menu saat ini.

    • Tombol UP, DOWN, SELECT: Untuk navigasi dan pengaturan. Tombol SELECT juga digunakan untuk memulai/menghentikan kalibrasi dan pengisian manual.

  6. Fitur Canggih:

    • Kalibrasi Aliran Air (Flow Calibration):

      • Memungkinkan Anda mengkalibrasi sensor aliran untuk akurasi yang lebih baik.

      • Anda akan diminta untuk menyiapkan volume air tertentu (misal: 1 liter = 1000mL) dan membiarkan pompa mengisi volume tersebut.

      • Selama kalibrasi, sistem akan menghitung total pulsa yang dihasilkan.

      • calibrationFactor = (float)pulseCount / CALIBRATION_VOLUME_ML;: Faktor kalibrasi dihitung berdasarkan pulsa yang terdeteksi dan volume air yang diketahui.

      • Penyimpanan EEPROM: Faktor kalibrasi yang dihitung disimpan di EEPROM Arduino (EEPROM.put/EEPROM.get). Ini berarti nilai kalibrasi akan tetap tersimpan bahkan setelah Arduino dimatikan. Ini sangat penting untuk akurasi jangka panjang.

    • Target Level dalam Persentase: Lebih intuitif daripada menggunakan jarak atau volume absolut.

    • Histeresis untuk Pengisian: Dalam STATE_HOME, pompa akan mulai mengisi jika level air di bawah targetLevelPercentage - 2.0%. Ini mencegah pompa sering hidup/mati (chattering) jika level air berfluktuasi sedikit di sekitar target.

    • Reset Volume Otomatis: totalVolumeML di-reset setiap kali pompa dimatikan (setelah pengisian selesai atau dihentikan manual).

    • Debounce Tombol: delay(200) atau delay(100) setelah pembacaan tombol untuk mencegah pembacaan ganda karena pantulan mekanis tombol.

    • Timeout Kalibrasi: Jika kalibrasi dimulai dan tidak ada tombol SELECT yang ditekan, ia akan berhenti otomatis setelah 30 detik untuk mencegah pompa berjalan terus-menerus.

Cara Penggunaan

  1. Pasang Semua Komponen: Ikuti skema pengkabelan yang disebutkan.

  2. Instal Library: Buka Arduino IDE, pergi ke Sketch > Include Library > Manage Libraries... dan cari "LiquidCrystal I2C" serta "NewPing" untuk menginstalnya.

  3. Sesuaikan Parameter:

    • Ubah LCD_ADDR jika alamat I2C LCD Anda berbeda.

    • Ubah MAX_DISTANCE dan terutama TANK_HEIGHT sesuai dengan tinggi tangki Anda yang sebenarnya (dari sensor ke dasar tangki saat kosong).

    • Sesuaikan CALIBRATION_VOLUME_ML jika Anda ingin menggunakan volume kalibrasi yang berbeda (misal, 500mL atau 2000mL).

  4. Unggah Kode: Unggah kode ke papan Arduino Anda.

  5. Kalibrasi Sensor Aliran:

    • Setelah diunggah, Arduino akan booting ke mode HOME.

    • Tekan tombol UP untuk masuk ke menu kalibrasi (STATE_CALIBRATE_FLOW).

    • Siapkan wadah berukuran tepat (misal, botol 1 Liter jika CALIBRATION_VOLUME_ML = 1000).

    • Tekan tombol SELECT untuk memulai kalibrasi. Pompa akan menyala.

    • Biarkan pompa mengisi wadah Anda hingga mencapai volume yang ditentukan (misal, 1 Liter).

    • Segera setelah volume tercapai, tekan tombol SELECT lagi untuk menghentikan kalibrasi.

    • LCD akan menampilkan "Kalibrasi Selesai!" dan faktor kalibrasi baru. Faktor ini akan disimpan di EEPROM.

  6. Atur Level Target:

    • Dari mode HOME, tekan tombol SELECT untuk masuk ke menu "Set Level".

    • Gunakan tombol UP dan DOWN untuk mengatur persentase level air yang Anda inginkan (misal, 80%).

    • Tekan tombol SELECT lagi untuk menyimpan pengaturan dan kembali ke HOME.

  7. Operasi Otomatis:

    • Dalam mode HOME, sistem akan secara otomatis memantau level air.

    • Jika level air turun di bawah target (dengan histeresis), pompa akan menyala secara otomatis untuk mengisi tangki.

    • Pompa akan mati ketika level air mencapai target.

    • Anda juga dapat menekan tombol SELECT kapan saja selama pengisian untuk menghentikannya secara manual.


Potensi Pengembangan Lanjut

  • Antarmuka Lebih Kaya: Gunakan LCD grafis atau antarmuka web (dengan modul ESP8266/ESP32) untuk visualisasi yang lebih baik.

  • Sensor Keamanan: Tambahkan sensor kebocoran, sensor level air maksimum/minimum fisik (float switch) sebagai redundansi.

  • Mode Manual: Tambahkan mode di mana pengguna dapat menyalakan/mematikan pompa secara manual.

  • Peringatan/Notifikasi: Buzzer, LED, atau bahkan notifikasi ke smartphone (dengan modul Wi-Fi) untuk level rendah, pengisian selesai, atau masalah.

  • Penjadwalan: Fitur untuk mengisi air pada waktu tertentu.

  • Riwayat Data: Menyimpan data pengisian atau konsumsi air.

  • Kontrol PID: Untuk pengisian yang lebih halus dan presisi, meskipun untuk pengisian tangki sederhana ini mungkin terlalu kompleks.

Program ini menyediakan dasar yang kuat untuk sistem pengisian air otomatis yang cerdas dan andal. Selamat mencoba!

Komentar