Membuat Tombol Touchscreen ON/OFF ESP32 untuk Mengontrol Relay dan Lampu di TFT ILI9488

Pelajari cara membuat tombol touchscreen ON/OFF ESP32 pada TFT ILI9488 untuk mengontrol relay, lampu, LED, dan perangkat elektronik lainnya.

 



1. Pendahuluan

Pada artikel ini kita akan membahas bagaimana cara membuat tombol touchscreen ON/OFF pada layar TFT ILI9488 menggunakan ESP32.
Proyek ini sangat menarik karena kita tidak lagi menggunakan tombol fisik biasa, melainkan membuat tombol digital langsung di layar TFT touchscreen.
Dengan teknik ini, ESP32 dapat diubah menjadi panel kontrol modern untuk berbagai kebutuhan seperti smart home, kontrol relay, lampu, pompa air, kipas, hingga dashboard otomatisasi sederhana.

Selain belajar membuat tombol ON dan OFF, kita juga akan memahami bagaimana cara kerja touchscreen pada TFT, bagaimana membaca koordinat sentuhan,
bagaimana menampilkan gambar tombol dari SPIFFS, serta bagaimana mengontrol relay menggunakan tombol virtual yang dibuat di layar.

Artikel ini dibuat khusus untuk pemula maupun maker yang ingin memahami alur berpikir dalam membuat antarmuka touchscreen pada ESP32.

2. Mengapa Menggunakan Tombol Touchscreen pada ESP32?


Pada proyek ESP32 sederhana, kita biasanya menggunakan push button fisik untuk menyalakan LED atau relay.
Namun ketika proyek mulai berkembang, penggunaan tombol fisik akan membuat perangkat terlihat kurang rapi dan kurang fleksibel.
Dengan menggunakan layar touchscreen TFT, semua tombol dapat dibuat langsung di layar.

Keuntungan menggunakan tombol touchscreen antara lain:
- Tampilan lebih modern
- Jumlah tombol tidak terbatas
- Mudah diubah desainnya
- Dapat dibuat seperti panel HMI industri
- Cocok untuk smart home
- Bisa dikembangkan menjadi dashboard monitoring

Selain itu, tombol digital pada TFT juga memberikan pengalaman penggunaan yang jauh lebih menarik dibandingkan tombol biasa.


Dalam pengembangan proyek ESP32, memahami konsep jauh lebih penting dibandingkan hanya sekadar copy-paste kode.
Banyak pengguna mengalami masalah ketika layar tidak merespons sentuhan, koordinat terbalik, gambar tidak tampil, atau relay tidak bekerja dengan benar.
Masalah seperti ini biasanya terjadi karena pengguna belum memahami hubungan antara touchscreen, SPIFFS, GPIO, dan sistem tampilan TFT.

Karena itu pada artikel ini fokus utamanya bukan hanya menghasilkan proyek yang bekerja, tetapi juga memahami cara berpikir dalam membangun antarmuka touchscreen menggunakan ESP32.
Pendekatan seperti ini akan sangat membantu ketika nantinya ingin membuat proyek yang lebih kompleks seperti smart home, panel industri, atau dashboard IoT.


3. Persiapan Hardware


 


Sebelum mulai membuat proyek, siapkan beberapa komponen berikut:

1. ESP32
2. TFT 4 Inch ILI9488 Touchscreen
3. Modul Relay ataupun kitset modul juga lebih baik
4. LED untuk pengujian awal
5. Kabel jumper

6.Komputer dengan Arduino IDE

Sebaiknya pengujian pertama dilakukan menggunakan LED terlebih dahulu sebelum mencoba mengontrol relay AC.



4. Konsep Cara Kerja Tombol Touchscreen


Tombol touchscreen sebenarnya bukan tombol fisik.
Yang kita lihat di layar hanyalah gambar atau objek visual.
Ketika layar disentuh, touchscreen akan mengirimkan koordinat sentuhan berupa nilai X dan Y.

Program kemudian akan memeriksa apakah koordinat sentuhan berada di area tombol.
Jika iya, maka ESP32 menjalankan aksi tertentu seperti:
- Menyalakan LED
- Mematikan relay
- Mengubah gambar tombol
- Menjalankan animasi
- Mengirim data ke internet

Inilah dasar dari sistem HMI atau Human Machine Interface.



5. Persiapan Gambar Tombol


Pada proyek ini kita menggunakan gambar tombol ON dan OFF.
Gambar dapat dibuat menggunakan Photoshop, Canva, atau aplikasi desain lainnya.

Tips membuat gambar tombol:
- Gunakan warna hijau untuk ON
- Gunakan warna merah untuk OFF
- Buat ukuran tidak terlalu besar dalam hal ini saya pakai 200 x 200 px
- Simpan dalam format JPG

Contoh nama file:
- on.jpg
- off.jpg

Gambar nantinya disimpan ke SPIFFS agar dapat dibaca ESP32.



6. Upload Gambar ke SPIFFS


SPIFFS memungkinkan ESP32 menyimpan file seperti gambar, HTML, maupun konfigurasi.
Dengan SPIFFS, gambar tombol tidak perlu ditulis langsung di kode program.

Langkah upload SPIFFS:
1. Buat folder data
2. Masukkan file gambar
3. Gunakan plugin ESP32 Sketch Data Upload
4. Upload ke ESP32

Setelah upload berhasil, gambar dapat dipanggil langsung dari memori internal ESP32.




7. Menampilkan Tombol di TFT


Setelah gambar berhasil diupload, langkah berikutnya adalah menampilkan gambar tombol di layar TFT.

Biasanya library yang digunakan:
- TFT_eSPI
- TJpg_Decoder

Keuntungan menggunakan gambar JPG:
- Tampilan lebih menarik
- UI lebih profesional
- Mudah mengganti desain tombol

Pada tahap ini, kita akan menampilkan tombol OFF sebagai tampilan awal.



8. Membaca Sentuhan pada Touchscreen


Touchscreen bekerja dengan membaca koordinat sentuhan.
Ketika layar disentuh, program akan mendapatkan nilai:
- X
- Y

Contoh logika sederhana:

Jika X berada antara 50 sampai 150
dan Y berada antara 100 sampai 180,
maka tombol dianggap ditekan.

Inilah dasar utama pembuatan tombol touchscreen.



9. Membuat Logika Tombol ON/OFF


Logika tombol dibuat menggunakan variabel status.

Contoh:
- status = false → relay OFF
- status = true → relay ON

Ketika tombol disentuh:
1. Status dibalik
2. Gambar tombol diganti
3. GPIO relay diubah

Dengan cara ini, tombol dapat berubah dari ON ke OFF secara dinamis.



10. Mengontrol Relay dan Lampu


Setelah tombol berhasil bekerja, kita dapat menghubungkan relay.

Relay memungkinkan ESP32 mengontrol:
- Lampu rumah
- Pompa air
- Kipas
- Stop kontak
- Perangkat elektronik lainnya

Namun perlu diperhatikan bahwa listrik AC sangat berbahaya.
Gunakan relay dengan hati-hati dan lakukan pengujian awal menggunakan LED.



11. Pengembangan Proyek Selanjutnya


Setelah memahami dasar tombol touchscreen, proyek dapat dikembangkan menjadi:
- Smart home panel
- Dashboard monitoring
- Kontrol multi relay
- Kontrol via WiFi
- MQTT dashboard
- Sistem otomatisasi rumah
- HMI industri mini

Dengan kombinasi ESP32 dan TFT ILI9488, kemungkinan pengembangan sangat luas.



12. Contoh Program Untuk menampilkan Tombol ON/OFF

#include 
#include 
#include 
#include 
#include 

// =====================================================
// PIN CONFIGURATION
// =====================================================
#define TOUCH_CS_PIN    15
#define RELAY_PIN       25  // HIGH level trigger

// =====================================================
// TOUCH SCREEN SETTINGS
// =====================================================
#define TOUCH_PRESSURE_THRESHOLD 1000
#define TOUCH_DEBOUNCE_DELAY     300

// =====================================================
// SCREEN RESOLUTION
// =====================================================
#define SCREEN_WIDTH  320
#define SCREEN_HEIGHT 480

// =====================================================
// OBJECTS
// =====================================================
TFT_eSPI tft = TFT_eSPI();
XPT2046_Touchscreen ts(TOUCH_CS_PIN);

// =====================================================
// TOUCH STATE VARIABLES
// =====================================================
bool touchEnabled = true;
bool isTouching = false;
unsigned long lastTouchTime = 0;

// =====================================================
// IMAGE BUTTON STRUCTURE
// =====================================================
struct ImageButton {
  String imageOffPath;
  String imageOnPath;
  bool state;
  int x, y;
  int w, h;
  uint8_t scale;
};

// =====================================================
// BUTTON INITIALIZATION
// =====================================================
ImageButton button1 = {
  "/off.jpg",      // Image when OFF
  "/on.jpg",       // Image when ON
  false,           // Initial state: OFF
  50,              // X position
  140,             // Y position
  0, 0,            // W, H (will be calculated)
  0                // Scale (will be calculated)
};

// =====================================================
// HELPER: Draw Bitmap 1-bit
// =====================================================
void drawBitmap1bit(int x, int y, const unsigned char *bitmap, 
                    int w, int h, uint16_t fgcolor, uint16_t bgcolor) {
  int byteWidth = (w + 7) / 8;
  for (int j = 0; j < h; j++) {
    for (int i = 0; i < w; i++) {
      uint8_t byte = pgm_read_byte(bitmap + j * byteWidth + i / 8);
      bool pixelOn = byte & (0x80 >> (i % 8));
      tft.drawPixel(x + i, y + j, pixelOn ? fgcolor : bgcolor);
    }
  }
}

// =====================================================
// JPG RENDER CALLBACK
// =====================================================
bool tft_jpg_render(int16_t x, int16_t y, uint16_t w, uint16_t h, uint16_t *bitmap) {
  if (y >= tft.height()) return false;
  tft.pushImage(x, y, w, h, bitmap);
  return true;
}

// =====================================================
// TOUCH MAPPING FUNCTIONS
// =====================================================
int mapX(int rawX) {
  return map(rawX, 3690, 470, 0, SCREEN_WIDTH);
}

int mapY(int rawY) {
  return map(rawY, 420, 3715, SCREEN_HEIGHT, 0);
}

// =====================================================
// BUZZER
// =====================================================
void beepBuzzer(int duration = 100) {
  // Optional: uncomment if you have buzzer connected
  // digitalWrite(BUZZER_PIN, HIGH);
  // delay(duration);
  // digitalWrite(BUZZER_PIN, LOW);
}

// =====================================================
// DISPLAY BOOT SCREEN
// =====================================================
void drawBootScreen() {
  tft.fillScreen(TFT_BLACK);
  tft.setTextColor(TFT_WHITE, TFT_BLACK);
  tft.setTextDatum(MC_DATUM);
  tft.setTextSize(1);
  
  tft.drawString("IMAGE BUTTON", SCREEN_WIDTH / 2, (int)(100 * 1.5), 4);
  tft.drawString("Relay HIGH Level Trigger", SCREEN_WIDTH / 2, (int)(150 * 1.5), 2);
  tft.drawString("Loading...", SCREEN_WIDTH / 2, (int)(200 * 1.5), 2);
  
  delay(2000);
}

// =====================================================
// DISPLAY BUTTON IMAGE & LABEL
// =====================================================
void displayButtonImage(ImageButton &btn) {
  String filename = btn.state ? btn.imageOnPath : btn.imageOffPath;

  Serial.println("\n--- Menampilkan Gambar ---");
  Serial.println("File: " + filename);
  Serial.println("State: " + String(btn.state ? "ON" : "OFF"));

  if (filename.isEmpty() || !SPIFFS.exists(filename)) {
    Serial.println("ERROR: File tidak ada: " + filename);
    return;
  }

  // =====================================================
  // SET JPG DECODER
  // =====================================================
  TJpgDec.setJpgScale(btn.scale);
  TJpgDec.setSwapBytes(true);
  TJpgDec.setCallback(tft_jpg_render);

  // =====================================================
  // CLEAR BUTTON AREA
  // =====================================================
  tft.fillRect(btn.x - 5, btn.y - 5, btn.w + 10, btn.h + 60, TFT_BLACK);

  // =====================================================
  // DISPLAY JPG IMAGE
  // =====================================================
  TJpgDec.drawFsJpg(btn.x, btn.y, filename.c_str());
  Serial.println("Gambar ditampilkan di: (" + String(btn.x) + ", " + String(btn.y) + ")");

  // =====================================================
  // DRAW LABEL (ON / OFF)
  // =====================================================
  int labelY = btn.y + btn.h + 15;
  
  tft.setTextDatum(MC_DATUM);
  tft.setTextSize(2);
  tft.setTextColor(btn.state ? TFT_GREEN : TFT_RED, TFT_BLACK);

  String labelText = btn.state ? "ON" : "OFF";
  int labelX = btn.x + btn.w / 2;

  tft.drawString(labelText, labelX, labelY, 2);
  Serial.println("Label: " + labelText);

  // =====================================================
  // RELAY CONTROL - HIGH LEVEL TRIGGER
  // =====================================================
  if (btn.state) {
    digitalWrite(RELAY_PIN, HIGH);  // ON - nyalakan relay
    Serial.println("Relay NYALA (HIGH) - Pin 25 = 3.3V");
  } else {
    digitalWrite(RELAY_PIN, LOW);   // OFF - matikan relay
    Serial.println("Relay MATI (LOW) - Pin 25 = 0V");
  }

  Serial.println("---");
}

// =====================================================
// LOAD IMAGE FROM SPIFFS
// =====================================================
void loadImageInfo(ImageButton &btn) {
  Serial.println("\n--- Loading Button Image ---");
  
  String filename = btn.imageOffPath;
  
  if (!SPIFFS.exists(filename)) {
    Serial.println("ERROR: File tidak ada: " + filename);
    return;
  }

  uint16_t w, h;
  if (TJpgDec.getFsJpgSize(&w, &h, filename.c_str()) != JDR_OK) {
    Serial.println("ERROR: Gagal membaca ukuran JPG");
    return;
  }

  Serial.println("Ukuran asli gambar: " + String(w) + " x " + String(h));

  // =====================================================
  // AUTO SCALE
  // =====================================================
  uint8_t scale = 0;
  while (scale < 3) {
    if ((w >> scale) <= 220 && (h >> scale) <= 200) {
      break;
    }
    scale++;
  }

  btn.scale = scale;
  btn.w = w >> scale;
  btn.h = h >> scale;

  Serial.println("Scale factor: " + String(scale));
  Serial.println("Ukuran akhir: " + String(btn.w) + " x " + String(btn.h));
  Serial.println("Posisi button: (" + String(btn.x) + ", " + String(btn.y) + ")");
}

// =====================================================
// CHECK IF TOUCH IS INSIDE BUTTON
// =====================================================
bool isTouchInsideButton(uint16_t touchX, uint16_t touchY, ImageButton &btn) {
  return (touchX >= btn.x && touchX <= (btn.x + btn.w) &&
          touchY >= btn.y && touchY <= (btn.y + btn.h));
}

// =====================================================
// HANDLE TOUCH
// =====================================================
void checkTouch() {
  if (!touchEnabled) return;

  if (ts.touched()) {
    TS_Point p = ts.getPoint();

    if (p.z > TOUCH_PRESSURE_THRESHOLD) {
      if (!isTouching && (millis() - lastTouchTime >= TOUCH_DEBOUNCE_DELAY)) {
        lastTouchTime = millis();
        isTouching = true;

        beepBuzzer();

        uint16_t touchX = mapX(p.x);
        uint16_t touchY = mapY(p.y);

        touchX = constrain(touchX, 0, SCREEN_WIDTH - 1);
        touchY = constrain(touchY, 0, SCREEN_HEIGHT - 1);

        Serial.println("\nTouch at: (" + String(touchX) + ", " + String(touchY) + ")");

        // =====================================================
        // CHECK IF TOUCH INSIDE BUTTON
        // =====================================================
        if (isTouchInsideButton(touchX, touchY, button1)) {
          Serial.println(">>> BUTTON DITEKAN!");
          button1.state = !button1.state;
          
          Serial.println("Button state berubah menjadi: " + String(button1.state ? "ON" : "OFF"));
          
          displayButtonImage(button1);
          
          // Debounce: wait for finger lift
          delay(500);
          while (ts.touched()) {
            delay(50);
          }
          delay(300);
        }
      }
    } else {
      isTouching = false;
    }
  } else {
    isTouching = false;
  }
}

// =====================================================
// DRAW HOME SCREEN
// =====================================================
void drawHomeScreen() {
  tft.fillScreen(TFT_BLACK);

  tft.setTextColor(TFT_WHITE, TFT_BLACK);
  tft.setTextDatum(MC_DATUM);
  tft.setTextSize(1);

  //tft.drawString("IMAGE BUTTON WITH RELAY", SCREEN_WIDTH / 2, 30, 4);
  //tft.drawString("PIN 25 - HIGH Level Trigger", SCREEN_WIDTH / 2, 70, 2);

  // Display button
  displayButtonImage(button1);

  // Instructions
  tft.setTextSize(1);
  tft.setTextColor(TFT_YELLOW, TFT_BLACK);
  //tft.drawString("Sentuh gambar untuk ON/OFF", SCREEN_WIDTH / 2, SCREEN_HEIGHT - 40, 1);

  tft.setTextDatum(TL_DATUM);
}

// =====================================================
// SETUP
// =====================================================
void setup() {
  Serial.begin(115200);
  delay(1000);

  Serial.println("\n\n=== IMAGE BUTTON PROGRAM START ===\n");

  // =====================================================
  // SPIFFS INITIALIZATION
  // =====================================================
  if (!SPIFFS.begin(true)) {
    Serial.println("ERROR: SPIFFS gagal mount");
    return;
  }
  Serial.println("✓ SPIFFS berhasil di-mount");

  // =====================================================
  // TFT INITIALIZATION
  // =====================================================
  tft.init();
  tft.setRotation(0);
  tft.fillScreen(TFT_BLACK);
  Serial.println("✓ TFT berhasil diinisialisasi");

  // =====================================================
  // JPG DECODER INITIALIZATION
  // =====================================================
  TJpgDec.setCallback(tft_jpg_render);
  TJpgDec.setSwapBytes(true);
  TJpgDec.setJpgScale(0);
  Serial.println("✓ JPG Decoder siap");

  // =====================================================
  // TOUCHSCREEN INITIALIZATION
  // =====================================================
  if (!ts.begin()) {
    touchEnabled = false;
    Serial.println("WARNING: Touchscreen tidak terdeteksi!");
  } else {
    ts.setRotation(0);
    Serial.println("✓ Touchscreen siap");
  }

  // =====================================================
  // RELAY PIN INITIALIZATION
  // =====================================================
  pinMode(RELAY_PIN, OUTPUT);
  digitalWrite(RELAY_PIN, LOW);  // Initial state: OFF
  Serial.println("✓ Relay di pin " + String(RELAY_PIN) + " siap (awal: LOW/OFF)");

  // =====================================================
  // SPI INITIALIZATION
  // =====================================================
  SPI.begin();
  Serial.println("✓ SPI berhasil diinisialisasi");

  // =====================================================
  // BOOT SCREEN
  // =====================================================
  drawBootScreen();

  // =====================================================
  // LOAD BUTTON IMAGE INFO
  // =====================================================
  loadImageInfo(button1);

  // =====================================================
  // DRAW HOME SCREEN
  // =====================================================
  drawHomeScreen();

  Serial.println("\n=== READY - SIAP SENTUH GAMBAR ===\n");
}

// =====================================================
// MAIN LOOP
// =====================================================
void loop() {
  checkTouch();
  delay(10);
}

13. Penutup


Membuat tombol touchscreen ON/OFF pada TFT ILI9488 menggunakan ESP32 merupakan langkah awal yang sangat bagus untuk mempelajari sistem HMI modern.
Melalui proyek ini kita belajar banyak hal sekaligus mulai dari touchscreen, SPIFFS, tampilan grafis, hingga kontrol relay.

Setelah memahami dasar proyek ini, Anda dapat mengembangkan sistem menjadi jauh lebih kompleks seperti smart home berbasis WiFi, panel monitoring sensor, hingga dashboard IoT profesional.

Semoga artikel ini membantu dan dapat menjadi referensi untuk proyek ESP32 berikutnya.

Baca juga : Cara Mengaktifkan Bluetooth ESP32 dengan ESP-IDF (Lengkap untuk Pemula)


web blog tempat sharing berbagai informasi dan trik serta tips seputar laptop komputer dan elektronika
carapaklek dot com... Welcome to WhatsApp chat
Howdy! How can we help you today?
Type here...