[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 -
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 theM5Core-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 theM5Core-Ink
library which does not yet use M5GFX. - try my modified
RTC_Clock.ino
example which works with the latest version of theM5Core-Ink
library.
Thanks
Felix - use the
-
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 -
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!
Lucavoid 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 -