1. Apa Itu GPIO?
GPIO singkatan dari General-Purpose Input/Output, yaitu pin-pin pada mikrokontroler yang bisa dikonfigurasi secara fleksibel sebagai input (menerima sinyal dari luar) atau output (mengirim sinyal ke komponen eksternal). GPIO adalah jantung dari hampir semua proyek embedded — dari menyalakan LED sederhana sampai mengendalikan robot kompleks.
ESP32 memiliki hingga 34 pin GPIO yang dilabeli dari GPIO 0 sampai GPIO 39. Namun, tidak semua pin bisa digunakan secara bebas. Beberapa pin sudah dialokasikan untuk fungsi internal seperti flash memory, ADC internal, atau bootstrapping. Kita akan membahas detailnya di bagian pin mapping.
Setiap pin GPIO ESP32 beroperasi pada level tegangan 3.3V. Jangan pernah memberikan tegangan 5V langsung ke pin GPIO — bisa merusak chip! Gunakan voltage divider atau level shifter jika perlu berkomunikasi dengan perangkat 5V.
2. Pin Mapping & Kapabilitas ESP32
Memahami pin mapping adalah langkah pertama sebelum mulai coding. Berikut tabel ringkasan pin GPIO ESP32 beserta kapabilitas dan batasannya:
| Pin GPIO | Fungsi | Catatan |
|---|---|---|
| GPIO 0 | Input/Output | Bootstrapping — harus LOW saat boot |
| GPIO 1 | TX0 (UART) | Digunakan untuk serial output |
| GPIO 2 | Input/Output | LED bawaan pada beberapa board |
| GPIO 3 | RX0 (UART) | Digunakan untuk serial input |
| GPIO 4–15 | Input/Output | General purpose, aman digunakan |
| GPIO 6–11 | Flash SPI | ❌ JANGAN DIGUNAKAN — terhubung ke flash internal |
| GPIO 12 | Input/Output | Bootstrapping — harus LOW saat boot |
| GPIO 13–19 | Input/Output | General purpose, aman digunakan |
| GPIO 20–23 | Input/Output | General purpose, aman digunakan |
| GPIO 25–26 | Input/Output + DAC | DAC1 dan DAC2 |
| GPIO 27 | Input/Output | General purpose |
| GPIO 32–33 | Input Only + ADC | ADC1_CH4 & ADC1_CH5 |
| GPIO 34–39 | Input Only | ⚠️ Tidak bisa dikonfigurasi sebagai output |
GPIO 6–11 terhubung ke modul flash SPI internal. Menggunakan pin ini dapat menyebabkan crash atau brick pada ESP32. GPIO 34–39 hanya bisa sebagai input — tidak memiliki driver output internal. Selalu cek datasheet sebelum memilih pin!
3. Mode Output: Menyalakan LED
Mode OUTPUT digunakan ketika ESP32 perlu mengontrol komponen eksternal. Cara paling sederhana untuk memahami output adalah dengan menyalakan dan mematikan LED.
Konfigurasi Pin sebagai Output
Di Arduino framework, kita menggunakan fungsi pinMode() untuk mengatur arah pin, dan digitalWrite() untuk mengatur level HIGH (3.3V) atau LOW (0V):
// === Program Blink LED pada ESP32 ===
// Hubungkan LED ke GPIO 2 dengan resistor 220Ω
#define LED_PIN 2 // GPIO 2 (LED bawaan pada banyak board ESP32)
void setup() {
pinMode(LED_PIN, OUTPUT); // Atur GPIO 2 sebagai OUTPUT
}
void loop() {
digitalWrite(LED_PIN, HIGH); // Nyalakan LED (3.3V)
delay(1000); // Tunggu 1 detik
digitalWrite(LED_PIN, LOW); // Matikan LED (0V)
delay(1000); // Tunggu 1 detik
}
Cara Kerja
pinMode(LED_PIN, OUTPUT)— Mengkonfigurasi GPIO 2 sebagai output. Setelah ini, pin bisa mengeluarkan tegangan HIGH atau LOW.digitalWrite(LED_PIN, HIGH)— Mengatur pin ke 3.3V, LED menyala.digitalWrite(LED_PIN, LOW)— Mengatur pin ke 0V, LED mati.delay(1000)— Menunda eksekusi selama 1000 milidetik (1 detik).
Selalu gunakan resistor pembatas arus (220Ω–330Ω) secara seri dengan LED. Tanpa resistor, arus berlebih bisa merusak LED dan pin GPIO ESP32. Rumus: R = (Vsupply - VLED) / ILED
4. Mode Input: Membaca Tombol
Mode INPUT digunakan untuk membaca status dari komponen eksternal seperti tombol (push button), saklar (switch), atau sensor digital. Saat pin dikonfigurasi sebagai input, ESP32 akan membaca apakah pin tersebut dalam keadaan HIGH (3.3V) atau LOW (0V).
Skema Dasar Tombol
// === Membaca Tombol Push Button ===
// Tombol terhubung ke GPIO 4, LED ke GPIO 2
#define BUTTON_PIN 4
#define LED_PIN 2
void setup() {
Serial.begin(115200);
pinMode(BUTTON_PIN, INPUT_PULLUP); // Gunakan pull-up internal
pinMode(LED_PIN, OUTPUT);
}
void loop() {
int tombolState = digitalRead(BUTTON_PIN);
if (tombolState == LOW) {
// Tombol ditekan (terhubung ke GND karena pull-up)
digitalWrite(LED_PIN, HIGH);
Serial.println("Tombol DITEKAN - LED ON");
} else {
// Tombol tidak ditekan
digitalWrite(LED_PIN, LOW);
Serial.println("Tombol dilepas - LED OFF");
}
delay(50); // Debounce sederhana
}
Perhatikan penggunaan INPUT_PULLUP di atas — ini mengaktifkan resistor pull-up internal ESP32 sehingga pin secara default berada di HIGH saat tombol tidak ditekan. Saat tombol ditekan, pin terhubung ke GND dan pembacaan menjadi LOW. Ini akan dijelaskan lebih detail di bagian berikutnya.
5. Pull-Up & Pull-Down Resistor
Saat membaca tombol atau sensor digital, kita perlu memastikan pin GPIO selalu berada dalam keadaan yang terdefinisi (HIGH atau LOW), bahkan saat tombol tidak ditekan. Tanpa pull-up atau pull-down resistor, pin bisa mengambang (floating) dan menghasilkan pembacaan yang tidak stabil.
Apa Itu Pull-Up Resistor?
Pull-up resistor menghubungkan pin ke tegangan VCC (3.3V) secara default. Saat tombol tidak ditekan, pin terbaca HIGH. Saat tombol ditekan (terhubung ke GND), pin terbaca LOW.
Apa Itu Pull-Down Resistor?
Kebalikan dari pull-up, pull-down resistor menghubungkan pin ke GND secara default. Saat tombol tidak ditekan, pin terbaca LOW. Saat tombol ditekan (terhubung ke VCC), pin terbaca HIGH.
Pull-Up Internal vs Eksternal
ESP32 memiliki resistor pull-up dan pull-down internal (sekitar 45kΩ) yang bisa diaktifkan melalui software. Ini sangat nyaman karena tidak perlu menambahkan resistor fisik:
// Mengaktifkan pull-up internal
pinMode(pin, INPUT_PULLUP);
// Mengaktifkan pull-down internal
pinMode(pin, INPUT_PULLDOWN);
// Input biasa (tanpa pull-up/pull-down, perlu resistor eksternal)
pinMode(pin, INPUT);
| Mode | Resistor | Default State | Kapan Digunakan |
|---|---|---|---|
| INPUT | Tidak ada | Floating | Sudah ada resistor eksternal |
| INPUT_PULLUP | ~45kΩ ke VCC | HIGH | Tombol aktif LOW (ke GND) |
| INPUT_PULLDOWN | ~45kΩ ke GND | LOW | Tombol aktif HIGH (ke VCC) |
6. Interrupt: Deteksi Perubahan Tanpa Polling
Dalam program biasa, kita terus-menerus membaca pin GPIO di dalam loop() (disebut polling). Ini sederhana tapi boros CPU. Interrupt memungkinkan ESP32 bereaksi terhadap perubahan pada pin GPIO secara instan tanpa harus mengecek pin terus-menerus.
Kapan Menggunakan Interrupt?
- Tombol yang harus dideteksi secepat mungkin
- Encoder rotary yang menghasilkan pulsa cepat
- Sensor gerak (PIR) yang memberikan sinyal singkat
- Sistem keamanan yang harus responsif 24/7
Macam-Macam Trigger Interrupt
| Trigger | Keterangan |
|---|---|
RISING | Saat sinyal berubah dari LOW ke HIGH |
FALLING | Saat sinyal berubah dari HIGH ke LOW |
CHANGE | Saat sinyal berubah apapun (LOW→HIGH atau HIGH→LOW) |
LOW | Selama sinyal dalam keadaan LOW |
HIGH | Selama sinyal dalam keadaan HIGH |
Contoh: Interrupt pada Tombol
// === Interrupt untuk mendeteksi tombol ===
#define BUTTON_PIN 4
#define LED_PIN 2
volatile bool tombolPressed = false; // volatile karena diubah di ISR
// ISR (Interrupt Service Routine) - harus sesingkat mungkin
void IRAM_ATTR onButtonPress() {
tombolPressed = true; // Hanya set flag, jangan Serial.print di sini!
}
void setup() {
Serial.begin(115200);
pinMode(LED_PIN, OUTPUT);
pinMode(BUTTON_PIN, INPUT_PULLUP);
// Pasang interrupt pada FALLING edge (saat tombol ditekan)
attachInterrupt(digitalPinToInterrupt(BUTTON_PIN),
onButtonPress,
FALLING);
Serial.println("Interrupt siap. Tekan tombol!");
}
void loop() {
if (tombolPressed) {
tombolPressed = false; // Reset flag
digitalWrite(LED_PIN, !digitalRead(LED_PIN)); // Toggle LED
Serial.println("Tombol ditekan! LED ditoggle.");
}
// Program lain bisa jalan di sini tanpa terganggu polling
delay(10);
}
ISR (Interrupt Service Routine) harus sesingkat mungkin. Jangan gunakan Serial.print(), delay(), atau operasi berat lainnya di dalam ISR. Cukup set flag boolean dan proses di loop(). Gunakan keyword volatile untuk variabel yang diakses dari ISR. Gunakan IRAM_ATTR agar ISR ditempatkan di RAM untuk akses cepat.
7. PWM: Mengatur Kecerahan & Kecepatan Motor
PWM (Pulse Width Modulation) adalah teknik untuk meniru sinyal analog menggunakan sinyal digital. Dengan mengatur lebar pulsa (duty cycle), kita bisa mengontrol kecerahan LED, kecepatan motor, posisi servo, dan banyak lagi.
ESP32 memiliki peripheral LEDC (LED Control) yang sangat fleksibel dengan 16 channel PWM (0–15), frekuensi dan resolusi yang bisa diatur.
Cara Kerja PWM
Kode PWM Sederhana
// === Mengatur Kecerahan LED dengan PWM ===
#define LED_PIN 2
#define PWM_CHANNEL 0
#define PWM_FREQ 5000 // Frekuensi 5 kHz
#define PWM_RESOLUTION 8 // Resolusi 8-bit (0-255)
void setup() {
// Konfigurasi channel PWM
ledcSetup(PWM_CHANNEL, PWM_FREQ, PWM_RESOLUTION);
// Hubungkan channel PWM ke pin GPIO
ledcAttachPin(LED_PIN, PWM_CHANNEL);
}
void loop() {
// Nyalakan LED secara bertahap (fade in)
for (int duty = 0; duty <= 255; duty++) {
ledcWrite(PWM_CHANNEL, duty);
delay(10);
}
// Matikan LED secara bertahap (fade out)
for (int duty = 255; duty >= 0; duty--) {
ledcWrite(PWM_CHANNEL, duty);
delay(10);
}
}
Parameter PWM ESP32
| Parameter | Nilai | Keterangan |
|---|---|---|
| Channel | 0 – 15 | 16 channel tersedia, bisa dipetakan ke pin manapun |
| Frekuensi | 1 Hz – 40 MHz | Servo: 50Hz, LED: 1-5kHz, Motor: 20kHz+ |
| Resolusi | 1 – 16 bit | 8-bit = 0-255, 10-bit = 0-1023, 16-bit = 0-65535 |
8. Proyek: Lampu Lalu Lintas Otomatis
Sekarang kita gabungkan semua materi di atas ke dalam satu proyek nyata: simulasi lampu lalu linta dengan 3 LED (merah, kuning, hijau), tombol untuk mode manual, dan buzzer untuk pejalan kaki.
Skema Rangkaian
| Komponen | Pin GPIO | Keterangan |
|---|---|---|
| LED Merah | GPIO 25 | Dengan resistor 220Ω |
| LED Kuning | GPIO 26 | Dengan resistor 220Ω |
| LED Hijau | GPIO 27 | Dengan resistor 220Ω |
| Tombol Pejalan Kaki | GPIO 4 | INPUT_PULLUP (aktif LOW) |
| Buzzer | GPIO 32 | PWM output untuk nada |
Kode Program Lengkap
// ============================================
// PROYEK: Lampu Lalu Lintas Otomatis ESP32
// Fitur:
// - 3 LED (merah, kuning, hijau) siklus otomatis
// - Tombol pejalan kaki dengan interrupt
// - Buzzer berbunyi saat hijau (pejalan kaki boleh jalan)
// - Kode bersih dan terstruktur
// ============================================
// === PIN DEFINITIONS ===
#define LED_MERAH 25
#define LED_KUNING 26
#define LED_HIJAU 27
#define BTN_PEJALAN 4
#define BUZZER_PIN 32
// === PWM UNTUK BUZZER ===
#define BUZZER_CHANNEL 0
#define BUZZER_FREQ 1000 // 1 kHz
#define BUZZER_RES 8
// === DURASI DALAM MILIDETIK ===
#define WAKTU_HIJAU 5000 // 5 detik hijau
#define WAKTU_KUNING 2000 // 2 detik kuning
#define WAKTU_MERAH 5000 // 5 detik merah
#define WAKTU_BUZZER 500 // 0.5 detik buzzer
// === GLOBAL VARIABLES ===
volatile bool mintaPejalanKaki = false;
unsigned long lastDebounce = 0;
// === ISR: Tombol Pejalan Kaki ===
void IRAM_ATTR onPejalanPress() {
unsigned long now = millis();
if (now - lastDebounce > 300) { // Debounce 300ms
mintaPejalanKaki = true;
lastDebounce = now;
}
}
// === FUNGSI HELPER ===
void setLampu(bool merah, bool kuning, bool hijau) {
digitalWrite(LED_MERAH, merah ? HIGH : LOW);
digitalWrite(LED_KUNING, kuning ? HIGH : LOW);
digitalWrite(LED_HIJAU, hijau ? HIGH : LOW);
}
void bunyibuzzer(int durasi, int freq) {
ledcWriteTone(BUZZER_CHANNEL, freq);
delay(durasi);
ledcWriteTone(BUZZER_CHANNEL, 0); // Matikan buzzer
}
void prosesPejalanKaki() {
// Kedipkan LED merah dan bunyikan buzzer
for (int i = 0; i < 3; i++) {
setLampu(true, false, false);
bunyibuzzer(WAKTU_BUZZER, 1500);
setLampu(false, false, false);
delay(300);
}
mintaPejalanKaki = false; // Reset flag
}
void setup() {
Serial.begin(115200);
// Setup LED sebagai output
pinMode(LED_MERAH, OUTPUT);
pinMode(LED_KUNING, OUTPUT);
pinMode(LED_HIJAU, OUTPUT);
// Setup tombol dengan pull-up internal
pinMode(BTN_PEJALAN, INPUT_PULLUP);
attachInterrupt(digitalPinToInterrupt(BTN_PEJALAN),
onPejalanPress, FALLING);
// Setup buzzer PWM
ledcSetup(BUZZER_CHANNEL, BUZZER_FREQ, BUZZER_RES);
ledcAttachPin(BUZZER_PIN, BUZZER_CHANNEL);
Serial.println("=== Sistem Lampu Lalu Lintas Aktif ===");
Serial.println("Tekan tombol untuk mode pejalan kaki.");
}
void loop() {
// FASE 1: HIJAU (kendaraan jalan)
Serial.println("🟢 HIJAU - Kendaraan jalan");
setLampu(false, false, true);
unsigned long startHijau = millis();
while (millis() - startHijau < WAKTU_HIJAU) {
if (mintaPejalanKaki) {
Serial.println("🚶 Pejalan kaki menekan tombol!");
prosesPejalanKaki();
break; // Langsung ke fase kuning
}
delay(10);
}
// FASE 2: KUNING (bersiap berhenti)
Serial.println("🟡 KUNING - Bersiap berhenti");
setLampu(false, true, false);
delay(WAKTU_KUNING);
// FASE 3: MERAH (kendaraan berhenti)
Serial.println("🔴 MERAH - Kendaraan berhenti");
setLampu(true, false, false);
delay(WAKTU_MERAH);
}
Program ini menggunakan interrupt pada tombol pejalan kaki agar responsif kapan saja. Fungsi IRAM_ATTR memastikan ISR ada di RAM. Flag volatile bool menandakan tombol ditekan, kemudian diproses di loop(). PWM digunakan untuk menghasilkan nada buzzer. Siklus lampu berjalan otomatis dan bisa diinterupsi oleh tombol.
9. Tips & Peringatan Penting
| Tips / Peringatan | Penjelasan |
|---|---|
| ⚠️ Jangan pakai GPIO 6-11 | Pin ini terhubung ke flash SPI internal. Menggunakannya bisa crash atau brick ESP32. |
| ⚠️ GPIO 34-39 input only | Tidak bisa dikonfigurasi sebagai output, tidak punya pull-up/pull-down internal. |
| 💡 Gunakan LEDC untuk PWM | Jangan gunakan analogWrite() — ESP32 tidak mendukungnya. Gunakan fungsi ledcSetup() dan ledcWrite(). |
| 💡 Debounce tombol | Tombol fisik menghasilkan noise saat ditekan. Selalu gunakan debounce (software atau hardware) untuk pembacaan yang stabil. |
| ⚠️ Arus maksimum GPIO | Setiap pin GPIO ESP32 maksimum mendorong 12mA (rekomendasi 6mA). Untuk mengendalikan motor atau LED strip, gunakan transistor atau MOSFET. |
| 💡 Serial Monitor | Gunakan Serial.begin(115200) untuk debug. Ini sangat membantu saat troubleshooting masalah GPIO. |
10. Quiz: Uji Pemahamanmu!
Setelah membaca tutorial di atas, jawablah 5 pertanyaan berikut untuk menguji pemahamanmu tentang GPIO ESP32: