Skip to content

Image Display on E-Ink

Image Display on E-Ink

This section covers displaying bitmap images on e-paper displays, including converting graphics to the required format, embedding them in the sketch, and combining images with text for professional-looking dashboards. After completing this section, you will be able to:

  • Convert PNG/JPG images to XBM byte arrays for e-paper display
  • Embed bitmap data in an Arduino sketch
  • Display images on the e-paper screen
  • Overlay text on top of background images

Before starting this section, please ensure:

  • GxEPD2 library is installed and working (see 02-03)
  • Text rendering basics are understood (see 02-04)
  • Basic image editing skills

The e-paper display requires images in XBM (X BitMap) format — a monochrome bitmap format where each pixel is represented by a single bit (0 = white, 1 = black).

How XBM Works:

Original Image (250×122 pixels)
Conversion to XBM
Each pixel → 1 bit (0=white, 1=black)
250 × 122 = 30,500 bits = 3,813 bytes
C byte array:
const unsigned char image_name[] = {
0xFF, 0x00, 0xFF, ... // Row 1
0x00, 0xFF, 0x00, ... // Row 2
...
};

Key Points:

  • XBM is purely black and white (no grayscale)
  • Data is stored row by row, left to right
  • Each byte represents 8 horizontal pixels
  • Bytes are formatted as hexadecimal values

The image must match the display resolution exactly:

Display SizeResolutionMax Image Dimensions
1.54”200×200200×200 pixels
2.13”250×122250×122 pixels
2.9”296×128296×128 pixels
4.2”400×300400×300 pixels
7.5”800×480800×480 pixels
  1. Create or resize your image to match the display resolution
  2. Convert to black and white (no grayscale, no color)
  3. Save as PNG or BMP

Image Preparation Tips:

  • Use high-contrast designs (pure black on pure white)
  • Avoid thin lines (less than 2 pixels wide)
  • Keep text in images large enough to be readable
  • Solid areas work better than complex patterns

Use an online converter to generate the XBM data:

  1. Go to image.online-convert.com/convert-to-xbm
  2. Upload your image
  3. Ensure settings:
    • Format: XBM (X BitMap)
    • Monochrome: Yes
    • Size: Match display resolution
  4. Click Convert
  5. Download the .xbm file

Alternative: Use image2cpp (http://javl.github.io/image2cpp/):

  1. Upload image
  2. Set output format: Plain C array
  3. Brightness threshold: 128 (adjust as needed)
  4. Check Draw mode: Black on white
  5. Click Generate code
  6. Copy the generated byte array

Create a new file called background.h in your sketch folder:

background.h
// Factory dashboard background image for 2.13" e-paper display (250×122)
#ifndef BACKGROUND_H
#define BACKGROUND_H
const unsigned char background_image [] = {
// Paste the converted XBM data here
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
// ... (full data omitted for brevity — include all bytes for your display size)
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
};
#endif // BACKGROUND_H
#include <GxEPD2_BW.h>
#include "background.h" // Include the bitmap header
#define EPD_CS 5
#define EPD_DC 17
#define EPD_RST 16
#define EPD_BUSY 4
GxEPD2_BW<GxEPD2_213_B72, GxEPD2_213_B72::HEIGHT> display(
GxEPD2_213_B72(EPD_CS, EPD_DC, EPD_RST, EPD_BUSY)
);
void setup() {
Serial.begin(115200);
display.init(115200);
display.setRotation(1);
// Clear screen
display.fillScreen(GxEPD_WHITE);
// Draw bitmap at position (0,0) — full screen
display.drawInvertedBitmap(
0, // X position
0, // Y position
background_image, // Bitmap data
display.width(), // Width
display.height(), // Height
GxEPD_BLACK // Color
);
display.display();
Serial.println("Image displayed!");
}
void loop() {}

Note: drawInvertedBitmap() is used because XBM format treats 0 as black and 1 as white, while the e-paper library expects the opposite.

Alternative: If your XBM uses standard encoding, use drawBitmap() instead:

display.drawBitmap(0, 0, background_image, display.width(), display.height(), GxEPD_BLACK);

This is the most practical use case — display a background design with dynamic text on top:

#include <GxEPD2_BW.h>
#include "background.h"
GxEPD2_BW<GxEPD2_213_B72, GxEPD2_213_B72::HEIGHT> display(
GxEPD2_213_B72(EPD_CS, EPD_DC, EPD_RST, EPD_BUSY)
);
void setup() {
display.init(115200);
display.setRotation(1);
// Step 1: Draw background
display.fillScreen(GxEPD_WHITE);
display.drawInvertedBitmap(0, 0, background_image,
display.width(), display.height(),
GxEPD_BLACK);
// Step 2: Overlay text on top of the background
display.setFont(&FreeMonoBold18pt7b);
display.setCursor(90, 55);
display.setTextColor(GxEPD_BLACK);
display.print("25.5");
display.setFont(&FreeMono9pt7b);
display.setCursor(95, 80);
display.print("Celsius");
display.setCursor(85, 105);
display.print("Humidity: 60%");
display.display();
}
void loop() {}

For weather forecast dashboards, you can embed small icons:

weather_icons.h
const unsigned char sun_icon[] = {
0x00, 0x18, 0x00, // Binary icon data for sun symbol (16×16)
0x00, 0x3C, 0x00,
// ... (16×16 = 32 bytes)
};
const unsigned char cloud_icon[] = {
// Cloud icon data
};
const unsigned char rain_icon[] = {
// Rain icon data
};
void displayWeatherIcon(int x, int y, const char* condition) {
if (strcmp(condition, "sunny") == 0) {
display.drawInvertedBitmap(x, y, sun_icon, 16, 16, GxEPD_BLACK);
} else if (strcmp(condition, "cloudy") == 0) {
display.drawInvertedBitmap(x, y, cloud_icon, 16, 16, GxEPD_BLACK);
} else if (strcmp(condition, "rainy") == 0) {
display.drawInvertedBitmap(x, y, rain_icon, 16, 16, GxEPD_BLACK);
}
}

Bitmap images consume significant flash memory:

Display SizeResolutionXBM Size (bytes)
1.54”200×200~5,000 bytes
2.13”250×122~3,813 bytes
2.9”296×128~4,736 bytes
4.2”400×300~15,000 bytes
7.5”800×480~48,000 bytes

Note: ESP32 typically has 4MB flash, so even a full-screen 7.5” image only uses ~1.2% of available flash. However, PROGMEM storage may be needed for very large images.

For ESP32, XBM data is stored in flash automatically by the compiler. No special PROGMEM handling is needed (unlike AVR-based Arduino boards).

  • Image displays at correct position and size
  • No distortion or pixel shifting
  • Text overlay appears at correct position on top of image
  • Icons (if used) display clearly
  • Contrast is good (clear black/white separation)

Symptom:

  • Image is stretched, squished, or shifted

Solution:

  • Verify image dimensions match display resolution exactly
  • Check that width and height parameters in drawInvertedBitmap() are correct
  • Ensure rotation is set before drawing the image

Issue 2: Image Appears Inverted (Colors Reversed)

Section titled “Issue 2: Image Appears Inverted (Colors Reversed)”

Symptom:

  • White areas appear black and vice versa

Solution:

// Option 1: Use the other bitmap function
display.drawBitmap(0, 0, image_data, width, height, GxEPD_BLACK);
// Option 2: Invert the data using drawInvertedBitmap
display.drawInvertedBitmap(0, 0, image_data, width, height, GxEPD_BLACK);

Issue 3: Compilation Error — “Image too large”

Section titled “Issue 3: Compilation Error — “Image too large””

Symptom:

  • Compiler error about array size

Solution:

  • Split large images into multiple smaller sections
  • Use lower resolution images
  • Consider using a background with fewer details
  • Prefer simple, high-contrast designs — They look better on e-paper
  • Use drawInvertedBitmap() to handle XBM byte ordering
  • Keep image files as separate .h files — Improves code organization
  • Avoid thin horizontal lines (< 2px) — They may not render clearly
  • Don’t use gradients — E-paper cannot display shades of gray
  • Avoid updating background image with every refresh — Draw it once and overlay text changes when possible
  1. Images must be in XBM format — Monochrome bitmaps with 1-bit per pixel
  2. Image dimensions must match display resolution exactly
  3. Use drawInvertedBitmap() or drawBitmap() depending on XBM encoding
  4. Text can be overlaid on images — Draw the background first, then add dynamic text
  5. Organize bitmap data in separate .h files for maintainability

Target Audience: Alibaba.com IoT Pre-sales Engineers
Status: ✅ Completed