Home / Peripherals / I²C

ESP32 I²C Guide: Default Pins, Pull-ups and Common Failures

Which two pins to use for I²C on the ESP32, what pull-up value to pick, and why your device never shows up on the bus scan.

The short version

On the original ESP32, I²C defaults to SDA = GPIO21 and SCL = GPIO22 in Arduino Core and most examples. That is not a hardware limit — I²C runs through the GPIO matrix, so almost any output-capable pin works. The real problems are the next three.

1. You must add external pull-ups

The ESP32's internal pull-ups are roughly 45 kΩ, far too weak for I²C. Rising edges crawl and 400 kHz will fail. Fit a 4.7 kΩ pull-up to 3.3 V on each line (2.2 kΩ for many devices or long runs).

Classic symptom: the scanner finds the address but reads back all 0x00 or 0xFF — or everything works only when you drop to 100 kHz. Nine times out of ten the pull-ups are too weak.

2. Avoid constrained pins

3. 7-bit vs 8-bit addresses

Datasheets usually quote the 8-bit address including the R/W bit (0xD0 / 0xD1 for the same MPU6050). ESP-IDF and Wire.beginTransmission() expect a 7-bit address, so shift the datasheet value right by one (0xD0 >> 1 = 0x68). Getting this wrong looks exactly like "no devices found".

Default pins by chip

ChipCommon default SDACommon default SCLNote
ESP32 (original)GPIO21GPIO22Arduino / most examples
ESP32-S2 / S3 / C3 / C6 / H2 / P4Verify against official docsDefaults have changed across ESP-IDF versions — check the documentation or soc headers for your target
Uncertain fields are left to the official documentation: I²C defaults for newer chips have moved between ESP-IDF releases, so this site quotes no number that could go stale. Always set the pins explicitly in your own code rather than relying on defaults.

Recovering a locked bus

If a slave is reset mid-transfer it may hold SDA low forever, killing the bus. The standard fix is for the master to emit nine clock pulses so the slave releases the line; failing that, power-cycle the slave. It is worth adding a bit-banged recovery routine that runs when init fails.

Data verified:2026-10-20 · Cross-checked against Espressif documentation. Anything that varies between chips or ESP-IDF releases is marked rather than guessed. Official peripheral docs →

Related