Skip to content

I2C CODE

Component Architecture

- driver
    - node_i2c
        - include
            - node_i2c.h
        - node_i2c.c
        - CMakeLists.txt

driver/node_i2c/CMakeLists.txt

set(src_dirs
    .
)

set(include_dirs
    include
)

set(requires
    driver
    log
)

idf_component_register(SRC_DIRS ${src_dirs} INCLUDE_DIRS ${include_dirs} REQUIRES ${requires})

Note

Note that in the drivers, we used i2c and gpio related functions from the builtin driver library, therefore, we need to indicate these dependencies in the CMakeLists.txt file by adding driver to the REQUIRES field.

node_i2c.h

/**
 * @file node_i2c.h
 * @author SHUAIWEN CUI (SHUAIWEN001@e.ntu.edu.sg)
 * @brief This file contains the function prototypes for i2c master initialization. This is to serve the peripherals that require I2C communication.
 * @version 1.0
 * @date 2025-10-22
 *
 * @copyright Copyright (c) 2025
 *
 */

#pragma once

#ifdef __cplusplus
extern "C"
{
#endif

#include <stdio.h>
#include "esp_log.h"
#include "driver/i2c_master.h"
#include "esp_system.h"

#define I2C_MASTER_SCL_IO 10       /*!< gpio number for I2C master clock */
#define I2C_MASTER_SDA_IO 11       /*!< gpio number for I2C master data  */
#define I2C_MASTER_NUM I2C_NUM_0  /*!< I2C port number for master dev */
#define I2C_MASTER_FREQ_HZ 100000 /*!< I2C master clock frequency */

/* I2C Bus and Device Handles */
extern i2c_master_bus_handle_t i2c_bus_handle;

   /**
    * @brief i2c master bus initialization
    * @return esp_err_t ESP_OK on success, error code on failure
    */
   esp_err_t i2c_bus_init(void);

   /**
    * @brief i2c master bus deinitialization
    * @return esp_err_t ESP_OK on success, error code on failure
    */
   esp_err_t i2c_bus_deinit(void);

   /**
    * @brief add i2c device to bus
    * @param device_addr I2C device address
    * @param scl_speed_hz SCL clock speed in Hz
    * @param dev_handle pointer to store device handle
    * @return esp_err_t ESP_OK on success, error code on failure
    */
   esp_err_t i2c_add_device(uint8_t device_addr, uint32_t scl_speed_hz, i2c_master_dev_handle_t *dev_handle);

   /**
    * @brief remove i2c device from bus
    * @param dev_handle device handle to remove
    * @return esp_err_t ESP_OK on success, error code on failure
    */
   esp_err_t i2c_remove_device(i2c_master_dev_handle_t dev_handle);

   /**
    * @brief write data to i2c device
    * @param dev_handle device handle
    * @param data data to write
    * @param data_len length of data
    * @param timeout_ms timeout in milliseconds
    * @return esp_err_t ESP_OK on success, error code on failure
    */
   esp_err_t i2c_write_data(i2c_master_dev_handle_t dev_handle, const uint8_t *data, size_t data_len, int timeout_ms);

   /**
    * @brief read data from i2c device
    * @param dev_handle device handle
    * @param data buffer to store read data
    * @param data_len length of data to read
    * @param timeout_ms timeout in milliseconds
    * @return esp_err_t ESP_OK on success, error code on failure
    */
   esp_err_t i2c_read_data(i2c_master_dev_handle_t dev_handle, uint8_t *data, size_t data_len, int timeout_ms);

   /**
    * @brief write then read data from i2c device (combined transaction)
    * @param dev_handle device handle
    * @param write_data data to write
    * @param write_len length of write data
    * @param read_data buffer to store read data
    * @param read_len length of data to read
    * @param timeout_ms timeout in milliseconds
    * @return esp_err_t ESP_OK on success, error code on failure
    */
   esp_err_t i2c_write_read_data(i2c_master_dev_handle_t dev_handle, 
                                 const uint8_t *write_data, size_t write_len,
                                 uint8_t *read_data, size_t read_len, 
                                 int timeout_ms);

#ifdef __cplusplus
}
#endif

node_i2c.c

/**
 * @file node_i2c.c
 * @author SHUAIWEN CUI (SHUAIWEN001@e.ntu.edu.sg)
 * @brief This file contains the functions for i2c master initialization using ESP-IDF 6.0 i2c_master driver.
 * @version 1.0
 * @date 2025-10-22
 *
 * @copyright Copyright (c) 2025
 *
 */
#include "node_i2c.h"

#ifdef __cplusplus
extern "C" {
#endif

/* Global I2C Bus Handle */
i2c_master_bus_handle_t i2c_bus_handle = NULL;

/**
 * @brief i2c master bus initialization using ESP-IDF 6.0 i2c_master driver
 * @return esp_err_t ESP_OK on success, error code on failure
 */
esp_err_t i2c_bus_init(void)
{
    esp_err_t ret;

    /* Configure I2C master bus */
    i2c_master_bus_config_t i2c_bus_config = {
        .clk_source = I2C_CLK_SRC_DEFAULT,
        .i2c_port = I2C_MASTER_NUM,
        .scl_io_num = I2C_MASTER_SCL_IO,
        .sda_io_num = I2C_MASTER_SDA_IO,
        .glitch_ignore_cnt = 7,
        .flags.enable_internal_pullup = true
    };

    ret = i2c_new_master_bus(&i2c_bus_config, &i2c_bus_handle);
    if (ret != ESP_OK) {
        ESP_LOGE("I2C", "I2C bus creation failed: %s", esp_err_to_name(ret));
        return ret;
    }

    ESP_LOGI("I2C", "I2C master bus initialized successfully");
    return ESP_OK;
}

/**
 * @brief i2c master bus deinitialization
 * @return esp_err_t ESP_OK on success, error code on failure
 */
esp_err_t i2c_bus_deinit(void)
{
    esp_err_t ret = ESP_OK;

    if (i2c_bus_handle != NULL) {
        ret = i2c_del_master_bus(i2c_bus_handle);
        if (ret != ESP_OK) {
            ESP_LOGE("I2C", "I2C bus deletion failed: %s", esp_err_to_name(ret));
            return ret;
        }
        i2c_bus_handle = NULL;
    }

    ESP_LOGI("I2C", "I2C master bus deinitialized successfully");
    return ESP_OK;
}

/**
 * @brief add i2c device to bus
 * @param device_addr I2C device address
 * @param scl_speed_hz SCL clock speed in Hz
 * @param dev_handle pointer to store device handle
 * @return esp_err_t ESP_OK on success, error code on failure
 */
esp_err_t i2c_add_device(uint8_t device_addr, uint32_t scl_speed_hz, i2c_master_dev_handle_t *dev_handle)
{
    esp_err_t ret;

    if (i2c_bus_handle == NULL) {
        ESP_LOGE("I2C", "I2C bus not initialized");
        return ESP_ERR_INVALID_STATE;
    }

    i2c_device_config_t dev_cfg = {
        .dev_addr_length = I2C_ADDR_BIT_LEN_7,
        .device_address = device_addr,
        .scl_speed_hz = scl_speed_hz
    };

    ret = i2c_master_bus_add_device(i2c_bus_handle, &dev_cfg, dev_handle);
    if (ret != ESP_OK) {
        ESP_LOGE("I2C", "I2C device addition failed: %s", esp_err_to_name(ret));
        return ret;
    }

    ESP_LOGI("I2C", "I2C device 0x%02X added successfully", device_addr);
    return ESP_OK;
}

/**
 * @brief remove i2c device from bus
 * @param dev_handle device handle to remove
 * @return esp_err_t ESP_OK on success, error code on failure
 */
esp_err_t i2c_remove_device(i2c_master_dev_handle_t dev_handle)
{
    esp_err_t ret = i2c_master_bus_rm_device(dev_handle);
    if (ret != ESP_OK) {
        ESP_LOGE("I2C", "I2C device removal failed: %s", esp_err_to_name(ret));
        return ret;
    }

    ESP_LOGI("I2C", "I2C device removed successfully");
    return ESP_OK;
}

/**
 * @brief write data to i2c device
 * @param dev_handle device handle
 * @param data data to write
 * @param data_len length of data
 * @param timeout_ms timeout in milliseconds
 * @return esp_err_t ESP_OK on success, error code on failure
 */
esp_err_t i2c_write_data(i2c_master_dev_handle_t dev_handle, const uint8_t *data, size_t data_len, int timeout_ms)
{
    esp_err_t ret = i2c_master_transmit(dev_handle, data, data_len, timeout_ms);
    if (ret != ESP_OK) {
        ESP_LOGE("I2C", "I2C write failed: %s", esp_err_to_name(ret));
        return ret;
    }

    ESP_LOGD("I2C", "I2C write successful, %d bytes", data_len);
    return ESP_OK;
}

/**
 * @brief read data from i2c device
 * @param dev_handle device handle
 * @param data buffer to store read data
 * @param data_len length of data to read
 * @param timeout_ms timeout in milliseconds
 * @return esp_err_t ESP_OK on success, error code on failure
 */
esp_err_t i2c_read_data(i2c_master_dev_handle_t dev_handle, uint8_t *data, size_t data_len, int timeout_ms)
{
    esp_err_t ret = i2c_master_receive(dev_handle, data, data_len, timeout_ms);
    if (ret != ESP_OK) {
        ESP_LOGE("I2C", "I2C read failed: %s", esp_err_to_name(ret));
        return ret;
    }

    ESP_LOGD("I2C", "I2C read successful, %d bytes", data_len);
    return ESP_OK;
}

/**
 * @brief write then read data from i2c device (combined transaction)
 * @param dev_handle device handle
 * @param write_data data to write
 * @param write_len length of write data
 * @param read_data buffer to store read data
 * @param read_len length of data to read
 * @param timeout_ms timeout in milliseconds
 * @return esp_err_t ESP_OK on success, error code on failure
 */
esp_err_t i2c_write_read_data(i2c_master_dev_handle_t dev_handle, 
                              const uint8_t *write_data, size_t write_len,
                              uint8_t *read_data, size_t read_len, 
                              int timeout_ms)
{
    esp_err_t ret = i2c_master_transmit_receive(dev_handle, write_data, write_len, read_data, read_len, timeout_ms);
    if (ret != ESP_OK) {
        ESP_LOGE("I2C", "I2C write-read failed: %s", esp_err_to_name(ret));
        return ret;
    }

    ESP_LOGD("I2C", "I2C write-read successful, wrote %d bytes, read %d bytes", write_len, read_len);
    return ESP_OK;
}

#ifdef __cplusplus
}
#endif

main.c

#include "node_i2c.h"

void app_main(void) {
    // 1. initialize the i2c bus
    esp_err_t ret = i2c_bus_init();
    if (ret != ESP_OK) return;

    // 2. add i2c device to bus
    i2c_master_dev_handle_t dev_handle;
    ret = i2c_add_device(0x68, 400000, &dev_handle);
    if (ret != ESP_OK) return;

    // 3. write data to i2c device
    uint8_t data[] = {0x75};
    i2c_write_data(dev_handle, data, 1, 1000);

    uint8_t response[1];
    i2c_read_data(dev_handle, response, 1, 1000);

    // 4. clean up resources
    i2c_remove_device(dev_handle);
    i2c_bus_deinit();
}

main.cpp

/**
 * @file main.cpp
 * @author SHUAIWEN CUI (SHUAIWEN001@e.ntu.edu.sg)
 * @brief I2C Test Application
 * @version 1.0
 * @date 2025-10-22
 * 
 * @copyright Copyright (c) 2024
 * 
 */

#include "node_i2c.h"

#ifdef __cplusplus
extern "C" {
#endif

/**
 * @brief Entry point of the program
 * @param None
 * @retval None
 */
void app_main(void) {
    // 1. initialize the i2c bus
    esp_err_t ret = i2c_bus_init();
    if (ret != ESP_OK) return;

    // 2. add i2c device to bus
    i2c_master_dev_handle_t dev_handle;
    ret = i2c_add_device(0x68, 400000, &dev_handle);
    if (ret != ESP_OK) return;

    // 3. write data to i2c device
    uint8_t data[] = {0x75};
    i2c_write_data(dev_handle, data, 1, 1000);

    uint8_t response[1];
    i2c_read_data(dev_handle, response, 1, 1000);

    // 4. clean up resources
    i2c_remove_device(dev_handle);
    i2c_bus_deinit();
}

#ifdef __cplusplus
}
#endif

Note

Please note that, the code in this chapter should be used in conjunction with other components that use the i2c to see the effect. Before you use the components based on i2c communication, you need to initialize the i2c bus by calling the i2c_bus_init() function in the main.c file.