Search This Blog

Saturday, 12 July 2025

CARGER AKI CONTROLER INA226-LIBARY WE ROTARI SWITCH DAN PUS LCD 16X 2 TOMBOL ERROR

 #include <Wire.h> // Untuk komunikasi I2C

#include <LiquidCrystal_I2C.h> // Library untuk LCD I2C - PASTIKAN INI TERINSTAL!

#include <INA226_WE.h> // Library untuk INA226_WE

#include <Preferences.h> // Untuk menyimpan data di NVS (Non-Volatile Storage) ESP32


// --- DEKLARASI PIN ESP32 ---

// Alamat I2C umum untuk LCD 16x2 dengan modul PCF8574 adalah 0x27 atau 0x3F

// Coba 0x27 terlebih dahulu, jika tidak berfungsi, coba 0x3F.

LiquidCrystal_I2C lcd(0x27, 16, 2); // Alamat I2C, Kolom, Baris


// Pin untuk Rotary Encoder

#define ROTARY_A_PIN 32 // Ganti dengan pin GPIO yang Anda gunakan

#define ROTARY_B_PIN 33 // Ganti dengan pin GPIO yang Anda gunakan

#define ROTARY_SW_PIN 25 // Pin untuk tombol push Rotary Encoder


// Pin untuk Relay

#define RELAY_PIN 26 // Ganti dengan pin GPIO yang terhubung ke modul relay


// --- OBJEK SENSOR ---

INA226_WE ina226; // Membuat objek INA226 menggunakan library INA226_WE


// --- OBJEK NVS ---

Preferences preferences;


// --- PARAMETER AKI & BATAS ---

float batasTeganganBawah = 10.8; // Nilai default, akan dimuat dari NVS

float batasTeganganAtas = 14.8; // Nilai default, akan dimuat dari NVS


// --- VARIABEL PENGUKURAN ---

float teganganAki = 0.0;


// --- VARIABEL KONTROL MODE & UI ---

enum ModeAlat { MODE_NORMAL, MODE_SETTING_BAWAH, MODE_SETTING_ATAS };

ModeAlat currentMode = MODE_NORMAL;


// Untuk Rotary Encoder

volatile int encoderPos = 0; // Posisi encoder saat ini (volatile karena diakses dari ISR)

int lastEncoderPos = 0;     // Posisi encoder terakhir yang dibaca di loop


// Untuk Tombol Push (Long Press & Single Press)

unsigned long pushButtonPressTime = 0;

bool buttonPressed = false;

bool longPressTriggered = false; // Flag untuk memastikan long press hanya terpicu sekali

const long LONG_PRESS_DURATION = 3000; // 3 detik

const long DEBOUNCE_DELAY = 50; // Millidetik untuk debounce tombol


// Untuk Kedip-kedip Teks di LCD

bool blinkState = false;

unsigned long lastBlinkMillis = 0;

const long BLINK_INTERVAL = 500; // 500 ms


// --- PROTOTYPE FUNGSI ---

// Deklarasi fungsi agar bisa dipanggil sebelum didefinisikan

void IRAM_ATTR readEncoder();

void IRAM_ATTR handleButtonInterrupt();

void saveSettings();

void loadSettings();

void updateLCDDisplay(); // Nama fungsi diganti

void checkLongPress();


// --- FUNGSI SETUP ---

void setup() {

  Serial.begin(115200);


  // --- Inisialisasi LCD ---

  lcd.init();      // Inisialisasi LCD

  lcd.backlight(); // Nyalakan backlight

  lcd.clear();

  lcd.setCursor(0, 0);

  lcd.print("Inisialisasi...");

  Serial.println("Inisialisasi LCD...");

  delay(1000);


  // --- Inisialisasi INA226 ---

  Wire.begin(); // PENTING: Inisialisasi komunikasi I2C sebelum menggunakan sensor

  ina226.init(); // Panggil init() untuk memulai inisialisasi sensor


  Serial.println("INA226 ditemukan (asumsi koneksi OK).");


  // KONFIGURASI INA226 (disesuaikan dengan INA226_WE.h yang Anda berikan)

  ina226.setAverage(AVERAGE_4);

  ina226.setConversionTime(CONV_TIME_1100);

  ina226.setMeasureMode(CONTINOUS);


  // --- Inisialisasi Rotary Encoder ---

  pinMode(ROTARY_A_PIN, INPUT_PULLUP);

  pinMode(ROTARY_B_PIN, INPUT_PULLUP);

  pinMode(ROTARY_SW_PIN, INPUT_PULLUP); // Tombol push dengan pull-up internal


  // Attach Interrupts untuk Rotary Encoder

  attachInterrupt(digitalPinToInterrupt(ROTARY_A_PIN), readEncoder, CHANGE);

  attachInterrupt(digitalPinToInterrupt(ROTARY_SW_PIN), handleButtonInterrupt, CHANGE);


  // --- Inisialisasi Relay ---

  pinMode(RELAY_PIN, OUTPUT);

  digitalWrite(RELAY_PIN, LOW); // Pastikan relay OFF saat startup


  // --- Muat Pengaturan dari NVS ---

  loadSettings();

  Serial.print("Batas Bawah: "); Serial.println(batasTeganganBawah);

  Serial.print("Batas Atas: "); Serial.println(batasTeganganAtas);


  lcd.clear(); // Bersihkan LCD setelah inisialisasi

}


// --- FUNGSI LOOP ---

void loop() {

  // --- BACA SENSOR & INPUT ---

  teganganAki = ina226.getBusVoltage_V();


  // Tangani putaran Rotary Encoder

  if (encoderPos != lastEncoderPos) {

    if (currentMode == MODE_SETTING_BAWAH) {

      if (encoderPos > lastEncoderPos) { // Putar Kanan

        batasTeganganBawah += 0.1;

      } else { // Putar Kiri

        batasTeganganBawah -= 0.1;

      }

      if (batasTeganganBawah < 0.0) batasTeganganBawah = 0.0;

      if (batasTeganganBawah > 36.0) batasTeganganBawah = 36.0;

    } else if (currentMode == MODE_SETTING_ATAS) {

      if (encoderPos > lastEncoderPos) { // Putar Kanan

        batasTeganganAtas += 0.1;

      } else { // Putar Kiri

        batasTeganganAtas -= 0.1;

      }

      if (batasTeganganAtas < 0.0) batasTeganganAtas = 0.0;

      if (batasTeganganAtas > 36.0) batasTeganganAtas = 36.0;

    }

    lastEncoderPos = encoderPos;


    blinkState = true; // Reset blink state

    lastBlinkMillis = millis();

  }


  // --- LOGIKA KONTROL RELAY ---

  if (currentMode == MODE_NORMAL) {

    if (digitalRead(RELAY_PIN) == LOW && teganganAki < batasTeganganBawah) {

      digitalWrite(RELAY_PIN, HIGH);

    } else if (digitalRead(RELAY_PIN) == HIGH && teganganAki > batasTeganganAtas) {

      digitalWrite(RELAY_PIN, LOW);

    }

  } else {

    digitalWrite(RELAY_PIN, LOW); // Relay OFF saat di mode setting

  }


  // Panggil checkLongPress() secara berkala di sini

  checkLongPress();


  // --- UPDATE TAMPILAN LCD ---

  updateLCDDisplay();


  delay(10);

}


// --- FUNGSI INTERRUPT ROTARY ENCODER ---

void IRAM_ATTR readEncoder() {

  static bool lastA = HIGH;

  static bool lastB = HIGH;

  bool currentA = digitalRead(ROTARY_A_PIN);

  bool currentB = digitalRead(ROTARY_B_PIN);


  if (currentA != lastA || currentB != lastB) {

    if (currentA == LOW && currentB == HIGH) { // Rotary diputar CW

      encoderPos++;

    } else if (currentA == HIGH && currentB == LOW) { // Rotary diputar CCW

      encoderPos--;

    }

  }

  lastA = currentA;

  lastB = currentB;

}


// --- FUNGSI INTERRUPT TOMBOL PUSH ROTARY ---

void IRAM_ATTR handleButtonInterrupt() {

  static unsigned long lastInterruptTime = 0;

  unsigned long currentTime = millis();


  if (currentTime - lastInterruptTime < DEBOUNCE_DELAY) {

    return;

  }

  lastInterruptTime = currentTime;


  if (digitalRead(ROTARY_SW_PIN) == LOW) { // Tombol ditekan

    if (!buttonPressed) {

      buttonPressed = true;

      pushButtonPressTime = currentTime;

      longPressTriggered = false;

    }

  } else { // Tombol dilepas

    if (buttonPressed) {

      if (!longPressTriggered) { // Ini adalah Short Press

        if (currentMode == MODE_SETTING_BAWAH) {

          currentMode = MODE_SETTING_ATAS;

          Serial.println("Mode: Setting Atas");

          // Langsung tampilkan di LCD

          lcd.clear();

          lcd.setCursor(0,0);

          lcd.print("SET TEGANGAN ATAS:");

          blinkState = true;

          lastBlinkMillis = millis();

        } else if (currentMode == MODE_SETTING_ATAS) {

          currentMode = MODE_NORMAL;

          saveSettings();

          Serial.println("Mode: Normal, Pengaturan Disimpan");

          // Tampilkan pesan simpan sebentar

          lcd.clear();

          lcd.setCursor(0,0);

          lcd.print("PENGATURAN");

          lcd.setCursor(0,1);

          lcd.print("DISIMPAN!");

          delay(1000); // Tampilkan selama 1 detik

        }

      }

      buttonPressed = false;

      longPressTriggered = false;

    }

  }

}


// --- FUNGSI UNTUK CEK LONG PRESS (Dipanggil di loop utama) ---

void checkLongPress() {

  if (buttonPressed && !longPressTriggered) {

    if (millis() - pushButtonPressTime >= LONG_PRESS_DURATION) {

      if (currentMode == MODE_NORMAL) {

        currentMode = MODE_SETTING_BAWAH;

        Serial.println("Mode: Setting Bawah (Long Press)");

        lcd.clear();

        lcd.setCursor(0,0);

        lcd.print("SET TEGANGAN BAWAH:");

      } else {

        // Jika sudah di mode setting, long press bisa jadi untuk keluar dan simpan

        currentMode = MODE_NORMAL;

        saveSettings();

        Serial.println("Mode: Normal (Long Press Keluar)");

        // Tampilkan pesan simpan sebentar

        lcd.clear();

        lcd.setCursor(0,0);

        lcd.print("PENGATURAN");

        lcd.setCursor(0,1);

        lcd.print("DISIMPAN!");

        delay(1000); // Tampilkan selama 1 detik

      }

      longPressTriggered = true;

      blinkState = true;

      lastBlinkMillis = millis();

    }

  }

}


// --- FUNGSI UPDATE TAMPILAN LCD ---

void updateLCDDisplay() {

  // Logika kedip-kedip teks

  if (millis() - lastBlinkMillis > BLINK_INTERVAL) {

    blinkState = !blinkState;

    lastBlinkMillis = millis();

  }


  // Menggunakan char array untuk format teks agar lebih efisien di mikrokontroler

  char line0[17]; // 16 karakter + null terminator

  char line1[17];


  if (currentMode == MODE_NORMAL) {

    lcd.setCursor(0, 0);

    snprintf(line0, sizeof(line0), "Volt Aki: %.1f V", teganganAki);

    lcd.print(line0);


    lcd.setCursor(0, 1);

    if (digitalRead(RELAY_PIN) == HIGH) {

      snprintf(line1, sizeof(line1), "RELAY: ON");

    } else {

      snprintf(line1, sizeof(line1), "RELAY: OFF");

    }

    lcd.print(line1);


  } else { // Mode Setting

    if (currentMode == MODE_SETTING_BAWAH) {

      lcd.setCursor(0, 0);

      lcd.print("SET BATAS BAWAH:"); // Baris atas tetap


      lcd.setCursor(0, 1);

      if (blinkState) {

        snprintf(line1, sizeof(line1), "%.1f V         ", batasTeganganBawah); // Tambah spasi untuk hapus sisa

        lcd.print(line1);

      } else {

        lcd.print("                "); // Blank space untuk efek kedip

      }

    } else if (currentMode == MODE_SETTING_ATAS) {

      lcd.setCursor(0, 0);

      lcd.print("SET BATAS ATAS: "); // Baris atas tetap


      lcd.setCursor(0, 1);

      if (blinkState) {

        snprintf(line1, sizeof(line1), "%.1f V         ", batasTeganganAtas); // Tambah spasi untuk hapus sisa

        lcd.print(line1);

      } else {

        lcd.print("                "); // Blank space untuk efek kedip

      }

    }

  }

}


// --- FUNGSI SIMPAN/MUAT PENGATURAN KE NVS (Non-Volatile Storage) ---

void saveSettings() {

  preferences.begin("charger_app", false);

  preferences.putFloat("batasBawah", batasTeganganBawah);

  preferences.putFloat("batasAtas", batasTeganganAtas);

  preferences.end();

  Serial.println("Pengaturan disimpan.");

}


void loadSettings() {

  preferences.begin("charger_app", true);

  batasTeganganBawah = preferences.getFloat("batasBawah", 10.8);

  batasTeganganAtas = preferences.getFloat("batasAtas", 14.8);

  preferences.end();

  Serial.println("Pengaturan dimuat.");

}

No comments:

Post a Comment

CARGER AKI CONTROLER INA226-LIBARY WE ROTARI SW+OLED 0.96 SUKSES TAMPILAN LEBIH BAIK

 #include <Wire.h>  #include <Adafruit_GFX.h>    // Library grafis Adafruit #include <Adafruit_SSD1306.h> // Library untuk...