📅 2026-07-04 · 🕐 约 30 分钟阅读 · 🏷️ ESP32 IoT HomeLab REST API

ESP32 + 树莓派:打造我的 HomeLab 信息面板

HomeLab 跑了一堆服务:OpenClaw 助手、Pi-hole 广告拦截、Uptime Kuma 监控……但每次想看系统状态,得掏出手机打开一堆管理页面。有没有办法把这些信息集中到一个屏幕上,扫一眼就了然?

答案是:一块 40 块钱的 ESP32 开发板 + 一块 2.8 寸 LCD 屏幕 + 树莓派上的 REST API,总成本不到 100 块,做出来的东西比想象中实用得多。


📐 整体架构

整个系统的设计思路很直接:

架构原则:数据计算放在树莓派,ESP32 只负责显示和交互。这样即使以后加更多监控指标,也不用动 ESP32 的代码。

🖥️ 硬件选型

ESP32 开发板

用的是 ESP32-2432S024R(又称 CYD - Cheap Yellow Display),这块板子大概 40 块人民币,板载 2.8 寸 320×240 IPS 屏幕和触控,使用 SPI 接口。性价比极高,缺点是文档比较碎片化,不同批次的屏幕驱动芯片可能不同。

屏幕驱动芯片

这块屏幕用的是 ST7789 驱动芯片(不是更常见的 ILI9341),需要用专门的驱动库。颜色排序是 BGR 而不是 RGB,这些细节如果不注意屏幕会显示奇怪的偏色。

接口定义

屏幕使用 VSPI 总线,触屏使用独立的 XPT2046 芯片(另占一组 SPI 引脚)。关键引脚如下(具体 GPIO 编号根据实际接线略有差异):


🔧 软件配置

PlatformIO 项目结构

使用 PlatformIO 管理 ESP32 项目,主要依赖两个库:

项目的核心配置文件 platformio.ini 中需要指定正确的驱动参数:

[env:esp32dev]
platform = espressif32
board = esp32dev
framework = arduino
lib_deps = bodmer/TFT_eSPI@^2.5.43

build_flags =
    -DST7789_DRIVER=1
    -DTFT_RGB_ORDER=TFT_BGR
    -DTFT_WIDTH=240
    -DTFT_HEIGHT=320
    -DTFT_RST=12
    -DSPI_FREQUENCY=55000000

WiFi 连接与 NTP 时间

ESP32 连接 WiFi 后,通过 NTP 服务器同步时间。连接时使用静态 IP 分配,指定一个同网段的固定地址,避免 DHCP 每次拿到的 IP 不一致导致通信失败。

WiFi.begin("SSID", "PASSWORD");
WiFi.config(
    IPAddress(192, 168, 51, 240),
    IPAddress(192, 168, 51, 1),
    IPAddress(255, 255, 255, 0)
);
configTime(8 * 3600, 0, "pool.ntp.org");

🖼️ UI 设计

配色方案

深色系主题,参考工业仪表盘的风格:

ST7789 的颜色显示有一定的绿色偏移(绿色通道偏亮),在选色时需要刻意降低绿色的比重,否则看起来会偏青。

布局结构

屏幕 320×240 像素,横向布局:


📡 树莓派 REST API

为什么需要 API 层?

ESP32 的 HTTP 客户端对 TLS 和复杂路由支持有限,且不适合做数据聚合。把数据获取逻辑放在树莓派上,ESP32 只需要定期 GET 一个 JSON,复杂度降低很多。

Flask 轻量实现

树莓派上用 Python Flask 跑一个极简 API,监听在 8099 端口:

@app.route("/api/dashboard")
def dashboard():
    return jsonify({
        "time": get_local_time(),
        "uptime": get_uptime(),
        "system": {
            "cpu": get_cpu_usage(),
            "ram": get_ram_usage(),
            "temp": get_cpu_temp()
        },
        "devices": {
            "pi5": True,
            "nas": check_nas(),
            "jellyfin": check_jellyfin()
        },
        "services": {
            "openclaw": check_service("openclaw-gateway"),
            "hermes": check_service("hermes-agent"),
            "pihole": check_service("pihole")
        },
        "controls": {
            "light": state["light"],
            "fan": state["fan"]
        }
    })

控制接口

触摸开关灯或风扇时,ESP32 发送 POST 请求:

POST /api/control/light
Body: {"action": "toggle"}  # 或 "on" / "off"

树莓派把控制状态写入本地文件,持久化保存,重启后不丢失。


🐛 踩坑记录

1. 屏幕型号搞错

开局屏幕一片白,代码确认没问题但就是没有显示。最后发现这块板子用的是 ST7789 驱动,不是更常见的 ILI9341。换对驱动后立刻正常。

2. 5GHz vs 2.4GHz WiFi

ESP32 的 WiFi 模块只支持 2.4GHz,而我家路由器有两个 SSID:一个带 _5G 后缀的 5GHz 网络,一个普通 2.4GHz 网络。连错了 SSID 就会一直显示连接失败。

3. 路由器 AP 隔离

WiFi 连接成功,HTTP 请求却一直超时。用 Python 模拟 TCP 连接发现:ESP32 发送的 SYN 能到达树莓派,但树莓派的 SYN-ACK 永远回不来——路由器开启了客户端隔离(Client Isolation),阻止 WiFi 客户端之间直接通信。

解决方案:关闭路由器 AP 隔离,或者把 ESP32 和树莓派放在同一网络平面。实测关掉隔离后一切正常。

4. ST7789 绿色偏移

在电脑上配的颜色,部署到真实屏幕上发现偏绿。这是 ST7789 驱动特有的颜色映射问题,需要在选色时主动降低绿色通道的比重。例如原本的深蓝色 (24, 4, 32) 实际显示效果不错,但高饱和度的颜色偏移明显。

5. 时钟来源

ESP32 内置 RTC 精度有限,长时间会漂移。改用从树莓派 API 获取基准时间,本地每秒递增,30 秒同步一次。这样即使 WiFi 偶尔抖动,时钟也依然流畅不卡顿。


⚙️ 最终架构

┌─────────────────┐     WiFi (2.4GHz)      ┌─────────────────┐
│  ESP32 + LCD    │ ◄──── HTTP GET ----─�►  │  树莓派 (Pi5)    │
│  Dashboard UI   │     192.168.51.x        │  Flask API      │
│  Touch Input    │                        │  端口: 8099     │
└─────────────────┘                        └────────┬────────┘
                                                    │
                                            CPU/RAM/温度/服务
                                            systemd 服务状态
                                            控制状态持久化

💡 后续计划

第一版做完了基本功能,接下来还有几个想加的东西:


📝 总结

整个项目从头到尾花了两天晚上,大部分时间花在网络调试上,屏幕和 UI 其实相对顺利。这个方案的性价比很高:硬件成本极低,树莓派提供数据,ESP32 只管显示,职责分离得清晰。

对于有 HomeLab 的朋友,这块 40 块的屏幕放在桌上或者服务器旁边,随时看到系统状态,比打开手机 App 方便多了。


🔧 最后更新:2026-07-04 · by Henry Jin

← 返回首页