ESP32 I2C (Inter-Integrated Circuit) çevre birimi; master (ana) veya slave (bağımlı) olarak çalışabilen iki adet bağımsız donanımsal I2C kontrolörüne sahiptir.
Modern gömülü sistem mimarilerinde ve Nesnelerin İnterneti (IoT) projelerinde, birden çok sensörün, ekranın ve harici çevre biriminin mikrodenetleyicilerle minimum pin kullanarak haberleşmesi büyük önem taşır.
ESP32’nin esnek GPIO matrisi sayesinde, varsayılan pinler haricinde neredeyse tüm dijital pinleri I2C veri yolu olarak yapılandırmak mümkündür. Bu kapsamlı rehberimizde, I2C iletişim protokolünün fiziksel elektriksel katmanını, pull-up direnci hesaplama formüllerini, ESP32’nin donanımsal I2C portlarını, I2C adres tarayıcı yazılımlarını, adres çakışmalarına karşı TCA9548A çoklayıcı ve çift bağımsız veri yolu (Wire / Wire1) kullanım senaryolarını en üst seviye mühendislik detaylarıyla inceleyeceğiz.

ESP32 programlama adımlarını Arduino IDE üzerinde gerçekleştireceğiz. Gerekli araç zinciri ve kart sürücüsü kurulumları için Arduino IDE’ye ESP32 Desteği Kurmak rehberimizi inceleyebilirsiniz. Ayrıca I2C protokolünün temeli olan veri yolu kavramını daha iyi kavramak adına Veri Yolu (Bus) Nedir? yazımıza da göz atabilirsiniz.
I2C İletişim Protokolünün Fiziksel ve Elektriksel Karakteristiği
I2C, Philips Semiconductors (şimdiki NXP) tarafından geliştirilen, senkron, çift yönlü, yarım çift yönlü (half-duplex) ve çoklu master/slave desteğine sahip seri bir haberleşme protokolüdür. Protokol fiziksel katmanda sadece iki adet sinyal hattı kullanır:
- SDA (Serial Data Line): Çift yönlü veri iletiminin yapıldığı hat.
- SCL (Serial Clock Line): Master tarafından üretilen ve iletimi senkronize eden saat sinyali hattı.

Açık Kollektör/Drain ve Pull-up Direnci Hesabı
I2C hatlarını kullanan tüm cihazların çıkış katları açık-drain (open-drain) veya açık-kollektör yapısındadır. Hatlar kendi başlarına mantıksal yüksek (
) seviyesine çıkamaz, sadece hattı şaseye (GND) çekerek düşük (
) seviyesine getirebilirler. Bu nedenle, hatların varsayılan durumda mantıksal yüksekte kalabilmesi için
hattına bağlı harici pull-up (yukarı çekme) dirençlerine ihtiyaç duyulur.
Optimum pull-up direnç değeri, hattın toplam parazitik sığası (
) ve çalışılan frekans limitleri doğrultusunda hesaplanır. Minimum ve maksimum direnç değerleri şu formüllerle belirlenir:
![]()
![]()
Burada
maksimum alçak seviye voltaj eşiği,
cihazların maksimum akım çekme kapasitesi,
ise sinyalin yükselme süresidir (rise time). Pratikte
beslemeli ESP32 sistemlerinde standart
veya
hızlar için
ile
arasındaki dirençler en kararlı sinyal formunu sunar. Birçok hazır sensör modülünde (örn. BME280, MPU6050) bu pull-up dirençleri PCB üzerinde yerleşik olarak bulunur, bu yüzden harici direnç eklenmesine gerek kalmaz.
ESP32 Donanımsal I2C Portları ve Pinleri
ESP32 SoC bünyesinde I2C0 ve I2C1 olmak üzere iki adet tamamen bağımsız donanımsal I2C kontrolörü barındırır. Bu kontrolörler donanımsal filtreleme yeteneklerine sahiptir ve hat gürültülerini otomatik olarak süzerler. Donanım seviyesinde şu hız modları desteklenir:
- Standart Mod (Standard-mode):
haberleşme hızı. - Hızlı Mod (Fast-mode):
haberleşme hızı (en yaygın kullanılan kararlı sınır). - Hızlı Mod Plus (Fast-mode Plus):
hıza kadar destek sunar.
Arduino Core kütüphanesini kullanırken ESP32 için varsayılan donanımsal I2C pin tanımlamaları şu şekildedir:
| I2C Sinyali | Varsayılan GPIO (ESP32) | Sensör Pin Adı (Örn: BME280) |
| SDA (Data) | GPIO 21 | SDA / SDI / SDX |
| SCL (Clock) | GPIO 22 | SCL / SCK / SCX |
| GND (Toprak) | GND | GND |
| VCC (Besleme) | 3.3V | VCC / VIN (3.3V) |

Dahili donanımsal I2C birimlerini kullanırken her iki port da (I2C0 ve I2C1) ESP32’nin dahili GPIO matrisi sayesinde herhangi bir çakışma olmadan başka dijital pinlere de yönlendirilebilir. ESP32’nin tüm genel amaçlı giriş/çıkış sınırlamalarını öğrenmek için kapsamlı ESP32 Pin Referansı Kılavuzumuzu inceleyebilirsiniz.
ESP32 I2C Adres Tarama Kod Uygulaması
I2C veri yolunda haberleşebilmek için her slave cihazın benzersiz bir 7-bitlik oltalık (hexadecimal) adresi bulunmalıdır. Master cihaz (ESP32), veri çerçevesini gönderirken ilk byte içerisinde bu adresi yayınlar ve sadece ilgili adrese sahip cihaz veri yoluna yanıt döner. Çoğu sensörün adresi veri sayfasında (datasheet) yazar, fakat bilinmeyen veya klon modüllerde adresi tespit etmek için I2C adres tarayıcı yazılımı çalıştırılmalıdır.
Sensörlerinizi varsayılan I2C pinlerine (SDA: GPIO 21, SCL: GPIO 22) bağladıktan sonra aşağıdaki tarama kodunu yükleyip seri port ekranını
hızında izleyebilirsiniz:
#include <Wire.h>
void setup() {
Serial.begin(115200);
while (!Serial); // USB bağlantısının kararlı hale gelmesini bekle
Serial.println("\n--- ESP32 I2C Adres Tarayıcı Başlatılıyor ---");
// Varsayılan I2C veri yolunu başlat
Wire.begin();
}
void loop() {
byte hata, adres;
int cihazSayisi = 0;
Serial.println("I2C hattı taranıyor...");
for (adres = 1; adres < 127; adres++) {
// Verilen adres üzerinden boş bir yazma isteği göndererek cihazın varlığını sorgula
Wire.beginTransmission(adres);
hata = Wire.endTransmission();
if (hata == 0) {
Serial.print("Cihaz bulundu! Adres: 0x");
if (adres < 16) Serial.print("0");
Serial.print(adres, HEX);
Serial.println("");
cihazSayisi++;
}
else if (hata == 4) {
Serial.print("Bilinmeyen hata! Adres: 0x");
if (adres < 16) Serial.print("0");
Serial.println(adres, HEX);
}
}
if (cihazSayisi == 0) {
Serial.println("I2C veri yolunda hiçbir cihaz bulunamadı.\n");
} else {
Serial.println("Tarama tamamlandı.\n");
}
delay(5000); // 5 saniyede bir yeniden tara
}
ESP32’de Varsayılan I2C Pinlerini Değiştirme ve Özelleştirme
ESP32’de projelerin donanımsal yerleşim kısıtlamalarına göre varsayılan pinleri değiştirmek oldukça kolaydır. Arduino Wire.h kitaplığı ile özel I2C pinleri tanımlamak için şu fonksiyon çağrılır:
Wire.begin(SDA_PIN, SCL_PIN, saat_frekansi);Ancak Adafruit veya SparkFun gibi popüler üçüncü parti sensör kütüphaneleri kullanılırken, sadece Wire.begin() fonksiyonunu özelleştirmek yetmez. Çünkü kütüphaneler kendi içlerinde varsayılan Wire nesnesini çağırarak pin ayarlarınızın üzerine yazabilir. Bunu aşmak için, ilgili kütüphanelerin yapıcı fonksiyonlarına (constructor) özel olarak yapılandırılmış bir TwoWire referansı geçilmelidir.

Örneğin, SDA olarak GPIO 33 ve SCL olarak GPIO 32 pinlerini kullanarak BME280 sıcaklık-nem sensöründen veri okumak için hazırlanmış profesyonel uygulama kodu şu şekildedir:
#include <Wire.h>
#include <Adafruit_Sensor.h>
#include <Adafruit_BME280.h>
// Özel I2C Pin Tanımlamaları
#define OZEL_SDA 33
#define OZEL_SCL 32
#define DENIZ_SEVIYESI_BASINC (1013.25)
// Donanımsal I2C0 portunu (0) kullanarak yeni bir TwoWire nesnesi oluştur
TwoWire ozelI2C = TwoWire(0);
Adafruit_BME280 bme;
void setup() {
Serial.begin(115200);
while(!Serial);
Serial.println("Özel I2C Pinli BME280 Testi Başlatılıyor...");
// Özel I2C portunu tanımlanan pinler ve 100 kHz hızla başlat
ozelI2C.begin(OZEL_SDA, OZEL_SCL, 100000);
// BME280 nesnesine özel TwoWire hattımızın adresini (&ozelI2C) parametre olarak geçiyoruz
bool durum = bme.begin(0x76, &ozelI2C);
if (!durum) {
Serial.println("BME280 sensörü bulunamadı, bağlantıları kontrol edin!");
while (1);
}
Serial.println("BME280 başarıyla başlatıldı.\n");
}
void loop() {
Serial.print("Sıcaklık = ");
Serial.print(bme.readTemperature());
Serial.println(" *C");
Serial.print("Basınç = ");
Serial.print(bme.readPressure() / 100.0F);
Serial.println(" hPa");
Serial.print("Nem = ");
Serial.print(bme.readHumidity());
Serial.println(" %");
Serial.println("-------------------------------------");
delay(2000);
}
Ortak I2C Veri Yolunda Çoklu Cihaz Kullanımı
I2C adres tabanlı bir protokol olduğu için, veri hattına bağlı olan cihazların adresleri farklı olduğu sürece, tek bir SDA ve SCL hattına düzinelerce sensör ve ekran paralel olarak bağlanabilir.
Senaryo 1: Farklı Adreslere Sahip Cihazlar
Örneğin; bir adet
SSD1306 OLED ekran (
adresi) ile bir adet BME280 sensörünü (
adresi) aynı SDA ve SCL hattına bağlayıp tek veri yolu üzerinden çalıştırmak tamamen sorunsuzdur:
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <Adafruit_BME280.h>
#define EKRAN_GENISLIK 128
#define EKRAN_YUKSEKLIK 64
// Donanımsal varsayılan Wire referansıyla ekran nesnesi oluştur
Adafruit_SSD1306 ekran(EKRAN_GENISLIK, EKRAN_YUKSEKLIK, &Wire, -1);
Adafruit_BME280 bme;
void setup() {
Serial.begin(115200);
// Varsayılan I2C hattını başlat
Wire.begin();
// Ekranı 0x3C adresiyle başlat
if(!ekran.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {
Serial.println(F("SSD1306 OLED ekran başlatılamadı!"));
for(;;);
}
// Sensörü 0x76 adresiyle başlat
if (!bme.begin(0x76)) {
Serial.println("BME280 sensörü başlatılamadı!");
while (1);
}
ekran.clearDisplay();
ekran.setTextColor(WHITE);
ekran.setTextSize(1);
}
void loop() {
float temp = bme.readTemperature();
float hum = bme.readHumidity();
ekran.clearDisplay();
ekran.setCursor(0, 0);
ekran.println("--- SENSOR VERILERI ---");
ekran.println("");
ekran.print("Sicaklik: ");
ekran.print(temp);
ekran.println(" C");
ekran.print("Nem: % ");
ekran.print(hum);
ekran.display();
delay(1000);
}
Fiziksel bağlantı şemasında görüldüğü gibi, iki cihazın SDA ve SCL hatları ESP32’nin aynı pinlerine paralel olarak birleştirilmiştir:

Senaryo 2: Aynı Adrese Sahip Birden Fazla Cihaz (Çakışma Çözümleri)
Eğer projenizde aynı I2C adresine sahip iki adet SSD1306 OLED ekran veya birden fazla BME280 sensörü bağlamanız gerekirse, veri yolunda çakışma (address collision) meydana gelir. Master cihaz çağrı yaptığında iki cihaz da aynı anda hatta yanıt vereceğinden sinyaller bozulur. Bu problemi aşmak için üç temel endüstriyel çözüm bulunur:
1. Donanımsal Adres Seçim Pinlerini Değiştirmek
Birçok I2C modülünde, adres seçimi yapabilmeniz için lehim köprüleri veya adres pini (SDO/ADDR) bulunur. Örneğin, SSD1306 OLED ekranların arkasında yer alan adres seçici SMD direncinin konumu değiştirilerek (lehim köprüsü aktarılarak) ekranın adresi kolayca
‘den
‘ye değiştirilebilir. Bu sayede aynı veri yoluna iki ekran çakışmasız bağlanabilir.

2. Donanımsal I2C Çoklayıcı (Multiplexer) Kullanmak
İkiden fazla aynı adresli cihazı bağlamak gerektiğinde veya donanımsal adres seçimi sunmayan sensörlerde en kesin çözüm TCA9548A 8-Kanal I2C Multiplexer entegresi kullanmaktır. Bu entegre, master tarafından gönderilen bir komut baytı doğrultusunda kendi arkasındaki 8 farklı alt I2C veri yolunu (SD0/SC0 – SD7/SC7) dinamik olarak ana I2C hattına bağlar. Böylece aynı adrese sahip 8 adet sensör çakışmasız kontrol edilebilir.

3. ESP32’nin Çift Donanımsal I2C Portunu Kullanmak
Harici bir entegre (multiplexer) satın almadan, ESP32’nin dahili çift donanımsal denetleyicisini (I2C0 ve I2C1) devreye alarak aynı adrese sahip iki adet sensörü tamamen izole bağımsız iki veri yolu üzerinden sürebilirsiniz. Bu yöntem hem maliyeti düşürür hem de veri yollarının elektriksel yükünü ikiye bölerek sinyal bütünlüğünü korur.

ESP32 Çift I2C Veri Yolu Uygulama Projesi
Bu uygulamamızda, aynı oltalık adrese (
) sahip iki adet BME280 sensörünü, ESP32’nin iki bağımsız I2C donanım birimi (I2C0 ve I2C1) üzerinden ayrı ayrı bağlayacağız. Birinci sensörü GPIO 27/26 pinlerine, ikinci sensörü ise GPIO 33/32 pinlerine bağlayacağız.

Ardunio Çift I2C Kodu
Çift bağımsız TwoWire nesnesi oluşturarak her iki sensörü de kendi donanımsal hatlarından okuyan profesyonel kod yapısı şu şekildedir:
#include <Wire.h>
#include <Adafruit_Sensor.h>
#include <Adafruit_BME280.h>
// I2C Veri Yolu 1 Pinleri (I2C0 Birimi)
#define SDA_1 27
#define SCL_1 26
// I2C Veri Yolu 2 Pinleri (I2C1 Birimi)
#define SDA_2 33
#define SCL_2 32
// İki bağımsız donanımsal TwoWire nesnesi oluşturuluyor
TwoWire I2Cone = TwoWire(0);
TwoWire I2Ctwo = TwoWire(1);
// Sensör kütüphanesi örnekleri
Adafruit_BME280 bme1;
Adafruit_BME280 bme2;
void setup() {
Serial.begin(115200);
while(!Serial);
Serial.println("Çift Donanımsal I2C Başlatılıyor...");
// İki bağımsız I2C veri yolunu farklı pinler ve hızlarda başlat
I2Cone.begin(SDA_1, SCL_1, 100000);
I2Ctwo.begin(SDA_2, SCL_2, 100000);
// 1. Sensörü 1. I2C hattından (I2Cone) başlat
bool durum1 = bme1.begin(0x76, &I2Cone);
if (!durum1) {
Serial.println("1. Sensör (BME1) bulunamadı, bağlantıları inceleyin!");
while (1);
}
// 2. Sensörü 2. I2C hattından (I2Ctwo) başlat
bool durum2 = bme2.begin(0x76, &I2Ctwo);
if (!durum2) {
Serial.println("2. Sensör (BME2) bulunamadı, bağlantıları inceleyin!");
while (1);
}
Serial.println("Her iki BME280 sensörü de başarıyla başlatıldı.\n");
}
void loop() {
// 1. Sensör Verilerini Oku (I2C0 Portu)
Serial.print("[BME1] Sıcaklık: ");
Serial.print(bme1.readTemperature());
Serial.print(" *C\t Nem: % ");
Serial.print(bme1.readHumidity());
Serial.print("\tBasınç: ");
Serial.print(bme1.readPressure() / 100.0F);
Serial.println(" hPa");
// 2. Sensör Verilerini Oku (I2C1 Portu)
Serial.print("[BME2] Sıcaklık: ");
Serial.print(bme2.readTemperature());
Serial.print(" *C\t Nem: % ");
Serial.print(bme2.readHumidity());
Serial.print("\tBasınç: ");
Serial.print(bme2.readPressure() / 100.0F);
Serial.println(" hPa");
Serial.println("-----------------------------------------------------------------");
delay(4000); // 4 saniyede bir verileri güncelle
}Pratik Alternatif: Wire ve Wire1 Nesneleri
Yukarıdaki gibi sıfırdan TwoWire nesneleri tanımlamak yerine, Arduino Core içerisinde önceden tanımlanmış hazır Wire ve Wire1 nesnelerinden de faydalanabilirsiniz:
Wirenesnesi varsayılan GPIO 21 ve GPIO 22 pinlerini kullanarak donanımsal I2C0 portunu arka planda otomatik yönetir.Wire1nesnesi ise, parametre olarak geçeceğiniz özel pinler ve frekans doğrultusunda donanımsal I2C1 portunu yönetir.

Bu alternatif yöntemde, setup fonksiyonu içindeki başlatma bloğu şu pratik yapıya kavuşur:
void setup() {
Serial.begin(115200);
// İlk veri yolunu varsayılan pinlerle (GPIO 21/22) başlat
Wire.begin();
// İkinci veri yolunu özel pinlerle (GPIO 33/32) başlat
Wire1.begin(33, 32, 100000);
// Sensörleri ilgili donanım nesnelerine bağla
bme1.begin(0x76, &Wire);
bme2.begin(0x76, &Wire1);
}Hangi yöntemi seçerseniz seçin, her iki sensörün okumaları da bağımsız olarak işlemciye aktarılacaktır. Seri port çizicisinde veya terminal ekranında alacağınız kusursuz çıktı şu şekilde olacaktır:

Özetle; ESP32’nin çift donanımsal I2C kontrolörü ve zengin pin atama kabiliyetleri, karmaşık gömülü sistem sensör ağlarında ek donanım maliyetleri olmadan şık, esnek ve yüksek sinyal kalitesine sahip profesyonel çözümler geliştirmenizi sağlar.
Örnek bir esp32 veri sayfasına buradan ulaşabilirsiniz.
Yorum yapma özelliği, forum tarafından gelen istek sebebiyle kapatılmıştır. Lütfen tartışmalar ve sorularınız için topluluk forumumuza katılın.

