sparkfun / SparkFun_Qwiic_OLED_Arduino_Library

Arduino Library for SparkFun's OLED Qwiic (I²C) boards
https://docs.sparkfun.com/SparkFun_Qwiic_OLED_Arduino_Library/introduction/
Other
9 stars 7 forks source link

Font not initialized when accessing an instance via a pointer? #20

Closed PaulZC closed 6 months ago

PaulZC commented 6 months ago

This is a fun one...!

#include <SparkFun_Qwiic_OLED.h> //http://librarymanager/All#SparkFun_Qwiic_OLED

QwiicMicroOLED *myOLED;

void setup()
{
    delay(1000);

    Serial.begin(115200);
    Serial.println("Running OLED example");

    Wire.begin();

    myOLED = new QwiicMicroOLED;

    if (myOLED->begin() == false)
    {
        Serial.println("Device begin failed. Freezing...");
        while (true)
            ;
    }
    Serial.println("Begin success");
}

void loop()
{
}

causes ESP32 to explode at this line.

My TemplateFu is not strong enough to understand why... But I think it has something to do with the QwFont or QwGrBufferDevice m_currentFont not being initialized properly? I think ->height is possibly returning nullptr?

PaulZC commented 6 months ago

My code example was missing Wire.begin(); . Added. Code is still failing in the same place. And I'm seeing failures on other platforms too, so I don't think this is ESP32-specific.

PaulZC commented 6 months ago

Humm. The more I play with this, the more I think something just isn't getting constructed or initiated correctly when using a pointer and new... Beyond my pay grade! ;-)

gigapod commented 6 months ago

On class QwGrBufferDevice, the constructor that had the (x, y, w, h) parameters wasn't calling the default constructor - which initialized m_currentFont to nullptr.

This worked when using a global/stack variable - since memory is probably zeroed in this case, but when alloc'd from the heap, memory is what it is - so m_currentFont was garbage - thus the font class variable looked valid, but it was not.

This is fixed - my mistake from the beginning...

-K

PaulZC commented 6 months ago

Thanks for the fast fix @gigapod ! Much appreciated. But sadly we're not out of the woods yet...

If I add myOLED->display(); it blows up again. I'm having trouble finding out exactly where.

I'll dig into this in the morning...

#include <SparkFun_Qwiic_OLED.h> //http://librarymanager/All#SparkFun_Qwiic_OLED

QwiicMicroOLED *myOLED;

void setup()
{
    delay(1000);

    Serial.begin(115200);
    Serial.println("Running OLED example");

    Wire.begin();

    myOLED = new QwiicMicroOLED;

    if (myOLED->begin() == false)
    {
        Serial.println("Device begin failed. Freezing...");
        while (true)
            ;
    }
    Serial.println("Begin success");

    myOLED->display();
}

void loop()
{
}
gigapod commented 6 months ago

ahh heck ... standby ...

gigapod commented 6 months ago

Similar issue with the grssd1306 driver - bad logic with instance variable initialization. I really had my head wrong on constructor chaining when implementing this. Fixed!! and version of library bumped

PaulZC commented 6 months ago

Oh, that is SO much better! It makes the following possible. Thanks!!

// This demo shows how to use the QwiicCustomOLED class,
// allowing the display width, height etc. to be set manually.
// It also shows how to use pointers to the OLED object and the I2C port.

#include <SparkFun_Qwiic_OLED.h> //http://librarymanager/All#SparkFun_Qwiic_OLED

QwiicCustomOLED *myOLED = nullptr;

TwoWire *I2C_0 = nullptr; // Note: do not use the name I2C0. It causes much badness on ESP32!
TwoWire *I2C_Display = nullptr; // If we have two I2C busses, we can point I2C_Display at either one. Handy!

void setup()
{
    delay(1000);

    Serial.begin(115200);
    Serial.println("Running OLED example");

    I2C_0 = new TwoWire(0); // Use I2C port 0 on ESP32

    I2C_Display = I2C_0; // Point I2C_Display at I2C_0

    I2C_0->begin(21, 22); // Begin I2C_0: SDA = 21, SCL = 22

    myOLED = new QwiicCustomOLED;

    // If desired, we can customize the OLED before we begin it.
    // Otherwise it will default to 128x64 (1.3" OLED).
    // These are the settings for the Micro OLED: 64x48
    myOLED->setXOffset(2);
    myOLED->setYOffset(0);
    myOLED->setDisplayWidth(64);
    myOLED->setDisplayHeight(48);
    myOLED->setPinConfig(0x12);
    myOLED->setPreCharge(0xF1);
    myOLED->setVcomDeselect(0x40);
    myOLED->setContrast(0x8F);

    // Initalize the OLED device and related graphics system
    if (myOLED->begin(*I2C_Display, 0x3D) == false)
    {
        Serial.println("Device begin failed. Freezing...");
        while (true)
            ;
    }
    Serial.println("Begin success");

    // Do a simple test - fill a rectangle on the screen and then print hello!

    // Fill a rectangle on the screen that has a 4 pixel board
    myOLED->rectangleFill(4, 4, myOLED->getWidth() - 8, myOLED->getHeight() - 8);

    String hello = "hello"; // our message

    // Center our message on the screen. Get the screen size of the "hello" string,
    // calling the getStringWidth() and getStringHeight() methods on the oled

    // starting x position - screen width minus string width  / 2
    int x0 = (myOLED->getWidth() - myOLED->getStringWidth(hello)) / 2;

    // starting y position - screen height minus string height / 2 
    int y0 = (myOLED->getHeight() - myOLED->getStringHeight(hello)) / 2;

    // Draw the text - color of black (0)
    myOLED->text(x0, y0, hello, 0);

    // There's nothing on the screen yet - Now send the graphics to the device
    myOLED->display();

    // That's it - HELLO!
}

void loop()
{
    delay(1000); // Do nothing
}
PaulZC commented 6 months ago

Just FYI: I bumped the library.properties version and re-released. Please Pull so you're all up to date!