I’m testing partial rendering on an ESP32 with a Waveshare 1.54-inch V2, 200×200 black-and-white e-paper display.
The current approach is:
- Keep a complete 1-bit, 200×200 shadow framebuffer in RAM.
- Draw the UI with the Waveshare
epdpaint library and Font16.
- Initialize with
LDirInit().
- Establish the controller’s base image once with
DisplayPartBaseImage(frame).
- Update only the changed 24-pixel rows using
SetFrameMemoryPartial().
- Trigger the refresh with
DisplayPartFrame().
- Resend the static
MATTY row together with the counter row to keep the title stable.
The display is connected to an ESP32 using SPI: SCK GPIO 8, MOSI GPIO 7, MISO GPIO 13, and e-paper CS GPIO 9. A MicroSD card shares the SPI bus on CS GPIO 1.
The counter updates every two seconds and appears stable in testing.
Is this the correct rendering pattern for this Waveshare driver, or should the partial buffer/controller RAM be managed differently?
I’m especially interested in feedback about ghosting, refresh-counter limits, row alignment, and whether calling DisplayPartBaseImage() once followed by repeated SetFrameMemoryPartial() + DisplayPartFrame() is considered the proper workflow.
Code variation: [V7 - E-paper Partial Refresh Counter Test](V7 - E-paper Partial Refresh Counter Test)
Relevant code
#include <Arduino.h>
#include <SPI.h>
#include <string.h>
#include "epd1in54_V2.h"
#include "epdpaint.h"
#include "fonts.h"
#define SPI_SCK 8
#define SPI_MOSI 7
#define SPI_MISO 13
#define EPD_CS 9
#define SD_CS 1
#define COLORED 0
#define UNCOLORED 1
#define ROW_HEIGHT 24
Epd epd;
unsigned char frame[200 * 200 / 8];
Paint paint(frame, 200, 200);
void drawRow(uint8_t row, const char* text) {
int y = row * ROW_HEIGHT;
paint.DrawFilledRectangle(
0, y, 199, y + ROW_HEIGHT - 1, UNCOLORED
);
int x = (200 - (int)strlen(text) * Font16.Width) / 2;
paint.DrawStringAt(x, y + 4, text, &Font16, COLORED);
}
const uint8_t PUSH_ROWS[] = {0, 1};
void pushRows() {
for (uint8_t i = 0; i < sizeof(PUSH_ROWS); i++) {
uint8_t row = PUSH_ROWS[i];
epd.SetFrameMemoryPartial(
frame + (row * ROW_HEIGHT * (200 / 8)),
0,
row * ROW_HEIGHT,
200,
ROW_HEIGHT
);
}
epd.DisplayPartFrame();
}
void setup() {
Serial.begin(115200);
pinMode(SD_CS, OUTPUT);
digitalWrite(SD_CS, HIGH);
pinMode(EPD_CS, OUTPUT);
digitalWrite(EPD_CS, HIGH);
SPI.begin(SPI_SCK, SPI_MISO, SPI_MOSI, EPD_CS);
if (epd.LDirInit() != 0) {
Serial.println("e-Paper init failed");
while (true) delay(1000);
}
epd.Clear();
paint.Clear(UNCOLORED);
drawRow(0, "MATTY");
drawRow(1, "1");
// Establish the complete base image in controller RAM.
epd.DisplayPartBaseImage(frame);
}
void loop() {
static uint32_t count = 1;
char buf[24];
count++;
snprintf(buf, sizeof(buf), "%lu", (unsigned long)count);
drawRow(1, buf);
pushRows();
Serial.printf("count = %lu\n", (unsigned long)count);
delay(2000);
}