Skip to content

Shared i2c bus handle#6

Closed
jnischler wants to merge 2 commits intoesp-idf-lib:mainfrom
section42:shared-i2c-bus-handle
Closed

Shared i2c bus handle#6
jnischler wants to merge 2 commits intoesp-idf-lib:mainfrom
section42:shared-i2c-bus-handle

Conversation

@jnischler
Copy link

@trombik
Copy link
Contributor

trombik commented Dec 22, 2025

@quinkq Would you comment on this?

Copy link
Contributor

@quinkq quinkq left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @trombik. Thanks for the initiative @jnischler. I couldn't get myself to finish this for a quite a while, but here's my idea with a few improvements:

  • Added mutex protection to prevent race conditions
  • Included defensive checks for uninitialized state and detailed error messages for better guidance
  • Added some documentation with usage examples and debugging logs

Comment on lines +898 to +909

esp_err_t i2cdev_get_shared_handle(i2c_port_t port, void **bus_handle)
{
if (port >= I2C_NUM_MAX || bus_handle == NULL)
return ESP_ERR_INVALID_ARG;

if (!i2c_ports[port].installed || i2c_ports[port].bus_handle == NULL)
return ESP_ERR_INVALID_STATE;

*bus_handle = (void *)i2c_ports[port].bus_handle;
return ESP_OK;
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
esp_err_t i2cdev_get_shared_handle(i2c_port_t port, void **bus_handle)
{
if (port >= I2C_NUM_MAX || bus_handle == NULL)
return ESP_ERR_INVALID_ARG;
if (!i2c_ports[port].installed || i2c_ports[port].bus_handle == NULL)
return ESP_ERR_INVALID_STATE;
*bus_handle = (void *)i2c_ports[port].bus_handle;
return ESP_OK;
}
esp_err_t i2cdev_get_shared_handle(i2c_port_t port, void **bus_handle)
{
if (port >= I2C_NUM_MAX || bus_handle == NULL)
{
ESP_LOGE(TAG, "Invalid arguments: port=%d, bus_handle=%p", port, bus_handle);
return ESP_ERR_INVALID_ARG;
}
i2c_port_state_t *port_state = &i2c_ports[port];
// Check if i2cdev subsystem is initialized
if (port_state->lock == NULL)
{
ESP_LOGE(TAG, "[Port %d] I2C subsystem not initialized - call i2cdev_init() first", port);
return ESP_ERR_INVALID_STATE;
}
// Take port mutex for thread-safe access
if (xSemaphoreTake(port_state->lock, pdMS_TO_TICKS(CONFIG_I2CDEV_TIMEOUT)) != pdTRUE)
{
ESP_LOGE(TAG, "[Port %d] Could not take port mutex for get_shared_handle", port);
return ESP_ERR_TIMEOUT;
}
// Check if bus is initialized
if (!port_state->installed || port_state->bus_handle == NULL)
{
xSemaphoreGive(port_state->lock);
ESP_LOGE(TAG, "[Port %d] Bus not initialized - create an i2c_dev_t device first to initialize the bus", port);
return ESP_ERR_INVALID_STATE;
}
// Return the bus handle
*bus_handle = (void *)port_state->bus_handle;
ESP_LOGI(TAG, "[Port %d] Shared bus handle retrieved: %p (SDA=%d, SCL=%d, ref_count=%" PRIu32 ")",
port, port_state->bus_handle, port_state->sda_pin_current,
port_state->scl_pin_current, port_state->ref_count);
xSemaphoreGive(port_state->lock);
return ESP_OK;
}

Comment on lines +318 to +324

/**
* @brief Get shared I2C bus handle (requires sensor initialized first)
*/
esp_err_t i2cdev_get_shared_handle(i2c_port_t port, void **bus_handle);


Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
/**
* @brief Get shared I2C bus handle (requires sensor initialized first)
*/
esp_err_t i2cdev_get_shared_handle(i2c_port_t port, void **bus_handle);
/**
* @brief Get shared I2C bus handle for external ESP-IDF components
*
* This function allows external ESP-IDF components (like esp_lcd) to access
* the i2cdev-managed I2C bus handle for shared bus operations. This enables
* thread-safe sharing of the I2C bus between i2cdev-based sensors and native
* ESP-IDF components.
*
* @note USAGE PATTERN:
* 1. Initialize i2cdev subsystem with i2cdev_init()
* 2. Create at least one i2c_dev_t device on the desired port (this initializes the bus)
* 3. Call this function to retrieve the bus handle
* 4. Pass the handle to ESP-IDF components (e.g., esp_lcd_new_panel_io_i2c)
*
* @note USAGE EXAMPLE:
* // 1. Initialize i2cdev
* i2cdev_init();
*
* // 2. Create a sensor device (initializes the bus)
* i2c_dev_t sensor = {
* .port = I2C_NUM_0,
* .addr = 0x29, // TSL2591 light sensor
* .cfg = {
* .sda_io_num = GPIO_NUM_21,
* .scl_io_num = GPIO_NUM_22,
* .master.clk_speed = 400000
* }
* };
* i2c_dev_create_mutex(&sensor);
*
* // 3. Get the shared bus handle
* i2c_master_bus_handle_t bus_handle;
* ESP_ERROR_CHECK(i2cdev_get_shared_handle(I2C_NUM_0, (void **)&bus_handle));
*
* // 4. Use it with esp_lcd or other ESP-IDF components
* esp_lcd_panel_io_handle_t io_handle;
* esp_lcd_panel_io_i2c_config_t io_config = {
* .dev_addr = 0x3C, // SSD1306 display
* .control_phase_bytes = 1,
* .dc_bit_offset = 6,
* .lcd_cmd_bits = 8,
* .lcd_param_bits = 8,
* };
* ESP_ERROR_CHECK(esp_lcd_new_panel_io_i2c(bus_handle, &io_config, &io_handle));
*
* // Both the sensor and display now share the same I2C bus with proper synchronization!
*
* @param port I2C port number (e.g., I2C_NUM_0)
* @param[out] bus_handle Pointer to store the bus handle
* @return ESP_OK on success
* @return ESP_ERR_INVALID_ARG if port is invalid or bus_handle is NULL
* @return ESP_ERR_INVALID_STATE if i2cdev_init() was not called or bus for this port is not initialized yet
* @return ESP_ERR_TIMEOUT if port mutex could not be acquired
*
* @warning The returned handle is managed by i2cdev. Do NOT call i2c_del_master_bus()
* on it directly - i2cdev will handle cleanup when the last device is removed.
*/
esp_err_t i2cdev_get_shared_handle(i2c_port_t port, void **bus_handle);

@quinkq quinkq mentioned this pull request Dec 27, 2025
@UncleRus
Copy link
Contributor

Done in #8

@UncleRus UncleRus closed this Jan 26, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants