Skip to Content

Paged Redraw of E-Paper Display

Paged Redraw of E-Paper Display

In this tutorial you will learn how to perform a paged redraw of an E-Paper display with an Arduino and why it is often needed when using microcontrollers with small memory.

Compared to personal computers or mobile phones, microcontrollers such as the Arduino have a much smaller memory. This limited memory can make it challenging to handler large images or complex graphics. E-Paper displays often require a significant amount of memory to store the entire screen’s content. When you want to update the display, you may not have enough RAM to hold the full image.

Paged redraw helps solve this problem. Instead of loading the entire display content into memory at once, you divide the screen into smaller sections or “pages.” You can then load and update one page at a time. This method reduces memory usage and allows for smoother updates.

In the following sections you will learn how to perform a paged redraw for an E-Paper display using an Arduino Uno.

Required Parts

You will need an E-Paper display. For this tutorial I picked a monochrome, 2.9 inch display with a resolution of 296×128 pixels. However, you could also use a E-Paper with a different size.

As for the microcontroller, any Arduino or ESP32/ESP8266 will work, but to really appreciate the need for paged redraws, I suggest you use an Arduino Uno. Its limited memory makes paged redraws essential, even for small E-Paper displays.

2.9″ E-Paper Display

Arduino

Arduino Uno

USB Data Sync cable Arduino

USB Cable for Arduino UNO

Dupont wire set

Dupont Wire Set

Half_breadboard56a

Breadboard

Makerguides is a participant in affiliate advertising programs designed to provide a means for sites to earn advertising fees by linking to Amazon, AliExpress, Elecrow, and other sites. As an Affiliate we may earn from qualifying purchases.

What is a Paged Redraw

Paged redraw is a technique used in graphics programming, especially in low-memory environments like microcontrollers. It allows you to update a display by drawing content in separate “pages” or sections.

Let’s look at a concrete example. The 2.9″ E-Paper display we are going to use in this tutorial has a resolution of 128×296 pixels. Assuming each pixel is 1-bit for a monochrome display (black, white), this means we need (128×296)/8 = 4736 bytes to hold the entire array of pixels (image, frame) in RAM.

Typically, the image to be displayed is first written to a display buffer within the RAM of the microcontroller and then sent to the E-paper controller to update the displayed content. However, an Arduino Uno has only 2KB of SRAM = 2048 bytes, which means we would not be able prepare a complete image in RAM. In other words, on an Arduino Uno we cannot create a display buffer larger enough to hold a 128×296 image.

Paged Redraw

This is where the Paged Redraw enters the game. Instead of preparing and sending the entire image at once, we are dividing the image into smaller sections called “pages” that fit into the RAM of the microcontroller. We then process one page by one, send them to the E-Paper controller, which assembles the image and refreshes the display when the image is complete. See the following picture for illustration:

Paged Redraw of an Image
Paged Redraw of an Image

If you use the GxEPD2 library, you can see this process reflected in the code. For a Paged Redraw, you first call firstPage() to initiate the redraw. You then call repeatedly nextPage() in a loop until all pages are transferred to the display.

  epd.firstPage();
  do {
    ...  // Graphics code
  } while (epd.nextPage());

Within the loop, you define the graphics operations you want to perform to create the content/image to be displayed. The paged redraw is obviously inefficient, since you have to create the same content multiple times, but E-Paper are slow to update, even for a partial update (< 0.3 seconds), so that this doesn’t really matter.

Buffer hight

While a page could be an arbitrary rectangular section, the GxEPD2 library uses pages in the form of stripes. The length of the stripe is the same as the width of the display and the height depend on the available memory. In case of a monochrome display, you can calculate the height h of a stripe or the display buffer as follows,

h = buff / (width / 8)

where buff is the size in bytes of the display buffer you can afford and width is the width of the display.

For E-papers with 4 gray levels, or 3 or 4 colors we need 2-bit per pixel and the formula becomes:

h = (buff / 2) / (width / 8)

An Arduino has 2048 bytes of RAM, so the maximum height h for the display in portrait mode would be

h = 2048 / (128 / 8) = 128 pixels

However, the display buffer and the code share the same memory and we can’t use the full 2048 bytes. If you use half of the memory for the display buffer, we get

h = 1024 / (128 / 8) = 64 pixels,

which means with a display hight of 296 pixels, we would have 296 / 64 = 5 pages.

Depending on the size of the code and the available RAM of your microcontroller, you will have to adjust the size of the buffer. If you get the following error message when compiling the code, you know your buffer size is too large:

Not enough memory; see https://support.arduino.cc/hc/en-us/articles/360013825179 for tips on reducing your footprint. 
data section exceeds available space in board.
Compilation error: data section exceeds available space in board

In the next sections we connect the E-Paper display to the Arduino and write some code to demonstrate how a paged redraw is implemented.

Installing GxEPD2 library for e-Paper

Before we can draw or write on the E-Paper we need to install two libraries. The Adafruit_GFX library is a core graphics library that provides a common set of graphics primitives (text, points, lines, circles, etc.). And the GxEPD2 library provides the graphics driver software to control an E-Paper via SPI.

Open the Library Manger, search for “Adafruit_GFX” and “GxEPD2” and press “INSTALL”. After the installation the libraries should appear in the Library Manager as follows.

Adafruit_GFX and GxEPD2 libraries in Library Manager
Adafruit_GFX and GxEPD2 libraries in Library Manager

Connecting E-Paper Display to Arduino

The wiring diagram below shows you how to connect the SPI interface of the E-Paper display to an Arduino. Most pins can be configured freely but for the DIN and CLK lines we need to use SPI specific pins. In case of an Arduino Uno, DIN is at pin 11 and CLK is at pin 13.

Connecting E-Paper to Arduino Uno via SPI
Connecting E-Paper to Arduino Uno via SPI

The following table lists all the other connection you have to make.

e-Paper displayArduino UNO
CS/SS4
SCL/SCK/CLK13
SDA/DIN/MOSI11
BUSY7
RES/RST6
DC5
VCC3.3V
GNDG

Paged Redraw for E-Paper with Arduino Uno

We are going to draw the following test picture on the E-Paper display:

Test Picture
Test Picture

Below you find the corresponding code. Have a quick look first, and then we will discuss the details.

#include "GxEPD2_BW.h"

#define EPD_CS 4
#define EPD_DC 5
#define EPD_RST 6
#define EPD_BUSY 7
// CLK = 13
// DIN = 11

#define BUFF 1024
#define HEIGHT(EPD) ((BUFF / 1) / (EPD::WIDTH / 8))

GxEPD2_BW<GxEPD2_290_BS, HEIGHT(GxEPD2_290_BS)>
   epd(GxEPD2_290_BS(EPD_CS, EPD_DC, EPD_RST, EPD_BUSY));

void setup() {
  epd.init(115200);
  epd.setRotation(1);
  epd.setTextSize(2);
  epd.setTextColor(GxEPD_BLACK);
  epd.setFullWindow();

  epd.firstPage();
  do {
    epd.fillScreen(GxEPD_WHITE);
    epd.fillRect(30, 30, 60, 60, GxEPD_BLACK);
    epd.fillTriangle (100, 120, 180, 120, 140, 60, GxEPD_BLACK);
    epd.setCursor(200, 60); epd.print("TEST");
  } while (epd.nextPage());

  epd.hibernate();
}

void loop() {}

We start by including the required graphics library and defining some constants for the SPI pins. Remember that DIN and CLK pins are fixed and specific for your microcontroller, while the others can be changed.

#include "GxEPD2_BW.h"

#define EPD_CS 4
#define EPD_DC 5
#define EPD_RST 6
#define EPD_BUSY 7
// CLK = 13
// DIN = 11

Next we define the size of the display buffer BUFF and the page height HIGHT. The hight is computed via a macro that takes the display object EPD as a parameter and uses its width property (EPD::WIDTH) to determine the page hight.

#define BUFF 1024
#define HEIGHT(EPD) ((BUFF / 1) / (EPD::WIDTH / 8))

The computation follows the formula we discussed before. For a three color display you would use the following formula instead:

#define HEIGHT(EPD) ((BUFF / 2) / (EPD::WIDTH / 8))

We can now use the HIGHT macro to create the display object epd. This looks rather complex but it simply takes the pin constants, the hight and a display type as template parameters to create the display.

GxEPD2_BW<GxEPD2_290_BS, HEIGHT(GxEPD2_290_BS)>
   epd(GxEPD2_290_BS(EPD_CS, EPD_DC, EPD_RST, EPD_BUSY));

If your display doesn’t show anything or corrupted text/images then either the display is not correctly wired or the wrong display type is chosen.

The Readme for GxEPD2 library lists all the supported displays and you can find the specifics in the header files, e.g. GxEPD2.h. Find the display driver specific for your display. And maybe have a look at the Interfacing Arduino with E-ink Display tutorial, where we connect a three color E-Paper to an Arduino and an ESP32.

Setup function

In the setup function, we first set some graphics parameters such as the orientation (portrait), the text size, the text color and the refresh mode (full window).

  epd.init(115200);
  epd.setRotation(1);
  epd.setTextSize(2);
  epd.setTextColor(GxEPD_BLACK);
  epd.setFullWindow();

There is also a “partial refresh”, but we are not going to use it here. If you want to learn more about this have a look at the Partial Refresh of e-Paper Display tutorial.

Finally, we get to the part, where the paged redraw is performed. We start by calling firstPage() to initiate the redraw. The we repeatedly draw the rectangle, triangle and text in a do-while loop until nextPage() returns false.

  epd.firstPage();
  do {
    epd.fillScreen(GxEPD_WHITE);
    epd.fillRect(30, 30, 60, 60, GxEPD_BLACK);
    epd.fillTriangle (100, 120, 180, 120, 140, 60, GxEPD_BLACK);
    epd.setCursor(200, 60); epd.print("TEST");
  } while (epd.nextPage());

This loops essentially fills the display buffer with the content of a page, sends that page to the E-Paper display, where the controller stitches the pages together until the image is complete.

You will not see the display updating page by page. Instead the display controller waits until all pages have been received and then performs a full update of the display. See the short video clip below:

Full refresh with Paged Redraw
Full refresh with Paged Redraw

By the way, if you want to know the actual page hight the display is using, you can call the following function in the setup function, to find out:

  Serial.println(epd.pageHeight());

Loop function

The loop function in this example is empty, since is no periodic content update.

void loop() {}

But let’s say you want to implement a Digital Clock on e-Paper Display, the loop function would contain the code for the paged redraw instead of the setup function.

Drawing function

If you have more complex applications, it is better to use a drawing function instead of the do-while loop for the paged redraw. The GxEPD2 library allows you to define a drawing function and then call epd.drawPaged(...) for paged drawing. Here is the code framework:

void draw(const void* pv) {
  ... // Graphics code
}

void setup() {
  ...
  epd.drawPaged(draw, 0);
  epd.hibernate();
}

void loop() {}

The drawing function draw() takes a pointer pv, which allows you to pass on parameters, when calling epd.drawPaged(draw, 0). But you can simply pass 0 as a parameter pointer, if you don’t need that.

The use of drawing functions for paged redraws makes the code more readable and extendable. Here is our test code again but now using a drawing function instead of the do-while loop:

#include "GxEPD2_BW.h"

#define EPD_CS 4
#define EPD_DC 5
#define EPD_RST 6
#define EPD_BUSY 7
// SCL(SCK)=13,
// SDA(MOSI)=11

#define BUFF 1024
#define HEIGHT(EPD) ((BUFF / 1) / (EPD::WIDTH / 8))

GxEPD2_BW<GxEPD2_290_BS, HEIGHT(GxEPD2_290_BS)>
  epd(GxEPD2_290_BS(EPD_CS, EPD_DC, EPD_RST, EPD_BUSY));


void draw(const void* pv) {
  epd.fillScreen(GxEPD_WHITE);
  epd.fillRect(30, 30, 60, 60, GxEPD_BLACK);
  epd.fillTriangle(100, 120, 180, 120, 140, 60, GxEPD_BLACK);
  epd.setCursor(200, 60); epd.print("TEST");
}

void setup() {
  epd.init(115200);
  epd.setRotation(1);
  epd.setTextSize(2);
  epd.setTextColor(GxEPD_BLACK);
  epd.setFullWindow();
  epd.drawPaged(draw, 0);
  epd.hibernate();
}

void loop() {}

And that’s it! Now you know what a Paged Redraw is, and how to implement it.

Conclusions

In this tutorial you learned how to perform a paged redraw of a monochrome E-Paper display with an Arduino Uno. If you have a tri-color E-Paper instead of a monochrome E-paper or an ESP32, have a look at the Interfacing Arduino with E-ink Display.

If you want to learn more about partial vs full refresh, I suggest our Partial Refresh of e-Paper Display tutorial.

Furthermore, a combination of page redraw, and partial and full refresh of E-Paper displays of different size is used in the Temperature Plotter on e-Paper Display, the Digital Clock on e-Paper Display
and the Analog Clock on e-Paper Display tutorials. If you need application examples, you will find them there.

If you have any other questions, feel free to ask in the comment section. Happy tinkering ; )