Fundamentals of Peripheral Control with ESP32: I2C Communication

1. Basics of the I2C Protocol

I2C (Inter-Integrated Circuit) is a two-wire serial communication protocol with the following core features:

  • Two-wire Communication: Only requires two signal lines, SCL (clock line) and SDA (data line)
  • Multi-master Multi-slave Architecture: Supports multiple master and slave devices
  • Addressing: 7-bit or 10-bit slave addresses (7-bit can address up to 112 devices)
  • Speed Grades:
    • Standard Mode: 100kHz
    • Fast Mode: 400kHz
    • High-speed Mode: 3.4MHz

Typical Application Scenarios

  • Temperature and Humidity Sensors (SHT30, BME280)
  • OLED Displays (SSD1306)
  • Real-time Clock Modules (DS1307, DS3231)
  • EEPROM Memory (AT24C Series)

2. ESP32 I2C Hardware Features

The ESP32 integrates two I2C controllers:

Controller Operating Mode Maximum Frequency Features
I2C0 Master/Slave Mode 1MHz Pin Remapping
I2C1 Master/Slave Mode 1MHz Pin Remapping

Key Advantages:

  • Hardware FIFO buffer improves communication efficiency
  • Supports Clock Stretching
  • Programmable digital noise filtering
  • Supports 10-bit addressing mode

3. ESP-IDF I2C Programming Interface

1. Configuration Structure

typedef struct {
    i2c_mode_t mode;             // Operating mode
    gpio_num_t sda_io_num;       // SDA pin
    gpio_pullup_t sda_pullup_en; // SDA pull-up enable
    gpio_num_t scl_io_num;       // SCL pin
    gpio_pullup_t scl_pullup_en; // SCL pull-up enable
    uint32_t clk_speed;          // Clock frequency (Hz)
    uint32_t clk_flags;          // Special clock flags
} i2c_config_t;

2. Common API Functions

Function Description
<span>i2c_param_config()</span> Configure I2C parameters
<span>i2c_driver_install()</span> Install I2C driver
<span>i2c_master_write_to_device()</span> Master writes data
<span>i2c_master_read_from_device()</span> Master reads data
<span>i2c_cmd_link_create()</span> Create command link
<span>i2c_cmd_link_delete()</span> Delete command link

4. I2C Communication Examples

1. Master Mode Initialization

#include "driver/i2c.h"
#include "esp_log.h"

#define I2C_PORT        I2C_NUM_0
#define SDA_PIN         GPIO_NUM_21
#define SCL_PIN         GPIO_NUM_22
#define I2C_FREQ_HZ     400000  // 400kHz

void i2c_master_init() {
    i2c_config_t conf = {
        .mode = I2C_MODE_MASTER,
        .sda_io_num = SDA_PIN,
        .sda_pullup_en = GPIO_PULLUP_ENABLE,
        .scl_io_num = SCL_PIN,
        .scl_pullup_en = GPIO_PULLUP_ENABLE,
        .clk_speed = I2C_FREQ_HZ,
    };

    ESP_ERROR_CHECK(i2c_param_config(I2C_PORT, &conf));
    ESP_ERROR_CHECK(i2c_driver_install(I2C_PORT, conf.mode, 0, 0, 0));
}

2. Data Write Example (OLED Control)

#define OLED_ADDR   0x3C

void oled_write_command(uint8_t cmd) {
    uint8_t buf[2] = {0x00, cmd}; // 0x00 is the command identifier
    ESP_ERROR_CHECK(i2c_master_write_to_device(
        I2C_PORT, 
        OLED_ADDR, 
        buf, 
        sizeof(buf), 
        pdMS_TO_TICKS(1000)
    ));
}

3. Data Read Example (SHT30 Sensor)

#define SHT30_ADDR  0x44

esp_err_t read_sht30(float *temp, float *humidity) {
    uint8_t cmd[2] = {0x2C, 0x06}; // Trigger measurement command
    uint8_t data[6];

    // Send measurement command
    ESP_ERROR_CHECK(i2c_master_write_to_device(
        I2C_PORT, SHT30_ADDR, cmd, sizeof(cmd), pdMS_TO_TICKS(100)
    ));

    vTaskDelay(pdMS_TO_TICKS(20)); // Wait for measurement to complete

    // Read data
    ESP_ERROR_CHECK(i2c_master_read_from_device(
        I2C_PORT, SHT30_ADDR, data, sizeof(data), pdMS_TO_TICKS(100)
    ));

    // Data conversion
    uint16_t raw_temp = (data[0] << 8) | data[1];
    uint16_t raw_hum = (data[3] << 8) | data[4];

    *temp = -45 + 175 * (float)raw_temp / 65535;
    *humidity = 100 * (float)raw_hum / 65535;

    return ESP_OK;
}

5. Advanced Application Techniques

1. I2C Device Scanning

void i2c_scanner() {
    printf("Scanning I2C bus...\n");
    for (int addr = 1; addr < 127; addr++) {
        esp_err_t ret = i2c_master_write_to_device(
            I2C_PORT, addr, NULL, 0, pdMS_TO_TICKS(50)
        );
        if (ret == ESP_OK) {
            printf("Found device at 0x%02X\n", addr);
        }
    }
    printf("Scan completed\n");
}

2. Composite Operation (Write + Read)

esp_err_t i2c_write_then_read(uint8_t addr, 
    uint8_t *write_buf, size_t write_len,
    uint8_t *read_buf, size_t read_len) {
    i2c_cmd_handle_t cmd = i2c_cmd_link_create();

    // Write phase
    i2c_master_start(cmd);
    i2c_master_write_byte(cmd, (addr << 1) | I2C_MASTER_WRITE, true);
    if (write_len > 0) {
        i2c_master_write(cmd, write_buf, write_len, true);
    }

    // Read phase
    i2c_master_start(cmd);
    i2c_master_write_byte(cmd, (addr << 1) | I2C_MASTER_READ, true);
    if (read_len > 0) {
        i2c_master_read(cmd, read_buf, read_len, I2C_MASTER_LAST_NACK);
    }
    i2c_master_stop(cmd);

    esp_err_t ret = i2c_master_cmd_begin(I2C_PORT, cmd, pdMS_TO_TICKS(1000));
    i2c_cmd_link_delete(cmd);

    return ret;
}

6. Common Troubleshooting

  1. No Response from Device:

  • Check physical connections (Are SCL/SDA reversed?)
  • Confirm the slave address is correct (Use a scanning tool to verify)
  • Check pull-up resistors (Usually 4.7kΩ)
  • Data Errors:

    • Test by lowering the clock frequency
    • Check power stability
    • Shorten communication cable length
  • Unstable Communication:

    • Increase pull-up resistor value
    • Enable built-in I2C noise filtering
    • Avoid running parallel to other high-frequency signal lines

    7. Performance Optimization Suggestions

    1. High-speed Mode Configuration:

      <span><span>i2c_config_t</span></span><span><span> conf = {</span></span><span><span> .clk_speed = </span></span><span><span>1000000</span></span><span><span>, </span></span><span><span>// 1MHz</span></span><span><span> .clk_flags = I2C_SCLK_SRC_FLAG_FOR_NORMAL </span></span><span><span>// Use main clock source</span></span><span> };</span>

    2. DMA Transfer:

      <span><span> i2c_driver_install(I2C_PORT, I2C_MODE_MASTER, </span></span><span><span>8</span></span><span><span> * </span></span><span><span>1024</span></span><span><span>, </span></span><span><span>8</span></span><span><span> * </span></span><span><span>1024</span></span><span><span>, </span></span><span><span>0</span></span><span><span>); </span></span><span><span>// 8KB RX/TX buffer</span></span>

    3. Low Power Optimization:
    • Enter sleep mode after communication is complete
    • Reduce operating voltage (if the device supports it)

    Leave a Comment