ESP32 I2C Communication Protocol

Anindha Catur Arifani
5 min readMar 6, 2021

--

I2C (I-squared-C) atau Inter Integrated Circuit adalah serial komunikasi dua arah yang dapat mengirim dan menerima data. I2C memiliki dua saluran, yaitu Serial Clock (SCL) dan Serial Data (SDA). External device yang dihubungkan dengan I2C bisa menjadi master atau slave dimana master merupakan device yang memulai trasfer dengan membentuk sinyal start dan mengakhirinya dengan membentuk sinyal stop. Sementara itu, slave merupakan device yang diatur oleh master.

Dengan menggunakan I2C, kita bisa memenghubungkan:

  • multiple slaves to one master: contohnya, ESP32 membaca input dengan menggunakan sensor BME280 dan memberikan output dengan menggunakan OLED Display.
  • multiple masters controlling the same slave: contohnya, dua ESP32 menuliskan data ke OLED Display yang sama.

SCL dan SDA pada ESP32 sebaiknya dihubungkan dengan resistor 4.7k Ohm untuk device yang menggunakan tegangan 5V dan resistor 2.4k Ohm untuk device yang menggunakan tegangan 3.3V. Namun, sensor yang kita gunakan biasanya sudah memiliki resistor built-in sehingga kita tidak perlu lagi menggunakan resistor.

Menghubungkan perangkan I2C dengan dengan ESP32 dapat dilakukan dengan cara berikut:
GND dihubungkan dengan GND pada ESP32.
VCC dihubungkan dengan sumber tegangan 3.3V.
SCL dihubungkan dengan GPIO22.
SDA dihubungkan dengan GPIO21.
Saat menggunakan ESP32 dengan Arduino IDE, GPIO22 (SCL) dan GPIO21 (SDA) merupakan default pin, tetapi kita bisa mengganti pin tersebut sesuai keinginan kita dengan mengonfigurasinya pada code kita.

Komponen

  1. Breadboard
  2. ESP32 Development Board
  3. BME280 Sensor
  4. OLED Display
  5. Male-to-male jumper wire
  6. USB Type-B
  7. Laptop

Scan I2C Address dengan ESP32

Melalui I2C communication, setiap slave memiliki address berupa angka hexadesimal yang memungkinkan ESP32 untuk berkomunikasi dengan setiap device. I2C address ini biasanya bisa ditemukan di datasheet komponen. Namun, jika tidak ditemukan kita bisa mengeceknya dengan menggunakan code berikut

/*********
Rui Santos
Complete project details at https://randomnerdtutorials.com
*********/

#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);
}

atau bisa juga dengan mengambil code dari https://randomnerdtutorials.com/esp32-i2c-communication-arduino-ide/. Address dari komponen dapat dilihat pada Serial Monitor.

Mengganti Default Pin I2C

Seperti yang sudah dijelaskan sebelumnya, kita bisa mengganti default pin sesuai dengan keinginan kita. Ketika menggunakan ESP32 dengan Arduino IDE, library Wire.h digunakan untuk melakukan komunikasi dengan device yang menggunakan I2C. Dengan libray tersebut, I2C dapat diinisialisasi dengan cara berikut

Wire.begin(I2C_SDA, I2C_SCL);

I2C_SDA dan I2C_SCL merupakan variabel yang berisi GPIO yang ingin digunakan.

Namun, jika kita menggunakan library untuk berkomunikasi dengan sensor, hal ini belum tentu berhasil dan akan sedikit sulit untuk menggunakan pin lain sebagai SCL dan SDA. Penyebabnya adalah library akan melakukan overwrite terhadap pin yang kita inginkan jika kita tidak meneruskan Wire instance kita sendiri ketika menginisialisasi library.

Pada kasus tersebut, kita harus melihat cara meneruskan parameter TwoWire di file .cpp pada library. Adafruit BME280 library memungkinkan kita untuk menrusakan TwoWire ke method begin().

Di bawah ini adalah contoh code untuk mengganti pin SCL dengan GPIO5 dan SDA degan GPIO4. Sebenernya, code ini didapat dari https://randomnerdtutorials.com/esp32-i2c-communication-arduino-ide/ tapi dengan sedikit perubahan pada GPIO yang digunakan karena breadboard-nya tidak cukup kalau menggunakan GPIO32 dan GPIO33.

#include <Wire.h>
#include <Adafruit_Sensor.h>
#include <Adafruit_BME280.h>
#define I2C_SDA 4
#define I2C_SCL 5
#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;// default settings
// (you can also pass in a Wire library object like &Wire2)
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");

// Convert temperature to Fahrenheit
/*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();
}

Penjelasan

Langkah pertama adalah mendefinisikan I2C pin yang akan digunakan pada variabel I2C_SDA dan I2C_SCL.

#define I2C_SDA 4
#define I2C_SCL 5

Kemudian, buat instance TwoWire dengan nama I2CBME.

TwoWire I2CBME = TwoWire(0);

Pada setup(), lakukan inisialisasi I2C communication dengan pin yang telah didefinisikan seblumnya. Parameter ketiga merupakan frekuensi dari clock.

I2CBME.begin(I2C_SDA, I2C_SCL, 400000);

Selanjutnya, BME280 diinisialisasi dengan address dari sensor dan TwoWire.

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

Code sisanya adalah code yang bisa diperoleh dari project sebelumnya saat kita belajar menggunakan sensor BME280.

Schema

Percobaan

Pada awa percobaan, sempat terjadi error saat menggunakan GPIO18 dan GPIO19. Masih belum tau kenapa, mungkin ada yang tertukar. Namun, saat mencoba mengganti pin yang digunakan dengan GPIO4 dan GPIO5 percobaan ini berjalan dengan lancar.

Multiple I2C Device (address berbeda)

Pada percobaan ini, kita menggunakan OLED Display dan juga sensor BME280 dimana keduanya memiliki address yang berbeda. Cara memasangnya adalah dengan menghubungkan keduanya dengan SCL dan SDA dari ESP32 dan pada code, referensikan keduanya dengan address-nya masing-masing.

Code di bawah ini juga didapat dari https://randomnerdtutorials.com/esp32-i2c-communication-arduino-ide/ , tetapi dengan sedikit tambahan, yaitu display juga akan menampilkan pressure dan perkiraan altitude.

#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <Adafruit_Sensor.h>
#include <Adafruit_BME280.h>
#define SCREEN_WIDTH 128 // OLED display width, in pixels
#define SCREEN_HEIGHT 64 // OLED display height, in pixels
#define SEALEVELPRESSURE_HPA (1013.25)
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(3000);display.clearDisplay();
// display pressure
display.setTextSize(1);
display.setCursor(0,0);
display.print("Pressure: ");
display.setTextSize(2);
display.setCursor(0,10);
display.print(String(bme.readPressure()/100.0F));
display.print(" hPa");
// display altitude
display.setTextSize(1);
display.setCursor(0, 35);
display.print("Approx. Altitude: ");
display.setTextSize(2);
display.setCursor(0, 45);
display.print(String(bme.readAltitude(SEALEVELPRESSURE_HPA)));
display.print(" %");
display.display();delay(3000);
}

Karena OLED (0x3c) dan BME280 (0x76) memiliki address yang berbeda, kita dapat menggunakan SCL dan SDA yang sama tanpa adanya masalah.

Schema

Percobaan

Percobaan ini juga berjalan dengan lancar, walaupun sempat tidak berhasil saat menambahkan pressure dan perkiraan altitude. Namun, hal itu pure terjadi karena kesalahan code.

NIM : 18219084
Nama : Anindha Catur Arifani

--

--

No responses yet