Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Will this work with RTC? #29

Open
hsnprojects opened this issue Sep 6, 2024 · 28 comments
Open

Will this work with RTC? #29

hsnprojects opened this issue Sep 6, 2024 · 28 comments

Comments

@hsnprojects
Copy link

DS3231 in the following repository is using a different strategy to initialize i2c bus.
https://github.com/nopnop2002/esp-idf-ds3231

We can't initialize i2c bus twice, am I right?
Do we need to configure anything differently to use the OLED display along with RTC?

Thank you for these libraries that saved me a ton of time!

@nopnop2002
Copy link
Owner

nopnop2002 commented Sep 6, 2024

We can't initialize i2c bus twice, am I right?

xtensa's ESP32 has two i2c buses.
It is necessary to initialize each i2c bus.

	i2c_config_t i2c_config_0 = {
		.mode = I2C_MODE_MASTER,
		.sda_io_num = GPIO4,
		.scl_io_num = GPIO5,
		.sda_pullup_en = GPIO_PULLUP_ENABLE,
		.scl_pullup_en = GPIO_PULLUP_ENABLE,
		.master.clk_speed = 1000000
	};
	ESP_ERROR_CHECK(i2c_param_config(I2C_NUM_0, &i2c_config_0));
	ESP_ERROR_CHECK(i2c_driver_install(I2C_NUM_0, I2C_MODE_MASTER, 0, 0, 0));

	i2c_config_t i2c_config_1 = {
		.mode = I2C_MODE_MASTER,
		.sda_io_num = GPIO21,
		.scl_io_num = GPIO22,
		.sda_pullup_en = GPIO_PULLUP_ENABLE,
		.scl_pullup_en = GPIO_PULLUP_ENABLE,
		.master.clk_speed = 4000000
	};
	ESP_ERROR_CHECK(i2c_param_config(I2C_NUM_1, &i2c_config_1));
	ESP_ERROR_CHECK(i2c_driver_install(I2C_NUM_1, I2C_MODE_MASTER, 0, 0, 0));

esp-idf-ds3231 always uses I2C_NUM_0.

You can select either I2C_NUM_0/1 for this repository.
config-i2c-port

@hsnprojects
Copy link
Author

Thanks for taking the time to reply to my message.

Do we still need to include i2cdev from DS3231? It has its own i2c_master_init() function, which conflicts with the one from SSD1306. I noticed that ESP-IDF itself has a different I2C bus initialization function (i2c_new_master_bus(&bus_config, &i2c_bus)).

Can all three of these co-exist?

@nopnop2002
Copy link
Owner

nopnop2002 commented Sep 6, 2024

It has its own i2c_master_init() function, which conflicts with the one from SSD1306.

Yes, you are correct.

$ cd esp-idf-ssd1306

$ cp -r components/ $HOME/esp-idf-ds3231

$ cd $HOME/esp-idf-ds3231/components/ssd1306
$ vi ssd1306.h
#change from i2c_master_init to i2c_master_init_ssd1306

$ vi ssd1306_i2c_legacy.c
#change from i2c_master_init to i2c_master_init_ssd1306

$ vi ssd1306_i2c_new.c
#change from i2c_master_init to i2c_master_init_ssd1306

$ grep -rn i2c_master_init *
ssd1306.h:142:void i2c_master_init_ssd1306(SSD1306_t * dev, int16_t sda, int16_t scl, int16_t reset);
ssd1306_i2c_legacy.c:24:void i2c_master_init_ssd1306(SSD1306_t * dev, int16_t sda, int16_t scl, int16_t reset)
ssd1306_i2c_new.c:26:void i2c_master_init_ssd1306(SSD1306_t * dev, int16_t sda, int16_t scl, int16_t reset)

$ cd ../..

$ cd main

$ vi main.c
# add this to end of code
    SSD1306_t dev;

#if CONFIG_I2C_INTERFACE
    ESP_LOGI(TAG, "INTERFACE is i2c");
    ESP_LOGI(TAG, "CONFIG_SDA_GPIO=%d",CONFIG_SDA_GPIO);
    ESP_LOGI(TAG, "CONFIG_SCL_GPIO=%d",CONFIG_SCL_GPIO);
    ESP_LOGI(TAG, "CONFIG_RESET_GPIO=%d",CONFIG_RESET_GPIO);
    i2c_master_init_ssd1306(&dev, CONFIG_SDA_GPIO, CONFIG_SCL_GPIO, CONFIG_RESET_GPIO);
#endif // CONFIG_I2C_INTERFACE

    ssd1306_clear_screen(&dev, false);
    ssd1306_contrast(&dev, 0xff);
    ssd1306_display_text_x3(&dev, 0, "Hello", 5, false);
    vTaskDelay(3000 / portTICK_PERIOD_MS);

$ rm -r build

$ rm sdkconfig

$ idf.py menuconfig

config-0
config-1

change gpio and i2c bus using menuconfig

config-2

The build will now pass.

$ idf.py build


Project build complete. To flash, run:
 idf.py flash
or
 idf.py -p PORT flash
or
 python -m esptool --chip esp32 -b 460800 --before default_reset --after hard_reset write_flash --flash_mode dio --flash_size 2MB --flash_freq 40m 0x1000 build/bootloader/bootloader.bin 0x8000 build/partition_table/partition-table.bin 0x10000 build/ds3231.bin
or from the "/home/nop/rtos/esp-idf-ds3231-test/build" directory
 python -m esptool --chip esp32 -b 460800 --before default_reset --after hard_reset write_flash "@flash_args"

@hsnprojects
Copy link
Author

Fantastic! That resolved my original issue.

But I am still unable to include protocol_examples_common.h properly. I am very new to the CMake build system.

Following is what I have in my project root folder's CMakeLists.txt:
Could you take a look at this to see if you notice anything glaring?

cmake_minimum_required(VERSION 3.5)

set(EXTRA_COMPONENT_DIRS $ENV{IDF_PATH}/examples/common_components/protocol_examples_common)
set(EXTRA_COMPONENT_DIRS ./components/ssd1306)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(ds3231)

@nopnop2002
Copy link
Owner

nopnop2002 commented Sep 7, 2024

This is my CMakeLists.txt

# The following lines of boilerplate have to be in your project's
# CMakeLists in this exact order for cmake to work correctly
cmake_minimum_required(VERSION 3.5)

# This example uses an extra component for common functions such as Wi-Fi and Ethernet connection.
set(EXTRA_COMPONENT_DIRS $ENV{IDF_PATH}/examples/common_components/protocol_examples_common)

include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(ds3231)

The components directory is linked unconditionally.

@hsnprojects
Copy link
Author

Here is the latest error:
E (558) i2c: CONFLICT! driver_ng is not allowed to be used with this old driver

But it looks like we are using ssd1306_i2c_new.c

@hsnprojects
Copy link
Author

By the way, I have another similar board working with these components, but I am forcing it to use the legacy driver, and it works fine with esp-idf v5.3.

@nopnop2002
Copy link
Owner

nopnop2002 commented Sep 7, 2024

I am forcing it to use the legacy driver

Yes.
Added forced legacy i2c driver mode to latest version of SSD1306.


$ cd $HOME

$ git clone https://github.com/nopnop2002/esp-idf-ssd1306

$ cd $HOME/esp-idf-ssd1306/components/ssd1306

$ cp CMakeLists.txt $HOME/esp-idf-ds3231/components/ssd1306/

$ cp Kconfig.projbuild $HOME/esp-idf-ds3231/components/ssd1306/

$ cd $HOME/esp-idf-ds3231

$ rm -r build

$ rm sdkconfig

$ idf.py menuconfig

config-i2c-driver


Please specify the GPIO number of SSD1306 directly.
CONFIG_SDA_GPIO and CONFIG_SCL_GPIO conflict on ds3231 and ssd1306

void ssd1306(void *pvParameters)
{
    SSD1306_t dev;

    ESP_LOGI(TAG, "INTERFACE is i2c");
    ESP_LOGI(TAG, "CONFIG_SDA_GPIO=%d",CONFIG_SDA_GPIO);
    ESP_LOGI(TAG, "CONFIG_SCL_GPIO=%d",CONFIG_SCL_GPIO);
    ESP_LOGI(TAG, "CONFIG_RESET_GPIO=%d",CONFIG_RESET_GPIO);
    //i2c_master_init_ssd1306(&dev, CONFIG_SDA_GPIO, CONFIG_SCL_GPIO, CONFIG_RESET_GPIO);
    i2c_master_init_ssd1306(&dev, 5, 4, -1);

#if CONFIG_FLIP
    dev._flip = true;
    ESP_LOGW(TAG, "Flip upside down");
#endif

#if CONFIG_SSD1306_128x64
    ESP_LOGI(TAG, "Panel is 128x64");
    ssd1306_init(&dev, 128, 64);
#endif // CONFIG_SSD1306_128x64
#if CONFIG_SSD1306_128x32
    ESP_LOGI(TAG, "Panel is 128x32");
    ssd1306_init(&dev, 128, 32);
#endif // CONFIG_SSD1306_128x32

    ssd1306_clear_screen(&dev, false);
    ssd1306_contrast(&dev, 0xff);
    ssd1306_display_text_x3(&dev, 0, "Hello", 5, false);
    vTaskDelete(NULL);
}

@hsnprojects
Copy link
Author

The following version works fine with the previous version of my program (originally in esp-idf 4.6).

void app_main()
{
i2c_dev_t devRTC;
memset(&devRTC, 0, sizeof(i2c_dev_t));

if (ds3231_init_desc(&devRTC, I2C_NUM_0, CONFIG_SDA_GPIO, CONFIG_SCL_GPIO) != ESP_OK)
{
    halt("RTC failed.");
}

struct tm timeinfo = {
    .tm_year = 2024, .tm_mon = 7, .tm_mday = 15, .tm_hour = 17, .tm_min = 50, .tm_sec = 55};

printf("Setting RTC to: %04d-%02d-%02d %02d:%02d:%02d\n",
       timeinfo.tm_year, timeinfo.tm_mon, timeinfo.tm_mday,
       timeinfo.tm_hour, timeinfo.tm_min, timeinfo.tm_sec);

if (ds3231_set_time(&devRTC, &timeinfo) != ESP_OK)
{
    halt("Error RTC Write.");
}

if (ds3231_get_time(&devRTC, &timeinfo) != ESP_OK)
{
    printf("Error while reading RTC");
}

printf("Read back from RTC: %04d-%02d-%02d %02d:%02d:%02d\n",
       timeinfo.tm_year, timeinfo.tm_mon, timeinfo.tm_mday,
       timeinfo.tm_hour, timeinfo.tm_min, timeinfo.tm_sec);

SSD1306_t devOled;

devOled._address = I2C_ADDRESS; // 0x3C
ssd1306_init(&devOled, 128, 32);

ssd1306_clear_screen(&devOled, false);
ssd1306_display_text_x3(&devOled, 0, "Hello", 5, false);

while (1)
{
    vTaskDelay(1000);
}

}

@nopnop2002
Copy link
Owner

nopnop2002 commented Sep 7, 2024

I am forcing it to use the legacy driver

Yes.
Added forced legacy i2c driver mode to latest version of SSD1306.


$ cd $HOME

$ git clone https://github.com/nopnop2002/esp-idf-ssd1306

$ cd $HOME/esp-idf-ssd1306/components/ssd1306

$ cp CMakeLists.txt $HOME/esp-idf-ds3231/components/ssd1306/

$ cp Kconfig.projbuild $HOME/esp-idf-ds3231/components/ssd1306/

$ cd $HOME/esp-idf-ds3231

$ rm -r build

$ rm sdkconfig

$ idf.py menuconfig

config-i2c-driver


Please specify the GPIO number of SSD1306 directly.
CONFIG_SDA_GPIO and CONFIG_SCL_GPIO conflict on ds3231 and ssd1306

void app_main()
{
i2c_dev_t devRTC;
memset(&devRTC, 0, sizeof(i2c_dev_t));

if (ds3231_init_desc(&devRTC, I2C_NUM_0, CONFIG_SDA_GPIO, CONFIG_SCL_GPIO) != ESP_OK)
{
    halt("RTC failed.");
}

struct tm timeinfo = {
    .tm_year = 2024, .tm_mon = 7, .tm_mday = 15, .tm_hour = 17, .tm_min = 50, .tm_sec = 55};

printf("Setting RTC to: %04d-%02d-%02d %02d:%02d:%02d\n",
       timeinfo.tm_year, timeinfo.tm_mon, timeinfo.tm_mday,
       timeinfo.tm_hour, timeinfo.tm_min, timeinfo.tm_sec);

if (ds3231_set_time(&devRTC, &timeinfo) != ESP_OK)
{
    halt("Error RTC Write.");
}

if (ds3231_get_time(&devRTC, &timeinfo) != ESP_OK)
{
    printf("Error while reading RTC");
}

printf("Read back from RTC: %04d-%02d-%02d %02d:%02d:%02d\n",
       timeinfo.tm_year, timeinfo.tm_mon, timeinfo.tm_mday,
       timeinfo.tm_hour, timeinfo.tm_min, timeinfo.tm_sec);

SSD1306_t devOled;

devOled._address = I2C_ADDRESS; // 0x3C

i2c_master_init_ssd1306(&devOled, 5, 4, -1);

ssd1306_init(&devOled, 128, 32);

ssd1306_clear_screen(&devOled, false);
ssd1306_display_text_x3(&devOled, 0, "Hello", 5, false);

while (1)
{
    vTaskDelay(1000);
}

@hsnprojects
Copy link
Author

The reason I tried to upgrade was due to a warning I saw while building the program, which recommended upgrading the I2C driver. However, I don't see that warning now, which is strange.

@nopnop2002
Copy link
Owner

nopnop2002 commented Sep 7, 2024

Let's organize some information.

Is your ESP-IDF version 4.6?

Is the clone of ssd1306 the latest?

@hsnprojects
Copy link
Author

  1. I have upgraded my ESP-IDF to 5.3. I may still have 4.6 on another machine, though.

  2. The version that works fine now even on 5.3 is an old clone of your repo, but the sample that I am working on now is based on your source from earlier today.

When I try to initialize I2C for the OLED with the function call
i2c_master_init_ssd1306(&devOled, 13, 12, -1);

I get the following error:
E (345) i2c: i2c driver install error.

However, it works fine if I remove that line.

@hsnprojects
Copy link
Author

hsnprojects commented Sep 7, 2024

I was hoping to use I2C in a manner similar to Arduino's Wire.begin(), where initializing the I2C bus would be separate from adding devices like the RTC or OLED to the bus.

@nopnop2002
Copy link
Owner

nopnop2002 commented Sep 7, 2024

C++ is very limited in esp-idf.
In order to use the class like Arduino, it is necessary to change from main.c to main.cpp, but the driver provided by ESP-IDF does not support main.cpp.

@nopnop2002
Copy link
Owner

nopnop2002 commented Sep 7, 2024

I get the following error:

It worked fine with the code below.

void app_main()
{
i2c_dev_t devRTC;
memset(&devRTC, 0, sizeof(i2c_dev_t));

if (ds3231_init_desc(&devRTC, I2C_NUM_0, CONFIG_SDA_GPIO, CONFIG_SCL_GPIO) != ESP_OK)
{
    halt("RTC failed.");
}

struct tm timeinfo = {
    .tm_year = 2024, .tm_mon = 7, .tm_mday = 15, .tm_hour = 17, .tm_min = 50, .tm_sec = 55};

printf("Setting RTC to: %04d-%02d-%02d %02d:%02d:%02d\n",
       timeinfo.tm_year, timeinfo.tm_mon, timeinfo.tm_mday,
       timeinfo.tm_hour, timeinfo.tm_min, timeinfo.tm_sec);

if (ds3231_set_time(&devRTC, &timeinfo) != ESP_OK)
{
    halt("Error RTC Write.");
}

if (ds3231_get_time(&devRTC, &timeinfo) != ESP_OK)
{
    printf("Error while reading RTC");
}

printf("Read back from RTC: %04d-%02d-%02d %02d:%02d:%02d\n",
       timeinfo.tm_year, timeinfo.tm_mon, timeinfo.tm_mday,
       timeinfo.tm_hour, timeinfo.tm_min, timeinfo.tm_sec);

SSD1306_t devOled;

i2c_master_init_ssd1306(&devOled, 5, 4, -1);

ssd1306_init(&devOled, 128, 32);

ssd1306_clear_screen(&devOled, false);
ssd1306_display_text_x3(&devOled, 0, "Hello", 5, false);

while (1)
{
    vTaskDelay(1000);
}

}
I (332) main_task: Started on CPU0
I (342) main_task: Calling app_main()
Setting RTC to: 2024-07-15 17:50:55
Read back from RTC: 2024-07-15 17:50:55
I (342) SSD1306: Legacy i2c driver is used
I (352) SSD1306: OLED configured successfully

The following SCL and SDA specifications will be ignored.
config-2

@hsnprojects
Copy link
Author

hsnprojects commented Sep 7, 2024

Not object-oriented, but rather C-style APIs. Initializing I2C might create an I2C channel handle or something similar. Devices are then added (registered) to one of the buses. The i2c_master_init() function is called only once to initialize the bus. I can look into how ESP-IDF handles this.

@hsnprojects
Copy link
Author

I get the following error:

It worked fine with the code below.

I will give it a try tomorrow. I may have to get latest of your source.
Thanks for working on this issue!

@nopnop2002
Copy link
Owner

nopnop2002 commented Sep 7, 2024

Initializing I2C might create an I2C channel handle or something similar. Devices are then added (registered) to one of the buses. The i2c_master_init() function is called only once to initialize the bus.

You're right.

I2C_NUM_0 --+-- DS3231  (i2c address = 0x68) 
            +-- SSD1306 (i2c address = 0x3c)

This code work fine.

void getClock(void *pvParameters)
{
    // Initialize RTC
    i2c_dev_t dev;
    if (ds3231_init_desc(&dev, I2C_NUM_0, CONFIG_SDA_GPIO, CONFIG_SCL_GPIO) != ESP_OK) {
        ESP_LOGE(pcTaskGetName(0), "Could not init device descriptor.");
        while (1) { vTaskDelay(1); }
    }

    // Initialize SSD1306
    SSD1306_t dev2;
    ESP_LOGI(TAG, "INTERFACE is i2c");
    dev2._address = I2C_ADDRESS;
    dev2._flip = false;
    ssd1306_init(&dev2, 128, 64);
    ssd1306_clear_screen(&dev2, false);

    // Initialise the xLastWakeTime variable with the current time.
    TickType_t xLastWakeTime = xTaskGetTickCount();


    // Get RTC date and time
    while (1) {
        float temp;
        struct tm rtcinfo;

        if (ds3231_get_temp_float(&dev, &temp) != ESP_OK) {
            ESP_LOGE(pcTaskGetName(0), "Could not get temperature.");
            while (1) { vTaskDelay(1); }
        }

        if (ds3231_get_time(&dev, &rtcinfo) != ESP_OK) {
            ESP_LOGE(pcTaskGetName(0), "Could not get time.");
            while (1) { vTaskDelay(1); }
        }

        ESP_LOGI(pcTaskGetName(0), "%04d-%02d-%02d %02d:%02d:%02d, %.2f deg Cel",
            rtcinfo.tm_year, rtcinfo.tm_mon + 1,
            rtcinfo.tm_mday, rtcinfo.tm_hour, rtcinfo.tm_min, rtcinfo.tm_sec, temp);

        ssd1306_clear_screen(&dev2, false);
        ssd1306_contrast(&dev2, 0xff);
        char buff[64];
        int len = sprintf(buff, "%02d:%02d:%02d", rtcinfo.tm_hour, rtcinfo.tm_min, rtcinfo.tm_sec);
        ssd1306_display_text(&dev2, 0, buff, len, false);
        vTaskDelayUntil(&xLastWakeTime, 1000);
    }
}

void app_main()
{
    ESP_LOGI(TAG, "CONFIG_SCL_GPIO = %d", CONFIG_SCL_GPIO);
    ESP_LOGI(TAG, "CONFIG_SDA_GPIO = %d", CONFIG_SDA_GPIO);
    ESP_LOGI(TAG, "CONFIG_TIMEZONE= %d", CONFIG_TIMEZONE);

    // Get clock
    xTaskCreate(getClock, "getClock", 1024*4, NULL, 2, NULL);
}

@nopnop2002
Copy link
Owner

nopnop2002 commented Sep 7, 2024

Changed i2c_master_init of esp-idf-ds3231 to i2c_dev_init.

https://github.com/nopnop2002/esp-idf-ds3231

The i2c_master_init conflict is now gone.

$ cd $HOME

$ git clone https://github.com/nopnop2002/esp-idf-ds3231

$ git clone https://github.com/nopnop2002/esp-idf-ssd1306

$ cd $HOME/esp-idf-ssd1306

$ cp -r components/ $HOME/esp-idf-ds3231

$ cd $HOME/esp-idf-ds3231

$ idf.py menuconfig

SCL and SDA GPIOs are shared with DS3231.
config-2

Wireing

DS3231 SSD1306 ESP32 ESP32S2/S3/H2 ESP32Cn
SCL SCL GPIO22 GPIO02 GPIO06
SDA DSA GPIO21 GPIO01 GPIO05
GND GND GND GND GND
VCC GND 3.3V 3.3V 3.3V
I2C_NUM_0(SDA=GPIO21 SCL=GPIO22) --+-- DS3231  (i2c address = 0x68) 
                                   +-- SSD1306 (i2c address = 0x3c)

My logging

I (337) main_task: Started on CPU0
I (347) main_task: Calling app_main()
I (347) DS3213: CONFIG_SCL_GPIO = 22
I (347) DS3213: CONFIG_SDA_GPIO = 21
I (347) DS3213: CONFIG_TIMEZONE= 9
I (357) DS3213: INTERFACE is i2c
I (357) SSD1306: OLED configured successfully
I (357) main_task: Returned from app_main()
I (447) getClock: 2024-09-07 16:25:44, 33.50 deg Cel
I (10447) getClock: 2024-09-07 16:25:54, 33.50 deg Cel
I (20447) getClock: 2024-09-07 16:26:04, 33.50 deg Cel
I (30447) getClock: 2024-09-07 16:26:14, 33.50 deg Cel
I (40447) getClock: 2024-09-07 16:26:24, 33.50 deg Cel
I (50447) getClock: 2024-09-07 16:26:34, 33.50 deg Cel
I (60447) getClock: 2024-09-07 16:26:44, 33.50 deg Cel
I (70447) getClock: 2024-09-07 16:26:54, 33.50 deg Cel
I (80447) getClock: 2024-09-07 16:27:04, 33.50 deg Cel
I (90447) getClock: 2024-09-07 16:27:14, 33.50 deg Cel
I (100447) getClock: 2024-09-07 16:27:24, 33.50 deg Cel
I (110447) getClock: 2024-09-07 16:27:34, 33.50 deg Cel
I (120447) getClock: 2024-09-07 16:27:44, 33.50 deg Cel
I (130447) getClock: 2024-09-07 16:27:54, 33.50 deg Cel
I (140447) getClock: 2024-09-07 16:28:04, 33.50 deg Cel
I (150447) getClock: 2024-09-07 16:28:14, 33.50 deg Cel
I (160447) getClock: 2024-09-07 16:28:24, 33.50 deg Cel

IMG_5804

Thank you for the great tips
This article made me aware of the issue with i2c_new_driver.
When using i2c_new_driver, we need an option to force the use of the legacy driver as an auxiliary function.

Note
If you implement all your code in the main function, you will need to increase the stack size of main.

@hsnprojects
Copy link
Author

hsnprojects commented Sep 7, 2024

Yes, that worked!

Shouldn't we be able to use the new I2C driver with ESP-IDF v5.3, rather than forcing legacy driver?

(BTW, I just have this test app all in one function for the sake of simplicity).

@nopnop2002
Copy link
Owner

nopnop2002 commented Sep 8, 2024

Shouldn't we be able to use the new I2C driver with ESP-IDF v5.3, rather than forcing legacy driver?

Achieving this with the new i2c driver would require major changes.

I2C_NUM_0 --+-- DS3231  (i2c address = 0x68) 
            +-- SSD1306 (i2c address = 0x3c)
  • Legacy driver

We only need to run this code once.
There is no need to memorize any data.
DS3231 and SSD1306 use the same frequency.

        i2c_config_t i2c_config = {
            .mode = I2C_MODE_MASTER,
            .sda_io_num = sda,
            .scl_io_num = scl,
            .sda_pullup_en = GPIO_PULLUP_ENABLE,
            .scl_pullup_en = GPIO_PULLUP_ENABLE,
            .master.clk_speed = I2C_MASTER_FREQ_HZ
        };
        ESP_ERROR_CHECK(i2c_param_config(I2C_NUM, &i2c_config));
        ESP_ERROR_CHECK(i2c_driver_install(I2C_NUM, I2C_MODE_MASTER, 0, 0, 0));
  • New i2c driver

We need to run this code on the first device.
We need to record bus_handle somewhere.
But In different libraries, there is no place to record bus_handle.

    i2c_master_bus_config_t i2c_mst_config = {
        .clk_source = I2C_CLK_SRC_DEFAULT,
        .glitch_ignore_cnt = 7,
        .i2c_port = I2C_NUM,
        .scl_io_num = scl,
        .sda_io_num = sda,
        .flags.enable_internal_pullup = true,
    };
    i2c_master_bus_handle_t bus_handle;
    ESP_ERROR_CHECK(i2c_new_master_bus(&i2c_mst_config, &bus_handle));

    i2c_device_config_t dev_cfg = {
        .dev_addr_length = I2C_ADDR_BIT_LEN_7,
        .device_address = I2C_ADDRESS1,
        .scl_speed_hz = I2C_MASTER_FREQ_HZ1,
    };
    i2c_master_dev_handle_t dev_handle;
    ESP_ERROR_CHECK(i2c_master_bus_add_device(bus_handle, &dev_cfg, &dev_handle));

    // We need to record bus_handle somewhere.

We need to run this code on the second device.
We need to load bus_handle from somewhere.
DS3231 and SSD1306 can use different frequencies.

    // We need to load bus_handle from somewhere.

    i2c_device_config_t dev_cfg = {
        .dev_addr_length = I2C_ADDR_BIT_LEN_7,
        .device_address = I2C_ADDRESS2,
        .scl_speed_hz = I2C_MASTER_FREQ_HZ2,
    };
    i2c_master_dev_handle_t dev_handle;
    ESP_ERROR_CHECK(i2c_master_bus_add_device(bus_handle, &dev_cfg, &dev_handle));

This library does not have the function to record bus_handle somewhere or retrieve bus_handle from somewhere.

And implementing that functionality is not very effective.


If SSD1306 use new i2c drivers, other i2c devices must also use new i2c drivers like this.

I2C_NUM_0 ----- DS3231 using new ic2 driver (i2c address = 0x68) 
I2C_NUM_1 ----- SSD1306 using new i2c driver (i2c address = 0x3c)

This is not acceptable.

I2C_NUM_0 ----- DS3231 using legacy ic2 driver (i2c address = 0x68) 
I2C_NUM_1 ----- SSD1306 using new i2c driver (i2c address = 0x3c)

ESP32C series has only one i2c bus.
This is not available on the ESP32C series.
Can only be used with XTENSA.

If I have time, I will update the DS3231 repository to make it I2C bus selectable and use the new IC2 driver.

@hsnprojects
Copy link
Author

hsnprojects commented Sep 8, 2024

Can main() initialize I2C and store the bus handle, then pass that to each of the device initialization routines? We can add multiple devices to the same bus using this method. I was also looking into expanding I/O using the PCF8574T.

Edit:
esp-idf documentation explains this strategy here:
https://docs.espressif.com/projects/esp-idf/en/v5.3.1/esp32/api-reference/peripherals/i2c.html

@nopnop2002
Copy link
Owner

nopnop2002 commented Sep 8, 2024

Can main() initialize I2C and store the bus handle, then pass that to each of the device initialization routines?

Yes we can.

But We cannot add arguments to i2c_master_init as it would break compatibility.
New functions are required to ensure compatibility.

#if CONFIG_I2C_INTERFACE
    ESP_LOGI(tag, "INTERFACE is i2c");
    ESP_LOGI(tag, "CONFIG_SDA_GPIO=%d",CONFIG_SDA_GPIO);
    ESP_LOGI(tag, "CONFIG_SCL_GPIO=%d",CONFIG_SCL_GPIO);
    ESP_LOGI(tag, "CONFIG_RESET_GPIO=%d",CONFIG_RESET_GPIO);
    //i2c_master_init(&dev, CONFIG_SDA_GPIO, CONFIG_SCL_GPIO, CONFIG_RESET_GPIO);

    i2c_master_bus_config_t i2c_mst_config = {
        .clk_source = I2C_CLK_SRC_DEFAULT,
        .glitch_ignore_cnt = 7,
        .i2c_port = I2C_NUM_0,
        .scl_io_num = CONFIG_SCL_GPIO,
        .sda_io_num = CONFIG_SDA_GPIO,
        .flags.enable_internal_pullup = true,
    };
    i2c_master_bus_handle_t bus_handle;
    ESP_ERROR_CHECK(i2c_new_master_bus(&i2c_mst_config, &bus_handle));

    // add new device to I2C_NUM_0
    i2c_bus_add(&dev, bus_handle, I2C_NUM_0, CONFIG_RESET_GPIO);

#endif // CONFIG_I2C_INTERFACE

@hsnprojects
Copy link
Author

hsnprojects commented Sep 9, 2024

By this approach, you can add devices in any order, and you don't have to rely on devices to initialize the bus. This also aligns with the architecture of ESP-IDF 5.3. I think the I2C bus initialization (driver load) might have changed between versions, but I haven't looked into it.

You said:

But We cannot add arguments to i2c_master_init as it would break compatibility.
New functions are required to ensure compatibility

I did not quite follow this, could you explain? If arguments changed between esp-idf versions, we can include code conditionally.

@nopnop2002
Copy link
Owner

nopnop2002 commented Sep 9, 2024

i2c_master_init(&dev, CONFIG_SDA_GPIO, CONFIG_SCL_GPIO, CONFIG_RESET_GPIO);

This function cannot change its arguments.
Because many users are already using this function in ESP-IDF V5.0/5.1/5.2/5.3/5.4.
Changing the arguments to this function will force many users to make modifications.

i2c_bus_add(&dev, bus_handle, I2C_NUM_0, CONFIG_RESET_GPIO);

Adding this function has no effect on existing apps.

@hsnprojects
Copy link
Author

That makes sense!

@nopnop2002
Copy link
Owner

This function is already available.
https://github.com/nopnop2002/esp-idf-ssd1306/blob/master/components/ssd1306/ssd1306_i2c_new.c#L62

i2c_bus_add(&dev, bus_handle, I2C_NUM_0, CONFIG_RESET_GPIO);

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

No branches or pull requests

2 participants