1. 项目缘起为什么选择XIAO ePaper Display Board - EN05如果你和我一样对电子墨水屏ePaper那种类纸般的显示效果、超低功耗和阳光下清晰可见的特性着迷同时又希望有一个足够小巧、易于上手的开发板来快速实现创意那么Seeed Studio的XIAO ePaper Display Board - EN05绝对是一个值得你花时间研究的“小钢炮”。我最初接触它是因为想做一个放在桌面的天气站。市面上的成品要么功能单一要么功耗感人要么就是体积太大。我需要一个能显示丰富信息时间、天气、日程、续航以周甚至月计、并且足够精致不占地方的设备。在对比了ESP8266/ESP32搭配各种墨水屏模块的方案后我发现了一个痛点连线。虽然原理简单但杜邦线连接总是显得凌乱而且SPI引脚配置、屏幕初始化代码调试对于新手来说又是一道坎。XIAO ePaper Display Board - EN05的出现完美解决了这个问题。它把一块2.66英寸、296x152分辨率的黑白三色黑、白、红墨水屏与一颗强大的微控制器我手上这款是XIAO ESP32S3 Sense直接集成在了一块比信用卡还小的板子上。这意味着开箱即用你不需要焊接不需要纠结接线图所有的硬件兼容性问题都由厂家帮你解决了你可以把100%的精力投入到软件和创意本身。这块板子的核心价值在我看来有三点集成度、低功耗和XIAO生态。集成度刚才说了硬件上几乎做到了“傻瓜式”。低功耗则是墨水屏和ESP32S3芯片共同带来的红利ESP32S3支持深度睡眠墨水屏只在刷新时耗电显示静态图像时零功耗这让它非常适合电池供电的长期显示设备。而XIAO生态意味着它有丰富的扩展板Grove接口和社区支持你可以轻松地给它加上温湿度传感器、光线传感器、蜂鸣器甚至摄像头ESP32S3 Sense版本自带极大地扩展了应用场景。从桌面摆件到智能标签从简易仪表盘到交互式艺术装置它的可能性远超一块简单的显示屏。2. 开箱与硬件初识这块板子到底有什么拿到XIAO ePaper Display Board - EN05第一印象就是精致。整个板子非常紧凑正面几乎被那块2.66英寸的墨水屏占据屏幕上方有一个简单的“XIAO”Logo。翻到背面核心是XIAO系列的微控制器模块以ESP32S3为例周围分布着必要的电路和接口。我们来详细拆解一下它的硬件构成这对后续的编程和问题排查至关重要2.1 核心显示部件ePaper显示屏这块屏幕的型号是EN05这是一个关键信息。它是一款2.66英寸、分辨率296x152的黑白红三色电子墨水屏。所谓三色是指它能显示黑色、白色和红色。请注意这里的“红色”是固定的颜色你不能用它显示蓝色或绿色。它的刷新原理是电泳通过施加电压驱动带电荷的颜料颗粒移动。因此刷新过程相对较慢全屏刷新可能需要2秒以上并且会有明显的“闪屏”全黑全白交替现象这是正常的目的是清除残影。屏幕通过排线直接与主板连接接口通常是SPI。但幸运的是作为用户我们不需要关心具体的引脚定义因为板载的微控制器已经通过内部线路连接好了。2.2 核心大脑XIAO微控制器模块这是板子的运算和控制中心。我使用的版本搭载了ESP32-S3这是一款双核240MHz的Wi-Fi Bluetooth 5 (LE) SoC性能对于驱动墨水屏和连接网络绰绰有余。它提供了充足的GPIO、SPI、I2C、UART等接口虽然部分被屏幕占用但剩余的引脚通过板载的Grove接口和焊盘引出依然有很强的扩展能力。板载了4MB的PSRAM和8MB的Flash这对于存储字库、图片资源以及复杂的图形界面非常友好。此外ESP32S3 Sense版本还集成了麦克风和OV2640摄像头为音频和视觉应用打开了大门。2.3 关键接口与按键USB Type-C接口用于供电、程序烧录和串口通信。这是你与板子交互的主要通道。复位按键RST按下重启整个系统。Boot按键B用于进入固件下载模式。在烧录程序时通常需要先按住Boot键再按一下RST键然后松开Boot键使板子进入等待烧录的状态。Grove接口这是一个标准的4针接口VCC, GND, SDA, SCL默认配置为I2C。你可以通过它连接数百种Grove系列的传感器和执行器无需焊接即插即用极大地简化了原型开发。电池接口板子支持连接一块3.7V的锂聚合物电池如常见的602530型号。板载了充电管理电路可以通过USB口为电池充电。这是实现设备便携和长续航的关键。2.4 电源管理板子的电源设计很贴心。当同时连接USB和电池时USB供电优先并为电池充电。断开USB后自动切换至电池供电。ESP32S3的深度睡眠功能配合墨水屏的零静态功耗可以让设备在仅显示静态信息时待机电流降到微安级别从而实现超长续航。注意首次使用前建议通过USB口为板子如果接了电池也包括电池充电一段时间确保供电稳定。3. 开发环境搭建从零到一的软件准备硬件认识了接下来就是让板子“动”起来。对于ESP32系列Arduino IDE和PlatformIO是两大主流选择。我个人更推荐PlatformIO因为它对库管理和项目结构更友好尤其适合多文件项目。这里我会以PlatformIO基于VS Code为例进行说明同时也会提一下Arduino IDE的要点。3.1 安装PlatformIO核心安装Visual Studio Code。在VSCode的扩展商店中搜索“PlatformIO IDE”并安装。安装完成后左侧活动栏会出现PlatformIO的图标一个小蚂蚁。第一次打开可能会自动安装核心耐心等待即可。3.2 创建新项目点击PlatformIO图标选择“PIO Home”。点击“New Project”。在“Name”中输入你的项目名例如xiao_epaper_weather。在“Board”框中输入“ESP32S3”在下拉列表中选择“Seeed Studio XIAO ESP32S3”。这一步非常关键必须选对开发板型号否则引脚定义和功能可能出错。Framework选择“Arduino”。选择项目保存位置然后点击“Finish”。PlatformIO会自动创建项目文件夹并下载对应的ESP32S3平台支持包和工具链这可能需要一些时间。3.3 安装必要的库墨水屏驱动需要特定的库。Seeed Studio为他们的ePaper产品维护了优秀的库。在PlatformIO主页点击“Libraries”。在搜索框中输入“GxEPD2”这是一个非常流行且强大的ePaper显示库对很多屏幕有很好的支持。找到“GxEPD2 by Jean-Marc Zingg”并点击进入。点击“Add to Project”选择你刚刚创建的项目然后选择版本通常选最新稳定版。PlatformIO会自动将这个库的依赖添加到项目的platformio.ini配置文件中。同样地搜索并安装“Seeed Arduino Graphics 2”和“Seeed Arduino SFUD”库。前者提供了丰富的绘图函数后者用于管理板载的Flash存储。3.4 配置项目 (platformio.ini)项目根目录下的platformio.ini文件是项目的核心配置文件。创建项目后它可能只有基础内容。我们需要根据XIAO ePaper Display Board进行修改。一个典型的配置如下[env:seeed_xiao_esp32s3] platform espressif32 board seeed_xiao_esp32s3 framework arduino monitor_speed 115200 ; 启用PSRAM非常重要用于图形缓冲 board_build.arduino.memory_type qio_opi board_build.flash_mode qio board_build.partitions default_8MB.csv ; 库依赖 lib_deps zinggjm/GxEPD2^1.5.6 seeed-studio/Seeed Arduino Graphics 2^2.1.7 seeed-studio/Seeed Arduino SFUD^2.0.23.5 Arduino IDE备选方案如果你坚持使用Arduino IDE打开Arduino IDE点击“文件”-“首选项”。在“附加开发板管理器网址”中添加ESP32的板支持网址https://espressif.github.io/arduino-esp32/package_esp32_index.json点击“工具”-“开发板”-“开发板管理器”搜索“esp32”找到“Espressif Systems”的ESP32平台并安装。安装完成后在“工具”-“开发板”中选择“XIAO ESP32S3”。点击“项目”-“加载库”-“管理库”搜索并安装“GxEPD2”、“Seeed Arduino Graphics 2”和“Seeed Arduino SFUD”。提示无论哪种环境首次烧录前可能需要手动让板子进入下载模式按住板子上的Boot键不放再按一下RST键然后松开Boot键。此时在IDE中点击上传即可。4. 第一个程序让屏幕显示点东西环境搭好了让我们写一个最简单的程序来验证硬件和软件是否工作正常。这个程序的目标是在屏幕上显示“Hello, ePaper!”。4.1 理解墨水屏编程的核心概念在编程前需要理解两个关键对象显示驱动对象负责与屏幕硬件通信执行底层刷新操作。对于EN05屏幕我们使用GxEPD2_BW类因为我们只用了黑白红色作为特殊颜色处理。画布/图形对象我们在内存中创建一个“画布”在这个画布上绘制文字、图形最后将这个画布的内容一次性发送给显示驱动对象进行刷新。4.2 代码实现在PlatformIO项目的src目录下打开或创建main.cpp文件输入以下代码#include Arduino.h #include GxEPD2_BW.h #include U8g2_for_Adafruit_GFX.h // 定义显示屏类型和引脚。对于XIAO ePaper Display Board - EN05这些引脚定义是固定的。 // GxEPD2_BWGxEPD2_266_BN, GxEPD2_266_BN::HEIGHT display(...); // 但为了兼容性和简化Seeed提供了预定义的型号。对于EN05我们可以使用 GxEPD2_BWGxEPD2_266_BN, GxEPD2_266_BN::HEIGHT display(GxEPD2_266_BN(/*CS*/ 3, /*DC*/ 1, /*RST*/ 0, /*BUSY*/ 2)); // 使用U8g2字体库进行文本渲染它支持丰富的中文字体。 U8G2_FOR_ADAGRUIT_GFX u8g2Fonts; void setup() { Serial.begin(115200); delay(1000); // 给串口和系统一点启动时间 Serial.println(XIAO ePaper Display Board EN05 Test Start!); display.init(115200, true, 2, false); // 初始化显示屏115200波特率使用SPI2不重置 display.setRotation(1); // 设置旋转方向0-3根据你的安装方向调整 display.setTextColor(GxEPD_BLACK); // 设置文本颜色为黑色 // 初始化U8g2字体并关联到我们的display对象 u8g2Fonts.begin(display); // u8g2Fonts.setFontMode(1); // 透明模式 u8g2Fonts.setFontDirection(0); // 从左到右 u8g2Fonts.setForegroundColor(GxEPD_BLACK); // 前景色黑 u8g2Fonts.setBackgroundColor(GxEPD_WHITE); // 背景色白 // 选择字体这里使用内置的8像素高度字体 u8g2Fonts.setFont(u8g2_font_6x10_tf); // 清屏为白色 display.fillScreen(GxEPD_WHITE); // 设置光标位置x, y。坐标原点(0,0)在屏幕的左上角。 u8g2Fonts.setCursor(20, 30); // 打印文本 u8g2Fonts.print(Hello, ePaper!); // 将内存中的画布内容更新到屏幕上 display.display(); Serial.println(Display update done. Going to deep sleep.); // 更新完成后可以进入深度睡眠以省电 // esp_deep_sleep_start(); } void loop() { // 本例中loop函数为空因为只显示一次 delay(1000); }4.3 代码解析与烧录GxEPD2_266_BN这个类对应的是2.66英寸、黑白显示BN可能代表Black/New的屏幕驱动。它是GxEPD2库中预定义好的型号。引脚参数(/*CS*/ 3, /*DC*/ 1, /*RST*/ 0, /*BUSY*/ 2)这些是XIAO ESP32S3与EN05屏幕连接的内置引脚号无需修改。display.init()初始化屏幕参数依次为SPI时钟频率、是否启用重置引脚、SPI总线编号ESP32S3有多个SPI、是否在初始化时执行硬件重置。u8g2Fonts这是一个强大的图形库提供了丰富的字体和绘图功能比直接使用Adafruit GFX基础函数更方便。display.display()这是最关键的一步所有在内存画布上的绘制操作都必须调用这个方法才会真正在屏幕上刷新出来。刷新过程你会看到屏幕闪烁几次。将代码保存连接好板子在PlatformIO中点击左下角的“→”箭头Upload进行编译和烧录。烧录成功后板子会自动重启你应该能看到屏幕上显示出“Hello, ePaper!”。注意第一次刷新或长时间未刷新后的第一次刷新时间会较长可能超过2秒并且伴随全屏黑白闪烁这是墨水屏的正常特性用于彻底清除残影请耐心等待。5. 进阶应用打造一个简易桌面天气站现在我们已经能让屏幕显示基本文字了让我们实现一个更实用的项目一个通过Wi-Fi获取网络时间NTP和天气信息并显示在屏幕上的桌面天气站。这个例子将涵盖Wi-Fi连接、HTTP请求、JSON解析和定时刷新等核心技能。5.1 项目设计思路连接Wi-Fi让设备接入互联网。获取网络时间NTP从网络时间服务器同步当前时间用于显示时钟。获取天气数据调用一个免费的天气API如和风天气、OpenWeatherMap等获取当地天气信息。解析与显示解析API返回的JSON数据提取温度、天气状况等信息与时间一起布局显示在墨水屏上。定时与低功耗设备每30分钟或1小时唤醒一次更新天气和时间然后进入深度睡眠以实现超长续航。5.2 代码结构概览我们将创建几个关键部分config.h存放Wi-Fi密码、天气API密钥等敏感信息切记不要上传到公开的代码仓库。weatherStation.ino或main.cpp主程序文件包含setup()和loop()逻辑。使用WiFi.h、HTTPClient.h、ArduinoJson.h等库。5.3 关键代码片段与解释首先创建config.h或直接在代码中定义但不推荐// config.h #define WIFI_SSID 你的Wi-Fi名称 #define WIFI_PASSWORD 你的Wi-Fi密码 #define API_KEY 你的天气API密钥 #define CITY_ID 你的城市ID // 例如和风天气的城市ID主程序核心逻辑简化版#include Arduino.h #include WiFi.h #include HTTPClient.h #include ArduinoJson.h #include config.h // ... 其他必要的头文件如GxEPD2、U8g2等 // 全局变量 GxEPD2_BWGxEPD2_266_BN, GxEPD2_266_BN::HEIGHT display(GxEPD2_266_BN(3, 1, 0, 2)); U8G2_FOR_ADAGRUIT_GFX u8g2Fonts; struct WeatherData { String temp; String weather; String humidity; }; struct TimeData { String hour; String minute; String weekday; }; void connectToWiFi() { WiFi.begin(WIFI_SSID, WIFI_PASSWORD); Serial.print(Connecting to WiFi); while (WiFi.status() ! WL_CONNECTED) { delay(500); Serial.print(.); } Serial.println(\nConnected!); } WeatherData fetchWeather() { WeatherData data; if (WiFi.status() WL_CONNECTED) { HTTPClient http; String url http://api.seniverse.com/v3/weather/now.json?key String(API_KEY) location String(CITY_ID) languagezh-Hansunitc; http.begin(url); int httpCode http.GET(); if (httpCode HTTP_CODE_OK) { String payload http.getString(); DynamicJsonDocument doc(1024); deserializeJson(doc, payload); data.temp doc[results][0][now][temperature].asString(); data.weather doc[results][0][now][text].asString(); data.humidity doc[results][0][now][humidity].asString(); } http.end(); } return data; } TimeData getNetworkTime() { TimeData t; // 使用NTP服务获取时间此处为简化示例实际需配置NTP服务器和时区 configTime(8 * 3600, 0, ntp.aliyun.com, ntp1.aliyun.com); // 东八区 struct tm timeinfo; if (!getLocalTime(timeinfo)) { Serial.println(Failed to obtain time); return t; } char buffer[10]; strftime(buffer, sizeof(buffer), %H, timeinfo); t.hour String(buffer); strftime(buffer, sizeof(buffer), %M, timeinfo); t.minute String(buffer); strftime(buffer, sizeof(buffer), %a, timeinfo); // 星期几缩写 t.weekday String(buffer); return t; } void updateDisplay(const WeatherData w, const TimeData t) { display.init(115200, true, 2, false); display.setRotation(1); display.fillScreen(GxEPD_WHITE); u8g2Fonts.begin(display); u8g2Fonts.setFont(u8g2_font_10x20_tf); // 使用大一点的时间字体 u8g2Fonts.setCursor(10, 30); u8g2Fonts.print(t.hour : t.minute); u8g2Fonts.setFont(u8g2_font_6x10_tf); u8g2Fonts.setCursor(10, 60); u8g2Fonts.print(星期 t.weekday); u8g2Fonts.setCursor(10, 90); u8g2Fonts.print(w.temp °C w.weather); u8g2Fonts.setCursor(10, 110); u8g2Fonts.print(湿度 w.humidity %); display.display(); } void setup() { Serial.begin(115200); connectToWiFi(); WeatherData weather fetchWeather(); TimeData currentTime getNetworkTime(); updateDisplay(weather, currentTime); WiFi.disconnect(true); // 断开Wi-Fi以省电 Serial.println(Going to deep sleep for 30 minutes); esp_sleep_enable_timer_wakeup(30 * 60 * 1000000ULL); // 休眠30分钟 esp_deep_sleep_start(); } void loop() { // 由于进入深度睡眠loop永远不会执行 }5.4 布局优化与图片显示单纯的文字显示可能有些单调。我们可以利用Seeed Arduino Graphics 2库绘制一些简单的图形比如温度计图标、湿度水滴图标或者用线条分割区域。 更高级的玩法是显示图片。墨水屏显示图片需要先将图片如PNG、JPG转换为位图数据。通常的流程是在电脑上用工具如Image2Lcd、GIMP或在线转换器将图片处理成黑白二值位图并调整尺寸为296x152。将生成的位图数据一个字节数组保存为头文件.h嵌入到项目中。在代码中使用display.drawBitmap(x, y, bitmap_data, width, height, color)函数来显示。提示由于墨水屏刷新慢频繁刷新全屏图片用户体验不好。通常的做法是只局部刷新变化的区域如时间数字但局部刷新容易产生残影。对于天气站这种更新不频繁的场景可以接受全屏刷新。在GxEPD2库中可以使用display.displayWindow(x, y, w, h)进行局部刷新但需要根据屏幕型号和驱动支持情况测试效果。6. 深度睡眠与电源管理实战对于电池供电的设备功耗是生命线。ESP32的深度睡眠Deep Sleep模式可以将电流降低到10μA左右而墨水屏在静态显示时不耗电。结合两者可以实现惊人的续航。6.1 深度睡眠的实现在上面的天气站代码中我们已经用到了esp_sleep_enable_timer_wakeup(30 * 60 * 1000000ULL); // 设置定时唤醒单位是微秒 esp_deep_sleep_start(); // 进入深度睡眠这段代码让ESP32休眠30分钟然后由内置的RTC定时器唤醒。唤醒后芯片会经历一次完整的重启从setup()函数开始重新执行。因此所有需要保持的变量状态都会丢失。如果需要保存数据比如上次的天气信息用于对比需要使用RTC内存RTC_DATA_ATTR修饰变量或者SPIFFS/LittleFS文件系统。6.2 功耗实测与优化为了最大化续航需要注意以下几点断开所有外围设备在睡眠前确保将未使用的GPIO引脚设置为输入上拉或下拉避免悬空引脚漏电。如果连接了传感器确保它们也有低功耗模式或直接断电。Wi-Fi和蓝牙在睡眠前调用WiFi.disconnect(true)和btStop()来彻底关闭无线模块。降低CPU频率在唤醒工作期间如果不需高性能可以通过setCpuFrequencyMhz()降低主频来省电。测量电流使用万用表串联在电池回路中分别测量工作状态和深度睡眠状态的电流。理想情况下深度睡眠电流应低于20μA。如果过高需要检查硬件电路或软件配置。6.3 外部唤醒源除了定时唤醒ESP32还支持外部唤醒EXT0/EXT1例如通过按键唤醒。XIAO板上的Boot键也可以配置为外部唤醒源。这对于需要手动刷新的设备很有用。配置代码如下// 将GPIO0Boot键配置为高电平唤醒源 esp_sleep_enable_ext0_wakeup(GPIO_NUM_0, 1); // 1 HIGH这样当设备深度睡眠时按下Boot键使其变为高电平即可唤醒设备。7. 常见问题排查与进阶技巧在实际开发中你肯定会遇到各种各样的问题。这里总结一些我踩过的坑和解决方案。7.1 屏幕不显示或显示乱码检查电源确保USB线连接良好或电池电量充足。墨水屏在刷新时需要较大电流供电不足会导致刷新不完全。检查引脚定义确认代码中的GxEPD2_266_BN和引脚号CS, DC, RST, BUSY与你的板子匹配。对于XIAO ePaper Display Board使用我上面提供的参数一般是正确的。检查库版本确保GxEPD2库是最新版本旧版本可能不支持特定屏幕。查看串口输出在setup()中开启串口调试Serial.begin(115200)并在初始化显示后打印一些状态信息看程序是否卡在display.init()。7.2 刷新速度慢且有严重闪屏这是正常现象电子墨水屏的物理特性决定了其刷新速度远低于LCD。全屏刷新Full Update必然会有黑-白-黑几次闪烁这是为了清除残影获得最清晰的显示效果。尝试局部刷新如果只更新部分区域如数字时钟的最后一个数字可以使用display.displayWindow()进行局部刷新速度更快且无全局闪烁。但局部刷新多次后可能产生残影需要定期进行一次全屏刷新来清除。选择合适的刷新模式GxEPD2库的display.display()默认使用全刷。可以尝试display.display(false)进行局部刷新如果屏幕支持但效果需测试。7.3 Wi-Fi连接不稳定或天气API获取失败检查Wi-Fi凭证确保config.h中的SSID和密码正确且网络可用。增加重试机制在connectToWiFi()函数中加入重试次数限制和延迟。检查API请求使用串口打印出完整的请求URL复制到浏览器中测试看是否能返回正确的JSON数据。检查API密钥是否过期城市ID是否正确。处理HTTPS如果天气API是HTTPS地址ESP32需要正确处理SSL证书。有些免费API提供HTTP接口以简化连接。7.4 电池续航远低于预期测量睡眠电流这是最关键的一步。如果睡眠电流在100μA以上说明有地方在漏电。检查外围电路断开所有Grove传感器测试睡眠电流。检查软件配置确保在深度睡眠前已断开Wi-Fi (WiFi.disconnect(true))、关闭蓝牙 (btStop())、并将未使用的GPIO设置为省电模式。电池本身问题劣质或老化的电池自放电会很严重。7.5 显示中文或自定义字体U8g2库支持显示中文但需要将中文字库集成到程序中。由于中文字库很大会占用大量Flash空间。使用U8g2提供的工具如bdfconv将你需要的汉字生成一个较小的自定义字体文件。将生成的.c文件添加到你的项目中。在代码中声明并使用这个字体u8g2Fonts.setFont(u8g2_font_myFont);。注意这会显著增加程序体积可能需要调整分区表partitions.csv。7.6 利用板载Flash存储资源XIAO ESP32S3有8MB Flash除了存储程序还可以用SPIFFS或LittleFS文件系统来存储网页、配置文件、图片资源等。例如你可以将多张图片素材存放到Flash中运行时按需读取显示无需每次都通过网络下载。#include FS.h #include SPIFFS.h void setup() { if(!SPIFFS.begin(true)){ Serial.println(SPIFFS Mount Failed); return; } // 读取文件 File file SPIFFS.open(/image.bmp, r); // ... 处理文件 }通过结合网络更新和本地存储你可以打造出功能更丰富、响应更快的墨水屏应用。从简单的信息显示器到复杂的交互设备XIAO ePaper Display Board - EN05这个小巧的平台给了我们足够的空间去实现创意。