基于ESPHome与reTerminal构建低功耗墨水屏智能家居仪表盘

📅 发布时间:2026/8/20 12:41:09
基于ESPHome与reTerminal构建低功耗墨水屏智能家居仪表盘 1. 项目缘起为什么需要一个墨水屏仪表盘如果你和我一样家里已经部署了Home Assistant并且对智能家居的状态监控有了一定的依赖那你可能也经历过这样的场景早上起床想看一眼室温和湿度得先摸到手机解锁打开Home Assistant App等待加载然后才能看到那几个数字。或者在厨房做饭时想确认一下冰箱温度或者阳台的窗户是否关闭又得重复一遍这个流程。这些操作虽然不复杂但累积起来总让人觉得不够“无缝”不够“优雅”。智能家居的终极目标不就是让信息和服务主动、无感地融入生活吗于是一个常显、低功耗、信息集中的物理仪表盘就成了一个很自然的需求。而电子墨水屏ePaper几乎是这个需求的完美答案。它只在刷新时耗电显示静态内容时几乎零功耗这意味着你可以把它放在床头、贴在冰箱上或者挂在玄关7x24小时显示关键信息而不用担心电费或者屏幕烧屏。它本身不发光光线越强显示越清晰非常适合作为环境信息板。这个项目就是要在十分钟内用最低的成本和最简单的步骤搭建起这样一个专属于你的Home Assistant墨水屏仪表盘。我们将使用一款非常流行的开源硬件——reTerminal或者其核心板reComputer作为主控配合一块ePaper屏幕通过ESPHome来桥接Home Assistant的数据。整个流程清晰直接即使你只有基础的动手能力也能轻松完成。下面我就带你一步步实现它。2. 硬件选型与核心原理为什么是reTerminal ESPHome在开始动手前我们先花几分钟搞清楚我们选择的硬件和软件方案背后的逻辑。这能帮你理解每一步操作的意义甚至在将来需要调整或排查问题时心里有底。2.1 主控选择reTerminal/reComputer的优势市面上能驱动ePaper的单板计算机很多比如树莓派Zero 2W、ESP32系列开发板等。我们选择Seeed Studio的reTerminal或reComputer主要基于以下几个考量集成度与易用性reTerminal本身就是一个带5英寸触摸屏的完整设备但它也提供了40针的GPIO排针可以方便地连接各种外设包括我们需要的ePaper屏幕。它的核心是一颗树莓派CM4模块这意味着它拥有完整的Linux操作系统Raspberry Pi OS和强大的计算能力远超一般的微控制器。供电与接口reTerminal通过Type-C接口供电和通信非常方便。其GPIO电压为3.3V与大多数ePaper屏幕兼容无需额外的电平转换电路。社区与生态作为树莓派生态的一员它有庞大的软件和社区支持。我们需要用到的ESPHome在Linux系统上部署和运行非常成熟稳定。如果你手头没有reTerminal使用reComputer不带屏幕的版本或者树莓派4B/3B搭配40Pin GPIO扩展板也是完全可行的原理和步骤几乎一致。2.2 显示核心电子墨水屏ePaper的工作原理电子墨水屏的技术核心是“电泳”。屏幕由数百万个微胶囊构成每个胶囊里充满了带负电的黑色粒子和带正电的白色粒子悬浮在透明的液体中。当在胶囊两侧施加一个电场时黑色或白色粒子就会被吸引到顶部从而形成像素点的黑或白显示。这个过程有两个关键特性决定了ePaper的优缺点双稳态一旦粒子移动到某个位置即使撤掉电场它们也会因为物理作用力而保持原位。这就是为什么ePaper在显示静态图像时不耗电——它只在刷新图像改变电场时消耗能量。刷新慢粒子的物理移动需要时间所以ePaper的刷新速度通常需要几百毫秒到几秒远低于LCD或OLED屏幕。这决定了它不适合显示快速变化的视频或动画但对于几分钟甚至几小时更新一次的仪表盘来说这根本不是问题。我们项目中常用的尺寸是7.5英寸800x480分辨率或4.2英寸400x300分辨率它们通常通过SPI接口与主控通信驱动库非常成熟。2.3 软件桥梁ESPHome的角色你可能会问既然reTerminal本身就能跑Home Assistant为什么不直接写个Python脚本去拉取数据并刷新屏幕呢当然可以但这会引入几个复杂度需要处理Home Assistant的认证API Token、需要管理网络请求、需要解析JSON数据、需要处理定时任务等等。而ESPHome巧妙地解决了这些问题。在这个架构中ESPHome并不直接运行在reTerminal上而是运行在同一个家庭网络中的另一台设备上通常是Home Assistant服务器本身或者一台常开的电脑。它的角色是一个“虚拟设备”或“桥接器”。虚拟设备我们在ESPHome中定义一个“虚拟”的显示屏设备它通过Home Assistant的API持续订阅我们关心的实体entity状态如sensor.living_room_temperature。数据桥接当这些实体的状态发生变化时Home Assistant会主动通过WebSocket或API调用通知这个ESPHome设备。串口转发这个运行在后台的ESPHome“设备”并不真正连接硬件屏幕。我们配置它将接收到的数据通过一个简单的协议比如自定义的文本格式输出到串口Serial。reTerminal接收与渲染reTerminal通过USB转TTL串口模块或者直接使用其GPIO上的UART引脚连接到运行ESPHome的电脑读取串口数据。reTerminal上运行一个轻量级的Python脚本解析这些数据然后调用ePaper的驱动库将新的状态值更新到屏幕上。这样做的最大好处是解耦和简化。reTerminal上的Python脚本只需要做两件事读串口和画屏幕。所有复杂的Home Assistant交互、数据订阅、网络通信都由ESPHome在后台默默完成。ESPHome成熟的yaml配置语法也让管理多个数据源变得异常简单。3. 十分钟快速搭建从零到显示理解了原理我们现在开始实战。请确保你手头有以下硬件reTerminal或reComputer/树莓派一台并已安装好Raspberry Pi OS推荐Lite版本以减少资源占用。7.5英寸或4.2英寸的SPI接口ePaper屏幕一块以Waveshare品牌为例。杜邦线母对母若干。可选USB转TTL串口模块一个用于调试和初期连接。如果reTerminal和运行ESPHome的电脑在同一台机器上可以直接使用虚拟串口。3.1 第一步硬件连接2分钟我们将ePaper屏幕的SPI引脚连接到reTerminal的GPIO上。以7.5英寸屏幕V2版本和reTerminal为例ePaper 引脚功能reTerminal GPIO (物理引脚号)备注VCC电源 (3.3V)Pin 1 (3.3V)务必接3.3V接5V会烧屏GND地Pin 6 (GND)DIN (MOSI)SPI数据输入Pin 19 (GPIO10, MOSI)CLK (SCK)SPI时钟Pin 23 (GPIO11, SCLK)CS (CE0)片选Pin 24 (GPIO8, CE0)使用CE0DC数据/命令选择Pin 18 (GPIO24)可自定义代码中需对应RST复位Pin 22 (GPIO25)可自定义代码中需对应BUSY忙状态指示Pin 16 (GPIO23)可自定义代码中需对应注意不同型号的ePaper屏幕引脚定义可能略有不同请务必以你购买屏幕的说明书为准。连接时最好断开电源。3.2 第二步在reTerminal上准备环境3分钟通过SSH登录到你的reTerminal。启用SPI接口运行sudo raspi-config选择Interface Options-SPI-Yes启用SPI驱动然后重启。安装Python库我们将使用Waveshare提供的Python库来驱动屏幕。sudo apt update sudo apt install python3-pip python3-pil python3-numpy -y git clone https://github.com/waveshare/e-Paper.git cd e-Paper/RaspberryPi_JetsonNano/python sudo pip3 install .这个库包含了各种尺寸ePaper的驱动代码。安装串口通信库用于从串口读取ESPHome发来的数据。sudo pip3 install pyserial3.3 第三步配置ESPHome“桥接”设备3分钟在你的Home Assistant服务器或另一台常开电脑上已有ESPHome插件或独立安装。我们创建一个新的ESPHome设备配置。新建配置在ESPHome中新建一个设备命名为epaper_bridge选择ESP32作为平台这里我们只是利用其软件功能并不需要真实硬件。编辑YAML配置核心是使用homeassistant传感器和uart组件。esphome: name: epaper_bridge platform: ESP32 board: nodemcu-32s # 启用Home Assistant API组件用于接收数据 api: encryption: key: 你的api加密密钥 # 启用日志便于调试 logger: # 配置UART将数据输出到串口 uart: id: uart_bus tx_pin: GPIO1 # 虚拟引脚实际对应串口输出 baud_rate: 115200 # 定义传感器关联Home Assistant中的实体 sensor: - platform: homeassistant name: Living Room Temperature entity_id: sensor.living_room_temperature id: temp_living on_value: then: - uart.write: TEMP,${id(temp_living).state}\n - platform: homeassistant name: Outdoor Humidity entity_id: sensor.outdoor_humidity id: humidity_outdoor on_value: then: - uart.write: HUMI,${id(humidity_outdoor).state}\n - platform: homeassistant name: Living Room Light State entity_id: light.living_room id: light_living on_value: then: - if: condition: lambda: return id(light_living).state on; then: - uart.write: LIGHT,ON\n else: - uart.write: LIGHT,OFF\n # 定义一个文本传感器用于接收来自reTerminal的简单指令可选 text_sensor: - platform: uart id: uart_receiver on_value: then: - logger.log: Received from ePaper: $value这个配置做了几件事定义了三个传感器分别绑定到Home Assistant里的温度、湿度和灯光实体。为每个传感器设置了on_value触发器。当Home Assistant中对应实体的状态发生变化时会执行其中的动作。动作为通过UART向串口写入一行格式化的文本例如TEMP,22.5。我们定义了一个简单的协议数据标签,数值以换行符\n结束。这样在reTerminal端容易解析。编译与运行由于我们没有真实ESP32硬件不能直接刷机。我们需要在ESPHome中“安装”这个配置到“本地”。在ESPHome仪表板对该配置选择“Install” - “Manual Download” - “Modern format (.pio)”。下载后在服务器上通过命令行运行它# 进入你存放ESPHome配置的目录 cd /path/to/esphome/config # 运行虚拟设备 esphome run epaper_bridge.yaml此时这个ESPHome服务会启动连接到你的Home Assistant并开始监听实体变化。它会把数据输出到标准输出stdout我们需要将其重定向到一个虚拟串口。3.4 第四步建立通信链路1分钟我们需要让reTerminal能读到ESPHome服务输出的数据。这里有两种方法方法A同一台主机推荐用于测试使用socat创建一对虚拟串口。# 在运行ESPHome和reTerminal SSH会话的电脑上执行 sudo apt install socat socat -d -d pty,raw,echo0 pty,raw,echo0命令会输出两个虚拟串口路径例如/dev/pts/2和/dev/pts/3。它们已经连接。将ESPHome的uart配置中的tx_pin注释掉改用debug组件输出到标准输出然后用socat重定向# 运行ESPHome将其输出通过管道发给socat的一个端口 esphome run epaper_bridge.yaml 21 | socat - /dev/pts/2在reTerminal的Python脚本中打开另一个虚拟串口/dev/pts/3进行读取。方法B两台主机更接近最终部署使用USB转TTL串口模块。将USB转TTL模块的TX引脚连接到reTerminal的RX引脚GPIO15物理引脚号10。将USB转TTL模块插入运行ESPHome的电脑的USB口。在ESPHome配置中uart的tx_pin可以保持为一个虚拟GPIO因为ESPHome在电脑上运行时uart.write默认是输出到标准输出。我们需要一个小的Python脚本在电脑上运行读取标准输出并写入真实的串口。# bridge_serial.py 运行在电脑上 import sys import serial import time # 替换为你的USB转TTL串口设备名如COM3(Windows)或/dev/ttyUSB0(Linux) ser serial.Serial(/dev/ttyUSB0, 115200, timeout1) try: while True: line sys.stdin.readline() if line: print(fSending to serial: {line.strip()}) ser.write(line.encode(utf-8)) except KeyboardInterrupt: ser.close()运行ESPHome服务并将其输出通过管道传递给这个脚本esphome run epaper_bridge.yaml 21 | python3 bridge_serial.py3.5 第五步编写reTerminal上的显示脚本1分钟在reTerminal上创建一个Python脚本例如epaper_dashboard.py。#!/usr/bin/env python3 import serial import time from waveshare_epd import epd7in5_V2 # 根据你的屏幕型号导入 from PIL import Image, ImageDraw, ImageFont import threading # 初始化屏幕 epd epd7in5_V2.EPD() epd.init() epd.Clear() # 准备画布和字体 font_large ImageFont.truetype(/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf, 48) font_small ImageFont.truetype(/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf, 24) # 数据存储字典 data { TEMP: --.-°C, HUMI: --%, LIGHT: OFF } def update_display(): 根据当前数据刷新屏幕 # 创建一张新图像初始为白色 image Image.new(1, (epd.width, epd.height), 255) draw ImageDraw.Draw(image) # 绘制标题 draw.text((10, 10), Home Dashboard, fontfont_large, fill0) draw.line((10, 70, epd.width-10, 70), fill0, width3) # 绘制数据 y_offset 90 line_height 60 draw.text((20, y_offset), fTemperature: {data[TEMP]}, fontfont_small, fill0) draw.text((20, y_offset line_height), fHumidity: {data[HUMI]}, fontfont_small, fill0) draw.text((20, y_offset 2*line_height), fLight: {data[LIGHT]}, fontfont_small, fill0) # 显示到屏幕 epd.display(epd.getbuffer(image)) print(Display updated.) def serial_listener(): 监听串口解析数据 # 替换为你的串口设备如果是虚拟串口或USB转TTL ser serial.Serial(/dev/ttyUSB0, 115200, timeout1) # 或 /dev/ttyAMA0 (GPIO UART) 或 /dev/pts/3 while True: try: line ser.readline().decode(utf-8).strip() if line: print(fReceived: {line}) parts line.split(,) if len(parts) 2: tag, value parts if tag in data: if tag TEMP: data[tag] f{value}°C elif tag HUMI: data[tag] f{value}% else: data[tag] value # 数据更新后触发屏幕刷新 update_display() except Exception as e: print(fSerial error: {e}) time.sleep(5) if __name__ __main__: # 首次全屏刷新 update_display() # 启动串口监听线程 listener_thread threading.Thread(targetserial_listener, daemonTrue) listener_thread.start() # 主线程保持运行也可以加入定时全刷逻辑防止残影 try: while True: time.sleep(60) # 每分钟检查一次可进行定时全刷等维护操作 # 例如每6小时强制全刷一次 # if time.time() % (6*3600) 60: # epd.init() # 重新初始化可以清残影 # update_display() except KeyboardInterrupt: print(Exiting...) epd.sleep()给脚本执行权限并运行chmod x epaper_dashboard.py然后python3 epaper_dashboard.py。如果一切顺利你将看到屏幕初始化并显示初始信息。当你在Home Assistant中改变温度、湿度或灯光状态时屏幕上的内容应该会在几秒内更新。4. 优化与进阶让仪表盘更实用、更稳定基础功能跑通后我们可以从多个维度进行优化让它从一个玩具变成一个真正可靠的家庭信息中枢。4.1 界面设计与布局优化上面的示例脚本只展示了最简单的文本排列。我们可以利用PIL库的强大功能设计更美观的界面。使用模板图片先用图形设计软件如Figma、GIMP设计好仪表盘的背景图包含静态的图标、装饰框和文字标签。将这个背景图黑白二值BMP或PNG格式放在reTerminal上。在Python脚本中先加载背景图然后在对应的坐标位置动态绘制变化的数据。这样能获得更专业的视觉效果。background Image.open(dashboard_bg.png) draw ImageDraw.Draw(background) draw.text((x, y), data[TEMP], fontfont, fill0) epd.display(epd.getbuffer(background))布局更多数据7.5英寸屏幕有800x480像素可以轻松布局8-10个关键信息。例如分为上下两栏上栏显示环境数据温湿度、空气质量、天气下栏显示设备状态灯光、门窗、电器功耗。添加简单图形可以用draw.rectangle或draw.line画简单的进度条来表示传感器数值如空气质量指数比纯数字更直观。4.2 通信协议与错误处理强化我们之前定义的简单文本协议TEMP,22.5在家庭网络环境下基本够用但为了健壮性可以考虑以下改进增加校验在行尾添加一个简单的校验和例如CRC8在reTerminal端进行验证丢弃错误数据。# ESPHome端 (使用lambda计算) on_value: then: - lambda: |- int crc 0; std::string msg TEMP, to_string(id(temp_living).state); for(char c : msg) crc ^ c; uart.write(msg * to_string(crc) \n);心跳与重连机制在ESPHome配置中添加一个定时器定期如每30秒发送一个PING消息。reTerminal端如果超过一定时间如2分钟没收到任何消息包括PING或数据则判定连接断开在屏幕上显示“连接中断”的提示并尝试重新初始化串口连接。数据缓冲与去抖对于频繁变化的传感器如功率计可以在ESPHome端或reTerminal端做简单的去抖处理避免屏幕因刷新过于频繁而出现残影或缩短寿命。例如只在数值变化超过一定阈值或距离上次更新超过30秒时才发送。4.3 系统服务化与自启动我们不想每次重启reTerminal后都手动SSH进去运行Python脚本。创建系统服务在reTerminal上创建一个systemd服务文件。sudo nano /etc/systemd/system/epaper-dashboard.service写入以下内容[Unit] DescriptionHome Assistant E-Paper Dashboard Afternetwork.target [Service] Typesimple Userpi WorkingDirectory/home/pi ExecStart/usr/bin/python3 /home/pi/epaper_dashboard.py Restarton-failure RestartSec10 [Install] WantedBymulti-user.target启用并启动服务sudo systemctl daemon-reload sudo systemctl enable epaper-dashboard.service sudo systemctl start epaper-dashboard.service现在你的墨水屏仪表盘会在reTerminal启动时自动运行并在崩溃后自动重启。4.4 功耗与屏幕维护ePaper虽然省电但reTerminal树莓派CM4本身功耗在2-5W左右。如果希望进一步省电可以考虑使用更省电的主控如ESP32-S3但会牺牲部分性能和部署便利性。对于屏幕维护最关键的是防止残影。长期显示同一静态图像可能导致粒子残留。解决方法定期全局刷新在脚本中加入逻辑每显示一定次数如20次局部更新后或每隔几小时执行一次完整的全屏清除和重绘epd.Clear() 全图display。我们的示例脚本注释里已经提到了定时全刷的思路。深度睡眠如果你不需要24小时显示可以配置屏幕在夜间或离家模式时进入深度睡眠epd.sleep()并让reTerminal也进入休眠或关闭屏幕背光如果是reTerminal设备。5. 排查与调试当屏幕没有如预期显示时即使按照步骤操作也可能会遇到问题。这里提供一个清晰的排查链路。5.1 屏幕无任何反应全白或全黑检查电源和连接这是最常见的问题。确认屏幕VCC接的是3.3V不是5V。确认所有杜邦线接触牢固特别是GND。可以用万用表测量屏幕排针上的电压。确认SPI已启用运行ls /dev/spi*应该能看到类似/dev/spidev0.0的设备。如果没有返回raspi-config确认SPI已开启并重启。检查驱动库安装在Python中尝试导入waveshare_epd库看是否报错。python3 -c import waveshare_epd; print(OK)。验证屏幕型号确认你导入的驱动类如epd7in5_V2与你的屏幕型号完全一致。不同版本的屏幕驱动可能不兼容。5.2 屏幕初始化成功但收不到数据更新确认串口连接在reTerminal上运行python3 -m serial.tools.list_ports查看可用串口。尝试用简单的串口监听程序测试是否有数据流入python3 -m serial.tools.miniterm /dev/ttyUSB0 115200替换为你的设备名。如果ESPHome端有数据发送你应该能在这里看到。检查ESPHome服务状态在运行ESPHome的电脑上查看其日志输出确认它已成功连接到Home Assistant并且没有报错。检查Home Assistant中对应实体的状态是否真的发生了变化。可以在Home Assistant开发者工具-状态中手动更改一个实体状态观察ESPHome日志是否有对应的输出。检查通信协议确认reTerminal脚本中解析数据的代码line.split(,)与ESPHome发送的格式完全匹配包括大小写和换行符。5.3 屏幕刷新出现残影或鬼影启用局部刷新模式一些ePaper驱动库支持局部刷新epd.display_Partial速度更快但更容易产生残影。对于仪表盘应用建议始终使用全局刷新epd.display。增加全刷频率如前所述增加定时全屏清除重绘的逻辑。检查电源稳定性屏幕刷新时功耗较大如果电源特别是3.3V线供电不足可能导致刷新不彻底。尝试缩短连接线或使用独立的3.3V稳压模块为屏幕供电。5.4 字体显示为乱码或方框这是因为系统中没有对应的字体文件。我们的脚本使用了DejaVu字体它是Raspberry Pi OS自带的。如果缺失可以安装sudo apt install fonts-dejavu-core。你也可以将喜欢的TTF字体文件上传到reTerminal并在脚本中指定绝对路径。整个项目从硬件连接到稳定运行核心在于理解数据流Home Assistant - ESPHome虚拟桥接- 串口 - reTerminalPython脚本- ePaper驱动。只要这个链条的每一环都打通了一个个性化、低功耗、常显的家庭信息中心就真正属于你了。你可以根据自己的需求轻松地修改ESPHome的配置来添加更多传感器或者修改Python脚本来设计更复杂的界面。