# Komponen I2C bus
Komponen ini membuat dan menghapus bus I2C master ESP-IDF 5.3.0 atau lebih
baru. Aplikasi membuat satu bus untuk satu jalur fisik SDA/SCL, lalu
memberikan handle bus itu ke driver semua perangkat yang terhubung di jalur
tersebut. Setiap perangkat menambahkan dirinya sendiri ke bus; hapus semua
handle perangkat sebelum menghapus bus.
## Konfigurasi bus
Gunakan `i2c_bus_get_default_config()` untuk memperoleh default, ubah pin dan
opsi yang diperlukan, lalu panggil `i2c_bus_create_with_config()`:
```c
#include "driver/gpio.h"
#include "driver/i2c_master.h"
#include "esp_check.h"
#include "i2c_bus.h"
i2c_bus_config_t config = i2c_bus_get_default_config();
config.i2c_port = I2C_NUM_0;
config.sda_gpio = GPIO_NUM_15;
config.scl_gpio = GPIO_NUM_16;
config.enable_internal_pullup = false;
i2c_master_bus_handle_t bus = NULL;
ESP_ERROR_CHECK(i2c_bus_create_with_config(&config, &bus));
```
Default konfigurasi:
| Field | Default | Makna |
|---|---:|---|
| `i2c_port` | `I2C_NUM_0` | Controller I2C; `-1` meminta pemilihan otomatis ESP-IDF |
| `sda_gpio` / `scl_gpio` | `GPIO_NUM_NC` | Harus diisi oleh aplikasi |
| `clock_source` | `I2C_CLK_SRC_DEFAULT` | Sumber clock bus |
| `glitch_ignore_count` | `7` | Filter glitch dalam siklus clock modul |
| `interrupt_priority` | `0` | Prioritas default yang dipilih driver |
| `transaction_queue_depth` | `0` | Tidak mengatur antrean untuk transaksi asynchronous |
| `enable_internal_pullup` | `false` | Pull-up internal dinonaktifkan |
`transaction_queue_depth` bernilai nol untuk transaksi sinkron. Nilai lebih
dari nol menyediakan antrean internal bagi transaksi asynchronous; gunakan
pengaturan ini hanya jika aplikasi juga menerapkan pola callback dan buffer
yang diwajibkan ESP-IDF. Pemindaian alamat melalui `i2c_bus_scan()` tidak
ditujukan untuk bus yang sedang menjalankan transaksi asynchronous.
`enable_internal_pullup` biasanya dibiarkan `false` jika rangkaian sudah
memiliki resistor pull-up eksternal. Pull-up internal ESP32 lemah dan tidak
menggantikan resistor eksternal yang sesuai. Pastikan SDA/SCL ditarik ke
tegangan logika yang aman untuk semua perangkat.
`i2c_bus_create(sda_gpio, scl_gpio, &bus)` tetap tersedia sebagai cara singkat
dengan `I2C_NUM_0` dan opsi default. Gunakan API konfigurasi jika memilih port
lain atau mengubah opsi bus.
## Banyak perangkat pada satu bus
INA226 dan MCP4725 pada contoh proyek berbagi kabel SDA/SCL yang sama. Aplikasi
membuat bus satu kali dan mengirim handle yang sama ke kedua driver:
```c
i2c_master_bus_handle_t bus = NULL;
ESP_ERROR_CHECK(i2c_bus_create_with_config(&bus_config, &bus));
ina226_handle_t monitor = NULL;
mcp4725_handle_t dac = NULL;
ESP_ERROR_CHECK(ina226_create(bus, &ina_config, &monitor));
ESP_ERROR_CHECK(mcp4725_create(bus, &dac_config, &dac));
```
Alamat I2C tiap perangkat harus unik pada jalur yang sama. Kecepatan SCL bukan
opsi global bus: driver menetapkan `scl_speed_hz` pada konfigurasi masing-masing
device. Transaksi pada satu bus tetap berlangsung bergantian, bukan paralel.
Pilih clock yang didukung oleh semua perangkat yang aktif di bus dan kondisi
kabel/pull-up.
Urutan pelepasan resource adalah kebalikan dari pembuatan:
```c
ESP_ERROR_CHECK(mcp4725_delete(dac));
ESP_ERROR_CHECK(ina226_delete(monitor));
ESP_ERROR_CHECK(i2c_bus_delete(bus));
```
## Beberapa bus fisik
Jika perangkat perlu menggunakan jalur SDA/SCL terpisah, buat handle bus
terpisah dengan port dan pin yang sesuai. Contoh berikut hanya berlaku jika
chip mempunyai controller I2C kedua dan pin tersebut mendukung fungsi I2C:
```c
i2c_bus_config_t bus0_config = i2c_bus_get_default_config();
bus0_config.i2c_port = I2C_NUM_0;
bus0_config.sda_gpio = GPIO_NUM_15;
bus0_config.scl_gpio = GPIO_NUM_16;
i2c_bus_config_t bus1_config = i2c_bus_get_default_config();
bus1_config.i2c_port = I2C_NUM_1;
bus1_config.sda_gpio = GPIO_NUM_17;
bus1_config.scl_gpio = GPIO_NUM_18;
i2c_master_bus_handle_t bus0 = NULL;
i2c_master_bus_handle_t bus1 = NULL;
ESP_ERROR_CHECK(i2c_bus_create_with_config(&bus0_config, &bus0));
ESP_ERROR_CHECK(i2c_bus_create_with_config(&bus1_config, &bus1));
```
ESP-IDF dan SoC akan menolak kombinasi port/pin yang tidak didukung. Pilihan
port controller bukan jaminan semua GPIO dapat dipakai sebagai SDA/SCL.
Perangkat hanya dapat diakses melalui bus yang kabelnya benar-benar
terhubung kepadanya.
Untuk informasi batasan controller dan konfigurasi pin, rujuk dokumentasi
I2C master pada versi ESP-IDF dan datasheet SoC yang digunakan.
## Pemindaian alamat perangkat
`i2c_bus_scan()` mengirim probe pada alamat 7-bit standar yang tidak reserved,
yaitu `0x08` sampai `0x77`. Hasil dikembalikan dalam urutan alamat menaik.
Contoh berikut menyediakan kapasitas maksimum untuk seluruh rentang:
```c
#include "esp_log.h"
#include "i2c_bus.h"
uint8_t addresses[I2C_BUS_SCAN_MAX_ADDRESS -
I2C_BUS_SCAN_MIN_ADDRESS + 1];
size_t found_count = 0;
ESP_ERROR_CHECK(i2c_bus_scan(bus,
I2C_BUS_SCAN_DEFAULT_TIMEOUT_MS,
addresses,
sizeof(addresses),
&found_count));
for (size_t i = 0; i < found_count; ++i) {
ESP_LOGI("i2c_scan", "Found device at 0x%02X", addresses[i]);
}
```
Timeout berlaku untuk setiap alamat, bukan seluruh proses scan. Artinya waktu
terburuk dapat mendekati 112 kali nilai timeout, ditambah overhead transaksi.
Alamat yang tidak merespons menghasilkan `ESP_ERR_NOT_FOUND` dari probe dan
dilewati. Timeout atau error bus lain menghentikan scan dan diteruskan ke
pemanggil. Jika buffer hasil terlalu kecil, scan tetap dilanjutkan, alamat
yang muat disimpan, jumlah total ditulis ke `found_count`, lalu fungsi
mengembalikan `ESP_ERR_INVALID_SIZE`. Untuk menghitung saja tanpa menyimpan
alamat, berikan `addresses = NULL` dan `capacity = 0`.
Pemindaian ditujukan untuk diagnostik atau saat startup. Probe mengirim alamat
dengan arah tulis tanpa data; sebagian perangkat mungkin tidak mendukung pola
ini atau dapat menafsirkannya secara khusus. Hindari scan berulang di loop
aplikasi dan jangan jalankan bersamaan dengan operasi perangkat yang sensitif.
Jalankan dari task, bukan ISR. Jika menggunakan transaksi asynchronous, scan
tidak sesuai dengan mode tersebut.
## Reset controller
`i2c_bus_reset(bus)` meneruskan ke `i2c_master_bus_reset()` ESP-IDF untuk
mereset state controller master. Gunakan hanya setelah kegagalan transaksi
yang memerlukan pemulihan controller dan setelah mengoordinasikan akses dari
task lain. Fungsi ini **bukan** pemulihan jalur GPIO: jika SDA atau SCL
tertahan secara elektrik, reset controller saja belum tentu melepaskannya.
Pemulihan jalur tersebut memerlukan prosedur perangkat keras yang sesuai
dengan board dan perangkat yang terhubung.
## Kontrak API dan distribusi
Semua fungsi mengembalikan `esp_err_t`; periksa hasilnya sebelum memakai handle
atau data keluaran. Keluaran `i2c_bus_scan()` hanya bermakna sesuai kontrak
buffer dan status yang dijelaskan di atas. Panggil fungsi driver blocking dari
task, bukan ISR.
Komponen ini ditujukan untuk ESP-IDF 5.3.0 atau lebih baru dan menggunakan
komponen bawaan `esp_driver_gpio` serta `esp_driver_i2c`. Metadata versi dan
lisensi untuk distribusi tersedia pada `idf_component.yml`; kode dilisensikan
di bawah MIT.
idf.py add-dependency "wahyu-widodo-dev/i2c_bus^1.0.0"