Belajar ESP32: Serial Communication

Selama seri artikel Belajar ESP32 ini, kita sudah beberapa kali berurusan dengan protokol komunikasi — mulai dari I2C untuk menghubungkan sensor BMP280, I2C lagi untuk layar OLED, hingga UART yang digunakan setiap kali kita melihat output di Serial Monitor. Namun kita belum pernah membahasnya secara menyeluruh dan terstruktur.

Di artikel ini, kita akan membahas tiga protokol komunikasi serial yang paling sering digunakan pada ESP32: UART, SPI, dan I2C. Ketiganya adalah fondasi dari hampir semua proyek embedded dan IoT yang melibatkan sensor, layar, modul komunikasi, maupun komunikasi antar-perangkat.


Mengapa “Serial” Communication?

Sebelum masuk ke masing-masing protokol, ada baiknya kita pahami dulu apa yang dimaksud dengan komunikasi serial.

Komunikasi data dapat dilakukan dengan dua cara:

  • Paralel — mengirim banyak bit sekaligus melalui banyak kabel secara bersamaan. Cepat, tapi membutuhkan banyak pin.
  • Serial — mengirim satu bit data pada satu waktu melalui satu jalur data. Lebih lambat per-siklus, tapi jauh lebih hemat pin dan lebih cocok untuk jarak yang lebih jauh.

Dalam dunia embedded dan IoT, komunikasi serial menjadi standar karena keterbatasan jumlah pin pada mikrokontroler dan kebutuhan untuk menghubungkan banyak perangkat dengan kabel seminimal mungkin.

ESP32 mendukung beberapa protokol komunikasi serial, dan tiga yang paling umum adalah:

ProtokolTipeJumlah Kabel DataTopologi
UARTAsinkron2 (TX, RX)Point-to-point
SPISinkron4 (MOSI, MISO, SCK, CS)Master-Slave
I2CSinkron2 (SDA, SCL)Multi-device Bus

Bagian 1: UART (Universal Asynchronous Receiver-Transmitter)

Apa Itu UART?

UART adalah protokol komunikasi serial yang paling sederhana dan paling tua. Kata kunci di sini adalah asinkron (asynchronous) — artinya UART tidak menggunakan sinyal clock bersama antara pengirim dan penerima. Sebagai gantinya, kedua pihak harus sepakat terlebih dahulu mengenai baud rate (kecepatan pengiriman data dalam bit per detik).

UART menggunakan dua jalur data:

  • TX (Transmit) — jalur pengiriman data keluar
  • RX (Receive) — jalur penerimaan data masuk

Karena komunikasi berjalan dua arah secara independen melalui dua jalur terpisah, UART bersifat full-duplex — data bisa dikirim dan diterima pada waktu yang bersamaan.

Cara Kerja UART

Ketika UART mengirim data, ia membungkus data dalam sebuah frame yang terdiri dari:

  1. Start bit (selalu LOW) — menandakan awal transmisi
  2. Data bits (biasanya 8 bit) — isi data yang dikirim
  3. Parity bit (opsional) — bit untuk deteksi error
  4. Stop bit (selalu HIGH) — menandakan akhir frame

Karena tidak ada sinyal clock, kedua perangkat harus dikonfigurasi dengan baud rate yang sama persis. Jika baud rate tidak cocok, data yang diterima akan rusak atau tidak terbaca.

Baud rate umum yang sering digunakan: 9600, 19200, 38400, 57600, 115200, 230400, 460800

Untuk keperluan debugging dan serial monitor di Arduino IDE, baud rate 115200 adalah yang paling umum digunakan pada ESP32.

UART pada ESP32

image
https://lastminuteengineers.com/esp32-pinout-reference/#esp32-uart-pins

ESP32 memiliki tiga interface UART hardware: UART0, UART1, dan UART2.

UARTFungsi UtamaDefault TXDefault RX
UART0Serial Monitor & UploadGPIO 1GPIO 3
UART1Tersedia (hati-hati*)GPIO 10GPIO 9
UART2Tersedia untuk perangkat eksternalGPIO 17GPIO 16

Perhatian untuk UART1: GPIO 9 dan GPIO 10 pada banyak modul ESP32 terhubung ke flash memory internal, sehingga tidak bisa digunakan langsung. Untuk menggunakan UART1, kita harus mendefinisikan ulang pinnya secara manual.

Salah satu keunggulan ESP32 adalah GPIO Matrix — hampir semua pin GPIO bisa dipetakan ulang untuk fungsi apapun, termasuk UART. Jadi kita bebas memilih pin mana yang digunakan untuk TX dan RX, selama pin tersebut bisa berfungsi sebagai output/input.

Latihan 1: UART0 — Komunikasi dengan Serial Monitor

Ini adalah contoh paling dasar: ESP32 mengirim data ke Serial Monitor, dan Serial Monitor bisa mengirim perintah balik ke ESP32.

String pesanMasuk = "";

void setup() {
  // Inisialisasi UART0 (Serial Monitor) dengan baud rate 115200
  Serial.begin(115200);
  Serial.println("ESP32 siap. Ketik perintah dan tekan Enter.");
}

void loop() {
  // Baca data dari Serial Monitor jika ada
  while (Serial.available() > 0) {
    char c = Serial.read();
    if (c == '\n') {
      // Saat menerima newline, proses pesan yang sudah terkumpul
      Serial.print("Kamu mengirim: ");
      Serial.println(pesanMasuk);
      pesanMasuk = "";  // reset buffer
    } else {
      pesanMasuk += c;
    }
  }
}

Upload kode ini, buka Serial Monitor (baud rate 115200), dan ketik apapun lalu tekan Enter. ESP32 akan memantulkan kembali apa yang Anda ketik.

uart 1

Tips: Pastikan di Serial Monitor dipilih opsi “Newline” atau “Both NL & CR” pada dropdown di bagian bawah, agar karakter \n terdeteksi dengan benar.

Latihan 2: UART2 — Komunikasi Antar Perangkat

image

Contoh ini menunjukkan cara menggunakan UART2 untuk berkomunikasi dengan perangkat eksternal (misalnya modul GPS, Bluetooth module, atau Arduino lain). Kita menggunakan library HardwareSerial.

/// ESP32 ///
#include <HardwareSerial.h>

// Deklarasikan objek HardwareSerial untuk UART2
HardwareSerial SerialDevice(2);

void setup() {
// UART0: untuk Serial Monitor
Serial.begin(115200);

// UART2: untuk perangkat eksternal
// Parameter: baud rate, config, RX pin, TX pin
SerialDevice.begin(9600, SERIAL_8N1, 16, 17);

Serial.println("UART2 siap. Menunggu data dari perangkat...");
}

void loop() {
// Jika ada data masuk dari perangkat eksternal (UART2)
if (SerialDevice.available()) {
String data = SerialDevice.readStringUntil('\n');
Serial.print("[UART2 Received]: ");
Serial.println(data);
}

// Jika ada perintah dari Serial Monitor, kirim ke perangkat
if (Serial.available()) {
String perintah = Serial.readStringUntil('\n');
SerialDevice.println(perintah);
Serial.print("[Sent to device]: ");
Serial.println(perintah);
}
}

Format begin():

SerialDevice.begin(baud_rate, config, RX_pin, TX_pin);
  • baud_rate — kecepatan komunikasi, harus sama dengan perangkat yang dihubungkan
  • config — format frame data; SERIAL_8N1 berarti 8 data bits, No parity, 1 stop bit (paling umum)
  • RX_pin — pin untuk menerima data
  • TX_pin — pin untuk mengirim data

Ingat: Saat menghubungkan dua perangkat via UART, TX satu perangkat harus terhubung ke RX perangkat lain, dan sebaliknya (TX ↔ RX, bukan TX ↔ TX).

/// Arduino Uno R3 ///
void setup() {
Serial.begin(9600);
}

void loop() {
// Terima dari ESP32
if (Serial.available()) {
String data = Serial.readStringUntil('\n');
Serial.print("[Dari ESP32]: ");
Serial.println(data);
}

// Kirim ke ESP32
Serial.println("Halo dari Arduino");
delay(10000);
}

Tampilan Serial Monitor: ESP32

17:45:06.264 -> [UART2 Received]: Halo dari Arduino
17:45:08.448 -> [Sent to device]: Halo dari ESP32
17:45:16.259 -> [UART2 Received]: [Dari ESP32]: Halo dari ESP32
17:45:16.296 -> [UART2 Received]: Halo dari Arduino
17:45:26.273 -> [UART2 Received]: Halo dari Arduino

Tampilan Serial Monitor: Arduino Uno R3

17:45:06.219 -> Halo dari Arduino
17:45:16.226 -> [Dari ESP32]: Halo dari ESP32
17:45:16.257 -> Halo dari Arduino
17:45:26.272 -> Halo dari Arduino
17:45:36.236 -> Halo dari Arduino

Latihan 3: UART1 dengan Custom Pins

Jika Anda ingin menggunakan UART1 (misalnya karena UART2 sudah dipakai), kita bisa memetakan ulang pinnya secara manual.

#include <HardwareSerial.h>

HardwareSerial Serial1Custom(1);  // UART1

void setup() {
  Serial.begin(115200);

  // Pindahkan UART1 ke pin yang aman (bukan GPIO 9 & 10)
  Serial1Custom.begin(115200, SERIAL_8N1, 25, 26);  // RX=GPIO25, TX=GPIO26

  Serial.println("UART1 pada GPIO 25 (RX) dan 26 (TX) siap.");
}

void loop() {
  if (Serial1Custom.available()) {
    Serial.println(Serial1Custom.readString());
  }
}

Kapan Menggunakan UART?

UART paling cocok digunakan untuk:

  • Komunikasi dengan modul GPS (NMEA sentences)
  • Komunikasi dengan modul Bluetooth serial (HC-05, HC-06)
  • Debug dan monitoring via Serial Monitor
  • Komunikasi dengan mikrokontroler lain (Arduino, ESP8266, dsb)
  • Perangkat yang mengirim data berbasis baris teks

Bagian 2: SPI (Serial Peripheral Interface)

Apa Itu SPI?

SPI adalah protokol komunikasi serial sinkron — artinya ada sinyal clock bersama yang menyinkronkan pengiriman data antara master dan slave. Dengan adanya clock, data bisa dikirim jauh lebih cepat dibandingkan UART.

SPI menggunakan topologi Master-Slave, di mana satu master (ESP32) mengendalikan satu atau lebih slave. Yang membedakan SPI dari I2C adalah SPI menggunakan jalur data terpisah untuk kirim dan terima, sehingga bisa full-duplex di kecepatan tinggi.

Jalur Sinyal SPI

SPI memiliki empat jalur sinyal utama:

Nama PinKeterangan
MOSI (Master Out Slave In)Data dari master ke slave
MISO (Master In Slave Out)Data dari slave ke master
SCK / CLKClock yang dihasilkan oleh master
CS / SS (Chip Select / Slave Select)Dipilih LOW untuk mengaktifkan slave tertentu

Ketika master ingin berkomunikasi dengan slave tertentu, ia menarik pin CS slave tersebut ke LOW. Semua slave yang CS-nya HIGH akan mengabaikan sinyal di bus. Ini adalah cara SPI memilih perangkat dalam topologi multi-slave.

SPI pada ESP32

image
https://lastminuteengineers.com/esp32-pinout-reference/#esp32-spi-pins

ESP32 memiliki empat bus SPI, tetapi hanya dua yang bisa digunakan untuk perangkat eksternal:

BusNama LamaDefault MOSIDefault MISODefault SCKDefault CS
SPI2HSPIGPIO 13GPIO 12GPIO 14GPIO 15
SPI3VSPIGPIO 23GPIO 19GPIO 18GPIO 5

SPI0 dan SPI1 digunakan secara internal oleh ESP32 untuk komunikasi dengan flash memory — tidak bisa digunakan untuk perangkat eksternal.

Seperti halnya UART dan I2C, ESP32 juga mendukung pemetaan ulang pin SPI ke GPIO manapun melalui GPIO Matrix.

Latihan 1: Membaca Pin SPI Default

Cara mudah untuk mengecek pin SPI default yang digunakan ESP32 adalah dengan program berikut:

#include <SPI.h>

void setup() {
Serial.begin(115200);
delay(3000);

Serial.println("=== Pin SPI Default ESP32 ===");
Serial.print("MOSI: GPIO ");
Serial.println(MOSI);
Serial.print("MISO: GPIO ");
Serial.println(MISO);
Serial.print("SCK: GPIO ");
Serial.println(SCK);
Serial.print("SS: GPIO ");
Serial.println(SS);
}

void loop() {}

Output yang dihasilkan akan menunjukkan pin SPI default yang aktif pada board Anda. Pada ESP32 DOIT DevKit, biasanya SPI3 (VSPI) yang digunakan secara default dengan pin MOSI=23, MISO=19, SCK=18, SS=5.

image

Latihan 2: Menggunakan Custom SPI Pins

Jika pin default sudah terpakai atau ingin menggunakan pin lain, kita bisa mendefinisikan custom SPI instance:

#include <SPI.h>

// Definisikan pin SPI custom
#define MY_MOSI  23
#define MY_MISO  19
#define MY_SCK   18
#define MY_CS    5

SPIClass mySPI(VSPI);  // Gunakan bus VSPI (SPI3)

void setup() {
  Serial.begin(115200);

  // Inisialisasi SPI dengan pin custom
  mySPI.begin(MY_SCK, MY_MISO, MY_MOSI, MY_CS);

  // Set CS sebagai output dan idle HIGH
  pinMode(MY_CS, OUTPUT);
  digitalWrite(MY_CS, HIGH);

  Serial.println("SPI custom siap.");
}

void loop() {}

Latihan 3: Transfer Data via SPI

Berikut contoh cara mengirim dan menerima data melalui SPI. Contoh ini melakukan transfer byte tunggal dengan CS dikendalikan secara manual:

#include <SPI.h>

#define CS_PIN 5

SPIClass spi(VSPI);

void setup() {
  Serial.begin(115200);
  pinMode(CS_PIN, OUTPUT);
  digitalWrite(CS_PIN, HIGH);

  spi.begin();

  Serial.println("SPI siap. Memulai transfer data...");
}

byte spiTransfer(byte data) {
  byte received;

  digitalWrite(CS_PIN, LOW);   // Aktifkan slave
  received = spi.transfer(data); // Kirim dan terima 1 byte secara bersamaan
  digitalWrite(CS_PIN, HIGH);  // Nonaktifkan slave

  return received;
}

void loop() {
  byte response = spiTransfer(0xAB);  // Kirim byte 0xAB ke slave

  Serial.print("Sent: 0xAB | Received: 0x");
  Serial.println(response, HEX);

  delay(1000);
}

spi.transfer() adalah fungsi full-duplex — ia mengirim satu byte sekaligus menerima satu byte dari slave dalam satu operasi clock yang sama.

Latihan 4: Dua Perangkat SPI pada Satu Bus

Keunggulan SPI adalah kemampuannya menghubungkan beberapa slave pada satu bus yang sama — cukup bedakan pin CS-nya:

#include <SPI.h>

#define CS_DEVICE1  5
#define CS_DEVICE2  15

SPIClass spi(VSPI);

void kirimKeDevice(int csPin, byte data) {
  digitalWrite(csPin, LOW);
  spi.transfer(data);
  digitalWrite(csPin, HIGH);
}

void setup() {
  Serial.begin(115200);

  pinMode(CS_DEVICE1, OUTPUT);
  pinMode(CS_DEVICE2, OUTPUT);
  digitalWrite(CS_DEVICE1, HIGH);
  digitalWrite(CS_DEVICE2, HIGH);

  spi.begin();
  Serial.println("Dua perangkat SPI siap.");
}

void loop() {
  Serial.println("Kirim ke Device 1...");
  kirimKeDevice(CS_DEVICE1, 0x01);
  delay(500);

  Serial.println("Kirim ke Device 2...");
  kirimKeDevice(CS_DEVICE2, 0x02);
  delay(500);
}

Aturan penting multi-slave SPI: Hanya satu CS yang boleh LOW pada satu waktu. Saat berbicara dengan Device 1, CS Device 2 harus HIGH, dan sebaliknya.

Latihan 5: BMP280 via SPI

Jika pada artikel Belajar ESP32: External Sensor kita menggunakan komunikasi I2C, maka disini kita akan gunakan komunikasi SPI untuk membaca sensor BMP280.

image
ESP32 PinBMP280 Pin
VCCVCC
GNDGND
GPIO18 / SCKSCL
GPIO23 / MOSISDA
GPIO5 / SSCSB
GPIO19 / MISOSDO
#include <SPI.h>
#include <Adafruit_BMP280.h>

#define BMP_CS 5 // CSB
#define BMP_MOSI 23 // SDA
#define BMP_MISO 19 // SDO
#define BMP_SCK 18 // SCL

Adafruit_BMP280 bmp(BMP_CS, BMP_MOSI, BMP_MISO, BMP_SCK);

void setup() {
Serial.begin(115200);
delay(3000);

if (!bmp.begin()) {
Serial.println("BMP280 tidak ditemukan!");
while (1);
}

Serial.println("BMP280 SPI siap!");
}

void loop() {
Serial.println("---- BMP280 SPI ----");

Serial.print("Suhu: ");
Serial.print(bmp.readTemperature());
Serial.println(" °C");

Serial.print("Tekanan: ");
Serial.print(bmp.readPressure() / 100.0F);
Serial.println(" hPa");

Serial.println("--------------------");
delay(3000);
}
image

Kapan Menggunakan SPI?

SPI paling cocok digunakan untuk:

  • Perangkat berkecepatan tinggi seperti layar TFT, e-Paper display
  • Modul SD Card
  • ADC/DAC eksternal yang butuh presisi dan kecepatan
  • Sensor berperforma tinggi seperti IMU (MPU9250, ICM-20948)
  • Aplikasi yang butuh throughput data besar dan latensi rendah

Bagian 3: I2C (Inter-Integrated Circuit)

Apa Itu I2C?

I2C (dibaca “I-squared-C” atau “I-two-C”) adalah protokol komunikasi serial sinkron yang dirancang oleh Philips Semiconductors (sekarang NXP) khusus untuk menghubungkan banyak perangkat dalam satu bus menggunakan jumlah kabel yang minimal.

Seperti SPI, I2C juga menggunakan sinyal clock — tetapi I2C hanya memerlukan dua jalur saja untuk menghubungkan perangkat sebanyak apapun ke bus yang sama.

Pada artikel ini kita tidak akan membahas begitu mendalam mengenai praktik sensing menggunakan komunikasi I2C karena pada artikel-artikel sebelumnya semua praktik yang kita gunakan itu menggunakan jalum komunikasi I2C.

Jalur Sinyal I2C

Nama PinFungsi
SDA (Serial Data)Jalur pengiriman dan penerimaan data (bidirectional)
SCL (Serial Clock)Sinyal clock yang dihasilkan oleh master

Kedua jalur ini membutuhkan pull-up resistor ke tegangan VCC (biasanya 3.3V atau 5V). Kebanyakan modul sensor yang dijual sudah menyertakan pull-up resistor di papan modulnya, sehingga kita tidak perlu menambahkan resistor eksternal secara terpisah dalam kondisi normal.

Sistem Pengalamatan I2C

Inilah yang membuat I2C berbeda dari SPI: setiap perangkat pada bus I2C memiliki alamat unik berupa 7-bit angka (rentang 0x000x7F, atau 0 hingga 127). Master menggunakan alamat ini untuk memilih perangkat mana yang diajak berkomunikasi — tidak perlu pin CS terpisah untuk setiap perangkat.

Cara kerja komunikasi I2C secara ringkas:

  1. Master mengirim START condition
  2. Master mengirim alamat 7-bit + bit R/W (baca atau tulis)
  3. Slave dengan alamat tersebut merespons dengan ACK (acknowledge)
  4. Data dikirim byte per byte, diikuti ACK dari penerima
  5. Master mengirim STOP condition

Ini berarti dalam satu bus I2C yang sama, kita bisa memasang sensor BMP280, layar OLED, sensor suhu, RTC, dan modul lainnya — semuanya cukup dengan dua kabel yang sama. Syaratnya: tidak ada dua perangkat yang memiliki alamat I2C yang sama.

I2C pada ESP32

image
https://lastminuteengineers.com/esp32-pinout-reference/#esp32-i2c-pins

ESP32 memiliki dua interface I2C hardware (I2C0 dan I2C1). Default I2C0 menggunakan pin:

FungsiGPIO Default
SDAGPIO 21
SCLGPIO 22

Namun seperti protokol lainnya, ESP32 mendukung pemetaan pin I2C ke GPIO manapun melalui GPIO Matrix. Cukup tentukan pin yang diinginkan saat inisialisasi Wire.begin().

Kecepatan I2C

I2C mendukung beberapa mode kecepatan:

ModeKecepatan
Standard Mode100 kHz
Fast Mode400 kHz
Fast Mode Plus1 MHz

Untuk mengubah kecepatan clock pada Arduino IDE:

Wire.setClock(400000);  // Fast Mode: 400 kHz

Latihan 1: I2C Scanner — Temukan Alamat Semua Perangkat

Sebelum menulis kode untuk sensor atau modul I2C apapun, langkah pertama yang sangat direkomendasikan adalah menjalankan I2C Scanner untuk mengetahui alamat perangkat yang terhubung.

#include <Wire.h>

void setup() {
  Wire.begin(21, 22);  // SDA = GPIO 21, SCL = GPIO 22
  Serial.begin(115200);
  delay(1000);
  Serial.println("===== I2C Scanner =====");
}

void loop() {
  byte error, address;
  int jumlahDevice = 0;

  Serial.println("\nMemulai pemindaian...");

  for (address = 1; address < 127; address++) {
    Wire.beginTransmission(address);
    error = Wire.endTransmission();

    if (error == 0) {
      Serial.print("Perangkat I2C ditemukan di alamat: 0x");
      if (address < 16) Serial.print("0");
      Serial.print(address, HEX);
      Serial.println("  [OK]");
      jumlahDevice++;
    } else if (error == 4) {
      Serial.print("Error tidak dikenal di alamat: 0x");
      if (address < 16) Serial.print("0");
      Serial.println(address, HEX);
    }
  }

  if (jumlahDevice == 0) {
    Serial.println("Tidak ada perangkat I2C yang ditemukan.");
    Serial.println("Periksa wiring dan koneksi Anda.");
  } else {
    Serial.print("Pemindaian selesai. Total perangkat ditemukan: ");
    Serial.println(jumlahDevice);
  }

  delay(5000);  // Ulangi setiap 5 detik
}

Cara membaca hasilnya: Hubungkan perangkat I2C (misalnya OLED SSD1306 dan sensor BMP280 secara bersamaan), upload kode ini, buka Serial Monitor, dan Anda akan melihat daftar alamat seperti:

===== I2C Scanner =====

Memulai pemindaian...
Perangkat I2C ditemukan di alamat: 0x3C  [OK]
Perangkat I2C ditemukan di alamat: 0x76  [OK]
Pemindaian selesai. Total perangkat ditemukan: 2

Alamat 0x3C adalah OLED SSD1306, dan 0x76 adalah BMP280. Simpan alamat-alamat ini — Anda akan membutuhkannya saat menulis kode program utama.

Troubleshooting: Jika tidak ada perangkat yang terdeteksi, periksa: (1) koneksi SDA dan SCL tidak tertukar, (2) VCC dan GND terhubung dengan benar, (3) coba ganti pin dengan Wire.begin(pin_sda, pin_scl) yang berbeda.

Latihan 2: I2C dengan Custom Pins

Misalnya Anda ingin menggunakan GPIO 32 dan 33 sebagai SDA dan SCL (karena GPIO 21 dan 22 sudah dipakai):

#include <Wire.h>

#define SDA_PIN  32
#define SCL_PIN  33

void setup() {
  Serial.begin(115200);

  // Inisialisasi I2C dengan pin custom
  Wire.begin(SDA_PIN, SCL_PIN);

  // Opsional: tingkatkan ke Fast Mode
  Wire.setClock(400000);

  Serial.println("I2C pada GPIO 32 (SDA) dan GPIO 33 (SCL) siap.");
}

void loop() {}

Latihan 3: I2C Komunikasi Manual (Menulis dan Membaca Register)

Memahami cara komunikasi I2C bekerja secara low-level sangat berguna untuk debugging atau ketika library tidak tersedia. Berikut contoh menulis dan membaca register perangkat I2C secara langsung:

#include <Wire.h>

#define DEVICE_ADDRESS  0x76   // Alamat BMP280
#define REG_CHIP_ID     0xD0   // Register Chip ID BMP280

void setup() {
  Wire.begin(21, 22);
  Serial.begin(115200);
  delay(1000);

  // --- Membaca satu register dari perangkat I2C ---
  Wire.beginTransmission(DEVICE_ADDRESS);
  Wire.write(REG_CHIP_ID);           // Kirim alamat register yang ingin dibaca
  Wire.endTransmission(false);       // false = repeated start (jangan putus koneksi)

  Wire.requestFrom(DEVICE_ADDRESS, 1);  // Minta 1 byte dari perangkat

  if (Wire.available()) {
    byte chipId = Wire.read();
    Serial.print("BMP280 Chip ID: 0x");
    Serial.println(chipId, HEX);  // Harusnya 0x60 untuk BMP280
  }
}

void loop() {}

Nilai 0x60 yang terbaca adalah Chip ID resmi dari BMP280 sesuai datasheet — ini membuktikan komunikasi I2C berjalan dengan benar bahkan tanpa library tambahan.

Alur komunikasi I2C dalam kode:

  1. Wire.beginTransmission(alamat) — kirim START + alamat perangkat
  2. Wire.write(register) — kirim byte alamat register
  3. Wire.endTransmission(false) — kirim repeated START (false = jangan tutup koneksi)
  4. Wire.requestFrom(alamat, n) — minta n byte data dari perangkat
  5. Wire.read() — baca byte yang diterima

Latihan 4: Dua Bus I2C Bersamaan

ESP32 memiliki dua hardware I2C (I2C0 dan I2C1). Ini berguna ketika dua perangkat memiliki alamat yang sama dan tidak bisa diubah, atau ketika kita ingin mengisolasi dua kelompok perangkat.

#include <Wire.h>

// Bus I2C pertama: menggunakan TwoWire default (Wire)
// Bus I2C kedua: buat instance TwoWire baru
TwoWire I2C_Bus1 = TwoWire(0);  // Peripheral I2C0
TwoWire I2C_Bus2 = TwoWire(1);  // Peripheral I2C1

void setup() {
  Serial.begin(115200);

  // Bus 1: GPIO 21 (SDA), GPIO 22 (SCL)
  I2C_Bus1.begin(21, 22);

  // Bus 2: GPIO 32 (SDA), GPIO 33 (SCL)
  I2C_Bus2.begin(32, 33);

  Serial.println("Dua bus I2C aktif:");
  Serial.println("  Bus 1 -> SDA: GPIO 21, SCL: GPIO 22");
  Serial.println("  Bus 2 -> SDA: GPIO 32, SCL: GPIO 33");
}

void loop() {
  // Scan Bus 1
  Serial.println("\n--- Scan Bus 1 ---");
  for (byte addr = 1; addr < 127; addr++) {
    I2C_Bus1.beginTransmission(addr);
    if (I2C_Bus1.endTransmission() == 0) {
      Serial.print("  Ditemukan di 0x"); Serial.println(addr, HEX);
    }
  }

  // Scan Bus 2
  Serial.println("--- Scan Bus 2 ---");
  for (byte addr = 1; addr < 127; addr++) {
    I2C_Bus2.beginTransmission(addr);
    if (I2C_Bus2.endTransmission() == 0) {
      Serial.print("  Ditemukan di 0x"); Serial.println(addr, HEX);
    }
  }

  delay(10000);
}

Kapan Menggunakan I2C?

I2C paling cocok digunakan untuk:

  • Menghubungkan banyak sensor sekaligus dalam satu bus (BMP280, SHT31, MPU6050, dsb)
  • Layar OLED/LCD berukuran kecil
  • RTC (Real-Time Clock) module
  • EEPROM eksternal
  • Aplikasi yang membutuhkan banyak perangkat tapi keterbatasan pin

Perbandingan Lengkap: UART vs SPI vs I2C

Setelah memahami ketiganya, berikut tabel perbandingan yang dapat dijadikan acuan dalam memilih protokol yang tepat:

AspekUARTSPII2C
TipeAsinkronSinkronSinkron
Kabel data2 (TX, RX)4 (MOSI, MISO, SCK, CS)2 (SDA, SCL)
Pin per tambahan slave+2 pin+1 pin (CS)0 pin tambahan
KecepatanSedang (hingga ~5 Mbps)Tinggi (hingga ~80 Mbps)Rendah-sedang (100–400 kHz)
TopologiPoint-to-pointMulti-slaveMulti-device bus
Jumlah perangkat1Banyak (dibatasi CS)Banyak (dibatasi alamat, maks 127)
KompleksitasRendahSedangSedang
Contoh penggunaanGPS, Bluetooth serial, debugSD Card, layar TFT, ADC/DACSensor, OLED, RTC, EEPROM

Kesimpulan

Tiga protokol serial — UART, SPI, dan I2C — masing-masing memiliki karakteristik, kelebihan, dan penggunaan yang berbeda. Tidak ada satu protokol yang “terbaik untuk semua kasus”. Pemilihan protokol yang tepat tergantung pada:

  • Seberapa cepat data harus dikirim
  • Berapa banyak perangkat yang perlu dihubungkan
  • Berapa banyak pin yang tersedia
  • Jenis perangkat yang ingin digunakan (lihat datasheetnya)

Sebagai panduan praktis:

  • Pilih UART jika menghubungkan perangkat point-to-point seperti GPS atau modul Bluetooth
  • Pilih SPI jika butuh kecepatan tinggi seperti layar TFT, SD card, atau ADC presisi
  • Pilih I2C jika menghubungkan banyak sensor dengan kabel minimal

Dengan memahami ketiganya, Anda memiliki bekal yang sangat solid untuk merancang sistem IoT yang efisien, mulai dari pemilihan sensor, perencanaan wiring, hingga penulisan kode komunikasi.


Referensi

Tinggalkan Komentar

Alamat email Anda tidak akan dipublikasikan. Ruas yang wajib ditandai *

Scroll to Top