ESP32 I2C Kullanımı

ESP32 I2C, ana(master) veya bağımlı(slave) olarak hizmet edebilen iki I2C veri yolu arabirimine sahiptir. Bu yazıda, Arduino IDE kullanarak ESP32 ile I2C iletişim protokolüne göz atacağız: I2C pinlerinin nasıl seçileceği, aynı veri yoluna birden fazla I2C cihazının nasıl bağlanacağı ve iki I2C veri yolu arayüzünün nasıl kullanılacağını göreceğiz.

ESP32 I2C Kullanımı esp32 i2c,esp32 i2c kullanımı,esp32 i2c lcd,esp32 i2c slave,esp32 i2c speed

ESP32’yi Arduino IDE kullanarak programlayacağız, bu yüzden devam etmeden önce ESP32 kütüphanesinin kurulu olduğundan emin olun:

Arduino IDE’ye ESP32 Desteği Kurmak (Windows, Linux, MacOS)

ESP32 I2C İletişim Protokolü ile Tanışın

I²C, Inter Integrated Circuit (I-kare-C olarak telaffuz edilir) anlamına gelir ve senkron, çok ana, çok bağımlı bir iletişim protokolüdür.:

Bir master’a birden fazla bağımlı: örneğin, ESP32’niz I2C kullanarak bir BME280 sensöründen okur ve sensör okumalarını bir I2C OLED ekranına yazar.
Aynı bağımlı birimi kontrol eden birden fazla master: örneğin, aynı I2C OLED ekranına veri yazan iki ESP32 kartı.

Sensörler ve ekranlar gibi harici cihazlarla iletişim kurmak için bu protokolü ESP32 ile birçok kez kullanırız. Bu durumlarda, ESP32 ana çiptir ve harici cihazlar bağımlıdır.

ESP32 I2C Kullanımı esp32 i2c,esp32 i2c kullanımı,esp32 i2c lcd,esp32 i2c slave,esp32 i2c speed

ESP32 I2C Veri Yolu Arayüzleri

ESP32, kullanıcının yapılandırmasına bağlı olarak I2C ana veya bağımlı olarak hizmet verebilen iki I2C veri yolu arabirimi aracılığıyla I2C iletişimini destekler. ESP32 veri sayfasına göre, ESP32’nin I2C arayüzleri şunları destekler:

  • Standart mod (100 Kbit/sn)
  • Hızlı mod (400 Kbit/sn)
  • 5 MHz’e kadar, ancak SDA pull-up gücü ile sınırlı
  • 7 bit/10 bit adresleme modu
  • Çift adresleme modu. Kullanıcılar, daha fazla esnekliğe sahip olmaları için I²C arayüzlerini kontrol etmek için komut kayıtlarını programlayabilir

I2C Cihazlarını ESP32 ile Bağlama

I2C iletişim protokolü, bilgileri paylaşmak için iki kablo kullanır. Biri saat sinyali (SCL) için, diğeri ise veri göndermek ve almak (SDA) için kullanılır.

Not: Birçok sensör modülünde, SDA satırı SDI ve SCL satırı da SCK olarak etiketlenebilir.

ESP32 I2C Kullanımı esp32 i2c,esp32 i2c kullanımı,esp32 i2c lcd,esp32 i2c slave,esp32 i2c speed

SDA ve SCL hatları düşük aktiftir, bu yüzden dirençlerle yukarı çekilmeleri gerekir. Tipik değerler 5V cihazlar için 4.7k Ohm ve 3.3V cihazlar için 2.4k Ohm’dur.

Projelerimizde kullandığımız sensörlerin çoğu, halihazırda yerleşik dirençlere sahip olan devre kartlarıdır. Bu nedenle, genellikle, bu tür elektronik bileşenlerle uğraşırken bunun için endişelenmenize gerek yoktur.

Bir I2C cihazını bir ESP32’ye bağlamak normalde GND’yi GND’ye, SDA’yı SDA’ya, SCL’yi SCL’ye ve pozitif bir güç kaynağını bir çevre birimine (genellikle 3,3V) bağlamak kadar basittir (ancak kullandığınız modüle bağlıdır).

I2C CihazıESP32
SDASDA (GPIO 21)
SCLSCL (GPIO 22) 
GNDGND
VCC3.3V ya da 5V

ESP32’yi Arduino IDE ile kullanırken, varsayılan I2C pinleri GPIO 22 (SCL) ve GPIO 21 (SDA)’dır ancak kodunuzu diğer pinleri kullanacak şekilde yapılandırabilirsiniz.

Önerilen Yazı: ESP32 Pin Referansı, Giriş ve Çıkışlar

ESP32 ile I2C Adresini Taramak

I2C iletişiminde, veri yolu üzerindeki her bağımlı, ESP32’nin her cihazla iletişim kurmasını sağlayan onaltılık bir sayı olan kendi adresine sahiptir.

I2C adresi genellikle bileşenin veri sayfasında bulunabilir. Ancak, bulmak zorsa, I2C adresini bulmak için bir I2C tarayıcı taslağı çalıştırmanız gerekebilir.

Cihazlarınızın I2C adresini bulmak için aşağıdaki kodu kullanabilirsiniz.


#include <Wire.h>
 
void setup() {
  Wire.begin();
  Serial.begin(115200);
  Serial.println("\nI2C Scanner");
}
 
void loop() {
  byte error, address;
  int nDevices;
  Serial.println("Scanning...");
  nDevices = 0;
  for(address = 1; address < 127; address++ ) {
    Wire.beginTransmission(address);
    error = Wire.endTransmission();
    if (error == 0) {
      Serial.print("I2C device found at address 0x");
      if (address<16) {
        Serial.print("0");
      }
      Serial.println(address,HEX);
      nDevices++;
    }
    else if (error==4) {
      Serial.print("Unknow error at address 0x");
      if (address<16) {
        Serial.print("0");
      }
      Serial.println(address,HEX);
    }    
  }
  if (nDevices == 0) {
    Serial.println("No I2C devices found\n");
  }
  else {
    Serial.println("done\n");
  }
  delay(5000);          
}

Seri Monitörünüzde benzer bir şey elde edeceksiniz.

ESP32 I2C Kullanımı esp32 i2c,esp32 i2c kullanımı,esp32 i2c lcd,esp32 i2c slave,esp32 i2c speed
,

ESP32 ile Farklı I2C Pinleri Kullanmak (varsayılan I2C pinlerini değiştirmek)

ESP32 ile hemen hemen her pini I2C özelliklerine sahip olacak şekilde ayarlayabilirsiniz, bunu kodunuzda ayarlamanız yeterlidir.

ESP32’yi Arduino IDE ile kullanırken, I2C kullanan cihazlarla iletişim kurmak için Wire.h kitaplığını kullanın. Bu kitaplık ile I2C’yi aşağıdaki gibi başlatırsınız:

Wire.begin(I2C_SDA, I2C_SCL);

Bu nedenle, I2C_SDA ve I2C_SCL değişkenlerinde istediğiniz SDA ve SCL GPIO’ları ayarlamanız yeterlidir.

Ancak, sensörlerle iletişim kurmak için farklı kitaplıklar kullanıyorsanız, bu çalışmayabilir ve diğer pinleri seçmek biraz zor olabilir. Bunun nedeni, kitaplığı başlatırken kendi Wire örneğinizi geçmezseniz bu kitaplıkların pinlerinizin üzerine yazabilmesidir.

Bu durumlarda, .cpp kitaplık dosyalarına daha yakından bakmanız ve kendi TwoWire parametrelerinizi nasıl ileteceğinizi görmeniz gerekir.

Örneğin, Adafruit BME280 kitaplığına daha yakından bakarsanız, kendi TwoWire’ınızı start() yöntemine geçirebileceğinizi göreceksiniz.

ESP32 I2C Kullanımı esp32 i2c,esp32 i2c kullanımı,esp32 i2c lcd,esp32 i2c slave,esp32 i2c speed

Bu nedenle, örneğin SDA olarak GPIO 33 ve SCL olarak GPIO 32 gibi diğer pinleri kullanarak BME280’den okunacak verileri görmek için örnek kod aşağıdaki gibidir.

#include <Wire.h>
#include <Adafruit_Sensor.h>
#include <Adafruit_BME280.h>

#define I2C_SDA 33
#define I2C_SCL 32

#define SEALEVELPRESSURE_HPA (1013.25)

TwoWire I2CBME = TwoWire(0);
Adafruit_BME280 bme;

unsigned long delayTime;

void setup() {
  Serial.begin(115200);
  Serial.println(F("BME280 test"));
  I2CBME.begin(I2C_SDA, I2C_SCL, 100000);

  bool status;

  status = bme.begin(0x76, &I2CBME);  
  if (!status) {
    Serial.println("Could not find a valid BME280 sensor, check wiring!");
    while (1);
  }

  Serial.println("-- Default Test --");
  delayTime = 1000;

  Serial.println();
}

void loop() { 
  printValues();
  delay(delayTime);
}

void printValues() {
  Serial.print("Temperature = ");
  Serial.print(bme.readTemperature());
  Serial.println(" *C");
  
  /*Serial.print("Temperature = ");
  Serial.print(1.8 * bme.readTemperature() + 32);
  Serial.println(" *F");*/
  
  Serial.print("Pressure = ");
  Serial.print(bme.readPressure() / 100.0F);
  Serial.println(" hPa");

  Serial.print("Approx. Altitude = ");
  Serial.print(bme.readAltitude(SEALEVELPRESSURE_HPA));
  Serial.println(" m");

  Serial.print("Humidity = ");
  Serial.print(bme.readHumidity());
  Serial.println(" %");

  Serial.println();
}
ESP32 I2C Kullanımı esp32 i2c,esp32 i2c kullanımı,esp32 i2c lcd,esp32 i2c slave,esp32 i2c speed

Diğer I2C pinlerini kullanmak için ilgili kısımlara bir göz atalım.

İlk olarak, yeni I2C pinlerinizi I2C_SDA ve I2C_SCL değişkenleri üzerinde tanımlayın. Bu durumda GPIO 33 ve GPIO 32 kullanıyoruz.

#define I2C_SDA 33
#define I2C_SCL 32

Yeni bir TwoWire örneği oluşturun. Bu durumda, I2CBME olarak adlandırılır. Bu basitçe bir I2C veri yolu oluşturur.

TwoWire I2CBME = TwoWire(0);

setup() içinde, daha önce tanımladığınız pinlerle I2C iletişimini başlatın. Üçüncü parametre saat frekansıdır.

I2CBME.begin(I2C_SDA, I2C_SCL, 400000);

Son olarak, sensör adresiniz ve TwoWire nesnenizle bir BME280 nesnesi başlatın.

status = bme.begin(0x76, &I2CBME);

Bundan sonra, sıcaklık, nem ve basınç istemek için bme nesnenizde olağan yöntemleri kullanabilirsiniz.

Not: Kullandığınız kütüphane dosyasında wire.begin() gibi bir ifade kullanıyorsa, kendi pinlerinizi kullanabilmeniz için o satırı yorumlamanız gerekebilir.

Çoklu I2C Cihazlı ESP32

Daha önce de belirttiğimiz gibi, her I2C cihazının kendi adresi vardır, bu nedenle aynı veri yolu üzerinde birden fazla I2C cihazı olması mümkündür.

Çoklu I2C Cihazları (aynı veri yolu, farklı adresler)

Farklı adreslere sahip birden fazla cihazımız olduğunda, bunların nasıl kurulacağı önemsizdir:

  • Her iki çevre birimini de ESP32 SCL ve SDA hatlarına bağlayın;
  • Kodda, her bir çevre birimine adresine göre ayarlayınn;


Bir BME280 sensöründen (I2C aracılığıyla) sensör okumalarını alan ve sonuçları bir I2C OLED ekranında görüntüleyen aşağıdaki örneğe bir göz atın.

#define SCREEN_WIDTH 128 // OLED display width, in pixels
#define SCREEN_HEIGHT 64 // OLED display height, in pixels

Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, -1);

Adafruit_BME280 bme;

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

  if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {
    Serial.println(F("SSD1306 allocation failed"));
    for(;;);
  }
  
  bool status = bme.begin(0x76);  
  if (!status) {
    Serial.println("Could not find a valid BME280 sensor, check wiring!");
    while (1);
  }
  
  delay(2000);
  display.clearDisplay();
  display.setTextColor(WHITE);
}

void loop() {
  display.clearDisplay();
  // display temperature
  display.setTextSize(1);
  display.setCursor(0,0);
  display.print("Temperature: ");
  display.setTextSize(2);
  display.setCursor(0,10);
  display.print(String(bme.readTemperature()));
  display.print(" ");
  display.setTextSize(1);
  display.cp437(true);
  display.write(167);
  display.setTextSize(2);
  display.print("C");
  
  // display humidity
  display.setTextSize(1);
  display.setCursor(0, 35);
  display.print("Humidity: ");
  display.setTextSize(2);
  display.setCursor(0, 45);
  display.print(String(bme.readHumidity()));
  display.print(" %"); 
  
  display.display();

  delay(1000);
}
ESP32 I2C Kullanımı esp32 i2c,esp32 i2c kullanımı,esp32 i2c lcd,esp32 i2c slave,esp32 i2c speed

OLED ve BME280 farklı adreslere sahip olduğu için aynı SDA ve SCL hatlarını sorunsuz kullanabiliyoruz. OLED ekran adresi 0x3C ve BME280 adresi 0x76’dır.

if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) { 
  Serial.println(F("SSD1306 allocation failed"));
  for(;;);
}
  
bool status = bme.begin(0x76);  
if (!status) {
  Serial.println("Could not find a valid BME280 sensor, check wiring!");
  while (1);
}
ESP32 I2C Kullanımı esp32 i2c,esp32 i2c kullanımı,esp32 i2c lcd,esp32 i2c slave,esp32 i2c speed

Birden Fazla I2C Cihazı (aynı adres)

Ancak aynı adrese sahip birden fazla çevre biriminiz varsa ne olur? Örneğin, birden çok OLED ekran veya birden çok BME280 sensörü? Birkaç çözüm var.

  • Cihazın I2C adresini değiştirin;
  • Bir I2C çoklayıcı(multiplexer) kullanın.

I2C Adresini Değiştirme

Birçok devre kartı, devrelerine bağlı olarak I2C adresini değiştirme seçeneğine sahiptir. Örneğin, aşağıdaki OLED ekrana bir göz atın.

ESP32 I2C Kullanımı esp32 i2c,esp32 i2c kullanımı,esp32 i2c lcd,esp32 i2c slave,esp32 i2c speed

Direnci bir tarafa veya diğer tarafa yerleştirerek farklı I2C adresleri seçebilirsiniz. Bu, diğer bileşenlerde de olur.

Bir I2C Çoklayıcı(Multiplexer) Kullanma

Ancak, bu önceki örnekte, bu yalnızca aynı veri yolu üzerinde iki I2C ekranına sahip olmanızı sağlar: biri 0x3C adresli ve diğeri 0x3D adresli.

Ek olarak, bazen I2C adresini değiştirmek önemsiz değildir. Yani aynı I2C veriyolunda aynı adrese sahip birden fazla cihaza sahip olmak için, aynı adrese sahip 8 cihaza kadar iletişim kurmanıza izin veren TCA9548A gibi bir I2C çoklayıcı kullanabilirsiniz.

ESP32 I2C Kullanımı esp32 i2c,esp32 i2c kullanımı,esp32 i2c lcd,esp32 i2c slave,esp32 i2c speed

İki I2C Veri Yolu Arabirimi Kullanan ESP32

ESP32’nin iki I2C veri yolu arabirimini kullanmak için iki TwoWire örneği oluşturmanız gerekir.

TwoWire I2Cone = TwoWire(0);
TwoWire I2Ctwo = TwoWire(1)

Ardından, tanımlanmış bir frekans ile istediğiniz pinlerde I2C iletişimini başlatın.

void setup() {
  I2Cone.begin(SDA_1, SCL_1, freq1);
  I2Ctwo.begin(SDA_2, SCL_2, freq2); 
}

Ardından, I2C veri yolu arabirimleriyle etkileşim kurmak için Wire.h kitaplığındaki yöntemleri kullanabilirsiniz.

Daha basit bir alternatif, önceden tanımlanmış Wire() ve Wire1() nesnelerini kullanmaktır. Wire().begin(), varsayılan pinleri ve varsayılan frekansı kullanarak ilk I2C veriyolunda bir I2C iletişimi oluşturur. Wire1.begin() için frekansın yanı sıra istediğiniz SDA ve SCL pinlerini geçmelisiniz.

setup(){
  Wire.begin();
  Wire1.begin(SDA_2, SCL_2, freq);
}

Bu yöntem, biri varsayılan parametreleri kullanan iki I2C veriyolu kullanmanıza izin verir.

Bunun nasıl çalıştığını daha iyi anlamak için iki BME280 sensöründen sıcaklık, nem ve basıncı okuyan basit bir örneğe göz atacağız.

ESP32 I2C Kullanımı esp32 i2c,esp32 i2c kullanımı,esp32 i2c lcd,esp32 i2c slave,esp32 i2c speed

Her sensör farklı bir I2C veriyoluna bağlıdır.

  • I2C Veri Yolu 1: GPIO 27 (SDA) ve GPIO 26 (SCL) kullanır;
  • I2C Veri Yolu 2: GPIO 33 (SDA) ve GPIO 32 (SCL) kullanır;
ESP32 I2C Kullanımı esp32 i2c,esp32 i2c kullanımı,esp32 i2c lcd,esp32 i2c slave,esp32 i2c speed
#include <Wire.h>
#include <Adafruit_Sensor.h>
#include <Adafruit_BME280.h>

#define SDA_1 27
#define SCL_1 26

#define SDA_2 33
#define SCL_2 32

TwoWire I2Cone = TwoWire(0);
TwoWire I2Ctwo = TwoWire(1);

Adafruit_BME280 bme1;
Adafruit_BME280 bme2;

void setup() {
  Serial.begin(115200);
  Serial.println(F("BME280 test"));

  I2Cone.begin(SDA_1, SCL_1, 100000); 
  I2Ctwo.begin(SDA_2, SCL_2, 100000);

  bool status1 = bme1.begin(0x76, &I2Cone);  
  if (!status1) {
    Serial.println("Could not find a valid BME280_1 sensor, check wiring!");
    while (1);
  }
  
  bool status2 = bme2.begin(0x76, &I2Ctwo);  
  if (!status2) {
    Serial.println("Could not find a valid BME280_2 sensor, check wiring!");
    while (1);
  }
  
  Serial.println();
}

void loop() { 
  // Read from bme1
  Serial.print("Temperature from BME1= ");
  Serial.print(bme1.readTemperature());
  Serial.println(" *C");

  Serial.print("Humidity from BME1 = ");
  Serial.print(bme1.readHumidity());
  Serial.println(" %");

  Serial.print("Pressure from BME1 = ");
  Serial.print(bme1.readPressure() / 100.0F);
  Serial.println(" hPa");

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

  // Read from bme2
  Serial.print("Temperature from BME2 = ");
  Serial.print(bme2.readTemperature());
  Serial.println(" *C");

  Serial.print("Humidity from BME2 = ");
  Serial.print(bme2.readHumidity());
  Serial.println(" %");

  Serial.print("Pressure from BME2 = ");
  Serial.print(bme2.readPressure() / 100.0F);
  Serial.println(" hPa");

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

İki I2C bus arabirimini kullanmak için ilgili bölümlere bir göz atalım.

Kullanmak istediğiniz SDA ve SCL pinlerini tanımlayın:

#define SDA_1 27
#define SCL_1 26

#define SDA_2 33
#define SCL_2 32

İki TwoWire nesnesi oluşturun (iki I2C veri yolu arabirimi):

TwoWire I2Cone = TwoWire(0);
TwoWire I2Ctwo = TwoWire(1);

Sensörlerinizle etkileşim kurmak için Adafruit_BME280 kitaplığının iki örneğini oluşturun: bme1 ve bme2.

Adafruit_BME280 bme1;
Adafruit_BME280 bme2;

Tanımlanan pinler ve frekansta bir I2C iletişimi başlatın:

I2Cone.begin(SDA_1, SCL_1, 100000); 
I2Ctwo.begin(SDA_2, SCL_2, 100000);

Ardından, bme1 ve bme2 nesnelerini doğru adres ve I2C bus ile başlatmalısınız. bme1, I2Cone’u kullanır:

bool status = bme1.begin(0x76, &I2Cone);  

Ve bme2, I2Ctwo’yu kullanır:

bool status1 = bme2.begin(0x76, &I2Ctwo);

Artık sıcaklık, nem ve basıncı okumak için bme1 ve bme2 nesnelerinizde Adafruit_BME280 kitaplığındaki yöntemleri kullanabilirsiniz.

Başka Bir Alternatif

Basit olması için önceden tanımlanmış Wire() ve Wire1() nesnelerini kullanabilirsiniz:

  • Wire(): GPIO 21 (SDA) ve GPIO 22 (SCL) varsayılan pinlerinde bir I2C veriyolu oluşturur
  • Wire1(SDA_2, SCL_2, freq): Tanımlı SDA_2 ve SCL_2 pinleri üzerinde istenilen frekansta bir I2C bus oluşturur.
ESP32 I2C Kullanımı esp32 i2c,esp32 i2c kullanımı,esp32 i2c lcd,esp32 i2c slave,esp32 i2c speed

İşte aynı örnek. Artık sensörlerinizden biri varsayılan pinleri, diğeri ise GPIO 32 ve GPIO 33’ü kullanıyor.

#include <Wire.h>
#include <Adafruit_Sensor.h>
#include <Adafruit_BME280.h>

#define SDA_2 33
#define SCL_2 32

Adafruit_BME280 bme1;
Adafruit_BME280 bme2;

void setup() {
  Serial.begin(115200);
  Serial.println(F("BME280 test"));

  Wire.begin();
  Wire1.begin(SDA_2, SCL_2);

  bool status1 = bme1.begin(0x76);  
  if (!status1) {
    Serial.println("Could not find a valid BME280_1 sensor, check wiring!");
    while (1);
  }
  
  bool status2 = bme2.begin(0x76, &Wire1);  
  if (!status2) {
    Serial.println("Could not find a valid BME280_2 sensor, check wiring!");
    while (1);
  }
 
  Serial.println();
}

void loop() { 
  // Read from bme1
  Serial.print("Temperature from BME1= ");
  Serial.print(bme1.readTemperature());
  Serial.println(" *C");

  Serial.print("Humidity from BME1 = ");
  Serial.print(bme1.readHumidity());
  Serial.println(" %");
  
  Serial.print("Pressure from BME1 = ");
  Serial.print(bme1.readPressure() / 100.0F);
  Serial.println(" hPa");

  Serial.println("--------------------");
  
  // Read from bme2
  Serial.print("Temperature from BME2 = ");
  Serial.print(bme2.readTemperature());
  Serial.println(" *C");

  Serial.print("Humidity from BME2 = ");
  Serial.print(bme2.readHumidity());
  Serial.println(" %");
  
  Serial.print("Pressure from BME2 = ");
  Serial.print(bme2.readPressure() / 100.0F);
  Serial.println(" hPa");

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

Seri Monitörünüzde her iki sensör okumasını da almalısınız.

ESP32 I2C Kullanımı esp32 i2c,esp32 i2c kullanımı,esp32 i2c lcd,esp32 i2c slave,esp32 i2c speed