[Core Ink] Unwanted display refresh on startup



  • Hello World,
    I've bought the Core Ink looking for a Wi-Fi battery clock, but since I was not completely satisfied by the "RTC_Clock.ino" example given on github on the official repository I'm making some modification to the original code (e.g. removing serial and LED, importing fonts from the FactoryTest example, adding RTC wakeup on alarm instead of fixed timer&& aligning to the second "00").

    I'm actually stuck on the very last bit which would reach che lowest execution times needed to get the longest battery life: avoid the EPD refresh during M5.begin().

    When the system starts from rtc wakeup, the display is refreshed (old sprite goes away-white display-some random pixels are turned on-white display) instead of printing the new sprite on the top of another (similar to the case when I do it at runtime).

    For what I have understood this happens during "M5.begin(true,false,false);" command, in particular in the "M5Ink.begin();" . I've tried commenting in this last call the "M5GFX::clear(TFT_BLACK);" which I thought was the problem but this not only didn't solved the problem but prevented any sprite to be printed...

    Any hints?

    Spaghetto



  • E-INK displays are not LCD, LED or OLED displays and so do not have the same write/refresh sequence.



  • @ajb2k3 thanks for the answer, I just don't get your point...this just strenghten my question. I'm trying to avoid any kind of clear screen on the system startup (since I'm going to just overwrite the Sprite printed before the shutdown) but in the setup() the M5. begin() forces the epd in a sort of (strange) clear, how would you avoid this?
    Tnx

    alt text



  • Up



  • You cant avoid the refresh/clear screen on start up because its part of the screens function and internal firmware not controlled by the ESP32.



  • Hello @Spaghetto

    the RTC_Clock.ino example I've provided ages ago (PR) got broken with the latest commits to the M5Core-Ink library which now uses M5GFX library.

    You have two options:

    • use the RTC_Clock.ino example as is, but with release version 0.0.7 of the M5Core-Ink library which does not yet use M5GFX.
    • try my modified RTC_Clock.ino example which works with the latest version of the M5Core-Ink library.

    Thanks
    Felix



  • Hi @felmue,
    thanks for the hint, I gave some tries with the modified example (2optn of yours) which is correctly working at the startup.

    M5.M5Ink.init_without_reset();
    M5.M5Ink.setEpdMode(epd_mode_t::epd_text);
    M5.M5Ink.invertDisplay(true); 
    

    is making the game and it works like a charm on the rtc triggered startup...BUT...now I'm having issues on the sprite push. Take a look at the 10:29>10:30 transition in the picture below: the old data is not correctly overwritten and new and old data mix up together!

    The main difference between your code (which I've tested successfully on my coreink and it work flawlessly) and mine is that I'm using drawImageToSprite() instead of TimePageSprite.drawString(). Probably the real problem is that I don't understand the real meaning of TimePageSprite.pushSprite() and it's not correct I'm pushing zeroed pixels over old ones... Is this correct?

    Thanks in advance,
    Luca

    0_1711359306019_6e4d01ce-7aac-4f0d-8831-ed18baca8e36-immagine.png



  • Hello @Spaghetto

    the way I understand the system is that there are three levels of "screen" data. The sprite, the internal display buffer and the actual pixels on the screen. Now, to put something onto screen, first data is drawn onto the sprite, next the sprite is pushed to the display driver which compares the sprite data with the internal buffer and every pixel which is different is then changed on the screen.

    So for example starting with a blank sprite, blank buffer and blank screen then update the screen two times (w/o shutdown in between):

                    sprite  buf     action  screen
                    00000 | 00000 | ----- | 00000
    draw to sprite  11000 | 00000 | ----- | 00000
    push sprite     11000 | 11000 | SS--- | 11000
    clear sprite    00000 | 11000 | ----- | 11000
    draw to sprite  10001 | 11000 | ----- | 11000
    push sprite     10001 | 10001 | -C--S | 10001
    

    Note: action means the old state of the buffer is compared to the new and then a pixel gets either set (S), cleared (C) or is unchanged (-).

    Now the same, but with shutdown and restart between first and second update:

                    sprite  buf     action  screen
                    00000 | 00000 | ----- | 00000
    draw to sprite  11000 | 00000 | ----- | 00000
    push sprite     11000 | 11000 | SS--- | 11000
    shutdown
    soft restart    00000 | 00000 | ----- | 11000
    draw to sprite  10001 | 00000 | ----- | 11000
    push sprite     10001 | 10001 | S---S | 11001
    

    Note: the shutdown also turns off the power to the display driver and this clears the display buffer which leads to pixels only getting set.

    The trick to avoid that is to setup the display buffer after the restart with the data it contained before the shutdown. In my RTC clock example I achieve this by waking the device up 2 seonds before the minute changes. This allows me to push the "old" data to the display driver first (which changes nothing on the screen), wait until the minute has changed (seconds equal zero) and then push the new data.

                    sprite  buf     action  screen
                    00000 | 00000 | ----- | 00000
    draw to sprite  11000 | 00000 | ----- | 00000
    push sprite     11000 | 11000 | SS--- | 11000
    shutdown
    soft restart    00000 | 00000 | ----- | 11000
    prepare buf with old data
    draw to sprite  11000 | 00000 | ----- | 00000
    push sprite     11000 | 11000 | SS--- | 11000
    wait for minute change
    draw to sprite  10001 | 11000 | ----- | 11000
    push sprite     10001 | 10001 | -C--S | 10001
    

    Thanks
    Felix



  • Hi @felmue,
    thanks in advance for your exquisite answer, which clarity left me stunned. Kudos to you!

    I missed the concept in the very first try because basically I'm doing my best to reduce the on-time, the main concept is that I'm not giving a shutdown(58) but a shutdown(time), so I didn't need to print the 58th second time then waiting for the refresh (worst case: 2sec on), I was directly updating the new time since the RTC was setted to trigger on the new minute tick (:actual_time+1 min). The simple solution is to print the previous minute or if xx:00 lcd full begin with refresh (so there isn't the need to subtract time cascading back to the year), than the new time, the on-time it's so rapid that the battery benefits and the system after two weeks is still running with no additional charges.

    The epd refresh on min 00 is barely noticeable but I'm trying to understand if is really needed since I never get "bad pixels" that would motivate a screen refresh (with the M5.M5Ink.begin() ), maybe this will be the very last try to improve furthermore the on-time...

    This is why I took so much time to answer here, I tought I would have give an answer in detail releasing the code here referring to it as the highest system autonomy that I reached but it still doesn't want to go off...

    Thanks for your support!
    Luca

    void setup() {    
        digitalWrite(LED_EXT_PIN, HIGH); //Turn off LED
    
        // Check power on reason before calling M5.begin(), which calls Rtc.begin() which clears the timer flag.
        uint8_t data = 0;
        Wire1.begin(21, 22);
        Wire1.beginTransmission(0x51);
        Wire1.write(0x01);
        Wire1.endTransmission();
        if (Wire1.requestFrom(0x51, 1)) {
            data = Wire1.read();
        }
    
        //Init system but not EPD (prevent EPD refresh)
        M5.begin(false);
        
        RTC_TimeTypeDef time;
        RTC_DateTypeDef date;
        TimePageSprite.creatSprite(0, 0, 200, 200);
    
        // Check timer flag
        if ((data & 0b00001000) == 0b00001000) { //Reset by RTC alarm
        
            M5.rtc.GetTime(&time);
            M5.rtc.GetDate(&date);
    		
            //If hh:00 full refresh...
            if (time.Minutes == 0) {
                M5.M5Ink.begin();
    			//M5.M5Ink.clear(); //Already cleared in the M5.M5Ink.begin()
            }
            //...else
            else {  
                //Init EPD without refreshing the screen
                M5.M5Ink.init_without_reset();
                M5.M5Ink.setEpdMode(epd_mode_t::epd_text);
                M5.M5Ink.invertDisplay(true);
                //Print previous hh:mm (prevent misprinting if a M5.M5Ink.clear() is not performed)
                drawTimeAndDate(time, date, 1);
                TimePageSprite.pushSprite();
            }
        } 
        else { //Reset by PON
            digitalWrite(LED_EXT_PIN, LOW); //CONFIGURATION: LED on
    
            //Start the system
            M5.M5Ink.begin();
            M5.M5Ink.clear();
    
            //Check if EPD is correctly initialized
            if (M5.M5Ink.isInit() == false) {
                while (1){ //ERROR: strobe
                    digitalWrite(LED_EXT_PIN, LOW);
                    delay(500);        
                    digitalWrite(LED_EXT_PIN, HIGH);
                    delay(500);        
                } 
            }    
    
            //Welcome screen
            StartupPageSprite.creatSprite(0, 0, 200, 200, true);
            StartupPageSprite.drawString(0, rowp*(rown++), "######## HELLO! ########");
            StartupPageSprite.pushSprite();
            
            //Fetch current time from Internet
            getNTPTime();
            M5.rtc.GetTime(&time);
            M5.rtc.GetDate(&date);
            StartupPageSprite.drawString(0, rowp*(rown++), "Going online...");
            StartupPageSprite.pushSprite();
            delay(1000);
            M5.M5Ink.clear();        
        }
    
        //Draw actual time and date
        drawTimeAndDate(time, date, 0); 
        TimePageSprite.pushSprite();
    
        //Config RTC for wakeup on the next minute (via HW interrupt) and shutdown ESP
        time.Seconds=0;
        if(time.Minutes<59) time.Minutes++;
        else{
            time.Minutes=00;
            if(time.Hours<23)  time.Hours++;
            else               time.Hours=00;
        } 
        M5.shutdown(time);
    
        //Following code is executed only in USB powered mode (e.g. Recharge\Debug) 
        delay(60*1000); 
        ESP.restart(); //<--Not working! Debug me!
    }
    


  • Hello @Spaghetto

    I am glad to hear you got it working the way you wanted. And thank you for sharing your code and the detailed explanation.

    Thanks
    Felix