一、项目介绍
本项目为智能家居应用场景设计了一款专属中控终端,以 ESP32S3 开发板为主控,搭配一块闲置的 2.4 寸电阻触摸屏为硬件平台,基于ESPHOME和LVGL图形框架开发。终端可稳定接入 Home Assistant 智能家居系统,实时展示平台内各类传感器监测数据、智能设备运行状态,同时支持屏幕触控操作,直接对 Home Assistant 中的灯光、插座等设备进行控制,实现全屋智能设备状态可视化与集中操控。
创意亮点:项目充分利用闲置硬件资源,仅用一块普通 ESP32S3 开发板 + 闲置 2.4 寸电阻触摸屏,即可实现智能家居中控功能。终端接入 Home Assistant 生态后,瞬间升级为全屋智能控制中枢,将分散的智能设备状态统一整合、可视化呈现。无需昂贵的商用中控屏,就能打造专属的本地化智能家居交互终端,兼顾实用性、性价比与 DIY 可玩性。
二、硬件介绍
1.ESP32S3开发板
一块普通的ESP32S3开发板,我用的是合宙的ESP32S3开发板。
2.XPT2046 电阻式触摸屏
2.4寸液晶屏模块,分辨率240*320,屏幕驱动为ILI9341,触摸为电阻式触摸,驱动为XPT2046。这块触摸屏在国外创客社区被戏称为Cheap Yellow Display(CYD,廉价,屏偏黄),以成本低廉、实用性强为特点。
3.PCB板,详见“四、原理图和PCB介绍”。
三、方案框图和设计思路
1.方案框图
2.项目设计思路
项目整体设计简洁清晰,核心流程为通过 ESPHome 实现 ESP32S3 开发板接入 Home Assistant,再借助 LVGL 实现触摸屏的图形界面设计与分页显示功能。界面主要分为4个页面,分别展示 Home Assistant 推送的传感器数据,包含室内外温湿度、气压、天气预警等环境信息;设备本机信息,包含 IP 地址、WIFI 信号强度等;以及已接入 Home Assistant 的智能设备,包含插座、灯光等。用户可通过触摸屏查看环境信息、网络信息,触控控制灯光开关与插座通断,将普通触摸屏打造为功能完整的智能家居中控屏。
四、原理图和PCB
项目使用之前做的一块PCB板,主要实现 ESP32S3 开发板与 2.4 寸触摸屏的电路对接,保障触摸屏稳定运行,板载的电池供电模块、摄像头模块等未使用。
原理图
PCB图
PS:未使用电池供电模块、摄像头模块
五、软件流程和关键代码
1.开发环境
- 操作系统:WINDOWS 10、Home Assistant OS 15.0
- 开发软件:VS Code或Notepad++、Git Bash
- 调试工具:ESPConnect
- 固件烧录:ESPConnect、ESPHome 在线工具(web.esphome.io)、乐鑫 Flash Download Tool
2.编程语言
- 核心开发语言:YAML,用于ESPHome 配置文件编写;
- 辅助开发语言:C/C++,用于LVGL界面与硬件控制逻辑实现;
- 脚本语言:Shell,用于Git 仓库操作。
3.软件流程图

4.开发流程
整个项目按照“驱动适配→功能扩展→视觉优化”三个步骤来做,下面详细说一下实现过程:
1)驱动适配
屏幕驱动 ILI9341 与触摸驱动 XPT2046 均有 ESPHome 官方适配支持,可参考官方示例,核心难点为触摸屏校准。因触摸方向与屏幕显示方向不匹配,经多次翻转测试,最终确定适配的显示与触摸方向,参考配置代码如下:
# 屏幕背光控制 (GPIO4)
output:
- platform: ledc
id: backlight_output
pin: GPIO4
# 屏幕背光配置
light:
- platform: monochromatic
name: Backlight
id: backlight
output: backlight_output
restore_mode: ALWAYS_ON
default_transition_length: 1s
internal: true
# SPI总线
spi:
- id: lcd_spi
clk_pin: GPIO18
mosi_pin: GPIO17
miso_pin: GPIO16
# 触摸屏 (xpt2046)
touchscreen:
- platform: xpt2046
id: tft_touch
spi_id: lcd_spi
cs_pin: 5
interrupt_pin: 8
transform:
swap_xy: false
mirror_x: false
mirror_y: true
calibration:
x_min: 600
x_max: 3520
y_min: 1250
y_max: 4095
2)功能扩展
ESPHome 可通过原生 API 读取 Home Assistant 内的实体状态与属性值,并生成本地传感器。基于此,可接入三类数据:传统传感器,如温湿度等数值型传感器;二进制传感器,如开关等状态型传感器;文本传感器,如天气预报等文本信息。传感器配置代码如下:
# ======常规传感器(数值型)======
sensor:
# 室外温度
- platform: homeassistant
name: xiamen temperature
entity_id: sensor.xiamen_temperature
id: xiamen_temp
unit_of_measurement: "°C"
on_value:
then:
- lvgl.label.update:
id: outdoor_info
text: !lambda |-
return str_sprintf("室外:温度%.1f℃/湿度%.0f%%", x, id(xiamen_humi).state);
# 室外湿度
- platform: homeassistant
name: xiamen humidity
id: xiamen_humi
entity_id: sensor.xiamen_humidity
on_value:
then:
- lvgl.label.update:
id: outdoor_info
text: !lambda |-
return str_sprintf("室外:温度%.1f℃/湿度%.0f%%", id(xiamen_temp).state, x);
# 气压
- platform: homeassistant
name: xiamen pressure
id: xiamen_pressure
entity_id: sensor.xiamen_atmospheric_pressure
unit_of_measurement: "hPa"
on_value:
then:
- lvgl.label.update:
id: air_info
text: !lambda |-
return str_sprintf(" 气压%.0fhPa", x);
# 室内温度
- platform: homeassistant
name: xiaomi temperature
entity_id: sensor.cleargras_cn_blt_3_u5c2qgggk400_dk1_temperature_p_2_1
id: xiaomi_temp
unit_of_measurement: "°C"
on_value:
then:
- lvgl.label.update:
id: indoor_info
text: !lambda |-
return str_sprintf("室内:温度%.1f℃/湿度%.0f%%", x, id(xiaomi_humi).state);
# 室内湿度
- platform: homeassistant
name: xiaomi humidity
id: xiaomi_humi
entity_id: sensor.cleargras_cn_blt_3_u5c2qgggk400_dk1_relative_humidity_p_2_2
on_value:
then:
- lvgl.label.update:
id: indoor_info
text: !lambda |-
return str_sprintf("室内:温度%.1f℃/湿度%.0f%%", id(xiaomi_temp).state, x);
# ======二进制传感器(开/关状态)======
binary_sensor:
# 传感器:小米插座1开关
- platform: homeassistant
id: chuangmi_socket1_status
entity_id: switch.chuangmi_cn_125697630_m3_on_p_2_1
trigger_on_initial_state: true
on_state:
then:
lvgl.widget.update:
id: chuangmi_socket1_button
state:
checked: !lambda return x;
# 传感器:小米插座2开关
- platform: homeassistant
id: chuangmi_socket2_status
entity_id: switch.chuangmi_cn_125796823_m3_on_p_2_1
trigger_on_initial_state: true
on_state:
then:
lvgl.widget.update:
id: chuangmi_socket2_button
state:
checked: !lambda return x;
# 传感器:小米插座3开关
- platform: homeassistant
id: chuangmi_socket3_status
entity_id: switch.chuangmi_cn_125520813_m3_on_p_2_1
trigger_on_initial_state: true
on_state:
then:
lvgl.widget.update:
id: chuangmi_socket3_button
state:
checked: !lambda return x;
# 文本传感器
text_sensor:
# 厦门天气预报
- platform: homeassistant
id: xiamen_weather
entity_id: sensor.xiamen_forecast_minutely
on_value:
then:
- lvgl.label.update:
id: weather_alert_text
text: !lambda |-
return "预警: " + x;
3)UI视觉优化
为了让界面更美观,我使用LVGL图形框架进行开发,并引入 Material Design Icons 图标字体。在屏幕顶部实时显示日期和时间,底部设置 4 个图标作为页面导航按键,点击对应图标切换页面,中间区域为页面内容展示区。
屏幕界面分为 4 页:
- 首页为传感器信息页,展示室外温度、湿度、气压、天气预警及室内温湿度;
- 第2页为灯光控制页,因无可控灯光设备,改为控制触摸屏背光;
- 第3页为插座控制页,展示接入 Home Assistant 中的三个小米智能插座;
- 第4页为WIFI信息页,展示本机 WIFI 信息,包括 IP 地址、WIFI 信号强度等。
LVGL配置代码如下:
lvgl:
displays:
- tft_display
touchscreens:
- touchscreen_id: tft_touch
buffer_size: 100%
# =====顶层页面=====
top_layer:
widgets:
# 显示时间(顶部居中)
- label:
text: "----/--/-- --:--"
id: display_time
styles: roboto15_style
align: top_mid
x: 0
y: 4
text_align: center
text_color: 0xFFFFFF
# 底部导航按键
- obj:
align: bottom_mid
width: 100%
height: 55
bg_color: Black
bg_opa: cover
pad_all: 4
border_width: 0
layout:
type: flex
flex_flow: row
flex_align_main: space_evenly
flex_align_cross: center
widgets:
- button:
height: 45px
width: 45px
radius: $radius
styles: control
widgets:
- label:
text_font: $icon_font
align: center
text: $homeicon
on_click:
- lvgl.page.show: main_page
- button:
height: 45px
width: 45px
radius: $radius
styles: control
widgets:
- label:
text_font: $icon_font
align: center
text: $lightbulb
on_click:
- lvgl.page.show: light_page
- button:
height: 45px
width: 45px
radius: $radius
styles: control
widgets:
- label:
text_font: $icon_font
align: center
text: $socket
on_click:
- lvgl.page.show: socket_page
- button:
height: 45px
width: 45px
radius: $radius
styles: control
widgets:
- label:
text_font: $icon_font
align: center
text: $wifi
on_click:
- lvgl.page.show: network_page
pages:
- id: main_page
layout:
type: flex
flex_flow: column
flex_align_main: start # 主轴:从顶部开始排列
flex_align_cross: stretch # 交叉轴:子元素宽度拉伸填满
flex_align_track: start
scrollbar_mode: "off"
width: 100%
bg_color: Black
bg_opa: cover
pad_top: 36
pad_bottom: 60
pad_left: 10
pad_right: 5
widgets:
# 中部信息区 - 纯文字,无容器无底色
- label:
id: outdoor_info
width: 100%
text: "室外:温度--℃/湿度--%"
text_color: 0xFFFFFF
text_font: cn_font
text_align: left
pad_top: 20
pad_bottom: 5
- label:
id: air_info
width: 100%
text: " 气压--hPa"
text_color: 0xFFFFFF
text_font: cn_font
text_align: left
pad_top: 5
pad_bottom: 5
- label:
id: weather_alert_text
width: 100%
text: "预警:加载中..."
text_color: 0xFF8C00
text_font: cn_font
text_align: left
long_mode: wrap
pad_top: 5
pad_bottom: 5
- label:
id: indoor_info
width: 100%
text: "室内:温度--℃/湿度--%"
text_color: 0xFFFFFF
text_font: cn_font
text_align: left
pad_top: 5
pad_bottom: 10
# ===== 灯光页(屏幕亮度控制)=====
- id: light_page
layout:
type: flex
flex_flow: column
flex_align_main: center
flex_align_cross: center
scrollbar_mode: "off"
width: 100%
bg_color: Black
bg_opa: cover
pad_top: 36
pad_bottom: 60
pad_left: 10
pad_right: 10
widgets:
- arc:
id: brightness_arc
width: 180
height: 180
min_value: 0
max_value: 100
value: 80
start_angle: 135
end_angle: 45
arc_color: 0x333333 # 背景圆弧颜色
arc_width: 12 # 圆弧宽度
adjustable: true # 允许拖动
indicator:
arc_color: 0xFF8C00 # 进度条颜色(橙色)
arc_width: 12 # 进度条宽度
knob:
bg_color: 0xFFFFFF # 旋钮背景色(白色)
radius: 10 # 旋钮圆角(圆形)
pad_all: 0 # 无内边距
on_value:
then:
- light.turn_on:
id: backlight
brightness: !lambda |-
return x / 100.0;
# 插座页面
- id: socket_page
layout:
type: grid
grid_columns: [fr(1), fr(1)]
grid_rows: [fr(1), fr(1)]
scrollbar_mode: "off"
scroll_dir: "hor"
width: 100%
bg_color: Black
bg_opa: cover
pad_top: 34
pad_bottom: 60
pad_left: 5
pad_right: 5
widgets:
# 按钮:插座1
- button:
height: $button_height_single
width: $button_width
checkable: true
id: chuangmi_socket1_button
styles: control
grid_cell_column_pos: 0
grid_cell_row_pos: 0
widgets:
- label:
text_font: $icon_font
align: top_left
id: chuangmi_socket1_icon
text: $power
- label:
align: bottom_left
id: chuangmi_socket1_label
text: "插座1"
text_font: cn_font
on_click:
- homeassistant.action:
service: switch.toggle
data:
entity_id: switch.chuangmi_cn_125697630_m3_on_p_2_1
# 按钮:插座2
- button:
height: $button_height_single
width: $button_width
checkable: true
id: chuangmi_socket2_button
styles: control
grid_cell_column_pos: 1
grid_cell_row_pos: 0
widgets:
- label:
text_font: $icon_font
align: top_left
id: chuangmi_socket2_icon
text: $power
- label:
align: bottom_left
id: chuangmi_socket2_label
text: "插座2"
text_font: cn_font
on_click:
- homeassistant.action:
service: switch.toggle
data:
entity_id: switch.chuangmi_cn_125796823_m3_on_p_2_1
# 按钮:插座3
- button:
height: $button_height_single
width: $button_width
checkable: true
id: chuangmi_socket3_button
styles: control
grid_cell_column_pos: 0
grid_cell_row_pos: 1
widgets:
- label:
text_font: $icon_font
align: top_left
id: chuangmi_socket3_icon
text: $power
- label:
align: bottom_left
id: chuangmi_socket3_label
text: "插座3"
text_font: cn_font
on_click:
- homeassistant.action:
service: switch.toggle
data:
entity_id: switch.chuangmi_cn_125520813_m3_on_p_2_1
# ===== 网络页 =====
- id: network_page
layout:
type: flex
flex_flow: column
flex_align_main: start
flex_align_cross: stretch
flex_align_track: start
scrollbar_mode: "off"
width: 100%
bg_color: Black
bg_opa: cover
pad_top: 36
pad_bottom: 60
pad_left: 10
pad_right: 5
widgets:
- label:
id: network_ip
width: 100%
text: "IP: 获取中..."
text_color: 0xFFFFFF
text_font: cn_font
text_align: left
pad_top: 20
pad_bottom: 6
- label:
id: network_wifi
width: 100%
text: "WiFi: --dB/--%"
text_color: 0xFFFFFF
text_font: cn_font
text_align: left
pad_top: 6
pad_bottom: 20
4.在线编译固件
受国内网络环境影响,本地编译固件易出现依赖包下载失败的问题,因此采用 GitHub Actions workflows进行云端编译。
本地 ESPHome 配置完成后,通过 Git Bash 将修改后的代码推送到个人 GitHub 仓库:
git status
git add .
git commit -m "esp32s3-cyd2.4"
git push
回到个人 GitHub 仓库,①点击GitHub Actions,②左侧选择工作流(build ESP32S3-CYD2.4),③右侧点击Run workflow → Run workflow,手动触发工作流:

工作流跑完后状态显示 ✅,④点击该条记录,⑤在底部 Artifacts下载编译好的固件:

5.烧录固件
下载的固件包包含 3 个文件,其中firmware.factory.bin为完整可烧录固件。
推荐使用 ESPHome 在线工具烧录,操作简单、无需安装软件,也可使用乐鑫官方 Flash Download Tool,以下为 ESPHome 在线工具烧录步骤:
- 访问 ESPHome 在线工具:https://web.esphome.io/
- 将ESP32S3 开发板通过 USB 数据线连接至电脑,进入下载模式:按住 BOOT 键和 RST 键,先释放 RST 键,再释放BOOT 键
- 点击页面CONNECT按钮,在弹出的设备列表中选择ESP32S3对应的串口(如 COM12,可通过设备管理器查看)
- 连接成功后,点击页面中的INSTALL按钮,在弹出的固件选择窗口中,选择Custom firmware,上传之前编译好的固件
- 点击右下角的INSTALL开始烧录,工具会自动擦除原有固件、写入新固件
- 烧录完成后,按开发板上的RST 键重启设备
6.接入 Home Assistant
设备重启并连网后,Home Assistant 设备页面会自动识别新增的 ESPHome 设备,点击添加即可完成设备接入。
六、功能展示及说明
经过多次界面调试与参数优化,成功解决分页导航、页面布局、图标显示异常等问题,最终得到一个比较满意的显示效果,整体简洁、清晰,符合预期。
主页:传感器信息页面,顶部显示日期、时间,中间展示室外、室内环境信息及天气预警。

第2页:灯光控制页面,可通过滑动圆弧滑块调节屏幕背光亮度。(由于我房间没有可控制的灯光,改为控制屏幕背光)

第3页:插座控制页面,支持点击控制三个小米智能插座的通断。

插座开启状态(按下按键):

第4页:WIFI信息页面,展示设备的IP地址、WIFI信号强度。

七、遇到的难题和解决方法
难题1. 受电阻式触摸屏硬件限制,触控灵敏度偏低,经常出现点击无反应,需要多次点击才能触发操作的情况(详见视频)。
解决方法:需要学习电阻屏操作,采用轻点、稍作停留的按压方式,在现有硬件条件下尽量提升使用体验。
难题 2. 项目原本计划实现左右滑动切换页面的功能,可能受电阻式触摸屏限制,暂时无法实现。
解决方法:放弃滑动切换方案,改用屏幕底部固定图标导航栏,通过点击对应图标直接跳转页面。
八、心得体会
通过本项目的开发与实践,我对智能家居中控的开发有了深入的了解,为后续智能家居 DIY 项目打下了坚实基础。开发过程中遇到了触摸屏校准、触控响应等多项技术难题,通过不断排查问题、测试优化,提升了硬件调试与问题解决能力,同时也清晰认识到电阻式触摸屏与电容触摸屏在使用体验上的差距。