GxEPD2 GDEY029T94 SSD1680 Weird partial update behaviour

I've been using a GDEM029T94 with a esp32-S3 In the past with my code no problem, i have a version of my code that uses deep sleep and one without. My GDEM029T94 worked well with both versions of my code. Now i switched to a GDEY029T94 (2.9 inch E Ink screen fast refresh, SPI e-paper display, GDEY029T94_Good Display) SSD1680. And my code that doesn't utilize deep sleep works as it should, but when i try to use my code that uses deep sleep the screen wont do partial updates. At the end of my code right before deep sleep i run this code:


  display.firstPage();
  display.setPartialWindow(timex-4, timey-5, 45, 18);
  do {
    display.fillScreen(GxEPD_WHITE);
    u8g2Fonts.setCursor(timex, timey + 10);
    u8g2Fonts.print(millis());
     PrintSec(seconds);
     PrintColon();
     PrintMin(minutes);
  } 
  while (display.nextPage());

  

sleepSensor(1000);
}

After this my screen doesnt do the partial update and only shows the initial print on the screen, but i can tell that there is some updating happening on the screen (borders flicker when trying to update), and after about a minute or two i can see the numbers faintly beginning to show (ghosting of the numbers). Now the weird thing is that if i run the update code twice (everything before sleepSensor(1000) right after each other. then it does the partial update as it should. This only happens with the new screen, partial update works in my code that doesn't utilize deep sleep and it cant tell any difference in the way the display method is implemented.

I would appreciate if anyone could give me a clue about what the problem could be. Here is the whole code. I've tried everything i could find but to no avail. If you need more information to help please tell me, i appreciate any help.

#include <WiFi.h>
#include "esp_wifi.h"
#include "esp_bt.h"
#include "esp_bt_main.h"

//
#include "GxEPD2_display_selection_added.h"
#define USE_HSPI_FOR_EPD
#define ENABLE_GxEPD2_GFX 0
int timex = 47;
int timey = 90;
#include <GxEPD2_BW.h>
#include <GxEPD2_3C.h>
#include <GxEPD2_4C.h>
#include <GxEPD2_7C.h>
#include <Fonts/FreeMonoBold9pt7b.h>
#include <U8g2_for_Adafruit_GFX.h>
#include <Fonts/FreeMonoBold9pt7b.h>
#include "Bitmap.h"
#define GxEPD2_DISPLAY_CLASS GxEPD2_BW
#define GxEPD2_DRIVER_CLASS GxEPD2_290_GDEY029T94 

#define GxEPD2_BW_IS_GxEPD2_BW true
#define GxEPD2_3C_IS_GxEPD2_3C true
#define GxEPD2_7C_IS_GxEPD2_7C true
#define GxEPD2_1248_IS_GxEPD2_1248 true
#define IS_GxEPD(c, x) (c##x)
#define IS_GxEPD2_BW(x) IS_GxEPD(GxEPD2_BW_IS_, x)
#define IS_GxEPD2_3C(x) IS_GxEPD(GxEPD2_3C_IS_, x)
#define IS_GxEPD2_7C(x) IS_GxEPD(GxEPD2_7C_IS_, x)
#define IS_GxEPD2_1248(x) IS_GxEPD(GxEPD2_1248_IS_, x)

unsigned long oldTime;  // The previous time the flow rate was calculated
GxEPD2_BW<GxEPD2_290_GDEY029T94, GxEPD2_290_GDEY029T94::HEIGHT> display(GxEPD2_290_GDEY029T94(/*CS=*/ 12, /*DC=*/ 11, /*RST=*/ 10, /*BUSY=*/ 9)); // GDEY029T94  128x296, SSD1680, (FPC-A005 20.06.15)

unsigned long currentMillis;
int seconds;
int minutes;

#if defined(ESP32) && defined(USE_HSPI_FOR_EPD)
SPIClass hspi(HSPI);
#endif
/////////////////DRAWING FUNCTIONS ///////////////////
#define BLACK 0x0000
#define WHITE 0xFFFF
////////////////////////////////////////////////////////////////////////////////////
U8G2_FOR_ADAFRUIT_GFX u8g2Fonts;
/////////////////////////////////SETUP////////////////////////////////////////


void PrintSec(int seconds) {
  u8g2Fonts.setCursor(timex + 20, timey + 10);
  //u8g2Fonts.print(millis());
  u8g2Fonts.print(seconds);
}

void PrintColon() {
  u8g2Fonts.setCursor(timex + 12, timey + 10);
  //u8g2Fonts.print(millis());
  u8g2Fonts.print(":");
}

void PrintMin(int minutes) {
  if (minutes > 9) {
    u8g2Fonts.setCursor(timex - 4, timey + 10);
    //u8g2Fonts.print(millis());
    u8g2Fonts.print(minutes);
  } else {
    u8g2Fonts.setCursor(timex + 6, timey + 10);
    //u8g2Fonts.print(millis());
    u8g2Fonts.print(minutes);
  }
}

void drawBitmaps(const unsigned char *bitmap) {
  // Configure the display according to our preferences
  display.setRotation(0);
  display.setFullWindow();
  // Display the bitmap image
  display.firstPage();
  do {
    display.fillScreen(GxEPD_WHITE);
    display.drawInvertedBitmap(0, 0, bitmap, display.epd2.WIDTH, display.epd2.HEIGHT, GxEPD_BLACK);
  } while (display.nextPage());
}


//////Deep SLeep ////////
RTC_DATA_ATTR unsigned long millisOffset = 0;
RTC_DATA_ATTR int bootCount = 0;
#define uS_TO_mS_FACTOR 1000

unsigned long offsetMillis()
{
    return millis() + millisOffset;
}

void sleepSensor(unsigned long sleepMillis)
{
    esp_sleep_enable_timer_wakeup(sleepMillis * uS_TO_mS_FACTOR);

    millisOffset = offsetMillis() + sleepMillis;

    esp_deep_sleep_start();
}
void setup() {
#if defined(ESP32) && defined(USE_HSPI_FOR_EPD)
  hspi.begin(13, -1, 14, 12); // remap hspi for EPD (swap pins)
  display.epd2.selectSPI(hspi, SPISettings(8000000, MSBFIRST, SPI_MODE0));
#endif
esp_bluedroid_disable();
esp_bt_controller_disable();
esp_wifi_stop();
setCpuFrequencyMhz(180);

  WiFi.mode(WIFI_OFF);

  // Turning off Bluetooth
  btStop();

  if (bootCount == 0) {
    display.init();
    bootCount++;
    display.firstPage();
    display.setTextColor(GxEPD_BLACK);
    //drawBitmaps(epd_bitmap_front4);
    display.setRotation(1);
    u8g2Fonts.begin(display);  // connect u8g2 procedures to Adafruit GFX
    u8g2Fonts.setFont(u8g2_font_10x20_tf); 
    uint16_t bg = GxEPD_WHITE;
    uint16_t fg = GxEPD_BLACK;
    u8g2Fonts.setForegroundColor(fg);  // apply Adafruit GFX color
    u8g2Fonts.setBackgroundColor(bg);
  } else {
    display.init(0, false);
    bootCount++;

    display.setRotation(1);
    u8g2Fonts.begin(display);  // connect u8g2 procedures to Adafruit GFX
    u8g2Fonts.setFont(u8g2_font_10x20_tf); 

    uint16_t bg = GxEPD_WHITE;
    uint16_t fg = GxEPD_BLACK;
    u8g2Fonts.setForegroundColor(fg);  // apply Adafruit GFX color
    u8g2Fonts.setBackgroundColor(bg);
  }

  currentMillis = millis() + millisOffset;
  seconds = currentMillis / 1000;
  minutes = seconds / 60;
  currentMillis %= 1000;
  seconds %= 60;
  minutes %= 60;
  currentMillis %= 1000;
  seconds %= 60;
  minutes %= 60;


  display.firstPage();
  display.setPartialWindow(timex-4, timey-5, 45, 18);
  do {
    display.fillScreen(GxEPD_WHITE);
    u8g2Fonts.setCursor(timex, timey + 10);
    u8g2Fonts.print(millis());
     PrintSec(seconds);
     PrintColon();
     PrintMin(minutes);
  } 
  while (display.nextPage());

  

sleepSensor(1000);
}

@bennybignut , Hi, welcome to the forum!

Your issue is related to the initial parameter of the init method.

I have never tested if this feature (initial false) really works, nor if it works with all driver classes.

I was going to ask you if you did reset your bootCount variable after you replaced your display panel. But most likely you did run your code without processor sleep first without power removal, before running your code with processor sleep. Then it should have worked.
Make sure you have a means to reset bootCount after every power removal.

With ESP32 you can check the reset reason. So you can use this check for the initial parameter, using false only after reset by timer.

I need to do tests with initial false, at least for driver class GxEPD2_290_GDEY029T94, for your issue. But I don't know how soon this will be.
-jz-

Thank you for the response, and thank you for developing this great library.

I tried implementing reset reason check to ensure the screen is initialized correctly every time, but it sadly didn't change anything. I assume it was working as it should because i see the screen doing the "flickering" on boot as it should. Just to be clear about what is happening, i connect my board the screen does the initialization refresh and shows the first print correctly on the screen "0:00" after that the screen "tries" to do partial refresh but it's extremely faint. It's as if the last update of the screen before going to deep sleep will not work properly for some reason on this screen, but every update before the last one works fine, i can reproduce this problem by

1. in my code posted in the OP here i commented out my drawBitmap(), however if its not commented out it, it draws the bitmap correctly but then the first time "0:00" is never printed on the screen (only can be seen faintly after a few minutes/attempted partial refreshes)

2. (without the bitmap printing)To correctly print the time i need to refresh the screen twice with the same information (because the last refresh will not work),

so this works:

  display.firstPage();
  display.setPartialWindow(timex-4, timey-5, 45, 18);
  do {
    display.fillScreen(GxEPD_WHITE);
    u8g2Fonts.setCursor(timex, timey + 10);
    u8g2Fonts.print(millis());
     PrintSec(seconds);
     PrintColon();
     PrintMin(minutes);
  } 
  while (display.nextPage());

    display.firstPage();
  display.setPartialWindow(timex-4, timey-5, 45, 18);
  do {
    display.fillScreen(GxEPD_WHITE);
    u8g2Fonts.setCursor(timex, timey + 10);
    u8g2Fonts.print(millis());
     PrintSec(seconds);
     PrintColon();
     PrintMin(minutes);
  } 
  while (display.nextPage());

sleepSensor(1000);
}

But this doesnt:

  display.firstPage();
  display.setPartialWindow(timex-4, timey-5, 45, 18);
  do {
    display.fillScreen(GxEPD_WHITE);
    u8g2Fonts.setCursor(timex, timey + 10);
    u8g2Fonts.print(millis());
     PrintSec(seconds);
     PrintColon();
     PrintMin(minutes);
  } 
  while (display.nextPage());

sleepSensor(1000);
}

The original code in my OP worked on my old screen (I might have said the wrong model number, basically i took this module (Amazon.de) and removed the screen and used it with this esp32 epaper board (https://www.waveshare.com/e-paper-esp32-driver-board.htm).

This new screen has 4 grayscale levels, however i'm only trying to use black and white for now. i don't know if this has something to do with the problem

Edit: This works too for some reason (running the while(display.nextpage()) twice)

  display.firstPage();
  display.setPartialWindow(timex-4, timey-5, 45, 18);
  do {
    display.fillScreen(GxEPD_WHITE);
    u8g2Fonts.setCursor(timex, timey + 10);
    u8g2Fonts.print(millis());
     PrintSec(seconds);
     PrintColon();
     PrintMin(minutes);
  } 
  while (display.nextPage());
  while (display.nextPage());

These two statements are in the wrong sequence.

These two statements are in the wrong sequence.

I don't know if this is the cause.

I don't have time to check your issue immediately. Please be patient.
-jz-

Thanks, no that wasn't it either,

Adding display.nextPage() after the while(display.nextPage()) works too.

 display.setPartialWindow(timex-4, timey-5, 45, 18);
  display.firstPage();

  do {
    display.fillScreen(GxEPD_WHITE);
    u8g2Fonts.setCursor(timex, timey + 10);
    u8g2Fonts.print(millis());
     PrintSec(seconds);
     PrintColon();
     PrintMin(minutes);
  } 
  while (display.nextPage());
  display.nextPage();

This is a temporary fix but this increases the partial refresh time to a total of about 1 second including the esp32 setup time, which is alot of active time in regards to battery consumption.

I appreciate your help very much, no stress, i'm just grateful you even want to take time to help me :slight_smile:

Edit: Update

if i define this as my display, and the associated drive it does update, however the outputs garbled data, might be useful to determine why

#define GxEPD2_DRIVER_CLASS GxEPD2_290_BS

GxEPD2_BW<GxEPD2_290_BS, GxEPD2_290_BS::HEIGHT> display(GxEPD2_290_BS(/*CS=*/ 12, /*DC=*/ 11, /*RST=*/ 10, /*BUSY=*/ 9)); // DEPG0290BS 128x296, SSD1680 //works but datamessed up

Ok, you need to take notice that I do voluntary work providing this library.
I found out that working on your issue is not fun. So I decided to put this aside, until fun returns to me.

You can try whatever you like, and you can report if you like. But I wont answer for quite some time.
-jz-

Fully understandable, i appreciate you even taking a look at it :slight_smile: Ill keep at it and post the solution here when i find it.

@ZinggJM I "fixed" the problem by commenting out _PowerOn() in _Update_Part() function in GxEPD2_290_GDEY029T94.cpp.

void GxEPD2_290_GDEY029T94::_Update_Part()
{
  //_PowerOn();
  _writeCommand(0x22);
  _writeData(0xfc);     
  _writeCommand(0x20);
  _waitWhileBusy("_Update_Part", partial_refresh_time);
}

Im not certain why it worked, there is properly a good reason for why the function is there. One thing i dont understand is in _PowerOn() function 0xf8 is written to registe 0x22, but from what i can see from the datasheet for ssd1680 that is not a valid command (see image), i'm properly misunderstanding something, as im not an expert in this stuff. (unless its to set the r/wf. D/C bits?)

It should be said that i'm using a custom designed PCB that might have some weird flaw that causes this bug, however its weird it would only be for this screen.

I confirm that your change fixes the issue.
Removing _PowerOn(); makes sense, as the Display Update Control 2 (0x22) for screen refresh has all the bits set to enable clock, enable analog, load temperature, load LUT, mode, display.
But the reason is the behavior of the Master Activation used for _PowerOn();.
The Display Update Control 2 used for _PowerOn();, 0xf8, is enable clock, enable analog, load temperature, load LUT, MODE 2, which is not wrong, but has a side effect, depending on the SSD controller model. The different SSD models have different behavior concerning buffer switching. I struggled with this behavior with each new controller, cost me a lot of time.
In this case, loading LUT for Mode 2, causes the buffer switch, even if the refresh bit is not set.

I will fix this in the next release. It will require a lot of re-testing and testing for processor sleep refresh continuation.
The next release will be delayed. I need a break and less stress.

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.