olikraus / u8g2

U8glib library for monochrome displays, version 2
Other
5.1k stars 1.05k forks source link

Two Displays with non-default I2C-Pins at ESP32S3 #2206

Open ams-hh opened 1 year ago

ams-hh commented 1 year ago

Dear Olikraus at first: Thank you for this magnificant library and the work you had with it!

I have the following issue: On a custom board with ESP32-WROOM-32 support two OLED-Display 128x64, SH1106, Ic2-Address 0x3d 128x34, SH1306, Ic2-Address 0x3c

Until now I used the default-Pins SDA:21, SCL:22 for this Microcontroller and everything works as expected.

Now I've made a PCB rebuild and use ESP32S3-WROOM-1. Every documatentation said: "You can use every Pins for I2C" So I did SDA:2, SCL:4 And in the construcor I used the 3rd and 4. Parameter for the GPIO.

And now the trouble started. The first Line -Output on the first display is working. When for the second Display the u8g2.begin() is executed, the system hangs until a timeout. (You call in begin() init_display(). There it hangs.) And when after the timeout I try to write a second line on the first Display, it does hang as well.

The #defines SDA and SCL are predefined somewhere. When compiled and loaded into an ESP32-WROOM-32 they show 21/22. I now learned, when compiled and loaded into an ESP32S3-WROOM-1 they show 8/9.

With my development-system I've made the following tests with the program see below:

ES32-WROOM_32: One Display, default-Pins, Constructor without I2C-Pin-Parameter >>> works One Display, non-default-Pins, Constructor WITH I2C-Pin-Parameter >>> works Two Displays, default-Pins, Constructor without I2C-Pin-Parameter >>> works Two Displays, non-default-Pins, Constructor WITH I2C-Pin-Parameter >>> works

ES32S3-WROOM-1: One Display, default-Pins, Constructor without I2C-Pin-Parameter >>> works One Display, default-Pins, Constructor WITH I2C-Pin-Parameter >>> works

Two Displays, default-Pins, Constructor without I2C-Pin-Parameter >>> works Two Displays, default-Pins, Constructor WITH I2C-Pin-Parameter >>> hangs at Second Display begin() Two Displays, non-default-Pins, Constructor WITH I2C-Pin-Parameter >>> hangs at Second Display begin()

So it seems to be dependend form the use of the constructor parameters for SCL/SDA GPIO.

I could now redesign the PCB once again and use 8/9. Or do you have an idea how to solve this other way? Thank you in advance and regards from Hamburg Andree


Test-Program. Use #define (un)commenting for the Test cases.

#include <Arduino.h>
#include "U8g2lib.h"

#define TWODISPLAYS
#define INIT_WITH_SCL_SDA

//ESP32-WROOM32 Default
//#define         I2C_SDA                     21
//#define         I2C_SCL                     22

//ESP32S3-WROOM-1 Default
//#define         I2C_SDA                     8
//#define         I2C_SCL                     9

//Test-Pins ESP32-WROOM-32
//#define         I2C_SDA                     17
//#define         I2C_SCL                     18

//Test-Pins ESP32-S3-WROOM-1
#define         I2C_SDA                     2
#define         I2C_SCL                     4

#define     OLED_SYSMON_I2CADDR             0x3d // !!7-Bit-Address
#define     OLED_GPIOMON_I2CADDR            0x3c // !!7-Bit-Address

#ifdef INIT_WITH_SCL_SDA
    U8G2_SH1106_128X64_NONAME_F_HW_I2C u8g2A(U8G2_R2      ,
                                             U8X8_PIN_NONE,  // Reset
                                             I2C_SCL      ,  // SCL
                                             I2C_SDA      ); // SDA
#else
    U8G2_SH1106_128X64_NONAME_F_HW_I2C u8g2A(U8G2_R2      ,
                                             U8X8_PIN_NONE); // Reset
#endif

#ifdef TWODISPLAYS
    #ifdef INIT_WITH_SCL_SDA
        U8G2_SSD1306_128X32_UNIVISION_F_HW_I2C u8g2B(U8G2_R2                 ,
                                                     U8X8_PIN_NONE,  // Reset
                                                     I2C_SCL      ,  // SCL
                                                     I2C_SDA      ); // SDA
    #else
        U8G2_SSD1306_128X32_UNIVISION_F_HW_I2C u8g2B(U8G2_R2      ,
                                                     U8X8_PIN_NONE);
    #endif
#endif

void setup()
{
    Serial.begin(115200);

    #ifdef TWODISPLAYS
        Serial.println(" >>>>>>>>> Startet TWO-Display-Test");
    #else
        Serial.println(" >>>>>>>>> Startet ONE-Display-Test");
    #endif
    #ifdef INIT_WITH_SCL_SDA
        Serial.println("Construct U8G2-Object with SDA/SCL  >>> Use #defined I2C_SCL/I2C_SDA");
    #else
        Serial.println("Construct U8G2-Object WITHOUT SDA/SCL  >>> Use Default");
    #endif

    Serial.printf("Default SDA/SCL: %d/%d\n", SDA, SCL);
    #ifdef INIT_WITH_SCL_SDA
        #if (SDA!= I2C_SDA)
            Serial.printf("Defined SDA/SCL: %d/%d\n", I2C_SDA, I2C_SCL);
        #endif
    #endif

    Serial.println("First Display, First Line");

    Serial.println(">>> 1a");
    u8g2A.setI2CAddress(OLED_SYSMON_I2CADDR << 1);
    Serial.println(">>> 1b");
    u8g2A.begin();
    Serial.println(">>> 1c");
    u8g2A.clearBuffer();

    Serial.println(">>> 2");
    u8g2A.setFont(u8g2_font_ncenB08_tr);    // choose a suitable font
    Serial.println(">>> 3");
    u8g2A.drawStr(0,10,"Hello World-1-1!"); // write something to the internal memory
    Serial.println(">>> 4");
    u8g2A.sendBuffer();                 // transfer internal memory to the display
    Serial.println(">>> 5");
    delay(1000);

#ifdef TWODISPLAYS
    Serial.println("Second Display, First Line");

    Serial.println(">>> 1a");
    u8g2B.setI2CAddress(OLED_GPIOMON_I2CADDR << 1);
    Serial.println(">>> 1b");
    u8g2B.begin();
    Serial.println(">>> 1c");
    u8g2B.clearBuffer();

    Serial.println(">>> 2");
    u8g2B.setFont(u8g2_font_ncenB08_tr);    // choose a suitable font
    Serial.println(">>> 3");
    u8g2B.drawStr(0,10,"Hello World-2-1");  // write something to the internal memory
    Serial.println(">>> 4");
    u8g2B.sendBuffer();                 // transfer internal memory to the display
    Serial.println(">>> 5");
    delay(1000);
#endif

    Serial.println("First Display, 2nd Line");
    Serial.println(">>> 6");
    u8g2A.drawStr(0,20,"Hello World-1-2!"); // write something to the internal memory
    Serial.println(">>> 7");
    u8g2A.sendBuffer();                 // transfer internal memory to the display
    delay(1000);

#ifdef TWODISPLAYS
    Serial.println("Second Display, 2nd Line");

    Serial.println(">>> 6");
    u8g2B.drawStr(0,20,"Hello World-2-2!"); // write something to the internal memory
    Serial.println(">>> 7");
    u8g2B.sendBuffer();                 // transfer internal memory to the display
    delay(1000);
#endif

}

void loop(void)
{
}
olikraus commented 1 year ago

U8g2 just passes the arguments to the (none-standard) wire function of the ESP32 board software:

https://github.com/olikraus/u8g2/blob/74e379c08f4e3db2c9ac18db72cdee0d47276a58/cppsrc/U8x8lib.cpp#L1343-L1354

ams-hh commented 1 year ago

So it seems there is no chance to solve it and the only solution is to redesign the PCB ...

Your code calls Wire.begin() in any case, either with or without SDA/SCL. I will at least make a test and manipulate the lib so that in the second u8g2.begin() NO Wire.begin is executed, because Wire.begin has always be done in the first u8g2.begin(). Maybe this helps ...?

Thanks!

ams-hh commented 1 year ago

Got it! When the FIRST Construtor is called WITH SDA/SCL and the SECOND one is called WITHOUT SDA/SCL then you call in u8x8_byte_arduino_hw_i2c in the second case Wire.begin() without SDA/SCL. And then it works. (But I am unsure why. I don't want to dig now into the Wire Code to see what happens then. No way ... it works)

olikraus commented 1 year ago

It also sounds strange to me, but I am happy you found a solution :-)

ams-hh commented 1 year ago

Yes.... but maybe its a fluke only ... So... I think the better way for more than one Display on the same I2C-Bus should be the possibility to announce to u8g2.begin() with a a parameter that the Wire-Interface is already initialized (maybe by a u8g2.begin() before or by an "outside" Wire.begin?) and then insideu8x8_byte_arduino_hw_i2c has not to call Wire.begin() (again).

olikraus commented 1 year ago

Yes, i know... that might be better. However the code impact would be huge and I don't have time to change this at the moment.

FriqueNFraque commented 9 months ago

@ams-hh, Ihave landed on this same issue. using i2c 64x32 OLEDs, with no address pin breakout. Using esp32s1 with dual i2c buses. i have been fighting this issue for years off and on as a hobbyist and assumed i wasn't using the libraries correctly. would you mind posting your code? if the solution is possibly just in the order of operations, would be invaluable to see code that is working. thanks!

ams-hh commented 9 months ago

Hello FriqueNFraque, at first: You write "with dual I2C". This is for me (as a not native english speaker) ambiguous. Does it mean

I fought with the second issue. (No idea if Oli Kraus changed something in the meantime) As written, I encountered a problem of the Wire Class, when it is called twice with the same SDA/SCL GPIO. The constructor of u8g2 calls Wire internally WHEN THE THIRD and FOURTH parameter is used. So I then called the first constructor for Display A WITH theses parameters and the for DISPLAY B WITHOUT.

                //OLED 128x32 als GPIO-Monitor
                U8G2_SSD1306_128X32_UNIVISION_F_HW_I2C 
                                                u8g2GPIOMon(U8G2_R2      ,
                                                            U8X8_PIN_NONE, /* reset=*/ 
                                                            I2C_SCL      , /* clock=*/ 
                                                            I2C_SDA      );/* data =*/ 

                //OLED 128x64 als Systemmonitor //V07.04.a
                U8G2_SH1106_128X64_NONAME_F_HW_I2C 
                                                u8g2SysMon(U8G2_R2      ,
                                                           U8X8_PIN_NONE); /* reset=*/ 

And after that, start work with:

            u8g2GPIOMon.setI2CAddress(OLED_GPIOMON_I2CADDR << 1);
            u8g2GPIOMon.begin();

and

            u8g2SysMon.setI2CAddress(OLED_SYSMON_I2CADDR << 1);
            u8g2SysMon.begin();

The defines in these examples are:

    #define         I2C_SDA                     42
    #define         I2C_SCL                     2

and

    #define     OLED_SYSMON_I2CADDR           0x3d //ACHTUNG! Echte Adresse wird auf obere 7 Bit abgebildet = 0x7A
    #define     OLED_GPIOMON_I2CADDR          0x3c //ACHTUNG! Echte Adresse wird auf obere 7 Bit abgebildet = 0x78

After that, everything works. Regards from Hamburg A.