☰
ESP8266驱动HINK-E0213墨水屏:从接线到联网的桌面信息牌实战
2026/10/3 1:36:32 网站建设 项目流程

最近把工作台收拾了一遍,从角落里翻出两块攒了好久的料,一块是2.13寸电子墨水屏,丝印上是HINK-E0213,另一块是已经退到抽屉底层的esp8266开发板。原本两个都不太想单独用,但合在一起反倒挺有意思:一块不发光、不耗眼、断电还能留住字的小屏幕,配上几十块钱的WiFi模块,正好做成桌面信息牌。整个过程不涉及复杂焊接,用杜邦线就能搭起来,代码量也不大。

这个项目能干的活其实很“专”:你让它待机它就待机,WiFi拿时间、拿天气,再在墨水屏上慢慢画出来。屏幕刷新一次要一两秒,所以它天生不适合做实时动画,但非常适合做“隔几分钟变一次”的信息看板。适合谁呢?适合刚接触esp8266的入门玩家,也适合喜欢捣鼓桌面摆件、又不想被跑马灯晃到眼睛的折腾型选手。整篇文章我会把从型号识别、接线、驱动库选择,到第一块画面点亮、再往上加网络时间这个完整链路拆开讲清楚。

1. 项目启动之前:先弄懂这块屏在干什么

1.1 HINK-E0213到底是一块什么样的屏

HINK-E0213是2.13寸电子墨水屏模组上常见的型号,我手上这个模组的PCB丝印直接写了HINK-E0213,驱动程序走的是SSD1680控制器。这类屏本身没有背光,靠环境光反射显示,所以看起来就像打印出来的纸面效果。分辨率常见有两档,有的人买到的2.13寸是212x104,我手里这块是250x122,代码里驱动类要选对,不然显示区域和坐标会错乱,这一点后面会详细说。

墨水屏最核心的物理特性是“双稳态”:施加一次电压完成画面翻转后,即使断电,像素仍能保持原有的黑白状态。这就意味着它不需要像LCD那样持续刷新,也不像OLED那样自发光,功耗主要集中在画面切换的瞬间。平时静态挂着一行字,电流小到几乎可以忽略,如果用电池供电,它可以比任何带背光的屏幕都待机得更久。

但代价也明显:刷新速度慢,刷一次全屏通常要2秒左右,而且有一定的电荷残留现象,每次切换画面如果不用足够的“握手”时序,屏幕上会留下淡淡的残影。这不是屏坏了,是技术特性决定的。所以这块屏最适合的场景,是那些“内容变化不频繁”的地方:桌面日历、温湿度计、股票价格、待办事项清单、工位门牌,随便一个都比手机架在旁边实用得多。

1.2 为什么用esp8266带它,而不是用树莓派

esp8266是块很有性格的芯片,它本身只是一个低成本的WiFi SoC,但因为社区资料多、玩法成熟,这些年已经成为各种入门联网小项目的默认首选。它跟这块墨水屏搭配有几个非常现实的原因:

  • 成本极低,整块NodeMCU或者Wemos D1 Mini都在十几块到二十几块,比树莓派低一个量级。
  • 逻辑电平是3.3V,跟HINK-E0213转接板上的SPI接口电平匹配,接错5V才会出事,接3.3V反而省一层电平转换。
  • 自带WiFi,能直接拉NTP时间、天气API,数据刷新天然适合接这种低频显示的屏。
  • 生态完善,SSD1680库在Arduino平台上有多个现成方案,能少踩很多底层的坑。

树莓派当然也能做,性能强得多,但对一个2.13寸的黑白屏来说,用树莓派有点“杀鸡用牛刀”。它开机慢、功耗高,还得配系统,而esp8266上电几百毫秒就能开始跑驱动,写完固件丢那不管就行。项目里需要实时性和计算量的地方并不多,一个简单的信息看板,esp8266完全能扛得住。

我实际用下来,比较推荐的板子是Wemos D1 Mini,体积小、引脚丝印清楚、插面包板也方便。NodeMCU也能用,只是稍大一点。下面所有接线图我都按照常见NodeMCU/D1 Mini的GPIO编号给,抄作业时别只看丝印,要留意GPIO编号。

2. 硬件接线与开发环境准备

2.1 需要的物料清单

物料是整个项目里最简单的部分,几乎都是手边常见的硬件。

  • HINK-E0213墨水屏模块一块,注意是带转接板的那种,不是裸屏玻璃片。裸屏需要自己贴FPC连接器,新手没必要碰。
  • esp8266开发板,推荐Wemos D1 Mini或NodeMCU,二者引脚定义不同但GPIO编号能对上。
  • 杜邦线若干,母对母最方便,推荐7根起步。
  • 面包板,非必须但建议准备一块,方便整理引脚,避免杜邦线直接悬空乱晃。
  • USB供电线,给esp8266供电,电流只要500mA就够用了。墨水屏瞬态刷新时电流大概几十毫安,整个系统的峰值不会太高。

我踩过的第一个坑是“裸屏和带板子的区别”。如果你买到的是一块光溜溜的2.13寸屏,只有排线没有PCB转接板,那就不能直接接esp8266,需要额外的驱动板或自己焊接FPC转接,对新手来说难度陡增。所以我建议先买带转接板的,接线只用考虑8个针脚,省心得多。

2.2 SPI接线逐脚对照,别被丝印骗了

HINK-E0213转接板上的引脚一般包括VCC、GND、DIN、CLK、CS、DC、RST、BUSY,这些名字一看就知道是SPI接口加一些控制信号。esp8266这边用的硬件SPI引脚基本是固定的:MOSI在GPIO13(D7),SCLK在GPIO14(D5)。其余的CS、DC、RST、BUSY四个控制脚可以随意指定到空闲GPIO,只要代码里跟接线一致就行,不是必须在特定引脚上。

我用的接线表整理如下,按照Wemos D1 Mini/NodeMCU的丝印和GPIO双重标注,方便对照:

墨水屏模块引脚连接目标GPIO编号
VCC3V3-
GNDGND-
DIND7 (MOSI)GPIO13
CLKD5 (SCLK)GPIO14
CSD8GPIO15
DCD1GPIO5
RSTD2GPIO4
BUSYD3GPIO0

这组连接里有几个需要特别解释的地方。VCC一定接3V3,不要接到5V。墨水屏控制器的逻辑电平和esp8266一样都是3.3V,接5V轻则屏幕工作异常、重则烧掉控制芯片。充电宝直接给esp8266的5V供电是没问题的,因为板载稳压芯片会把5V降到3.3V给外部引脚用。

E-paper的CS和DC引脚比较好理解,一起决定往SPI总线上写的是命令还是数据。RST是硬复位,如果模块状态不对,拉一下RST能救回来。BUSY引脚最容易被人忽略,它是屏幕输出给主控的“忙信号”,当屏幕正在刷新或初始化时,BUSY会保持某种电平,主控必须等它释放后才能真正开始下一轮操作,否则控制时序会乱掉。

还有一个点需要注意,NodeMCU的D8引脚(GPIO15)在启动时内部状态和Flash搭配有些特殊性,一般作为CS用没啥问题。但保险起见,别在GPIO0、GPIO2、GPIO15这些有上电状态要求的引脚上接容易拉高的设备,我这个接法里哪一个都不太会造成启动失败,可以直接抄。

2.3 Arduino IDE里装esp8266环境和库

开发环境方面,我推荐直接使用Arduino IDE,理由只有一个:资料多。一行代码搜到的问题解法可能比手册还全。

打开Arduino IDE后,先进入“文件 -> 首选项 -> 附加开发板管理器网址”,添加esp8266开发板的JSON地址。这一步网上随手一搜就有官方链接,加完后在“工具 -> 开发板 -> 开发板管理器”里搜索esp8266并安装,版本选择默认即可。安装完选择对应板卡,比如“LOLIN(WEMOS) D1 Mini R2 & R3”或“NodeMCU 1.0 (ESP-12E Module)”,烧录速度选115200。

驱动屏幕我推荐直接装两个库:GxEPD2和Adafruit GFX Library。GxEPD2是墨水屏社区用得最广的集合库,把各种主流EPD控制器都封装了进去,写代码时只需告诉它具体型号,不需要自己操作寄存器。Adafruit GFX则提供文字、图形、位图绘制接口,画圆、写字、显图片都靠它。GxEPD2会依赖Adafruit BusIO等基础库,在库管理器里安装时通常会自动拉取,你只需确认几个库都装上了就行。

有些朋友可能还会纠结要不要用Adafruit_SSD1680库,或者直接用官方SSD1680驱动。我的建议是不要折腾,GxEPD2已经帮你处理了全刷、局刷、休眠等大量细节,对esp8266的内存占用也可接受。除非你有特殊需求,否则没必要重复造轮子。

3. 点亮屏幕的核心原理与代码实现

3.1 SSD1680时序与寄存器到底做了什么

很多人一看到“寄存器”“时序”就开始发怵,其实只需要站在“怎么把屏幕当一张纸来用”的角度理解就可以。SSD1680是屏幕内部的驱动控制器,esp8266通过SPI总线向它发送命令和数据。它的工作方式有点像老式磁盘:要先告诉它“我要往哪个位置写”,然后连续写入一串点阵数据,最后再下“刷新”命令把数据画出来。

整个流程可以拆成四步:

  • 初始化控制器,设置扫描方向、行数、列数等基础参数。
  • 把要显示的图片数据写入屏幕内部的RAM区。
  • 通知控制器执行刷新命令,控制器内部根据预设的波形和时序把RAM里的内容“印制”到墨水颗粒上。
  • 刷新完成后,读取BUSY状态或等它变回空闲,才可以继续下一步。

为什么必须等BUSY?我这样记:墨水屏的物理变化比电信号慢得多,electron纸材料上下翻转需要一定时间,控制器正在翻转时如果主控又急匆匆给它写新数据,画面很容易错乱或出残影。所以所有靠谱的程序都会在每次刷新前或者写数据前等BUSY释放。

SSD1680的具体寄存器地址不需要背,库都包好了。但了解一点没有坏处,比如写RAM的命令是0x10和0x00之类,调试硬件问题时看官方驱动源码会快很多。总之,你的目标不是裸写寄存器,而是会用库。

3.2 最小可运行的Arduino例程

下面这段代码能让你直接点亮屏幕并输出一行字,接线按表走。注意GxEPD2库中,2.13寸250x122对应驱动类是GxEPD2_213_B74,如果你的屏是212x104那种,就要换成GxEPD2_213_B72或B73,否则尺寸和坐标会不对。

#include <GxEPD2_BW.h> #include <Fonts/FreeMonoBold9pt7b.h> // 四个控制引脚:CS、DC、RST、BUSY GxEPD2_BW<GxEPD2_213_B74, GxEPD2_213_B74::HEIGHT> display( GxEPD2_213_B74(/*CS=*/ 15, /*DC=*/ 5, /*RST=*/ 4, /*BUSY=*/ 0) ); void setup() { Serial.begin(115200); display.init(); display.setRotation(1); display.setFullWindow(); display.firstPage(); do { display.fillScreen(GxEPD_WHITE); display.setFont(&FreeMonoBold9pt7b); display.setCursor(10, 30); display.print("Hello E-Paper"); display.setCursor(10, 60); display.print("2.13inch 250x122"); } while (display.nextPage()); display.hibernate(); } void loop() { }

这段代码做的事情很简单:初始化屏幕,全屏填充白色,在左上角打印两行文字,最后进入休眠。display.hibernate()值得说一句,库会把控制器切到极低功耗状态,显示内容仍然保留在屏上,但是后续再想刷新时必须重新唤醒或重新初始化。所以如果在loop里反复刷新,别一直调用hibernate,只有确定长时间不刷新时才建议休眠。

第一次跑起来如果发现屏幕只是白屏,不要慌,先检查BUSY、RST、DC、CS四根线是否接对,再来回看代码里的GPIO编号是否跟接线一致。esp8266上最常犯的错就是把丝印名当GPIO号用,比如“D4”在Wemos D1 Mini上是GPIO2,在NodeMCU上又是另一个映射,接线上必须区分。

3.3 显示图片:从Image2LCD到drawBitmap

文字只是开胃菜,做信息看板肯定会想放logo、天气图标、小图案。常见做法是把图片转成C语言数组,然后调用drawBitmap画出来。

准备一张宽250像素、高122像素的黑白BMP图片,尺寸不匹配时会显示错位。可以用开源工具image2cpp或者网页版转换器,把图片调成“1-bit bitmap”模式,通常会生成一个const unsigned char数组。生成时如果支持选择字节顺序和宽度,建议用默认值,然后在代码里用PROGMEM存储到Flash里,避免占用宝贵的内存。

#include "logo.h" ... display.setFullWindow(); display.firstPage(); do { display.fillScreen(GxEPD_WHITE); display.drawBitmap(0, 0, gImage_logo, 250, 122, GxEPD_BLACK); } while (display.nextPage()); display.hibernate();

这里会发现“黑和白”有时候会反过来,原因是转换工具对“1”的语义可能有不同定义,有的认为1=黑,有的认为1=白,遇到这种情况就检查转换工具的设置,或者把生成数组里的0和1全取反。GxEPD2库只负责把数组里的值按黑或白画出来,不负责处理图片语义。

我在实际转换图片时,踩过一个更隐蔽的坑:有些转换工具会额外生成一个“偏移量”表或“索引色表”,如果直接复制整个数组,程序编译没问题,但显示会花屏或错位。解决方案是选择“直接输出原始位图数据”而不是“带调色板的BMP”,或者干脆用支持C数组导出的工具,比如GIMP配合输出插件。椭圆、矩形、圆角矩形等基本图形,GxEPD2配合GFX库能直接画,不用转换图片,做边框会比图片更灵活。

3.4 中文字体的处理思路

Arduino的GFX库默认字体是英文字符,直接print中文会变成乱码或空白。想显示中文,最省事的方法是先用取模工具把需要显示的汉字转成图片数组,再当成位图贴上去;另一种方法是引入一个支持中文的字库,比如u8g2库,它的字体系统里可以加载不少中文字体。不过u8g2和GxEPD2的坐标接口不太一样,混用时要转换一下。

如果只是显示固定几个中文比如“温度”“湿度”,直接取模图片最简单。我习惯把整张信息卡做成一个250x122的完整位图,在电脑端用Python或PS先把文字布局好,再一次性生成全屏位图,这样能避免逐个字符拼接时出现宽度不对、换行错乱的问题。这种“整页渲染成图再显示”的思路,在墨水屏项目里比实时排版更容易获得好看效果。

4. 进阶:让墨水屏真正"有用"

4.1 联网拿时间,变成桌面日历

墨水屏信息牌最常见的用法就是显示时间日期。esp8266的优势是自带WiFi,可以走NTP协议从时间服务器拿到精确时间。

代码思路很简单:先连WiFi,然后调用configTime设置时区,再通过getLocalTime拿到当地时间的struct tm结构体。只要esp8266不断电,系统内部会使用SNTP定时校准时间,不需要自己维护延时累计误差。

#include <ESP8266WiFi.h> #include <time.h> #include <GxEPD2_BW.h> #include <Fonts/FreeMonoBold9pt7b.h> GxEPD2_BW<GxEPD2_213_B74, GxEPD2_213_B74::HEIGHT> display( GxEPD2_213_B74(/*CS=*/ 15, /*DC=*/ 5, /*RST=*/ 4, /*BUSY=*/ 0) ); void setup() { Serial.begin(115200); WiFi.mode(WIFI_STA); WiFi.begin("你的WiFi名", "你的WiFi密码"); while (WiFi.status() != WL_CONNECTED) { delay(500); Serial.print("."); } configTime(8 * 3600, 0, "ntp.aliyun.com"); delay(2000); struct tm timeinfo; if (!getLocalTime(&timeinfo)) { Serial.println("Failed to obtain time"); } display.init(); display.setRotation(1); display.setFullWindow(); display.firstPage(); do { display.fillScreen(GxEPD_WHITE); display.setFont(&FreeMonoBold9pt7b); display.setCursor(10, 30); display.printf("%04d-%02d-%02d", timeinfo.tm_year + 1900, timeinfo.tm_mon + 1, timeinfo.tm_mday); display.setCursor(10, 60); display.printf("%02d:%02d:%02d", timeinfo.tm_hour, timeinfo.tm_min, timeinfo.tm_sec); } while (display.nextPage()); display.hibernate(); } void loop() { }

每次刷新都重新连接WiFi会浪费不少电。我建议程序启动时连一次网,刷新完就断开WiFi,或者干脆在loop里每隔10分钟让esp8266手动执行一次时间请求。另外,NTP服务器不是必须使用阿里云的,公共时间服务器都可以,国内环境选一个延迟低的即可。别纠结协议细节,能用就行。

4.2 全局刷新与局部刷新,怎么取舍

屏幕刷新模式分两种:全局刷新和局部刷新。全局刷新会先把整个屏幕清一遍、再显示新内容,效果干净,残影少,但耗时最长,通常2秒多。局部刷新则只更新屏幕某一小块区域,速度能快到几百毫秒甚至更快,适合“每分钟只改一个数字”的场景。

SSD1680控制器本身支持局部刷新,GxEPD2里调用setPartialWindow就能用。但局部刷新有个绕不开的代价:墨水颗粒残留电荷更明显,刷几次之后整块屏幕会发灰,字边会有淡淡影子。所以正确的姿势是,大部分时间用全局刷新,遇到数字频繁变化才用局部刷新,且每隔几次局部刷新强制补一次全局刷新来“洗掉”残影。

display.setPartialWindow(10, 80, 60, 20); display.firstPage(); do { display.setCursor(10, 90); display.printf("%02d", minute); } while (display.nextPage());

我在做时间显示时,就只让“分钟”两位数字走局部刷新,日期和“时间”这个标题则全局固定。这样既保持了相对快的更新频率,又不至于让整块屏一直闪来闪去。

还有一个跟刷新相关的经验:不要频繁整屏刷新。墨水屏的电子纸本质上有寿命问题,虽然不是那么容易坏,但频繁刷新会加速老化和残影。一个信息展示屏,设置成5分钟、10分钟刷一次完全足够了。

4.3 低功耗调度:让屏彻底“睡”过去

如果把esp8266做成电池供电的工位门牌或者室内气象站,功耗就要认真考虑了。

esp8266在跑WiFi时的电流波动很大,峰值能到几百毫安,待机时Modem Sleep模式下电流能降很多。墨水屏的好处是不刷新时屏幕自身几乎不耗电,所以整体功耗主要看esp8266怎么管理。

一种常见方案是:上电后连WiFi获取数据,刷新墨水屏,然后调用ESP.deepSleep(),让整个芯片进入深度睡眠,隔一段时间再用定时器唤醒。比如每10分钟醒一次,醒来后刷新一次日期温度,然后又睡回去。搭配一块18650锂电池,理论上能用几十天,具体取决于唤醒频率和工作时间。

ESP.deepSleep(10 * 60 * 1000000); // 单位是微秒

需要注意,deepsleep醒来后程序会重新从setup执行,这和看门狗复位有点像,所以代码里要把刷新逻辑放在一个函数里,不要在loop里写累计变量,因为醒过来后变量全丢了。此外,deepsleep期间GPIO状态不受控制,BUSY和RST引脚可能会变成高阻态,因此接线时别让这些引脚悬空受干扰,最好加一下外部上拉或下拉,确保唤醒后工作正常。

如果你想要精确的“几点几分几秒”定时唤醒,原生deepsleep只能做相对时间,不够准。这时可以外接一个RTC时钟模块,让RTC到点后通过GPIO唤醒esp8266。但这个项目里10分钟级别的刷新不需要太精确,用原生deepsleep就够了。

5. 常见问题排查与避坑建议

5.1 初始化白屏、黑屏、花屏怎么查

折腾硬件一定会遇到屏幕“不听话”的时刻,我列一个自己常用的排查表,按优先级从低到高检查:

现象可能原因处理办法
屏幕上什么都不显示接线错误,尤其CS/DC/RST/BUSY用万用表或肉眼逐根复查杜邦线,确认GPIO编号
白屏但SDK输出正常屏幕初始化时序不对,BUSY接错换一个空闲GPIO,尤其检查GPIO0/GPIO15是否被其他设备占用
花屏、乱码SPI速率过高、导线过长、模块供电不足降低SPI频率,杜邦线尽量短,改用独立3.3V稳压供电
有画面但偏移或截断驱动型号选错或图片尺寸不对确认SSD1680属于哪种2.13寸,改GxEPD2_213_B72/B73/B74
残影严重频繁局刷后未恢复执行全屏刷新,填充白屏几次,或调用display.clearScreen()

其中最难排查的一条是GPIO冲突。esp8266不像Arduino Uno那样“随便找个引脚就完事”,部分引脚在上电时承担启动模式选择功能,比如GPIO0、GPIO2、GPIO15,同时它们也可能被开发板板载LED等外设占用。如果BUSY刚好接在某个引脚上,而这个引脚在刷新瞬间被其他外设干扰,那就容易出问题。遇到莫名其妙的花屏,优先把相关引脚全部换到GPIO5、GPIO4、GPIO12这些“安全”引脚上试试。

5.2 供电不足和电平冲突

esp8266开发板上的3.3V引脚不是万能的,它的最大输出电流取决于板载稳压器,一般几百毫安。墨水屏刷新时会有瞬态电流需求,如果供电线太细、接触不良或者电源适配器电流太小,屏幕很容易刷新失败。

我遇到过一种情况:插着杜邦线用电脑USB口供电,屏幕大面积图案能显示出来,但一旦在局部写密集文字时就会变浅、漏笔画。后来换了一个支持1A输出的5V电源适配器,问题就消失了。原因很简单,USB口供电波动导致3.3V电压在刷新瞬间跌落,SSD1680的工作电压不够稳定,电气性能就变差。

另外,HINK-E0213如果是老款转接板,上面可能有一颗电平转换芯片,那它对3.3V逻辑信号很友好。但不管怎样,绝不接5V到VCC,这是所有模块共同的红线。如果要把屏幕放在远离esp8266的位置,SPI线长度尽量控制在20厘米以内,长了之后信号完整性会下降,表现为偶发性花屏。

5.3 ESP8266内存不够怎么办

这块屏的帧缓冲是250x122个像素,每个像素1比特,换算下来才3813字节左右,也就是不到4KB,对esp8266来说很轻松。真正吃内存的是字体、位图数组和String对象。

如果用Arduino的String类去拼句子,在内存紧张时会导致大量碎片,最终malloc失败甚至重启。我的习惯是尽量用C语言的snprintf格式化字符串,或者直接display.printf输出,避免中间临时String对象。位图数组要用PROGMEM放到Flash里,而不是const数组,虽然const在esp8266上有时也会被优化进Flash,但显式使用PROGMEM更保险。

还有一个方法能进一步减内存:分块更新。如果你只需要更新屏幕中间一个100x50的小区域,不需要维护整块屏的缓冲,用setPartialWindow后再绘制即可。GxEPD2在局部刷新时会自动处理对应区域的缓冲,内存开销随区域大小变化。

5.4 温度对墨水屏的影响

墨水屏是物理变化,对温度很敏感。夏天室内正常使用问题不大,但冬天如果放在阳台或无暖气的房间里,屏幕刷新时会明显变慢、颜色变浅,甚至出现大面积灰底。这是墨水颗粒在低温下迁移速度变慢导致的,不是硬件故障。

处理办法也很简单:如果周围温度低于10℃,刷新前给屏幕“热身”,可以靠esp8266局部的发热,也可以把刷新间隔拉长,让它每次刷新前有足够时间稳定。更简单粗暴的方法是把设备移到室内正常温度环境。这个特性在项目规划时就该考虑到,特别是做户外天气指示牌的话,低温问题比想象中常见。

结尾:给后来者的三个小建议

这套项目做完之后,我最大的感受是:它不复杂,但很考验耐心。第一次接线时我糊涂地把DIN接到了SCLK上,白屏了半小时才查出来,后来把接线表打印出来贴在面包板旁边才彻底省心。第二,驱动型号别只记商品标题,买到手第一时间看丝印、对分辨率,GxEPD2库里不同型号的类名差一个数字显示效果就差很远。第三,不要在第一版就想着加很多功能,先把“显示文字-显示图片-联网刷时间”这三级台阶走通,再去考虑按钮、服务器、低功耗,一个个叠加反而更顺。

我最后还留了个习惯:每次改完程序,先执行一次整屏白屏刷新,把上一张图的残影彻底冲掉,再画新内容。这样屏幕看起来一直是干干净净的。这个项目后面还能延伸成天气播报器、邮件提醒器或者实验室里的测试状态牌,核心逻辑其实都一样。如果你也正好手里有一块HINK-E0213和一块esp8266,那就别让它们继续吃灰了,接上线,把第一行字跑出来,这个成就感比想象中大多了。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询