An e-paper display needs no power to keep its image — ideal for a clock that just stays "on screen", even without power.
The advantage of an e-paper display is that no power is needed to display information on the screen. Only a little bit to change the data on the screen. What you put on it stays there forever, even without power. The disadvantage is a slower update than the tft-spi displays (which is why the seconds are omitted), and you have to pay attention to how you "refresh" the screen so as not to get a violent flicker. The library used does not have a function "drawCentreString", so we have to create our own function for a string we want to display centered.
Connections:
ESP32 ---> 2.13 inch e-paper
============================
G4 --------> BUSY
G16 --------> RST
G17 --------> DC
G5 --------> CS
G18 --------> CLK
G23 --------> DIN
GND --------> GND
3V3 --------> VCC
Sketch:
#include <GxEPD.h>
#include <GxDEPG0213BN/GxDEPG0213BN.h> // 2.13" black/white 128x250 px
#include <WiFiManager.h> // https://github.com/tzapu/WiFiManager/
#include <esp_sntp.h> // ESP32 core
#include <Fonts/FreeSansBold24pt7b.h>
#include <Fonts/FreeSansBold18pt7b.h>
#include <Fonts/FreeSansBold9pt7b.h>
#include <GxIO/GxIO_SPI/GxIO_SPI.h>
#include <GxIO/GxIO.h>
char days[7][10] = { "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday" };
int previousMinute;
GxIO_Class io(SPI, /*CS=5*/ SS, /*DC=*/17, /*RST=*/16); // 17 & 16 = arbitrary choice
GxEPD_Class display(io, /*RST=*/16, /*BUSY=*/4); // 16 & 4 = arbitrary choice
struct tm tInfo; // https://cplusplus.com/reference/ctime/tm/
WiFiManager myWiFi;
void setup() {
display.init();
display.setRotation(1); // landscape
display.setTextColor(GxEPD_BLACK, GxEPD_WHITE); // GxEPD_BLACK = 0x0000, GxEPD_WHITE = 0xFFFF
myWiFi.setAPCallback(noConnection); // on screen instructions for wifi connection
myWiFi.autoConnect("E-PAPER-ESP32"); // will also reconnect after connection was lost
sntp_set_sync_interval(4 * 60 * 60 * 1000UL); // sntp sync every 4 hr
configTzTime("CET-1CEST,M3.5.0,M10.5.0/3", "be.pool.ntp.org");
display.fillScreen(GxEPD_WHITE);
display.update(); // fully updating the display causes flicker for 2 seconds
}
void loop() {
char hourMin[6], theDate[11];
getLocalTime(&tInfo);
if (previousMinute != tInfo.tm_min) { // update display only when minute changes
strftime(hourMin, sizeof(hourMin), "%R", &tInfo); // https://cplusplus.com/reference/ctime/strftime
strftime(theDate, sizeof(theDate), "%d-%m-%G", &tInfo); // this display is too slow to show the seconds
display.fillScreen(GxEPD_WHITE);
showCentered(39, hourMin);
showCentered(84, days[tInfo.tm_wday]);
showCentered(126, theDate);
previousMinute = tInfo.tm_min;
display.updateWindow(66, 0, 116, 41, true); // partial update time (= no flicker)
display.updateWindow(0, 50, 250, 78, true); // partial update date
display.powerDown();
}
}
void showCentered(byte myHeight, const char* myText) {
display.setTextColor(GxEPD_WHITE);
display.setFont(strlen(myText) > 9 ? &FreeSansBold18pt7b : &FreeSansBold24pt7b); // date = smaller font
display.setCursor(0, myHeight);
display.print(myText);
display.setTextColor(GxEPD_BLACK);
display.setCursor((display.width() - display.getCursorX()) / 2, myHeight);
display.print(myText);
}
void noConnection(WiFiManager* myWiFi) {
display.fillScreen(GxEPD_WHITE);
display.setFont(&FreeSansBold9pt7b);
display.setTextSize(1);
showNoConnection(18, F("WiFi: no connection."));
showNoConnection(35, F("Connect to hotspot:"));
showNoConnection(52, F("E-PAPER-ESP32 and open"));
showNoConnection(69, F("a browser at address"));
showNoConnection(86, F("192.168.4.1"));
showNoConnection(103, F("to enter network name"));
showNoConnection(120, F("and password"));
display.update();
}
void showNoConnection(byte top, const __FlashStringHelper* tekst) {
display.setCursor(0, top); // allows for 7 lines on a 128 px height display
display.print(tekst); // println without setCursor = interline of text is too large
}
display.updateWindow() only refreshes the changed area of the screen instead of the whole display — so there's no visible flicker on every minute change. A full display.update(), like at startup, does cause a brief ~2 second flicker.
Curious about other displays? Check out the full overview, including TM1637, SSD1306, SH1106, ST7735, ST7789 and integrated displays like the Lilygo T-Display and the Waveshare ESP32-S3.