Waveshare e-paper displays with SPI

It is connected directly via cable to the Joy-it MEGA 2560 (Atmega2560) and the recommended wiring, and it only happens with the new display.With the old display it worked fine with the same setup (power supply 5V), I wanted to exchange the displays 1 to 1 after the damage and that's how I came to this post.

It's strange that after a few updates the display reaches full strength, and after switching it off the contrast drops again slightly. But the initial state is actually not cool :confused:

Sometimes an improvement ("V2") doesn't seem to be better after all....


there may be a difference between the booster circuit of the board and the booster circuit in the panel specs.

If enough users recommend to Waveshare that they should provide me with a free sample, then one day I may be able to analyze these issues. But I am not willing to buy this board.

Or is it possible that there is a problem like you explained in this post:

At the very beginning, the display updates everything completely during init, after that I have the feeling that it is interrupted when building up the contrast.

Post diagnostic output, in a code window please.

There is no diagnostic output when I upload the code....

Everything works, only when the power source is connected for the first time I have the problem. Once the application is running, it is no longer a problem, but only at the beginning. Unfortunately, I can't explain this at the moment.

I have found out what the problem could be, or what has an influence:

 display.setPartialWindow(0, 0, display.width(), display.height());

I use this line to refresh the entire display, but without re-initiating it and flickering each time. As a result, the complete screen content changes quasi statically... As soon as the line is activated in the code (only with the new display, with the old one it worked perfectly as I said) the strength only comes with time after the reboot. If the line is commented out, the display flickers every time it changes, but the strength is there immediately... I would like to avoid this flickering, is there another way?

Update: I have found a workaround.Instead of putting the expression before the do .... while expression, I put it after all the commands and now it works almost completely the way I want. I don't know what this did in terms of source code, but it seems to work.... At least the display is there immediately!

With the incomplete and unstructured information you provide, I can't help you.

You could either provide your complete code, or better a minimal example that shows your effect.

Do you use hibernate()? Did you read the README.md remark on "clever" reset circuit?

Hi Jean-Marc,

I'm trying to use your library together with an eInk display to display gif files from an SD card (using the AnimatedGIF library - I increased the max image size buffer to 880 in the header fileThis text will be hidden). In my case I'm using a 7.5 inch b/w 880x528 from Waveshare with the e-Paper driver HAT and an ESP32 Adafruit HUZZAH32.

When reading images from an SD card, some are displayed as the full image, but most of the images are displayed only partially (1 out of 4 pages) and during the picture loop the connection to the SD card is lost and I have to fully restart the board (it does not crash though). A wake up from deep sleep will not be enough to reconnect the SD card.

This code will always work (reading the image and parsing the gif) as long as it is not painted using display.drawPixel in the GIFDraw function.
Meaning, when the display.drawPixel lines are removed, it will work, otherwise, it will cause the above described issue.

I assume this is a problem related to memory, do you have any ideas what could cause the issue.


#include <SPI.h>
#include <SD.h>

/* Animated GIF */
#include "src/AnimatedGIF/AnimatedGIF.h"
AnimatedGIF gif;

/* Enable or disable GxEPD2_GFX base class */
#define ENABLE_GxEPD2_GFX 1

#define DISPLAY_WIDTH 880
#define DISPLAY_HEIGHT 528

/* Display Dependencies */
#include <GxEPD2_BW.h>
#include <GxEPD2_3C.h>
#include <U8g2_for_Adafruit_GFX.h>


/* Connections for Adafruit Feather */
static const uint8_t EPD_BUSY = 32; // to EPD BUSY
static const uint8_t EPD_CS   = 15; // to EPD CS
static const uint8_t EPD_RST  = 27; // to EPD RST
static const uint8_t EPD_DC   = 33; // to EPD DC
static const uint8_t EPD_SCK  =  5; // to EPD CLK
static const uint8_t EPD_MISO = 19; // Master-In Slave-Out not used, as no data from display
static const uint8_t EPD_MOSI = 18; // to EPD DIN

/* Mapping of the ESP32 Driver Board */
GxEPD2_3C < GxEPD2_750c_Z90, GxEPD2_750c_Z90::HEIGHT / 4 > display(GxEPD2_750c_Z90(/*CS=D8*/ EPD_CS, /*DC=D3*/ EPD_DC, /*RST=D4*/ EPD_RST, /*BUSY=D2*/ EPD_BUSY)); // GDEH075Z90 880x528


/* Files */
File f;

/* Setup */
void setup()
  // Serial Port

  // Display

  // Variables
  int error = -1;

  // Prepare GIF

  // SD Card
  if (!SD.begin())
      Serial.println(F("Error: SD not initialization failed!"));

  Serial.print("Start Free Heap: ");

  if ( gif.open( "/movie/thumbnail013.gif" , GIFOpenFile, GIFCloseFile, GIFReadFile, GIFSeekFile, GIFDraw) )
    GIFINFO gi;
    Serial.printf("GIF opened (%dx%d)\n", gif.getCanvasWidth(), gif.getCanvasHeight());
    if (gif.getInfo(&gi)) {
      Serial.printf("Frames: %d\n", gi.iFrameCount);
      Serial.printf("Duration: %d ms\n", gi.iDuration);
      Serial.printf("Max delay: %d ms\n", gi.iMaxDelay);
      Serial.printf("Min delay: %d ms\n", gi.iMinDelay);
    Serial.print(F("GIF display Error: "));
    Serial.println( gif.getLastError() );

  // Build the View
  int page = 1;
    Serial.printf("Page: %d\n", page);

    while (gif.playFrame(true, NULL))
      if( gif.getLastError() > 0 )
        Serial.printf("Issue: %d\n", gif.getLastError() );

    Serial.printf("Free Heap: %d bytes\n", ESP.getFreeHeap() );
  while (display.nextPage());

  /* Power off the display and put ESP32 into sleep mode */

  int delayTime = 60 * 60 * 24; // Deep sleep for a day;

  esp_sleep_enable_timer_wakeup( 120 * 1000000LL);
  Serial.printf("Deep-sleep: %d seconds\n", delayTime );

/* Program Loop */
void loop()

/* Open file from SD card */
void * GIFOpenFile(const char *fname, int32_t *pSize)
  f = SD.open(fname);
  // Serial.println("GIF: Open File...");
  if (f)
    *pSize = f.size();
    return (void *)&f;
  return NULL;
} /* GIFOpenFile() */

/* Close file */
void GIFCloseFile(void *pHandle)
  // Serial.println("GIF: Close File...");
  File *f = static_cast<File *>(pHandle);
  if (f != NULL)
} /* GIFCloseFile() */

/* Read a given GIF file */
int32_t GIFReadFile(GIFFILE *pFile, uint8_t *pBuf, int32_t iLen)
    int32_t iBytesRead;
    iBytesRead = iLen;
    File *f = static_cast<File *>(pFile->fHandle);
    // Note: If you read a file all the way to the last byte, seek() stops working
    if ((pFile->iSize - pFile->iPos) < iLen)
       iBytesRead = pFile->iSize - pFile->iPos - 1; // <-- ugly work-around
    if (iBytesRead <= 0)
       return 0;
    iBytesRead = (int32_t)f->read(pBuf, iBytesRead);
    pFile->iPos = f->position();
    return iBytesRead;
} /* GIFReadFile() */

/* Seek a position in a given GIF file */
int32_t GIFSeekFile(GIFFILE *pFile, int32_t iPosition)
  int i = micros();
  File *f = static_cast<File *>(pFile->fHandle);
  pFile->iPos = (int32_t)f->position();
  i = micros() - i;
  // Serial.printf("Seek time = %d us\n", i);
  return pFile->iPos;
} /* GIFSeekFile() */

// Draw a line of image directly on the display
void GIFDraw(GIFDRAW *pDraw)
    int x, y, iWidth;

    iWidth = pDraw->iWidth;
    if (iWidth + pDraw->iX > DISPLAY_WIDTH)
       iWidth = DISPLAY_WIDTH - pDraw->iX;

    y = pDraw->iY + pDraw->y; // current line
    if (y >= DISPLAY_HEIGHT || pDraw->iX >= DISPLAY_WIDTH || iWidth < 1)

    String out = "";

    for (x=0; x<iWidth; x++)
      if( pDraw->pPixels[x] == 0)
        display.drawPixel(x, y, GxEPD_BLACK);
        display.drawPixel(x, y, GxEPD_WHITE);
} /* GIFDraw() */

This strongly suggests that memory has been overwritten, member variables of the display class.

Your example might work, if gif.reset(); works as it should, completely resets the gif state machine. But I don't expect this to be true.

Note that each iteration of the page loop needs to draw exactly the same. Needs to call drawPixel() with the same data. drawPixel() will just ignore calls that would draw to outside of the current page.

You could consider to let GIFDraw write a bitmap line directly to the controller memory using writeBitmap(), like the example GxEPD2_SD_Example.ino does.


Added: experience showed that on ESP32 the static RAM space check of the compiler is not reliable. Even if compiled ok, the program may hang because of insufficient RAM.
You could check with reduced page_height, e.g. divisor 8.

I would like to present additions in blue, or use other colors. But this seems missing in this forum SW. works! (manually).

This [color = red]reply[/color] is partly in [color = blue]colour[/color]

This reply is partly in colour

Thanks to @UKHeliBob

Thanks for the quick response!

I will look into the suggestions you had made and let you know if it works.

I'm trying to get the GxEPD2 library examples to work with a waveshare 2.66" display and so far I'm not having any luck. Is this display supported? The constructor I'm trying to use is

GxEPD2_BW<GxEPD2_260, GxEPD2_260::HEIGHT> display(GxEPD2_260(/*CS=D8*/ SS, /*DC=D3*/ 4, /*RST=D4*/ 2, /*BUSY=D2*/ 5)); // GDEW026T0

Here is the display: https://www.waveshare.com/2.66inch-e-paper-module.htm

No updates occur on the display when I use examples from this library. The same pinout configuration works with the waveshare example code. I'm using a wemos d1 esp8266.

No, it is not supported. README.md has a list of supported panels.

Added: might be this panel: GDEM0266T90

It has the same controller as the GDEM029T94. You could try choosing this panel.


Progress. Using that display resulted in this:

Update: I was able to copy the .h/.cpp files for 290T94 and update them for this display. That got rid of the black area at the top. I'll see about opening a PR request with the changes when I work on this again this evening. Thanks for your help! I'm planning on using these displays with the esp8266 to fetch sensor data being collected on a raspberry pi and output via node red. This is to display depth and speed information from a commercial transducer on our sailboat that will be connected to the rpi.

Before putting too much work into your PR, read README.md once more, and also CONTRIBUTING.md.

Fair enough. I'll create a fork and add the display classes there. I'm assuming your response is in reference to only supporting displays you have. Once I've added the updated files to my fork I'll reference it here in case it's of use to you or anyone else. I selected these 2.66" displays as they fit my existing instrument housings. Once I have a proof of concept working I'll probably build with one of the more common, larger displays. Thank you for the direction to the 290T94 display and controller difference.

Hi Jean-Marc,
I now have your code working very nicely on the Raspberry Pi. As you would expect, there's not a lot of changes; some different pins for the SPI interface, and a difference in the debugging output. All of the changes are enabled/disabled via a compiler variable RPI, similar to the ESP8266 etc variants.

I'd like to upload it to your GitHub repo as a new RPI branch, but once you're happy with it, it could be merged back into the master. How can we do this?


@frankvnz, Hi Frank,

We don't. See CONTRIBUTING.md.

Thank you for the information. I am interested to learn about the changes needed for this.
You could provide a link to your repo.

There are actually 92 forks of GxEPD2. I have no idea about which ones could be interesting.
I have no time to look at all the differences.
There are actually 20 closed pull request. Two of them are merged; one was welcome, the other one was regretted afterwards, but it is famous because it had fixed many typos.



This post might be interesting for you.


1 Like

Hi Jean-Marc,
Thanks for that link... yes, that's very interesting.

In the meantime, I've created yet another fork of your repository GitHub - frankvdh/GxEPD2-RPi: Arduino Display Library for SPI E-Paper Displays which, as the name suggests, contains my modified version of your code that runs on a Raspberry Pi. If you want to move my files into your repository, I'm absolutely fine with that. If not, I'll continue to maintain my fork to keep it up-to-date with yours.


@frankvnz , @ZinggJM
I unsuccessfully tried to run a 1.54 inch 3c e-paper Module from Waveshare with examples of both the libaries GxEPD and GxEPD2. I want to use the new Arduino Nano RP2040 Connect with mBed Core AND this e-paper module. Can you help me? Maybe Frank knows how because the new Arduino board uses the PI-Chip.
I'm new to Arduino and C++ but not new to programming ยตC. I'll get it. As I want to play around with the Arduino board and the display I eventually could adapt the libary to the mBed Core and RP2040 Connect Pinout with your help at the beginning according to definitions which I have to know about or where in the libary essential changes have to be made. For example: Without any chances in the example code the IDE output shows (I don't understand anything of it by now) "unknown exit status 1" and compiler doesn't find declaration of "display". I wonder why SPI interface is not initialized in void setup.
It would also be very kind if you update the libary for mBed Core (maybe already in progress!?).

Thanks for every advise.