ESP32 Basics: I2C Scanner for Detecting I2C Devices
Introduction
The I2C (Inter-Integrated Circuit) bus is widely used for connecting sensors, displays, and other peripherals to microcontrollers like the ESP32. When working with I2C devices, one of the first challenges is determining which addresses these devices are using on the bus. The I2C scanner is a simple utility sketch that sweeps through all possible I2C addresses and reports which ones respond, helping you identify connected devices and their addresses.
ESP32 I2C Pins
The ESP32 has hardware I2C support and allows I2C communication on most GPIO pins. However, there are default pins that are commonly used:
- SDA (Data Line): GPIO 21 (default)
- SCL (Clock Line): GPIO 22 (default)
It's worth noting that the ESP32 supports two I2C controllers (I2C_NUM_0 and I2C_NUM_1), allowing for connections to multiple I2C buses if needed.
I2C Pull-up Resistors
I2C is an open-drain interface, which means it requires pull-up resistors on both SDA and SCL lines. While the ESP32 has internal pull-up resistors, external pull-up resistors (typically 4.7kΩ to 10kΩ) are recommended for reliable operation, especially with longer wires or multiple devices.
Note: Many I2C devices and development boards already include pull-up resistors. Before adding your own, check if they're already present to avoid too strong pull-ups.
I2C Addresses
I2C devices use 7-bit addresses (0x00 to 0x7F), but the Arduino Wire library typically shows 8-bit addresses including the read/write bit. Some address ranges are reserved:
- 0x00 - Reserved for general call address
- 0x01 - Reserved for CBUS address
- 0x02 - Reserved for different bus format
- 0x03 - Reserved for future purposes
- 0x04-0x07 - Reserved for high-speed controller code
- 0x78-0x7B - 10-bit slave addressing
- 0x7C-0x7F - Reserved for future purposes
I2C Scanner Code
Here's a reliable I2C scanner code for the ESP32. It will scan through all possible I2C addresses and report any detected devices in the Serial Monitor:
#include// Define default I2C pins for ESP32 const int SDA_PIN = 21; const int SCL_PIN = 22; void setup() { Serial.begin(115200); // Allow serial console to establish delay(1000); Serial.println("ESP32 I2C Scanner"); // Initialize I2C with specific pins Wire.begin(SDA_PIN, SCL_PIN); } void loop() { byte error, address; int deviceCount = 0; Serial.println("Scanning I2C bus..."); // Scan addresses from 1 to 127 // (Address 0 is the general call address and not typically used by devices) for(address = 1; address < 128; address++) { // The i2c_scanner uses the return value of // the Wire.endTransmission() function to see if // a device acknowledged the address. Wire.beginTransmission(address); error = Wire.endTransmission(); if (error == 0) { Serial.print("I2C device found at address 0x"); if (address < 16) { Serial.print("0"); // Print leading zero for addresses < 0x10 } Serial.print(address, HEX); // Try to identify common I2C devices String deviceName = identifyDevice(address); if (deviceName != "") { Serial.print(" - Possible device: "); Serial.print(deviceName); } Serial.println(); deviceCount++; } else if (error == 4) { Serial.print("Unknown error at address 0x"); if (address < 16) { Serial.print("0"); } Serial.println(address, HEX); } } if (deviceCount == 0) { Serial.println("No I2C devices found"); } else { Serial.print("Found "); Serial.print(deviceCount); Serial.println(" device(s)"); } // Wait 5 seconds before next scan Serial.println(); delay(5000); } // Function to identify common I2C devices based on their addresses String identifyDevice(byte address) { // This is a simplified list, many devices share addresses or have configurable addresses switch(address) { case 0x1E: return "HMC5883L/QMC5883L Magnetometer"; case 0x20: case 0x21: case 0x22: case 0x23: case 0x24: case 0x25: case 0x26: case 0x27: return "PCF8574/MCP23017 I/O Expander"; case 0x29: return "TSL2561/TSL2591 Light Sensor or VL53L0X ToF Sensor"; case 0x3C: case 0x3D: return "SSD1306/SH1106 OLED Display"; case 0x38: case 0x39: return "AHT10/AHT20 Temp & Humidity Sensor"; case 0x40: return "Si7021/HTU21D Humidity & Temp Sensor"; case 0x48: case 0x49: case 0x4A: case 0x4B: return "ADS1115/ADS1015 ADC"; case 0x50: case 0x51: case 0x52: case 0x53: case 0x54: case 0x55: case 0x56: case 0x57: return "AT24Cxx EEPROM"; case 0x68: return "DS3231/DS1307 RTC or MPU6050/MPU9250 Gyroscope"; case 0x76: case 0x77: return "BMP280/BME280 Pressure/Temp/Humidity"; case 0x5C: return "BH1750 Light Sensor"; default: return ""; } }
Example Usage
To use the I2C scanner:
- Connect your I2C devices to the ESP32 with proper connections:
- SDA (ESP32 GPIO 21) to SDA on your device
- SCL (ESP32 GPIO 22) to SCL on your device
- VCC and GND to appropriate power pins on your device
- Upload the scanner code to your ESP32
- Open the Serial Monitor at 115200 baud
- You'll see a list of detected I2C addresses and possible device identifications
Sample Output:
ESP32 I2C Scanner Scanning I2C bus... I2C device found at address 0x3C - Possible device: SSD1306/SH1106 OLED Display I2C device found at address 0x68 - Possible device: DS3231/DS1307 RTC or MPU6050/MPU9250 Gyroscope I2C device found at address 0x76 - Possible device: BMP280/BME280 Pressure/Temp/Humidity Found 3 device(s)
Using Custom I2C Pins
One of the advantages of the ESP32 is the ability to configure I2C on almost any GPIO pins. To use different pins, simply modify the SDA_PIN and SCL_PIN constants in the code:
// Define custom I2C pins for ESP32 const int SDA_PIN = 16; // Change to your desired SDA pin const int SCL_PIN = 17; // Change to your desired SCL pin // Initialize I2C with your custom pins Wire.begin(SDA_PIN, SCL_PIN);
Note: Some ESP32 pins have special functions or limitations. For reliable I2C communication, avoid using pins that are connected to the internal flash (GPIO6-GPIO11) or strapping pins (GPIO0, GPIO2, GPIO5, GPIO12, GPIO15).
Troubleshooting
If your I2C scanner isn't detecting devices:
- Check connections: Ensure SDA and SCL are connected to the correct pins
- Verify power: Check that your I2C devices are properly powered
- Pull-up resistors: Add 4.7kΩ pull-up resistors to both SDA and SCL lines
- Wire length: I2C has limited range; keep wires as short as possible
- Bus speed: Try reducing the I2C clock speed for longer wires or multiple devices:
Wire.setClock(100000); // Set to 100kHz (default is 400kHz)