i2cdev - I2C master thread-safe functions for communication with I2C slave
- group i2cdev
ESP-IDF I2C master thread-safe functions for communication with I2C slave.
This implementation uses the newer ESP-IDF I2C master driver (v5.0+). For ESP-IDF versions using the legacy I2C driver, use i2cdev_legacy.c instead.
Copyright (C) 2018 Ruslan V. Uss unclerus@gmail.com Updated 2025 by quinkq to use newer ESP-IDF I2C master driver API
MIT Licensed as described in the file LICENSE
OPTIONAL I2C PULLUP AUTO-CONFIGURATION
This library can optionally enable internal I2C pullups when no explicit pullup configuration is provided. Feature is DISABLED by default for backward compatibility (CONFIG_I2CDEV_AUTO_ENABLE_PULLUPS=n).
Optional auto-pullup (CONFIG_I2CDEV_AUTO_ENABLE_PULLUPS=y):
If both pullup flags are false (not set/default state), automatically enables internal pullups
Only available on ESP32 family (modern driver)
Legacy driver always uses explicit configuration
Example - Enable internal pullups: i2c_dev_t sensor = { .port = I2C_NUM_0, .addr = 0x48, .cfg = { .sda_io_num = GPIO_NUM_21, .scl_io_num = GPIO_NUM_22, .sda_pullup_en = true, // Enable internal pullups .scl_pullup_en = true, // Enable internal pullups .master.clk_speed = 400000 } };
Defines
-
I2CDEV_MAX_STRETCH_TIME
-
CONFIG_I2CDEV_TIMEOUT
-
CONFIG_I2CDEV_NOLOCK
-
CONFIG_I2CDEV_MAX_DEVICES_PER_PORT
-
CONFIG_I2CDEV_DEFAULT_SDA_PIN
-
CONFIG_I2CDEV_DEFAULT_SCL_PIN
-
CONFIG_FREERTOS_HZ
-
CONFIG_LOG_MAXIMUM_LEVEL
-
I2C_DEV_TAKE_MUTEX(dev)
Take device mutex with error checking.
-
I2C_DEV_GIVE_MUTEX(dev)
Give device mutex with error checking.
-
I2C_DEV_CHECK(dev, X)
Execute operation, assuming mutex is held.
Gives mutex ONLY on error.
-
I2C_DEV_CHECK_LOGE(dev, X, msg, ...)
Execute operation, assuming mutex is held.
Gives mutex ONLY on error, logs error.
Enums
Functions
-
esp_err_t i2cdev_init(void)
Initialize I2C subsystem (port mutexes and internal states)
Note
This should be called once at the beginning of your application before any I2C devices are initialized.
- Returns:
ESP_OK on success
-
esp_err_t i2cdev_done(void)
Release I2C subsystem (deletes all devices, buses, and mutexes)
Note
Call this when no more I2C operations will be performed to clean up resources.
- Returns:
ESP_OK on success
-
esp_err_t i2c_dev_create_mutex(i2c_dev_t *dev)
Create mutex for device descriptor and register device.
Note
IMPORTANT: Before calling this function, you must properly initialize the i2c_dev_t structure with device address, port, and pin settings. For ESP-IDF legacy driver, set pins in dev->cfg.sda_io_num and dev->cfg.scl_io_num. For newer ESP-IDF version, either method is compatible. See the structure documentation for details.
- Parameters:
dev – Pointer to device descriptor
- Returns:
ESP_OKon success
-
esp_err_t i2c_dev_delete_mutex(i2c_dev_t *dev)
Delete mutex for device descriptor and perform device cleanup.
Note
This function performs cleanup tasks including removing the device from the I2C bus, deregistering it, and deleting its mutex.
- Parameters:
dev – Pointer to device descriptor
- Returns:
ESP_OKon success
-
esp_err_t i2c_dev_take_mutex(i2c_dev_t *dev)
Take device mutex.
- Parameters:
dev – Pointer to device descriptor
- Returns:
ESP_OKon success
-
esp_err_t i2c_dev_give_mutex(i2c_dev_t *dev)
Give device mutex.
- Parameters:
dev – Pointer to device descriptor
- Returns:
ESP_OKon success
-
esp_err_t i2c_dev_check_present(const i2c_dev_t *dev)
Check the availability of a device on the I2C bus (New Driver) - legacy’s i2c_dev_probe function equivalent.
This function attempts to communicate with the I2C device to see if it ACKs. It is non-intrusive; if the device is found, any temporary setup for the check is torn down. Uses the new I2C driver logic.
- Parameters:
dev – Pointer to the device descriptor. Pins and address must be configured.
- Returns:
ESP_OKif the device ACKs (is present), an error code otherwise.
-
esp_err_t i2c_dev_probe(const i2c_dev_t *dev, i2c_dev_type_t operation_type)
Check the availability of a device on the I2C bus (Legacy Driver).
Issue an operation of
operation_typeto the I2C device then stops. Primarily for use with the legacy i2cdev_legacy.c implementation.- Parameters:
dev – Device descriptor.
operation_type – Operation type (I2C_DEV_WRITE or I2C_DEV_READ).
- Returns:
ESP_OKif device is available for the specified operation type.
-
esp_err_t i2c_dev_read(const i2c_dev_t *dev, const void *out_data, size_t out_size, void *in_data, size_t in_size)
Read from device.
- Parameters:
dev – Pointer to device descriptor
out_data – [in] Data to write before reading (can be NULL if out_size is 0)
out_size – Size of data to write
in_data – [out] Buffer to store data read
in_size – Number of bytes to read
- Returns:
ESP_OKon success
-
esp_err_t i2c_dev_write(const i2c_dev_t *dev, const void *out_reg, size_t out_reg_size, const void *out_data, size_t out_size)
Write to device.
- Parameters:
dev – Pointer to device descriptor
out_reg – [in] Register address to write to (can be NULL if out_reg_size is 0)
out_reg_size – Size of register address
out_data – [in] Data to write (can be NULL if out_size is 0)
out_size – Size of data to write
- Returns:
ESP_OKon success
-
esp_err_t i2c_dev_read_reg(const i2c_dev_t *dev, uint8_t reg, void *data, size_t size)
Read from device register (8-bit register address)
- Parameters:
dev – Pointer to device descriptor
reg – Command to write before reading
data – [out] Buffer to store data
size – Number of bytes to read
- Returns:
ESP_OKon success
-
esp_err_t i2c_dev_write_reg(const i2c_dev_t *dev, uint8_t reg, const void *data, size_t size)
Write to device register (8-bit register address)
- Parameters:
dev – Pointer to device descriptor
reg – Command to write before writing data
data – Buffer with data to write
size – Number of bytes to write
- Returns:
ESP_OKon success
-
struct i2c_dev_t
- #include <i2cdev.h>
I2C device descriptor.
This structure supports both legacy ESP-IDF I2C driver and modern i2c_master driver.
┌─── REQUIRED (Set by user) ───────────────────────────────────────────┐ │ - dev->port - I2C port number (e.g., I2C_NUM_0) │ │ - dev->addr - Device I2C address (e.g., 0x48) │ │ - dev->cfg.sda_io_num - SDA pin (-1 = use Kconfig default) │ │ - dev->cfg.scl_io_num - SCL pin (-1 = use Kconfig default) │ │ - dev->cfg.master.clk_speed - Clock speed in Hz (e.g., 400000) │ └──────────────────────────────────────────────────────────────────────┘
┌─── OPTIONAL (Set by user if needed) ─────────────────────────────────┐ │ - dev->addr_bit_len - Address format (defaults to 7-bit) - NEW │ │ - dev->cfg.sda_pullup_en - Enable internal SDA pullup │ │ - dev->cfg.scl_pullup_en - Enable internal SCL pullup │ │ - dev->timeout_ticks - Legacy driver timeout (legacy only) │ └──────────────────────────────────────────────────────────────────────┘
┌─── AUTO-POPULATED (library fills these) ─────────────────────────────┐ │ - dev->mutex - Device mutex handle │ │ - dev->dev_handle - I2C device handle (modern driver) - NEW │ │ - dev->sda_pin - Actual SDA pin used by bus │ │ - dev->scl_pin - Actual SCL pin used by bus │ └──────────────────────────────────────────────────────────────────────┘
Note
INITIALIZATION CHECKLIST - Set these fields before calling i2c_dev_create_mutex():
Note
BACKWARD COMPATIBILITY DESIGN: The custom ‘cfg’ structure mimics ESP-IDF’s deprecated i2c_config_t layout to maintain zero-change compatibility with existing device drivers. ESP-IDF ≥5.2 deprecated i2c_config_t and split it into separate bus/device configs, but this library preserves the familiar field paths like: dev->cfg.sda_io_num, dev->cfg.scl_io_num, dev->cfg.master.clk_speed
Public Members
-
i2c_port_t port
I2C port number (e.g., I2C_NUM_0)
-
uint16_t addr
Device I2C address (e.g., 0x48 for 7-bit)
-
i2c_addr_bit_len_t addr_bit_len
Address format: I2C_ADDR_BIT_LEN_7 (default) or I2C_ADDR_BIT_LEN_10.
-
SemaphoreHandle_t mutex
Device mutex - Created by i2c_dev_create_mutex()
-
void *dev_handle
Device handle - Modern driver only, created lazily (when actual I2C operation is performed)
-
int sda_pin
Actual SDA pin used - Populated after port setup.
-
int scl_pin
Actual SCL pin used - Populated after port setup.
-
uint32_t timeout_ticks
Clock stretching timeout - Legacy driver only.
-
gpio_num_t sda_io_num
Desired SDA pin (-1 = use Kconfig default)
-
gpio_num_t scl_io_num
Desired SCL pin (-1 = use Kconfig default)
-
uint8_t sda_pullup_en
Enable internal SDA pullup (optional)
-
uint8_t scl_pullup_en
Enable internal SCL pullup (optional)
-
uint32_t clk_speed
Clock speed in Hz.
-
i2c_port_t port