ARTICLE DETAIL

建站实战干货

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

树莓派驱动10.3英寸墨水屏:从硬件连接到动态信息看板实战

2026/8/2 14:36:42 拓冰建站 浏览量
树莓派驱动10.3英寸墨水屏:从硬件连接到动态信息看板实战 1. 项目缘起为什么选择墨水屏最近在捣鼓一个需要长时间显示固定信息的小项目比如一个放在桌面的天气预报站或者一个展示日程的电子日历。这类应用有个共同点信息更新不频繁但需要一直亮着。如果用传统的LCD或者OLED屏幕功耗就成了大问题而且长时间显示静态画面还可能有烧屏的风险。这时候墨水屏E-Paper就成了一个近乎完美的选择。墨水屏的原理和我们看的纸质书很像它本身不发光依靠反射环境光来显示所以看起来非常柔和不伤眼。更重要的是它只在刷新画面时才耗电一旦画面显示完成即使断电图像也能保持住实现真正的“零功耗”显示。这特性让它特别适合那些需要“常亮”但又对功耗敏感的场景比如电子价签、电子书阅读器以及我们DIY爱好者喜欢的各种信息看板。在众多尺寸和规格中10.3英寸这个尺寸非常讨喜。它足够大可以清晰地展示多行文字、图表甚至简单的图片但又不像更大的屏幕那样昂贵和笨重非常适合作为桌面摆件或者嵌入式设备的显示终端。我手头这块“10.3inch e-Paper HAT”就是一个典型的代表它是一块可以直接插在树莓派GPIO接口上的扩展板HAT省去了复杂的接线让开发者能快速上手。2. 开箱与硬件初探这块屏到底什么样拿到这块10.3英寸的墨水屏HAT第一感觉就是“大而薄”。屏幕本身是黑白两色显示后期也有黑白红三色版本分辨率是1872x1404这个分辨率在10.3英寸的尺寸下像素密度PPI相当不错显示文字非常锐利完全没有颗粒感。作为一块HATHardware Attached on Top它的设计初衷就是为树莓派特别是树莓派3B/3B/4B/Zero等40针GPIO的型号量身定做的。翻到背面可以看到标准的40针GPIO排母对准树莓派的排针插上去就行物理连接非常简单。板上除了屏幕驱动电路通常还会集成必要的电源管理芯片、电平转换电路以及一个TF卡槽用于存储字库或图片资源。有些版本还会预留一个PH2.0的接口用于连接外置的电池这对于制作完全无线的移动设备非常有用。这里需要特别注意一点墨水屏的驱动电压通常比较高。普通LCD屏可能是3.3V或5V但墨水屏的驱动电压可能高达十几甚至二十伏。所以这块HAT板上一定会有一个升压电路Boost Converter将树莓派提供的5V或3.3V电源升压到屏幕所需的高压。这意味着虽然屏幕本身刷新时不怎么耗电但驱动板上的升压电路在工作时是有一定功耗的。在评估整体项目的功耗时这一点不能忽略。另一个硬件细节是屏幕的刷新方式。墨水屏刷新时你会看到屏幕先快速闪烁几次全黑-全白-全黑然后才显示出最终图像。这个过程是正常的是墨水屏粒子在电压驱动下重新排列的物理过程。不同的驱动芯片和波形文件Waveform会决定刷新速度、残影控制以及是否支持局部刷新。这块10.3寸屏通常支持全局刷新和局部刷新两种模式。全局刷新彻底无残影但速度慢可能需要2-3秒局部刷新快可能几百毫秒但有轻微残影适合频繁更新部分内容比如更新一个数字。3. 驱动与环境搭建让树莓派认识这块屏硬件连接好后下一步就是让树莓派系统能驱动这块屏幕。由于墨水屏不是标准显示设备如HDMI我们需要通过GPIO来模拟它的通信时序或者使用厂商提供的专用驱动库。最通用的方法是使用wiringPi或RPi.GPIO这类库来操作GPIO然后按照屏幕驱动芯片常见的有SSD1675、IL0373等的数据手册逐行编写初始化、发送数据和刷新命令的代码。但这对于新手来说门槛太高且极易出错。因此绝大多数厂商都会提供封装好的函数库。对于这块10.3寸HAT通常可以在GitHub上找到对应的仓库。以Python为例搭建环境的典型步骤如下3.1 系统准备与依赖安装首先确保你的树莓派系统如Raspbian/Raspberry Pi OS是最新的并启用SPI接口。墨水屏通常使用SPI协议与主控通信速度比纯GPIO模拟快得多。# 更新系统 sudo apt update sudo apt upgrade -y # 启用SPI接口 sudo raspi-config # 选择 Interfacing Options - SPI - Yes然后安装必要的Python库和系统工具# 安装Python3开发环境和pip sudo apt install python3-dev python3-pip -y # 安装用于图像处理的Pillow库这是必须的 sudo apt install libopenjp2-7 libtiff5 -y # Pillow的依赖 pip3 install Pillow # 安装操作GPIO和SPI的库例如RPi.GPIO和spidev pip3 install RPi.GPIO spidev3.2 获取并安装专属驱动库接下来需要获取屏幕的专属驱动库。你可以在产品提供的资料链接或GitHub上搜索“10.3inch e-Paper HAT”找到它。假设库的GitHub地址是https://github.com/waveshare/e-Paper。# 克隆驱动库仓库 git clone https://github.com/waveshare/e-Paper.git cd e-Paper/RaspberryPi_JetsonNano/python # 安装这个本地库 sudo python3 setup.py install安装成功后你会在Python的site-packages目录下找到类似waveshare_epd的模块。库里面会针对不同尺寸和型号的屏幕提供对应的Python类比如epd10in3。3.3 基础测试显示一张图片驱动库安装好后最好的验证方法就是运行一个示例程序。库中通常会有一个main.py或epd_test.py文件。我们来看一个最简化的测试脚本#!/usr/bin/python3 # -*- coding:utf-8 -*- import sys import os picdir os.path.join(os.path.dirname(os.path.dirname(os.path.realpath(__file__))), pic) libdir os.path.join(os.path.dirname(os.path.dirname(os.path.realpath(__file__))), lib) if os.path.exists(libdir): sys.path.append(libdir) from waveshare_epd import epd10in3 from PIL import Image, ImageDraw, ImageFont import time try: # 1. 初始化屏幕对象 epd epd10in3.EPD() epd.init() print(屏幕初始化成功正在清屏...) epd.Clear() # 2. 创建一个与屏幕分辨率相同的空白图像 image Image.new(1, (epd.width, epd.height), 255) # 255: white draw ImageDraw.Draw(image) # 3. 加载字体并绘制文字 font ImageFont.truetype(/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf, 40) draw.text((50, 50), Hello, E-Paper!, fontfont, fill0) # 0: black # 4. 显示图像 epd.display(epd.getbuffer(image)) print(图像显示完成。) # 5. 休眠并关闭 time.sleep(5) epd.sleep() # 让屏幕进入深度睡眠模式省电 except IOError as e: print(e) except KeyboardInterrupt: print(程序被中断) epd10in3.epdconfig.module_exit() exit()运行这个脚本(sudo python3 test.py)如果一切顺利你应该能看到屏幕上显示出“Hello, E-Paper!”的字样。这个过程可能会花费几秒钟期间屏幕会闪烁这是正常的全局刷新过程。注意很多墨水屏驱动库要求必须使用sudo权限运行因为需要直接访问硬件层的SPI和GPIO。如果遇到权限错误请务必加上sudo。4. 核心编程实践从静态显示到动态更新能显示静态文字只是第一步。一个实用的信息看板需要能显示时间、天气、图表等内容并且能定时更新。这就涉及到图像处理、网络请求和刷新策略。4.1 创建复杂布局天气信息看板假设我们要做一个简单的天气看板顶部显示日期时间中间显示天气图标和温度底部显示一段名言。我们需要用PIL库来精确控制每个元素的位置。from datetime import datetime import requests import json def draw_weather_dashboard(): # 初始化屏幕 epd epd10in3.EPD() epd.init() # 创建画布 image Image.new(1, (epd.width, epd.height), 255) draw ImageDraw.Draw(image) # 加载不同大小的字体 font_large ImageFont.truetype(DejaVuSans.ttf, 60) font_medium ImageFont.truetype(DejaVuSans.ttf, 36) font_small ImageFont.truetype(DejaVuSans.ttf, 24) # 区域1顶部日期时间 now datetime.now() date_str now.strftime(%Y-%m-%d) time_str now.strftime(%H:%M) draw.text((50, 20), date_str, fontfont_medium, fill0) draw.text((epd.width - 250, 20), time_str, fontfont_medium, fill0) draw.line([(30, 70), (epd.width-30, 70)], fill0, width2) # 画一条分隔线 # 区域2中间天气信息这里用模拟数据 # 实际应用中这里应该调用天气API例如和风天气、OpenWeatherMap city Beijing temp 22°C weather Sunny # 绘制天气图标这里用文字代替实际可以加载一个单色位图 draw.text((150, 120), ☀️, fontfont_large, fill0) # 注意需要字体支持这个字符 draw.text((300, 130), temp, fontfont_large, fill0) draw.text((150, 220), weather, fontfont_medium, fill0) draw.text((150, 270), city, fontfont_small, fill0) # 区域3底部名言同样可以调用API quote Stay hungry, stay foolish. author - Steve Jobs # 计算文字宽度以便居中 quote_bbox draw.textbbox((0,0), quote, fontfont_small) quote_width quote_bbox[2] - quote_bbox[0] quote_x (epd.width - quote_width) // 2 draw.text((quote_x, epd.height - 80), quote, fontfont_small, fill0) draw.text((epd.width - 200, epd.height - 40), author, fontfont_small, fill0) # 显示到屏幕 epd.display(epd.getbuffer(image)) epd.sleep()这个例子展示了如何将屏幕划分为不同区域进行布局。关键在于使用draw.textbbox()来获取文本的渲染尺寸从而实现居中或右对齐这比手动估算像素位置要准确得多。4.2 优化刷新局部刷新 vs 全局刷新频繁全局刷新每次2-3秒体验很差而且据说会略微影响屏幕寿命。对于只变化部分内容的场景如更新分钟数字应该使用局部刷新。查看驱动库通常会有displayPartial或类似的方法。但使用局部刷新有严格限制区域必须对齐局部刷新的区域坐标和宽高通常必须是8或16的倍数具体取决于驱动芯片。残影累积连续多次局部刷新后屏幕可能会出现“鬼影”残影。因此通常建议在每进行若干次比如5-10次局部刷新后强制进行一次全局刷新来彻底清屏。# 假设epd对象有局部刷新方法 partial_count 0 MAX_PARTIAL_REFRESH 5 def update_time_only(): global partial_count # 只重绘时间区域 image Image.new(1, (epd.width, epd.height), 255) draw ImageDraw.Draw(image) # ... 绘制所有静态内容背景 # 只更新动态的时间部分 new_time_str datetime.now().strftime(%H:%M) draw.text((epd.width - 250, 20), new_time_str, fontfont_medium, fill0) if partial_count MAX_PARTIAL_REFRESH: # 计算时间区域的坐标示例需根据实际布局调整 partial_area (epd.width - 300, 15, epd.width - 50, 75) # (x1, y1, x2, y2) epd.displayPartial(epd.getbuffer(image), partial_area) partial_count 1 print(f局部刷新 {partial_count}/{MAX_PARTIAL_REFRESH}) else: # 达到次数进行全局刷新 epd.display(epd.getbuffer(image)) partial_count 0 print(全局刷新清屏)4.3 图像处理技巧如何显示图片和图标墨水屏是单色1-bit的这意味着每个像素非黑即白。我们要显示的彩色图片或图标必须先进行“二值化”处理。PIL库的convert(1)方法可以做到但效果往往不佳会丢失很多细节。更好的方法是使用抖动算法Dithering。PIL的convert方法就支持def prepare_image_for_epaper(image_path, target_width, target_height): # 打开图片 img Image.open(image_path) # 转换为灰度图 img img.convert(L) # 调整大小保持比例避免变形 img.thumbnail((target_width, target_height), Image.Resampling.LANCZOS) # 创建一个新画布将调整后的图片居中粘贴 new_img Image.new(L, (target_width, target_height), 255) # 白色背景 offset ((target_width - img.width)//2, (target_height - img.height)//2) new_img.paste(img, offset) # 使用Floyd-Steinberg抖动算法转换为1位黑白图 dithered_img new_img.convert(1, ditherImage.Dither.FLOYDSTEINBERG) return dithered_img # 使用 weather_icon prepare_image_for_epaper(sunny.png, 100, 100) # 然后将weather_icon粘贴到主画布上 main_image.paste(weather_icon, (150, 120))FloydSteinberg抖动算法能产生视觉效果较好的灰度过渡比简单的阈值二值化看起来自然得多非常适合显示徽标、照片或复杂的图标。5. 系统集成与自动化让它真正“活”起来一个孤立的显示程序价值有限。我们需要把它集成到一个系统中实现定时更新、数据获取和稳定运行。5.1 使用Crontab定时任务最朴素的方式是利用Linux的crontab定时执行我们的Python脚本。例如每分钟更新一次时间每30分钟更新一次天气。# 编辑当前用户的crontab crontab -e在末尾添加# 每分钟的第10秒运行一次时间更新脚本使用局部刷新 * * * * * sleep 10 cd /home/pi/e-Paper_Project sudo /usr/bin/python3 update_time.py /tmp/epaper.log 21 # 每30分钟运行一次天气更新脚本使用全局刷新 */30 * * * * cd /home/pi/e-Paper_Project sudo /usr/bin/python3 update_weather.py /tmp/epaper.log 21 # 每天凌晨3点进行一次全局刷新清屏消除可能的残影 0 3 * * * cd /home/pi/e-Paper_Project sudo /usr/bin/python3 clear_screen.py /tmp/epaper.log 21重要提示使用sudo运行Python脚本时crontab的环境变量可能与终端不同。务必在脚本中使用绝对路径来指定字体文件、图片资源和Python库的路径否则很可能找不到文件而失败。例如font ImageFont.truetype(/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf, 40)。5.2 构建一个常驻的Python服务对于更复杂的应用比如需要响应网络请求或传感器数据实时更新使用crontab就不够灵活了。我们可以写一个常驻的Python服务。一个简单的架构是主循环结合事件驱动。我们可以使用schedule库来管理定时任务并在一个while True循环中运行。# service_main.py import schedule import time from weather_update import fetch_and_display_weather from time_update import update_time_display from screen_manager import EPaperManager def job_update_weather(): print(执行天气更新任务) fetch_and_display_weather() def job_update_time(): print(执行时间更新任务) update_time_display() def job_daily_refresh(): print(执行每日全局清屏) epd_manager EPaperManager() epd_manager.full_clear() if __name__ __main__: # 定义任务 schedule.every(30).minutes.do(job_update_weather) schedule.every(1).minutes.do(job_update_time) schedule.every().day.at(03:00).do(job_daily_refresh) # 立即运行一次 job_update_weather() job_update_time() print(墨水屏信息服务已启动...) while True: schedule.run_pending() time.sleep(1) # 避免CPU空转然后使用systemd将这个脚本作为系统服务来管理实现开机自启和状态监控。# 创建服务文件 sudo nano /etc/systemd/system/epaper-display.service写入以下内容[Unit] DescriptionE-Paper Display Service Aftermulti-user.target [Service] Typesimple Userpi WorkingDirectory/home/pi/e-Paper_Project ExecStart/usr/bin/python3 /home/pi/e-Paper_Project/service_main.py Restarton-failure RestartSec10 [Install] WantedBymulti-user.target保存后启用并启动服务sudo systemctl daemon-reload sudo systemctl enable epaper-display.service sudo systemctl start epaper-display.service # 查看状态 sudo systemctl status epaper-display.service这种方式比crontab更健壮日志管理也更方便通过journalctl -u epaper-display.service查看。6. 功耗实测与电源方案选择墨水屏的“零功耗”特性常常被误解。实测下来对于这块10.3寸HAT在树莓派4B平台上待机状态屏幕显示静态画面树莓派和HAT板均未休眠整体系统功耗约1.8W - 2.2W。这主要是树莓派自身运行和HAT板上待机电路的消耗。刷新瞬间全局刷新时峰值功耗可达3.5W - 4W持续约2-3秒。局部刷新峰值功耗略低约3W持续约0.5秒。所以如果你希望制作一个完全依靠电池供电的便携设备必须认真考虑功耗。方案有以下几种树莓派完全关机仅屏幕显示这是不可能的因为屏幕需要主控来维持图像虽然断电后图像仍在但无法更新。必须有一个主控在工作。使用低功耗MCU如ESP32驱动这是更专业的方案。ESP32深度睡眠时功耗可低至10μA定时唤醒刷新屏幕刷新完继续睡眠。这样整体平均功耗可以做到极低一颗大容量锂电池可以续航数月。但这需要将HAT的驱动移植到ESP32通常有Arduino或MicroPython的库并重新设计硬件连接。使用树莓派Zero W并优化系统树莓派Zero W本身功耗较低。可以关闭HDMI、蓝牙、Wi-Fi如果不需联网降低CPU频率并让系统在刷新间隙进入休眠状态。配合脚本控制可以将平均功耗控制在0.5W左右。对于10000mAh的充电宝理论续航可达20小时以上适合短期展示。外接太阳能板对于户外固定安装结合太阳能板和充电管理模块可以实现永久续航。我个人的经验是如果项目固定且有插座直接插电是最省心的。如果是移动或户外项目强烈建议转向ESP32墨水屏的方案功耗控制完全不是一个量级。树莓派方案更适合作为原型验证或需要复杂计算如图形渲染、网络服务的场景。7. 常见问题与避坑指南在实际操作中我踩过不少坑这里总结几个最典型的问题一屏幕刷新后残留“鬼影”严重。原因连续使用局部刷新displayPartial而未定期进行全局刷新display。墨水粒子在多次局部电压驱动后状态不完全稳定。解决严格遵守“N次局部刷新后必跟一次全局刷新”的规则。这个N值需要根据具体屏幕和驱动波形测试一般在5-10次之间。可以在每次全局刷新后用epd.Clear()如果库提供先清为全白再显示新内容效果更彻底。问题二运行Python脚本提示“No module named waveshare_epd”。原因Python路径问题或者驱动库没有正确安装。解决确认安装时使用了sudo python3 setup.py install。在脚本中手动添加库路径如示例代码开头的sys.path.append(libdir)。尝试使用绝对路径导入from lib.waveshare_epd import epd10in3。问题三显示的图片有黑色边框或位置偏移。原因图片尺寸与屏幕分辨率不匹配或者粘贴图片时的坐标计算有误。解决在绘制前打印epd.width和epd.height确认分辨率。使用image.resize((epd.width, epd.height))进行精确缩放但注意这可能变形。推荐使用thumbnail保持比例再粘贴到居中位置。仔细检查draw.text或image.paste的坐标参数。问题四屏幕偶尔花屏或显示乱码。原因SPI通信受到干扰或者在刷新过程中被强行中断如按CtrlC。解决确保树莓派和屏幕连接牢固排线没有松动。在Python脚本中增加异常捕获确保在任何情况下包括键盘中断都能执行epd.sleep()或epd.module_exit()来让屏幕进入安全状态。如果问题频繁可以尝试在raspi-config中降低SPI总线速度。问题五crontab定时任务不执行。原因环境变量缺失尤其是PATH和用户权限、路径错误、sudo密码问题。解决在crontab文件顶部显式设置环境变量PATH/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin。所有命令和文件路径使用绝对路径。将脚本的输出重定向到日志文件如 /tmp/epaper.log 21方便排查。考虑使用systemd服务代替crontab可靠性更高。这块10.3英寸墨水屏HAT的可玩性非常高从简单的信息展示到复杂的交互界面都可以实现。它的核心魅力在于那种独特的、类似纸张的显示质感以及“一次刷新永久显示”的极客浪漫。开始你的项目时不妨从最简单的“Hello World”开始逐步添加天气、日历、RSS新闻摘要等功能最终打造出一个独一无二的、属于你自己的桌面信息中心。过程中遇到问题多查阅驱动库的示例代码和芯片数据手册大部分难题都能找到答案。