跳转到内容

电子墨水屏图像显示

电子墨水屏图像显示

本节介绍在电子纸显示屏上显示位图图像,包括将图形转换为所需格式、嵌入到草图中,以及将图像与文本结合以创建专业外观的仪表板。完成本节后,你将能够:

  • 将 PNG/JPG 图像转换为电子纸显示屏用的 XBM 字节数组
  • 在 Arduino 草图中嵌入位图数据
  • 在电子纸屏幕上显示图像
  • 在背景图像上叠加文本

开始本节前,请确保:

  • 已安装并验证 GxEPD2 库(参见 02-03
  • 理解文本渲染基础(参见 02-04
  • 基本图像编辑技能

电子纸显示屏需要 XBM(X BitMap)格式 的图像——一种单色位图格式,每个像素由单个位表示(0 = 白色,1 = 黑色)。

XBM 工作原理

原始图像(250×122 像素)
转换为 XBM
每个像素 → 1 位(0=白色,1=黑色)
250 × 122 = 30,500 位 = 3,813 字节
C 字节数组:
const unsigned char image_name[] = {
0xFF, 0x00, 0xFF, ... // 第 1 行
0x00, 0xFF, 0x00, ... // 第 2 行
...
};

关键点

  • XBM 纯粹是黑白的(无灰度)
  • 数据按行从左到右存储
  • 每个字节代表 8 个水平像素
  • 字节格式化为十六进制值

图像必须与显示分辨率完全匹配:

显示屏尺寸分辨率最大图像尺寸
1.54”200×200200×200 像素
2.13”250×122250×122 像素
2.9”296×128296×128 像素
4.2”400×300400×300 像素
7.5”800×480800×480 像素
  1. 创建或调整图像大小以匹配显示分辨率
  2. 转换为黑白(无灰度,无彩色)
  3. 保存为 PNG 或 BMP

图像准备技巧

  • 使用高对比度设计(纯黑对纯白)
  • 避免细线(小于 2 像素宽)
  • 保持图像中的文字足够大以可读
  • 实心区域比复杂图案效果更好

步骤 2:使用在线工具转换为 XBM

Section titled “步骤 2:使用在线工具转换为 XBM”

使用在线转换器生成 XBM 数据:

  1. 访问 image.online-convert.com/convert-to-xbm
  2. 上传你的图像
  3. 确保设置:
    • 格式:XBM(X BitMap)
    • 单色:是
    • 尺寸:匹配显示分辨率
  4. 点击 转换
  5. 下载 .xbm 文件

替代方案:使用 image2cpp(http://javl.github.io/image2cpp/):

  1. 上传图像
  2. 设置输出格式:Plain C array
  3. 亮度阈值:128(根据需要调整)
  4. 勾选 Draw mode:Black on white
  5. 点击 Generate code
  6. 复制生成的字节数组

在草图文件夹中创建一个名为 background.h 的新文件:

background.h
// 2.13" 电子纸显示屏的工厂仪表板背景图像(250×122)
#ifndef BACKGROUND_H
#define BACKGROUND_H
const unsigned char background_image [] = {
// 在此粘贴转换后的 XBM 数据
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,
// ...(出于简洁省略了完整数据——为你的显示尺寸包含所有字节)
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" // 包含位图头文件
#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);
// 清除屏幕
display.fillScreen(GxEPD_WHITE);
// 在位置 (0,0) 绘制位图 — 全屏
display.drawInvertedBitmap(
0, // X 位置
0, // Y 位置
background_image, // 位图数据
display.width(), // 宽度
display.height(), // 高度
GxEPD_BLACK // 颜色
);
display.display();
Serial.println("图像已显示!");
}
void loop() {}

注意:使用 drawInvertedBitmap() 是因为 XBM 格式将 0 视为黑色、1 视为白色,而电子纸库期望相反。

替代方案:如果您的 XBM 使用标准编码,改用 drawBitmap()

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

这是最实用的用例——显示背景设计并在其上叠加动态文本:

#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);
// 步骤 1:绘制背景
display.fillScreen(GxEPD_WHITE);
display.drawInvertedBitmap(0, 0, background_image,
display.width(), display.height(),
GxEPD_BLACK);
// 步骤 2:在背景上叠加文本
display.setFont(&FreeMonoBold18pt7b);
display.setCursor(90, 55);
display.setTextColor(GxEPD_BLACK);
display.print("25.5");
display.setFont(&FreeMono9pt7b);
display.setCursor(95, 80);
display.print("摄氏度");
display.setCursor(85, 105);
display.print("湿度:60%");
display.display();
}
void loop() {}

用于天气预报仪表板,可以嵌入小图标:

weather_icons.h
const unsigned char sun_icon[] = {
0x00, 0x18, 0x00, // 太阳符号的二进制图标数据(16×16)
0x00, 0x3C, 0x00,
// ...(16×16 = 32 字节)
};
const unsigned char cloud_icon[] = {
// 云图标数据
};
const unsigned char rain_icon[] = {
// 雨图标数据
};
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);
}
}

位图图像消耗显著的闪存空间:

显示屏尺寸分辨率XBM 大小(字节)
1.54”200×200~5,000 字节
2.13”250×122~3,813 字节
2.9”296×128~4,736 字节
4.2”400×300~15,000 字节
7.5”800×480~48,000 字节

注意:ESP32 通常有 4MB 闪存,因此即使全屏 7.5” 图像也只使用约 1.2% 的可用闪存。但超大图像可能需要 PROGMEM 存储。

对于 ESP32,编译器会自动将 XBM 数据存储在闪存中。不需要特殊的 PROGMEM 处理(与基于 AVR 的 Arduino 开发板不同)。

  • 图像在正确位置和大小显示
  • 无变形或像素偏移
  • 文本叠加在图像上的正确位置显示
  • 图标(如果使用)清晰显示
  • 对比度良好(黑白分明)

症状

  • 图像被拉伸、压缩或偏移

解决方案

  • 验证图像尺寸是否与显示分辨率完全匹配
  • 检查 drawInvertedBitmap() 中的宽度和高度参数是否正确
  • 确保在绘制图像之前设置了旋转

问题 2:图像颜色反转(黑白颠倒)

Section titled “问题 2:图像颜色反转(黑白颠倒)”

症状

  • 白色区域变黑,反之亦然

解决方案

// 选项 1:使用另一个位图函数
display.drawBitmap(0, 0, image_data, width, height, GxEPD_BLACK);
// 选项 2:使用 drawInvertedBitmap 反转数据
display.drawInvertedBitmap(0, 0, image_data, width, height, GxEPD_BLACK);

问题 3:编译错误——“Image too large”

Section titled “问题 3:编译错误——“Image too large””

症状

  • 关于数组大小的编译器错误

解决方案

  • 将大图像拆分为多个较小的部分
  • 使用更低分辨率的图像
  • 考虑使用细节较少的背景
  • 首选简单、高对比度的设计——在电子纸上效果更好
  • 使用 drawInvertedBitmap() 来处理 XBM 字节顺序
  • 将图像文件保存为独立的 .h 文件——改善代码组织
  • 避免细的水平线(< 2px)——可能无法清晰渲染
  • 不要使用渐变——电子纸无法显示灰度
  • 避免每次刷新都更新背景图像——绘制一次并在可能时叠加文本更改
  1. 图像必须为 XBM 格式——1 位每像素的单色位图
  2. 图像尺寸必须与显示分辨率完全匹配
  3. 使用 drawInvertedBitmap()drawBitmap() 取决于 XBM 编码
  4. 文本可以叠加在图像上——先绘制背景,然后添加动态文本
  5. 将位图数据组织在独立的 .h 文件中以便于维护