把天气装进一块墨水屏,桌面上多了个小东西
发布时间:2026/10/31 2:08:37 锦皓数字建站

一块 200 × 200 像素的墨水屏配上 ESP32把图案、天气和时间留在桌面上。本文从电子纸的反射显示、画面保持与刷新节奏讲起拆解 LVGL 到黑白画布的交接并给出工程入口、引脚和复刻时需要修正的代码分清面板的省电潜力与整机功耗。本文资料 · 接着动手开源https://zhcmqtt.top/article/deskscreen鸿尘客 Honchic · 机器人开发 · 2024-02-26一只粉色外壳正面嵌着一块方方正正的墨水屏黑白头像占了大半个画面。它很适合安静地留在桌面上把一张图案、一页天气或一组时间放在那里等人抬头看。里面的 ESP32 和触摸控制器则负责让这张「电子小纸片」换上下一页内容。我喜欢这种体量的项目也喜欢墨水屏给它带来的节奏。天气、日期和时间慢慢更新图案可以停留得久一些外壳又留了一点发挥空间让这张电子小纸片长成自己喜欢的样子。脑洞不大落到电路板上事情倒是一件也不会少。屏幕怎么画手指点哪里天气从哪来几个任务怎样接力都得接上。这篇就顺着这块桌面墨水屏把它拆开聊聊。先看画面怎样留下来、刷新为什么需要等待再看 ESP32 怎样把天气和时间送上去。想复刻的朋友可以从接线和工程入手也能借走一套适合墨水屏的显示和交互思路。小屏幕里装了什么工程使用 PlatformIO 和 Arduino 框架主控配置为esp32dev。界面交给 LVGL触摸由 FT6336U 读取本地温湿度传感器使用 AHT20。部分工程采用的方案当前用途主控ESP32PlatformIO 的esp32dev板型运行界面、外设和网络任务显示200 × 200 像素单色墨水屏显示图案、时间、日期和天气页面触摸FT6336U向 LVGL 提供按下状态和坐标温湿度AHT20读取本地温湿度输出到串口网络WiFi、心知天气、NTP获取城市天气和网络时间界面LVGLSquareLine Studio 导出的 UI组织页面、按钮、图标与点击事件UI 分成主页、爱心页、菜单页、时间页和天气页。启动时加载的是显示图案的HomePage时间和天气各有自己的页面。头像也就有了用武之地桌面信息牌顺手兼任了一个小摆件。触摸驱动会把 FT6336U 的第一个触点交给 LVGL注册的设备类型是LV_INDEV_TYPE_POINTER。UI 事件里已经写了点击切页菜单按钮通往时间、天气和主页返回按钮再回到菜单。这里适合先做清楚、少量的交互。当前接通的是点击事件不能因为用上了触摸芯片就把滑动手势也算进功能清单。模型文件提供了 STL、3MF、STEP 和 SLDPRT 等格式可以作为外壳修改的起点。打印前仍要对照自己选用的屏幕、排线和实际板子检查尺寸尤其是屏幕开口和内部装配空间。硬件材料还要看开源页面中的电气设计结构模型解决的是装配问题。LVGL 画好了墨水屏还要再接一棒一张靠环境光看见的画面常见的黑白电泳电子纸靠电场移动带电的黑白颗粒让不同颜色来到可见的一侧组成文字和图案。它反射环境光显示本身不靠背光亮处能借光看清暗处则需要额外照明。还有一个有意思的特性叫双稳态。画面写好后可以保持面板保持这张图本身不需要持续供电。放一个头像它可以留在那里不必反复重画放天气就得记住留下来的也可能是旧天气。断网或停止更新后字还在信息却会慢慢过时。把界面收进黑白画布用 LVGL 做界面按钮、文字和图片可以放在同一套对象系统里。到了墨水屏这一端画面就要收敛成黑白两色。工程里有一块完整的单色画布大小是200 × 200 ÷ 8 5000 字节每个像素用一位表示Paint负责往这块缓冲区画点。LVGL 自己还有一个可容纳 10 行像素的绘制缓冲区它和这块 5000 字节的单色画布承担不同工作。关键的交接发生在display.cpp的disp_flush()。LVGL 每次给出需要更新的区域回调逐个读取颜色再写进Paint。当前转换规则很直接颜色值等于0xffff的像素画成白色其余画成黑色。灰色也会进黑色那一边。做 UI 时就要考虑这个规则用大一点的文字、明确的黑白边界和适当留白让信息看起来清楚。图标尽量简洁交互安排少量点击切页别指望一张彩色素材放进去后自动得到细腻的灰阶效果。这个回调完成后调用lv_disp_flush_ready()告诉 LVGL像素已经搬完。真正往设备写入画面是另一处调用voidDisplay::refresh(void){epd.SetFrameMemory(paint.GetImage());// 设置电子屏的帧内存epd.DisplayFrame();// 显示图像}刷新要按屏幕自己的节奏来界面画完和屏幕刷新是两个环节。看见标签的内容已经改了屏幕却没动就要顺着这条链检查不能只盯着 UI 对象。当前初始化使用的是epd.Init(lut_full_update)刷新时把整幅Paint画布送进屏幕。驱动库里虽然还有lut_partial_update这条初始化路径没有启用它。LVGL 只重画一个小区域也不能直接算作电子纸已经完成了局部刷新。全刷可能出现黑白闪烁刷新波形也会帮助清理上一帧留下的痕迹。局刷可以减少这种打断但有残影的折中还需要按具体面板的要求周期性全刷。要改刷新方式得把面板支持的波形和驱动一起对上不能只把 UI 的重绘区域缩小。负责刷屏的Device_Screen任务会执行刷新然后阻塞等待任务通知。收到通知后再延时 150 毫秒进入下一轮。UI 事件回调会发出通知天气和时间任务也会在对应页面处于前台时通知它。因此代码里的150不能读成「每 150 毫秒固定刷新一次」。它还要等待通知也要经历实际的屏幕驱动过程。底层驱动会轮询BUSY等待低电平表示屏幕空闲其中的 100 毫秒是轮询间隔也不能当作屏幕的刷新耗时。给墨水屏安排任务得把这几个环节一起看。双稳态带来了省电的空间整机功耗还要另算。当前工程没有调用Epd::Sleep()也没有让 ESP32 进入深度睡眠WiFi 和 MCU 仍在工作。想继续优化可以让面板更新后睡眠唤醒时按要求重新初始化再配合 MCU、网络和数据更新的唤醒调度这些需要一起设计和验证不能仅凭一张能保持的画面就推算续航。天气里的温度和桌上的温度这个项目有两路温度数据很容易聊着聊着就把它们混成一个功能。天气页显示的气温来自网络。天气任务请求心知天气默认城市参数为qingdao拿到天气代码和气温后更新对应的图标、天气文字和温度标签。任务在每轮末尾等待一小时再进行下一轮请求。图标映射包含晴、云、阴、雨、雪几类尚未覆盖的代码会落到默认分支。做自己的天气界面时可以继续补齐映射避免把陌生天气代码一律画成晴天。换城市要改两个地方。一个是System_Interaction.cpp中的请求参数另一个是src/ui/screens/ui_WeatherPage.c中城市标签的静态文字。只改请求页面还写着「青岛」天气就成了人在别处、地名还没搬家。另一条来自 AHT20它读的是传感器所在位置的本地温湿度。当前任务每分钟读取一次随后打印到串口。返回值放在std::mapString, int里浮点读数转成了整数它没有绑定到天气页的温度标签。所以想做室内温湿度卡片还得继续接上 UI 标签按需要保留小数位再安排页面更新和刷屏通知。传感器已经读到数据是这条链的起点。时间页走的是 NTP代码使用ntp.aliyun.com时区偏移设为 UTC8。时间任务每轮先等待 30 秒再尝试读取并更新日期、时间和星期。如果继续调整我会优先考虑分钟级显示少让秒数牵着墨水屏频繁刷新给画面留一点停下来的时间。这里还有两个小地方需要修正。当前顺序是先getLocalTime()后configTime()复刻时应先配置校时再检查时间读取是否成功。星期二的判断字符串写成了Tueaday应改为Tuesday。一个字母放错位置周二就可能在页面上消失时间倒还照常往前走。从工程开始复刻先取源码工程入口在02_firmware/ESP32_Deskgitclone https://github.com/zhanghongchen1213/DIY_DesktopScreen.gitcdDIY_DesktopScreen/02_firmware/ESP32_DeskVS Code 安装 PlatformIO 后可以直接打开这个工程目录。platformio.ini的环境名叫denky32实际板型配置是esp32dev构建时使用环境名别把两者当成同一个参数。依赖声明如下[env:denky32] platform espressif32 board esp32dev framework arduino lib_deps knolleary/PubSubClient^2.8 bblanchon/ArduinoJson^7.0.2 adafruit/Adafruit AHTX0^2.0.5 lvgl/lvgl^8.3.4^表示一个允许更新的版本范围这份配置没有把依赖锁死到单个版本。UI 文件的生成注释写的是 SquareLine Studio 1.3.4 和 LVGL 8.2.0而依赖声明使用 LVGL^8.3.4。排查兼容问题时要看本机实际安装的版本。UI 代码还明确检查两项配置。在实际使用的lv_conf.h中确认#defineLV_COLOR_DEPTH16#defineLV_COLOR_16_SWAP0墨水屏最终显示黑白并不等于 LVGL 这端也应改成 1 位色深。这里的 UI 和颜色转换按 16 位输入来写擅自改掉代码里的检查就会阻止编译。还要确认 LVGL 的时基来源。仓库没有提供可见的lv_conf.h、lv_tick_inc()调用或LV_TICK_CUSTOM配置需要检查实际编译环境是否已提供必要时补上。主循环里的lv_task_handler()和delay(20)并不能代替给 LVGL 提供经过的时间。几个任务一起运行时交接也要加一道约束。当前天气和时间任务直接修改 LVGL 控件主循环同时运行它的处理函数尚未看到共享互斥可以把 UI 更新统一放进一个任务或者按 LVGL 的要求用互斥保护。单色画布也存在一边写入、一边送往屏幕的路径读写同步需要一起联调。界面代码能画出一页和并发运行时始终画对是两件要分别接住的事。接线可以按当前固件定义逐项核对模块信号ESP32 GPIO墨水屏CLK26墨水屏MOSI / DIN25墨水屏BUSY13墨水屏RST12墨水屏DC14墨水屏CS27AHT20SDA21AHT20SCL22FT6336USDA33FT6336USCL32FT6336URST5FT6336UINT4这张表对应固件 GPIO模块供电、接口方向和排线连接仍以所用硬件的原理图为准。驱动头文件还留着早期 ESP8266、NodeMCU 的注释接 ESP32 时应按这些宏定义和 SPI 初始化参数核对别照旧注释接过去。联调建议按这个顺序推进在src/Application/System_Config.cpp中设置自己的 WiFi 信息在src/network.cpp中设置自己的天气服务参数。不要沿用仓库中的账号配置。修正天气请求的Host字段。当前代码拼入的是端口80这里应使用主机名api.seniverse.com同时给连接失败路径补上明确的返回结果并检查 HTTP 状态和 JSON 解析结果再更新 UI。修正时间初始化顺序和星期二拼写核对 LVGL 配置、时基、任务间同步与引脚然后运行构建命令。构建通过后连接对应硬件再上传固件并打开串口。串口初始化速率是 9600要手动选择一致的监视速率。依次检查图案显示、触摸坐标、点击切页、天气请求、时间读取和 AHT20 输出。每次先接通一条链出现问题会更容易找到位置。pio run-edenky32 pio run-edenky32-tupload pio device monitor-b9600有个启动顺序值得提前记住。System_Resource_Init()先连接 WiFi并一直等待连接成功之后才初始化串口和屏幕等外设。如果上电后既没有画面也没有预期的串口日志先查 WiFi 配置和网络程序可能还没走到屏幕初始化。让串口更早启动、给联网增加超时都是后续可以改进的地方。准备把它长期放在桌面上这些失败路径和异常提示也应一并补好。留一点空位给下一个脑洞代码里已经有 MQTT 类可以看到连接、订阅、发布和 JSON 打包方法。不过默认的连接与订阅调用被注释了AHT20 任务里的发布调用也被注释了回调尚未注册客户端loop()也没有接到运行循环中。想让它接入自己的消息服务可以从这里继续做把连接、回调、客户端处理和断线重连补齐再决定哪些数据应该更新页面。当前工程还不能直接算作已经完成智能家居联动。我更愿意把这块屏幕看成一个可以继续动手的小底座。先让天气与时间各守住一页再接本地温湿度或者换上自己画的图案。功能每多一个就把数据、交互和刷新之间的交接再理顺一点也问问这条信息是否值得让屏幕再刷一次。粉色外壳里的黑白头像依然可以留着。天气该更新时换一页其他时候就让画面安静地待在那里。桌面上这个小东西的意思也在这里实用之外还有一点属于自己的样子。鸿尘客 Honchic · 把脑洞焊到电路板上本文资料 · 接着动手开源https://zhcmqtt.top/article/deskscreen
锦
锦皓数字建站
深耕本土企业品牌数字化升级,专注原创端正雅致商务官网,从视觉设计到稳定运维全程保驾护航。