ESP32+墨水屏驱动避坑记录
简介
这里没有教科书式枯燥的“背景”或“定义”,如果需要那些,搜索引擎会给你更好的答案。我只是一个对硬件感兴趣的门外汉,在这里记录一下我的“爬坑”经历,以及那些我觉得值得被留下的瞬间。
故事的起因是一个说大不大、说小不小的"安全隐患"。我当时把一部退役的旧手机插着电,强行充当桌面时钟。结果电池很快鼓包了,像个随时会炸的地雷;再加上 LCD 屏幕在深夜里的漏光实在刺眼,体验极差。
恰巧我瞥见了桌角的 Kindle——那种几周不用充电、低功耗且护眼的质感,让我一下子就想明白了:墨水屏,才是桌面时钟的最佳选择。不仅如此,它还可以显示更多自定义的内容。在 B 站转了一圈后,发现这早已是神仙打架的成熟领域,各种开源项目、现成方案应有尽有。
以我的性格,能自己动手折腾的,绝不买现成的。于是我筹划着买回原材料自己拼装。一来是追求绝对的自定义自由,二来也想以此为切口,正式敲开单片机世界的大门。
至于为什么强调是“去年年初”开始的……这个后文再说(如果我没忘的话😝)。这些不重要的细节,也不必纠结。
声明:本文记录的是我个人使用 4.2 寸墨水屏的经验,其他尺寸或型号的屏幕我没测试过,仅供参考。
正文
以下内容是我对单片机的一些基础知识的总结,所查学习的资料均来源于互联网,并基于我自己粗浅的理解。如果有错误的地方,欢迎指正。 目前我的项目,还处于早期测试阶段,还没有连接电池,所以电池电压采样引脚暂时没有提及。之后会在项目完善后,再添加相关内容。
技术栈速查
这一节是给自己和后来人留的"备忘录",列出了这个项目涉及的核心概念。如果你也是新手,可以当成一份快速索引。
单片机的基础逻辑
- GPIO(开关):硬件只有 0 和 1,高电平和低电平。
- SPI (墨水屏的方言)
- CS (片选): 告诉某个屏幕“我要对你说话了”。
- BUSY (忙状态): 屏幕在刷新时会变“忙”,代码必须等它刷完。
- ADC (电力感知): 通过 GPIO 34 检测电压。这不只是为了显示电量,更是为了在电压过低时强制休眠,保护电池。
图形处理
- 像素与坐标:从左上角(0,0)开始,向右是 X 轴递增,向下是 Y 轴递增。
- 4.2寸屏,分辨率是400*300。每个像素都有自己的坐标,例如(100,200)表示距离左边缘100个单位,距离上边缘200个单位的位置。
- 点阵字体:这里的核心在于精简。ESP32 空间有限,通常只提取常用汉字生成
.u8g2字库,而不是塞一整套完整字体。
网络&数据
- Wi-Fi 连接管理:让 ESP32 自动重连家里路由器的代码模板。
- API 调用与 JSON 解析:
- HTTP 请求:去抓取天气、B站、或者个人网站/Blog的后台抓取数据。
- 数据清洗:从一大串乱码中精准抠出“温度:20°C”或“粉丝:5000”。
低功耗设计
- 低功耗睡眠 (Deep Sleep):让单片机刷完屏后进入“休眠”,这样几节电池就能让它跑几个月。
开发环境
- Arduino IDE 是目前对新手最友好的选择,自带丰富的库和示例代码,支持 ESP32 和 ESP8266 开发。但是没有办法使用AI辅助开发,这也导致了新手在使用Arduino IDE时,会遇到很多问题。
Esp-32/8266
刚开始我也分不清这俩,干脆在闲鱼上两个都买了回来。简单来说,ESP32 性能更强、功能更多(双核、蓝牙、更多 GPIO),ESP8266 更便宜、更省电。 对于墨水屏时钟这种应用,用ESP32更好,而且后续扩展空间更大。
- 在 Arduino IDE 中
- 打开
工具→开发板→开发板管理器 - 搜索
esp32,安装官方库 - 选择对应的开发板型号即可
- 打开
转接板
市面上有很多不同版本的转接板,但是功能几乎相同。需要将墨水屏的引脚(24Pin是常见的屏幕引脚)连接到转接板的24Pin上。再将转接板的引脚连接到Esp-32的引脚。每个转接板的引脚标注可能都不太一样,但只是名称不同,其功能是相同的,具体引脚的链接,可以参考下方引脚的表格
墨水屏
单IC vs 双IC:一个让我放弃一年的坑
最开始,我没有注意到屏幕 IC 的问题——就是墨水屏背面那条黑色的小芯片。
24年初买回来的第一块屏幕,兴冲冲地接好线、上传代码,结果屏幕死活不亮。翻遍了 GitHub 、试了无数个示例代码,甚至怀疑过是转接板坏了、ESP32 坏了、排线接触不良……折腾了几个星期,最后彻底放弃,把整套东西扔进了抽屉。
这一放,就是一年
直到最近打算做一些新的硬件项目,重新翻出这堆东西,才在某个 B 站视频的弹幕里看到有人提了一嘴"双 IC 要用专门的库"。我把屏幕翻出来一看——果然是双 IC。换了某位大神魔改的 <GxEPD2_2IC_BW.h> 库——秒亮。
那一刻的心情,大概就是"我 TM 白白浪费了一年"。
所以,如果你用单 IC 的库去驱动双 IC 屏幕,大概率不工作,甚至完全白屏。反之亦然。
重要提醒:单 IC 双色和单 IC 三色虽然都是单 IC,但用的库也不一样!
血泪教训 双 IC 屏幕如果需要飞线,一定要问清楚商家或找准位置,接错会直接烧板子(我已经烧了两块了😭)。
库 GxEPD2:目前最靠谱的库
GxEPD2 是目前对墨水屏支持最完善的 Arduino 库,工程化程度高,文档也齐全。
根据屏幕类型选择对应的头文件:
#include <GxEPD2_BW.h> // 单IC 双色屏幕(黑白)
#include <GxEPD2_3C.h> // 单IC 三色屏幕(黑白红/黑白黄)
#include <GxEPD2_2IC.h> // 双IC 双色屏幕(黑白)
GitHub 仓库:ZinggJM/GxEPD2
引脚与电阻选型
4.2 寸屏幕面积大,驱动电流需求也大。如果用 3Ω 电阻,内阻太大会导致屏幕可能刷不出来或者显示极淡。
推荐配置:
- 3.7 寸及以下:3Ω
- 4.2 寸及以上:0.47Ω
常用接线方案
| 转接板标识 | ESP32 引脚 | 功能说明 | 备注 |
|---|---|---|---|
| VCC | 3.3V | 电源 | 可接 3.3V 或 5V |
| GND | GND | 接地 | - |
| DIN (MOSI/SDI) | GPIO 11 | SPI 数据引脚 | 主机输出,从机输入 |
| CLK (SCK/SCL) | GPIO 12 | SPI 时钟引脚 | 同步时钟信号 |
| CS | GPIO 10 | 片选引脚 | 低电平有效 |
| DC | GPIO 9 | 数据/命令控制 | 高=数据,低=命令 |
| RST (RES) | GPIO 14 | 复位引脚 | 低电平复位 |
| BUSY | GPIO 13 | 忙状态输出 | 低电平表示屏幕正在刷新 |
| ADC | GPIO 34 | 电池电压检测 | 用于低电量保护 |
引脚尽量避开 26-32 号引脚,因为它们通常用于其他功能(如 SPI 或 I2C)。
如果板子上有RST引脚,千万不要直接插,它和墨水屏代码里的 RST 是两码事。屏幕代码里的 RST:它是屏幕驱动芯片(控制墨水颗粒那个小芯片)的“清空键”。
需要避开的引脚
- GPIO 0:启动模式切换引脚,上电时状态会影响启动
- GPIO 2:通常接板载 LED,启动时不稳定
- GPIO 12/15:某些模组用这两个引脚决定启动电压,慎用
测试代码
下面是我在 Arduino IDE 中使用的代码模板,用于驱动 4.2 寸双色墨水屏(GDEY042T81)。它不只是静态显示,而是加入了一些“随机性”——就像生活一样,虽然规则确定,但每一刻都独一无二。
注意❗️ Arduino 默认会把上传速率设为921600。如果你的程序上传中报错,尝试把上传速率设为115200(在 工具 -> upload speed 中选择)。
#include <GxEPD2_BW.h>
#include <U8g2_for_Adafruit_GFX.h>
// 引脚定义:CS(10), DC(9), RST(14), BUSY(13)
GxEPD2_BW<GxEPD2_420_GDEY042T81, GxEPD2_420_GDEY042T81::HEIGHT> display(
GxEPD2_420_GDEY042T81(10, 9, 14, 13));
U8G2_FOR_ADAFRUIT_GFX u8g2Fonts;
// 删掉物理连线,直接给个满电数值
float batteryVoltage = 4.2;
void setup() {
Serial.begin(115200);
// 降低波特率至 115200 烧录更稳
display.init(115200, true, 50, false);
u8g2Fonts.begin(display);
randomSeed(analogRead(0) + micros());
generateTreasureArt();
}
void generateTreasureArt() {
display.setRotation(0);
display.setFullWindow(); // 满屏刷新,防止残影
display.firstPage();
do {
display.fillScreen(GxEPD_WHITE);
for(int i=0; i<100; i++) {
int x = random(0, 400);
int y = random(0, 300);
display.drawPixel(x, y, GxEPD_BLACK); // 随机噪点
if(i % 5 == 0) {
display.drawLine(x, y, x + random(-20, 20), y + random(-20, 20), GxEPD_BLACK);
}
}
for(int i=0; i<5; i++) {
int x = random(0, 300);
int y = random(0, 200);
int r = random(20, 60);
display.drawCircle(x, y, r, GxEPD_BLACK);
display.drawCircle(x, y, r-2, GxEPD_BLACK);
if(random(0, 2) > 0) display.fillCircle(x+10, y+10, r/3, GxEPD_BLACK);
}
display.fillRoundRect(80, 100, 220, 100, 12, GxEPD_BLACK);
display.drawRoundRect(80, 100, 220, 100, 12, GxEPD_WHITE);
u8g2Fonts.setForegroundColor(GxEPD_WHITE);
u8g2Fonts.setBackgroundColor(GxEPD_BLACK);
u8g2Fonts.setFont(u8g2_font_wqy16_t_gb2312);
u8g2Fonts.drawUTF8(135, 145, "仝 遥 的 宝 藏");
u8g2Fonts.setFont(u8g2_font_helvB12_tf);
u8g2Fonts.drawUTF8(148, 170, "Troves.me");
u8g2Fonts.setForegroundColor(GxEPD_BLACK);
u8g2Fonts.setBackgroundColor(GxEPD_WHITE);
u8g2Fonts.setFont(u8g2_font_6x10_tf);
String meta = "RANDOM_GEN: " + String(millis()) + " | ADDR: 0x" + String(random(0xFFFF), HEX);
u8g2Fonts.drawUTF8(80, 290, meta.c_str());
} while (display.nextPage());
display.hibernate(); // 必须休眠!否则屏幕寿命会骤减
}
void loop() {}加载库
#include <GxEPD2_BW.h> // 单IC 双色屏幕
#include <GxEPD2_3C.h> // 单IC 三色屏幕
定义屏幕对象
GxEPD2_BW<GxEPD2_420_GDEY042T81, GxEPD2_420_GDEY042T81::HEIGHT> display(GxEPD2_420_GDEY042T81(/*CS=*/ 10, /*DC=*/ 9, /*RST=*/ 14, /*BUSY=*/ 13));初始化屏幕
display.init(115200);完整代码
#include <GxEPD2_BW.h>
#include <GxEPD2_3C.h>
GxEPD2_BW<GxEPD2_420_GDEY042T81, GxEPD2_420_GDEY042T81::HEIGHT> display(GxEPD2_420_GDEY042T81(/*CS=*/ 10, /*DC=*/ 9, /*RST=*/ 14, /*BUSY=*/ 13));
void setup() {
display.init(115200);
}
void loop() {
display.fillScreen(GxEPD_WHITE);
display.setCursor(0, 0);
display.setTextColor(GxEPD_BLACK);
display.setTextSize(2);
display.println("Hello, World!");
display.update();
delay(2000);
}墨水屏使用禁忌
这些都是容易踩的坑,有些甚至会直接损坏屏幕:
-
局刷不能滥用 支持局刷的屏幕,不能一直用局刷。建议每做 5-10 次局刷后,进行一次全刷清屏,否则会出现残影和鬼影。
-
不刷新时必须休眠 屏幕不刷新时,必须调用
display.hibernate()进入睡眠,或者直接断电。长时间保持高电压会损坏电泳膜片,且无法修复。 -
刷新频率限制
- 建议刷新间隔 ≥ 180 秒
- 至少每 24 小时刷新一次
- 长期不用时,刷白后存放(参考数据手册的储存条件)
-
睡眠后需重新初始化 屏幕进入睡眠模式后会忽略所有数据指令,必须重新调用
display.init()才能刷新。 -
FPC 排线很脆弱 只能沿屏幕水平方向弯曲,绝对不能垂直弯曲,否则会断线。
-
屏幕本体怕摔怕压 墨水屏比普通 LCD 脆弱得多,避免跌落、碰撞、按压。
我走过的弯路(也许你能避开)
回顾这一年多的折腾,有些坑真的让人哭笑不得:
-
最大的坑:单IC vs 双IC 去年买的第一块屏幕,因为不知道双 IC 需要专门的库,整整一年没点亮。今年重新捡起来,换了个库,秒亮。
-
烧了两块板子 双 IC 屏幕需要飞线时,我没问清楚位置,直接按照单 IC 的接法来,结果两块转接板直接报废。这个学费交得有点贵。
-
上传失败的循环 Arduino IDE 默认波特率是 921600,我的 USB 线不太稳定,一开始以为是代码有问题,后来才发现降到 115200 就好了。
-
屏幕越刷越淡 刚开始不知道 4.2 寸屏要用 0.47Ω 电阻,用的 3Ω,屏幕能刷新但特别淡,还以为是屏幕质量问题。
这些坑,希望你能绕过去。
