I2C is the bus you will use most on an ESP32. OLED displays, temperature and pressure sensors, real-time clocks, motion sensors, LCD backpacks and ADC chips almost all talk I2C, and the best part is that they all share the same two wires. Wire an OLED, a BME280 and an RTC to the same pair of pins and the ESP32 talks to each one by address.
That sharing is also where things go wrong. Two devices with the same address, a missing pull-up resistor, a 5V module on a 3.3V bus or a swapped SDA and SCL will all produce the same symptom: nothing works and the error message is unhelpful. This guide covers the default pins, how to move them, how to find every device on the bus, how to run several at once, and how to fix the usual failures.
ESP32 I2C Pins
The ESP32 has two hardware I2C controllers, and thanks to the GPIO matrix each can use almost any pins. The Arduino core's defaults are:
| Signal | Default GPIO | Function |
|---|---|---|
| SDA | 21 | Serial data |
| SCL | 22 | Serial clock |
These are the pins every library assumes unless told otherwise, so use them when you can. They are the pins labeled SDA and SCL on most dev boards (see the ESP32 pinout guide).
If you need other pins, one line changes them:
#include <Wire.h>
void setup() {
Wire.begin(25, 26); // SDA = GPIO 25, SCL = GPIO 26
}
Pick output-capable pins that are not strapping pins. GPIO 34 to 39 are input only and cannot be used for I2C, because both lines must be able to drive low.
Bus speed. The default is 100 kHz (standard mode). Most devices support 400 kHz (fast mode):
Wire.begin(21, 22);
Wire.setClock(400000);
Use 100 kHz when you have long wires or many devices and see unreliable readings.
How I2C Works in 60 Seconds
I2C uses two wires plus power:
- SDA: data, both directions.
- SCL: clock, driven by the controller (the ESP32).
- Every device has a 7-bit address. The ESP32 sends the address first, and only the matching device answers.
- Both lines are open-drain: devices only pull them low and a pull-up resistor brings them high again. Without pull-ups, the bus does not work.
You can connect many devices in parallel on the same two wires, as long as no two share an address and the total wiring stays short.
For a comparison with the other common protocol, see I2C vs SPI.
Step 1: Scan the Bus
Before writing any library code, confirm the ESP32 can see your device. This I2C scanner prints the address of everything it finds:
#include <Wire.h>
void setup() {
Serial.begin(115200);
Wire.begin(21, 22);
delay(500);
Serial.println("\nScanning I2C bus...");
int found = 0;
for (byte addr = 1; addr < 127; addr++) {
Wire.beginTransmission(addr);
if (Wire.endTransmission() == 0) {
Serial.printf("Device found at 0x%02X\n", addr);
found++;
}
}
if (found == 0) Serial.println("No I2C devices found");
Serial.println("Scan complete");
}
void loop() {
}
With an OLED and a BME280 connected you might see:
Device found at 0x3C
Device found at 0x76
If nothing appears, the problem is wiring, power or pull-ups, not your code. Run this scanner first every time you add a new I2C device.
Common I2C Addresses
| Device | Typical address | Alternate |
|---|---|---|
| SSD1306 OLED (128x64) | 0x3C | 0x3D |
| BME280 / BMP280 | 0x76 | 0x77 |
| MPU6050 IMU | 0x68 | 0x69 |
| DS3231 RTC | 0x68 | none (fixed) |
| 16x2 LCD with PCF8574 backpack | 0x27 | 0x3F |
| BH1750 light sensor | 0x23 | 0x5C |
| ADS1115 ADC | 0x48 | 0x49, 0x4A, 0x4B |
| PCA9685 servo driver | 0x40 | up to 0x7F with jumpers |
| INA219 current sensor | 0x40 | 0x41, 0x44, 0x45 |
| AHT20 temperature and humidity | 0x38 | none |
Boards differ, so trust your scanner over any table. Notice that MPU6050 and DS3231 both use 0x68, which is a classic conflict covered below.
Step 2: Connect Multiple Devices on One Bus
Wire every device's SDA to GPIO 21, every SCL to GPIO 22, every VCC to 3.3V and every GND to GND. The wiring is just a shared pair of rails.
| Device pin | ESP32 pin |
|---|---|
| SSD1306 SDA, BME280 SDA | GPIO 21 |
| SSD1306 SCL, BME280 SCL | GPIO 22 |
| VCC (both) | 3.3V |
| GND (both) | GND |
This sketch reads a BME280 and shows the result on an SSD1306 OLED. Install Adafruit BME280, Adafruit SSD1306 and Adafruit GFX from the Library Manager.
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <Adafruit_BME280.h>
Adafruit_SSD1306 display(128, 64, &Wire, -1);
Adafruit_BME280 bme;
void setup() {
Serial.begin(115200);
Wire.begin(21, 22);
if (!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {
Serial.println("OLED not found");
while (true) delay(1000);
}
if (!bme.begin(0x76)) {
Serial.println("BME280 not found");
while (true) delay(1000);
}
display.setTextColor(SSD1306_WHITE);
}
void loop() {
float t = bme.readTemperature();
float h = bme.readHumidity();
float p = bme.readPressure() / 100.0F;
display.clearDisplay();
display.setTextSize(1);
display.setCursor(0, 0);
display.printf("Temp: %.1f C\n", t);
display.printf("Hum: %.0f %%\n", h);
display.printf("Pres: %.0f hPa\n", p);
display.display();
delay(1000);
}
Both devices respond only to their own address (0x3C and 0x76), so they never interfere. You can add a third or fourth device the same way, as long as the addresses are unique.
Fixing Address Conflicts
If two devices share an address, the bus returns garbage or only one of them answers. You have four ways out.
1. Change the address with a jumper or solder pad. Many breakouts have an address pin. The BME280 moves from 0x76 to 0x77 by tying SDO to VCC, and the MPU6050 moves from 0x68 to 0x69 by tying AD0 high. This is the simplest fix.
2. Use the ESP32's second I2C bus. Put the conflicting device on its own pins:
#include <Wire.h>
void setup() {
Wire.begin(21, 22); // bus 0: OLED, BME280
Wire1.begin(25, 26); // bus 1: MPU6050 and DS3231 can both be 0x68 here, one per bus
// pass the bus to a library that supports it, for example:
// Adafruit_BME280 bme2; bme2.begin(0x77, &Wire1);
}
3. Use an I2C multiplexer. A TCA9548A gives you eight separate sub-buses behind one address (0x70 by default) and is the standard answer for many identical sensors.
4. Choose a different part. If a device has a fixed address and a conflict with something you cannot move, swap one component.
The classic conflict is MPU6050 (0x68) with DS3231 (0x68). Using the MPU6050 on its 0x69 address is the cheapest fix.
Pull-Up Resistors
I2C needs pull-ups on SDA and SCL, typically 4.7 kΩ to 3.3V. Most breakout boards include them, so a single sensor usually works with nothing added. Problems appear in two situations:
- Too few pull-ups. A bare chip or a board with no resistors. Symptom: the scanner finds nothing. Add a 4.7 kΩ resistor from SDA to 3.3V and another from SCL to 3.3V.
- Too many pull-ups. Each breakout board adds its own resistors in parallel, and with four or five boards the total resistance becomes too low for the devices to pull the line down. Symptom: unreliable communication. Remove pull-ups from some boards, or measure the combined value.
The ESP32's internal pull-ups (roughly 45 kΩ) are too weak for reliable I2C at speed, although the Wire library enables them by default, which is why short and simple setups sometimes work without resistors. Do not rely on them.
Voltage levels. The ESP32 is a 3.3V device. A module with pull-ups to 5V pulls the bus to 5V, which stresses the ESP32 pins. Power I2C modules from 3.3V where possible, and use a bidirectional level shifter for 5V-only modules.
Troubleshooting I2C on the ESP32
Scanner finds nothing. Check in this order: power on the module (3.3V and GND), SDA and SCL not swapped, solid connections, and pull-ups present. Then try a slower clock with Wire.setClock(100000).
Error "i2cRead returned Error 263" or "requestFrom(): i2cRead returned Error". The ESP32 sent a request and got no reply. Usually a wrong address, a loose wire or a missing pull-up.
Device found at an unexpected address. Many boards have an address pad. Use the address your scanner reports, not the one in the tutorial.
It works alone but fails with two devices. Check for an address conflict, or too many parallel pull-up resistors.
Random freezes after hours. Add a 100 nF capacitor near each sensor, shorten cables (I2C is designed for centimeters, not meters), and lower the clock speed.
Library cannot find the device even though the scanner can. The library may be using different pins. Call Wire.begin(sda, scl) before the library's begin(), and pass &Wire1 if the device is on the second bus.
Long cables. Keep I2C wiring under about 30 to 50 cm. For longer runs use lower speeds and stronger pull-ups, or move to a protocol designed for distance such as RS485.
Frequently Asked Questions
Which ESP32 pins are I2C? GPIO 21 (SDA) and GPIO 22 (SCL) by default, and any other output-capable pins you choose with Wire.begin(sda, scl).
How many I2C devices can I connect? The 7-bit address space allows up to 112 usable addresses, but in practice electrical limits of the bus capacitance cap you at around eight to ten devices on short wires.
Can I use I2C and WiFi at the same time? Yes. They do not conflict.
Do I need to set pinMode for I2C pins? No. Wire.begin() configures them.
Is I2C or SPI better for ESP32? I2C uses fewer wires and is ideal for slow sensors and small displays. SPI is faster and better for large displays and SD cards. Details in I2C vs SPI and the SPI guide.
Where can I see every pin? The ESP32 pinout guide maps every pin and covers the board layout.