In this tutorial you will learn how to use the Waveshare 3-inch 4-color E-paper display with an ESP32.
The 3-inch 4-color E-paper display module by Waveshare is a nice piece of hardware but the demo software only has examples for Arduino, STM32 and Raspberry. So, if you want to use the display with an ESP32, you are out of luck.
Furthermore the Arduino example only shows you how to display a pre-made, static image, but not how to dynamically create and display content, for instance, a running clock or weather station.
It would be nice, if we could use the GxEPD2 library, but as of Dec 2024 it has no driver for the 3-inch 4-color E-paper. I therefore, implemented a simple library (epd4c), which does essentially the same.
In this tutorial, I show you how to get the Arduino example code working with an ESP32, how to dynamically create content using a canvas and how to use the epd4c library.
Required Parts
I am using the ES32 lite as a microprocessor, because it is cheap and has a battery interface. Since e-Paper displays consume very little power they are a great choice for battery powered projects, and the ESP32 lite fits in here as well. However, any other ESP32 will also work.
You obviously will also need the 3-inch 4-color e-Paper display module listed below. I tested the code with that one but it should work also with the 2.13-inch 4-color E-Paper or the 2.66-inch 4-color E-Paper. However, it does not support 3-color or black-white displays.
3-inch 4-color e-Paper Display
ESP32 lite
USB Data Cable
Dupont Wire Set
Breadboard
Makerguides.com is a participant in the Amazon Services LLC Associates Program, an affiliate advertising program designed to provide a means for sites to earn advertising fees by advertising and linking to products on Amazon.com. As an Amazon Associate we earn from qualifying purchases.
3″ 4-color E-paper Display
The E-paper display used in this project is a 3-inch display module, with 400×168 pixels resolution, 4 colors (white, black, red, yellow), a refresh time of 12 seconds (only full refresh is supported), and an embedded controller with an SPI interface.
Note that the module has a little jumper pad/switch at the back to switch from 4-wire SPI to 3-wire SPI. We are going to use the default 4-wire SPI here. So you don’t have to change anything there.
The display module runs on 3.3V or 5V, has a very low sleep current of 0.01µA and consumes only about 60mW while refreshing. For more information on E-paper displays, in general, have a look at the Interfacing Arduino To An E-ink Display tutorial.
Below I linked the datasheet for the display and this link is for the Waveshare manual:
Connecting 4-color E-paper display with ESP32
In this section we are going to connect the E-paper display to an ESP32 lite. The following picture shows the complete wiring between ESP32 and display for power and the SPI interface.
You can power the display with 3.3V or 5V but the ESP32-lite has only a 3.3V output. Note that you must use the hardware SPI pins of your microcontroller. In the case of the ESP32-lite these are pins 18 and 23. Below a table with all the connections for convenience.
e-Paper display | ESP32 lite |
---|---|
CS/SS | 5 |
SCL/SCK | 18 |
SDA/DIN/MOSI | 23 |
BUSY | 4 |
RES/RST | 16 |
DC | 17 |
VCC | 3.3V |
GND | G |
Make demo code working with ESP32
If you download the demo software for the Waveshare E-paper display, you will find a folder within that zip file that is named Arduino/epd3in0g
. In there is a sketch named epd3in0g.ino
that essentially produces the following image on the display:
Here is the shortened bit of that example code the produces the image. You can see it creates the display object epd
, initializes it, displays the image data and then sends the display to sleep:
#include "epd3in0g.h" #include "imagedata.h" Epd epd; void setup() { epd.Init(); epd.Display(IMAGE_DATA); epd.Sleep(); } void loop() { }
This code will not run on an ESP32. However you can make it work by following the steps listed below (see this blog post for more context):
- Change
const unsigned char
tounsigned char
inimagedata.cpp
andimagedata.h
- Change
#include <avr/pgmspace>
to#include <pgmspace>
inimagedata.h
- Change pin definitions in
epdif.h
to use the ESP32 hardware SPI pins - Add
SPI.endTransaction();
to theIfInit()
function inepdif.cpp
Here are the pin definitions you need to use in epdif.h
:
#define RST_PIN 16 #define DC_PIN 17 #define CS_PIN 5 #define BUSY_PIN 4
and here is the code in epdif.cpp
that you have to change from
int EpdIf::IfInit(void) { ... SPI.begin(); SPI.beginTransaction(SPISettings(2000000, MSBFIRST, SPI_MODE0)); return 0; }
to this code by adding one line (endTransaction
):
int EpdIf::IfInit(void) { ... SPI.begin(); SPI.endTransaction(); // <== ADD THIS LINE SPI.beginTransaction(SPISettings(2000000, MSBFIRST, SPI_MODE0)); return 0; }
With those changes the example code should work. However, it is still limited to the display of static images. In the next section, I show you a little addition that allows you to use the Adafruit_GFX graphics library, to dynamically display content such as text and graphics.
Drawing on 4-color E-Paper display using Adafruit_GFX canvas
The Adafruit_GFX library is graphics library that provides a common set of graphics primitives (text, points, lines, circles, etc.). Just install it via the Library Manager. After the installation it should appear in the Library Manager as follows:
The Adafruit_GFX Library has the concept of a “canvas” that allows you to draw to a display buffer (=canvas) in the background (without showing it). Below is a code excerpt that creates an 8-bit depth canvas with the dimensions of the E-paper display.
#include "Adafruit_GFX.h" GFXcanvas8 canvas(EPD_WIDTH, EPD_HEIGHT); void setup() { canvas.setRotation(1); canvas.fillScreen(WHITE); canvas.fillCircle(200, 84, 60, RED); }
It then sets the orientation of the buffer/display, fills the display buffer with white, and draws a red circle. Since the display buffer is essentially an image, it would be nice if we could use the following code to display the drawing in the buffer on the E-paper:
epd.Display(canvas.getBuffer());
However, the 4-color E-paper is actually using a compressed image, where four pixels are squeezed into one byte to preserve memory. Since there are only four colors, each color can be represented by 2 bits:
Color | Bits |
---|---|
black | 00 |
white | 01 |
yellow | 10 |
red | 11 |
Which means, if we have a sequence of four pixels with the colors black, white, yellow and red in the original image, we can compress it into one byte (00011011 = 0x1B) as follows:
You have to do this for all 4-pixel chunks in the original image to create a compressed image that can be displayed on the E-paper via epd.Display(image)
. The following compress()
function takes the display buffer of the canvas and compresses it as described:
uint8_t* compress(GFXcanvas8& canvas) { uint8_t* buf = canvas.getBuffer(); int n = canvas.width() * canvas.height(); int ci = 0; for (int i = 0; i < n; i += 4) { uint8_t com = buf[i]; com = (com << 2) | buf[i + 1]; com = (com << 2) | buf[i + 2]; com = (com << 2) | buf[i + 3]; buf[ci++] = com; } return buf; }
Note that it reuses the display buffer. So no additional memory is needed for the compressed image but you cannot draw to the canvas after it is compressed. You would have to clear it first.
With the compress()
function you can dynamically create content and display it on the E-Paper. The code framework looks like this:
Epd epd; GFXcanvas8 canvas(EPD_WIDTH, EPD_HEIGHT); void setup() { canvas.setRotation(1); canvas.fillScreen(WHITE); ... // more graphics code epd.Init(); epd.Display(compress(canvas)); // display compressed image epd.Sleep(); }
Here is a complete code example with everything included:
#include "Adafruit_GFX.h" #include "epd3in0g.h" Epd epd; GFXcanvas8 canvas(EPD_WIDTH, EPD_HEIGHT); uint8_t* compress(GFXcanvas8& canvas) { uint8_t* buf = canvas.getBuffer(); int n = canvas.width() * canvas.height(); int ci = 0; for (int i = 0; i < n; i += 4) { uint8_t com = buf[i]; com = (com << 2) | buf[i + 1]; com = (com << 2) | buf[i + 2]; com = (com << 2) | buf[i + 3]; buf[ci++] = com; } return buf; } void setup() { canvas.setRotation(1); canvas.fillScreen(red); canvas.fillCircle(200, 84, 50, yellow); canvas.setCursor(190, 80); canvas.setTextColor((black)); canvas.print("Test"); epd.Init(); epd.Display(compress(canvas)); epd.Sleep(); } void loop() { }
If you upload and run this code, you should see a yellow circle on a red background with the text “Test” in it:
While the above code works, it is a bit cumbersome to use. Firstly, you have to integrate the Waveshare demo code files (epd3in0g.h
, epd3in0g.cpp
, epdif.h
, epdif.cpp
) in every new project that uses the E-paper display. Secondly, you also have to re-implement the compress()
function each time.
I therefore created a small library named epd4c
, which avoids all of that. You just have to install it and then can use it independently of the demo code. In the next section, I show you how it works.
Drawing on 4-color E-Paper display using epd4c Library
To install the epd4c Library go to the epd4c_arduino_lib github repo and click on the green “Code
” button. Then click on “Download Zip
” as show below:
Then go “Sketch" -> "Include Library" -> "Add .Zip Library..
” and select the “epd4c_arduino_lib-main.zip
” file you just downloaded before:
With the epd4c library (and the Adafruit_GFX library), writing text and drawing on the 4-color E-paper becomes much easier.
Example code
Here is some example code. It draws a a series of colored circles along with a centered text on the screen. As you can see there is no need for a separate canvas anymore and you don’t need to use any of the Waveshare demo code.
#include "epd4c.h" #define RST_PIN 16 #define DC_PIN 17 #define CS_PIN 5 #define BUSY_PIN 4 #define EPD_WIDTH 168 #define EPD_HEIGHT 400 Epd4c epd(EPD_WIDTH, EPD_HEIGHT, RST_PIN, DC_PIN, CS_PIN, BUSY_PIN); void setup() { epd.init(); epd.setRotation(1); epd.fillScreen(WHITE); epd.fillCircle(200, 84, 60, RED); epd.fillCircle(200, 84, 50, YELLOW); epd.fillCircle(200, 84, 40, BLACK); epd.fillCircle(200, 84, 30, WHITE); epd.setCursor(190, 80); epd.setTextColor((BLACK)); epd.print("Test"); epd.display(); epd.sleep(); } void loop() {}
Let’s break down the code into its components for a better understanding.
Library Inclusion
We start by including the epd4c library for controlling the E-paper display, which is an extension of an Adafruit_GFX canvas, and therefore supports all the graphics primitives of the Adafruit_GFX library.
#include "epd4c.h"
Pin Definitions
Next, we define the pins used to connect the E-paper display to the ESP32. Note that the SPI pins can not be defined. You have to use and connect to the default hardware SPI pins for your microcontroller.
#define RST_PIN 16 #define DC_PIN 17 #define CS_PIN 5 #define BUSY_PIN 4
Display Dimensions
We also define the width and height of the E-paper display. As mentioned, the code should work for other 4-color E-paper displays, with different dimensions but I have not tested this.
#define EPD_WIDTH 168 #define EPD_HEIGHT 400
Display Object Creation
Here, we create an instance of the Epd4c
class, passing the display dimensions and pin definitions as parameters. This object will be used to control the display throughout the program.
Epd4c epd(EPD_WIDTH, EPD_HEIGHT, RST_PIN, DC_PIN, CS_PIN, BUSY_PIN);
Setup Function
In the setup()
function, we initialize the E-paper display and configure its settings. This is where we set the display rotation, fill the screen with white color, and draw several overlapping circles of different colors and sizes.
void setup() { epd.init(); epd.setRotation(1); epd.fillScreen(WHITE); epd.fillCircle(200, 84, 60, RED); epd.fillCircle(200, 84, 50, YELLOW); epd.fillCircle(200, 84, 40, BLACK); epd.fillCircle(200, 84, 30, WHITE); epd.setCursor(190, 80); epd.setTextColor((BLACK)); epd.print("Test"); epd.display(); epd.sleep(); }
The output on the E-paper display will look as follows
where
epd.init();
initializes the display.epd.setRotation(1);
sets the orientation of the display.epd.fillScreen(WHITE);
fills the entire screen with white color.- The
fillCircle()
functions draw circles of varying radii and colors at the specified coordinates (200, 84). epd.setCursor(190, 80);
sets the position for the text to be printed.epd.setTextColor((BLACK));
sets the text color to black.epd.print("Test");
prints the text “Test” on the display.epd.display();
updates the display with the drawn graphicsepd.sleep();
puts the display into sleep mode to save power.
Loop Function
The loop()
function is empty in this example, meaning that once the setup is complete, the program will not perform any further actions. The display will remain in sleep mode and continue to show the image, even when the ESP32 is powered of.
void loop() {}
If you want to update the content continuously, e.g. to display a running clock, you would place the graphics code in the loop function. See the Digital Clock on e-Paper Display or the Weather Station on e-Paper Display tutorials for examples.
And that’s it!
Conclusions
In this tutorial you learned how to control the 3-inch 4-color E-paper display by Waveshare with an ESP32.
I showed you how to change the Arduino demo code to make it work with an ESP32. We also used the Adafruit_GFX canvas to dynamically create content for the E-paper display. And finally, I introduced you to the epd4c library.
The epd4c library does essentially the same as the GxEPD2 library but unfortunately the latter currently does not support the 3-inch 4-color E-paper display by Waveshare. I would have preferred that. I could have extended the GxEPD2 library but was too lazy and simply cleaned up and extended the Waveshare demo code instead to create the epd4c library.
If you have any questions, feel free to leave them 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.