Skip to Content

4-color E-paper display with ESP32

4-color E-paper display with ESP32

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 Lolin32

ESP32 lite

USB data cable

USB Data Cable

Dupont wire set

Dupont Wire Set

Half_breadboard56a

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.

Front and Back of 3" 4-color e-Paper display module
Front and Back of 3″ 4-color e-Paper display module

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.

Back of display module with SPI-interface and controller
Back of display module with SPI-interface and controller

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.

Connecting 4-color e-Paper to ESP32 lite via SPI
Connecting 4-color e-Paper to ESP32 lite via SPI

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 displayESP32 lite
CS/SS5
SCL/SCK 18
SDA/DIN/MOSI23
BUSY4
RES/RST16
DC17
VCC3.3V
GNDG

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:

Demo image for 3" 4-color e-Paper
Demo image for 3″ 4-color e-Paper

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 to unsigned char in imagedata.cpp and imagedata.h
  • Change #include <avr/pgmspace> to #include <pgmspace> in imagedata.h
  • Change pin definitions in epdif.h to use the ESP32 hardware SPI pins
  • Add SPI.endTransaction(); to the IfInit() function in epdif.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:


Adafruit_GFX in Library Manager
Adafruit_GFX in Library Manager

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:

ColorBits
black 00
white01
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:

Four pixels with 2-bit color codes
Four pixels with 2-bit color codes

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:

Output example for compress() function
Output example for compress() function

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:

Download EPD4c Library library
Download EPD4c Library

Then go “Sketch" -> "Include Library" -> "Add .Zip Library..” and select the “epd4c_arduino_lib-main.zip” file you just downloaded before:

Adding TOF10120 library to sketch
Adding EPD4c library to sketch

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

Output on 3" 4-color E-paper Display
Output on 3″ 4-color E-paper Display

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 graphics
  • epd.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 ; )