lexus2k / ssd1306

Driver for SSD1306, SSD1331, SSD1351, IL9163, ILI9341, ST7735, PCD8544, Nokia 5110 displays running on Arduino/ESP32/Linux (Rasperry) platforms
MIT License
662 stars 127 forks source link

Re-initialize OLED after power off #116

Closed deHarro closed 3 years ago

deHarro commented 3 years ago

Hi Aleksei, your lib is fine stuff and is working fast and reliable.

My question deals with OLEDs and their inherent wear of pixels when running for a long time with the same picture. To prevent this degradation, I want to power off the OLED display and switch it back on just to read the values, the attached Arduino sampled in the meanwhile.

But I can't get the display to show anything after cycling the power. There must be a function to get it back on its feet, but how is it done?

Thanks! Harald

krukhlis commented 3 years ago

It looks like you should use something like this to play with contrast( in order to save OLED and lower its current):

#define BRIGHTNESS 0x01 //your brightness
#define BRIGHTNESSREG 0x81
void setBrightness( ){
    Wire.beginTransmission(0x3c);
    Wire.write(0x00);
    Wire.write(BRIGHTNESSREG);
    Wire.endTransmission();
    Wire.beginTransmission(0x3c);
    Wire.write(0x00);
    Wire.write(BRIGHTNESS);
    Wire.endTransmission();
}

Copied from: https://bengoncalves.wordpress.com/2015/10/01/oled-display-and-arduino-with-power-save-mode/

Regarding ON/OFF, I think you can follow this issue: https://github.com/adafruit/Adafruit_SSD1306/issues/106 , e.g. this advice: https://github.com/adafruit/Adafruit_SSD1306/issues/106#issuecomment-357586629

lexus2k commented 3 years ago

Hello,

According to ssd1306 datasheet there are 2 special commands, supported by ssd1306 library: ssd1306_displayOff() and ssd1306_displayOn() Please, try those ones. They should preserve GDDRAM content in sleep mode.

Best regards

lexus2k commented 3 years ago

@deHarro But may I know, what type of display do you use in your project? ssd1306 library supports different displays.

deHarro commented 3 years ago

Hi Aleksei and krukhlis,

thanks for the fast responses! My OLED is a no name 128x64 type, all white. It seems to have a SSD1306 chip on board. [edit] It has a 4 pin connector, VCC, GND, SDA, SCL, no reset signal. [/edit]

At least I supppose that (SSD1306 chip), since every thing works as expected besides the power OFF/ON thing.

Perhaps I should point out, that I just want to remove power and reapply it afterwards (so, not using displayOFF/ON code for that purpose).

I don't see the difference between starting the OLED through code in setup() and the identical code sequence called once in a while (e.g. every 10 s) in loop(). When resetting the Arduino and going through setup(), the display is started. But if I remove power from the OLED and reapply it, calling the "initialization" in the loop, nothing is displayed.

Here is the setup():

void setup()
{
    // Pins initialisieren
    pinMode(RXD, INPUT);
    pinMode(TXD, OUTPUT);

    pinMode(LED, OUTPUT);

    // Initialisierung serielle SW SS für AtTiny_Daemon
    mySerial.begin(57600);

    // Initialisierung serielle HW SS   
    Serial.begin(115200);
    Serial.println("Initialise OLED screen...");

    /* Select the font to use with menu and all font functions */
    ssd1306_setFixedFont(ssd1306xled_font6x8);                             // <-------------
    ssd1306_128x64_i2c_init();                                             // <-------------
    ssd1306_clearScreen();                                                 // <-------------

    // Ausgabe des Splashscreen
    ssd1306_printFixed(col(0), line(1), "Terminal", STYLE_NORMAL);
    ssd1306_printFixed(col(0), line(3), "V1.0 - 11/2020", STYLE_NORMAL);
    ssd1306_printFixed(col(0), line(5), "Harald Sattler", STYLE_NORMAL);
    delay(3000);
    ssd1306_clearScreen();
}

And here the same sequence in loop():


void loop()
{
        // define some variables

    if (mySerial.available() > 0) 
    {
            // read data from serial
    }

    // Ausgabe auf OLED, jede halbe Sekunde aktualisiert
    if (millis() - displaytime > 500)
    {   
               // display some info on OLED
        }

    if (millis() - OLEDtime > 10000)                // alle 10 Sekunden OLED initialisieren
    {
        ssd1306_setFixedFont(ssd1306xled_font6x8);               // <-------------
        ssd1306_128x64_i2c_init();                               // <-------------
        ssd1306_clearScreen();                                   // <-------------
        OLEDtime = millis();
    }
}

I marked the corresponding lines of code in both routines with "// <-------------". Since there are no calls concerning the OLED display before the three marked lines in setup, I expected, that when calling the same routines in loop(), I would get the same result.

What am I missing? Thanks!

Harald

deHarro commented 3 years ago

Hi Aleksei,

I now read the SSD1306 manual (perhaps I should have done that before... ;)

RES# | Input | This pin is reset signal input. When the pin is pulled LOW, initialization of the chip is executed. Keep this pin HIGH (i.e. connect to VDD) during normal operation.

This is the crucial point! If I disconnect VCC at the OLED, this condition is violated.

If, on the other hand, I disconnect GND, #RES is kept high during power OFF and the following sequence restores the display to operation after reconnecting the OLED to GND:

    if (millis() - OLEDtime > 10000)        // alle 10 Sekunden OLED initialisieren
    {
        ssd1306_128x64_i2c_init();               // <-------------
        ssd1306_clearScreen();                   // <-------------

        OLEDtime = millis();
    }

Solved (for me).

nota bene: I read some issues on that topic on other forums and all stated, that on some types of OLED displays there is no reset pin available and one has to live with the fact, that the display has to be kept powered permanently, turning them off/to sleep by command.

Harald

[edit] After roughly 15 minutes some capacitors seem to be depleted and the display turns on (without GND!), but isn't refreshed (statically showing the last characters it received). Afterwards it is not possible to reactivate the display without resetting the Arduino. So I'll claim that "solution" as busted :(

If you have another idea to work around the problem, I'll happily try it. Keep me posted... Thanks!

Harald [/edit]

lexus2k commented 3 years ago

Thank you for all details on the issue. The library has Arkanoid project that uses deep sleep mode for Attiny85 and sleep mode for ssd1306. Although I had not compiled and run for a long time, but it worked with sleep mode successfully. With good schematics I was able to wake up device powered from battery (CR2032) after 1 year. First of all, that example uses ssd1306_displayOff() and ssd1306_displayOn(). In your case you have to clear screen content (ssd1306_clearScreen();) because you cut the power off for the display.

It seems to have a SSD1306 chip on board.

Since you use ssd1306_128x64_i2c_init(); and it works for you, yes, you have ssd1306 display.

Perhaps I should point out, that I just want to remove power and reapply it afterwards (so, not using displayOFF/ON code for that purpose).

If you cut the power, you will need do all initialization steps after powering on device. You can use displayON/displayOFF functions only, if power remains. By the way, in sleep mode display do not consumpts much power, may some nano amperes.

But if I remove power from the OLED and reapply it, calling the "initialization" in the loop, nothing is displayed.

It depends on how you remove power. If you remove power completely for the whole your device, then everything should be OK.

I expected, that when calling the same routines in loop(), I would get the same result.

Yes, if schematics is correct.

If I disconnect VCC at the OLED, this condition is violated.

Try to not disconnect VCC and GND lines. This display does not consumpts much power in sleep mode. Did try to measure Amperes using Multi-meter?

After roughly 15 minutes some capacitors seem to be depleted and the display turns on (without GND!),

What does't it mean without GND? Did you disconnect GND? Then i2c SDA/SCL lines can lead as GND, the hardware behavior is unpredictable in this case. Do you have schematics for your device?

I need more details

Best regards

deHarro commented 3 years ago

Hi Aleksei, the schematic is straight forward... Just an Arduino with an OLED SSD1306, connected via I2C, VCC (5V from Arduino) and GND. The display has 4 pins only, no #RES.

"Removing power" means: Only the OLED gets powered off, the Arduino is still running, gathering the data I want to read on the display if I repower it.

Some background information on the application: It's an AtTiny85, sending voltage readings from the battery of an UPS HAT for a Raspberry PI. I want to monitor the values for a long time (many days). Formerly I sent those readings to the serial console on my computer. But since the AtTiny gets resetted when I start or power off my computer, I searched another possibilty to show the values on demand. Therefore I used the Arduino with OLED display. On the other hand, from another project I know, that those OLED displays suffer from derating when running for a long time, displaying the ever same data on screen. The lighted pixels wear out and get dim, compared against the unlit pixels. (Hint: At the bottom of each page of my homepage you can choose the language via Google translate).

This very specialized application as Monitor for voltage readings is not very impressive and I surely may live with a push button to shut off and on the display, using displayOFF/ON commands. Even current consumption is of no importance here. But as I use these displays often in my applications, I am searching for a way to get the initialization done on will anywhere in my code, not only in the setup() routine.

Can you please elaborate on "...you will need do all initialization steps after powering on device." As far as I see, I have no additional steps besides this 3 lines

    /* Select the font to use with menu and all font functions */
    ssd1306_setFixedFont(ssd1306xled_font6x8);                             // <-------------
    ssd1306_128x64_i2c_init();                                             // <-------------
    ssd1306_clearScreen();                                                 // <-------------

in my setup(), at least nothing dealing with the OLED display.

But if I repeat these exactly 3 lines in loop() every now and then, I cannot get the display to show anything after repowering it. The difference obviously is, if I apply those 3 lines after power cycling the display in loop(), there may still be signals on SDA and SCL, respectively, in the moment I reapply the power. This is different in setup().

Possible work around: Is there a way to check, whether the display is operational (that is: powered) and so can act on commands and data on SCL/SDA lines? If so, perhaps I can check prior to the initialization steps whether the display is working and if not, I can stop all communication on I2C bus for a while, and then do the initialization similar as in setup().

Do you think this is feasible?

Of course, you are right in saying, that the display reacts erratically when I remove GND instead of VCC to power it off. I proved that beeing correct yesterday... :)

Thanks! Harald

lexus2k commented 3 years ago

Hello, Harald.

The lighted pixels wear out and get dim, compared against the unlit pixels.

Yeah, I know that too :)

As far as I see, I have no additional steps besides this 3 lines

Your code is correct, you don't need to do anything else in initialization sequence. But may be there is a bug in the library. And one more ssd1306 display has also software reset capability and I don't use that in my library.

According datasheet:

Power ON sequence: 1.Power ON VDD 2.After VDD become stable, set RES# pin LOW (logic low) for at least 3us (t1) (4) and then HIGH (logic high). 3.After set RES# pin LOW (logic low), wait for at least 3us (t2). Then Power ON VCC.(1) 4.After VCC become stable, send command AFh for display ON. SEG/COM will be ON after 100ms (tAF)

I've forgotten, do you have access to Reset pin? What if to start SW init sequence 100ms after resetting display?

Is there a way to check, whether the display is operational (that is: powered) and so can act on commands and data on SCL/SDA lines?

  1. Do you have any logic analyzer (something like that https://aliexpress.ru/item/4000146595503.html)?

  2. You can try in the loop without removing power:

ssd1306_displayOff();
delay(5000);
ssd1306_displayOn();
delay(5000);

So, you should see that display preserving content.

deHarro commented 3 years ago

Hi Aleksei, perhaps we talk about different issues. We both are not native english speakers, I am german. So perhaps I am misunderstood...

I know, that the display can hold its information when "cleared" by issuing ssd1306_displayOff(); But that's not the point. My concerns are located at power OFF/ON when the program is already running. And the fact, that running two times identical code (once in setup, secondly in loop) does not result in identical reaction of the display.

My display has no RESET pin, only 4 pins.

I am equipped with some instruments and are willing to help you debug your lib, if you want to.

Harald

lexus2k commented 3 years ago

Hello Harald,

  1. Earlier you mentioned that power consumption is not the problem for your project. So, the most simple way to avoid display wearing issue is to use displayOFF/displayON commands. And I confused that you need remove power and thus need full initialization sequence in loop method. Could you please clarify how you remove the power from oled display, when, and why?

  2. Your display is i2c, I have such, and yes, it doesn't have dedicated RES pin (but not all i2c displays). One moment I missed that you use ssd1306_128x64_i2c_init() in the loop. Every time ssd1306_128x64_i2c_init() function initializes i2c bus and sends init commands to display. I'm not sure, what happens on your platform, when i2c bus is initialized several times. Maybe that is the issue. So, can you check this code in the loop:

    ssd1306_128x64_init();  // send init commands, do not touch i2c bus config
    ssd1306_clearScreen();  // just clear screen

Best regards, Alexey

deHarro commented 3 years ago

Hello Aleksei,

first of all: I found that my code isn't executed as expected and the initialization in the loop is not called. See below...

To your questions:

  1. Yes, power is no issue in this project. But as stated, I want to get a solution for any other project where power may be an issue.

In this actual project I want to trace max and min values of a battery voltage for many days. To keep it simple I wanted to just pull the power wire of the OLED which is mounted on a breadboard together with the Arduino:

2020-11-16_IMG_3068

After 27 hours I had those two readings for max and min, respectively:

2020-11-18_IMG_3071

  1. I just tested your proposal (...init() instead of ...i2c_init()). It made no difference.

--->>> I then added debug options to the project and found, that my code for initializing the display every 10 seconds is not called at all!! :(

After some debugging (that is, Serial.print(..) statements and LED ON/OFF scattered into the code) it seems to me, that mostly when trying to send data to the unpowered OLED via I2C (using "ssd1306_printFixed(..)"), the code hangs forever.

I tried to circumvent this by implementing a watchdog triggered reset routine issuing this ssd1306_128x64_i2c_init(); ssd1306_clearScreen(); or this ssd1306_128x64_init(); ssd1306_clearScreen(); but both failed, the OLED doesn't come to life again.

Another attempt without watchdog, this time a button triggered interrupt routine with identical code as above for the watchdog, was to no avail, either.

I found no way to reset the I2C communication.

I'm near to giving up... :-(

Harald

deHarro commented 3 years ago

Hi Aleksei,

I implemented another (very basic and slow) OLED lib into the project and found more or less identical behavior when removing and reapplying power to the OLED display.

I now believe that those OLED displays without reset line have to be powered all the time, as you said. So I can confirm that your code is correct, no failure in your lib, fortunately :)

Having said, that actual I have no urgent need for this, I now stop investigating on that problem, wasted already too much time.

Harald