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 Uno
USB Cable for Arduino UNO
Dupont Wire Set
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:
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.
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.
The following table lists all the other connection you have to make.
e-Paper display | Arduino UNO |
---|---|
CS/SS | 4 |
SCL/SCK/CLK | 13 |
SDA/DIN/MOSI | 11 |
BUSY | 7 |
RES/RST | 6 |
DC | 5 |
VCC | 3.3V |
GND | G |
Paged Redraw for E-Paper with Arduino Uno
We are going to draw the following test picture on the E-Paper display:
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:
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 ; )
Stefan is a professional software developer and researcher. He has worked in robotics, bioinformatics, image/audio processing and education at Siemens, IBM and Google. He specializes in AI and machine learning and has a keen interest in DIY projects involving Arduino and 3D printing.