ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

树莓派驱动7.5英寸三色墨水屏:从原理到天气站实战

2026/8/2 9:21:34 拓冰建站 浏览量
树莓派驱动7.5英寸三色墨水屏:从原理到天气站实战 1. 项目缘起为什么选择这款墨水屏模组最近在折腾一个需要低功耗、常亮显示的小项目比如一个桌面天气站或者一个智能家居的控制面板。这类项目对显示设备有几个核心要求一是功耗要足够低最好能实现“零功耗”显示二是要能在阳光下清晰阅读不反光三是希望显示效果能接近纸张长时间观看不伤眼。翻来覆去对比了各种屏幕最终把目光锁定在了电子墨水屏也就是我们常说的E-Ink屏上。在众多E-Ink屏模组中Waveshare微雪的7.5英寸E-Paper HAT (B) 型号引起了我的注意。选择它主要是基于几个非常实际的考量。首先7.5英寸这个尺寸对于显示信息量适中的仪表盘来说刚刚好既不会太小看不清也不会太大导致项目体积臃肿。其次它是一款“HAT”Hardware Attached on Top专为树莓派设计通过排针直接插在树莓派的GPIO接口上省去了复杂的飞线物理连接极其简洁可靠。最后也是最重要的一点这款屏幕是黑、白、红三色显示。别小看这个红色在显示警告信息、关键数据如温度超标、或者做简单的UI区分时它能极大地提升信息的可读性和界面的友好度比单纯的黑白屏实用太多。然而当我拿到这块屏幕和它的手册时发现官方提供的手册更偏向于引脚定义和基础驱动的罗列像一个精简的数据表。对于想快速上手、并希望深入理解其工作原理、避开常见坑点的开发者来说信息量远远不够。比如驱动波形LUT到底是什么为什么刷新一次屏幕需要那么久如何优化刷新策略来避免恼人的全屏闪烁这些实战中必然会遇到的问题手册里往往一笔带过或者根本没有提及。因此我决定结合自己从零开始摸索这块屏幕的完整过程写一份更贴近开发者实战视角的“手册补全指南”。这份指南不会重复官方手册已有的引脚接线图而是会深入驱动原理拆解刷新机制分享从环境搭建、驱动调试到性能优化的全链路经验特别是那些容易让人卡住好几个小时的“坑”。无论你是想用它做电子相框、智能标签还是工业看板希望这篇近万字的详实记录都能让你少走弯路。2. 核心硬件解析与驱动原理探秘在动手接线之前我们有必要先搞懂手里这块屏幕到底是怎么工作的。这不仅能帮助我们在后续编程时理解每一个API调用的意义更能在出现显示异常时快速定位问题是出在硬件、驱动还是我们的代码逻辑上。2.1 电子墨水屏E-Paper显示原理简述电子墨水屏的核心技术是电泳显示技术。你可以把它想象成无数个微小的“胶囊”每个胶囊里充满了带负电的黑色粒子和带正电的白色粒子它们悬浮在透明的液体中。这些胶囊被夹在两片电极之间。当我们在电极上施加一个电压场时带电的粒子就会在电场力的作用下移动。例如如果上电极施加负电压带正电的白色粒子就会被吸引到屏幕顶部我们就会看到这个像素点显示为白色反之如果上电极施加正电压黑色粒子被吸引上来像素点就显示为黑色。对于三色屏如我们手上的这款原理类似但增加了红色粒子或红色滤光片层通过更复杂的电压序列来控制红、黑、白粒子的位置。关键特性与误区澄清双稳态特性这是E-Ink屏最迷人的地方。一旦粒子移动到目标位置即使撤掉电压它们也会因为介电泳力和范德华力而保持原位。这意味着屏幕在静态显示时不消耗任何电量只有刷新图像时才需要用电。我们常说的“零功耗显示”指的就是这个状态。刷新慢与全屏闪烁由于带电粒子的物理移动需要时间所以E-Ink屏的刷新速度远低于LCD或OLED通常需要几百毫秒到几秒。全屏刷新时为了清除上一帧图像的“残影”驱动芯片会先让屏幕快速在全黑、全白之间闪烁几次这个过程就是应用完整的驱动波形这导致了肉眼可见的闪烁。这是其物理特性决定的无法根除但可以通过优化刷新策略来改善体验。驱动波形LUT这是E-Ink驱动的灵魂。LUTLook-Up Table查找表定义了为了从一种颜色切换到另一种颜色需要施加怎样的电压序列电压值、持续时间。例如从白到黑、从黑到白、从白到红所需的电压波形都是不同的。官方提供的驱动代码里已经内置了针对这块屏幕优化好的LUT我们通常不需要修改。但理解它的存在就能明白为什么epd.init()、epd.Clear()等操作是必须的——它们就是在向屏幕控制器写入这些关键的波形数据。2.2 7.5inch E-Paper HAT (B) 硬件拆解这款模组不仅仅是一块屏幕它是一个高度集成的系统屏幕本体7.5英寸分辨率640x384像素。支持黑、白、红三色。驱动芯片通常是一颗专用的E-Paper控制器如IL3895或兼容芯片它负责接收来自树莓派的数据并按照LUT生成精确的时序电压信号来驱动屏幕。HAT接口板将驱动芯片、必要的电源电路、电平转换电路树莓派是3.3V逻辑屏幕驱动可能需要其他电压以及GPIO扩展接口集成在一块小板上。它通过40Pin的排针与树莓派直接对接。引脚连接的精简与可靠 正因为是HAT设计我们不需要关心具体的SPI引脚是哪个。只需要确保物理对齐将HAT板稳稳地插在树莓派的GPIO排针上即可。电源3.3V, 5V, GND、SPI接口MOSI, MISO, SCLK、控制引脚CS, DC, RST, BUSY都通过这40个引脚完成了连接。这种设计最大程度避免了接错线的风险是快速原型开发的利器。2.3 与树莓派的通信SPI协议屏幕与树莓派之间通过SPISerial Peripheral Interface串行外设接口通信。这是一种高速、全双工的同步通信总线。在我们的场景里树莓派作为SPI主设备Master负责发起通信并控制时钟。屏幕驱动芯片作为SPI从设备Slave接收指令和数据。关键信号线SCLK时钟线由主设备产生数据在时钟边沿被采样。MOSI主设备输出从设备输入。树莓派通过这条线发送命令和数据给屏幕。MISO主设备输入从设备输出。屏幕可以通过这条线返回状态数据虽然在本屏幕驱动中可能未使用但引脚保留。CS片选信号。当树莓派需要与屏幕通信时将此引脚拉低激活。这允许多个SPI设备共享同一组MOSI/MISO/SCLK线。DC数据/命令选择引脚。这是一个非常重要的引脚。当DC为低电平时SPI总线上的数据被解释为命令如“开始刷新”、“进入睡眠模式”当DC为高电平时数据被解释为要显示的图像数据。驱动库中EPD.send_command()和EPD.send_data()函数的核心区别就在于操作这个引脚。RST复位引脚。拉低此引脚可以强制屏幕控制器硬件复位用于从异常状态恢复。BUSY忙状态引脚。这是一个输入引脚用于树莓派查询屏幕状态。当屏幕正在执行刷新、初始化等耗时操作时此引脚会被驱动芯片拉高或拉低取决于具体芯片告诉主设备“我正在忙别打扰”。驱动库中大量使用EPD.ReadBusy()函数来等待操作完成。理解这些引脚的功能对于后续调试至关重要。例如如果屏幕完全无反应首先应该检查RST和CS引脚的电平状态如果图像数据发送了但没显示可以检查DC引脚在发送数据时的时序是否正确。3. 从零开始的环境搭建与驱动测试理论了解得差不多了现在让我们动手让屏幕亮起来或者说显示起来。这个过程主要包括树莓派系统配置、SPI接口启用、驱动库获取与安装以及运行一个最简单的测试程序。3.1 树莓派系统准备与SPI使能假设你已经在树莓派上安装好了Raspbian或Bullseye等基于Debian的系统。连接硬件在树莓派完全断电的情况下将7.5英寸E-Paper HAT (B) 对准树莓派的40Pin GPIO排针轻轻按下确保接触牢固。然后接通树莓派电源。启用SPI接口树莓派的SPI接口默认是关闭的需要手动开启。sudo raspi-config在配置工具中依次选择Interface Options-SPI-Yes来启用SPI。完成后退出并重启树莓派。sudo reboot验证SPI启用重启后可以通过以下命令检查SPI设备是否已被识别。如果看到类似/dev/spidev0.0和/dev/spidev0.1的设备文件说明SPI启用成功。3.2 获取并安装驱动库Waveshare通常会在产品Wiki页面上提供Python驱动库。我们可以直接使用git克隆到本地。# 更新软件包列表并安装必要的依赖 sudo apt update sudo apt install python3-pip python3-pil python3-numpy git # 克隆驱动库仓库仓库地址请以Waveshare官方最新为准此处为示例 git clone https://github.com/waveshare/e-Paper.git # 进入对应屏幕尺寸的驱动目录 cd e-Paper/raspberrypi\jetson_nano/python/examples/在这个examples目录下你应该能找到类似epd_7in5b_HD.py这样的驱动文件以及一些测试例程如epd_7in5b_test.py。重要步骤安装本地库。为了让Python在任何位置都能导入这个驱动模块我们需要将其安装到Python的站点包目录。通常驱动库的根目录会有一个setup.py文件。# 返回到驱动库的python目录 cd ../.. # 使用pip进行安装 sudo pip3 install .如果没有setup.py你也可以手动将lib目录下的驱动文件如waveshare_epd复制到Python的路径或者更简单地在你的项目代码中通过添加系统路径的方式引入。import sys sys.path.append(/home/pi/e-Paper/raspberrypijetson_nano/python/lib) from waveshare_epd import epd7in5b_HD3.3 运行基础测试与常见问题排查进入examples目录运行官方提供的测试程序。cd examples sudo python3 epd_7in5b_test.py注意许多E-Ink屏驱动需要直接操作硬件SPI和GPIO因此经常需要使用sudo以root权限运行。理想情况屏幕会开始刷新你可能看到它先全屏闪烁几次清屏然后显示一些测试图案、文字和图片最后可能进入睡眠模式。常见问题与排查屏幕无任何反应程序报错或立即退出检查电源确保树莓派电源适配器功率足够推荐5V/3A。E-Ink屏在刷新瞬间电流较大供电不足会导致树莓派重启或屏幕无法驱动。检查SPI启用再次确认raspi-config中SPI已启用并重启。检查物理连接断电后重新插拔HAT板确保所有引脚接触良好没有弯曲或错位。检查驱动库版本确认下载的驱动库是否明确支持你的屏幕型号7.5inch e-Paper HAT (B) V2?。不同版本屏幕的驱动芯片可能不同。程序运行但屏幕显示乱码、残影或局部刷新不正常初始化与清屏顺序确保代码中严格按照epd.init()-epd.Clear()- 发送显示数据 -epd.display()-epd.sleep()的顺序操作。缺少Clear()可能导致残影。BUSY信号等待检查驱动代码中在epd.display()等关键操作后是否有足够的延时或ReadBusy()等待。如果主程序跑得太快在屏幕还没刷新完时就发送新数据会导致混乱。图像数据格式确认你准备发送的图像数据格式与驱动要求一致。通常是1位位图黑白1bit对于三色屏可能需要特殊的颜色编码如0黑1白2红。官方例程中的Image.open().convert(1)是关键。刷新过程异常缓慢或卡在某个阶段检查BUSY引脚用万用表或逻辑分析仪检查BUSY引脚在刷新时的电平变化。如果它一直处于“忙”状态可能是屏幕硬件故障或驱动芯片死锁。尝试在代码中增加一个带超时的ReadBusy()函数避免无限等待。def wait_until_idle(self, timeout20): start time.time() while self.digital_read(self.busy_pin) 1: # 假设忙时为高电平 if time.time() - start timeout: raise RuntimeError(EPD Busy timeout!) time.sleep(0.01)SPI速度有些驱动库允许设置SPI速度。过高的速度可能导致数据传输出错过慢则拉长刷新时间。可以在初始化SPI时尝试调整spi_speed_hz参数如果有的话。第一次成功让屏幕显示出预定的内容是一个重要的里程碑。它验证了硬件连接、基础驱动和环境都是正确的。接下来我们就可以深入代码打造自己的应用了。4. 驱动代码深度剖析与自定义显示实战官方例程能跑通只是第一步。要真正驾驭这块屏幕我们必须深入驱动库的内部理解其关键函数并学会如何准备和发送我们自己的图像数据。4.1 关键驱动类与方法解读以典型的epd7in5b_HD类为例我们剖析几个核心方法__init__(self)构造函数。初始化GPIO引脚模式设置RST、DC、CS、BUSY为输入或输出并创建SPI对象。这里决定了使用哪个SPI设备/dev/spidev0.0以及SPI的模式和速度。init(self)初始化屏幕。这是最关键的操作之一。它会拉低RST引脚再拉高进行硬件复位。通过send_command()发送一系列初始化指令序列到屏幕驱动芯片。最关键的是它会写入驱动波形LUT。这个波形数据决定了屏幕如何从一种颜色转换到另一种颜色直接影响刷新速度和效果。此函数必须在每次唤醒屏幕后调用。Clear(self)清屏。实际上也是发送一系列命令驱动屏幕执行一个“全白”或“全黑”的刷新周期以消除残影。它内部会调用display()函数。display(self, imageblack, imagered)执行显示。这是另一个核心函数。它接收两个图像缓冲区imageblack和imagered。注意对于三色屏通常不是直接发送彩色图像而是将目标图像分解为两个单色位图一个表示黑色部分一个表示红色部分。函数内部会发送“开始写RAM”的命令。将imageblack缓冲区数据通过SPI发送出去。可能再发送另一个命令接着发送imagered缓冲区数据。最后发送“刷新显示”的命令并调用ReadBusy()等待刷新完成。sleep(self)使屏幕进入深度睡眠模式。在这个模式下屏幕驱动芯片大部分电路关闭功耗降至极低约几微安。在显示完成后务必调用此函数这是实现低功耗的关键。下次显示前必须再次调用init()来唤醒屏幕。send_command(self, command)和send_data(self, data)底层通信函数。前者在发送前将DC引脚拉低命令模式后者拉高数据模式。然后通过SPI的xfer方法发送数据。4.2 准备显示数据从图片到像素缓冲区我们如何将一张JPG或PNG图片显示到屏幕上这个过程称为“图像光栅化”。步骤一图像预处理屏幕分辨率是640x384颜色是黑、白、红。我们的源图可能是任何尺寸和颜色。from PIL import Image import numpy as np # 1. 打开图片并转换为屏幕分辨率 image Image.open(your_image.jpg) # 使用Image.LANCZOS进行高质量缩放 image image.resize((epd.width, epd.height), Image.LANCZOS) # 2. 转换为RGB模式便于后续颜色分离 image_rgb image.convert(RGB)步骤二颜色分离与二值化这是三色屏显示的核心逻辑。我们需要判断每个像素最终应该是黑、白还是红并分别生成对应的黑白图层和红色图层。# 创建两个单色1位图像缓冲区初始为白色像素值1 # 注意有些驱动库约定0为黑/有色1为白请根据实际驱动库调整 buffer_black Image.new(1, (epd.width, epd.height), 1) # 1 表示白色 buffer_red Image.new(1, (epd.width, epd.height), 1) # 1 表示白色即无红色 # 获取像素数据 pixels image_rgb.load() for y in range(epd.height): for x in range(epd.width): r, g, b pixels[x, y] # 简单的颜色判断逻辑示例偏红色调且饱和度高的像素设为红色 # 这是一个非常基础的算法实际应用可能需要更复杂的色彩空间转换和阈值判断 if r 160 and g 100 and b 100: # 简单红色判断 buffer_red.putpixel((x, y), 0) # 0 表示这个位置需要显示红色 buffer_black.putpixel((x, y), 1) # 黑色图层对应位置为白不显示黑 elif (r g b) 384: # 暗色区域判断为黑色阈值可调 buffer_black.putpixel((x, y), 0) # 0 表示这个位置需要显示黑色 buffer_red.putpixel((x, y), 1) # 红色图层对应位置为白 else: # 其他情况为白色 buffer_black.putpixel((x, y), 1) buffer_red.putpixel((x, y), 1) # 步骤三将PIL图像对象转换为驱动库需要的缓冲区格式 # 通常驱动库的display函数接受一个字节数组其中每个位代表一个像素 # PIL的tobytes()方法可以做到这一点但要注意位顺序MSB/LSB black_buffer buffer_black.tobytes() red_buffer buffer_red.tobytes()步骤四调用显示函数epd.init() epd.display(black_buffer, red_buffer) # 注意参数顺序根据库定义可能是(black, red)或(imageblack, imagered) epd.sleep()这个颜色分离算法if r 160 ...非常初级对于复杂图片效果可能不好。在实际项目中你可能需要用到更高级的方法如转换到HSV色彩空间根据色调H和饱和度S来判断红色根据明度V来判断黑白。4.3 绘制文字与图形除了显示图片我们经常需要动态绘制文字和简单图形如线条、矩形。我们可以使用PIL的ImageDraw模块在内存中创建一个画布先绘制好再按上述流程处理。from PIL import Image, ImageDraw, ImageFont # 创建一个新的RGB图像作为画布 canvas Image.new(RGB, (epd.width, epd.height), (255, 255, 255)) # 白色背景 draw ImageDraw.Draw(canvas) # 加载字体确保树莓派上安装了该字体 try: font ImageFont.truetype(/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf, 24) except IOError: font ImageFont.load_default() # 回退到默认字体 # 绘制黑色文字 draw.text((10, 10), Hello, E-Paper!, fontfont, fill(0, 0, 0)) # 绘制红色矩形 draw.rectangle([(50, 50), (150, 100)], outline(255, 0, 0), fill(255, 0, 0)) # 绘制黑色线条 draw.line([(10, 150), (200, 150)], fill(0, 0, 0), width3) # 此时canvas就是一张包含我们绘制内容的RGB图像 # 后续步骤与“图片显示”完全一样缩放如果需要、颜色分离、转换缓冲区、显示。 # 注意因为我们创建画布时就是屏幕分辨率所以可以跳过resize步骤。 image_rgb canvas # 直接使用canvas作为RGB图像源 # ... 后续颜色分离、二值化、显示流程同上 ...通过结合PIL的强大绘图功能我们就能在墨水屏上实现各种信息展示界面。5. 性能优化与高级应用技巧当基础显示功能实现后我们自然会追求更快的刷新速度、更低的功耗以及更稳定的长期运行。这部分分享的是一些在实战中积累的进阶经验。5.1 刷新策略优化告别全屏闪烁全屏刷新epd.Clear()epd.display()带来的闪烁是无法避免的但我们可以通过策略减少其发生频率。局部刷新Partial Refresh这是最重要的优化手段。有些E-Ink屏幕驱动芯片支持局部刷新。其原理是只更新屏幕上发生变化的那一部分区域而不是整个屏幕。这能极大缩短刷新时间可能从2秒降到300毫秒并完全避免全屏闪烁。你需要仔细查阅屏幕的详细数据手册或驱动库源码确认你的型号是否支持局部刷新以及对应的命令序列是什么。如果支持通常会有类似epd.display_partial()的函数。使用局部刷新时需要指定需要更新的矩形区域坐标。差异化刷新Differential Refresh这是一种软件策略。在内存中保留上一帧的完整图像缓冲区。当需要更新时不直接调用Clear()而是先计算出当前帧与上一帧的像素差异diff然后只对发生了变化的像素区域进行全刷新。对于变化不大的界面如更新一个数字这可以模拟出局部刷新的效果。实现起来稍复杂但能显著提升视觉体验。刷新节奏控制对于信息看板类应用不要一有数据变化就立即刷新屏幕。可以设置一个最小刷新间隔例如10秒或者将多次小的数据更新累积起来一次性刷新。这既减少了闪烁也降低了功耗和屏幕损耗E-Ink屏有刷新寿命通常几十万次。5.2 低功耗设计与睡眠管理E-Ink屏的功耗优势只有在正确管理下才能发挥。务必调用sleep()在每次完成显示操作后毫无例外地调用epd.sleep()。这是将屏幕驱动芯片功耗从毫安级降至微安级的关键。忘记这一步屏幕可能持续消耗数毫安电流。树莓派侧的功耗管理屏幕睡了树莓派本身还在耗电。对于电池供电的项目需要考虑使用树莓派Zero W等低功耗型号。在刷新间隔期间让树莓派也进入休眠sudo halt或深度睡眠模式通过外部RTC定时器或传感器中断唤醒。但这需要额外的硬件和更复杂的系统设计。最简单的优化关闭树莓派上未使用的接口HDMI, USB控制器, WiFi/蓝牙等降低CPU频率和电压。电源选择使用效率高的DC-DC降压模块为整个系统供电避免线性稳压器带来的额外损耗。5.3 长期运行稳定性与可靠性一个需要7x24小时运行的项目稳定性至关重要。异常处理与恢复在你的主循环中对epd.init(),epd.display()等操作进行try...except捕获。一旦发生超时或SPI通信错误除了记录日志可以尝试执行一次硬件复位控制RST引脚。重新初始化SPI总线。如果多次失败则安全地关闭屏幕并重启树莓派服务或整个系统。def safe_display(epd, black_buf, red_buf, retries3): for i in range(retries): try: epd.init() epd.display(black_buf, red_buf) epd.sleep() return True except (IOError, RuntimeError) as e: print(fDisplay attempt {i1} failed: {e}) if i retries - 1: # 最后一次尝试进行硬件复位 epd.Reset() # 假设驱动库提供了复位方法 time.sleep(0.5) else: time.sleep(1) return False防止图像残影Ghosting长期显示静态图像后即使刷新为新图也可能看到旧图的淡淡痕迹。这是E-Ink的物理特性。对策定期全刷即使内容无变化每24小时或若干次局部刷新后强制进行一次全屏刷新Clear()。像素反转在显示纯文本时可以偶尔将背景色和前景色对调显示一次如黑底白字变成白底黑字然后再改回来利用全刷清除残影。文件系统与日志将需要显示的图片、字体等资源放在只读文件系统如/boot或内存文件系统tmpfs中避免SD卡因频繁读写而损坏。同时将程序运行日志写入到内存文件系统或远程服务器减少对SD卡的写入。6. 项目实战构建一个桌面天气信息站让我们将以上所有知识融会贯通实现一个具体的项目一个基于树莓派和7.5寸墨水屏的桌面天气站。它将定时从网络API获取天气信息并以美观、低功耗的方式显示。6.1 系统架构设计硬件树莓派任何型号推荐3B或4B用于网络稳定性 7.5inch E-Paper HAT (B)。软件栈操作系统Raspberry Pi OS Lite无桌面环境更省资源。编程语言Python 3。核心库waveshare_epd(屏幕驱动)PIL(图像处理)requests(网络请求)python-dotenv(管理配置如API密钥)。数据流主程序作为一个系统服务systemd在后台运行。每30分钟通过requests库调用公共天气API如OpenWeatherMap。解析返回的JSON数据提取温度、湿度、天气状况晴、雨等、预报等信息。使用PIL在内存中绘制包含这些信息的图片。设计布局将关键数据用大字体突出显示天气图标用红色点缀。应用颜色分离算法生成黑白和红色缓冲区。唤醒屏幕执行显示操作然后让屏幕进入睡眠。树莓派本身在间隔期间可以保持低负载运行。6.2 核心代码结构示例# weather_station.py import time import requests from PIL import Image, ImageDraw, ImageFont from waveshare_epd import epd7in5b_HD import logging from datetime import datetime # 配置日志和API logging.basicConfig(levellogging.INFO) API_KEY your_api_key CITY Shanghai API_URL fhttp://api.openweathermap.org/data/2.5/weather?q{CITY}appid{API_KEY}unitsmetric def fetch_weather(): 获取天气数据 try: resp requests.get(API_URL, timeout10) resp.raise_for_status() data resp.json() return { temp: data[main][temp], humidity: data[main][humidity], desc: data[weather][0][description], icon: data[weather][0][icon] # 用于选择图标 } except Exception as e: logging.error(fFailed to fetch weather: {e}) return None def create_weather_image(weather_data, epd): 根据天气数据创建显示图像 # 创建画布 image Image.new(RGB, (epd.width, epd.height), (255, 255, 255)) draw ImageDraw.Draw(image) # 加载字体 font_large ImageFont.truetype(DejaVuSans-Bold.ttf, 80) font_medium ImageFont.truetype(DejaVuSans.ttf, 40) font_small ImageFont.truetype(DejaVuSans.ttf, 24) # 绘制内容 # 1. 城市和日期黑色 draw.text((20, 20), f{CITY}, fontfont_medium, fill(0,0,0)) draw.text((20, 70), datetime.now().strftime(%Y-%m-%d %H:%M), fontfont_small, fill(0,0,0)) if weather_data: # 2. 温度特大号黑色 temp_text f{weather_data[temp]:.1f}°C draw.text((epd.width//2, 150), temp_text, fontfont_large, fill(0,0,0), anchormm) # 居中 # 3. 湿度黑色 draw.text((50, 300), fHumidity: {weather_data[humidity]}%, fontfont_medium, fill(0,0,0)) # 4. 天气描述和图标红色 # 这里简化处理根据icon代码绘制一个简单的红色图标例如太阳、云朵的轮廓 draw.ellipse([(500, 100), (600, 200)], outline(255,0,0), width5) # 简单画个红圈代表太阳 draw.text((500, 220), weather_data[desc], fontfont_small, fill(255,0,0)) else: # 获取数据失败时显示错误信息红色 draw.text((epd.width//2, epd.height//2), Weather Data Error, fontfont_medium, fill(255,0,0), anchormm) return image def image_to_epd_buffers(image, epd): 将PIL图像转换为屏幕需要的黑白、红缓冲区 # ... 此处接入前面章节的颜色分离算法 ... # 简化示例将所有非白色像素都视为黑色实际项目需要更精细的分离 image_bw image.convert(1, ditherImage.NONE) # 直接二值化 # 红色图层我们可以简单地将原图中红色分量较高的区域标记出来 # 这里使用一个简化的阈值方法实际应用需要优化 image_rgb image.convert(RGB) pixels image_rgb.load() buffer_red Image.new(1, (epd.width, epd.height), 1) for y in range(epd.height): for x in range(epd.width): r, g, b pixels[x, y] if r 200 and g 100 and b 100: # 简单红色判断 buffer_red.putpixel((x, y), 0) return image_bw.tobytes(), buffer_red.tobytes() def main(): epd epd7in5b_HD.EPD() logging.info(Weather Station Started) last_refresh_time 0 refresh_interval 1800 # 30分钟 while True: current_time time.time() if current_time - last_refresh_time refresh_interval: try: # 1. 获取数据 weather fetch_weather() # 2. 生成图像 pil_image create_weather_image(weather, epd) # 3. 转换缓冲区 black_buf, red_buf image_to_epd_buffers(pil_image, epd) # 4. 显示 epd.init() # 为了减少闪烁这里可以尝试先不清屏直接显示。但可能残留残影。 # 更好的做法是仅在每天第一次刷新或每N次刷新时执行一次全清。 if datetime.now().hour 0: # 每天凌晨全清一次 epd.Clear() epd.display(black_buf, red_buf) epd.sleep() logging.info(fWeather displayed at {datetime.now()}) last_refresh_time current_time except Exception as e: logging.error(fDisplay cycle failed: {e}) # 等待一段时间后重试 time.sleep(60) time.sleep(60) # 每分钟检查一次是否到达刷新时间 if __name__ __main__: main()6.3 部署为系统服务为了让天气站在树莓派启动后自动运行并能在后台稳定工作我们将其配置为systemd服务。创建服务文件sudo nano /etc/systemd/system/weather-station.service写入以下内容根据你的实际路径修改[Unit] DescriptionWeather Station E-Paper Display Aftermulti-user.target [Service] Typesimple Userpi WorkingDirectory/home/pi/weather_station ExecStart/usr/bin/python3 /home/pi/weather_station/weather_station.py Restarton-failure RestartSec10 [Install] WantedBymulti-user.target启用并启动服务sudo systemctl daemon-reload sudo systemctl enable weather-station.service sudo systemctl start weather-station.service检查服务状态和日志sudo systemctl status weather-station.service sudo journalctl -u weather-station.service -f通过这个完整的项目你将不仅掌握了驱动一块墨水屏更学会了如何将其融入一个实用的、可长期运行的嵌入式应用系统中。从硬件驱动到软件架构从图像处理到系统部署这套组合拳打下来你对E-Ink屏项目的开发就算真正入门了。