### I2C Basics Guide
#### What is I2C?
I2C (Inter-Integrated Circuit) is a serial communication protocol for connecting low-speed peripherals (e.g., sensors) to microcontrollers using two wires: SDA (data) and SCL (clock).
#### SDA and SCL Purpose
- **SDA (Serial Data)**: Bidirectional line for transmitting/receiving data bits.
- **SCL (Serial Clock)**: Generated by the master device to synchronize data transfer.
#### ESP32-S3 Default SDA/SCL GPIOs
Defaults: SDA = GPIO 8, SCL = GPIO 9 (configurable in code).
We can use this code to printout the default SDA and SCL for any MCU in Arduino IDE
``` cpp
void setup() {
Serial.begin(115200);
delay(3000);
Serial.print("SDA: ");Serial.println(SDA);
Serial.print("SCL: ");Serial.println(SCL);
}
void loop() {}
```

#### Routing and Using Any Pin for I2C on ESP32-S3
ESP32-S3 supports multiple I2C buses via software configuration. In Arduino/ESP-IDF:
- Use `Wire.begin(SDA_PIN, SCL_PIN);` to assign any available GPIOs (e.g., GPIO 4 for SDA, GPIO 5 for SCL).
- For multiple buses: Use `TwoWire I2C1 = TwoWire(1);` then `I2C1.begin(SDA_PIN, SCL_PIN);`.
- Ensure pins are not multiplexed with other functions; add pull-ups if needed.
### Verifying/Retrieving Current SDA and SCL GPIO Numbers via Wire Library
In the Arduino Wire library for ESP32-S3, the pins set via Wire.begin(SDA_PIN, SCL_PIN) are stored in protected class members (_sda_pin and _scl_pin). There's no public getter, but you can use a preprocessor hack to access them for verification at runtime.
``` cpp
// Hack to access protected members (use only for testing)
#define protected public
#include <Wire.h>
#undef protected
#define SDA_PIN 4
#define SCL_PIN 5
void setup() {
Serial.begin(115200);
delay(3000);
Wire.begin(SDA_PIN, SCL_PIN);
Serial.print("SDA GPIO used: ");Serial.println(Wire.sda);
Serial.print("SCL GPIO used: ");Serial.println(Wire.scl);
}
void loop() {}
```

#### I2C Address
Each device has a unique 7-bit address (0x00–0x7F) for identification on the bus. Multiple devices share the same lines; the master selects by sending the address. Some devices allow address configuration via pins.
#### I2C Scan Code (Arduino/ESP Example)
Use this code to scan for connected devices:
```cpp
#include <Wire.h>
void setup() {
Wire.begin();
Serial.begin(115200);
Serial.println("I2C Scanner");
}
void loop() {
byte error, address;
int nDevices = 0;
for (address = 1; address < 127; address++) {
Wire.beginTransmission(address);
error = Wire.endTransmission();
if (error == 0) {
Serial.print("Device at 0x");
if (address < 16) Serial.print("0");
Serial.println(address, HEX);
nDevices++;
}
}
if (nDevices == 0) Serial.println("No devices found");
delay(5000); // Scan every 5s
}
```
#### Why External Pull-Up Resistors Needed?
If you are unable to get the I2C address, probably the external pull-up are required for both I2C line. I2C uses open-drain outputs, so lines are pulled low by devices but need resistors to pull high (logic 1). Many sensors have built-in pull-ups; if not, add 4.7K–10KΩ resistors from SDA/SCL to VCC (e.g., 3.3V/5V) to ensure reliable signaling.

- Choosing a selection results in a full page refresh.