Proyek Keren: Bikin Pengontrol Charger Aki Pintar Sendiri dengan ESP32!
Hai para hobiis elektronika dan penggemar DIY! Pernahkah Anda ingin memiliki kontrol penuh atas proses pengisian aki atau baterai Anda? Mungkin Anda punya aki bekas yang ingin dipertahankan kinerjanya, atau butuh sistem otomatis yang aman untuk pengisian daya. Nah, di artikel ini, kita akan membuat sebuah pengontrol charger aki pintar menggunakan mikrokontroler canggih ESP32!
Sistem ini tidak hanya mampu memantau tegangan aki secara real-time, tapi juga bisa mengatur kapan charger (atau beban) harus aktif atau nonaktif secara otomatis, lengkap dengan antarmuka pengguna yang intuitif lewat layar OLED dan rotary encoder. Keren, kan? Yuk, langsung saja kita mulai!
Apa Saja yang Kita Butuhkan? (Daftar Komponen)
Untuk membangun proyek ini, Anda memerlukan beberapa komponen elektronik yang cukup umum dan mudah didapatkan.
ESP32 Development Board: Ini adalah "otak" dari proyek kita. ESP32 sangat populer karena memiliki Wi-Fi dan Bluetooth built-in, serta banyak pin GPIO yang fleksibel.
Sensor Arus/Tegangan INA226: Sensor presisi tinggi ini akan membaca tegangan aki Anda dengan sangat akurat.
Layar OLED 0.96 inch (SSD1306): Tampilan mungil ini akan menjadi antarmuka visual utama kita, menampilkan tegangan dan status sistem.
Rotary Encoder dengan Tombol Push: Ini adalah alat navigasi dan input utama kita. Anda bisa memutar untuk mengubah nilai dan menekannya untuk konfirmasi atau mengganti mode.
Modul Relay 1 Channel (5V): Relay ini berfungsi sebagai saklar otomatis yang akan menghidupkan atau mematikan charger (atau beban) sesuai perintah ESP32. Pastikan yang sesuai dengan kebutuhan arus charger Anda.
Kabel Jumper: Secukupnya, untuk menghubungkan semua komponen.
Breadboard (Opsional): Untuk mempermudah prototyping.
Power Supply (5V): Untuk ESP32 (bisa dari port USB PC/laptop atau charger HP).
Aki / Baterai yang ingin Anda kontrol.
Charger Aki (yang akan dikontrol relay).
Rangkaian dan Jalur Perkabelan (Skema Sederhana)
Bagian ini adalah kunci! Perhatikan baik-baik jalur perkabelan agar tidak terjadi kesalahan.
(Catatan: Ilustrasi ini adalah skema penyambungan, bukan diagram sirkuit lengkap. Pastikan Anda memahami dasar-dasar elektronika.)
Berikut adalah detail koneksi pin:
1. Layar OLED (SSD1306)
VCC --> 3.3V atau 5V pada ESP32 (tergantung modul OLED Anda, sebagian besar kompatibel 3.3V/5V)
GND --> GND pada ESP32
SDA --> GPIO21 pada ESP32 (Data I2C)
SCL --> GPIO22 pada ESP32 (Clock I2C)
2. Sensor Arus/Tegangan INA226
VCC --> 3.3V atau 5V pada ESP32 (tergantung modul INA226 Anda, pastikan sesuai dengan datasheet)
GND --> GND pada ESP32
SDA --> GPIO21 pada ESP32 (Berbagi jalur I2C dengan OLED)
SCL --> GPIO22 pada ESP32 (Berbagi jalur I2C dengan OLED)
VIN+ --> Terminal Positif Aki (sebelum shunt resistor, jika ada)
VIN- --> Terminal Positif Aki (setelah shunt resistor / input ke beban/charger) (Penting: Pastikan Anda memahami cara menghubungkan INA226 sesuai dengan sumber daya/beban Anda. Biasanya, INA226 dipasang secara seri dengan beban/charger yang ingin Anda ukur tegangannya.)
3. Rotary Encoder (dengan Tombol)
CLK (Pin A) --> GPIO32 pada ESP32
DT (Pin B) --> GPIO33 pada ESP32
SW (Switch/Tombol) --> GPIO25 pada ESP32
VCC --> 3.3V pada ESP32
GND --> GND pada ESP32
4. Modul Relay 1 Channel
VCC --> 5V pada ESP32 (atau VBUS jika ESP32 Anda memiliki regulator 5V yang kuat, atau sumber 5V eksternal terpisah)
GND --> GND pada ESP32
IN (Signal Pin) --> GPIO26 pada ESP32
NO (Normally Open) & COM (Common): Sambungkan charger aki Anda ke terminal ini. Saat relay "ON", charger akan terhubung ke aki. (Penting: Sambungan relay ke charger dan aki harus dilakukan dengan hati-hati dan benar. Pastikan tegangan dan arus charger Anda sesuai dengan spesifikasi relay.)
Kode Program (Sketch Arduino IDE)
Kode ini ditulis dalam bahasa C++ untuk lingkungan Arduino IDE. Pastikan Anda sudah menginstal ESP32 Board Support Package dan pustaka-pustaka yang dibutuhkan melalui Library Manager (Sketch > Include Library > Manage Libraries...).
Pustaka yang Dibutuhkan:
Wire
(sudah ada di Arduino IDE)Adafruit GFX Library
Adafruit SSD1306
INA226_WE
(ini penting! Pastikan Anda menginstalnya)Preferences
(sudah ada di ESP32 Board Support Package)
#include <Wire.h> // Untuk komunikasi I2C
#include <Adafruit_GFX.h> // Library grafis dasar
#include <Adafruit_SSD1306.h> // Library untuk OLED
#include <INA226_WE.h> // Library untuk INA226_WE - PASTIKAN INI TERINSTAL!
#include <Preferences.h> // Untuk menyimpan data di NVS (Non-Volatile Storage) ESP32
// --- DEKLARASI PIN ESP32 ---
#define OLED_RESET -1 // OLED terhubung ke pin reset ESP32 (biasanya tidak perlu)
#define SCREEN_WIDTH 128 // OLED display width, in pixels
#define SCREEN_HEIGHT 64 // OLED display height, in pixels
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
// 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 OLED
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 updateOLEDDisplay();
void checkLongPress(); // Deklarasi untuk fungsi checkLongPress
// --- FUNGSI SETUP ---
void setup() {
Serial.begin(115200);
// --- Inisialisasi OLED ---
if (!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) { // Alamat I2C umum untuk OLED 0.96"
Serial.println(F("SSD1306 allocation failed"));
for (;;); // Jangan lanjutkan jika gagal
}
display.display();
delay(1000);
display.clearDisplay();
display.setTextSize(1);
display.setTextColor(SSD1306_WHITE);
display.setCursor(0, 0);
display.println("Inisialisasi...");
display.display();
delay(500);
// --- Inisialisasi INA226 ---
Wire.begin(); // PENTING: Inisialisasi komunikasi I2C sebelum menggunakan sensor
ina226.init(); // Panggil init() untuk memulai inisialisasi sensor
// Karena init() di library Anda tidak mengembalikan boolean dan tidak ada isConnected(),
// kita tidak bisa langsung mengecek di sini. Asumsikan koneksi berhasil.
Serial.println("INA226 ditemukan (asumsi koneksi OK).");
// KONFIGURASI INA226 (disesuaikan dengan INA226_WE.h yang Anda berikan)
// Default shunt resistor untuk INA226_WE adalah 0.1 Ohm.
// Jika shunt Anda berbeda (misal 0.01 Ohm), aktifkan baris ini:
// ina226.setShuntResistor(0.01); // Ganti 0.01 dengan nilai shunt resistor Anda
// Menggunakan setAverage dan konstanta AVERAGE_4 dari INA226_WE.h
ina226.setAverage(AVERAGE_4);
// Menggunakan setConversionTime dan konstanta CONV_TIME_1100 dari INA226_WE.h
ina226.setConversionTime(CONV_TIME_1100);
// Menggunakan setMeasureMode dan konstanta CONTINOUS dari INA226_WE.h
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 (sesuaikan dengan modul relay Anda, HIGH/LOW)
// --- Muat Pengaturan dari NVS ---
loadSettings();
Serial.print("Batas Bawah: "); Serial.println(batasTeganganBawah);
Serial.print("Batas Atas: "); Serial.println(batasTeganganAtas);
display.clearDisplay();
}
// --- FUNGSI LOOP ---
void loop() {
// --- BACA SENSOR & INPUT ---
// Baca tegangan aki dari INA226
teganganAki = ina226.getBusVoltage_V(); // Fungsi ini umum di INA226_WE
// Anda juga bisa membaca arus jika diperlukan:
// float arusAki = ina226.getCurrent_A();
// 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;
}
// Batasi range 0.0V hingga 36.0V
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;
}
// Batasi range 0.0V hingga 36.0V
if (batasTeganganAtas < 0.0) batasTeganganAtas = 0.0;
if (batasTeganganAtas > 36.0) batasTeganganAtas = 36.0;
}
lastEncoderPos = encoderPos; // Update posisi terakhir
// Reset blink state agar tidak terlalu cepat berkedip saat diputar
blinkState = true;
lastBlinkMillis = millis();
}
// --- LOGIKA KONTROL RELAY ---
// Logika histeresis untuk mencegah relay berkedip terlalu cepat
if (currentMode == MODE_NORMAL) { // Hanya aktifkan logika kontrol di mode normal
if (digitalRead(RELAY_PIN) == LOW && teganganAki < batasTeganganBawah) { // Jika relay OFF dan tegangan di bawah batas bawah
digitalWrite(RELAY_PIN, HIGH); // Aktifkan relay
} else if (digitalRead(RELAY_PIN) == HIGH && teganganAki > batasTeganganAtas) { // Jika relay ON dan tegangan di atas batas atas
digitalWrite(RELAY_PIN, LOW); // Nonaktifkan relay
}
} else {
// Saat di mode setting, relay selalu OFF agar aman saat pengaturan
digitalWrite(RELAY_PIN, LOW);
}
// --- UPDATE TAMPILAN OLED ---
updateOLEDDisplay();
// Beri sedikit jeda untuk stabilitas
delay(10);
}
// --- FUNGSI INTERRUPT ROTARY ENCODER ---
// Dipanggil setiap kali pin ROTARY_A_PIN berubah (CHANGE)
void IRAM_ATTR readEncoder() {
static bool lastA = HIGH; // Asumsi PULLUP
static bool lastB = HIGH; // Asumsi PULLUP
bool currentA = digitalRead(ROTARY_A_PIN);
bool currentB = digitalRead(ROTARY_B_PIN);
// Hindari bouncing dengan membaca kedua 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 ---
// Dipanggil setiap kali pin ROTARY_SW_PIN berubah (CHANGE)
void IRAM_ATTR handleButtonInterrupt() {
static unsigned long lastInterruptTime = 0;
unsigned long currentTime = millis();
// Debounce tombol
if (currentTime - lastInterruptTime < DEBOUNCE_DELAY) {
return;
}
lastInterruptTime = currentTime;
// Cek status tombol
if (digitalRead(ROTARY_SW_PIN) == LOW) { // Tombol ditekan
if (!buttonPressed) { // Jika baru ditekan
buttonPressed = true;
pushButtonPressTime = currentTime;
longPressTriggered = false; // Reset flag long press
}
} else { // Tombol dilepas
if (buttonPressed) { // Tombol sebelumnya ditekan dan kini dilepas
if (!longPressTriggered) { // Ini adalah Short Press
if (currentMode == MODE_SETTING_BAWAH) {
currentMode = MODE_SETTING_ATAS; // Pindah ke setting Tegangan Atas
Serial.println("Mode: Setting Atas");
blinkState = true; // Langsung tampilkan berkedip
lastBlinkMillis = millis();
} else if (currentMode == MODE_SETTING_ATAS) {
currentMode = MODE_NORMAL; // Keluar dari setting
saveSettings(); // Simpan pengaturan
Serial.println("Mode: Normal, Pengaturan Disimpan");
}
}
buttonPressed = false;
longPressTriggered = false;
}
}
}
// --- FUNGSI UNTUK CEK LONG PRESS (Dipanggil di loop utama) ---
// Note: Lebih baik deteksi long press di loop utama daripada ISR
void checkLongPress() {
if (buttonPressed && !longPressTriggered) {
if (millis() - pushButtonPressTime >= LONG_PRESS_DURATION) {
// Long press terdeteksi
if (currentMode == MODE_NORMAL) {
currentMode = MODE_SETTING_BAWAH; // Masuk ke menu setting Tegangan Bawah
Serial.println("Mode: Setting Bawah (Long Press)");
} else {
// Jika sudah di mode setting, long press bisa jadi untuk keluar dan simpan
currentMode = MODE_NORMAL;
saveSettings(); // Simpan pengaturan
Serial.println("Mode: Normal (Long Press Keluar)");
}
longPressTriggered = true; // Set flag agar tidak terpicu berulang
blinkState = true; // Langsung tampilkan berkedip
lastBlinkMillis = millis();
}
}
}
// --- FUNGSI UPDATE TAMPILAN OLED ---
void updateOLEDDisplay() {
display.clearDisplay();
display.setTextSize(1);
display.setTextColor(SSD1306_WHITE);
// Panggil checkLongPress() secara berkala di sini
// Agar logika long press bisa terdeteksi dan tidak macet
checkLongPress();
if (currentMode == MODE_NORMAL) {
display.setCursor(0, 0);
display.println("Tegangan Aki:");
display.setTextSize(2);
display.setCursor(0, 16);
display.print(String(teganganAki, 1)); // Tampilkan 1 desimal
display.println(" V");
// Tampilkan status relay
display.setTextSize(1);
display.setCursor(0, 40);
if (digitalRead(RELAY_PIN) == HIGH) { // Sesuaikan HIGH/LOW untuk ON
display.println("RELAY ON");
} else {
display.println("RELAY OFF");
}
} else { // Mode Setting
// Logika kedip-kedip teks
if (millis() - lastBlinkMillis > BLINK_INTERVAL) {
blinkState = !blinkState;
lastBlinkMillis = millis();
}
if (currentMode == MODE_SETTING_BAWAH) {
display.setCursor(0, 0);
display.println("SET TEGANGAN BAWAH:");
display.setTextSize(2);
display.setCursor(0, 16);
if (blinkState) {
display.print(String(batasTeganganBawah, 1));
display.println(" V");
} else {
display.println(" "); // Blank space untuk efek kedip
}
display.setTextSize(1);
display.setCursor(0, 40);
display.print("Batas Atas: ");
display.print(String(batasTeganganAtas, 1));
display.println(" V");
} else if (currentMode == MODE_SETTING_ATAS) {
display.setCursor(0, 0);
display.println("SET TEGANGAN ATAS:");
display.setTextSize(2);
display.setCursor(0, 16);
if (blinkState) {
display.print(String(batasTeganganAtas, 1));
display.println(" V");
} else {
display.println(" "); // Blank space untuk efek kedip
}
display.setTextSize(1);
display.setCursor(0, 40);
display.print("Batas Bawah: ");
display.print(String(batasTeganganBawah, 1));
display.println(" V");
}
}
display.display(); // Update OLED
}
// --- FUNGSI SIMPAN/MUAT PENGATURAN KE NVS (Non-Volatile Storage) ---
void saveSettings() {
preferences.begin("charger_app", false); // "charger_app" adalah nama namespace
preferences.putFloat("batasBawah", batasTeganganBawah);
preferences.putFloat("batasAtas", batasTeganganAtas);
preferences.end();
Serial.println("Pengaturan disimpan.");
}
void loadSettings() {
preferences.begin("charger_app", true); // true untuk mode read-only
// Membaca nilai, jika tidak ada, gunakan nilai default yang diberikan
batasTeganganBawah = preferences.getFloat("batasBawah", 10.8);
batasTeganganAtas = preferences.getFloat("batasAtas", 14.8);
preferences.end();
Serial.println("Pengaturan dimuat.");
}
Cara Menggunakan Sistem ini:
Setelah semua komponen terpasang dan kode diunggah ke ESP32 Anda, sistem akan secara otomatis memuat pengaturan terakhir (atau nilai default jika ini pertama kali).
Mode Normal: Layar akan menampilkan tegangan aki saat ini dan status relay (ON/OFF).
Masuk Mode Pengaturan:
Tekan dan tahan (long press) tombol rotary encoder selama sekitar 3 detik. Layar akan beralih ke mode "SET TEGANGAN BAWAH".
Mengatur Tegangan Bawah (
batasTeganganBawah
):Di mode ini, nilai "SET TEGANGAN BAWAH" akan berkedip.
Putar rotary encoder ke kanan untuk menaikkan nilai (0.1V per langkah) atau ke kiri untuk menurunkannya.
Pindah ke Pengaturan Tegangan Atas (
batasTeganganAtas
):Setelah selesai mengatur tegangan bawah, tekan sebentar (short press) tombol rotary encoder. Layar akan beralih ke mode "SET TEGANGAN ATAS".
Mengatur Tegangan Atas:
Sama seperti sebelumnya, nilai "SET TEGANGAN ATAS" akan berkedip.
Putar rotary encoder untuk menyesuaikan nilai.
Keluar dari Mode Pengaturan & Simpan:
Setelah selesai mengatur tegangan atas, tekan sebentar (short press) tombol rotary encoder lagi. Sistem akan kembali ke Mode Normal dan secara otomatis menyimpan pengaturan ke memori permanen ESP32 (NVS).
Atau, jika Anda sedang berada di salah satu mode pengaturan dan ingin keluar serta menyimpan, Anda juga bisa melakukan tekan tahan (long press) tombol rotary encoder.
Bagaimana Cara Kerja Kontrol Relaynya? (Histeresis)
Sistem ini menggunakan logika histeresis untuk mengontrol relay agar tidak "berkedip" terlalu cepat saat tegangan berfluktuasi di sekitar batas.
Jika Relay OFF dan Tegangan Aki < Batas Bawah, maka Relay akan ON.
Jika Relay ON dan Tegangan Aki > Batas Atas, maka Relay akan OFF.
Misalnya, jika batas bawah Anda 10.8V dan batas atas 14.8V:
Aki berada di 10.0V (relay OFF), maka charger akan dihidupkan.
Aki mulai diisi dan tegangannya naik. Charger akan terus menyala sampai tegangan mencapai 14.8V.
Begitu tegangan mencapai 14.8V, charger akan dimatikan.
Tegangan aki akan perlahan turun. Charger akan tetap mati sampai tegangan turun lagi ke bawah 10.8V.
Ini memastikan charger hanya aktif ketika diperlukan dan tidak terus-menerus menyala-mati, yang bisa merusak charger atau aki itu sendiri.
Peningkatan dan Ide Lanjutan
Proyek ini adalah dasar yang bagus, namun Anda bisa mengembangkannya lebih jauh:
Pemantauan Arus: INA226 juga bisa membaca arus. Anda bisa menampilkannya di OLED atau menggunakannya untuk logika kontrol yang lebih canggih (misalnya, mematikan charger jika arus terlalu tinggi).
Indikator Status LED: Tambahkan LED untuk indikasi visual yang cepat (misalnya, LED hijau saat charger ON, LED merah saat OFF).
Konektivitas Wi-Fi: Karena ESP32 punya Wi-Fi, Anda bisa mengirim data tegangan ke platform IoT seperti Blynk, Ubidots, atau bahkan server lokal Anda untuk pemantauan jarak jauh.
Proteksi Lebih Lanjut: Tambahkan proteksi over-current atau over-temperature.
Case Custom: Rancang dan cetak 3D casing khusus untuk proyek Anda agar terlihat lebih profesional.
Selamat mencoba proyek pengontrol charger aki pintar ini! Jika ada pertanyaan atau Anda berhasil membuatnya, jangan ragu untuk berbagi pengalaman di kolom komentar.
No comments:
Post a Comment